mirror of
https://github.com/9001/copyparty.git
synced 2026-02-20 17:30:08 +10:00
@@ -36,7 +36,7 @@
|
||||
|
||||
for (var a = 0; a < files.length; a++) {
|
||||
var file = files[a],
|
||||
is_pic = /\.(jpe?g|png|gif|webp)$/i.exec(file.vp),
|
||||
is_pic = /\.(jpe?g|png|gif|webp|jxl)$/i.exec(file.vp),
|
||||
is_audio = re_au_all.exec(file.vp),
|
||||
basename = file.vp.replace(/\.[^\.]+$/, ""),
|
||||
entry = pairs[basename];
|
||||
|
||||
@@ -1668,7 +1668,7 @@ def add_logging(ap):
|
||||
ap2.add_argument("--log-htp", action="store_true", help="debug: print http-server threadpool scaling")
|
||||
ap2.add_argument("--ihead", metavar="HEADER", type=u, action='append', help="print request \033[33mHEADER\033[0m; [\033[32m*\033[0m]=all")
|
||||
ap2.add_argument("--ohead", metavar="HEADER", type=u, action='append', help="print response \033[33mHEADER\033[0m; [\033[32m*\033[0m]=all")
|
||||
ap2.add_argument("--lf-url", metavar="RE", type=u, default=r"^/\.cpr/|[?&]th=[wjp]|/\.(_|ql_|DS_Store$|localized$)", help="dont log URLs matching regex \033[33mRE\033[0m")
|
||||
ap2.add_argument("--lf-url", metavar="RE", type=u, default=r"^/\.cpr/|[?&]th=[xwjp]|/\.(_|ql_|DS_Store$|localized$)", help="dont log URLs matching regex \033[33mRE\033[0m")
|
||||
ap2.add_argument("--scan-st-r", metavar="SEC", type=float, default=0.1, help="fs-indexing: wait \033[33mSEC\033[0m between each status-message")
|
||||
ap2.add_argument("--scan-pr-r", metavar="SEC", type=float, default=10, help="fs-indexing: wait \033[33mSEC\033[0m between each 'progress:' message")
|
||||
ap2.add_argument("--scan-pr-s", metavar="MiB", type=float, default=1, help="fs-indexing: say 'file: <name>' when a file larger than \033[33mMiB\033[0m is about to be hashed")
|
||||
@@ -1706,6 +1706,7 @@ def add_thumbnail(ap):
|
||||
ap2.add_argument("--th-dec", metavar="LIBS", default="vips,pil,raw,ff", help="image decoders, in order of preference")
|
||||
ap2.add_argument("--th-no-jpg", action="store_true", help="disable jpg output")
|
||||
ap2.add_argument("--th-no-webp", action="store_true", help="disable webp output")
|
||||
ap2.add_argument("--th-no-jxl", action="store_true", help="disable jpeg-xl output")
|
||||
ap2.add_argument("--th-ff-jpg", action="store_true", help="force jpg output for video thumbs (avoids issues on some FFmpeg builds)")
|
||||
ap2.add_argument("--th-ff-swr", action="store_true", help="use swresample instead of soxr for audio thumbs (faster, lower accuracy, avoids issues on some FFmpeg builds)")
|
||||
ap2.add_argument("--th-poke", metavar="SEC", type=int, default=300, help="activity labeling cooldown -- avoids doing keepalive pokes (updating the mtime) on thumbnail folders more often than \033[33mSEC\033[0m seconds")
|
||||
|
||||
@@ -95,7 +95,7 @@ def enthumb(
|
||||
if not thp:
|
||||
raise Exception()
|
||||
|
||||
ext = "jpg" if fmt == "j" else "webp" if fmt == "w" else fmt
|
||||
ext = "jpg" if fmt == "j" else "webp" if fmt == "w" else "jxl" if fmt == "x" else fmt
|
||||
sz = bos.path.getsize(thp)
|
||||
st: os.stat_result = f["st"]
|
||||
ts = st.st_mtime
|
||||
|
||||
@@ -8,7 +8,7 @@ import stat
|
||||
from .__init__ import TYPE_CHECKING
|
||||
from .authsrv import VFS
|
||||
from .bos import bos
|
||||
from .th_srv import EXTS_AC, HAVE_WEBP, thumb_path
|
||||
from .th_srv import EXTS_AC, HAVE_WEBP, HAVE_JXL, thumb_path
|
||||
from .util import Cooldown, Pebkac
|
||||
|
||||
if True: # pylint: disable=using-constant-test
|
||||
@@ -20,7 +20,7 @@ if TYPE_CHECKING:
|
||||
|
||||
IOERROR = "reading the file was denied by the server os; either due to filesystem permissions, selinux, apparmor, or similar:\n%r"
|
||||
|
||||
IMG_EXTS = set(["webp", "jpg", "png"])
|
||||
IMG_EXTS = set(["webp", "jpg", "png", "jxl"])
|
||||
|
||||
|
||||
class ThumbCli(object):
|
||||
@@ -54,6 +54,7 @@ class ThumbCli(object):
|
||||
# defer args.th_ff_jpg, can change at runtime
|
||||
d = next((x for x in self.args.th_dec if x in ("vips", "pil")), None)
|
||||
self.can_webp = HAVE_WEBP or d == "vips"
|
||||
self.can_jxl = HAVE_JXL or d == "vips"
|
||||
|
||||
def log(self, msg: str, c: Union[int, str] = 0) -> None:
|
||||
self.log_func("thumbcli", msg, c)
|
||||
@@ -94,7 +95,7 @@ class ThumbCli(object):
|
||||
if rem.startswith(".hist/th/") and rem.split(".")[-1] in IMG_EXTS:
|
||||
return os.path.join(ptop, rem)
|
||||
|
||||
if fmt[:1] in "jw" and fmt != "wav":
|
||||
if fmt[:1] in "jwx" and fmt != "wav":
|
||||
sfmt = fmt[:1]
|
||||
|
||||
if sfmt == "j" and self.args.th_no_jpg:
|
||||
@@ -108,6 +109,14 @@ class ThumbCli(object):
|
||||
):
|
||||
sfmt = "j"
|
||||
|
||||
if sfmt == "x":
|
||||
if (
|
||||
self.args.th_no_jxl
|
||||
or (is_img and not self.can_jxl)
|
||||
or (self.args.th_ff_jpg and (not is_img or preferred == "ff"))
|
||||
):
|
||||
sfmt = "j"
|
||||
|
||||
vf_crop = dbv.flags["crop"]
|
||||
vf_th3x = dbv.flags["th3x"]
|
||||
|
||||
|
||||
@@ -49,8 +49,9 @@ HAVE_PILF = False
|
||||
HAVE_HEIF = False
|
||||
HAVE_AVIF = False
|
||||
HAVE_WEBP = False
|
||||
HAVE_JXL = False
|
||||
|
||||
EXTS_TH = set(["jpg", "webp", "png"])
|
||||
EXTS_TH = set(["jpg", "webp", "jxl", "png"])
|
||||
EXTS_AC = set(["opus", "owa", "caf", "mp3", "flac", "wav"])
|
||||
EXTS_SPEC_SAFE = set("aif aiff flac mp3 opus wav".split())
|
||||
|
||||
@@ -132,6 +133,20 @@ try:
|
||||
except:
|
||||
pass
|
||||
|
||||
try:
|
||||
if os.environ.get("PRTY_NO_PIL_JXL"):
|
||||
raise Exception()
|
||||
|
||||
try:
|
||||
import pillow_jxl
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
Image.new("RGB", (2, 2)).save(BytesIO(), format="jxl")
|
||||
HAVE_JXL = True
|
||||
except:
|
||||
pass
|
||||
|
||||
try:
|
||||
if os.environ.get("PRTY_NO_PIL_HEIF"):
|
||||
raise Exception()
|
||||
@@ -203,7 +218,7 @@ def thumb_path(histpath: str, rem: str, mtime: float, fmt: str, ffa: set[str]) -
|
||||
|
||||
# spectrograms are never cropped; strip fullsize flag
|
||||
ext = rem.split(".")[-1].lower()
|
||||
if ext in ffa and fmt[:2] in ("wf", "jf"):
|
||||
if ext in ffa and fmt[:2] in ("wf", "jf", "xf"):
|
||||
fmt = fmt.replace("f", "")
|
||||
|
||||
dcache = th_dir_cache
|
||||
@@ -225,7 +240,7 @@ def thumb_path(histpath: str, rem: str, mtime: float, fmt: str, ffa: set[str]) -
|
||||
cat = "ac"
|
||||
else:
|
||||
fc = fmt[:1]
|
||||
fmt = "webp" if fc == "w" else "png" if fc == "p" else "jpg"
|
||||
fmt = "webp" if fc == "w" else "png" if fc == "p" else "jxl" if fc == "x" else "jpg"
|
||||
cat = "th"
|
||||
|
||||
return "%s/%s/%s/%s.%x.%s" % (histpath, cat, rd, fn, int(mtime), fmt)
|
||||
@@ -304,6 +319,10 @@ class ThumbSrv(object):
|
||||
for f in "webp".split(" "):
|
||||
self.fmt_pil.discard(f)
|
||||
|
||||
if not HAVE_JXL:
|
||||
for f in "jxl".split(" "):
|
||||
self.fmt_pil.discard(f)
|
||||
|
||||
self.thumbable: set[str] = set()
|
||||
|
||||
if "pil" in self.args.th_dec:
|
||||
|
||||
@@ -36,7 +36,7 @@ window.baguetteBox = (function () {
|
||||
touchFlag = false, // busy
|
||||
scrollCSS = ['', ''],
|
||||
scrollTimer = 0,
|
||||
re_i = /^[^?]+\.(a?png|avif|bmp|gif|heif|jfif|jpe?g|svg|tiff?|webp)(\?|$)/i,
|
||||
re_i = /^[^?]+\.(a?png|avif|bmp|gif|heif|jfif|jpe?g|jxl|svg|tiff?|webp)(\?|$)/i,
|
||||
re_v = /^[^?]+\.(webm|mkv|mp4|m4v|mov)(\?|$)/i,
|
||||
re_cbz = /^[^?]+\.(cbz)(\?|$)/i,
|
||||
cbz_pics = ["png", "jpg", "jpeg", "gif", "bmp", "tga", "tif", "tiff", "webp", "avif"],
|
||||
|
||||
@@ -1154,22 +1154,26 @@ function read_sbw() {
|
||||
onresize100.add(read_sbw, true);
|
||||
|
||||
|
||||
var have_webp = sread('have_webp');
|
||||
(function () {
|
||||
if (have_webp !== null)
|
||||
function check_image_support(format, uri) {
|
||||
var cached
|
||||
= window['have_' + format]
|
||||
= sread('have_' + format);
|
||||
if (cached !== null)
|
||||
return;
|
||||
|
||||
var img = new Image();
|
||||
img.onload = function () {
|
||||
have_webp = img.width > 0 && img.height > 0;
|
||||
swrite('have_webp', 'ya');
|
||||
window['have_' + format] = img.width > 0 && img.height > 0;
|
||||
swrite('have_' + format, 'ya');
|
||||
};
|
||||
img.onerror = function () {
|
||||
have_webp = false;
|
||||
swrite('have_webp', '');
|
||||
window['have_' + format] = false;
|
||||
swrite('have_' + format, '');
|
||||
};
|
||||
img.src = "data:image/webp;base64,UklGRhoAAABXRUJQVlA4TA0AAAAvAAAAEAcQERGIiP4HAA==";
|
||||
})();
|
||||
img.src = uri;
|
||||
}
|
||||
check_image_support('webp', "data:image/webp;base64,UklGRhoAAABXRUJQVlA4TA0AAAAvAAAAEAcQERGIiP4HAA==");
|
||||
check_image_support('jxl', "data:image/jxl;base64,/woIAAAMABKIAgC4AF3lEgA=");
|
||||
|
||||
|
||||
function set_files_html(html) {
|
||||
@@ -5685,7 +5689,7 @@ var thegrid = (function () {
|
||||
};
|
||||
|
||||
function loadgrid() {
|
||||
if (have_webp === null)
|
||||
if (have_webp === null || have_jxl === null)
|
||||
return setTimeout(loadgrid, 50);
|
||||
|
||||
r.setvis();
|
||||
@@ -5738,7 +5742,11 @@ var thegrid = (function () {
|
||||
ihref = ext_th[ext] || ext_th[ext0];
|
||||
}
|
||||
else if (r.thumbs) {
|
||||
ihref = addq(ihref, 'th=' + (have_webp ? 'w' : 'j'));
|
||||
ihref = addq(ihref, 'th=' + (
|
||||
have_jxl ? 'x' :
|
||||
have_webp ? 'w' :
|
||||
'j'
|
||||
));
|
||||
if (!r.crop)
|
||||
ihref += 'f';
|
||||
if (r.x3)
|
||||
|
||||
@@ -1015,7 +1015,7 @@
|
||||
ohead: set-cooke # hint; default is unset
|
||||
|
||||
# dont log URLs matching regex RE
|
||||
lf-url: ^/\.cpr/|[?&]th=[wjp]|/\.(_|ql_|DS_Store$|localized$) # default
|
||||
lf-url: ^/\.cpr/|[?&]th=[xwjp]|/\.(_|ql_|DS_Store$|localized$) # default
|
||||
|
||||
###000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\
|
||||
###// admin panel options \\000000000000000000000000000000000000000000000000000000000000000000\
|
||||
@@ -1091,6 +1091,9 @@
|
||||
# disable webp output
|
||||
th-no-webp
|
||||
|
||||
# disable jpeg-xl output
|
||||
th-no-jxl
|
||||
|
||||
# force jpg output for video thumbs (avoids issues on some FFmpeg builds)
|
||||
th-ff-jpg
|
||||
|
||||
|
||||
Reference in New Issue
Block a user