copyparty 1.16.11__py3-none-any.whl → 1.16.13__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 +6 -2
- copyparty/__version__.py +2 -2
- copyparty/authsrv.py +56 -7
- copyparty/cfg.py +40 -1
- copyparty/httpcli.py +93 -26
- copyparty/metrics.py +1 -1
- copyparty/multicast.py +1 -0
- copyparty/svchub.py +1 -1
- copyparty/up2k.py +2 -2
- copyparty/web/baguettebox.js.gz +0 -0
- copyparty/web/browser.css.gz +0 -0
- copyparty/web/browser.html +0 -2
- copyparty/web/browser.js.gz +0 -0
- copyparty/web/deps/marked.js.gz +0 -0
- copyparty/web/util.js.gz +0 -0
- {copyparty-1.16.11.dist-info → copyparty-1.16.13.dist-info}/METADATA +226 -6
- {copyparty-1.16.11.dist-info → copyparty-1.16.13.dist-info}/RECORD +21 -21
- {copyparty-1.16.11.dist-info → copyparty-1.16.13.dist-info}/LICENSE +0 -0
- {copyparty-1.16.11.dist-info → copyparty-1.16.13.dist-info}/WHEEL +0 -0
- {copyparty-1.16.11.dist-info → copyparty-1.16.13.dist-info}/entry_points.txt +0 -0
- {copyparty-1.16.11.dist-info → copyparty-1.16.13.dist-info}/top_level.txt +0 -0
copyparty/__main__.py
CHANGED
@@ -1175,6 +1175,7 @@ def add_webdav(ap):
|
|
1175
1175
|
ap2.add_argument("--dav-mac", action="store_true", help="disable apple-garbage filter -- allow macos to create junk files (._* and .DS_Store, .Spotlight-*, .fseventsd, .Trashes, .AppleDouble, __MACOS)")
|
1176
1176
|
ap2.add_argument("--dav-rt", action="store_true", help="show symlink-destination's lastmodified instead of the link itself; always enabled for recursive listings (volflag=davrt)")
|
1177
1177
|
ap2.add_argument("--dav-auth", action="store_true", help="force auth for all folders (required by davfs2 when only some folders are world-readable) (volflag=davauth)")
|
1178
|
+
ap2.add_argument("--dav-ua1", metavar="PTN", type=u, default=r" kioworker/", help="regex of tricky user-agents which expect 401 from GET requests; disable with [\033[32mno\033[0m] or blank")
|
1178
1179
|
|
1179
1180
|
|
1180
1181
|
def add_tftp(ap):
|
@@ -1255,7 +1256,8 @@ def add_optouts(ap):
|
|
1255
1256
|
ap2.add_argument("-nih", action="store_true", help="no info hostname -- don't show in UI")
|
1256
1257
|
ap2.add_argument("-nid", action="store_true", help="no info disk-usage -- don't show in UI")
|
1257
1258
|
ap2.add_argument("-nb", action="store_true", help="no powered-by-copyparty branding in UI")
|
1258
|
-
ap2.add_argument("--
|
1259
|
+
ap2.add_argument("--zip-who", metavar="LVL", type=int, default=3, help="who can download as zip/tar? [\033[32m0\033[0m]=nobody, [\033[32m1\033[0m]=admins, [\033[32m2\033[0m]=authenticated-with-read-access, [\033[32m3\033[0m]=everyone-with-read-access (volflag=zip_who)\n\033[1;31mWARNING:\033[0m if a nested volume has a more restrictive value than a parent volume, then this will be \033[33mignored\033[0m if the download is initiated from the parent, more lenient volume")
|
1260
|
+
ap2.add_argument("--no-zip", action="store_true", help="disable download as zip/tar; same as \033[33m--zip-who=0\033[0m")
|
1259
1261
|
ap2.add_argument("--no-tarcmp", action="store_true", help="disable download as compressed tar (?tar=gz, ?tar=bz2, ?tar=xz, ?tar=gz:9, ...)")
|
1260
1262
|
ap2.add_argument("--no-lifetime", action="store_true", help="do not allow clients (or server config) to schedule an upload to be deleted after a given time")
|
1261
1263
|
ap2.add_argument("--no-pipe", action="store_true", help="disable race-the-beam (lockstep download of files which are currently being uploaded) (volflag=nopipe)")
|
@@ -1386,7 +1388,7 @@ def add_transcoding(ap):
|
|
1386
1388
|
|
1387
1389
|
def add_rss(ap):
|
1388
1390
|
ap2 = ap.add_argument_group('RSS options')
|
1389
|
-
ap2.add_argument("--rss", action="store_true", help="enable RSS output (experimental)")
|
1391
|
+
ap2.add_argument("--rss", action="store_true", help="enable RSS output (experimental) (volflag=rss)")
|
1390
1392
|
ap2.add_argument("--rss-nf", metavar="HITS", type=int, default=250, help="default number of files to return (url-param 'nf')")
|
1391
1393
|
ap2.add_argument("--rss-fext", metavar="E,E", type=u, default="", help="default list of file extensions to include (url-param 'fext'); blank=all")
|
1392
1394
|
ap2.add_argument("--rss-sort", metavar="ORD", type=u, default="m", help="default sort order (url-param 'sort'); [\033[32mm\033[0m]=last-modified [\033[32mu\033[0m]=upload-time [\033[32mn\033[0m]=filename [\033[32ms\033[0m]=filesize; Uppercase=oldest-first. Note that upload-time is 0 for non-uploaded files")
|
@@ -1477,7 +1479,9 @@ def add_ui(ap, retry):
|
|
1477
1479
|
ap2.add_argument("--hsortn", metavar="N", type=int, default=2, help="number of sorting rules to include in media URLs by default (volflag=hsortn)")
|
1478
1480
|
ap2.add_argument("--unlist", metavar="REGEX", type=u, default="", help="don't show files matching \033[33mREGEX\033[0m in file list. Purely cosmetic! Does not affect API calls, just the browser. Example: [\033[32m\\.(js|css)$\033[0m] (volflag=unlist)")
|
1479
1481
|
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")
|
1482
|
+
ap2.add_argument("--ext-th", metavar="E=VP", type=u, action="append", help="use thumbnail-image \033[33mVP\033[0m for file-extension \033[33mE\033[0m, example: [\033[32mexe=/.res/exe.png\033[0m] (volflag=ext_th)")
|
1480
1483
|
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])")
|
1484
|
+
ap2.add_argument("--spinner", metavar="TXT", type=u, default="🌲", help="\033[33memoji\033[0m or \033[33memoji,css\033[0m Example: [\033[32m🥖,padding:0\033[0m]")
|
1481
1485
|
ap2.add_argument("--css-browser", metavar="L", type=u, default="", help="URL to additional CSS to include in the filebrowser html")
|
1482
1486
|
ap2.add_argument("--js-browser", metavar="L", type=u, default="", help="URL to additional JS to include in the filebrowser html")
|
1483
1487
|
ap2.add_argument("--js-other", metavar="L", type=u, default="", help="URL to additional JS to include in all other pages")
|
copyparty/__version__.py
CHANGED
copyparty/authsrv.py
CHANGED
@@ -1282,10 +1282,10 @@ class AuthSrv(object):
|
|
1282
1282
|
# one or more bools before the final flag; eat them
|
1283
1283
|
n1, uname = uname.split(",", 1)
|
1284
1284
|
for _, vp, _, _ in vols:
|
1285
|
-
self._read_volflag(flags[vp], n1, True, False)
|
1285
|
+
self._read_volflag(vp, flags[vp], n1, True, False)
|
1286
1286
|
|
1287
1287
|
for _, vp, _, _ in vols:
|
1288
|
-
self._read_volflag(flags[vp], uname, cval, False)
|
1288
|
+
self._read_volflag(vp, flags[vp], uname, cval, False)
|
1289
1289
|
|
1290
1290
|
return
|
1291
1291
|
|
@@ -1372,20 +1372,34 @@ class AuthSrv(object):
|
|
1372
1372
|
|
1373
1373
|
def _read_volflag(
|
1374
1374
|
self,
|
1375
|
+
vpath ,
|
1375
1376
|
flags ,
|
1376
1377
|
name ,
|
1377
1378
|
value ,
|
1378
1379
|
is_list ,
|
1379
1380
|
) :
|
1381
|
+
if name not in flagdescs:
|
1382
|
+
name = name.lower()
|
1383
|
+
|
1384
|
+
# volflags are snake_case, but a leading dash is the removal operator
|
1385
|
+
if name not in flagdescs and "-" in name[1:]:
|
1386
|
+
name = name[:1] + name[1:].replace("-", "_")
|
1387
|
+
|
1380
1388
|
desc = flagdescs.get(name.lstrip("-"), "?").replace("\n", " ")
|
1381
1389
|
|
1390
|
+
if not name:
|
1391
|
+
self._e("└─unreadable-line")
|
1392
|
+
t = "WARNING: the config for volume [/%s] indicated that a volflag was to be defined, but the volflag name was blank"
|
1393
|
+
self.log(t % (vpath,), 3)
|
1394
|
+
return
|
1395
|
+
|
1382
1396
|
if re.match("^-[^-]+$", name):
|
1383
1397
|
t = "└─unset volflag [{}] ({})"
|
1384
1398
|
self._e(t.format(name[1:], desc))
|
1385
1399
|
flags[name] = True
|
1386
1400
|
return
|
1387
1401
|
|
1388
|
-
zs = "mtp on403 on404 xbu xau xiu xbc xac xbr xar xbd xad xm xban"
|
1402
|
+
zs = "ext_th mtp on403 on404 xbu xau xiu xbc xac xbr xar xbd xad xm xban"
|
1389
1403
|
if name not in zs.split():
|
1390
1404
|
if value is True:
|
1391
1405
|
t = "└─add volflag [{}] = {} ({})"
|
@@ -1508,6 +1522,14 @@ class AuthSrv(object):
|
|
1508
1522
|
if not mount and not self.args.idp_h_usr:
|
1509
1523
|
# -h says our defaults are CWD at root and read/write for everyone
|
1510
1524
|
axs = AXS(["*"], ["*"], None, None)
|
1525
|
+
if os.path.exists("/z/initcfg"):
|
1526
|
+
t = "Read-access has been disabled due to failsafe: Docker detected, but the config does not define any volumes. This failsafe is to prevent unintended access if this is due to accidental loss of config. You can override this safeguard and allow read/write to all of /w/ by adding the following arguments to the docker container: -v .::rw"
|
1527
|
+
self.log(t, 1)
|
1528
|
+
axs = AXS()
|
1529
|
+
elif self.args.c:
|
1530
|
+
t = "Read-access has been disabled due to failsafe: No volumes were defined by the config-file. This failsafe is to prevent unintended access if this is due to accidental loss of config. You can override this safeguard and allow read/write to the working-directory by adding the following arguments: -v .::rw"
|
1531
|
+
self.log(t, 1)
|
1532
|
+
axs = AXS()
|
1511
1533
|
vfs = VFS(self.log_func, absreal("."), "", axs, {})
|
1512
1534
|
elif "" not in mount:
|
1513
1535
|
# there's volumes but no root; make root inaccessible
|
@@ -1542,6 +1564,17 @@ class AuthSrv(object):
|
|
1542
1564
|
vol.all_vps.sort(key=lambda x: len(x[0]), reverse=True)
|
1543
1565
|
vol.root = vfs
|
1544
1566
|
|
1567
|
+
zs = "neversymlink"
|
1568
|
+
k_ign = set(zs.split())
|
1569
|
+
for vol in vfs.all_vols.values():
|
1570
|
+
unknown_flags = set()
|
1571
|
+
for k, v in vol.flags.items():
|
1572
|
+
if k not in flagdescs and k not in k_ign:
|
1573
|
+
unknown_flags.add(k)
|
1574
|
+
if unknown_flags:
|
1575
|
+
t = "WARNING: the config for volume [/%s] has unrecognized volflags; will ignore: '%s'"
|
1576
|
+
self.log(t % (vol.vpath, "', '".join(unknown_flags)), 3)
|
1577
|
+
|
1545
1578
|
enshare = self.args.shr
|
1546
1579
|
shr = enshare[1:-1]
|
1547
1580
|
shrs = enshare[1:]
|
@@ -1907,7 +1940,7 @@ class AuthSrv(object):
|
|
1907
1940
|
if k not in vol.flags:
|
1908
1941
|
vol.flags[k] = getattr(self.args, k)
|
1909
1942
|
|
1910
|
-
for k in ("nrand", "u2abort"):
|
1943
|
+
for k in ("nrand", "u2abort", "ups_who", "zip_who"):
|
1911
1944
|
if k in vol.flags:
|
1912
1945
|
vol.flags[k] = int(vol.flags[k])
|
1913
1946
|
|
@@ -1959,8 +1992,10 @@ class AuthSrv(object):
|
|
1959
1992
|
|
1960
1993
|
# append additive args from argv to volflags
|
1961
1994
|
hooks = "xbu xau xiu xbc xac xbr xar xbd xad xm xban".split()
|
1962
|
-
for name in "mtp on404 on403".split() + hooks:
|
1963
|
-
self._read_volflag(
|
1995
|
+
for name in "ext_th mtp on404 on403".split() + hooks:
|
1996
|
+
self._read_volflag(
|
1997
|
+
vol.vpath, vol.flags, name, getattr(self.args, name), True
|
1998
|
+
)
|
1964
1999
|
|
1965
2000
|
for hn in hooks:
|
1966
2001
|
cmds = vol.flags.get(hn)
|
@@ -1988,6 +2023,16 @@ class AuthSrv(object):
|
|
1988
2023
|
ncmds.append(ocmd)
|
1989
2024
|
vol.flags[hn] = ncmds
|
1990
2025
|
|
2026
|
+
ext_th = vol.flags["ext_th_d"] = {}
|
2027
|
+
etv = "(?)"
|
2028
|
+
try:
|
2029
|
+
for etv in vol.flags.get("ext_th") or []:
|
2030
|
+
k, v = etv.split("=")
|
2031
|
+
ext_th[k] = v
|
2032
|
+
except:
|
2033
|
+
t = "WARNING: volume [/%s]: invalid value specified for ext-th: %s"
|
2034
|
+
self.log(t % (vol.vpath, etv), 3)
|
2035
|
+
|
1991
2036
|
# d2d drops all database features for a volume
|
1992
2037
|
for grp, rm in [["d2d", "e2d"], ["d2t", "e2t"], ["d2d", "e2v"]]:
|
1993
2038
|
if not vol.flags.get(grp, False):
|
@@ -2339,6 +2384,7 @@ class AuthSrv(object):
|
|
2339
2384
|
"sb_lg": "" if "no_sb_lg" in vf else (vf.get("lg_sbf") or "y"),
|
2340
2385
|
}
|
2341
2386
|
js_htm = {
|
2387
|
+
"SPINNER": self.args.spinner,
|
2342
2388
|
"s_name": self.args.bname,
|
2343
2389
|
"have_up2k_idx": "e2d" in vf,
|
2344
2390
|
"have_acode": not self.args.no_acode,
|
@@ -2348,6 +2394,7 @@ class AuthSrv(object):
|
|
2348
2394
|
"have_del": not self.args.no_del,
|
2349
2395
|
"have_unpost": int(self.args.unpost),
|
2350
2396
|
"have_emp": self.args.emp,
|
2397
|
+
"ext_th": vf.get("ext_th_d") or {},
|
2351
2398
|
"sb_md": "" if "no_sb_md" in vf else (vf.get("md_sbf") or "y"),
|
2352
2399
|
"sba_md": vf.get("md_sba") or "",
|
2353
2400
|
"sba_lg": vf.get("lg_sba") or "",
|
@@ -2751,7 +2798,9 @@ class AuthSrv(object):
|
|
2751
2798
|
zs = "c ihead ohead mtm mtp on403 on404 xac xad xar xau xiu xban xbc xbd xbr xbu xm"
|
2752
2799
|
lst = set(zs.split())
|
2753
2800
|
askip = set("a v c vc cgen exp_lg exp_md theme".split())
|
2754
|
-
|
2801
|
+
|
2802
|
+
t = "exp_lg exp_md ext_th_d mv_re_r mv_re_t rm_re_r rm_re_t srch_re_dots srch_re_nodot"
|
2803
|
+
fskip = set(t.split())
|
2755
2804
|
|
2756
2805
|
# keymap from argv to vflag
|
2757
2806
|
amap = vf_bmap()
|
copyparty/cfg.py
CHANGED
@@ -5,6 +5,9 @@ from __future__ import print_function, unicode_literals
|
|
5
5
|
zs = "a c e2d e2ds e2dsa e2t e2ts e2tsr e2v e2vp e2vu ed emp i j lo mcr mte mth mtm mtp nb nc nid nih nth nw p q s ss sss v z zv"
|
6
6
|
onedash = set(zs.split())
|
7
7
|
|
8
|
+
# verify that all volflags are documented here:
|
9
|
+
# grep volflag= __main__.py | sed -r 's/.*volflag=//;s/\).*//' | sort | uniq | while IFS= read -r x; do grep -E "\"$x(=[^ \"]+)?\": \"" cfg.py || printf '%s\n' "$x"; done
|
10
|
+
|
8
11
|
|
9
12
|
def vf_bmap() :
|
10
13
|
"""argv-to-volflag: simple bools"""
|
@@ -94,6 +97,7 @@ def vf_vmap() :
|
|
94
97
|
"u2abort",
|
95
98
|
"u2ts",
|
96
99
|
"ups_who",
|
100
|
+
"zip_who",
|
97
101
|
):
|
98
102
|
ret[k] = k
|
99
103
|
return ret
|
@@ -105,6 +109,7 @@ def vf_cmap() :
|
|
105
109
|
for k in (
|
106
110
|
"exp_lg",
|
107
111
|
"exp_md",
|
112
|
+
"ext_th",
|
108
113
|
"mte",
|
109
114
|
"mth",
|
110
115
|
"mtp",
|
@@ -178,8 +183,11 @@ flagcats = {
|
|
178
183
|
"e2dsa": "scans all folders for new files on startup; also sets -e2d",
|
179
184
|
"e2t": "enable multimedia indexing; makes it possible to search for tags",
|
180
185
|
"e2ts": "scan existing files for tags on startup; also sets -e2t",
|
181
|
-
"
|
186
|
+
"e2tsr": "delete all metadata from DB (full rescan); also sets -e2ts",
|
182
187
|
"d2ts": "disables metadata collection for existing files",
|
188
|
+
"e2v": "verify integrity on startup by hashing files and comparing to db",
|
189
|
+
"e2vu": "when e2v fails, update the db (assume on-disk files are good)",
|
190
|
+
"e2vp": "when e2v fails, panic and quit copyparty",
|
183
191
|
"d2ds": "disables onboot indexing, overrides -e2ds*",
|
184
192
|
"d2t": "disables metadata collection, overrides -e2t*",
|
185
193
|
"d2v": "disables file verification, overrides -e2v*",
|
@@ -199,6 +207,8 @@ flagcats = {
|
|
199
207
|
"srch_excl": "exclude search results with URL matching this regex",
|
200
208
|
},
|
201
209
|
'database, audio tags\n"mte", "mth", "mtp", "mtm" all work the same as -mte, -mth, ...': {
|
210
|
+
"mte=artist,title": "media-tags to index/display",
|
211
|
+
"mth=fmt,res,ac": "media-tags to hide by default",
|
202
212
|
"mtp=.bpm=f,audio-bpm.py": 'uses the "audio-bpm.py" program to\ngenerate ".bpm" tags from uploads (f = overwrite tags)',
|
203
213
|
"mtp=ahash,vhash=media-hash.py": "collects two tags at once",
|
204
214
|
},
|
@@ -212,6 +222,7 @@ flagcats = {
|
|
212
222
|
"crop": "center-cropping (y/n/fy/fn)",
|
213
223
|
"th3x": "3x resolution (y/n/fy/fn)",
|
214
224
|
"convt": "conversion timeout in seconds",
|
225
|
+
"ext_th=s=/b.png": "use /b.png as thumbnail for file-extension s",
|
215
226
|
},
|
216
227
|
"handlers\n(better explained in --help-handlers)": {
|
217
228
|
"on404=PY": "handle 404s by executing PY file",
|
@@ -234,8 +245,12 @@ flagcats = {
|
|
234
245
|
"grid": "show grid/thumbnails by default",
|
235
246
|
"gsel": "select files in grid by ctrl-click",
|
236
247
|
"sort": "default sort order",
|
248
|
+
"nsort": "natural-sort of leading digits in filenames",
|
249
|
+
"hsortn": "number of sort-rules to add to media URLs",
|
237
250
|
"unlist": "dont list files matching REGEX",
|
238
251
|
"html_head=TXT": "includes TXT in the <head>, or @PATH for file at PATH",
|
252
|
+
"tcolor=#fc0": "theme color (a hint for webbrowsers, discord, etc.)",
|
253
|
+
"nodirsz": "don't show total folder size",
|
239
254
|
"robots": "allows indexing by search engines (default)",
|
240
255
|
"norobots": "kindly asks search engines to leave",
|
241
256
|
"no_sb_md": "disable js sandbox for markdown files",
|
@@ -248,10 +263,33 @@ flagcats = {
|
|
248
263
|
"lg_sba": "value of iframe allow-prop for *logue-sandbox",
|
249
264
|
"nohtml": "return html and markdown as text/html",
|
250
265
|
},
|
266
|
+
"opengraph (discord embeds)": {
|
267
|
+
"og": "enable OG (disables hotlinking)",
|
268
|
+
"og_site": "sitename; defaults to --name, disable with '-'",
|
269
|
+
"og_desc": "description text for all files; disable with '-'",
|
270
|
+
"og_th=jf": "thumbnail format; j / jf / jf3 / w / w3 / ...",
|
271
|
+
"og_title_a": "audio title format; default: {{ artist }} - {{ title }}",
|
272
|
+
"og_title_v": "video title format; default: {{ title }}",
|
273
|
+
"og_title_i": "image title format; default: {{ title }}",
|
274
|
+
"og_title=foo": "fallback title if there's nothing in the db",
|
275
|
+
"og_s_title": "force default title; do not read from tags",
|
276
|
+
"og_tpl": "custom html; see --og-tpl in --help",
|
277
|
+
"og_no_head": "you want to add tags manually with og_tpl",
|
278
|
+
"og_ua": "if defined: only send OG html if useragent matches this regex",
|
279
|
+
},
|
280
|
+
"textfiles": {
|
281
|
+
"exp": "enable textfile expansion; see --help-exp",
|
282
|
+
"exp_md": "placeholders to expand in markdown files; see --help",
|
283
|
+
"exp_lg": "placeholders to expand in prologue/epilogue; see --help",
|
284
|
+
},
|
251
285
|
"others": {
|
252
286
|
"dots": "allow all users with read-access to\nenable the option to show dotfiles in listings",
|
253
287
|
"fk=8": 'generates per-file accesskeys,\nwhich are then required at the "g" permission;\nkeys are invalidated if filesize or inode changes',
|
254
288
|
"fka=8": 'generates slightly weaker per-file accesskeys,\nwhich are then required at the "g" permission;\nnot affected by filesize or inode numbers',
|
289
|
+
"rss": "allow '?rss' URL suffix (experimental)",
|
290
|
+
"ups_who=2": "restrict viewing the list of recent uploads",
|
291
|
+
"zip_who=2": "restrict access to download-as-zip/tar",
|
292
|
+
"nopipe": "disable race-the-beam (download unfinished uploads)",
|
255
293
|
"mv_retry": "ms-windows: timeout for renaming busy files",
|
256
294
|
"rm_retry": "ms-windows: timeout for deleting busy files",
|
257
295
|
"davauth": "ask webdav clients to login for all folders",
|
@@ -261,3 +299,4 @@ flagcats = {
|
|
261
299
|
|
262
300
|
|
263
301
|
flagdescs = {k.split("=")[0]: v for tab in flagcats.values() for k, v in tab.items()}
|
302
|
+
|
copyparty/httpcli.py
CHANGED
@@ -186,7 +186,7 @@ class HttpCli(object):
|
|
186
186
|
self.is_vproxied = False
|
187
187
|
self.in_hdr_recv = True
|
188
188
|
self.headers = {}
|
189
|
-
self.mode = " "
|
189
|
+
self.mode = " " # http verb
|
190
190
|
self.req = " "
|
191
191
|
self.http_ver = ""
|
192
192
|
self.hint = ""
|
@@ -726,10 +726,10 @@ class HttpCli(object):
|
|
726
726
|
return self.handle_unlock() and self.keepalive
|
727
727
|
elif self.mode == "MKCOL":
|
728
728
|
return self.handle_mkcol() and self.keepalive
|
729
|
-
elif self.mode
|
730
|
-
return self.
|
729
|
+
elif self.mode in ("MOVE", "COPY"):
|
730
|
+
return self.handle_cpmv() and self.keepalive
|
731
731
|
else:
|
732
|
-
raise Pebkac(400, 'invalid HTTP
|
732
|
+
raise Pebkac(400, 'invalid HTTP verb "{0}"'.format(self.mode))
|
733
733
|
|
734
734
|
except Exception as ex:
|
735
735
|
if not isinstance(ex, Pebkac):
|
@@ -1228,6 +1228,20 @@ class HttpCli(object):
|
|
1228
1228
|
else:
|
1229
1229
|
return self.tx_404(True)
|
1230
1230
|
else:
|
1231
|
+
vfs = self.asrv.vfs
|
1232
|
+
if (
|
1233
|
+
not vfs.nodes
|
1234
|
+
and not vfs.axs.uread
|
1235
|
+
and not vfs.axs.uwrite
|
1236
|
+
and not vfs.axs.uget
|
1237
|
+
and not vfs.axs.uhtml
|
1238
|
+
and not vfs.axs.uadmin
|
1239
|
+
):
|
1240
|
+
t = "<h2>access denied due to failsafe; check server log</h2>"
|
1241
|
+
html = self.j2s("splash", this=self, msg=t)
|
1242
|
+
self.reply(html.encode("utf-8", "replace"), 500)
|
1243
|
+
return True
|
1244
|
+
|
1231
1245
|
if self.vpath:
|
1232
1246
|
ptn = self.args.nonsus_urls
|
1233
1247
|
if not ptn or not ptn.search(self.vpath):
|
@@ -1754,6 +1768,12 @@ class HttpCli(object):
|
|
1754
1768
|
if "%" in self.req:
|
1755
1769
|
self.log(" `-- %r" % (self.vpath,))
|
1756
1770
|
|
1771
|
+
if self.args.no_dav:
|
1772
|
+
raise Pebkac(405, "WebDAV is disabled in server config")
|
1773
|
+
|
1774
|
+
if not self.can_write:
|
1775
|
+
raise Pebkac(401, "authenticate")
|
1776
|
+
|
1757
1777
|
try:
|
1758
1778
|
return self._mkdir(self.vpath, True)
|
1759
1779
|
except Pebkac as ex:
|
@@ -1763,14 +1783,35 @@ class HttpCli(object):
|
|
1763
1783
|
self.reply(b"", ex.code)
|
1764
1784
|
return True
|
1765
1785
|
|
1766
|
-
def
|
1786
|
+
def handle_cpmv(self) :
|
1767
1787
|
dst = self.headers["destination"]
|
1768
|
-
|
1788
|
+
|
1789
|
+
# dolphin (kioworker/6.10) "webdav://127.0.0.1:3923/a/b.txt"
|
1790
|
+
dst = re.sub("^[a-zA-Z]+://[^/]+", "", dst).lstrip()
|
1791
|
+
|
1792
|
+
if self.is_vproxied and dst.startswith(self.args.SRS):
|
1793
|
+
dst = dst[len(self.args.RS) :]
|
1794
|
+
|
1795
|
+
if self.do_log:
|
1796
|
+
self.log("%s %s --//> %s @%s" % (self.mode, self.req, dst, self.uname))
|
1797
|
+
if "%" in self.req:
|
1798
|
+
self.log(" `-- %r" % (self.vpath,))
|
1799
|
+
|
1800
|
+
if self.args.no_dav:
|
1801
|
+
raise Pebkac(405, "WebDAV is disabled in server config")
|
1802
|
+
|
1769
1803
|
dst = unquotep(dst)
|
1770
|
-
if not self._mv(self.vpath, dst.lstrip("/")):
|
1771
|
-
return False
|
1772
1804
|
|
1773
|
-
|
1805
|
+
# overwrite=True is default; rfc4918 9.8.4
|
1806
|
+
overwrite = self.headers.get("overwrite", "").lower() != "f"
|
1807
|
+
|
1808
|
+
try:
|
1809
|
+
fun = self._cp if self.mode == "COPY" else self._mv
|
1810
|
+
return fun(self.vpath, dst.lstrip("/"), overwrite)
|
1811
|
+
except Pebkac as ex:
|
1812
|
+
if ex.code == 403:
|
1813
|
+
ex.code = 401
|
1814
|
+
raise
|
1774
1815
|
|
1775
1816
|
def _applesan(self) :
|
1776
1817
|
if self.args.dav_mac or "Darwin/" not in self.ua:
|
@@ -4263,8 +4304,14 @@ class HttpCli(object):
|
|
4263
4304
|
rem ,
|
4264
4305
|
items ,
|
4265
4306
|
) :
|
4266
|
-
|
4267
|
-
|
4307
|
+
lvl = vn.flags["zip_who"]
|
4308
|
+
if self.args.no_zip or not lvl:
|
4309
|
+
raise Pebkac(400, "download-as-zip/tar is disabled in server config")
|
4310
|
+
elif lvl <= 1 and not self.can_admin:
|
4311
|
+
raise Pebkac(400, "download-as-zip/tar is admin-only on this server")
|
4312
|
+
elif lvl <= 2 and self.uname in ("", "*"):
|
4313
|
+
t = "you must be authenticated to download-as-zip/tar on this server"
|
4314
|
+
raise Pebkac(400, t)
|
4268
4315
|
|
4269
4316
|
logmsg = "{:4} {} ".format("", self.req)
|
4270
4317
|
self.keepalive = False
|
@@ -4768,9 +4815,12 @@ class HttpCli(object):
|
|
4768
4815
|
# that the client is not a graphical browser
|
4769
4816
|
if (
|
4770
4817
|
rc == 403
|
4771
|
-
and
|
4772
|
-
and not self.ua.startswith("Mozilla/")
|
4818
|
+
and self.uname == "*"
|
4773
4819
|
and "sec-fetch-site" not in self.headers
|
4820
|
+
and (
|
4821
|
+
not self.ua.startswith("Mozilla/")
|
4822
|
+
or (self.args.dav_ua1 and self.args.dav_ua1.search(self.ua))
|
4823
|
+
)
|
4774
4824
|
):
|
4775
4825
|
rc = 401
|
4776
4826
|
self.out_headers["WWW-Authenticate"] = 'Basic realm="a"'
|
@@ -4804,7 +4854,7 @@ class HttpCli(object):
|
|
4804
4854
|
|
4805
4855
|
def scanvol(self) :
|
4806
4856
|
if not self.can_admin:
|
4807
|
-
raise Pebkac(403, "not allowed for user " + self.uname)
|
4857
|
+
raise Pebkac(403, "'scanvol' not allowed for user " + self.uname)
|
4808
4858
|
|
4809
4859
|
if self.args.no_rescan:
|
4810
4860
|
raise Pebkac(403, "the rescan feature is disabled in server config")
|
@@ -4827,7 +4877,7 @@ class HttpCli(object):
|
|
4827
4877
|
raise Pebkac(400, "only config files ('cfg') can be reloaded rn")
|
4828
4878
|
|
4829
4879
|
if not self.avol:
|
4830
|
-
raise Pebkac(403, "not allowed for user " + self.uname)
|
4880
|
+
raise Pebkac(403, "'reload' not allowed for user " + self.uname)
|
4831
4881
|
|
4832
4882
|
if self.args.no_reload:
|
4833
4883
|
raise Pebkac(403, "the reload feature is disabled in server config")
|
@@ -4837,7 +4887,7 @@ class HttpCli(object):
|
|
4837
4887
|
|
4838
4888
|
def tx_stack(self) :
|
4839
4889
|
if not self.avol and not [x for x in self.wvol if x in self.rvol]:
|
4840
|
-
raise Pebkac(403, "not allowed for user " + self.uname)
|
4890
|
+
raise Pebkac(403, "'stack' not allowed for user " + self.uname)
|
4841
4891
|
|
4842
4892
|
if self.args.no_stack:
|
4843
4893
|
raise Pebkac(403, "the stackdump feature is disabled in server config")
|
@@ -5130,7 +5180,7 @@ class HttpCli(object):
|
|
5130
5180
|
adm = "*" in vol.axs.uadmin or self.uname in vol.axs.uadmin
|
5131
5181
|
dots = "*" in vol.axs.udot or self.uname in vol.axs.udot
|
5132
5182
|
|
5133
|
-
lvl =
|
5183
|
+
lvl = vol.flags["ups_who"]
|
5134
5184
|
if not lvl:
|
5135
5185
|
continue
|
5136
5186
|
elif lvl == 1 and not adm:
|
@@ -5399,7 +5449,9 @@ class HttpCli(object):
|
|
5399
5449
|
|
5400
5450
|
def handle_rm(self, req ) :
|
5401
5451
|
if not req and not self.can_delete:
|
5402
|
-
|
5452
|
+
if self.mode == "DELETE" and self.uname == "*":
|
5453
|
+
raise Pebkac(401, "authenticate") # webdav
|
5454
|
+
raise Pebkac(403, "'delete' not allowed for user " + self.uname)
|
5403
5455
|
|
5404
5456
|
if self.args.no_del:
|
5405
5457
|
raise Pebkac(403, "the delete feature is disabled in server config")
|
@@ -5433,14 +5485,22 @@ class HttpCli(object):
|
|
5433
5485
|
if not dst:
|
5434
5486
|
raise Pebkac(400, "need dst vpath")
|
5435
5487
|
|
5436
|
-
return self._mv(self.vpath, dst.lstrip("/"))
|
5488
|
+
return self._mv(self.vpath, dst.lstrip("/"), False)
|
5437
5489
|
|
5438
|
-
def _mv(self, vsrc , vdst ) :
|
5490
|
+
def _mv(self, vsrc , vdst , overwrite ) :
|
5439
5491
|
if self.args.no_mv:
|
5440
5492
|
raise Pebkac(403, "the rename/move feature is disabled in server config")
|
5441
5493
|
|
5442
|
-
|
5443
|
-
self.asrv.vfs.get(
|
5494
|
+
# `handle_cpmv` will catch 403 from these and raise 401
|
5495
|
+
svn, srem = self.asrv.vfs.get(vsrc, self.uname, True, False, True)
|
5496
|
+
dvn, drem = self.asrv.vfs.get(vdst, self.uname, False, True)
|
5497
|
+
|
5498
|
+
if overwrite:
|
5499
|
+
dabs = dvn.canonical(drem)
|
5500
|
+
if bos.path.exists(dabs):
|
5501
|
+
self.log("overwriting %s" % (dabs,))
|
5502
|
+
self.asrv.vfs.get(vdst, self.uname, False, True, False, True)
|
5503
|
+
wunlink(self.log, dabs, dvn.flags)
|
5444
5504
|
|
5445
5505
|
x = self.conn.hsrv.broker.ask("up2k.handle_mv", self.uname, self.ip, vsrc, vdst)
|
5446
5506
|
self.loud_reply(x.get(), status=201)
|
@@ -5456,14 +5516,21 @@ class HttpCli(object):
|
|
5456
5516
|
if not dst:
|
5457
5517
|
raise Pebkac(400, "need dst vpath")
|
5458
5518
|
|
5459
|
-
return self._cp(self.vpath, dst.lstrip("/"))
|
5519
|
+
return self._cp(self.vpath, dst.lstrip("/"), False)
|
5460
5520
|
|
5461
|
-
def _cp(self, vsrc , vdst ) :
|
5521
|
+
def _cp(self, vsrc , vdst , overwrite ) :
|
5462
5522
|
if self.args.no_cp:
|
5463
5523
|
raise Pebkac(403, "the copy feature is disabled in server config")
|
5464
5524
|
|
5465
|
-
self.asrv.vfs.get(vsrc, self.uname, True, False)
|
5466
|
-
self.asrv.vfs.get(vdst, self.uname, False, True)
|
5525
|
+
svn, srem = self.asrv.vfs.get(vsrc, self.uname, True, False)
|
5526
|
+
dvn, drem = self.asrv.vfs.get(vdst, self.uname, False, True)
|
5527
|
+
|
5528
|
+
if overwrite:
|
5529
|
+
dabs = dvn.canonical(drem)
|
5530
|
+
if bos.path.exists(dabs):
|
5531
|
+
self.log("overwriting %s" % (dabs,))
|
5532
|
+
self.asrv.vfs.get(vdst, self.uname, False, True, False, True)
|
5533
|
+
wunlink(self.log, dabs, dvn.flags)
|
5467
5534
|
|
5468
5535
|
x = self.conn.hsrv.broker.ask("up2k.handle_cp", self.uname, self.ip, vsrc, vdst)
|
5469
5536
|
self.loud_reply(x.get(), status=201)
|
copyparty/metrics.py
CHANGED
copyparty/multicast.py
CHANGED
@@ -160,6 +160,7 @@ class MCast(object):
|
|
160
160
|
sck.settimeout(None)
|
161
161
|
sck.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
162
162
|
try:
|
163
|
+
# safe for this purpose; https://lwn.net/Articles/853637/
|
163
164
|
sck.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1)
|
164
165
|
except:
|
165
166
|
pass
|
copyparty/svchub.py
CHANGED
@@ -759,7 +759,7 @@ class SvcHub(object):
|
|
759
759
|
vs = os.path.expandvars(os.path.expanduser(vs))
|
760
760
|
setattr(al, k, vs)
|
761
761
|
|
762
|
-
for k in "sus_urls nonsus_urls".split(" "):
|
762
|
+
for k in "dav_ua1 sus_urls nonsus_urls".split(" "):
|
763
763
|
vs = getattr(al, k)
|
764
764
|
if not vs or vs == "no":
|
765
765
|
setattr(al, k, None)
|
copyparty/up2k.py
CHANGED
@@ -4610,7 +4610,7 @@ class Up2k(object):
|
|
4610
4610
|
|
4611
4611
|
cur = self.cur.get(ptop)
|
4612
4612
|
if not cur:
|
4613
|
-
return None, None, None, None,
|
4613
|
+
return None, None, None, None, "", None
|
4614
4614
|
|
4615
4615
|
rd, fn = vsplit(vrem)
|
4616
4616
|
q = "select w, mt, sz, ip, at from up where rd=? and fn=? limit 1"
|
@@ -4623,7 +4623,7 @@ class Up2k(object):
|
|
4623
4623
|
if hit:
|
4624
4624
|
wark, ftime, fsize, ip, at = hit
|
4625
4625
|
return cur, wark, ftime, fsize, ip, at
|
4626
|
-
return cur, None, None, None,
|
4626
|
+
return cur, None, None, None, "", None
|
4627
4627
|
|
4628
4628
|
def _forget_file(
|
4629
4629
|
self,
|
copyparty/web/baguettebox.js.gz
CHANGED
Binary file
|
copyparty/web/browser.css.gz
CHANGED
Binary file
|
copyparty/web/browser.html
CHANGED
copyparty/web/browser.js.gz
CHANGED
Binary file
|
copyparty/web/deps/marked.js.gz
CHANGED
Binary file
|
copyparty/web/util.js.gz
CHANGED
Binary file
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.2
|
2
2
|
Name: copyparty
|
3
|
-
Version: 1.16.
|
3
|
+
Version: 1.16.13
|
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
|
@@ -201,6 +201,9 @@ just run **[copyparty-sfx.py](https://github.com/9001/copyparty/releases/latest/
|
|
201
201
|
* or if you are on android, [install copyparty in termux](#install-on-android)
|
202
202
|
* or maybe you have a [synology nas / dsm](./docs/synology-dsm.md)
|
203
203
|
* or if your computer is messed up and nothing else works, [try the pyz](#zipapp)
|
204
|
+
* or if you don't trust copyparty yet and want to isolate it a little, then...
|
205
|
+
* ...maybe [prisonparty](./bin/prisonparty.sh) to create a tiny [chroot](https://wiki.archlinux.org/title/Chroot) (very portable),
|
206
|
+
* ...or [bubbleparty](./bin/bubbleparty.sh) to wrap it in [bubblewrap](https://github.com/containers/bubblewrap) (much better)
|
204
207
|
* or if you prefer to [use docker](./scripts/docker/) 🐋 you can do that too
|
205
208
|
* docker has all deps built-in, so skip this step:
|
206
209
|
|
@@ -312,7 +315,7 @@ also see [comparison to similar software](./docs/versus.md)
|
|
312
315
|
* ☑ search by name/path/date/size
|
313
316
|
* ☑ [search by ID3-tags etc.](#searching)
|
314
317
|
* client support
|
315
|
-
* ☑ [folder sync](#folder-sync)
|
318
|
+
* ☑ [folder sync](#folder-sync) (one-way only; full sync will never be supported)
|
316
319
|
* ☑ [curl-friendly](https://user-images.githubusercontent.com/241032/215322619-ea5fd606-3654-40ad-94ee-2bc058647bb2.png)
|
317
320
|
* ☑ [opengraph](#opengraph) (discord embeds)
|
318
321
|
* markdown
|
@@ -529,6 +532,40 @@ examples:
|
|
529
532
|
|
530
533
|
anyone trying to bruteforce a password gets banned according to `--ban-pw`; default is 24h ban for 9 failed attempts in 1 hour
|
531
534
|
|
535
|
+
and if you want to use config files instead of commandline args (good!) then here's the same examples as a configfile; save it as `foobar.conf` and use it like this: `python copyparty-sfx.py -c foobar.conf`
|
536
|
+
|
537
|
+
```yaml
|
538
|
+
[accounts]
|
539
|
+
u1: p1 # create account "u1" with password "p1"
|
540
|
+
u2: p2 # (note that comments must have
|
541
|
+
u3: p3 # two spaces before the # sign)
|
542
|
+
|
543
|
+
[/] # this URL will be mapped to...
|
544
|
+
/srv # ...this folder on the server filesystem
|
545
|
+
accs:
|
546
|
+
r: * # read-only for everyone, no account necessary
|
547
|
+
|
548
|
+
[/music] # create another volume at this URL,
|
549
|
+
/mnt/music # which is mapped to this folder
|
550
|
+
accs:
|
551
|
+
r: u1, u2 # only these accounts can read,
|
552
|
+
rw: u3 # and only u3 can read-write
|
553
|
+
|
554
|
+
[/inc]
|
555
|
+
/mnt/incoming
|
556
|
+
accs:
|
557
|
+
w: u1 # u1 can upload but not see/download any files,
|
558
|
+
rm: u2 # u2 can browse + move files out of this volume
|
559
|
+
|
560
|
+
[/i]
|
561
|
+
/mnt/ss
|
562
|
+
accs:
|
563
|
+
rw: u1 # u1 can read-write,
|
564
|
+
g: * # everyone can access files if they know the URL
|
565
|
+
flags:
|
566
|
+
fk: 4 # each file URL will have a 4-character password
|
567
|
+
```
|
568
|
+
|
532
569
|
|
533
570
|
## shadowing
|
534
571
|
|
@@ -536,6 +573,8 @@ hiding specific subfolders by mounting another volume on top of them
|
|
536
573
|
|
537
574
|
for example `-v /mnt::r -v /var/empty:web/certs:r` mounts the server folder `/mnt` as the webroot, but another volume is mounted at `/web/certs` -- so visitors can only see the contents of `/mnt` and `/mnt/web` (at URLs `/` and `/web`), but not `/mnt/web/certs` because URL `/web/certs` is mapped to `/var/empty`
|
538
575
|
|
576
|
+
the example config file right above this section may explain this better; the first volume `/` is mapped to `/srv` which means http://127.0.0.1:3923/music would try to read `/srv/music` on the server filesystem, but since there's another volume at `/music` mapped to `/mnt/music` then it'll go to `/mnt/music` instead
|
577
|
+
|
539
578
|
|
540
579
|
## dotfiles
|
541
580
|
|
@@ -547,6 +586,19 @@ a client can request to see dotfiles in directory listings if global option `-ed
|
|
547
586
|
|
548
587
|
dotfiles do not appear in search results unless one of the above is true, **and** the global option / volflag `dotsrch` is set
|
549
588
|
|
589
|
+
config file example, where the same permission to see dotfiles is given in two different ways just for reference:
|
590
|
+
|
591
|
+
```yaml
|
592
|
+
[/foo]
|
593
|
+
/srv/foo
|
594
|
+
accs:
|
595
|
+
r.: ed # user "ed" has read-access + dot-access in this volume;
|
596
|
+
# dotfiles are visible in listings, but not in searches
|
597
|
+
flags:
|
598
|
+
dotsrch # dotfiles will now appear in search results too
|
599
|
+
dots # another way to let everyone see dotfiles in this vol
|
600
|
+
```
|
601
|
+
|
550
602
|
|
551
603
|
# the browser
|
552
604
|
|
@@ -669,6 +721,26 @@ enabling `multiselect` lets you click files to select them, and then shift-click
|
|
669
721
|
* `multiselect` is mostly intended for phones/tablets, but the `sel` option in the `[⚙️] settings` tab is better suited for desktop use, allowing selection by CTRL-clicking and range-selection with SHIFT-click, all without affecting regular clicking
|
670
722
|
* the `sel` option can be made default globally with `--gsel` or per-volume with volflag `gsel`
|
671
723
|
|
724
|
+
to show `/icons/exe.png` as the thumbnail for all .exe files, `--ext-th=exe=/icons/exe.png` (optionally as a volflag)
|
725
|
+
|
726
|
+
config file example:
|
727
|
+
|
728
|
+
```yaml
|
729
|
+
[global]
|
730
|
+
no-thumb # disable ALL thumbnails and audio transcoding
|
731
|
+
no-vthumb # only disable video thumbnails
|
732
|
+
|
733
|
+
[/music]
|
734
|
+
/mnt/nas/music
|
735
|
+
accs:
|
736
|
+
r: * # everyone can read
|
737
|
+
flags:
|
738
|
+
dthumb # disable ALL thumbnails and audio transcoding
|
739
|
+
dvthumb # only disable video thumbnails
|
740
|
+
ext-th: exe=/ico/exe.png # /ico/exe.png is the thumbnail of *.exe
|
741
|
+
th-covers: folder.png,folder.jpg,cover.png,cover.jpg # the default
|
742
|
+
```
|
743
|
+
|
672
744
|
|
673
745
|
## zip downloads
|
674
746
|
|
@@ -793,6 +865,14 @@ undo/delete accidental uploads using the `[🧯]` tab in the UI
|
|
793
865
|
|
794
866
|
you can unpost even if you don't have regular move/delete access, however only for files uploaded within the past `--unpost` seconds (default 12 hours) and the server must be running with `-e2d`
|
795
867
|
|
868
|
+
config file example:
|
869
|
+
|
870
|
+
```yaml
|
871
|
+
[global]
|
872
|
+
e2d # enable up2k database (remember uploads)
|
873
|
+
unpost: 43200 # 12 hours (default)
|
874
|
+
```
|
875
|
+
|
796
876
|
|
797
877
|
### self-destruct
|
798
878
|
|
@@ -958,6 +1038,15 @@ will show uploader IP and upload-time if the visitor has the admin permission
|
|
958
1038
|
|
959
1039
|
note that the [🧯 unpost](#unpost) feature is better suited for viewing *your own* recent uploads, as it includes the option to undo/delete them
|
960
1040
|
|
1041
|
+
config file example:
|
1042
|
+
|
1043
|
+
```yaml
|
1044
|
+
[global]
|
1045
|
+
ups-when # everyone can see upload times
|
1046
|
+
ups-who: 1 # but only admins can see the list,
|
1047
|
+
# so ups-when doesn't take effect
|
1048
|
+
```
|
1049
|
+
|
961
1050
|
|
962
1051
|
## media player
|
963
1052
|
|
@@ -1102,7 +1191,16 @@ using arguments or config files, or a mix of both:
|
|
1102
1191
|
|
1103
1192
|
announce enabled services on the LAN ([pic](https://user-images.githubusercontent.com/241032/215344737-0eae8d98-9496-4256-9aa8-cd2f6971810d.png)) -- `-z` enables both [mdns](#mdns) and [ssdp](#ssdp)
|
1104
1193
|
|
1105
|
-
* `--z-on` / `--z-off`
|
1194
|
+
* `--z-on` / `--z-off` limits the feature to certain networks
|
1195
|
+
|
1196
|
+
config file example:
|
1197
|
+
|
1198
|
+
```yaml
|
1199
|
+
[global]
|
1200
|
+
z # enable all zeroconf features (mdns, ssdp)
|
1201
|
+
zm # only enables mdns (does nothing since we already have z)
|
1202
|
+
z-on: 192.168.0.0/16, 10.1.2.0/24 # restrict to certain subnets
|
1203
|
+
```
|
1106
1204
|
|
1107
1205
|
|
1108
1206
|
### mdns
|
@@ -1243,7 +1341,7 @@ dependencies: `python3 -m pip install --user -U impacket==0.11.0`
|
|
1243
1341
|
|
1244
1342
|
some **BIG WARNINGS** specific to SMB/CIFS, in decreasing importance:
|
1245
1343
|
* not entirely confident that read-only is read-only
|
1246
|
-
* the smb backend is not fully integrated with vfs, meaning there could be security issues (path traversal). Please use `--smb-port` (see below) and [prisonparty](./bin/prisonparty.sh)
|
1344
|
+
* the smb backend is not fully integrated with vfs, meaning there could be security issues (path traversal). Please use `--smb-port` (see below) and [prisonparty](./bin/prisonparty.sh) or [bubbleparty](./bin/bubbleparty.sh)
|
1247
1345
|
* account passwords work per-volume as expected, and so does account permissions (read/write/move/delete), but `--smbw` must be given to allow write-access from smb
|
1248
1346
|
* [shadowing](#shadowing) probably works as expected but no guarantees
|
1249
1347
|
|
@@ -1329,6 +1427,18 @@ advantages of using symlinks (default):
|
|
1329
1427
|
|
1330
1428
|
global-option `--xlink` / volflag `xlink` additionally enables deduplication across volumes, but this is probably buggy and not recommended
|
1331
1429
|
|
1430
|
+
config file example:
|
1431
|
+
|
1432
|
+
```yaml
|
1433
|
+
[global]
|
1434
|
+
e2dsa # scan and index filesystem on startup
|
1435
|
+
dedup # symlink-based deduplication for all volumes
|
1436
|
+
|
1437
|
+
[/media]
|
1438
|
+
/mnt/nas/media
|
1439
|
+
flags:
|
1440
|
+
hardlinkonly # this vol does hardlinks instead of symlinks
|
1441
|
+
```
|
1332
1442
|
|
1333
1443
|
|
1334
1444
|
## file indexing
|
@@ -1360,6 +1470,14 @@ note:
|
|
1360
1470
|
* `e2tsr` is probably always overkill, since `e2ds`/`e2dsa` would pick up any file modifications and `e2ts` would then reindex those, unless there is a new copyparty version with new parsers and the release note says otherwise
|
1361
1471
|
* the rescan button in the admin panel has no effect unless the volume has `-e2ds` or higher
|
1362
1472
|
|
1473
|
+
config file example (these options are recommended btw):
|
1474
|
+
|
1475
|
+
```yaml
|
1476
|
+
[global]
|
1477
|
+
e2dsa # scan and index all files in all volumes on startup
|
1478
|
+
e2ts # check newly-discovered or uploaded files for media tags
|
1479
|
+
```
|
1480
|
+
|
1363
1481
|
### exclude-patterns
|
1364
1482
|
|
1365
1483
|
to save some time, you can provide a regex pattern for filepaths to only index by filename/path/size/last-modified (and not the hash of the file contents) by setting `--no-hash '\.iso$'` or the volflag `:c,nohash=\.iso$`, this has the following consequences:
|
@@ -1369,12 +1487,24 @@ to save some time, you can provide a regex pattern for filepaths to only index
|
|
1369
1487
|
|
1370
1488
|
similarly, you can fully ignore files/folders using `--no-idx [...]` and `:c,noidx=\.iso$`
|
1371
1489
|
|
1490
|
+
NOTE: `no-idx` and/or `no-hash` prevents deduplication of those files
|
1491
|
+
|
1372
1492
|
* when running on macos, all the usual apple metadata files are excluded by default
|
1373
1493
|
|
1374
1494
|
if you set `--no-hash [...]` globally, you can enable hashing for specific volumes using flag `:c,nohash=`
|
1375
1495
|
|
1376
1496
|
to exclude certain filepaths from search-results, use `--srch-excl` or volflag `srch_excl` instead of `--no-idx`, for example `--srch-excl 'password|logs/[0-9]'`
|
1377
1497
|
|
1498
|
+
config file example:
|
1499
|
+
|
1500
|
+
```yaml
|
1501
|
+
[/games]
|
1502
|
+
/mnt/nas/games
|
1503
|
+
flags:
|
1504
|
+
noidx: \.iso$ # skip indexing iso-files
|
1505
|
+
srch_excl: password|logs/[0-9] # filter search results
|
1506
|
+
```
|
1507
|
+
|
1378
1508
|
### filesystem guards
|
1379
1509
|
|
1380
1510
|
avoid traversing into other filesystems using `--xdev` / volflag `:c,xdev`, skipping any symlinks or bind-mounts to another HDD for example
|
@@ -1395,6 +1525,20 @@ argument `--re-maxage 60` will rescan all volumes every 60 sec, same as volflag
|
|
1395
1525
|
|
1396
1526
|
uploads are disabled while a rescan is happening, so rescans will be delayed by `--db-act` (default 10 sec) when there is write-activity going on (uploads, renames, ...)
|
1397
1527
|
|
1528
|
+
note: folder-thumbnails are selected during filesystem indexing, so periodic rescans can be used to keep them accurate as images are uploaded/deleted (or manually do a rescan with the `reload` button in the controlpanel)
|
1529
|
+
|
1530
|
+
config file example:
|
1531
|
+
|
1532
|
+
```yaml
|
1533
|
+
[global]
|
1534
|
+
re-maxage: 3600
|
1535
|
+
|
1536
|
+
[/pics]
|
1537
|
+
/mnt/nas/pics
|
1538
|
+
flags:
|
1539
|
+
scan: 900
|
1540
|
+
```
|
1541
|
+
|
1398
1542
|
|
1399
1543
|
## upload rules
|
1400
1544
|
|
@@ -1420,6 +1564,26 @@ you can also set transaction limits which apply per-IP and per-volume, but these
|
|
1420
1564
|
notes:
|
1421
1565
|
* `vmaxb` and `vmaxn` requires either the `e2ds` volflag or `-e2dsa` global-option
|
1422
1566
|
|
1567
|
+
config file example:
|
1568
|
+
|
1569
|
+
```yaml
|
1570
|
+
[/inc]
|
1571
|
+
/mnt/nas/uploads
|
1572
|
+
accs:
|
1573
|
+
w: * # anyone can upload here
|
1574
|
+
rw: ed # only user "ed" can read-write
|
1575
|
+
flags:
|
1576
|
+
e2ds: # filesystem indexing is required for many of these:
|
1577
|
+
sz: 1k-3m # accept upload only if filesize in this range
|
1578
|
+
df: 4g # free disk space cannot go lower than this
|
1579
|
+
vmaxb: 1g # volume can never exceed 1 GiB
|
1580
|
+
vmaxn: 4k # ...or 4000 files, whichever comes first
|
1581
|
+
nosub # must upload to toplevel folder
|
1582
|
+
lifetime: 300 # uploads are deleted after 5min
|
1583
|
+
maxn: 250,3600 # each IP can upload 250 files in 1 hour
|
1584
|
+
maxb: 1g,300 # each IP can upload 1 GiB over 5 minutes
|
1585
|
+
```
|
1586
|
+
|
1423
1587
|
|
1424
1588
|
## compress uploads
|
1425
1589
|
|
@@ -1465,10 +1629,24 @@ this can instead be kept in a single place using the `--hist` argument, or the `
|
|
1465
1629
|
* `--hist ~/.cache/copyparty -v ~/music::r:c,hist=-` sets `~/.cache/copyparty` as the default place to put volume info, but `~/music` gets the regular `.hist` subfolder (`-` restores default behavior)
|
1466
1630
|
|
1467
1631
|
note:
|
1632
|
+
* putting the hist-folders on an SSD is strongly recommended for performance
|
1468
1633
|
* markdown edits are always stored in a local `.hist` subdirectory
|
1469
1634
|
* on windows the volflag path is cyglike, so `/c/temp` means `C:\temp` but use regular paths for `--hist`
|
1470
1635
|
* you can use cygpaths for volumes too, `-v C:\Users::r` and `-v /c/users::r` both work
|
1471
1636
|
|
1637
|
+
config file example:
|
1638
|
+
|
1639
|
+
```yaml
|
1640
|
+
[global]
|
1641
|
+
hist: ~/.cache/copyparty # put db/thumbs/etc. here by default
|
1642
|
+
|
1643
|
+
[/pics]
|
1644
|
+
/mnt/nas/pics
|
1645
|
+
flags:
|
1646
|
+
hist: - # restore the default (/mnt/nas/pics/.hist/)
|
1647
|
+
hist: /mnt/nas/cache/pics/ # can be absolute path
|
1648
|
+
```
|
1649
|
+
|
1472
1650
|
|
1473
1651
|
## metadata from audio files
|
1474
1652
|
|
@@ -1520,6 +1698,18 @@ copyparty can invoke external programs to collect additional metadata for files
|
|
1520
1698
|
|
1521
1699
|
if something doesn't work, try `--mtag-v` for verbose error messages
|
1522
1700
|
|
1701
|
+
config file example; note that `mtp` is an additive option so all of the mtp options will take effect:
|
1702
|
+
|
1703
|
+
```yaml
|
1704
|
+
[/music]
|
1705
|
+
/mnt/nas/music
|
1706
|
+
flags:
|
1707
|
+
mtp: .bpm=~/bin/audio-bpm.py # assign ".bpm" (numeric) with script
|
1708
|
+
mtp: key=f,t5,~/bin/audio-key.py # force/overwrite, 5sec timeout
|
1709
|
+
mtp: ext=an,~/bin/file-ext.py # will only run on non-audio files
|
1710
|
+
mtp: arch,built,ver,orig=an,eexe,edll,~/bin/exe.py # only exe/dll
|
1711
|
+
```
|
1712
|
+
|
1523
1713
|
|
1524
1714
|
## event hooks
|
1525
1715
|
|
@@ -1548,13 +1738,35 @@ the PUSH and REQ examples have `t3` (timeout after 3 seconds) because they block
|
|
1548
1738
|
|
1549
1739
|
see [zmq-recv.py](https://github.com/9001/copyparty/blob/hovudstraum/bin/zmq-recv.py) if you need something to receive the messages with
|
1550
1740
|
|
1741
|
+
config file example; note that the hooks are additive options, so all of the xau options will take effect:
|
1742
|
+
|
1743
|
+
```yaml
|
1744
|
+
[global]
|
1745
|
+
xau: zmq:pub:tcp://*:5556` # send a PUB to any/all connected SUB clients
|
1746
|
+
xau: t3,zmq:push:tcp://*:5557` # send PUSH to exactly one connected PULL cli
|
1747
|
+
xau: t3,j,zmq:req:tcp://localhost:5555` # send REQ to the connected REP cli
|
1748
|
+
```
|
1749
|
+
|
1551
1750
|
|
1552
1751
|
### upload events
|
1553
1752
|
|
1554
1753
|
the older, more powerful approach ([examples](./bin/mtag/)):
|
1555
1754
|
|
1556
1755
|
```
|
1557
|
-
-v /mnt/inc:inc:w:c,mte=+x1:c,mtp=x1=ad,kn,/usr/bin/notify-send
|
1756
|
+
-v /mnt/inc:inc:w:c,e2d,e2t,mte=+x1:c,mtp=x1=ad,kn,/usr/bin/notify-send
|
1757
|
+
```
|
1758
|
+
|
1759
|
+
that was the commandline example; here's the config file example:
|
1760
|
+
|
1761
|
+
```yaml
|
1762
|
+
[/inc]
|
1763
|
+
/mnt/inc
|
1764
|
+
accs:
|
1765
|
+
w: *
|
1766
|
+
flags:
|
1767
|
+
e2d, e2t # enable indexing of uploaded files and their tags
|
1768
|
+
mte: +x1
|
1769
|
+
mtp: x1=ad,kn,/usr/bin/notify-send
|
1558
1770
|
```
|
1559
1771
|
|
1560
1772
|
so filesystem location `/mnt/inc` shared at `/inc`, write-only for everyone, appending `x1` to the list of tags to index (`mte`), and using `/usr/bin/notify-send` to "provide" tag `x1` for any filetype (`ad`) with kill-on-timeout disabled (`kn`)
|
@@ -1568,6 +1780,8 @@ note that this is way more complicated than the new [event hooks](#event-hooks)
|
|
1568
1780
|
|
1569
1781
|
note that it will occupy the parsing threads, so fork anything expensive (or set `kn` to have copyparty fork it for you) -- otoh if you want to intentionally queue/singlethread you can combine it with `--mtag-mt 1`
|
1570
1782
|
|
1783
|
+
for reference, if you were to do this using event hooks instead, it would be like this: `-e2d --xau notify-send,hello,--`
|
1784
|
+
|
1571
1785
|
|
1572
1786
|
## handlers
|
1573
1787
|
|
@@ -1575,6 +1789,8 @@ redefine behavior with plugins ([examples](./bin/handlers/))
|
|
1575
1789
|
|
1576
1790
|
replace 404 and 403 errors with something completely different (that's it for now)
|
1577
1791
|
|
1792
|
+
as for client-side stuff, there is [plugins for modifying UI/UX](./contrib/plugins/)
|
1793
|
+
|
1578
1794
|
|
1579
1795
|
## ip auth
|
1580
1796
|
|
@@ -1636,6 +1852,8 @@ connecting to an aws s3 bucket and similar
|
|
1636
1852
|
|
1637
1853
|
there is no built-in support for this, but you can use FUSE-software such as [rclone](https://rclone.org/) / [geesefs](https://github.com/yandex-cloud/geesefs) / [JuiceFS](https://juicefs.com/en/) to first mount your cloud storage as a local disk, and then let copyparty use (a folder in) that disk as a volume
|
1638
1854
|
|
1855
|
+
if copyparty is unable to access the local folder that rclone/geesefs/JuiceFS provides (for example if it looks invisible) then you may need to run rclone with `--allow-other` and/or enable `user_allow_other` in `/etc/fuse.conf`
|
1856
|
+
|
1639
1857
|
you will probably get decent speeds with the default config, however most likely restricted to using one TCP connection per file, so the upload-client won't be able to send multiple chunks in parallel
|
1640
1858
|
|
1641
1859
|
> before [v1.13.5](https://github.com/9001/copyparty/releases/tag/v1.13.5) it was recommended to use the volflag `sparse` to force-allow multiple chunks in parallel; this would improve the upload-speed from `1.5 MiB/s` to over `80 MiB/s` at the risk of provoking latent bugs in S3 or JuiceFS. But v1.13.5 added chunk-stitching, so this is now probably much less important. On the contrary, `nosparse` *may* now increase performance in some cases. Please try all three options (default, `sparse`, `nosparse`) as the optimal choice depends on your network conditions and software stack (both the FUSE-driver and cloud-server)
|
@@ -1896,7 +2114,7 @@ change the association of a file extension
|
|
1896
2114
|
|
1897
2115
|
using commandline args, you can do something like `--mime gif=image/jif` and `--mime ts=text/x.typescript` (can be specified multiple times)
|
1898
2116
|
|
1899
|
-
in a config
|
2117
|
+
in a config file, this is the same as:
|
1900
2118
|
|
1901
2119
|
```yaml
|
1902
2120
|
[global]
|
@@ -2156,6 +2374,8 @@ NOTE: curl will not send the original filename if you use `-T` combined with url
|
|
2156
2374
|
|
2157
2375
|
sync folders to/from copyparty
|
2158
2376
|
|
2377
|
+
NOTE: full bidirectional sync, like what [nextcloud](https://docs.nextcloud.com/server/latest/user_manual/sv/files/desktop_mobile_sync.html) and [syncthing](https://syncthing.net/) does, will never be supported! Only single-direction sync (server-to-client, or client-to-server) is possible with copyparty
|
2378
|
+
|
2159
2379
|
the commandline uploader [u2c.py](https://github.com/9001/copyparty/tree/hovudstraum/bin#u2cpy) with `--dr` is the best way to sync a folder to copyparty; verifies checksums and does files in parallel, and deletes unexpected files on the server after upload has finished which makes file-renames really cheap (it'll rename serverside and skip uploading)
|
2160
2380
|
|
2161
2381
|
alternatively there is [rclone](./docs/rclone.md) which allows for bidirectional sync and is *way* more flexible (stream files straight from sftp/s3/gcs to copyparty, ...), although there is no integrity check and it won't work with files over 100 MiB if copyparty is behind cloudflare
|
@@ -1,37 +1,37 @@
|
|
1
1
|
copyparty/__init__.py,sha256=VR6ZZhB9IxaK5TDXDTBM_OIP5ydkrdbaEnstktLM__s,2649
|
2
|
-
copyparty/__main__.py,sha256=
|
3
|
-
copyparty/__version__.py,sha256=
|
4
|
-
copyparty/authsrv.py,sha256
|
2
|
+
copyparty/__main__.py,sha256=Tcvi7uIGA37yGVXJbbhn-Cr0OBtt81aRF2Or0ZHXz6U,116449
|
3
|
+
copyparty/__version__.py,sha256=Kh8nmhFwgLz9KpLvVPB-drOjIKb4uSXTYfW7c0xGr3E,252
|
4
|
+
copyparty/authsrv.py,sha256=-oy1PtcMONLcrMllSY2aPuyHWQmE0Ot_EQTX7UJ5tuc,107035
|
5
5
|
copyparty/broker_mp.py,sha256=QdOXXvV2Xn6J0CysEqyY3GZbqxQMyWnTpnba-a5lMc0,4987
|
6
6
|
copyparty/broker_mpw.py,sha256=PpSS4SK3pItlpfD8OwVr3QmJEPKlUgaf2nuMOozixgU,3347
|
7
7
|
copyparty/broker_thr.py,sha256=fjoYtpSscUA7-nMl4r1n2R7UK3J9lrvLS3rUZ-iJzKQ,1721
|
8
8
|
copyparty/broker_util.py,sha256=76mfnFOpX1gUUvtjm8UQI7jpTIaVINX10QonM-B7ggc,1680
|
9
9
|
copyparty/cert.py,sha256=0ZAPeXeMR164vWn9GQU3JDKooYXEq_NOQkDeg543ivg,8009
|
10
|
-
copyparty/cfg.py,sha256
|
10
|
+
copyparty/cfg.py,sha256=-Cbva1shfXQVGFl8Xbo-bkUYjkpU6amPZpTyPJcE7M0,12926
|
11
11
|
copyparty/dxml.py,sha256=vu5uZQtwvwoqnFHbULs2Zh_y2DETu0T-ENpMZ1i2CV4,2505
|
12
12
|
copyparty/fsutil.py,sha256=IVOFG8zBQPMQDDv7RIStSJHwHiAnVNROZS37O5k465A,4524
|
13
13
|
copyparty/ftpd.py,sha256=T97SFS7JFtvRLbJX8C4fJSYwe13vhN3-E6emtlVmqLA,17608
|
14
|
-
copyparty/httpcli.py,sha256=
|
14
|
+
copyparty/httpcli.py,sha256=Dr776ra7_wTCghymLOymy_Bpv8A15Qmgt9ERe-8kZ5A,219131
|
15
15
|
copyparty/httpconn.py,sha256=mQSgljh0Q-jyWjF4tQLrHbRKRe9WKl19kGqsGMsJpWo,6880
|
16
16
|
copyparty/httpsrv.py,sha256=pxH_Eh8ElBLvOEDejgpP9Bvk65HNEou-03aYIcgXhrs,18090
|
17
17
|
copyparty/ico.py,sha256=eWSxEae4wOCfheHl-m-wchYvFRAR_97kJDb4NGaB-Z8,3561
|
18
18
|
copyparty/mdns.py,sha256=G73OWWg1copda47LgayCRK7qjVrk6cnUGpMR5ugmi7o,18315
|
19
|
-
copyparty/metrics.py,sha256=
|
19
|
+
copyparty/metrics.py,sha256=1dim0ShnsD5cfspRbeN9Mt14wOIxPRtxCZY2IScikKw,8974
|
20
20
|
copyparty/mtag.py,sha256=wWWc3BHMRP0u85KvdZpX3Dp4djDgAbXVrL9Pvab4nPQ,19925
|
21
|
-
copyparty/multicast.py,sha256=
|
21
|
+
copyparty/multicast.py,sha256=Me4XEEJijvvK2lMRwmGU2hsaI5_E9AEpCjIC4b9UefA,12393
|
22
22
|
copyparty/pwhash.py,sha256=X87RWeay8IhzGVZLDKE5LctF9YVUcYxPAJ1BX6r_9CU,4248
|
23
23
|
copyparty/smbd.py,sha256=dixFl2wlWymq_Cycc8a4cVB4gY8RSg2e3tE7Xr-aDa0,14614
|
24
24
|
copyparty/ssdp.py,sha256=R1Z61GZOxBMF2Sk4RTxKWMOemogmcjEWG-CvLihd45k,7023
|
25
25
|
copyparty/star.py,sha256=tV5BbX6AiQ7N4UU8DYtSTckNYeoeey4DBqq4LjfymbY,3818
|
26
26
|
copyparty/sutil.py,sha256=6zEEGl4hRe6bTB83Y_RtnBGxr2JcUa__GdiAMqNJZnY,3208
|
27
|
-
copyparty/svchub.py,sha256=
|
27
|
+
copyparty/svchub.py,sha256=gBp7x1hGF4b6_nanW5QUQcz8UmMCad6DzdE2KD5cLvE,41462
|
28
28
|
copyparty/szip.py,sha256=HFtnwOiBgx0HMLUf-h_T84zSlRijPxmhRo5PM613kRA,8602
|
29
29
|
copyparty/tcpsrv.py,sha256=2q18dGR8jnezA4SMfUXa-wrGRGX3nHIwkxkWvkTzF2A,19889
|
30
30
|
copyparty/tftpd.py,sha256=PXgG4rTmiaU_TavSyZWD5cFphdfChs9YvNY21qfExt8,13611
|
31
31
|
copyparty/th_cli.py,sha256=PxDAmUvO_8Vm5edXiWtsCft0Fw69QL9rCHf9zLmUNeA,4800
|
32
32
|
copyparty/th_srv.py,sha256=tHbh_Ve3v8tYclWH2thLs5oFufeXgJi1duUMveKIx9k,30725
|
33
33
|
copyparty/u2idx.py,sha256=G6MDbD4I_sJSOwaNFZ6XLTQhnEDrB12pVKuKhzQ_leE,13676
|
34
|
-
copyparty/up2k.py,sha256=
|
34
|
+
copyparty/up2k.py,sha256=yli2ALT61o1sPta4ckJu4v7hwUrV7IAJSsJo1-OEZWY,175423
|
35
35
|
copyparty/util.py,sha256=Y_znSn3hBNYaaduwcCB7mmBYsi6vv9CYC1zJ9rq9yeQ,99435
|
36
36
|
copyparty/bos/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
37
37
|
copyparty/bos/bos.py,sha256=Wb7eWsXJgR5AFlBR9ZOyKrLTwy-Kct9RrGiOu4Jo37Y,1622
|
@@ -54,10 +54,10 @@ copyparty/stolen/ifaddr/__init__.py,sha256=vpREjAyPubr5s1NJi91icXV3q1o4DrKAvHABw
|
|
54
54
|
copyparty/stolen/ifaddr/_posix.py,sha256=-67NdfGrCktfQPakT2fLbjl2U00QMvyBGkSvrUuTOrU,2626
|
55
55
|
copyparty/stolen/ifaddr/_shared.py,sha256=uNC4SdEIgdSLKvuUzsf1aM-H1Xrc_9mpLoOT43YukGs,6206
|
56
56
|
copyparty/stolen/ifaddr/_win32.py,sha256=EE-QyoBgeB7lYQ6z62VjXNaRozaYfCkaJBHGNA8QtZM,4026
|
57
|
-
copyparty/web/baguettebox.js.gz,sha256=
|
58
|
-
copyparty/web/browser.css.gz,sha256=
|
59
|
-
copyparty/web/browser.html,sha256=
|
60
|
-
copyparty/web/browser.js.gz,sha256=
|
57
|
+
copyparty/web/baguettebox.js.gz,sha256=r2c_hOZV_RTyl4CqWWX14FDWP8nnDVwGkDl4Sfk0rU4,8239
|
58
|
+
copyparty/web/browser.css.gz,sha256=A44DddZBf-PEEMOj-u5YF_JrSk5DZYf-8f_J5jqC2is,11651
|
59
|
+
copyparty/web/browser.html,sha256=auvhLVE_t0aIN0q-nk0zOWFqITgDhroMAAviBNLoFfc,4788
|
60
|
+
copyparty/web/browser.js.gz,sha256=Ec5tzZutoI8daxzHdfRfDP7L9ovDTY10j2g-hkeRlmA,91598
|
61
61
|
copyparty/web/browser2.html,sha256=NRUZ08GH-e2YcGXcoz0UjYg6JIVF42u4IMX4HHwWTmg,1587
|
62
62
|
copyparty/web/cf.html,sha256=lJThtNFNAQT1ClCHHlivAkDGE0LutedwopXD62Z8Nys,589
|
63
63
|
copyparty/web/dbg-audio.js.gz,sha256=Ma-KZtK8LnmiwNvNKFKXMPYl_Nn_3U7GsJ6-DRWC2HE,688
|
@@ -84,7 +84,7 @@ copyparty/web/svcs.html,sha256=dnE1fG15zOpq7u0GYt8ij6BUv_LTwsiipFeneVYlMsM,14140
|
|
84
84
|
copyparty/web/svcs.js.gz,sha256=lMXEP9W-VlXyANlva4q0ASSxvvHYlE2CrmxGgZXZop0,713
|
85
85
|
copyparty/web/ui.css.gz,sha256=0sHIwGsL3_xH8Uu6N0Ag3bKBTjf-e_yfFbKynEZXAnk,2800
|
86
86
|
copyparty/web/up2k.js.gz,sha256=N4idIqOefXurVh4ZhN6lv9nFPX_azLwE41vhXT2qBkI,23833
|
87
|
-
copyparty/web/util.js.gz,sha256=
|
87
|
+
copyparty/web/util.js.gz,sha256=wD3tP5j1iE5Uj5AvLW5zZbQJXDIFDlqgBTGdXeRVqo0,15110
|
88
88
|
copyparty/web/w.hash.js.gz,sha256=l3GpSJD6mcU-1CRWkIj7PybgbjlfSr8oeO3vortIrQk,1105
|
89
89
|
copyparty/web/a/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
90
90
|
copyparty/web/a/partyfuse.py,sha256=9p5Hpg_IBiSimv7j9kmPhCGpy-FLXSRUOYnLjJ5JifU,28049
|
@@ -100,7 +100,7 @@ copyparty/web/deps/busy.mp3.gz,sha256=EVphk1_HYyRKJmtpeK99vbAstF7ub1f9ndu020H8PQ
|
|
100
100
|
copyparty/web/deps/easymde.css.gz,sha256=vWxfueI64rPikuqFj69wJBtGisqf93AheQtOZqgUI_c,3041
|
101
101
|
copyparty/web/deps/easymde.js.gz,sha256=rHBs4XWQe2bmv7ZzDIk43oxnTwrwpq5laYHhV5sKQQo,77014
|
102
102
|
copyparty/web/deps/fuse.py,sha256=6j4Zy3VpQg629pwwIW77v2LJ1hy-qlyrxwhXfKl9B7I,33426
|
103
|
-
copyparty/web/deps/marked.js.gz,sha256=
|
103
|
+
copyparty/web/deps/marked.js.gz,sha256=M6FwmQujGDX33fpo32JZRbxqtSdPzmh7WcaHK6SpZlc,22650
|
104
104
|
copyparty/web/deps/mini-fa.css.gz,sha256=CTPrNaH8OTVmxajrGP88E2MkjadY9_81TBVnd9sw9Y8,572
|
105
105
|
copyparty/web/deps/mini-fa.woff,sha256=L9DNncV2TIyvsrspMbJouvnnt7F068Hbn7YZYvN76AU,2784
|
106
106
|
copyparty/web/deps/prism.css.gz,sha256=Z_A6rJ3MN5KWnjvXaV787aTW_5DT-xjFd0YZ7_W-Krk,1468
|
@@ -109,9 +109,9 @@ copyparty/web/deps/prismd.css.gz,sha256=ObUlksQVr-OuYlTz-I4B23TeBg2QDVVGRnWBz8cV
|
|
109
109
|
copyparty/web/deps/scp.woff2,sha256=w99BDU5i8MukkMEL-iW0YO9H4vFFZSPWxbkH70ytaAg,8612
|
110
110
|
copyparty/web/deps/sha512.ac.js.gz,sha256=lFZaCLumgWxrvEuDr4bqdKHsqjX82AbVAb7_F45Yk88,7033
|
111
111
|
copyparty/web/deps/sha512.hw.js.gz,sha256=UAed2_ocklZCnIzcSYz2h4P1ycztlCLj-ewsRTud2lU,7939
|
112
|
-
copyparty-1.16.
|
113
|
-
copyparty-1.16.
|
114
|
-
copyparty-1.16.
|
115
|
-
copyparty-1.16.
|
116
|
-
copyparty-1.16.
|
117
|
-
copyparty-1.16.
|
112
|
+
copyparty-1.16.13.dist-info/LICENSE,sha256=gOr4h33pCsBEg9uIy9AYmb7qlocL4V9t2uPJS5wllr0,1072
|
113
|
+
copyparty-1.16.13.dist-info/METADATA,sha256=hm4IR2Xqc3bnZLZuEE3MPuNqwi73-IXzd_WqnVo_2NI,154166
|
114
|
+
copyparty-1.16.13.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
|
115
|
+
copyparty-1.16.13.dist-info/entry_points.txt,sha256=4zw6a3rqASywQomiYLObjjlxybaI65LYYOTJwgKz7b0,128
|
116
|
+
copyparty-1.16.13.dist-info/top_level.txt,sha256=LnYUPsDyk-8kFgM6YJLG4h820DQekn81cObKSu9g-sI,10
|
117
|
+
copyparty-1.16.13.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|