copyparty 1.9.10__py3-none-any.whl → 1.9.12__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
copyparty/__main__.py CHANGED
@@ -800,6 +800,7 @@ def add_upload(ap):
800
800
  ap2.add_argument("--no-dedup", action="store_true", help="disable symlink/hardlink creation; copy file contents instead (volflag=copydupes)")
801
801
  ap2.add_argument("--no-dupe", action="store_true", help="reject duplicate files during upload; only matches within the same volume (volflag=nodupe)")
802
802
  ap2.add_argument("--no-snap", action="store_true", help="disable snapshots -- forget unfinished uploads on shutdown; don't create .hist/up2k.snap files -- abandoned/interrupted uploads must be cleaned up manually")
803
+ ap2.add_argument("--u2ts", metavar="TXT", type=u, default="c", help="how to timestamp uploaded files; [\033[32mc\033[0m]=client-last-modified, [\033[32mu\033[0m]=upload-time, [\033[32mfc\033[0m]=force-c, [\033[32mfu\033[0m]=force-u (volflag=u2ts)")
803
804
  ap2.add_argument("--rand", action="store_true", help="force randomized filenames, --nrand chars long (volflag=rand)")
804
805
  ap2.add_argument("--nrand", metavar="NUM", type=int, default=9, help="randomized filenames length (volflag=nrand)")
805
806
  ap2.add_argument("--magic", action="store_true", help="enable filetype detection on nameless uploads (volflag=magic)")
@@ -1062,7 +1063,7 @@ def add_thumbnail(ap):
1062
1063
  ap2.add_argument("--th-size", metavar="WxH", default="320x256", help="thumbnail res (volflag=thsize)")
1063
1064
  ap2.add_argument("--th-mt", metavar="CORES", type=int, default=CORES, help="num cpu cores to use for generating thumbnails")
1064
1065
  ap2.add_argument("--th-convt", metavar="SEC", type=float, default=60, help="conversion timeout in seconds (volflag=convt)")
1065
- ap2.add_argument("--th-no-crop", action="store_true", help="dynamic height; show full image (volflag=nocrop)")
1066
+ ap2.add_argument("--th-no-crop", action="store_true", help="dynamic height; show full image by default (volflag=nocrop)")
1066
1067
  ap2.add_argument("--th-dec", metavar="LIBS", default="vips,pil,ff", help="image decoders, in order of preference")
1067
1068
  ap2.add_argument("--th-no-jpg", action="store_true", help="disable jpg output")
1068
1069
  ap2.add_argument("--th-no-webp", action="store_true", help="disable webp output")
@@ -1075,9 +1076,9 @@ def add_thumbnail(ap):
1075
1076
  # https://pillow.readthedocs.io/en/stable/handbook/image-file-formats.html
1076
1077
  # https://github.com/libvips/libvips
1077
1078
  # ffmpeg -hide_banner -demuxers | awk '/^ D /{print$2}' | while IFS= read -r x; do ffmpeg -hide_banner -h demuxer=$x; done | grep -E '^Demuxer |extensions:'
1078
- ap2.add_argument("--th-r-pil", metavar="T,T", type=u, default="avif,avifs,blp,bmp,dcx,dds,dib,emf,eps,fits,flc,fli,fpx,gif,heic,heics,heif,heifs,icns,ico,im,j2p,j2k,jp2,jpeg,jpg,jpx,pbm,pcx,pgm,png,pnm,ppm,psd,sgi,spi,tga,tif,tiff,webp,wmf,xbm,xpm", help="image formats to decode using pillow")
1079
+ ap2.add_argument("--th-r-pil", metavar="T,T", type=u, default="avif,avifs,blp,bmp,dcx,dds,dib,emf,eps,fits,flc,fli,fpx,gif,heic,heics,heif,heifs,icns,ico,im,j2p,j2k,jp2,jpeg,jpg,jpx,pbm,pcx,pgm,png,pnm,ppm,psd,qoi,sgi,spi,tga,tif,tiff,webp,wmf,xbm,xpm", help="image formats to decode using pillow")
1079
1080
  ap2.add_argument("--th-r-vips", metavar="T,T", type=u, default="avif,exr,fit,fits,fts,gif,hdr,heic,jp2,jpeg,jpg,jpx,jxl,nii,pfm,pgm,png,ppm,svg,tif,tiff,webp", help="image formats to decode using pyvips")
1080
- ap2.add_argument("--th-r-ffi", metavar="T,T", type=u, default="apng,avif,avifs,bmp,dds,dib,fit,fits,fts,gif,hdr,heic,heics,heif,heifs,icns,ico,jp2,jpeg,jpg,jpx,jxl,pbm,pcx,pfm,pgm,png,pnm,ppm,psd,sgi,tga,tif,tiff,webp,xbm,xpm", help="image formats to decode using ffmpeg")
1081
+ ap2.add_argument("--th-r-ffi", metavar="T,T", type=u, default="apng,avif,avifs,bmp,dds,dib,fit,fits,fts,gif,hdr,heic,heics,heif,heifs,icns,ico,jp2,jpeg,jpg,jpx,jxl,pbm,pcx,pfm,pgm,png,pnm,ppm,psd,qoi,sgi,tga,tif,tiff,webp,xbm,xpm", help="image formats to decode using ffmpeg")
1081
1082
  ap2.add_argument("--th-r-ffv", metavar="T,T", type=u, default="3gp,asf,av1,avc,avi,flv,h264,h265,hevc,m4v,mjpeg,mjpg,mkv,mov,mp4,mpeg,mpeg2,mpegts,mpg,mpg2,mts,nut,ogm,ogv,rm,ts,vob,webm,wmv", help="video formats to decode using ffmpeg")
1082
1083
  ap2.add_argument("--th-r-ffa", metavar="T,T", type=u, default="aac,ac3,aif,aiff,alac,alaw,amr,apac,ape,au,bonk,dfpwm,dts,flac,gsm,ilbc,it,m4a,mo3,mod,mp2,mp3,mpc,mptm,mt2,mulaw,ogg,okt,opus,ra,s3m,tak,tta,ulaw,wav,wma,wv,xm,xpk", help="audio formats to decode using ffmpeg")
1083
1084
 
@@ -1138,6 +1139,7 @@ def add_ui(ap, retry):
1138
1139
  ap2.add_argument("--lang", metavar="LANG", type=u, default="eng", help="language; one of the following: eng nor")
1139
1140
  ap2.add_argument("--theme", metavar="NUM", type=int, default=0, help="default theme to use")
1140
1141
  ap2.add_argument("--themes", metavar="NUM", type=int, default=8, help="number of themes installed")
1142
+ ap2.add_argument("--sort", metavar="C,C,C", type=u, default="href", help="default sort order, comma-separated column IDs (see header tooltips), prefix with '-' for descending. Examples: \033[32mhref -href ext sz ts tags/Album tags/.tn\033[0m (volflag=sort)")
1141
1143
  ap2.add_argument("--unlist", metavar="REGEX", type=u, default="", help="don't show files matching REGEX in file list. Purely cosmetic! Does not affect API calls, just the browser. Example: [\033[32m\\.(js|css)$\033[0m] (volflag=unlist)")
1142
1144
  ap2.add_argument("--favico", metavar="TXT", type=u, default="c 000 none" if retry else "🎉 000 none", help="\033[33mfavicon-text\033[0m [ \033[33mforeground\033[0m [ \033[33mbackground\033[0m ] ], set blank to disable")
1143
1145
  ap2.add_argument("--mpmc", metavar="URL", type=u, default="", help="change the mediaplayer-toggle mouse cursor; URL to a folder with {2..5}.png inside (or disable with [\033[32m.\033[0m])")
copyparty/__version__.py CHANGED
@@ -1,8 +1,8 @@
1
1
  # coding: utf-8
2
2
 
3
- VERSION = (1, 9, 10)
3
+ VERSION = (1, 9, 12)
4
4
  CODENAME = "prometheable"
5
- BUILD_DT = (2023, 10, 8)
5
+ BUILD_DT = (2023, 10, 15)
6
6
 
7
7
  S_VERSION = ".".join(map(str, VERSION))
8
8
  S_BUILD_DT = "{0:04d}-{1:02d}-{2:02d}".format(*BUILD_DT)
copyparty/authsrv.py CHANGED
@@ -12,7 +12,7 @@ import threading
12
12
  import time
13
13
  from datetime import datetime
14
14
 
15
- from .__init__ import ANYWIN, E, TYPE_CHECKING, WINDOWS
15
+ from .__init__ import ANYWIN, TYPE_CHECKING, WINDOWS, E
16
16
  from .bos import bos
17
17
  from .cfg import flagdescs, permdescs, vf_bmap, vf_cmap, vf_vmap
18
18
  from .pwhash import PWHash
copyparty/cfg.py CHANGED
@@ -42,7 +42,7 @@ def vf_bmap() :
42
42
  def vf_vmap() :
43
43
  """argv-to-volflag: simple values"""
44
44
  ret = {"th_convt": "convt", "th_size": "thsize"}
45
- for k in ("dbd", "lg_sbf", "md_sbf", "nrand", "unlist"):
45
+ for k in ("dbd", "lg_sbf", "md_sbf", "nrand", "sort", "unlist", "u2ts"):
46
46
  ret[k] = k
47
47
  return ret
48
48
 
@@ -86,6 +86,7 @@ flagcats = {
86
86
  "vmaxn=4k": "max 4096 files in volume (suffixes: b, k, m, g, t)",
87
87
  "rand": "force randomized filenames, 9 chars long by default",
88
88
  "nrand=N": "randomized filenames are N chars long",
89
+ "u2ts=fc": "[f]orce [c]lient-last-modified or [u]pload-time",
89
90
  "sz=1k-3m": "allow filesizes between 1 KiB and 3MiB",
90
91
  "df=1g": "ensure 1 GiB free disk space",
91
92
  },
@@ -129,7 +130,7 @@ flagcats = {
129
130
  "dathumb": "disables audio thumbnails (spectrograms)",
130
131
  "dithumb": "disables image thumbnails",
131
132
  "thsize": "thumbnail res; WxH",
132
- "nocrop": "disable center-cropping",
133
+ "nocrop": "disable center-cropping by default",
133
134
  "convt": "conversion timeout in seconds",
134
135
  },
135
136
  "handlers\n(better explained in --help-handlers)": {
@@ -149,6 +150,7 @@ flagcats = {
149
150
  },
150
151
  "client and ux": {
151
152
  "grid": "show grid/thumbnails by default",
153
+ "sort": "default sort order",
152
154
  "unlist": "dont list files matching REGEX",
153
155
  "html_head=TXT": "includes TXT in the <head>",
154
156
  "robots": "allows indexing by search engines (default)",
copyparty/httpcli.py CHANGED
@@ -128,7 +128,7 @@ class HttpCli(object):
128
128
  self.mode = " "
129
129
  self.req = " "
130
130
  self.http_ver = " "
131
- self.hint = " "
131
+ self.hint = ""
132
132
  self.host = " "
133
133
  self.ua = " "
134
134
  self.is_rclone = False
@@ -217,13 +217,6 @@ class HttpCli(object):
217
217
 
218
218
  def run(self) :
219
219
  """returns true if connection can be reused"""
220
- self.keepalive = False
221
- self.is_https = False
222
- self.headers = {}
223
- self.hint = ""
224
- self.uname = self.pw = " "
225
-
226
- self.out_headerlist = []
227
220
  self.out_headers = {
228
221
  "Vary": "Origin, PW, Cookie",
229
222
  "Cache-Control": "no-store, max-age=0",
@@ -3836,6 +3829,9 @@ class HttpCli(object):
3836
3829
  "acct": self.uname,
3837
3830
  "idx": e2d,
3838
3831
  "itag": e2t,
3832
+ "dsort": vf["sort"],
3833
+ "dfull": "nocrop" in vf,
3834
+ "u2ts": vf["u2ts"],
3839
3835
  "lifetime": vn.flags.get("lifetime") or 0,
3840
3836
  "frand": bool(vn.flags.get("rand")),
3841
3837
  "unlist": unlist,
@@ -3843,41 +3839,46 @@ class HttpCli(object):
3843
3839
  "logues": logues,
3844
3840
  "readme": readme,
3845
3841
  }
3846
- j2a = {
3847
- "vdir": quotep(self.vpath),
3848
- "vpnodes": vpnodes,
3849
- "files": [],
3842
+ cgv = {
3850
3843
  "ls0": None,
3851
3844
  "acct": self.uname,
3852
- "perms": json.dumps(perms),
3845
+ "perms": perms,
3846
+ "u2ts": vf["u2ts"],
3853
3847
  "lifetime": ls_ret["lifetime"],
3854
3848
  "frand": bool(vn.flags.get("rand")),
3855
- "taglist": [],
3856
3849
  "def_hcols": [],
3857
3850
  "have_emp": self.args.emp,
3858
3851
  "have_up2k_idx": e2d,
3859
- "have_tags_idx": e2t,
3860
3852
  "have_acode": (not self.args.no_acode),
3861
3853
  "have_mv": (not self.args.no_mv),
3862
3854
  "have_del": (not self.args.no_del),
3863
3855
  "have_zip": (not self.args.no_zip),
3864
3856
  "have_unpost": int(self.args.unpost),
3865
- "have_b_u": (self.can_write and self.uparam.get("b") == "u"),
3866
3857
  "sb_md": "" if "no_sb_md" in vf else (vf.get("md_sbf") or "y"),
3867
- "sb_lg": "" if "no_sb_lg" in vf else (vf.get("lg_sbf") or "y"),
3868
- "url_suf": url_suf,
3869
- "logues": logues,
3870
3858
  "readme": readme,
3871
- "title": html_escape("%s %s" % (self.args.bname, self.vpath), crlf=True),
3872
- "srv_info": srv_infot,
3873
3859
  "dgrid": "grid" in vf,
3874
- "unlist": unlist,
3875
- "dtheme": self.args.theme,
3860
+ "dfull": "nocrop" in vf,
3861
+ "dsort": vf["sort"],
3876
3862
  "themes": self.args.themes,
3877
3863
  "turbolvl": self.args.turbo,
3878
3864
  "idxh": int(self.args.ih),
3879
3865
  "u2sort": self.args.u2sort,
3880
3866
  }
3867
+ j2a = {
3868
+ "cgv": cgv,
3869
+ "vpnodes": vpnodes,
3870
+ "files": [],
3871
+ "ls0": None,
3872
+ "taglist": [],
3873
+ "have_tags_idx": e2t,
3874
+ "have_b_u": (self.can_write and self.uparam.get("b") == "u"),
3875
+ "sb_lg": "" if "no_sb_lg" in vf else (vf.get("lg_sbf") or "y"),
3876
+ "url_suf": url_suf,
3877
+ "logues": logues,
3878
+ "title": html_escape("%s %s" % (self.args.bname, self.vpath), crlf=True),
3879
+ "srv_info": srv_infot,
3880
+ "dtheme": self.args.theme,
3881
+ }
3881
3882
 
3882
3883
  if self.args.js_browser:
3883
3884
  zs = self.args.js_browser
@@ -4123,7 +4124,7 @@ class HttpCli(object):
4123
4124
  dirs.sort(key=itemgetter("name"))
4124
4125
 
4125
4126
  if is_js:
4126
- j2a["ls0"] = {
4127
+ j2a["ls0"] = cgv["ls0"] = {
4127
4128
  "dirs": dirs,
4128
4129
  "files": files,
4129
4130
  "taglist": taglist,
copyparty/ico.py CHANGED
@@ -4,9 +4,10 @@ from __future__ import print_function, unicode_literals
4
4
  import argparse # typechk
5
5
  import colorsys
6
6
  import hashlib
7
+ import re
7
8
 
8
9
  from .__init__ import PY2
9
- from .th_srv import HAVE_PIL
10
+ from .th_srv import HAVE_PIL, HAVE_PILF
10
11
  from .util import BytesIO
11
12
 
12
13
 
@@ -24,7 +25,7 @@ class Ico(object):
24
25
  zb = [ord(x) for x in zb]
25
26
 
26
27
  c1 = colorsys.hsv_to_rgb(zb[0] / 256.0, 1, 0.3)
27
- c2 = colorsys.hsv_to_rgb(zb[0] / 256.0, 1, 1)
28
+ c2 = colorsys.hsv_to_rgb(zb[0] / 256.0, 0.8 if HAVE_PILF else 1, 1)
28
29
  ci = [int(x * 255) for x in list(c1) + list(c2)]
29
30
  c = "".join(["{:02x}".format(x) for x in ci])
30
31
 
@@ -37,6 +38,32 @@ class Ico(object):
37
38
 
38
39
  if chrome:
39
40
  # cannot handle more than ~2000 unique SVGs
41
+ if HAVE_PILF:
42
+ # pillow 10.1 made this the default font;
43
+ # svg: 3.7s, this: 36s
44
+ try:
45
+ from PIL import Image, ImageDraw
46
+
47
+ # [.lt] are hard to see lowercase / unspaced
48
+ ext2 = re.sub("(.)", "\\1 ", ext).upper()
49
+
50
+ h = int(128 * h / w)
51
+ w = 128
52
+ img = Image.new("RGB", (w, h), "#" + c[:6])
53
+ pb = ImageDraw.Draw(img)
54
+ _, _, tw, th = pb.textbbox((0, 0), ext2, font_size=16)
55
+ xy = ((w - tw) // 2, (h - th) // 2)
56
+ pb.text(xy, ext2, fill="#" + c[6:], font_size=16)
57
+
58
+ img = img.resize((w * 2, h * 2), Image.NEAREST)
59
+
60
+ buf = BytesIO()
61
+ img.save(buf, format="PNG", compress_level=1)
62
+ return "image/png", buf.getvalue()
63
+
64
+ except:
65
+ pass
66
+
40
67
  if HAVE_PIL:
41
68
  # svg: 3s, cache: 6s, this: 8s
42
69
  from PIL import Image, ImageDraw
copyparty/smbd.py CHANGED
@@ -159,6 +159,7 @@ class SMB(object):
159
159
  if "connData" in cl:
160
160
  return cl["connData"]["partygoer"]
161
161
  cf = cf.f_back
162
+ raise Exception()
162
163
  except:
163
164
  warning(
164
165
  "nyoron... %s <<-- %s <<-- %s <<-- %s",
copyparty/th_cli.py CHANGED
@@ -28,7 +28,7 @@ class ThumbCli(object):
28
28
  if not c:
29
29
  raise Exception()
30
30
  except:
31
- c = {k: {} for k in ["thumbable", "pil", "vips", "ffi", "ffv", "ffa"]}
31
+ c = {k: set() for k in ["thumbable", "pil", "vips", "ffi", "ffv", "ffa"]}
32
32
 
33
33
  self.thumbable = c["thumbable"]
34
34
  self.fmt_pil = c["pil"]
@@ -91,7 +91,7 @@ class ThumbCli(object):
91
91
  self.log("no histpath for [{}]".format(ptop))
92
92
  return None
93
93
 
94
- tpath = thumb_path(histpath, rem, mtime, fmt)
94
+ tpath = thumb_path(histpath, rem, mtime, fmt, self.fmt_ffa)
95
95
  tpaths = [tpath]
96
96
  if fmt == "w":
97
97
  # also check for jpg (maybe webp is unavailable)
copyparty/th_srv.py CHANGED
@@ -34,14 +34,21 @@ if TYPE_CHECKING:
34
34
  from .svchub import SvcHub
35
35
 
36
36
  HAVE_PIL = False
37
+ HAVE_PILF = False
37
38
  HAVE_HEIF = False
38
39
  HAVE_AVIF = False
39
40
  HAVE_WEBP = False
40
41
 
41
42
  try:
42
- from PIL import ExifTags, Image, ImageOps
43
+ from PIL import ExifTags, Image, ImageFont, ImageOps
43
44
 
44
45
  HAVE_PIL = True
46
+ try:
47
+ ImageFont.load_default(size=16)
48
+ HAVE_PILF = True
49
+ except:
50
+ pass
51
+
45
52
  try:
46
53
  Image.new("RGB", (2, 2)).save(BytesIO(), format="webp")
47
54
  HAVE_WEBP = True
@@ -76,17 +83,23 @@ except:
76
83
  HAVE_VIPS = False
77
84
 
78
85
 
79
- def thumb_path(histpath , rem , mtime , fmt ) :
86
+ def thumb_path(histpath , rem , mtime , fmt , ffa ) :
80
87
  # base16 = 16 = 256
81
88
  # b64-lc = 38 = 1444
82
89
  # base64 = 64 = 4096
83
90
  rd, fn = vsplit(rem)
84
- if rd:
85
- h = hashlib.sha512(afsenc(rd)).digest()
86
- b64 = base64.urlsafe_b64encode(h).decode("ascii")[:24]
87
- rd = "{}/{}/".format(b64[:2], b64[2:4]).lower() + b64
88
- else:
89
- rd = "top"
91
+ if not rd:
92
+ rd = "\ntop"
93
+
94
+ # spectrograms are never cropped; strip fullsize flag
95
+ ext = rem.split(".")[-1].lower()
96
+ if ext in ffa and fmt in ("wf", "jf"):
97
+ fmt = fmt[:1]
98
+
99
+ rd += "\n" + fmt
100
+ h = hashlib.sha512(afsenc(rd)).digest()
101
+ b64 = base64.urlsafe_b64encode(h).decode("ascii")[:24]
102
+ rd = "{}/{}/".format(b64[:2], b64[2:4]).lower() + b64
90
103
 
91
104
  # could keep original filenames but this is safer re pathlen
92
105
  h = hashlib.sha512(afsenc(fn)).digest()
@@ -95,7 +108,8 @@ def thumb_path(histpath , rem , mtime , fmt ) :
95
108
  if fmt in ("opus", "caf"):
96
109
  cat = "ac"
97
110
  else:
98
- fmt = "webp" if fmt == "w" else "png" if fmt == "p" else "jpg"
111
+ fc = fmt[:1]
112
+ fmt = "webp" if fc == "w" else "png" if fc == "p" else "jpg"
99
113
  cat = "th"
100
114
 
101
115
  return "{}/{}/{}/{}.{:x}.{}".format(histpath, cat, rd, fn, int(mtime), fmt)
@@ -115,7 +129,7 @@ class ThumbSrv(object):
115
129
  self.stopping = False
116
130
  self.nthr = max(1, self.args.th_mt)
117
131
 
118
- self.q = Queue(self.nthr * 4)
132
+ self.q = Queue(self.nthr * 4)
119
133
  for n in range(self.nthr):
120
134
  Daemon(self.worker, "thumb-{}-{}".format(n, self.nthr))
121
135
 
@@ -190,7 +204,7 @@ class ThumbSrv(object):
190
204
  self.log("no histpath for [{}]".format(ptop))
191
205
  return None
192
206
 
193
- tpath = thumb_path(histpath, rem, mtime, fmt)
207
+ tpath = thumb_path(histpath, rem, mtime, fmt, self.fmt_ffa)
194
208
  abspath = os.path.join(ptop, rem)
195
209
  cond = threading.Condition(self.mutex)
196
210
  do_conv = False
@@ -217,8 +231,8 @@ class ThumbSrv(object):
217
231
  self.log("ptop [{}] not in {}".format(ptop, allvols), 3)
218
232
  vn = self.asrv.vfs.all_aps[0][1]
219
233
 
220
- self.q.put((abspath, tpath, vn))
221
- self.log("conv {} \033[0m{}".format(tpath, abspath), c=6)
234
+ self.q.put((abspath, tpath, fmt, vn))
235
+ self.log("conv {} :{} \033[0m{}".format(tpath, fmt, abspath), c=6)
222
236
 
223
237
  while not self.stopping:
224
238
  with self.mutex:
@@ -254,7 +268,7 @@ class ThumbSrv(object):
254
268
  if not task:
255
269
  break
256
270
 
257
- abspath, tpath, vn = task
271
+ abspath, tpath, fmt, vn = task
258
272
  ext = abspath.split(".")[-1].lower()
259
273
  png_ok = False
260
274
  funs = []
@@ -287,7 +301,7 @@ class ThumbSrv(object):
287
301
 
288
302
  for fun in funs:
289
303
  try:
290
- fun(abspath, ttpath, vn)
304
+ fun(abspath, ttpath, fmt, vn)
291
305
  break
292
306
  except Exception as ex:
293
307
  msg = "{} could not create thumbnail of {}\n{}"
@@ -321,7 +335,7 @@ class ThumbSrv(object):
321
335
  with self.mutex:
322
336
  self.nthr -= 1
323
337
 
324
- def fancy_pillow(self, im , vn ) :
338
+ def fancy_pillow(self, im , fmt , vn ) :
325
339
  # exif_transpose is expensive (loads full image + unconditional copy)
326
340
  res = self.getres(vn)
327
341
  r = max(*res) * 2
@@ -338,7 +352,7 @@ class ThumbSrv(object):
338
352
  if rot in rots:
339
353
  im = im.transpose(rots[rot])
340
354
 
341
- if "nocrop" in vn.flags:
355
+ if fmt.endswith("f"):
342
356
  im.thumbnail(res, resample=Image.LANCZOS)
343
357
  else:
344
358
  iw, ih = im.size
@@ -348,10 +362,10 @@ class ThumbSrv(object):
348
362
 
349
363
  return im
350
364
 
351
- def conv_pil(self, abspath , tpath , vn ) :
365
+ def conv_pil(self, abspath , tpath , fmt , vn ) :
352
366
  with Image.open(fsenc(abspath)) as im:
353
367
  try:
354
- im = self.fancy_pillow(im, vn)
368
+ im = self.fancy_pillow(im, fmt, vn)
355
369
  except Exception as ex:
356
370
  self.log("fancy_pillow {}".format(ex), "90")
357
371
  im.thumbnail(self.getres(vn))
@@ -377,9 +391,9 @@ class ThumbSrv(object):
377
391
 
378
392
  im.save(tpath, **args)
379
393
 
380
- def conv_vips(self, abspath , tpath , vn ) :
394
+ def conv_vips(self, abspath , tpath , fmt , vn ) :
381
395
  crops = ["centre", "none"]
382
- if "nocrop" in vn.flags:
396
+ if fmt.endswith("f"):
383
397
  crops = ["none"]
384
398
 
385
399
  w, h = self.getres(vn)
@@ -396,7 +410,7 @@ class ThumbSrv(object):
396
410
 
397
411
  img.write_to_file(tpath, Q=40)
398
412
 
399
- def conv_ffmpeg(self, abspath , tpath , vn ) :
413
+ def conv_ffmpeg(self, abspath , tpath , fmt , vn ) :
400
414
  ret, _ = ffprobe(abspath, int(vn.flags["convt"] / 2))
401
415
  if not ret:
402
416
  return
@@ -409,7 +423,7 @@ class ThumbSrv(object):
409
423
  seek = [b"-ss", "{:.0f}".format(dur / 3).encode("utf-8")]
410
424
 
411
425
  scale = "scale={0}:{1}:force_original_aspect_ratio="
412
- if "nocrop" in vn.flags:
426
+ if fmt.endswith("f"):
413
427
  scale += "decrease,setsar=1:1"
414
428
  else:
415
429
  scale += "increase,crop={0}:{1},setsar=1:1"
@@ -494,7 +508,7 @@ class ThumbSrv(object):
494
508
  self.log(t + txt, c=c)
495
509
  raise sp.CalledProcessError(ret, (cmd[0], b"...", cmd[-1]))
496
510
 
497
- def conv_waves(self, abspath , tpath , vn ) :
511
+ def conv_waves(self, abspath , tpath , fmt , vn ) :
498
512
  ret, _ = ffprobe(abspath, int(vn.flags["convt"] / 2))
499
513
  if "ac" not in ret:
500
514
  raise Exception("not audio")
@@ -522,7 +536,7 @@ class ThumbSrv(object):
522
536
  cmd += [fsenc(tpath)]
523
537
  self._run_ff(cmd, vn)
524
538
 
525
- def conv_spec(self, abspath , tpath , vn ) :
539
+ def conv_spec(self, abspath , tpath , fmt , vn ) :
526
540
  ret, _ = ffprobe(abspath, int(vn.flags["convt"] / 2))
527
541
  if "ac" not in ret:
528
542
  raise Exception("not audio")
@@ -565,7 +579,7 @@ class ThumbSrv(object):
565
579
  cmd += [fsenc(tpath)]
566
580
  self._run_ff(cmd, vn)
567
581
 
568
- def conv_opus(self, abspath , tpath , vn ) :
582
+ def conv_opus(self, abspath , tpath , fmt , vn ) :
569
583
  if self.args.no_acode:
570
584
  raise Exception("disabled in server config")
571
585
 
copyparty/up2k.py CHANGED
@@ -2336,6 +2336,9 @@ class Up2k(object):
2336
2336
  vols = [(ptop, jcur)] if jcur else []
2337
2337
  if vfs.flags.get("xlink"):
2338
2338
  vols += [(k, v) for k, v in self.cur.items() if k != ptop]
2339
+ if vfs.flags.get("up_ts", "") == "fu" or not cj["lmod"]:
2340
+ # force upload time rather than last-modified
2341
+ cj["lmod"] = int(time.time())
2339
2342
 
2340
2343
  alts = []
2341
2344
  for ptop, cur in vols:
Binary file
@@ -135,35 +135,17 @@
135
135
 
136
136
  <script>
137
137
  var SR = {{ r|tojson }},
138
+ CGV = {{ cgv|tojson }},
138
139
  TS = "{{ ts }}",
139
- acct = "{{ acct }}",
140
- perms = {{ perms }},
141
- dgrid = {{ dgrid|tojson }},
142
- themes = {{ themes }},
143
140
  dtheme = "{{ dtheme }}",
144
141
  srvinf = "{{ srv_info }}",
145
142
  s_name = "{{ s_name }}",
146
143
  lang = "{{ lang }}",
147
144
  dfavico = "{{ favico }}",
148
- def_hcols = {{ def_hcols|tojson }},
149
- have_up2k_idx = {{ have_up2k_idx|tojson }},
150
145
  have_tags_idx = {{ have_tags_idx|tojson }},
151
- have_acode = {{ have_acode|tojson }},
152
- have_mv = {{ have_mv|tojson }},
153
- have_del = {{ have_del|tojson }},
154
- have_unpost = {{ have_unpost }},
155
- have_zip = {{ have_zip|tojson }},
156
- sb_md = "{{ sb_md }}",
157
146
  sb_lg = "{{ sb_lg }}",
158
- lifetime = {{ lifetime }},
159
- turbolvl = {{ turbolvl }},
160
- idxh = {{ idxh }},
161
- frand = {{ frand|tojson }},
162
- u2sort = "{{ u2sort }}",
163
- have_emp = {{ have_emp|tojson }},
164
147
  txt_ext = "{{ txt_ext }}",
165
148
  logues = {{ logues|tojson if sb_lg else "[]" }},
166
- readme = {{ readme|tojson }},
167
149
  ls0 = {{ ls0|tojson }};
168
150
 
169
151
  document.documentElement.className = localStorage.cpp_thm || dtheme;
Binary file
copyparty/web/ui.css.gz CHANGED
Binary file
copyparty/web/up2k.js.gz CHANGED
Binary file
copyparty/web/util.js.gz CHANGED
Binary file
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: copyparty
3
- Version: 1.9.10
3
+ Version: 1.9.12
4
4
  Summary: Portable file server with accelerated resumable uploads, deduplication, WebDAV, FTP, zeroconf, media indexer, video thumbnails, audio transcoding, and write-only folders
5
5
  Author-email: ed <copyparty@ocv.me>
6
6
  License: MIT
@@ -631,7 +631,8 @@ the up2k UI is the epitome of polished inutitive experiences:
631
631
  * "parallel uploads" specifies how many chunks to upload at the same time
632
632
  * `[🏃]` analysis of other files should continue while one is uploading
633
633
  * `[🥔]` shows a simpler UI for faster uploads from slow devices
634
- * `[💭]` ask for confirmation before files are added to the queue
634
+ * `[🎲]` generate random filenames during upload
635
+ * `[📅]` preserve last-modified timestamps; server times will match yours
635
636
  * `[🔎]` switch between upload and [file-search](#file-search) mode
636
637
  * ignore `[🔎]` if you add files by dragging them into the browser
637
638
 
@@ -1,36 +1,36 @@
1
1
  copyparty/__init__.py,sha256=JA6xnECt2WITjgMEUsk7ZKYgxieda4QvXQtgxfnfkR4,1812
2
- copyparty/__main__.py,sha256=G5o4Guw1q5Vsg2qOSeCHBsEp0cs3ISpbAf-Nx4imvSU,81881
3
- copyparty/__version__.py,sha256=Q7KLpm_bjC3QoS4qwaAqwo9LTqf662NJfLiH57tx68Q,254
4
- copyparty/authsrv.py,sha256=XUq76axNNhjb9_869ON9mJLwzA45agvu3eBuNEocY9c,71161
2
+ copyparty/__main__.py,sha256=PbARYemugn8cIe46h9ETZmjP0-LNaguQgOGgJ-Pwcm8,82416
3
+ copyparty/__version__.py,sha256=Ou6gDONoBs-QPeCmmPzCFZAYGWtREya3Y-EXVFHciKQ,255
4
+ copyparty/authsrv.py,sha256=EcrgRS8SRIWR3hoi1S5V4rt6TFrIh4S6zjS-pW6r2pE,71161
5
5
  copyparty/broker_mp.py,sha256=OwxJk6mil0aPopz_JbONsk-b4Qmoa_sdS5G-tIIXjoM,3916
6
6
  copyparty/broker_mpw.py,sha256=GlSn4PRd_OqqeG39FiXgNvPzXVQW6UCiAcqmBSr2q6g,3200
7
7
  copyparty/broker_thr.py,sha256=eKr--HJGig5zqvNGwH9UoBG9Nvi9mT2axrRmJwknd0s,1759
8
8
  copyparty/broker_util.py,sha256=CnX_LAhQQqouONcDLtVkVlcBX3Z6pWuKDQDmmbHGEg4,1489
9
9
  copyparty/cert.py,sha256=r6zG-eLxwJW8AYz6ZTYzIlzuN6Wb62Ea4mZUrc_t6F0,7239
10
- copyparty/cfg.py,sha256=XlXIPfPLuTPZt6tMc2ibEZo6EmDGXJz5tq7yihotaY4,7891
10
+ copyparty/cfg.py,sha256=7-ZfUt3fKeAY5bOUNXU44b5Mj1d_7WIAmVG4AaMh-rI,8026
11
11
  copyparty/dxml.py,sha256=lZpg-kn-kQsXRtNY1n6fRaS-b7uXzMCyv8ovKnhZcZc,1548
12
12
  copyparty/fsutil.py,sha256=c4fTvmclKbVABNsjU4rGddsjCgRwi9YExAyo-06ATc8,3932
13
13
  copyparty/ftpd.py,sha256=6o_HpezJ-afXLsOkKXyIMQYKVz_MqZSOoy5L4z4dEeU,15369
14
- copyparty/httpcli.py,sha256=TsXB8yN0FUBFJDIVyVsYQFuikg_otp2zmuF6MO5xEio,136115
14
+ copyparty/httpcli.py,sha256=qEuMvb58VaIjQO1_iks_Zzk9Im_zMJ_qagiW4zAXfl8,136145
15
15
  copyparty/httpconn.py,sha256=CJU4wK_6QJFvgK9mQwEV55j6Muh_9k-GTe4AIehzJiw,6825
16
16
  copyparty/httpsrv.py,sha256=B2wmyXfohtqxC0lZ3j9UACYS8f9Zxlh4QSRH8_1owkw,16044
17
- copyparty/ico.py,sha256=q9UYAUcIaZ1WgLwVoVY5BFOTEKotF8HVEUZI2tpjDbo,3016
17
+ copyparty/ico.py,sha256=8QXApIqAVC1WnpM_RVFUgqerP7Y71DbxxF-IpUeV-n0,4042
18
18
  copyparty/mdns.py,sha256=CcraggbDxTT1ntYzD8Ebgqmw5Q4HkyZcfh5ymtCV_ak,17469
19
19
  copyparty/metrics.py,sha256=els7hu31vA1pEojAwHiygb9G7e7RpVGOn14adnKy9EU,6220
20
20
  copyparty/mtag.py,sha256=ImzIKZ7Powa91wDy6Dr2F-uWu11t20zRosQb1_3Y5js,16891
21
21
  copyparty/multicast.py,sha256=KZVNNtNW6uFHY7z9zZ94RWL3_3xZ4hJCUC55rWRtc_0,12285
22
22
  copyparty/pwhash.py,sha256=6RcQE35B-vo7evU4SS2YktEvT1h-r0FZRWP5cSNp98c,3859
23
- copyparty/smbd.py,sha256=TkkB8PTJnCrb0BySfAqUUO4jiX6Aodqvmz4mr65VxGs,13967
23
+ copyparty/smbd.py,sha256=-ey_edugqUGCKZtsKC1TZnFdaeSw7M7RvphUIpL75V4,13997
24
24
  copyparty/ssdp.py,sha256=H6ZftXttydcnBxcg2-Prm4P-XiybgT3xiJRUXU1pbrE,6343
25
25
  copyparty/star.py,sha256=LUfDc6Oy3EC5IZHxAgVzqBPKEDb4n7U94HB6U0-GndQ,3771
26
26
  copyparty/sutil.py,sha256=Wseu6-8bWFpLAHTLQf77oWm-aJLcslqFG6czyyqhGdQ,2962
27
27
  copyparty/svchub.py,sha256=zwww0dP4QUhmcE_5ndWlbE8YJ9NT8ojjW8zl1BvRTgM,26150
28
28
  copyparty/szip.py,sha256=SJg5nzN_5oaIsIYXlRvcLHVTqngLAOPfe4_WVsuhSkw,8520
29
29
  copyparty/tcpsrv.py,sha256=vz94zy-eUg5-U1lskN3VAQiTXVr80PPnwJfwzybVcW4,17169
30
- copyparty/th_cli.py,sha256=_YY8-hbGxpcrLmetj1NAEP-7JP3JLQlqi3Q6TQxpVjc,3852
31
- copyparty/th_srv.py,sha256=7QG7MmSoVG6KrbyncAYtEyPjxthLmFmyCX8GHeBtE6s,22828
30
+ copyparty/th_cli.py,sha256=MSp2kpoAPiX1bndMthv6JK2gt3K6CjrloWuJsI_CL94,3869
31
+ copyparty/th_srv.py,sha256=68MCzYVTm62Yct25Fs2udiLEB7tW7t8bLc3zext9IPs,23226
32
32
  copyparty/u2idx.py,sha256=xakOZ8TRB2VuwpcZmWw4TbATuv9bdbgT35b3IA9dXnY,10677
33
- copyparty/up2k.py,sha256=_kVv0eguuwrxFksx8GXmKHrIDwgFndUdMh8rP419BRM,128349
33
+ copyparty/up2k.py,sha256=Q1XEXv7ZwIqAVChQ0DkCZwpvVEpsmJH1FRJzsK_U5BA,128526
34
34
  copyparty/util.py,sha256=UsxOQYJx2LTNMVXwVK_6tywHWG7YBz1Y2w8O32GBdI4,73532
35
35
  copyparty/bos/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
36
36
  copyparty/bos/bos.py,sha256=Md2RwMauEdOS7SfK5aiHby8T1KgQoTaoEjVCYU98_4I,1560
@@ -54,9 +54,9 @@ copyparty/stolen/ifaddr/_posix.py,sha256=-67NdfGrCktfQPakT2fLbjl2U00QMvyBGkSvrUu
54
54
  copyparty/stolen/ifaddr/_shared.py,sha256=cJACl8cOxQ-HSYphZTzKMAjAx_TAFyJwUPjfD102Xqw,6111
55
55
  copyparty/stolen/ifaddr/_win32.py,sha256=EE-QyoBgeB7lYQ6z62VjXNaRozaYfCkaJBHGNA8QtZM,4026
56
56
  copyparty/web/baguettebox.js.gz,sha256=fyJfNSipzg093wMQAWiqI6SGoPcCe4JdLIvlOYkFG9Q,7379
57
- copyparty/web/browser.css.gz,sha256=H6HScVPq6crc8IzJHXtMKVghXCHBi5K_Y4kvRHoOOys,11127
58
- copyparty/web/browser.html,sha256=DX2ZJqhuWafpqB7DbvFKTtzEwuyiruerYvjmviYpGVI,5426
59
- copyparty/web/browser.js.gz,sha256=47GgNEWY0d9PKyfjAUJYtV5zDOWYBd0aUnObFieOsw0,61939
57
+ copyparty/web/browser.css.gz,sha256=zdo1FFmxPKcQEetdSUgWgB6hMEiuDWbhpF_nnHb_yXs,11150
58
+ copyparty/web/browser.html,sha256=3L31n92yTaMpHFmvoiNjAgCUMi60YHN2qYN6YQdUcBs,4839
59
+ copyparty/web/browser.js.gz,sha256=lsigw3wUlUtxPcvNxjoGg55QWhfq4KDxtE-f1xmEUDg,62445
60
60
  copyparty/web/browser2.html,sha256=3kR3QiDXYqtAo7gOoBtdTP3eRvibUCZHVVAbmAfaAVU,1604
61
61
  copyparty/web/cf.html,sha256=_tgwgNtK5MpjvvthGXx6Q9sasDaiWruyZfXsXYVW2KA,588
62
62
  copyparty/web/dbg-audio.js.gz,sha256=Ma-KZtK8LnmiwNvNKFKXMPYl_Nn_3U7GsJ6-DRWC2HE,688
@@ -75,9 +75,9 @@ copyparty/web/splash.html,sha256=8YLn9OVJRNce1Dp7tjZsGPItQREOaPUjN_bA3KZGHw8,374
75
75
  copyparty/web/splash.js.gz,sha256=3PMKsMLwEsHP8eonBmtvonm9yeNq69xjMi8ZqnVeKcA,1255
76
76
  copyparty/web/svcs.html,sha256=esTRzw4tkgWR_BPFZpDP0NX9slnkNnysE1E5weUdlOA,10591
77
77
  copyparty/web/svcs.js.gz,sha256=k81ZvZ3I-f4fMHKrNGGOgOlvXnCBz0mVjD-8mieoWCA,520
78
- copyparty/web/ui.css.gz,sha256=m-aJzGwAZ6_MpSI4eMkZ0DuwB0-Lk5Z8fEuceyYOm90,2441
79
- copyparty/web/up2k.js.gz,sha256=I-uVXXiIGw4tRB_VUkYTQLlNt2QNq6nTMNtC3hemxQM,21729
80
- copyparty/web/util.js.gz,sha256=vK7E3D4T1uTW-HKLnlU3Gx5V-llcbZay9gn-amHyoiU,13709
78
+ copyparty/web/ui.css.gz,sha256=_3liYHDeJjgtiQecpTHQ3qvcMGvx8347ViKpEm8DjWU,2456
79
+ copyparty/web/up2k.js.gz,sha256=xeU0oNCBvf5n3pZgVG3akGJAyMe7RAPdQE8e8J039zk,21882
80
+ copyparty/web/util.js.gz,sha256=oJDTYgqi6tKLKbRYAtxW-RFzy6eNjbgayct6ZhB3SEE,13735
81
81
  copyparty/web/w.hash.js.gz,sha256=P9469QknH8-1aKwI_1n1_S4yKvIGOu7bGoty9N3zYMI,1060
82
82
  copyparty/web/a/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
83
83
  copyparty/web/a/partyfuse.py,sha256=mJKqR8LjX19P4Knqa02ob5ogwPVtJXSqxkEEGZU0r60,32276
@@ -98,9 +98,9 @@ copyparty/web/deps/prismd.css.gz,sha256=ObUlksQVr-OuYlTz-I4B23TeBg2QDVVGRnWBz8cV
98
98
  copyparty/web/deps/scp.woff2,sha256=w99BDU5i8MukkMEL-iW0YO9H4vFFZSPWxbkH70ytaAg,8612
99
99
  copyparty/web/deps/sha512.ac.js.gz,sha256=lFZaCLumgWxrvEuDr4bqdKHsqjX82AbVAb7_F45Yk88,7033
100
100
  copyparty/web/deps/sha512.hw.js.gz,sha256=km3_b5IoaVwg02Ex6PxnhDLZxKiOMP2cHW35j9CSWFA,8107
101
- copyparty-1.9.10.dist-info/LICENSE,sha256=yyzj1id78vWoLs8zbMRJY7xkkLz0lv-9dfyeIauqdfM,1059
102
- copyparty-1.9.10.dist-info/METADATA,sha256=VvZ-tC2jQy2j-hWJH-4475jscpX6VuBSa2M0DBhrjiw,104189
103
- copyparty-1.9.10.dist-info/WHEEL,sha256=yQN5g4mg4AybRjkgi-9yy4iQEFibGQmlz78Pik5Or-A,92
104
- copyparty-1.9.10.dist-info/entry_points.txt,sha256=4zw6a3rqASywQomiYLObjjlxybaI65LYYOTJwgKz7b0,128
105
- copyparty-1.9.10.dist-info/top_level.txt,sha256=LnYUPsDyk-8kFgM6YJLG4h820DQekn81cObKSu9g-sI,10
106
- copyparty-1.9.10.dist-info/RECORD,,
101
+ copyparty-1.9.12.dist-info/LICENSE,sha256=yyzj1id78vWoLs8zbMRJY7xkkLz0lv-9dfyeIauqdfM,1059
102
+ copyparty-1.9.12.dist-info/METADATA,sha256=7C8UqsyHNe1zWvGyto8NcAqBRzjKgjbQvFjyY6--EkQ,104248
103
+ copyparty-1.9.12.dist-info/WHEEL,sha256=yQN5g4mg4AybRjkgi-9yy4iQEFibGQmlz78Pik5Or-A,92
104
+ copyparty-1.9.12.dist-info/entry_points.txt,sha256=4zw6a3rqASywQomiYLObjjlxybaI65LYYOTJwgKz7b0,128
105
+ copyparty-1.9.12.dist-info/top_level.txt,sha256=LnYUPsDyk-8kFgM6YJLG4h820DQekn81cObKSu9g-sI,10
106
+ copyparty-1.9.12.dist-info/RECORD,,