copyparty 1.13.5__py3-none-any.whl → 1.13.7__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.
Files changed (46) hide show
  1. copyparty/__main__.py +25 -7
  2. copyparty/__version__.py +2 -2
  3. copyparty/authsrv.py +9 -6
  4. copyparty/cert.py +1 -1
  5. copyparty/fsutil.py +3 -3
  6. copyparty/ftpd.py +15 -2
  7. copyparty/httpcli.py +221 -81
  8. copyparty/httpconn.py +3 -0
  9. copyparty/httpsrv.py +35 -11
  10. copyparty/ico.py +1 -1
  11. copyparty/mtag.py +15 -6
  12. copyparty/pwhash.py +10 -0
  13. copyparty/smbd.py +20 -2
  14. copyparty/ssdp.py +3 -3
  15. copyparty/stolen/dnslib/dns.py +6 -0
  16. copyparty/stolen/ifaddr/__init__.py +15 -1
  17. copyparty/stolen/ifaddr/_shared.py +1 -0
  18. copyparty/stolen/qrcodegen.py +6 -0
  19. copyparty/sutil.py +1 -1
  20. copyparty/svchub.py +72 -3
  21. copyparty/szip.py +1 -3
  22. copyparty/tcpsrv.py +60 -8
  23. copyparty/tftpd.py +30 -4
  24. copyparty/th_srv.py +22 -1
  25. copyparty/u2idx.py +4 -1
  26. copyparty/up2k.py +222 -94
  27. copyparty/util.py +166 -31
  28. copyparty/web/a/u2c.py +22 -9
  29. copyparty/web/baguettebox.js.gz +0 -0
  30. copyparty/web/browser.css.gz +0 -0
  31. copyparty/web/browser.js.gz +0 -0
  32. copyparty/web/browser2.html +0 -1
  33. copyparty/web/md.html +3 -0
  34. copyparty/web/mde.html +3 -0
  35. copyparty/web/msg.html +3 -0
  36. copyparty/web/splash.html +3 -0
  37. copyparty/web/svcs.html +5 -2
  38. copyparty/web/ui.css.gz +0 -0
  39. copyparty/web/up2k.js.gz +0 -0
  40. copyparty/web/util.js.gz +0 -0
  41. {copyparty-1.13.5.dist-info → copyparty-1.13.7.dist-info}/METADATA +66 -14
  42. {copyparty-1.13.5.dist-info → copyparty-1.13.7.dist-info}/RECORD +46 -46
  43. {copyparty-1.13.5.dist-info → copyparty-1.13.7.dist-info}/WHEEL +1 -1
  44. {copyparty-1.13.5.dist-info → copyparty-1.13.7.dist-info}/LICENSE +0 -0
  45. {copyparty-1.13.5.dist-info → copyparty-1.13.7.dist-info}/entry_points.txt +0 -0
  46. {copyparty-1.13.5.dist-info → copyparty-1.13.7.dist-info}/top_level.txt +0 -0
copyparty/mtag.py CHANGED
@@ -26,6 +26,17 @@ from .util import (
26
26
  wunlink,
27
27
  )
28
28
 
29
+ try:
30
+ if os.environ.get("PRTY_NO_MUTAGEN"):
31
+ raise Exception()
32
+
33
+ from mutagen import version # noqa: F401
34
+
35
+ HAVE_MUTAGEN = True
36
+ except:
37
+ HAVE_MUTAGEN = False
38
+
39
+
29
40
  def have_ff(scmd ) :
30
41
  if ANYWIN:
31
42
  scmd += ".exe"
@@ -42,8 +53,8 @@ def have_ff(scmd ) :
42
53
  return bool(shutil.which(scmd))
43
54
 
44
55
 
45
- HAVE_FFMPEG = have_ff("ffmpeg")
46
- HAVE_FFPROBE = have_ff("ffprobe")
56
+ HAVE_FFMPEG = not os.environ.get("PRTY_NO_FFMPEG") and have_ff("ffmpeg")
57
+ HAVE_FFPROBE = not os.environ.get("PRTY_NO_FFPROBE") and have_ff("ffprobe")
47
58
 
48
59
 
49
60
  class MParser(object):
@@ -330,9 +341,7 @@ class MTag(object):
330
341
 
331
342
  if self.backend == "mutagen":
332
343
  self._get = self.get_mutagen
333
- try:
334
- from mutagen import version # noqa: F401
335
- except:
344
+ if not HAVE_MUTAGEN:
336
345
  self.log("could not load Mutagen, trying FFprobe instead", c=3)
337
346
  self.backend = "ffprobe"
338
347
 
@@ -572,7 +581,7 @@ class MTag(object):
572
581
  continue
573
582
 
574
583
  if k == ".aq":
575
- v /= 1000
584
+ v /= 1000 # type: ignore
576
585
 
577
586
  if k == "ac" and v.startswith("mp4a.40."):
578
587
  v = "aac"
copyparty/pwhash.py CHANGED
@@ -4,11 +4,21 @@ from __future__ import print_function, unicode_literals
4
4
  import argparse
5
5
  import base64
6
6
  import hashlib
7
+ import os
7
8
  import sys
8
9
  import threading
9
10
 
10
11
  from .__init__ import unicode
11
12
 
13
+ try:
14
+ if os.environ.get("PRTY_NO_ARGON2"):
15
+ raise Exception()
16
+
17
+ HAVE_ARGON2 = True
18
+ from argon2 import __version__ as argon2ver
19
+ except:
20
+ HAVE_ARGON2 = False
21
+
12
22
 
13
23
  class PWHash(object):
14
24
  def __init__(self, args ):
copyparty/smbd.py CHANGED
@@ -184,6 +184,8 @@ class SMB(object):
184
184
 
185
185
  debug('%s("%s", %s) %s @%s\033[K\033[0m', caller, vpath, str(a), perms, uname)
186
186
  vfs, rem = self.asrv.vfs.get(vpath, uname, *perms)
187
+ if not vfs.realpath:
188
+ raise Exception("unmapped vfs")
187
189
  return vfs, vfs.canonical(rem)
188
190
 
189
191
  def _listdir(self, vpath , *a , **ka ) :
@@ -192,6 +194,8 @@ class SMB(object):
192
194
  uname = self._uname()
193
195
  # debug('listdir("%s", %s) @%s\033[K\033[0m', vpath, str(a), uname)
194
196
  vfs, rem = self.asrv.vfs.get(vpath, uname, False, False)
197
+ if not vfs.realpath:
198
+ raise Exception("unmapped vfs")
195
199
  _, vfs_ls, vfs_virt = vfs.ls(
196
200
  rem, uname, not self.args.no_scandir, [[False, False]]
197
201
  )
@@ -237,7 +241,21 @@ class SMB(object):
237
241
 
238
242
  xbu = vfs.flags.get("xbu")
239
243
  if xbu and not runhook(
240
- self.nlog, xbu, ap, vpath, "", "", "", 0, 0, "1.7.6.2", 0, ""
244
+ self.nlog,
245
+ None,
246
+ self.hub.up2k,
247
+ "xbu.smb",
248
+ xbu,
249
+ ap,
250
+ vpath,
251
+ "",
252
+ "",
253
+ "",
254
+ 0,
255
+ 0,
256
+ "1.7.6.2",
257
+ time.time(),
258
+ "",
241
259
  ):
242
260
  yeet("blocked by xbu server config: " + vpath)
243
261
 
@@ -294,7 +312,7 @@ class SMB(object):
294
312
  t = "blocked rename (no-move-acc %s): /%s @%s"
295
313
  yeet(t % (vfs1.axs.umove, vp1, uname))
296
314
 
297
- self.hub.up2k.handle_mv(uname, vp1, vp2)
315
+ self.hub.up2k.handle_mv(uname, "1.7.6.2", vp1, vp2)
298
316
  try:
299
317
  bos.makedirs(ap2)
300
318
  except:
copyparty/ssdp.py CHANGED
@@ -5,11 +5,11 @@ import errno
5
5
  import re
6
6
  import select
7
7
  import socket
8
- from email.utils import formatdate
8
+ import time
9
9
 
10
10
  from .__init__ import TYPE_CHECKING
11
11
  from .multicast import MC_Sck, MCast
12
- from .util import CachedSet, html_escape, min_ex
12
+ from .util import CachedSet, formatdate, html_escape, min_ex
13
13
 
14
14
  if TYPE_CHECKING:
15
15
  from .broker_util import BrokerCli
@@ -225,7 +225,7 @@ CONFIGID.UPNP.ORG: 1
225
225
 
226
226
  """
227
227
  v4 = srv.ip.replace("::ffff:", "")
228
- zs = zs.format(formatdate(usegmt=True), v4, srv.hport, self.args.zsid)
228
+ zs = zs.format(formatdate(), v4, srv.hport, self.args.zsid)
229
229
  zb = zs[1:].replace("\n", "\r\n").encode("utf-8", "replace")
230
230
  srv.sck.sendto(zb, addr[:2])
231
231
 
@@ -12,6 +12,12 @@ from .label import DNSBuffer, DNSLabel
12
12
  from .ranges import IP4, IP6, H, I, check_bytes
13
13
 
14
14
 
15
+ try:
16
+ range = xrange
17
+ except:
18
+ pass
19
+
20
+
15
21
  class DNSError(Exception):
16
22
  pass
17
23
 
@@ -11,7 +11,21 @@ import os
11
11
 
12
12
  from ._shared import IP, Adapter
13
13
 
14
- if os.name == "nt":
14
+
15
+ def nope(include_unconfigured=False):
16
+ return []
17
+
18
+
19
+ try:
20
+ S390X = os.uname().machine == "s390x"
21
+ except:
22
+ S390X = False
23
+
24
+
25
+ if os.environ.get("PRTY_NO_IFADDR") or S390X:
26
+ # s390x deadlocks at libc.getifaddrs
27
+ get_adapters = nope
28
+ elif os.name == "nt":
15
29
  from ._win32 import get_adapters
16
30
  elif os.name == "posix":
17
31
  from ._posix import get_adapters
@@ -13,6 +13,7 @@ if not PY2:
13
13
  U = str
14
14
  else:
15
15
  U = unicode # noqa: F821 # pylint: disable=undefined-variable,self-assigning-variable
16
+ range = xrange # noqa: F821 # pylint: disable=undefined-variable,self-assigning-variable
16
17
 
17
18
 
18
19
  class Adapter(object):
@@ -11,6 +11,12 @@ from __future__ import print_function, unicode_literals
11
11
  import collections
12
12
  import itertools
13
13
 
14
+ try:
15
+ range = xrange
16
+ except:
17
+ pass
18
+
19
+
14
20
  def num_char_count_bits(ver ) :
15
21
  return 16 if (ver + 7) // 17 else 8
16
22
 
copyparty/sutil.py CHANGED
@@ -6,7 +6,7 @@ import tempfile
6
6
  from datetime import datetime
7
7
 
8
8
  from .__init__ import CORES
9
- from .authsrv import AuthSrv, VFS
9
+ from .authsrv import VFS, AuthSrv
10
10
  from .bos import bos
11
11
  from .th_cli import ThumbCli
12
12
  from .util import UTC, vjoin, vol_san
copyparty/svchub.py CHANGED
@@ -22,18 +22,30 @@ from datetime import datetime, timedelta
22
22
  # print(currentframe().f_lineno)
23
23
 
24
24
 
25
- from .__init__ import ANYWIN, EXE, MACOS, TYPE_CHECKING, E, EnvParams, unicode
25
+ from .__init__ import ANYWIN, EXE, MACOS, PY2, TYPE_CHECKING, E, EnvParams, unicode
26
26
  from .authsrv import BAD_CFG, AuthSrv
27
27
  from .cert import ensure_cert
28
- from .mtag import HAVE_FFMPEG, HAVE_FFPROBE
28
+ from .mtag import HAVE_FFMPEG, HAVE_FFPROBE, HAVE_MUTAGEN
29
+ from .pwhash import HAVE_ARGON2
29
30
  from .tcpsrv import TcpSrv
30
- from .th_srv import HAVE_PIL, HAVE_VIPS, HAVE_WEBP, ThumbSrv
31
+ from .th_srv import (
32
+ HAVE_AVIF,
33
+ HAVE_FFMPEG,
34
+ HAVE_FFPROBE,
35
+ HAVE_HEIF,
36
+ HAVE_PIL,
37
+ HAVE_VIPS,
38
+ HAVE_WEBP,
39
+ ThumbSrv,
40
+ )
31
41
  from .up2k import Up2k
32
42
  from .util import (
33
43
  DEF_EXP,
34
44
  DEF_MTE,
35
45
  DEF_MTH,
36
46
  FFMPEG_URL,
47
+ HAVE_PSUTIL,
48
+ HAVE_SQLITE3,
37
49
  UTC,
38
50
  VERSIONS,
39
51
  Daemon,
@@ -59,6 +71,9 @@ if TYPE_CHECKING:
59
71
  except:
60
72
  pass
61
73
 
74
+ if PY2:
75
+ range = xrange # type: ignore
76
+
62
77
 
63
78
  class SvcHub(object):
64
79
  """
@@ -226,6 +241,8 @@ class SvcHub(object):
226
241
 
227
242
  self.up2k = Up2k(self)
228
243
 
244
+ self._feature_test()
245
+
229
246
  decs = {k: 1 for k in self.args.th_dec.split(",")}
230
247
  if not HAVE_VIPS:
231
248
  decs.pop("vips", None)
@@ -414,6 +431,58 @@ class SvcHub(object):
414
431
 
415
432
  Daemon(self.sd_notify, "sd-notify")
416
433
 
434
+ def _feature_test(self) :
435
+ fok = []
436
+ fng = []
437
+ t_ff = "transcode audio, create spectrograms, video thumbnails"
438
+ to_check = [
439
+ (HAVE_SQLITE3, "sqlite", "file and media indexing"),
440
+ (HAVE_PIL, "pillow", "image thumbnails (plenty fast)"),
441
+ (HAVE_VIPS, "vips", "image thumbnails (faster, eats more ram)"),
442
+ (HAVE_WEBP, "pillow-webp", "create thumbnails as webp files"),
443
+ (HAVE_FFMPEG, "ffmpeg", t_ff + ", good-but-slow image thumbnails"),
444
+ (HAVE_FFPROBE, "ffprobe", t_ff + ", read audio/media tags"),
445
+ (HAVE_MUTAGEN, "mutagen", "read audio tags (ffprobe is better but slower)"),
446
+ (HAVE_ARGON2, "argon2", "secure password hashing (advanced users only)"),
447
+ (HAVE_HEIF, "pillow-heif", "read .heif images with pillow (rarely useful)"),
448
+ (HAVE_AVIF, "pillow-avif", "read .avif images with pillow (rarely useful)"),
449
+ ]
450
+ if ANYWIN:
451
+ to_check += [
452
+ (HAVE_PSUTIL, "psutil", "improved plugin cleanup (rarely useful)")
453
+ ]
454
+
455
+ verbose = self.args.deps
456
+ if verbose:
457
+ self.log("dependencies", "")
458
+
459
+ for have, feat, what in to_check:
460
+ lst = fok if have else fng
461
+ lst.append((feat, what))
462
+ if verbose:
463
+ zi = 2 if have else 5
464
+ sgot = "found" if have else "missing"
465
+ t = "%7s: %s \033[36m(%s)"
466
+ self.log("dependencies", t % (sgot, feat, what), zi)
467
+
468
+ if verbose:
469
+ self.log("dependencies", "")
470
+ return
471
+
472
+ sok = ", ".join(x[0] for x in fok)
473
+ sng = ", ".join(x[0] for x in fng)
474
+
475
+ t = ""
476
+ if sok:
477
+ t += "OK: \033[32m" + sok
478
+ if sng:
479
+ if t:
480
+ t += ", "
481
+ t += "\033[0mNG: \033[35m" + sng
482
+
483
+ t += "\033[0m, see --deps"
484
+ self.log("dependencies", t, 6)
485
+
417
486
  def _check_env(self) :
418
487
  try:
419
488
  files = os.listdir(E.cfg)
copyparty/szip.py CHANGED
@@ -31,9 +31,7 @@ def dostime2unix(buf ) :
31
31
 
32
32
 
33
33
  def unixtime2dos(ts ) :
34
- tt = time.gmtime(ts + 1)
35
- dy, dm, dd, th, tm, ts = list(tt)[:6]
36
-
34
+ dy, dm, dd, th, tm, ts, _, _, _ = time.gmtime(ts + 1)
37
35
  bd = ((dy - 1980) << 9) + (dm << 5) + dd
38
36
  bt = (th << 11) + (tm << 5) + ts // 2
39
37
  try:
copyparty/tcpsrv.py CHANGED
@@ -17,7 +17,9 @@ from .util import (
17
17
  E_UNREACH,
18
18
  HAVE_IPV6,
19
19
  IP6ALL,
20
+ VF_CAREFUL,
20
21
  Netdev,
22
+ atomic_move,
21
23
  min_ex,
22
24
  sunpack,
23
25
  termsize,
@@ -214,14 +216,29 @@ class TcpSrv(object):
214
216
  if self.args.qr or self.args.qrs:
215
217
  self.qr = self._qr(qr1, qr2)
216
218
 
219
+ def nlog(self, msg , c = 0) :
220
+ self.log("tcpsrv", msg, c)
221
+
217
222
  def _listen(self, ip , port ) :
218
- ipv = socket.AF_INET6 if ":" in ip else socket.AF_INET
223
+ if "unix:" in ip:
224
+ tcp = False
225
+ ipv = socket.AF_UNIX
226
+ ip = ip.split("unix:")[1]
227
+ elif ":" in ip:
228
+ tcp = True
229
+ ipv = socket.AF_INET6
230
+ else:
231
+ tcp = True
232
+ ipv = socket.AF_INET
233
+
219
234
  srv = socket.socket(ipv, socket.SOCK_STREAM)
220
235
 
221
236
  if not ANYWIN or self.args.reuseaddr:
222
237
  srv.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
223
238
 
224
- srv.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
239
+ if tcp:
240
+ srv.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
241
+
225
242
  srv.settimeout(None) # < does not inherit, ^ opts above do
226
243
 
227
244
  try:
@@ -233,8 +250,19 @@ class TcpSrv(object):
233
250
  srv.setsockopt(socket.SOL_IP, socket.IP_FREEBIND, 1)
234
251
 
235
252
  try:
236
- srv.bind((ip, port))
237
- sport = srv.getsockname()[1]
253
+ if tcp:
254
+ srv.bind((ip, port))
255
+ else:
256
+ if ANYWIN or self.args.rm_sck:
257
+ if os.path.exists(ip):
258
+ os.unlink(ip)
259
+ srv.bind(ip)
260
+ else:
261
+ tf = "%s.%d" % (ip, os.getpid())
262
+ srv.bind(tf)
263
+ atomic_move(self.nlog, tf, ip, VF_CAREFUL)
264
+
265
+ sport = srv.getsockname()[1] if tcp else port
238
266
  if port != sport:
239
267
  # linux 6.0.16 lets you bind a port which is in use
240
268
  # except it just gives you a random port instead
@@ -246,12 +274,23 @@ class TcpSrv(object):
246
274
  except:
247
275
  pass
248
276
 
277
+ e = ""
249
278
  if ex.errno in E_ADDR_IN_USE:
250
279
  e = "\033[1;31mport {} is busy on interface {}\033[0m".format(port, ip)
280
+ if not tcp:
281
+ e = "\033[1;31munix-socket {} is busy\033[0m".format(ip)
251
282
  elif ex.errno in E_ADDR_NOT_AVAIL:
252
283
  e = "\033[1;31minterface {} does not exist\033[0m".format(ip)
253
- else:
284
+
285
+ if not e:
286
+ if not tcp:
287
+ t = "\n\n\n NOTE: this crash may be due to a unix-socket bug; try --rm-sck\n"
288
+ self.log("tcpsrv", t, 2)
254
289
  raise
290
+
291
+ if not tcp and not self.args.rm_sck:
292
+ e += "; maybe this is a bug? try --rm-sck"
293
+
255
294
  raise Exception(e)
256
295
 
257
296
  def run(self) :
@@ -259,7 +298,14 @@ class TcpSrv(object):
259
298
  bound = []
260
299
  srvs = []
261
300
  for srv in self.srv:
262
- ip, port = srv.getsockname()[:2]
301
+ if srv.family == socket.AF_UNIX:
302
+ tcp = False
303
+ ip = re.sub(r"\.[0-9]+$", "", srv.getsockname())
304
+ port = 0
305
+ else:
306
+ tcp = True
307
+ ip, port = srv.getsockname()[:2]
308
+
263
309
  if ip == IP6ALL:
264
310
  ip = "::" # jython
265
311
 
@@ -291,8 +337,12 @@ class TcpSrv(object):
291
337
  bound.append((ip, port))
292
338
  srvs.append(srv)
293
339
  fno = srv.fileno()
294
- hip = "[{}]".format(ip) if ":" in ip else ip
295
- msg = "listening @ {}:{} f{} p{}".format(hip, port, fno, os.getpid())
340
+ if tcp:
341
+ hip = "[{}]".format(ip) if ":" in ip else ip
342
+ msg = "listening @ {}:{} f{} p{}".format(hip, port, fno, os.getpid())
343
+ else:
344
+ msg = "listening @ {} f{} p{}".format(ip, fno, os.getpid())
345
+
296
346
  self.log("tcpsrv", msg)
297
347
  if self.args.q:
298
348
  print(msg)
@@ -345,6 +395,8 @@ class TcpSrv(object):
345
395
  def detect_interfaces(self, listen_ips ) :
346
396
  from .stolen.ifaddr import get_adapters
347
397
 
398
+ listen_ips = [x for x in listen_ips if "unix:" not in x]
399
+
348
400
  nics = get_adapters(True)
349
401
  eps = {}
350
402
  for nic in nics:
copyparty/tftpd.py CHANGED
@@ -36,11 +36,14 @@ from partftpy.TftpShared import TftpException
36
36
  from .__init__ import EXE, PY2, TYPE_CHECKING
37
37
  from .authsrv import VFS
38
38
  from .bos import bos
39
- from .util import BytesIO, Daemon, ODict, exclude_dotfiles, min_ex, runhook, undot
39
+ from .util import UTC, BytesIO, Daemon, ODict, exclude_dotfiles, min_ex, runhook, undot
40
40
 
41
41
  if TYPE_CHECKING:
42
42
  from .svchub import SvcHub
43
43
 
44
+ if PY2:
45
+ range = xrange # type: ignore
46
+
44
47
 
45
48
  lg = logging.getLogger("tftp")
46
49
  debug, info, warning, error = (lg.debug, lg.info, lg.warning, lg.error)
@@ -160,9 +163,16 @@ class Tftpd(object):
160
163
  if "::" in ips:
161
164
  ips.append("0.0.0.0")
162
165
 
166
+ ips = [x for x in ips if "unix:" not in x]
167
+
163
168
  if self.args.tftp4:
164
169
  ips = [x for x in ips if ":" not in x]
165
170
 
171
+ if not ips:
172
+ t = "cannot start tftp-server; no compatible IPs in -i"
173
+ self.nlog(t, 1)
174
+ return
175
+
166
176
  ips = list(ODict.fromkeys(ips)) # dedup
167
177
 
168
178
  for ip in ips:
@@ -238,6 +248,8 @@ class Tftpd(object):
238
248
 
239
249
  debug('%s("%s", %s) %s\033[K\033[0m', caller, vpath, str(a), perms)
240
250
  vfs, rem = self.asrv.vfs.get(vpath, "*", *perms)
251
+ if not vfs.realpath:
252
+ raise Exception("unmapped vfs")
241
253
  return vfs, vfs.canonical(rem)
242
254
 
243
255
  def _ls(self, vpath , raddress , rport , force=False) :
@@ -259,7 +271,7 @@ class Tftpd(object):
259
271
  dirs1 = [(v.st_mtime, v.st_size, k + "/") for k, v in vfs_ls if k in dnames]
260
272
  fils1 = [(v.st_mtime, v.st_size, k) for k, v in vfs_ls if k not in dnames]
261
273
  real1 = dirs1 + fils1
262
- realt = [(datetime.fromtimestamp(mt), sz, fn) for mt, sz, fn in real1]
274
+ realt = [(datetime.fromtimestamp(mt, UTC), sz, fn) for mt, sz, fn in real1]
263
275
  reals = [
264
276
  (
265
277
  "%04d-%02d-%02d %02d:%02d:%02d"
@@ -325,7 +337,21 @@ class Tftpd(object):
325
337
 
326
338
  xbu = vfs.flags.get("xbu")
327
339
  if xbu and not runhook(
328
- self.nlog, xbu, ap, vpath, "", "", "", 0, 0, "8.3.8.7", 0, ""
340
+ self.nlog,
341
+ None,
342
+ self.hub.up2k,
343
+ "xbu.tftpd",
344
+ xbu,
345
+ ap,
346
+ vpath,
347
+ "",
348
+ "",
349
+ "",
350
+ 0,
351
+ 0,
352
+ "8.3.8.7",
353
+ time.time(),
354
+ "",
329
355
  ):
330
356
  yeet("blocked by xbu server config: " + vpath)
331
357
 
@@ -333,7 +359,7 @@ class Tftpd(object):
333
359
  return self._ls(vpath, "", 0, True)
334
360
 
335
361
  if not a:
336
- a = [self.args.iobuf]
362
+ a = (self.args.iobuf,)
337
363
 
338
364
  return open(ap, mode, *a, **ka)
339
365
 
copyparty/th_srv.py CHANGED
@@ -12,7 +12,7 @@ import time
12
12
 
13
13
  from queue import Queue
14
14
 
15
- from .__init__ import ANYWIN, TYPE_CHECKING
15
+ from .__init__ import ANYWIN, PY2, TYPE_CHECKING
16
16
  from .authsrv import VFS
17
17
  from .bos import bos
18
18
  from .mtag import HAVE_FFMPEG, HAVE_FFPROBE, au_unpk, ffprobe
@@ -35,6 +35,9 @@ from .util import (
35
35
  if TYPE_CHECKING:
36
36
  from .svchub import SvcHub
37
37
 
38
+ if PY2:
39
+ range = xrange # type: ignore
40
+
38
41
  HAVE_PIL = False
39
42
  HAVE_PILF = False
40
43
  HAVE_HEIF = False
@@ -42,22 +45,34 @@ HAVE_AVIF = False
42
45
  HAVE_WEBP = False
43
46
 
44
47
  try:
48
+ if os.environ.get("PRTY_NO_PIL"):
49
+ raise Exception()
50
+
45
51
  from PIL import ExifTags, Image, ImageFont, ImageOps
46
52
 
47
53
  HAVE_PIL = True
48
54
  try:
55
+ if os.environ.get("PRTY_NO_PILF"):
56
+ raise Exception()
57
+
49
58
  ImageFont.load_default(size=16)
50
59
  HAVE_PILF = True
51
60
  except:
52
61
  pass
53
62
 
54
63
  try:
64
+ if os.environ.get("PRTY_NO_PIL_WEBP"):
65
+ raise Exception()
66
+
55
67
  Image.new("RGB", (2, 2)).save(BytesIO(), format="webp")
56
68
  HAVE_WEBP = True
57
69
  except:
58
70
  pass
59
71
 
60
72
  try:
73
+ if os.environ.get("PRTY_NO_PIL_HEIF"):
74
+ raise Exception()
75
+
61
76
  from pyheif_pillow_opener import register_heif_opener
62
77
 
63
78
  register_heif_opener()
@@ -66,6 +81,9 @@ try:
66
81
  pass
67
82
 
68
83
  try:
84
+ if os.environ.get("PRTY_NO_PIL_AVIF"):
85
+ raise Exception()
86
+
69
87
  import pillow_avif # noqa: F401 # pylint: disable=unused-import
70
88
 
71
89
  HAVE_AVIF = True
@@ -77,6 +95,9 @@ except:
77
95
  pass
78
96
 
79
97
  try:
98
+ if os.environ.get("PRTY_NO_VIPS"):
99
+ raise Exception()
100
+
80
101
  HAVE_VIPS = True
81
102
  import pyvips
82
103
 
copyparty/u2idx.py CHANGED
@@ -8,7 +8,7 @@ import threading
8
8
  import time
9
9
  from operator import itemgetter
10
10
 
11
- from .__init__ import ANYWIN, TYPE_CHECKING, unicode
11
+ from .__init__ import ANYWIN, PY2, TYPE_CHECKING, unicode
12
12
  from .authsrv import LEELOO_DALLAS, VFS
13
13
  from .bos import bos
14
14
  from .up2k import up2k_wark_from_hashlist
@@ -35,6 +35,9 @@ except:
35
35
  if TYPE_CHECKING:
36
36
  from .httpsrv import HttpSrv
37
37
 
38
+ if PY2:
39
+ range = xrange # type: ignore
40
+
38
41
 
39
42
  class U2idx(object):
40
43
  def __init__(self, hsrv ) :