copyparty 1.19.0__py3-none-any.whl → 1.19.2__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/th_srv.py CHANGED
@@ -2,6 +2,7 @@
2
2
  from __future__ import print_function, unicode_literals
3
3
 
4
4
  import hashlib
5
+ import io
5
6
  import logging
6
7
  import os
7
8
  import re
@@ -82,7 +83,10 @@ try:
82
83
  if os.environ.get("PRTY_NO_PIL_HEIF"):
83
84
  raise Exception()
84
85
 
85
- from pyheif_pillow_opener import register_heif_opener
86
+ try:
87
+ from pillow_heif import register_heif_opener
88
+ except ImportError:
89
+ from pyheif_pillow_opener import register_heif_opener
86
90
 
87
91
  register_heif_opener()
88
92
  HAVE_HEIF = True
@@ -109,14 +113,28 @@ except:
109
113
 
110
114
  try:
111
115
  if os.environ.get("PRTY_NO_VIPS"):
112
- raise Exception()
116
+ raise ImportError()
113
117
 
114
118
  HAVE_VIPS = True
115
119
  import pyvips
116
120
 
117
121
  logging.getLogger("pyvips").setLevel(logging.WARNING)
118
- except:
122
+ except Exception as e:
119
123
  HAVE_VIPS = False
124
+ if not isinstance(e, ImportError):
125
+ logging.warning("libvips found, but failed to load: " + str(e))
126
+
127
+
128
+ try:
129
+ if os.environ.get("PRTY_NO_RAW"):
130
+ raise Exception()
131
+
132
+ HAVE_RAW = True
133
+ import rawpy
134
+
135
+ logging.getLogger("rawpy").setLevel(logging.WARNING)
136
+ except:
137
+ HAVE_RAW = False
120
138
 
121
139
 
122
140
  th_dir_cache = {}
@@ -202,11 +220,19 @@ class ThumbSrv(object):
202
220
  if self.args.th_clean:
203
221
  Daemon(self.cleaner, "thumb.cln")
204
222
 
205
- self.fmt_pil, self.fmt_vips, self.fmt_ffi, self.fmt_ffv, self.fmt_ffa = [
223
+ (
224
+ self.fmt_pil,
225
+ self.fmt_vips,
226
+ self.fmt_raw,
227
+ self.fmt_ffi,
228
+ self.fmt_ffv,
229
+ self.fmt_ffa,
230
+ ) = [
206
231
  set(y.split(","))
207
232
  for y in [
208
233
  self.args.th_r_pil,
209
234
  self.args.th_r_vips,
235
+ self.args.th_r_raw,
210
236
  self.args.th_r_ffi,
211
237
  self.args.th_r_ffv,
212
238
  self.args.th_r_ffa,
@@ -229,6 +255,9 @@ class ThumbSrv(object):
229
255
  if "vips" in self.args.th_dec:
230
256
  self.thumbable |= self.fmt_vips
231
257
 
258
+ if "raw" in self.args.th_dec:
259
+ self.thumbable |= self.fmt_raw
260
+
232
261
  if "ff" in self.args.th_dec:
233
262
  for zss in [self.fmt_ffi, self.fmt_ffv, self.fmt_ffa]:
234
263
  self.thumbable |= zss
@@ -310,6 +339,7 @@ class ThumbSrv(object):
310
339
  "thumbable": self.thumbable,
311
340
  "pil": self.fmt_pil,
312
341
  "vips": self.fmt_vips,
342
+ "raw": self.fmt_raw,
313
343
  "ffi": self.fmt_ffi,
314
344
  "ffv": self.fmt_ffv,
315
345
  "ffa": self.fmt_ffa,
@@ -365,6 +395,8 @@ class ThumbSrv(object):
365
395
  funs.append(self.conv_pil)
366
396
  elif lib == "vips" and ext in self.fmt_vips:
367
397
  funs.append(self.conv_vips)
398
+ elif lib == "raw" and ext in self.fmt_raw:
399
+ funs.append(self.conv_raw)
368
400
  elif can_au and (want_png or want_au):
369
401
  if want_opus:
370
402
  funs.append(self.conv_opus)
@@ -477,35 +509,38 @@ class ThumbSrv(object):
477
509
 
478
510
  return im
479
511
 
512
+ def conv_image_pil(self, im , tpath , fmt , vn ) :
513
+ try:
514
+ im = self.fancy_pillow(im, fmt, vn)
515
+ except Exception as ex:
516
+ self.log("fancy_pillow {}".format(ex), "90")
517
+ im.thumbnail(self.getres(vn, fmt))
518
+
519
+ fmts = ["RGB", "L"]
520
+ args = {"quality": 40}
521
+
522
+ if tpath.endswith(".webp"):
523
+ # quality 80 = pillow-default
524
+ # quality 75 = ffmpeg-default
525
+ # method 0 = pillow-default, fast
526
+ # method 4 = ffmpeg-default
527
+ # method 6 = max, slow
528
+ fmts.extend(("RGBA", "LA"))
529
+ args["method"] = 6
530
+ else:
531
+ # default q = 75
532
+ args["progressive"] = True
533
+
534
+ if im.mode not in fmts:
535
+ # print("conv {}".format(im.mode))
536
+ im = im.convert("RGB")
537
+
538
+ im.save(tpath, **args)
539
+
480
540
  def conv_pil(self, abspath , tpath , fmt , vn ) :
481
541
  self.wait4ram(0.2, tpath)
482
542
  with Image.open(fsenc(abspath)) as im:
483
- try:
484
- im = self.fancy_pillow(im, fmt, vn)
485
- except Exception as ex:
486
- self.log("fancy_pillow {}".format(ex), "90")
487
- im.thumbnail(self.getres(vn, fmt))
488
-
489
- fmts = ["RGB", "L"]
490
- args = {"quality": 40}
491
-
492
- if tpath.endswith(".webp"):
493
- # quality 80 = pillow-default
494
- # quality 75 = ffmpeg-default
495
- # method 0 = pillow-default, fast
496
- # method 4 = ffmpeg-default
497
- # method 6 = max, slow
498
- fmts.extend(("RGBA", "LA"))
499
- args["method"] = 6
500
- else:
501
- # default q = 75
502
- args["progressive"] = True
503
-
504
- if im.mode not in fmts:
505
- # print("conv {}".format(im.mode))
506
- im = im.convert("RGB")
507
-
508
- im.save(tpath, **args)
543
+ self.conv_image_pil(im, tpath, fmt, vn)
509
544
 
510
545
  def conv_vips(self, abspath , tpath , fmt , vn ) :
511
546
  self.wait4ram(0.2, tpath)
@@ -527,9 +562,52 @@ class ThumbSrv(object):
527
562
 
528
563
  img.write_to_file(tpath, Q=40)
529
564
 
565
+ def conv_raw(self, abspath , tpath , fmt , vn ) :
566
+ self.wait4ram(0.2, tpath)
567
+ with rawpy.imread(abspath) as raw:
568
+ thumb = raw.extract_thumb()
569
+ if thumb.format == rawpy.ThumbFormat.JPEG and tpath.endswith(".jpg"):
570
+ # if we have a jpg thumbnail and no webp output is available,
571
+ # just write the jpg directly (it'll be the wrong size, but it's fast)
572
+ with open(tpath, "wb") as f:
573
+ f.write(thumb.data)
574
+ if HAVE_VIPS:
575
+ crops = ["centre", "none"]
576
+ if "f" in fmt:
577
+ crops = ["none"]
578
+ w, h = self.getres(vn, fmt)
579
+ kw = {"height": h, "size": "down", "intent": "relative"}
580
+
581
+ for c in crops:
582
+ try:
583
+ kw["crop"] = c
584
+ if thumb.format == rawpy.ThumbFormat.BITMAP:
585
+ img = pyvips.Image.new_from_array(
586
+ thumb.data, interpretation="rgb"
587
+ )
588
+ img = img.thumbnail_image(w, **kw)
589
+ else:
590
+ img = pyvips.Image.thumbnail_buffer(thumb.data, w, **kw)
591
+ break
592
+ except:
593
+ if c == crops[-1]:
594
+ raise
595
+
596
+ img.write_to_file(tpath, Q=40)
597
+ elif HAVE_PIL:
598
+ if thumb.format == rawpy.ThumbFormat.BITMAP:
599
+ im = Image.fromarray(thumb.data, "RGB")
600
+ else:
601
+ im = Image.open(io.BytesIO(thumb.data))
602
+ self.conv_image_pil(im, tpath, fmt, vn)
603
+ else:
604
+ raise Exception(
605
+ "either pil or vips is needed to process embedded bitmap thumbnails in raw files"
606
+ )
607
+
530
608
  def conv_ffmpeg(self, abspath , tpath , fmt , vn ) :
531
609
  self.wait4ram(0.2, tpath)
532
- ret, _ = ffprobe(abspath, int(vn.flags["convt"] / 2))
610
+ ret, _, _, _ = ffprobe(abspath, int(vn.flags["convt"] / 2))
533
611
  if not ret:
534
612
  return
535
613
 
@@ -540,6 +618,17 @@ class ThumbSrv(object):
540
618
  dur = ret[".dur"][1] if ".dur" in ret else 4
541
619
  seek = [b"-ss", "{:.0f}".format(dur / 3).encode("utf-8")]
542
620
 
621
+ self._ffmpeg_im(abspath, tpath, fmt, vn, seek, b"0:v:0")
622
+
623
+ def _ffmpeg_im(
624
+ self,
625
+ abspath ,
626
+ tpath ,
627
+ fmt ,
628
+ vn ,
629
+ seek ,
630
+ imap ,
631
+ ) :
543
632
  scale = "scale={0}:{1}:force_original_aspect_ratio="
544
633
  if "f" in fmt:
545
634
  scale += "decrease,setsar=1:1"
@@ -558,7 +647,7 @@ class ThumbSrv(object):
558
647
  cmd += seek
559
648
  cmd += [
560
649
  b"-i", fsenc(abspath),
561
- b"-map", b"0:v:0",
650
+ b"-map", imap,
562
651
  b"-vf", bscale,
563
652
  b"-frames:v", b"1",
564
653
  b"-metadata:s:v:0", b"rotate=0",
@@ -579,11 +668,11 @@ class ThumbSrv(object):
579
668
  ]
580
669
 
581
670
  cmd += [fsenc(tpath)]
582
- self._run_ff(cmd, vn)
671
+ self._run_ff(cmd, vn, "convt")
583
672
 
584
- def _run_ff(self, cmd , vn , oom = 400) :
673
+ def _run_ff(self, cmd , vn , kto , oom = 400) :
585
674
  # self.log((b" ".join(cmd)).decode("utf-8"))
586
- ret, _, serr = runcmd(cmd, timeout=vn.flags["convt"], nice=True, oom=oom)
675
+ ret, _, serr = runcmd(cmd, timeout=vn.flags[kto], nice=True, oom=oom)
587
676
  if not ret:
588
677
  return
589
678
 
@@ -627,7 +716,7 @@ class ThumbSrv(object):
627
716
  raise sp.CalledProcessError(ret, (cmd[0], b"...", cmd[-1]))
628
717
 
629
718
  def conv_waves(self, abspath , tpath , fmt , vn ) :
630
- ret, _ = ffprobe(abspath, int(vn.flags["convt"] / 2))
719
+ ret, _, _, _ = ffprobe(abspath, int(vn.flags["convt"] / 2))
631
720
  if "ac" not in ret:
632
721
  raise Exception("not audio")
633
722
 
@@ -665,7 +754,7 @@ class ThumbSrv(object):
665
754
  # fmt: on
666
755
 
667
756
  cmd += [fsenc(tpath)]
668
- self._run_ff(cmd, vn)
757
+ self._run_ff(cmd, vn, "convt")
669
758
 
670
759
  if "pngquant" in vn.flags:
671
760
  wtpath = tpath + ".png"
@@ -686,11 +775,31 @@ class ThumbSrv(object):
686
775
  else:
687
776
  atomic_move(self.log, wtpath, tpath, vn.flags)
688
777
 
778
+ def conv_emb_cv(
779
+ self, abspath , tpath , fmt , vn , strm
780
+ ) :
781
+ self.wait4ram(0.2, tpath)
782
+ self._ffmpeg_im(
783
+ abspath, tpath, fmt, vn, [], b"0:" + strm["index"].encode("ascii")
784
+ )
785
+
689
786
  def conv_spec(self, abspath , tpath , fmt , vn ) :
690
- ret, _ = ffprobe(abspath, int(vn.flags["convt"] / 2))
787
+ ret, raw, strms, ctnr = ffprobe(abspath, int(vn.flags["convt"] / 2))
691
788
  if "ac" not in ret:
692
789
  raise Exception("not audio")
693
790
 
791
+ want_spec = vn.flags.get("th_spec_p", 1)
792
+ if want_spec < 2:
793
+ for strm in strms:
794
+ if (
795
+ strm.get("codec_type") == "video"
796
+ and strm.get("DISPOSITION:attached_pic") == "1"
797
+ ):
798
+ return self.conv_emb_cv(abspath, tpath, fmt, vn, strm)
799
+
800
+ if not want_spec:
801
+ raise Exception("spectrograms forbidden by volflag")
802
+
694
803
  fext = abspath.split(".")[-1].lower()
695
804
 
696
805
  # https://trac.ffmpeg.org/ticket/10797
@@ -726,7 +835,7 @@ class ThumbSrv(object):
726
835
  b"-y", fsenc(infile),
727
836
  ]
728
837
  # fmt: on
729
- self._run_ff(cmd, vn)
838
+ self._run_ff(cmd, vn, "convt")
730
839
 
731
840
  fc = "[0:a:0]aresample=48000{},showspectrumpic=s="
732
841
  if "3" in fmt:
@@ -768,7 +877,7 @@ class ThumbSrv(object):
768
877
  ]
769
878
 
770
879
  cmd += [fsenc(tpath)]
771
- self._run_ff(cmd, vn)
880
+ self._run_ff(cmd, vn, "convt")
772
881
 
773
882
  def conv_mp3(self, abspath , tpath , fmt , vn ) :
774
883
  quality = self.args.q_mp3.lower()
@@ -776,7 +885,7 @@ class ThumbSrv(object):
776
885
  raise Exception("disabled in server config")
777
886
 
778
887
  self.wait4ram(0.2, tpath)
779
- tags, rawtags = ffprobe(abspath, int(vn.flags["convt"] / 2))
888
+ tags, rawtags, _, _ = ffprobe(abspath, int(vn.flags["convt"] / 2))
780
889
  if "ac" not in tags:
781
890
  raise Exception("not audio")
782
891
 
@@ -807,14 +916,14 @@ class ThumbSrv(object):
807
916
  fsenc(tpath)
808
917
  ]
809
918
  # fmt: on
810
- self._run_ff(cmd, vn, oom=300)
919
+ self._run_ff(cmd, vn, "aconvt", oom=300)
811
920
 
812
921
  def conv_flac(self, abspath , tpath , fmt , vn ) :
813
922
  if self.args.no_acode or not self.args.allow_flac:
814
923
  raise Exception("flac not permitted in server config")
815
924
 
816
925
  self.wait4ram(0.2, tpath)
817
- tags, rawtags = ffprobe(abspath, int(vn.flags["convt"] / 2))
926
+ tags, _, _, _ = ffprobe(abspath, int(vn.flags["convt"] / 2))
818
927
  if "ac" not in tags:
819
928
  raise Exception("not audio")
820
929
 
@@ -832,14 +941,14 @@ class ThumbSrv(object):
832
941
  fsenc(tpath)
833
942
  ]
834
943
  # fmt: on
835
- self._run_ff(cmd, vn, oom=300)
944
+ self._run_ff(cmd, vn, "aconvt", oom=300)
836
945
 
837
946
  def conv_wav(self, abspath , tpath , fmt , vn ) :
838
947
  if self.args.no_acode or not self.args.allow_wav:
839
948
  raise Exception("wav not permitted in server config")
840
949
 
841
950
  self.wait4ram(0.2, tpath)
842
- tags, rawtags = ffprobe(abspath, int(vn.flags["convt"] / 2))
951
+ tags, _, _, _ = ffprobe(abspath, int(vn.flags["convt"] / 2))
843
952
  if "ac" not in tags:
844
953
  raise Exception("not audio")
845
954
 
@@ -867,14 +976,14 @@ class ThumbSrv(object):
867
976
  fsenc(tpath)
868
977
  ]
869
978
  # fmt: on
870
- self._run_ff(cmd, vn, oom=300)
979
+ self._run_ff(cmd, vn, "aconvt", oom=300)
871
980
 
872
981
  def conv_opus(self, abspath , tpath , fmt , vn ) :
873
982
  if self.args.no_acode or not self.args.q_opus:
874
983
  raise Exception("disabled in server config")
875
984
 
876
985
  self.wait4ram(0.2, tpath)
877
- tags, rawtags = ffprobe(abspath, int(vn.flags["convt"] / 2))
986
+ tags, rawtags, _, _ = ffprobe(abspath, int(vn.flags["convt"] / 2))
878
987
  if "ac" not in tags:
879
988
  raise Exception("not audio")
880
989
 
@@ -923,7 +1032,7 @@ class ThumbSrv(object):
923
1032
  fsenc(tpath)
924
1033
  ]
925
1034
  # fmt: on
926
- self._run_ff(cmd, vn, oom=300)
1035
+ self._run_ff(cmd, vn, "aconvt", oom=300)
927
1036
 
928
1037
  def _conv_caf(
929
1038
  self,
@@ -963,7 +1072,7 @@ class ThumbSrv(object):
963
1072
  fsenc(tmp_opus)
964
1073
  ]
965
1074
  # fmt: on
966
- self._run_ff(cmd, vn, oom=300)
1075
+ self._run_ff(cmd, vn, "aconvt", oom=300)
967
1076
 
968
1077
  # iOS fails to play some "insufficiently complex" files
969
1078
  # (average file shorter than 8 seconds), so of course we
@@ -990,7 +1099,7 @@ class ThumbSrv(object):
990
1099
  fsenc(tpath)
991
1100
  ]
992
1101
  # fmt: on
993
- self._run_ff(cmd, vn, oom=300)
1102
+ self._run_ff(cmd, vn, "aconvt", oom=300)
994
1103
 
995
1104
  else:
996
1105
  # simple remux should be safe
@@ -1009,7 +1118,7 @@ class ThumbSrv(object):
1009
1118
  fsenc(tpath)
1010
1119
  ]
1011
1120
  # fmt: on
1012
- self._run_ff(cmd, vn, oom=300)
1121
+ self._run_ff(cmd, vn, "aconvt", oom=300)
1013
1122
 
1014
1123
  try:
1015
1124
  wunlink(self.log, tmp_opus, vn.flags)
copyparty/u2idx.py CHANGED
@@ -385,7 +385,7 @@ class U2idx(object):
385
385
  fk_alg = 2 if "fka" in flags else 1
386
386
  c = cur.execute(uq, tuple(vuv))
387
387
  for hit in c:
388
- w, ts, sz, rd, fn, ip, at = hit[:7]
388
+ w, ts, sz, rd, fn = hit[:5]
389
389
 
390
390
  if rd.startswith("//") or fn.startswith("//"):
391
391
  rd, fn = s3dec(rd, fn)