copyparty 1.9.11__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 +5 -3
- copyparty/__version__.py +2 -2
- copyparty/authsrv.py +1 -1
- copyparty/cfg.py +4 -2
- copyparty/httpcli.py +24 -16
- copyparty/ico.py +29 -2
- copyparty/smbd.py +1 -0
- copyparty/th_cli.py +2 -2
- copyparty/th_srv.py +40 -26
- copyparty/up2k.py +3 -0
- copyparty/web/browser.css.gz +0 -0
- copyparty/web/browser.html +1 -19
- copyparty/web/browser.js.gz +0 -0
- copyparty/web/ui.css.gz +0 -0
- copyparty/web/up2k.js.gz +0 -0
- copyparty/web/util.js.gz +0 -0
- {copyparty-1.9.11.dist-info → copyparty-1.9.12.dist-info}/METADATA +3 -2
- {copyparty-1.9.11.dist-info → copyparty-1.9.12.dist-info}/RECORD +22 -22
- {copyparty-1.9.11.dist-info → copyparty-1.9.12.dist-info}/LICENSE +0 -0
- {copyparty-1.9.11.dist-info → copyparty-1.9.12.dist-info}/WHEEL +0 -0
- {copyparty-1.9.11.dist-info → copyparty-1.9.12.dist-info}/entry_points.txt +0 -0
- {copyparty-1.9.11.dist-info → copyparty-1.9.12.dist-info}/top_level.txt +0 -0
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
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,
|
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
@@ -3829,6 +3829,9 @@ class HttpCli(object):
|
|
3829
3829
|
"acct": self.uname,
|
3830
3830
|
"idx": e2d,
|
3831
3831
|
"itag": e2t,
|
3832
|
+
"dsort": vf["sort"],
|
3833
|
+
"dfull": "nocrop" in vf,
|
3834
|
+
"u2ts": vf["u2ts"],
|
3832
3835
|
"lifetime": vn.flags.get("lifetime") or 0,
|
3833
3836
|
"frand": bool(vn.flags.get("rand")),
|
3834
3837
|
"unlist": unlist,
|
@@ -3836,41 +3839,46 @@ class HttpCli(object):
|
|
3836
3839
|
"logues": logues,
|
3837
3840
|
"readme": readme,
|
3838
3841
|
}
|
3839
|
-
|
3840
|
-
"vdir": quotep(self.vpath),
|
3841
|
-
"vpnodes": vpnodes,
|
3842
|
-
"files": [],
|
3842
|
+
cgv = {
|
3843
3843
|
"ls0": None,
|
3844
3844
|
"acct": self.uname,
|
3845
|
-
"perms":
|
3845
|
+
"perms": perms,
|
3846
|
+
"u2ts": vf["u2ts"],
|
3846
3847
|
"lifetime": ls_ret["lifetime"],
|
3847
3848
|
"frand": bool(vn.flags.get("rand")),
|
3848
|
-
"taglist": [],
|
3849
3849
|
"def_hcols": [],
|
3850
3850
|
"have_emp": self.args.emp,
|
3851
3851
|
"have_up2k_idx": e2d,
|
3852
|
-
"have_tags_idx": e2t,
|
3853
3852
|
"have_acode": (not self.args.no_acode),
|
3854
3853
|
"have_mv": (not self.args.no_mv),
|
3855
3854
|
"have_del": (not self.args.no_del),
|
3856
3855
|
"have_zip": (not self.args.no_zip),
|
3857
3856
|
"have_unpost": int(self.args.unpost),
|
3858
|
-
"have_b_u": (self.can_write and self.uparam.get("b") == "u"),
|
3859
3857
|
"sb_md": "" if "no_sb_md" in vf else (vf.get("md_sbf") or "y"),
|
3860
|
-
"sb_lg": "" if "no_sb_lg" in vf else (vf.get("lg_sbf") or "y"),
|
3861
|
-
"url_suf": url_suf,
|
3862
|
-
"logues": logues,
|
3863
3858
|
"readme": readme,
|
3864
|
-
"title": html_escape("%s %s" % (self.args.bname, self.vpath), crlf=True),
|
3865
|
-
"srv_info": srv_infot,
|
3866
3859
|
"dgrid": "grid" in vf,
|
3867
|
-
"
|
3868
|
-
"
|
3860
|
+
"dfull": "nocrop" in vf,
|
3861
|
+
"dsort": vf["sort"],
|
3869
3862
|
"themes": self.args.themes,
|
3870
3863
|
"turbolvl": self.args.turbo,
|
3871
3864
|
"idxh": int(self.args.ih),
|
3872
3865
|
"u2sort": self.args.u2sort,
|
3873
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
|
+
}
|
3874
3882
|
|
3875
3883
|
if self.args.js_browser:
|
3876
3884
|
zs = self.args.js_browser
|
@@ -4116,7 +4124,7 @@ class HttpCli(object):
|
|
4116
4124
|
dirs.sort(key=itemgetter("name"))
|
4117
4125
|
|
4118
4126
|
if is_js:
|
4119
|
-
j2a["ls0"] = {
|
4127
|
+
j2a["ls0"] = cgv["ls0"] = {
|
4120
4128
|
"dirs": dirs,
|
4121
4129
|
"files": files,
|
4122
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
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:
|
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
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
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
|
-
|
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
|
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 "
|
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 "
|
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 "
|
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:
|
copyparty/web/browser.css.gz
CHANGED
Binary file
|
copyparty/web/browser.html
CHANGED
@@ -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;
|
copyparty/web/browser.js.gz
CHANGED
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.
|
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
|
-
* `[
|
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=
|
3
|
-
copyparty/__version__.py,sha256=
|
4
|
-
copyparty/authsrv.py,sha256=
|
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=
|
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=
|
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=
|
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
|
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=
|
31
|
-
copyparty/th_srv.py,sha256=
|
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=
|
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=
|
58
|
-
copyparty/web/browser.html,sha256=
|
59
|
-
copyparty/web/browser.js.gz,sha256=
|
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=
|
79
|
-
copyparty/web/up2k.js.gz,sha256=
|
80
|
-
copyparty/web/util.js.gz,sha256=
|
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.
|
102
|
-
copyparty-1.9.
|
103
|
-
copyparty-1.9.
|
104
|
-
copyparty-1.9.
|
105
|
-
copyparty-1.9.
|
106
|
-
copyparty-1.9.
|
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,,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|