mirror of
				https://github.com/9001/copyparty.git
				synced 2025-10-30 20:22:20 -06:00 
			
		
		
		
	not even the deprecationwarning that got silently generated burning 20~30% of all CPU-time without actually displaying it anywhere, nice python 3.12.0 is now only 5% slower than 3.11.6 also fixes some other, less-performance-fatal deprecations
		
			
				
	
	
		
			112 lines
		
	
	
		
			2.9 KiB
		
	
	
	
		
			Python
		
	
	
		
			Executable file
		
	
	
	
	
			
		
		
	
	
			112 lines
		
	
	
		
			2.9 KiB
		
	
	
	
		
			Python
		
	
	
		
			Executable file
		
	
	
	
	
| #!/usr/bin/env python3
 | |
| 
 | |
| import hashlib
 | |
| import json
 | |
| import sys
 | |
| from datetime import datetime, timezone
 | |
| 
 | |
| 
 | |
| _ = r"""
 | |
| this hook will produce a single sha512 file which
 | |
| covers all recent uploads (plus metadata comments)
 | |
| 
 | |
| use this with --xiu, which makes copyparty buffer
 | |
| uploads until server is idle, providing file infos
 | |
| on stdin (filepaths or json)
 | |
| 
 | |
| example usage as global config:
 | |
|     --xiu i5,j,bin/hooks/xiu-sha.py
 | |
| 
 | |
| example usage as a volflag (per-volume config):
 | |
|     -v srv/inc:inc:r:rw,ed:c,xiu=i5,j,bin/hooks/xiu-sha.py
 | |
|                            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 | |
| 
 | |
|     (share filesystem-path srv/inc as volume /inc,
 | |
|      readable by everyone, read-write for user 'ed',
 | |
|      running this plugin on batches of uploads with the params listed below)
 | |
| 
 | |
| parameters explained,
 | |
|     xiu = execute after uploads...
 | |
|     i5  = ...after volume has been idle for 5sec
 | |
|     j   = provide json instead of filepath list
 | |
| 
 | |
| note the "f" (fork) flag is not set, so this xiu
 | |
| will block other xiu hooks while it's running
 | |
| """
 | |
| 
 | |
| 
 | |
| try:
 | |
|     from copyparty.util import fsenc
 | |
| except:
 | |
| 
 | |
|     def fsenc(p):
 | |
|         return p
 | |
| 
 | |
| 
 | |
| UTC = timezone.utc
 | |
| 
 | |
| 
 | |
| def humantime(ts):
 | |
|     return datetime.fromtimestamp(ts, UTC).strftime("%Y-%m-%d %H:%M:%S")
 | |
| 
 | |
| 
 | |
| def find_files_root(inf):
 | |
|     di = 9000
 | |
|     for f1, f2 in zip(inf, inf[1:]):
 | |
|         p1 = f1["ap"].replace("\\", "/").rsplit("/", 1)[0]
 | |
|         p2 = f2["ap"].replace("\\", "/").rsplit("/", 1)[0]
 | |
|         di = min(len(p1), len(p2), di)
 | |
|         di = next((i for i in range(di) if p1[i] != p2[i]), di)
 | |
| 
 | |
|     return di + 1
 | |
| 
 | |
| 
 | |
| def find_vol_root(inf):
 | |
|     return len(inf[0]["ap"][: -len(inf[0]["vp"])])
 | |
| 
 | |
| 
 | |
| def main():
 | |
|     zb = sys.stdin.buffer.read()
 | |
|     zs = zb.decode("utf-8", "replace")
 | |
|     inf = json.loads(zs)
 | |
| 
 | |
|     # root directory (where to put the sha512 file);
 | |
|     # di = find_files_root(inf)  # next to the file closest to volume root
 | |
|     di = find_vol_root(inf)  # top of the entire volume
 | |
| 
 | |
|     ret = []
 | |
|     total_sz = 0
 | |
|     for md in inf:
 | |
|         ap = md["ap"]
 | |
|         rp = ap[di:]
 | |
|         total_sz += md["sz"]
 | |
|         fsize = "{:,}".format(md["sz"])
 | |
|         mtime = humantime(md["mt"])
 | |
|         up_ts = humantime(md["at"])
 | |
| 
 | |
|         h = hashlib.sha512()
 | |
|         with open(fsenc(md["ap"]), "rb", 512 * 1024) as f:
 | |
|             while True:
 | |
|                 buf = f.read(512 * 1024)
 | |
|                 if not buf:
 | |
|                     break
 | |
| 
 | |
|                 h.update(buf)
 | |
| 
 | |
|         cksum = h.hexdigest()
 | |
|         meta = " | ".join([md["wark"], up_ts, mtime, fsize, md["ip"]])
 | |
|         ret.append("# {}\n{} *{}".format(meta, cksum, rp))
 | |
| 
 | |
|     ret.append("# {} files, {} bytes total".format(len(inf), total_sz))
 | |
|     ret.append("")
 | |
|     ftime = datetime.now(UTC).strftime("%Y-%m%d-%H%M%S.%f")
 | |
|     fp = "{}xfer-{}.sha512".format(inf[0]["ap"][:di], ftime)
 | |
|     with open(fsenc(fp), "wb") as f:
 | |
|         f.write("\n".join(ret).encode("utf-8", "replace"))
 | |
| 
 | |
|     print("wrote checksums to {}".format(fp))
 | |
| 
 | |
| 
 | |
| if __name__ == "__main__":
 | |
|     main()
 |