copyparty 1.12.2__py3-none-any.whl → 1.13.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
copyparty/__main__.py CHANGED
@@ -850,10 +850,11 @@ def add_qr(ap, tty):
850
850
 
851
851
  def add_fs(ap):
852
852
  ap2 = ap.add_argument_group("filesystem options")
853
- rm_re_def = "5/0.1" if ANYWIN else "0/0"
853
+ rm_re_def = "15/0.1" if ANYWIN else "0/0"
854
854
  ap2.add_argument("--rm-retry", metavar="T/R", type=u, default=rm_re_def, help="if a file cannot be deleted because it is busy, continue trying for \033[33mT\033[0m seconds, retry every \033[33mR\033[0m seconds; disable with 0/0 (volflag=rm_retry)")
855
855
  ap2.add_argument("--mv-retry", metavar="T/R", type=u, default=rm_re_def, help="if a file cannot be renamed because it is busy, continue trying for \033[33mT\033[0m seconds, retry every \033[33mR\033[0m seconds; disable with 0/0 (volflag=mv_retry)")
856
856
  ap2.add_argument("--iobuf", metavar="BYTES", type=int, default=256*1024, help="file I/O buffer-size; if your volumes are on a network drive, try increasing to \033[32m524288\033[0m or even \033[32m4194304\033[0m (and let me know if that improves your performance)")
857
+ ap2.add_argument("--mtab-age", metavar="SEC", type=int, default=60, help="rebuild mountpoint cache every \033[33mSEC\033[0m to keep track of sparse-files support; keep low on servers with removable media")
857
858
 
858
859
 
859
860
  def add_upload(ap):
@@ -1085,6 +1086,8 @@ def add_optouts(ap):
1085
1086
  ap2.add_argument("--no-zip", action="store_true", help="disable download as zip/tar")
1086
1087
  ap2.add_argument("--no-tarcmp", action="store_true", help="disable download as compressed tar (?tar=gz, ?tar=bz2, ?tar=xz, ?tar=gz:9, ...)")
1087
1088
  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")
1089
+ ap2.add_argument("--no-pipe", action="store_true", help="disable race-the-beam (lockstep download of files which are currently being uploaded) (volflag=nopipe)")
1090
+ ap2.add_argument("--no-db-ip", action="store_true", help="do not write uploader IPs into the database")
1088
1091
 
1089
1092
 
1090
1093
  def add_safety(ap):
@@ -1210,7 +1213,7 @@ def add_db_general(ap, hcores):
1210
1213
  ap2.add_argument("--no-hash", metavar="PTN", type=u, help="regex: disable hashing of matching absolute-filesystem-paths during e2ds folder scans (volflag=nohash)")
1211
1214
  ap2.add_argument("--no-idx", metavar="PTN", type=u, default=noidx, help="regex: disable indexing of matching absolute-filesystem-paths during e2ds folder scans (volflag=noidx)")
1212
1215
  ap2.add_argument("--no-dhash", action="store_true", help="disable rescan acceleration; do full database integrity check -- makes the db ~5%% smaller and bootup/rescans 3~10x slower")
1213
- ap2.add_argument("--re-dhash", action="store_true", help="rebuild the cache if it gets out of sync (for example crash on startup during metadata scanning)")
1216
+ ap2.add_argument("--re-dhash", action="store_true", help="force a cache rebuild on startup; enable this once if it gets out of sync (should never be necessary)")
1214
1217
  ap2.add_argument("--no-forget", action="store_true", help="never forget indexed files, even when deleted from disk -- makes it impossible to ever upload the same file twice -- only useful for offloading uploads to a cloud service or something (volflag=noforget)")
1215
1218
  ap2.add_argument("--dbd", metavar="PROFILE", default="wal", help="database durability profile; sets the tradeoff between robustness and speed, see \033[33m--help-dbd\033[0m (volflag=dbd)")
1216
1219
  ap2.add_argument("--xlink", action="store_true", help="on upload: check all volumes for dupes, not just the target volume (volflag=xlink)")
@@ -1248,19 +1251,38 @@ def add_txt(ap):
1248
1251
  ap2.add_argument("--exp-lg", metavar="V,V,V", type=u, default=DEF_EXP, help="comma/space-separated list of placeholders to expand in prologue/epilogue files (volflag=exp_lg)")
1249
1252
 
1250
1253
 
1254
+ def add_og(ap):
1255
+ ap2 = ap.add_argument_group('og / open graph / discord-embed options')
1256
+ ap2.add_argument("--og", action="store_true", help="disable hotlinking and return an html document instead; this is required by open-graph, but can also be useful on its own (volflag=og)")
1257
+ ap2.add_argument("--og-ua", metavar="RE", type=u, default="", help="only disable hotlinking / engage OG behavior if the useragent matches regex \033[33mRE\033[0m (volflag=og_ua)")
1258
+ ap2.add_argument("--og-tpl", metavar="PATH", type=u, default="", help="do not return the regular copyparty html, but instead load the jinja2 template at \033[33mPATH\033[0m (if path contains 'EXT' then EXT will be replaced with the requested file's extension) (volflag=og_tpl)")
1259
+ ap2.add_argument("--og-no-head", action="store_true", help="do not automatically add OG entries into <head> (useful if you're doing this yourself in a template or such) (volflag=og_no_head)")
1260
+ ap2.add_argument("--og-th", metavar="FMT", type=u, default="jf3", help="thumbnail format; j=jpeg, jf=jpeg-uncropped, jf3=jpeg-uncropped-large, w=webm, ... (volflag=og_th)")
1261
+ ap2.add_argument("--og-title", metavar="TXT", type=u, default="", help="fallback title if there is nothing in the \033[33m-e2t\033[0m database (volflag=og_title)")
1262
+ ap2.add_argument("--og-title-a", metavar="T", type=u, default="🎵 {{ artist }} - {{ title }}", help="audio title format; takes any metadata key (volflag=og_title_a)")
1263
+ ap2.add_argument("--og-title-v", metavar="T", type=u, default="{{ title }}", help="video title format; takes any metadata key (volflag=og_title_v)")
1264
+ ap2.add_argument("--og-title-i", metavar="T", type=u, default="{{ title }}", help="image title format; takes any metadata key (volflag=og_title_i)")
1265
+ ap2.add_argument("--og-s-title", action="store_true", help="force default title; do not read from tags (volflag=og_s_title)")
1266
+ ap2.add_argument("--og-desc", metavar="TXT", type=u, default="", help="description text; same for all files, disable with [\033[32m-\033[0m] (volflag=og_desc)")
1267
+ ap2.add_argument("--og-site", metavar="TXT", type=u, default="", help="sitename; defaults to \033[33m--name\033[0m, disable with [\033[32m-\033[0m] (volflag=og_site)")
1268
+ ap2.add_argument("--tcolor", metavar="RGB", type=u, default="333", help="accent color (3 or 6 hex digits); may also affect safari and/or android-chrome (volflag=tcolor)")
1269
+ ap2.add_argument("--uqe", action="store_true", help="query-string parceling; translate a request for \033[33m/foo/.uqe/BASE64\033[0m into \033[33m/foo?TEXT\033[0m, or \033[33m/foo/?TEXT\033[0m if the first character in \033[33mTEXT\033[0m is a slash. Automatically enabled for \033[33m--og\033[0m")
1270
+
1271
+
1251
1272
  def add_ui(ap, retry):
1252
1273
  ap2 = ap.add_argument_group('ui options')
1253
1274
  ap2.add_argument("--grid", action="store_true", help="show grid/thumbnails by default (volflag=grid)")
1254
1275
  ap2.add_argument("--lang", metavar="LANG", type=u, default="eng", help="language; one of the following: \033[32meng nor\033[0m")
1255
1276
  ap2.add_argument("--theme", metavar="NUM", type=int, default=0, help="default theme to use (0..7)")
1256
1277
  ap2.add_argument("--themes", metavar="NUM", type=int, default=8, help="number of themes installed")
1278
+ ap2.add_argument("--au-vol", metavar="0-100", type=int, default=50, choices=range(0, 101), help="default audio/video volume percent")
1257
1279
  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)")
1258
1280
  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)")
1259
1281
  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")
1260
1282
  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])")
1261
1283
  ap2.add_argument("--js-browser", metavar="L", type=u, help="URL to additional JS to include")
1262
1284
  ap2.add_argument("--css-browser", metavar="L", type=u, help="URL to additional CSS to include")
1263
- ap2.add_argument("--html-head", metavar="TXT", type=u, default="", help="text to append to the <head> of all HTML pages")
1285
+ ap2.add_argument("--html-head", metavar="TXT", type=u, default="", help="text to append to the <head> of all HTML pages; can be @PATH to send the contents of a file at PATH, and/or begin with %% to render as jinja2 template (volflag=html_head)")
1264
1286
  ap2.add_argument("--ih", action="store_true", help="if a folder contains index.html, show that instead of the directory listing by default (can be changed in the client settings UI, or add ?v to URL for override)")
1265
1287
  ap2.add_argument("--textfiles", metavar="CSV", type=u, default="txt,nfo,diz,cue,readme", help="file extensions to present as plaintext")
1266
1288
  ap2.add_argument("--txt-max", metavar="KiB", type=int, default=64, help="max size of embedded textfiles on ?doc= (anything bigger will be lazy-loaded by JS)")
@@ -1348,6 +1370,7 @@ def run_argparse(
1348
1370
  add_hooks(ap)
1349
1371
  add_stats(ap)
1350
1372
  add_txt(ap)
1373
+ add_og(ap)
1351
1374
  add_ui(ap, retry)
1352
1375
  add_admin(ap)
1353
1376
  add_logging(ap)
@@ -1382,12 +1405,16 @@ def run_argparse(
1382
1405
  return ret
1383
1406
 
1384
1407
 
1385
- def main(argv = None) :
1408
+ def main(argv = None, rsrc = None) :
1386
1409
  time.strptime("19970815", "%Y%m%d") # python#7980
1387
1410
  if WINDOWS:
1388
1411
  os.system("rem") # enables colors
1389
1412
 
1390
1413
  init_E(E)
1414
+
1415
+ if rsrc: # pyz
1416
+ E.mod = rsrc
1417
+
1391
1418
  if argv is None:
1392
1419
  argv = sys.argv
1393
1420
 
copyparty/__version__.py CHANGED
@@ -1,8 +1,8 @@
1
1
  # coding: utf-8
2
2
 
3
- VERSION = (1, 12, 2)
4
- CODENAME = "locksmith"
5
- BUILD_DT = (2024, 4, 12)
3
+ VERSION = (1, 13, 1)
4
+ CODENAME = "race the beam"
5
+ BUILD_DT = (2024, 5, 6)
6
6
 
7
7
  S_VERSION = ".".join(map(str, VERSION))
8
8
  S_BUILD_DT = "{0:04d}-{1:02d}-{2:02d}".format(*BUILD_DT)
copyparty/authsrv.py CHANGED
@@ -1435,6 +1435,7 @@ class AuthSrv(object):
1435
1435
  elif "" not in mount:
1436
1436
  # there's volumes but no root; make root inaccessible
1437
1437
  vfs = VFS(self.log_func, "", "", AXS(), {})
1438
+ vfs.flags["tcolor"] = self.args.tcolor
1438
1439
  vfs.flags["d2d"] = True
1439
1440
 
1440
1441
  maxdepth = 0
@@ -1720,7 +1721,11 @@ class AuthSrv(object):
1720
1721
  if self.args.e2d or "e2ds" in vol.flags:
1721
1722
  vol.flags["e2d"] = True
1722
1723
 
1723
- for ga, vf in [["no_hash", "nohash"], ["no_idx", "noidx"]]:
1724
+ for ga, vf in [
1725
+ ["no_hash", "nohash"],
1726
+ ["no_idx", "noidx"],
1727
+ ["og_ua", "og_ua"],
1728
+ ]:
1724
1729
  if vf in vol.flags:
1725
1730
  ptn = re.compile(vol.flags.pop(vf))
1726
1731
  else:
@@ -1766,6 +1771,13 @@ class AuthSrv(object):
1766
1771
  t = 'volume "/%s" has invalid %stry [%s]'
1767
1772
  raise Exception(t % (vol.vpath, k, vol.flags.get(k + "try")))
1768
1773
 
1774
+ if vol.flags.get("og"):
1775
+ self.args.uqe = True
1776
+
1777
+ zs = str(vol.flags.get("tcolor", "")).lstrip("#")
1778
+ if len(zs) == 3: # fc5 => ffcc55
1779
+ vol.flags["tcolor"] = "".join([x * 2 for x in zs])
1780
+
1769
1781
  for k1, k2 in IMPLICATIONS:
1770
1782
  if k1 in vol.flags:
1771
1783
  vol.flags[k2] = True
@@ -2403,7 +2415,7 @@ def expand_config_file(
2403
2415
  if not cnames:
2404
2416
  t = "warning: tried to read config-files from folder '%s' but it does not contain any "
2405
2417
  if names:
2406
- t += ".conf files; the following files were ignored: %s"
2418
+ t += ".conf files; the following files/subfolders were ignored: %s"
2407
2419
  t = t % (fp, ", ".join(names[:8]))
2408
2420
  else:
2409
2421
  t += "files at all"
copyparty/cfg.py CHANGED
@@ -16,6 +16,7 @@ def vf_bmap() :
16
16
  "no_dedup": "copydupes",
17
17
  "no_dupe": "nodupe",
18
18
  "no_forget": "noforget",
19
+ "no_pipe": "nopipe",
19
20
  "no_robots": "norobots",
20
21
  "no_thumb": "dthumb",
21
22
  "no_vthumb": "dvthumb",
@@ -38,6 +39,9 @@ def vf_bmap() :
38
39
  "magic",
39
40
  "no_sb_md",
40
41
  "no_sb_lg",
42
+ "og",
43
+ "og_no_head",
44
+ "og_s_title",
41
45
  "rand",
42
46
  "xdev",
43
47
  "xlink",
@@ -60,12 +64,23 @@ def vf_vmap() :
60
64
  }
61
65
  for k in (
62
66
  "dbd",
67
+ "html_head",
63
68
  "lg_sbf",
64
69
  "md_sbf",
65
70
  "nrand",
71
+ "og_desc",
72
+ "og_site",
73
+ "og_th",
74
+ "og_title",
75
+ "og_title_a",
76
+ "og_title_v",
77
+ "og_title_i",
78
+ "og_tpl",
79
+ "og_ua",
66
80
  "mv_retry",
67
81
  "rm_retry",
68
82
  "sort",
83
+ "tcolor",
69
84
  "unlist",
70
85
  "u2abort",
71
86
  "u2ts",
@@ -80,7 +95,6 @@ def vf_cmap() :
80
95
  for k in (
81
96
  "exp_lg",
82
97
  "exp_md",
83
- "html_head",
84
98
  "mte",
85
99
  "mth",
86
100
  "mtp",
@@ -200,7 +214,7 @@ flagcats = {
200
214
  "grid": "show grid/thumbnails by default",
201
215
  "sort": "default sort order",
202
216
  "unlist": "dont list files matching REGEX",
203
- "html_head=TXT": "includes TXT in the <head>",
217
+ "html_head=TXT": "includes TXT in the <head>, or @PATH for file at PATH",
204
218
  "robots": "allows indexing by search engines (default)",
205
219
  "norobots": "kindly asks search engines to leave",
206
220
  "no_sb_md": "disable js sandbox for markdown files",
copyparty/fsutil.py CHANGED
@@ -1,6 +1,7 @@
1
1
  # coding: utf-8
2
2
  from __future__ import print_function, unicode_literals
3
3
 
4
+ import argparse
4
5
  import os
5
6
  import re
6
7
  import time
@@ -11,20 +12,26 @@ from .bos import bos
11
12
  from .util import chkcmd, min_ex
12
13
 
13
14
  class Fstab(object):
14
- def __init__(self, log ):
15
+ def __init__(self, log , args ):
15
16
  self.log_func = log
16
17
 
18
+ self.warned = False
17
19
  self.trusted = False
18
20
  self.tab = None
21
+ self.oldtab = None
22
+ self.srctab = "a"
19
23
  self.cache = {}
20
24
  self.age = 0.0
25
+ self.maxage = args.mtab_age
21
26
 
22
27
  def log(self, msg , c = 0) :
23
28
  self.log_func("fstab", msg, c)
24
29
 
25
30
  def get(self, path ) :
26
- if len(self.cache) > 9000:
27
- self.age = time.time()
31
+ now = time.time()
32
+ if now - self.age > self.maxage or len(self.cache) > 9000:
33
+ self.age = now
34
+ self.oldtab = self.tab or self.oldtab
28
35
  self.tab = None
29
36
  self.cache = {}
30
37
 
@@ -69,7 +76,7 @@ class Fstab(object):
69
76
  self.trusted = False
70
77
 
71
78
  def build_tab(self) :
72
- self.log("building tab")
79
+ self.log("inspecting mtab for changes")
73
80
 
74
81
  sptn = r"^.*? on (.*) type ([^ ]+) \(.*"
75
82
  if MACOS:
@@ -78,6 +85,7 @@ class Fstab(object):
78
85
  ptn = re.compile(sptn)
79
86
  so, _ = chkcmd(["mount"])
80
87
  tab1 = []
88
+ atab = []
81
89
  for ln in so.split("\n"):
82
90
  m = ptn.match(ln)
83
91
  if not m:
@@ -85,6 +93,15 @@ class Fstab(object):
85
93
 
86
94
  zs1, zs2 = m.groups()
87
95
  tab1.append((str(zs1), str(zs2)))
96
+ atab.append(ln)
97
+
98
+ # keep empirically-correct values if mounttab unchanged
99
+ srctab = "\n".join(sorted(atab))
100
+ if srctab == self.srctab:
101
+ self.tab = self.oldtab
102
+ return
103
+
104
+ self.log("mtab has changed; reevaluating support for sparse files")
88
105
 
89
106
  tab1.sort(key=lambda x: (len(x[0]), x[0]))
90
107
  path1, fs1 = tab1[0]
@@ -93,6 +110,7 @@ class Fstab(object):
93
110
  tab.add(fs, path.lstrip("/"))
94
111
 
95
112
  self.tab = tab
113
+ self.srctab = srctab
96
114
 
97
115
  def relabel(self, path , nval ) :
98
116
  assert self.tab
@@ -127,7 +145,9 @@ class Fstab(object):
127
145
  self.trusted = True
128
146
  except:
129
147
  # prisonparty or other restrictive environment
130
- self.log("failed to build tab:\n{}".format(min_ex()), 3)
148
+ if not self.warned:
149
+ self.warned = True
150
+ self.log("failed to build tab:\n{}".format(min_ex()), 3)
131
151
  self.build_fallback()
132
152
 
133
153
  assert self.tab