copyparty 1.16.18__py3-none-any.whl → 1.16.20__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/__init__.py +1 -1
- copyparty/__main__.py +21 -2
- copyparty/__version__.py +2 -2
- copyparty/authsrv.py +62 -9
- copyparty/cfg.py +3 -0
- copyparty/httpcli.py +44 -24
- copyparty/ico.py +13 -2
- copyparty/pwhash.py +1 -1
- copyparty/svchub.py +164 -61
- copyparty/tcpsrv.py +1 -1
- copyparty/th_cli.py +23 -4
- copyparty/th_srv.py +62 -7
- copyparty/u2idx.py +2 -2
- copyparty/up2k.py +7 -4
- copyparty/util.py +81 -3
- copyparty/web/browser.css.gz +0 -0
- copyparty/web/browser.js.gz +0 -0
- copyparty/web/deps/marked.js.gz +0 -0
- copyparty/web/splash.html +1 -1
- copyparty/web/ui.css.gz +0 -0
- copyparty/web/up2k.js.gz +0 -0
- copyparty/web/util.js.gz +0 -0
- {copyparty-1.16.18.dist-info → copyparty-1.16.20.dist-info}/METADATA +15 -3
- {copyparty-1.16.18.dist-info → copyparty-1.16.20.dist-info}/RECORD +28 -28
- {copyparty-1.16.18.dist-info → copyparty-1.16.20.dist-info}/WHEEL +1 -1
- {copyparty-1.16.18.dist-info → copyparty-1.16.20.dist-info}/entry_points.txt +0 -0
- {copyparty-1.16.18.dist-info → copyparty-1.16.20.dist-info}/licenses/LICENSE +0 -0
- {copyparty-1.16.18.dist-info → copyparty-1.16.20.dist-info}/top_level.txt +0 -0
copyparty/svchub.py
CHANGED
@@ -58,6 +58,7 @@ from .util import (
|
|
58
58
|
expat_ver,
|
59
59
|
gzip,
|
60
60
|
load_ipu,
|
61
|
+
lock_file,
|
61
62
|
min_ex,
|
62
63
|
mp,
|
63
64
|
odfusion,
|
@@ -67,6 +68,9 @@ from .util import (
|
|
67
68
|
ub64enc,
|
68
69
|
)
|
69
70
|
|
71
|
+
if HAVE_SQLITE3:
|
72
|
+
import sqlite3
|
73
|
+
|
70
74
|
if TYPE_CHECKING:
|
71
75
|
try:
|
72
76
|
from .mdns import MDNS
|
@@ -78,6 +82,10 @@ if PY2:
|
|
78
82
|
range = xrange # type: ignore
|
79
83
|
|
80
84
|
|
85
|
+
VER_SESSION_DB = 1
|
86
|
+
VER_SHARES_DB = 2
|
87
|
+
|
88
|
+
|
81
89
|
class SvcHub(object):
|
82
90
|
"""
|
83
91
|
Hosts all services which cannot be parallelized due to reliance on monolithic resources.
|
@@ -180,8 +188,14 @@ class SvcHub(object):
|
|
180
188
|
|
181
189
|
if not args.use_fpool and args.j != 1:
|
182
190
|
args.no_fpool = True
|
183
|
-
t = "multithreading enabled with -j {}, so disabling fpool -- this can reduce upload performance on some filesystems"
|
184
|
-
|
191
|
+
t = "multithreading enabled with -j {}, so disabling fpool -- this can reduce upload performance on some filesystems, and make some antivirus-softwares "
|
192
|
+
c = 0
|
193
|
+
if ANYWIN:
|
194
|
+
t += "(especially Microsoft Defender) stress your CPU and HDD severely during big uploads"
|
195
|
+
c = 3
|
196
|
+
else:
|
197
|
+
t += "consume more resources (CPU/HDD) than normal"
|
198
|
+
self.log("root", t.format(args.j), c)
|
185
199
|
|
186
200
|
if not args.no_fpool and args.j != 1:
|
187
201
|
t = "WARNING: ignoring --use-fpool because multithreading (-j{}) is enabled"
|
@@ -400,25 +414,48 @@ class SvcHub(object):
|
|
400
414
|
self.log("root", t, 3)
|
401
415
|
return
|
402
416
|
|
403
|
-
import sqlite3
|
404
417
|
|
405
|
-
|
418
|
+
# policy:
|
419
|
+
# the sessions-db is whatever, if something looks broken then just nuke it
|
420
|
+
|
406
421
|
db_path = self.args.ses_db
|
407
|
-
|
408
|
-
|
422
|
+
db_lock = db_path + ".lock"
|
423
|
+
try:
|
424
|
+
create = not os.path.getsize(db_path)
|
425
|
+
except:
|
426
|
+
create = True
|
427
|
+
zs = "creating new" if create else "opening"
|
428
|
+
self.log("root", "%s sessions-db %s" % (zs, db_path))
|
429
|
+
|
430
|
+
for tries in range(2):
|
431
|
+
sver = 0
|
409
432
|
try:
|
410
433
|
db = sqlite3.connect(db_path)
|
411
434
|
cur = db.cursor()
|
412
435
|
try:
|
436
|
+
zs = "select v from kv where k='sver'"
|
437
|
+
sver = cur.execute(zs).fetchall()[0][0]
|
438
|
+
if sver > VER_SESSION_DB:
|
439
|
+
zs = "this version of copyparty only understands session-db v%d and older; the db is v%d"
|
440
|
+
raise Exception(zs % (VER_SESSION_DB, sver))
|
441
|
+
|
413
442
|
cur.execute("select count(*) from us").fetchone()
|
414
|
-
create = False
|
415
|
-
break
|
416
443
|
except:
|
417
|
-
|
444
|
+
if sver:
|
445
|
+
raise
|
446
|
+
sver = 1
|
447
|
+
self._create_session_db(cur)
|
448
|
+
err = self._verify_session_db(cur, sver, db_path)
|
449
|
+
if err:
|
450
|
+
tries = 99
|
451
|
+
self.args.no_ses = True
|
452
|
+
self.log("root", err, 3)
|
453
|
+
break
|
454
|
+
|
418
455
|
except Exception as ex:
|
419
|
-
if
|
456
|
+
if tries or sver > VER_SESSION_DB:
|
420
457
|
raise
|
421
|
-
t = "sessions-db
|
458
|
+
t = "sessions-db is unusable; deleting and recreating: %r"
|
422
459
|
self.log("root", t % (ex,), 3)
|
423
460
|
try:
|
424
461
|
cur.close() # type: ignore
|
@@ -428,8 +465,13 @@ class SvcHub(object):
|
|
428
465
|
db.close() # type: ignore
|
429
466
|
except:
|
430
467
|
pass
|
468
|
+
try:
|
469
|
+
os.unlink(db_lock)
|
470
|
+
except:
|
471
|
+
pass
|
431
472
|
os.unlink(db_path)
|
432
473
|
|
474
|
+
def _create_session_db(self, cur ) :
|
433
475
|
sch = [
|
434
476
|
r"create table kv (k text, v int)",
|
435
477
|
r"create table us (un text, si text, t0 int)",
|
@@ -439,15 +481,44 @@ class SvcHub(object):
|
|
439
481
|
r"create index us_t0 on us(t0)",
|
440
482
|
r"insert into kv values ('sver', 1)",
|
441
483
|
]
|
484
|
+
for cmd in sch:
|
485
|
+
cur.execute(cmd)
|
486
|
+
self.log("root", "created new sessions-db")
|
442
487
|
|
443
|
-
|
444
|
-
|
445
|
-
|
446
|
-
|
447
|
-
|
488
|
+
def _verify_session_db(self, cur , sver , db_path ) :
|
489
|
+
# ensure writable (maybe owned by other user)
|
490
|
+
db = cur.connection
|
491
|
+
|
492
|
+
try:
|
493
|
+
zil = cur.execute("select v from kv where k='pid'").fetchall()
|
494
|
+
if len(zil) > 1:
|
495
|
+
raise Exception()
|
496
|
+
owner = zil[0][0]
|
497
|
+
except:
|
498
|
+
owner = 0
|
499
|
+
|
500
|
+
if not lock_file(db_path + ".lock"):
|
501
|
+
t = "the sessions-db [%s] is already in use by another copyparty instance (pid:%d). This is not supported; please provide another database with --ses-db or give this copyparty-instance its entirely separate config-folder by setting another path in the XDG_CONFIG_HOME env-var. You can also disable this safeguard by setting env-var PRTY_NO_DB_LOCK=1. Will now disable sessions and instead use plaintext passwords in cookies."
|
502
|
+
return t % (db_path, owner)
|
503
|
+
|
504
|
+
vars = (("pid", os.getpid()), ("ts", int(time.time() * 1000)))
|
505
|
+
if owner:
|
506
|
+
# wear-estimate: 2 cells; offsets 0x10, 0x50, 0x19720
|
507
|
+
for k, v in vars:
|
508
|
+
cur.execute("update kv set v=? where k=?", (v, k))
|
509
|
+
else:
|
510
|
+
# wear-estimate: 3~4 cells; offsets 0x10, 0x50, 0x19180, 0x19710, 0x36000, 0x360b0, 0x36b90
|
511
|
+
for k, v in vars:
|
512
|
+
cur.execute("insert into kv values(?, ?)", (k, v))
|
448
513
|
|
514
|
+
if sver < VER_SESSION_DB:
|
515
|
+
cur.execute("delete from kv where k='sver'")
|
516
|
+
cur.execute("insert into kv values('sver',?)", (VER_SESSION_DB,))
|
517
|
+
|
518
|
+
db.commit()
|
449
519
|
cur.close()
|
450
520
|
db.close()
|
521
|
+
return ""
|
451
522
|
|
452
523
|
def setup_share_db(self) :
|
453
524
|
al = self.args
|
@@ -456,7 +527,6 @@ class SvcHub(object):
|
|
456
527
|
al.shr = ""
|
457
528
|
return
|
458
529
|
|
459
|
-
import sqlite3
|
460
530
|
|
461
531
|
al.shr = al.shr.strip("/")
|
462
532
|
if "/" in al.shr or not al.shr:
|
@@ -467,34 +537,48 @@ class SvcHub(object):
|
|
467
537
|
al.shr = "/%s/" % (al.shr,)
|
468
538
|
al.shr1 = al.shr[1:]
|
469
539
|
|
470
|
-
|
471
|
-
|
540
|
+
# policy:
|
541
|
+
# the shares-db is important, so panic if something is wrong
|
542
|
+
|
472
543
|
db_path = self.args.shr_db
|
473
|
-
|
474
|
-
|
475
|
-
|
476
|
-
|
477
|
-
|
478
|
-
|
479
|
-
|
480
|
-
|
481
|
-
|
482
|
-
|
483
|
-
|
484
|
-
|
485
|
-
|
486
|
-
|
487
|
-
|
488
|
-
|
489
|
-
|
490
|
-
|
491
|
-
|
492
|
-
|
493
|
-
|
494
|
-
|
495
|
-
|
496
|
-
|
497
|
-
|
544
|
+
db_lock = db_path + ".lock"
|
545
|
+
try:
|
546
|
+
create = not os.path.getsize(db_path)
|
547
|
+
except:
|
548
|
+
create = True
|
549
|
+
zs = "creating new" if create else "opening"
|
550
|
+
self.log("root", "%s shares-db %s" % (zs, db_path))
|
551
|
+
|
552
|
+
sver = 0
|
553
|
+
try:
|
554
|
+
db = sqlite3.connect(db_path)
|
555
|
+
cur = db.cursor()
|
556
|
+
if not create:
|
557
|
+
zs = "select v from kv where k='sver'"
|
558
|
+
sver = cur.execute(zs).fetchall()[0][0]
|
559
|
+
if sver > VER_SHARES_DB:
|
560
|
+
zs = "this version of copyparty only understands shares-db v%d and older; the db is v%d"
|
561
|
+
raise Exception(zs % (VER_SHARES_DB, sver))
|
562
|
+
|
563
|
+
cur.execute("select count(*) from sh").fetchone()
|
564
|
+
except Exception as ex:
|
565
|
+
t = "could not open shares-db; will now panic...\nthe following database must be repaired or deleted before you can launch copyparty:\n%s\n\nERROR: %s\n\nadditional details:\n%s\n"
|
566
|
+
self.log("root", t % (db_path, ex, min_ex()), 1)
|
567
|
+
raise
|
568
|
+
|
569
|
+
try:
|
570
|
+
zil = cur.execute("select v from kv where k='pid'").fetchall()
|
571
|
+
if len(zil) > 1:
|
572
|
+
raise Exception()
|
573
|
+
owner = zil[0][0]
|
574
|
+
except:
|
575
|
+
owner = 0
|
576
|
+
|
577
|
+
if not lock_file(db_lock):
|
578
|
+
t = "the shares-db [%s] is already in use by another copyparty instance (pid:%d). This is not supported; please provide another database with --shr-db or give this copyparty-instance its entirely separate config-folder by setting another path in the XDG_CONFIG_HOME env-var. You can also disable this safeguard by setting env-var PRTY_NO_DB_LOCK=1. Will now panic."
|
579
|
+
t = t % (db_path, owner)
|
580
|
+
self.log("root", t, 1)
|
581
|
+
raise Exception(t)
|
498
582
|
|
499
583
|
sch1 = [
|
500
584
|
r"create table kv (k text, v int)",
|
@@ -506,32 +590,35 @@ class SvcHub(object):
|
|
506
590
|
r"create index sf_k on sf(k)",
|
507
591
|
r"create index sh_k on sh(k)",
|
508
592
|
r"create index sh_t1 on sh(t1)",
|
593
|
+
r"insert into kv values ('sver', 2)",
|
509
594
|
]
|
510
595
|
|
511
|
-
if
|
512
|
-
|
513
|
-
modified = True
|
596
|
+
if not sver:
|
597
|
+
sver = VER_SHARES_DB
|
514
598
|
for cmd in sch1 + sch2:
|
515
599
|
cur.execute(cmd)
|
516
600
|
self.log("root", "created new shares-db")
|
517
|
-
else:
|
518
|
-
(dver,) = cur.execute("select v from kv where k = 'sver'").fetchall()[0]
|
519
601
|
|
520
|
-
if
|
521
|
-
modified = True
|
602
|
+
if sver == 1:
|
522
603
|
for cmd in sch2:
|
523
604
|
cur.execute(cmd)
|
524
605
|
cur.execute("update sh set st = 0")
|
525
606
|
self.log("root", "shares-db schema upgrade ok")
|
526
607
|
|
527
|
-
if
|
528
|
-
|
529
|
-
|
530
|
-
r"insert into kv values ('sver', %d)" % (2,),
|
531
|
-
]:
|
532
|
-
cur.execute(cmd)
|
533
|
-
db.commit()
|
608
|
+
if sver < VER_SHARES_DB:
|
609
|
+
cur.execute("delete from kv where k='sver'")
|
610
|
+
cur.execute("insert into kv values('sver',?)", (VER_SHARES_DB,))
|
534
611
|
|
612
|
+
vars = (("pid", os.getpid()), ("ts", int(time.time() * 1000)))
|
613
|
+
if owner:
|
614
|
+
# wear-estimate: same as sessions-db
|
615
|
+
for k, v in vars:
|
616
|
+
cur.execute("update kv set v=? where k=?", (v, k))
|
617
|
+
else:
|
618
|
+
for k, v in vars:
|
619
|
+
cur.execute("insert into kv values(?, ?)", (k, v))
|
620
|
+
|
621
|
+
db.commit()
|
535
622
|
cur.close()
|
536
623
|
db.close()
|
537
624
|
|
@@ -669,10 +756,11 @@ class SvcHub(object):
|
|
669
756
|
t += ", "
|
670
757
|
t += "\033[0mNG: \033[35m" + sng
|
671
758
|
|
672
|
-
t += "\033[0m, see --deps"
|
673
|
-
self.log("dependencies", t, 6)
|
759
|
+
t += "\033[0m, see --deps (this is fine btw)"
|
760
|
+
self.log("optional-dependencies", t, 6)
|
674
761
|
|
675
762
|
def _check_env(self) :
|
763
|
+
al = self.args
|
676
764
|
try:
|
677
765
|
files = os.listdir(E.cfg)
|
678
766
|
except:
|
@@ -689,6 +777,21 @@ class SvcHub(object):
|
|
689
777
|
if self.args.bauth_last:
|
690
778
|
self.log("root", "WARNING: ignoring --bauth-last due to --no-bauth", 3)
|
691
779
|
|
780
|
+
have_tcp = False
|
781
|
+
for zs in al.i:
|
782
|
+
if not zs.startswith("unix:"):
|
783
|
+
have_tcp = True
|
784
|
+
if not have_tcp:
|
785
|
+
zb = False
|
786
|
+
zs = "z zm zm4 zm6 zmv zmvv zs zsv zv"
|
787
|
+
for zs in zs.split():
|
788
|
+
if getattr(al, zs, False):
|
789
|
+
setattr(al, zs, False)
|
790
|
+
zb = True
|
791
|
+
if zb:
|
792
|
+
t = "only listening on unix-sockets; cannot enable zeroconf/mdns/ssdp as requested"
|
793
|
+
self.log("root", t, 3)
|
794
|
+
|
692
795
|
if not self.args.no_dav:
|
693
796
|
from .dxml import DXML_OK
|
694
797
|
|
@@ -753,7 +856,7 @@ class SvcHub(object):
|
|
753
856
|
vl = [os.path.expandvars(os.path.expanduser(x)) for x in vl]
|
754
857
|
setattr(al, k, vl)
|
755
858
|
|
756
|
-
for k in "lo hist ssl_log".split(" "):
|
859
|
+
for k in "lo hist dbpath ssl_log".split(" "):
|
757
860
|
vs = getattr(al, k)
|
758
861
|
if vs:
|
759
862
|
vs = os.path.expandvars(os.path.expanduser(vs))
|
copyparty/tcpsrv.py
CHANGED
@@ -563,7 +563,7 @@ class TcpSrv(object):
|
|
563
563
|
ip = None
|
564
564
|
ips = list(t1) + list(t2)
|
565
565
|
qri = self.args.qri
|
566
|
-
if self.args.zm and not qri:
|
566
|
+
if self.args.zm and not qri and ips:
|
567
567
|
name = self.args.name + ".local"
|
568
568
|
t1[name] = next(v for v in (t1 or t2).values())
|
569
569
|
ips = [name] + ips
|
copyparty/th_cli.py
CHANGED
@@ -1,18 +1,23 @@
|
|
1
1
|
# coding: utf-8
|
2
2
|
from __future__ import print_function, unicode_literals
|
3
3
|
|
4
|
+
import errno
|
4
5
|
import os
|
6
|
+
import stat
|
5
7
|
|
6
8
|
from .__init__ import TYPE_CHECKING
|
7
9
|
from .authsrv import VFS
|
8
10
|
from .bos import bos
|
9
11
|
from .th_srv import EXTS_AC, HAVE_WEBP, thumb_path
|
10
|
-
from .util import Cooldown
|
12
|
+
from .util import Cooldown, Pebkac
|
11
13
|
|
12
14
|
if TYPE_CHECKING:
|
13
15
|
from .httpsrv import HttpSrv
|
14
16
|
|
15
17
|
|
18
|
+
IOERROR = "reading the file was denied by the server os; either due to filesystem permissions, selinux, apparmor, or similar:\n%r"
|
19
|
+
|
20
|
+
|
16
21
|
class ThumbCli(object):
|
17
22
|
def __init__(self, hsrv ) :
|
18
23
|
self.broker = hsrv.broker
|
@@ -121,7 +126,7 @@ class ThumbCli(object):
|
|
121
126
|
|
122
127
|
tpath = thumb_path(histpath, rem, mtime, fmt, self.fmt_ffa)
|
123
128
|
tpaths = [tpath]
|
124
|
-
if fmt == "w":
|
129
|
+
if fmt[:1] == "w":
|
125
130
|
# also check for jpg (maybe webp is unavailable)
|
126
131
|
tpaths.append(tpath.rsplit(".", 1)[0] + ".jpg")
|
127
132
|
|
@@ -154,8 +159,22 @@ class ThumbCli(object):
|
|
154
159
|
if abort:
|
155
160
|
return None
|
156
161
|
|
157
|
-
|
158
|
-
|
162
|
+
ap = os.path.join(ptop, rem)
|
163
|
+
try:
|
164
|
+
st = bos.stat(ap)
|
165
|
+
if not st.st_size or not stat.S_ISREG(st.st_mode):
|
166
|
+
return None
|
167
|
+
|
168
|
+
with open(ap, "rb", 4) as f:
|
169
|
+
if not f.read(4):
|
170
|
+
raise Exception()
|
171
|
+
except OSError as ex:
|
172
|
+
if ex.errno == errno.ENOENT:
|
173
|
+
raise Pebkac(404)
|
174
|
+
else:
|
175
|
+
raise Pebkac(500, IOERROR % (ex,))
|
176
|
+
except Exception as ex:
|
177
|
+
raise Pebkac(500, IOERROR % (ex,))
|
159
178
|
|
160
179
|
x = self.broker.ask("thumbsrv.get", ptop, rem, mtime, fmt)
|
161
180
|
return x.get() # type: ignore
|
copyparty/th_srv.py
CHANGED
@@ -4,8 +4,10 @@ from __future__ import print_function, unicode_literals
|
|
4
4
|
import hashlib
|
5
5
|
import logging
|
6
6
|
import os
|
7
|
+
import re
|
7
8
|
import shutil
|
8
9
|
import subprocess as sp
|
10
|
+
import tempfile
|
9
11
|
import threading
|
10
12
|
import time
|
11
13
|
|
@@ -18,6 +20,7 @@ from .mtag import HAVE_FFMPEG, HAVE_FFPROBE, au_unpk, ffprobe
|
|
18
20
|
from .util import BytesIO # type: ignore
|
19
21
|
from .util import (
|
20
22
|
FFMPEG_URL,
|
23
|
+
VF_CAREFUL,
|
21
24
|
Cooldown,
|
22
25
|
Daemon,
|
23
26
|
afsenc,
|
@@ -45,6 +48,10 @@ HAVE_WEBP = False
|
|
45
48
|
|
46
49
|
EXTS_TH = set(["jpg", "webp", "png"])
|
47
50
|
EXTS_AC = set(["opus", "owa", "caf", "mp3"])
|
51
|
+
EXTS_SPEC_SAFE = set("aif aiff flac mp3 opus wav".split())
|
52
|
+
|
53
|
+
PTN_TS = re.compile("^-?[0-9a-f]{8,10}$")
|
54
|
+
|
48
55
|
|
49
56
|
try:
|
50
57
|
if os.environ.get("PRTY_NO_PIL"):
|
@@ -160,12 +167,15 @@ class ThumbSrv(object):
|
|
160
167
|
|
161
168
|
self.mutex = threading.Lock()
|
162
169
|
self.busy = {}
|
170
|
+
self.untemp = {}
|
163
171
|
self.ram = {}
|
164
172
|
self.memcond = threading.Condition(self.mutex)
|
165
173
|
self.stopping = False
|
166
174
|
self.rm_nullthumbs = True # forget failed conversions on startup
|
167
175
|
self.nthr = max(1, self.args.th_mt)
|
168
176
|
|
177
|
+
self.exts_spec_unsafe = set(self.args.th_spec_cnv.split(","))
|
178
|
+
|
169
179
|
self.q = Queue(self.nthr * 4)
|
170
180
|
for n in range(self.nthr):
|
171
181
|
Daemon(self.worker, "thumb-{}-{}".format(n, self.nthr))
|
@@ -382,8 +392,12 @@ class ThumbSrv(object):
|
|
382
392
|
self.log(msg, c)
|
383
393
|
if getattr(ex, "returncode", 0) != 321:
|
384
394
|
if fun == funs[-1]:
|
385
|
-
|
386
|
-
|
395
|
+
try:
|
396
|
+
with open(ttpath, "wb") as _:
|
397
|
+
pass
|
398
|
+
except Exception as ex:
|
399
|
+
t = "failed to create the file [%s]: %r"
|
400
|
+
self.log(t % (ttpath, ex), 3)
|
387
401
|
else:
|
388
402
|
# ffmpeg may spawn empty files on windows
|
389
403
|
try:
|
@@ -396,13 +410,24 @@ class ThumbSrv(object):
|
|
396
410
|
|
397
411
|
try:
|
398
412
|
wrename(self.log, ttpath, tpath, vn.flags)
|
399
|
-
except:
|
413
|
+
except Exception as ex:
|
414
|
+
if not os.path.exists(tpath):
|
415
|
+
t = "failed to move [%s] to [%s]: %r"
|
416
|
+
self.log(t % (ttpath, tpath, ex), 3)
|
400
417
|
pass
|
401
418
|
|
419
|
+
untemp = []
|
402
420
|
with self.mutex:
|
403
421
|
subs = self.busy[tpath]
|
404
422
|
del self.busy[tpath]
|
405
423
|
self.ram.pop(ttpath, None)
|
424
|
+
untemp = self.untemp.pop(ttpath, None) or []
|
425
|
+
|
426
|
+
for ap in untemp:
|
427
|
+
try:
|
428
|
+
wunlink(self.log, ap, VF_CAREFUL)
|
429
|
+
except:
|
430
|
+
pass
|
406
431
|
|
407
432
|
for x in subs:
|
408
433
|
with x:
|
@@ -655,15 +680,43 @@ class ThumbSrv(object):
|
|
655
680
|
if "ac" not in ret:
|
656
681
|
raise Exception("not audio")
|
657
682
|
|
683
|
+
fext = abspath.split(".")[-1].lower()
|
684
|
+
|
658
685
|
# https://trac.ffmpeg.org/ticket/10797
|
659
686
|
# expect 1 GiB every 600 seconds when duration is tricky;
|
660
687
|
# simple filetypes are generally safer so let's special-case those
|
661
|
-
|
662
|
-
|
663
|
-
dur = ret[".dur"][1] if ".dur" in ret else 300
|
688
|
+
coeff = 1800 if fext in EXTS_SPEC_SAFE else 600
|
689
|
+
dur = ret[".dur"][1] if ".dur" in ret else 900
|
664
690
|
need = 0.2 + dur / coeff
|
665
691
|
self.wait4ram(need, tpath)
|
666
692
|
|
693
|
+
infile = abspath
|
694
|
+
if dur >= 900 or fext in self.exts_spec_unsafe:
|
695
|
+
with tempfile.NamedTemporaryFile(suffix=".spec.flac", delete=False) as f:
|
696
|
+
f.write(b"h")
|
697
|
+
infile = f.name
|
698
|
+
try:
|
699
|
+
self.untemp[tpath].append(infile)
|
700
|
+
except:
|
701
|
+
self.untemp[tpath] = [infile]
|
702
|
+
|
703
|
+
# fmt: off
|
704
|
+
cmd = [
|
705
|
+
b"ffmpeg",
|
706
|
+
b"-nostdin",
|
707
|
+
b"-v", b"error",
|
708
|
+
b"-hide_banner",
|
709
|
+
b"-i", fsenc(abspath),
|
710
|
+
b"-map", b"0:a:0",
|
711
|
+
b"-ac", b"1",
|
712
|
+
b"-ar", b"48000",
|
713
|
+
b"-sample_fmt", b"s16",
|
714
|
+
b"-t", b"900",
|
715
|
+
b"-y", fsenc(infile),
|
716
|
+
]
|
717
|
+
# fmt: on
|
718
|
+
self._run_ff(cmd, vn)
|
719
|
+
|
667
720
|
fc = "[0:a:0]aresample=48000{},showspectrumpic=s="
|
668
721
|
if "3" in fmt:
|
669
722
|
fc += "1280x1024,crop=1420:1056:70:48[o]"
|
@@ -683,7 +736,7 @@ class ThumbSrv(object):
|
|
683
736
|
b"-nostdin",
|
684
737
|
b"-v", b"error",
|
685
738
|
b"-hide_banner",
|
686
|
-
b"-i", fsenc(
|
739
|
+
b"-i", fsenc(infile),
|
687
740
|
b"-filter_complex", fc.encode("utf-8"),
|
688
741
|
b"-map", b"[o]",
|
689
742
|
b"-frames:v", b"1",
|
@@ -987,6 +1040,8 @@ class ThumbSrv(object):
|
|
987
1040
|
# thumb file
|
988
1041
|
try:
|
989
1042
|
b64, ts, ext = f.split(".")
|
1043
|
+
if len(ts) > 8 and PTN_TS.match(ts):
|
1044
|
+
ts = "yeahokay"
|
990
1045
|
if len(b64) != 24 or len(ts) != 8 or ext not in exts:
|
991
1046
|
raise Exception()
|
992
1047
|
except:
|
copyparty/u2idx.py
CHANGED
@@ -128,9 +128,9 @@ class U2idx(object):
|
|
128
128
|
|
129
129
|
|
130
130
|
ptop = vn.realpath
|
131
|
-
histpath = self.asrv.vfs.
|
131
|
+
histpath = self.asrv.vfs.dbpaths.get(ptop)
|
132
132
|
if not histpath:
|
133
|
-
self.log("no
|
133
|
+
self.log("no dbpath for %r" % (ptop,))
|
134
134
|
return None
|
135
135
|
|
136
136
|
db_path = os.path.join(histpath, "up2k.db")
|
copyparty/up2k.py
CHANGED
@@ -91,7 +91,7 @@ VF_AFFECTS_INDEXING = set(zsg.split(" "))
|
|
91
91
|
|
92
92
|
SBUSY = "cannot receive uploads right now;\nserver busy with %s.\nPlease wait; the client will retry..."
|
93
93
|
|
94
|
-
HINT_HISTPATH = "you could try moving the database to another location (preferably an SSD or NVME drive) using either the --hist argument (global option for all volumes), or the hist volflag (just for this volume)"
|
94
|
+
HINT_HISTPATH = "you could try moving the database to another location (preferably an SSD or NVME drive) using either the --hist argument (global option for all volumes), or the hist volflag (just for this volume), or, if you want to keep the thumbnails in the current location and only move the database itself, then use --dbpath or volflag dbpath"
|
95
95
|
|
96
96
|
|
97
97
|
NULLSTAT = os.stat_result((0, -1, -1, 0, 0, 0, 0, 0, 0, 0))
|
@@ -1089,9 +1089,9 @@ class Up2k(object):
|
|
1089
1089
|
self, ptop , flags
|
1090
1090
|
) :
|
1091
1091
|
"""mutex(main,reg) me"""
|
1092
|
-
histpath = self.vfs.
|
1092
|
+
histpath = self.vfs.dbpaths.get(ptop)
|
1093
1093
|
if not histpath:
|
1094
|
-
self.log("no
|
1094
|
+
self.log("no dbpath for %r" % (ptop,))
|
1095
1095
|
return None
|
1096
1096
|
|
1097
1097
|
db_path = os.path.join(histpath, "up2k.db")
|
@@ -1336,12 +1336,15 @@ class Up2k(object):
|
|
1336
1336
|
]
|
1337
1337
|
excl += [absreal(x) for x in excl]
|
1338
1338
|
excl += list(self.vfs.histtab.values())
|
1339
|
+
excl += list(self.vfs.dbpaths.values())
|
1339
1340
|
if WINDOWS:
|
1340
1341
|
excl = [x.replace("/", "\\") for x in excl]
|
1341
1342
|
else:
|
1342
1343
|
# ~/.wine/dosdevices/z:/ and such
|
1343
1344
|
excl.extend(("/dev", "/proc", "/run", "/sys"))
|
1344
1345
|
|
1346
|
+
excl = list({k: 1 for k in excl})
|
1347
|
+
|
1345
1348
|
if self.args.re_dirsz:
|
1346
1349
|
db.c.execute("delete from ds")
|
1347
1350
|
db.n += 1
|
@@ -5078,7 +5081,7 @@ class Up2k(object):
|
|
5078
5081
|
|
5079
5082
|
def _snap_reg(self, ptop , reg ) :
|
5080
5083
|
now = time.time()
|
5081
|
-
histpath = self.vfs.
|
5084
|
+
histpath = self.vfs.dbpaths.get(ptop)
|
5082
5085
|
if not histpath:
|
5083
5086
|
return
|
5084
5087
|
|