diff --git a/.gitignore b/.gitignore index 2f48b149..302136b9 100644 --- a/.gitignore +++ b/.gitignore @@ -29,6 +29,10 @@ copyparty.egg-info/ copyparty/res/COPYING.txt copyparty/web/deps/ srv/ +scripts/docker/base/b/ +scripts/docker/base/cver* +scripts/docker/base/test-aac/ +scripts/docker/base/whl/ scripts/docker/i/ scripts/deps-docker/uncomment.py contrib/package/arch/pkg/ diff --git a/README.md b/README.md index e0dc0801..ca13cfa6 100644 --- a/README.md +++ b/README.md @@ -491,6 +491,9 @@ upgrade notes * thumbnails are broken (you get a colorful square which says the filetype instead) * you need to install `FFmpeg` or `Pillow`; see [thumbnails](#thumbnails) +* thumbnails are broken, specifically for photos and videos taken by iphones + * the [docker image](https://github.com/9001/copyparty/blob/hovudstraum/scripts/docker) and [bootable flashdrive](https://a.ocv.me/pub/stuff/edcd001/enterprise-edition/) are not able to read heif/heic images and h265/HEVC video due to [legal reasons](docs/bad-codecs.md) + * thumbnails are broken (some images appear, but other files just get a blank box, and/or the broken-image placeholder) * probably due to a reverse-proxy messing with the request URLs and stripping the query parameters (`?th=w`), so check your URL rewrite rules * could also be due to incorrect caching settings in reverse-proxies and/or CDNs, so make sure that nothing is set to ignore the query string @@ -760,8 +763,7 @@ to show `/icons/exe.png` and `/icons/elf.gif` as the thumbnail for all `.exe` an * be careful with svg; chrome will crash if you have too many unique svg files showing on the same page (the limit is 250 or so) -- showing the same handful of svg files thousands of times is ok however note: -* heif/heifs/heic/heics images usually require the `libvips` [optional dependency](#optional-dependencies) (available in the `iv` docker image, `withFastThumbnails` in nixos) - * technical trivia: FFmpeg has basic support for tiled heic as of v7.0; need `-show_stream_groups` for correct resolution +* heif/heifs/heic/heics images usually require the `libvips` [optional dependency](#optional-dependencies) but this is not possible with the docker-images due to [legal reasons](docs/bad-codecs.md) config file example: @@ -1437,7 +1439,7 @@ general usage: on macos, connect from finder: * [Go] -> [Connect to Server...] -> http://192.168.123.1:3923/ -to upload or edit files with WebDAV clients, enable the `daw` volflag (because most WebDAV clients expect this) and give your account the delete-permission. This avoids getting several copies of the same file on the server. HOWEVER: This will also make all PUT-uploads overwrite existing files if the user has delete-access, so use with caution. +to be able to edit existing files, the client must have the Delete-permission, and some webdav clients will also require the [daw](https://copyparty.eu/cli/#g-daw) volflag or global-option (not necessary if the client sends the `x-oc-mtime` header). Without `daw`, those clients will fail to modify existing files and instead create new copies with names like `notes.txt-1771978661.726032-3i9GPghL.txt`. **NOTE:** Enabling `daw` will also make all PUT-uploads overwrite existing files if the user has delete-access, so use with caution. Another alternative is the [dav-port](https://copyparty.eu/cli/#g-dav-port) option > note: if you have enabled [IdP authentication](#identity-providers) then that may cause issues for some/most webdav clients; see [the webdav section in the IdP docs](https://github.com/9001/copyparty/blob/hovudstraum/docs/idp.md#connecting-webdav-clients) diff --git a/contrib/package/arch/PKGBUILD b/contrib/package/arch/PKGBUILD index 41ae53a0..0d38c99d 100644 --- a/contrib/package/arch/PKGBUILD +++ b/contrib/package/arch/PKGBUILD @@ -3,7 +3,7 @@ # NOTE: You generally shouldn't use this PKGBUILD on Arch, as it is mainly for testing purposes. Install copyparty using pacman instead. pkgname=copyparty -pkgver="1.20.7" +pkgver="1.20.10" pkgrel=1 pkgdesc="File server with accelerated resumable uploads, dedup, WebDAV, SFTP, FTP, TFTP, zeroconf, media indexer, thumbnails++" arch=("any") @@ -24,7 +24,7 @@ optdepends=("ffmpeg: thumbnails for videos, images (slower) and audio, music tag ) source=("https://github.com/9001/${pkgname}/releases/download/v${pkgver}/${pkgname}-${pkgver}.tar.gz") backup=("etc/${pkgname}/copyparty.conf" ) -sha256sums=("a05ae0226c6171551d0af0e8dbbbbf6d6c32fa19ec06446b5f56726dcce1b8c4") +sha256sums=("a651df2ab768ebdf2f41b7ff1e1fec788ae8a34848ce228c189f2d0f566c9fd9") build() { cd "${srcdir}/${pkgname}-${pkgver}/copyparty/web" diff --git a/contrib/package/makedeb-mpr/PKGBUILD b/contrib/package/makedeb-mpr/PKGBUILD index 7551b575..1b922f65 100644 --- a/contrib/package/makedeb-mpr/PKGBUILD +++ b/contrib/package/makedeb-mpr/PKGBUILD @@ -2,7 +2,7 @@ pkgname=copyparty -pkgver=1.20.7 +pkgver=1.20.10 pkgrel=1 pkgdesc="File server with accelerated resumable uploads, dedup, WebDAV, SFTP, FTP, TFTP, zeroconf, media indexer, thumbnails++" arch=("any") @@ -21,7 +21,7 @@ optdepends=("ffmpeg: thumbnails for videos, images (slower) and audio, music tag ) source=("https://github.com/9001/${pkgname}/releases/download/v${pkgver}/${pkgname}-${pkgver}.tar.gz") backup=("/etc/${pkgname}.d/init" ) -sha256sums=("a05ae0226c6171551d0af0e8dbbbbf6d6c32fa19ec06446b5f56726dcce1b8c4") +sha256sums=("a651df2ab768ebdf2f41b7ff1e1fec788ae8a34848ce228c189f2d0f566c9fd9") build() { cd "${srcdir}/${pkgname}-${pkgver}/copyparty/web" diff --git a/contrib/package/nix/copyparty/pin.json b/contrib/package/nix/copyparty/pin.json index e4a3ff48..b5e76c75 100644 --- a/contrib/package/nix/copyparty/pin.json +++ b/contrib/package/nix/copyparty/pin.json @@ -1,5 +1,5 @@ { - "url": "https://github.com/9001/copyparty/releases/download/v1.20.7/copyparty-1.20.7.tar.gz", - "version": "1.20.7", - "hash": "sha256-oFrgImxhcVUdCvDo27u/bWwy+hnsBkRrX1ZybczhuMQ=" + "url": "https://github.com/9001/copyparty/releases/download/v1.20.10/copyparty-1.20.10.tar.gz", + "version": "1.20.10", + "hash": "sha256-plHfKrdo698vQbf/Hh/seIroo0hIziKMGJ8tD1Zsn9k=" } \ No newline at end of file diff --git a/copyparty/__main__.py b/copyparty/__main__.py index fc843b87..eda7e104 100644 --- a/copyparty/__main__.py +++ b/copyparty/__main__.py @@ -1482,7 +1482,7 @@ def add_ftp(ap): def add_webdav(ap): ap2 = ap.add_argument_group("WebDAV options") - ap2.add_argument("--daw", action="store_true", help="enable full write support, even if client may not be webdav. \033[1;31mWARNING:\033[0m This has side-effects -- PUT-operations will now \033[1;31mOVERWRITE\033[0m existing files, rather than inventing new filenames to avoid loss of data. You might want to instead set this as a volflag where needed. By not setting this flag, uploaded files can get written to a filename which the client does not expect (which might be okay, depending on client)") + ap2.add_argument("--daw", action="store_true", help="enable full write support, even if client may not be webdav. Some webdav clients need this option for editing existing files; not necessary for clients that send the 'x-oc-mtime' header. Regardless, the delete-permission must always be given. \033[1;31mWARNING:\033[0m This has side-effects -- PUT-operations will now \033[1;31mOVERWRITE\033[0m existing files, rather than inventing new filenames to avoid loss of data. You might want to instead set this as a volflag where needed. By not setting this flag, uploaded files can get written to a filename which the client does not expect (which might be okay, depending on client)") ap2.add_argument("--dav-inf", action="store_true", help="allow depth:infinite requests (recursive file listing); extremely server-heavy but required for spec compliance -- luckily few clients rely on this") ap2.add_argument("--dav-mac", action="store_true", help="disable apple-garbage filter -- allow macos to create junk files (._* and .DS_Store, .Spotlight-*, .fseventsd, .Trashes, .AppleDouble, __MACOS)") ap2.add_argument("--dav-rt", action="store_true", help="show symlink-destination's lastmodified instead of the link itself; always enabled for recursive listings (volflag=davrt)") diff --git a/copyparty/__version__.py b/copyparty/__version__.py index 8a42927c..e2db10bb 100644 --- a/copyparty/__version__.py +++ b/copyparty/__version__.py @@ -1,8 +1,8 @@ # coding: utf-8 -VERSION = (1, 20, 7) +VERSION = (1, 20, 10) CODENAME = "sftp is fine too" -BUILD_DT = (2026, 2, 14) +BUILD_DT = (2026, 2, 25) S_VERSION = ".".join(map(str, VERSION)) S_BUILD_DT = "{0:04d}-{1:02d}-{2:02d}".format(*BUILD_DT) diff --git a/copyparty/authsrv.py b/copyparty/authsrv.py index da4774b4..503e3dd3 100644 --- a/copyparty/authsrv.py +++ b/copyparty/authsrv.py @@ -147,8 +147,11 @@ class AXS(object): class Lim(object): - def __init__(self, log_func: Optional["RootLogger"]) -> None: + def __init__( + self, args: argparse.Namespace, log_func: Optional["RootLogger"] + ) -> None: self.log_func = log_func + self.use_scandir = not args.no_scandir self.reg: Optional[dict[str, dict[str, Any]]] = None # up2k registry @@ -190,12 +193,12 @@ class Lim(object): self.log_func("up-lim", msg, c) def set_rotf(self, fmt: str, tz: str) -> None: - self.rotf = fmt + self.rotf = fmt.rstrip("/\\") if tz != "UTC": from zoneinfo import ZoneInfo self.rotf_tz = ZoneInfo(tz) - r = re.escape(fmt).replace("%Y", "[0-9]{4}").replace("%j", "[0-9]{3}") + r = re.escape(self.rotf).replace("%Y", "[0-9]{4}").replace("%j", "[0-9]{3}") r = re.sub("%[mdHMSWU]", "[0-9]{2}", r) self.rot_re = re.compile("(^|/)" + r + "$") @@ -312,12 +315,17 @@ class Lim(object): return ret, d def dive(self, path: str, lvs: int) -> Optional[str]: - items = bos.listdir(path) - if not lvs: # at leaf level + items = statdir(self.log_func, self.use_scandir, False, path, True) + items = [ + x + for x in items + if not stat.S_ISDIR(x[1].st_mode) and not x[0].endswith(".PARTIAL") + ] return None if len(items) >= self.rotn else "" + items = bos.listdir(path) dirs = [int(x) for x in items if x and all(y in "1234567890" for y in x)] dirs.sort() @@ -2284,7 +2292,7 @@ class AuthSrv(object): vol.flags["zipmax"] = True for vol in vfs.all_vols.values(): - lim = Lim(self.log_func) + lim = Lim(self.args, self.log_func) use = False if vol.flags.get("nosub"): diff --git a/copyparty/httpcli.py b/copyparty/httpcli.py index 3f84bb17..472c270c 100644 --- a/copyparty/httpcli.py +++ b/copyparty/httpcli.py @@ -178,6 +178,7 @@ RE_HTTP1 = re.compile(r"(GET|HEAD|POST|PUT) [^ ]+ HTTP/1.1$") RE_HR = re.compile(r"[<>\"'&]") RE_MDV = re.compile(r"(.*)\.([0-9]+\.[0-9]{3})(\.[Mm][Dd])$") RE_RSS_KW = re.compile(r"(\{[^} ]+\})") +RE_SETCK = re.compile(r"[^0-9a-z=]") UPARAM_CC_OK = set("doc move tree".split()) @@ -649,8 +650,8 @@ class HttpCli(object): if len(zso) > self.args.cookie_cmax: self.loud_reply("cookie header too big", status=400) return False - zsll = [x.split("=", 1) for x in zso.split(";") if "=" in x] - cookies = {k.strip(): unescape_cookie(zs) for k, zs in zsll} + zsll = [x.lstrip().split("=", 1) for x in zso.split(";") if "=" in x] + cookies = {k.rstrip(): unescape_cookie(zs.strip(), k) for k, zs in zsll} cookie_pw = cookies.get("cppws" if self.is_https else "cppwd") or "" if "b" in cookies and "b" not in uparam: uparam["b"] = cookies["b"] @@ -2575,6 +2576,10 @@ class HttpCli(object): vfs.flags.get("daw") or "replace" in self.headers or "x-oc-mtime" in self.headers + or ( + self.args.dav_port + and self.args.dav_port == self.s.getsockname()[1] + ) ) ) or ( not bos.path.exists(os.path.join(fdir, tnam)) @@ -5628,7 +5633,10 @@ class HttpCli(object): return True def setck(self) -> bool: - k, v = self.uparam["setck"].split("=", 1) + zs = self.uparam["setck"] + if len(zs) > 9 or RE_SETCK.search(zs): + raise Pebkac(400, "illegal value") + k, v = zs.split("=") t = 0 if v in ("", "x") else 86400 * 299 ck = gencookie(k, v, self.args.R, True, False, t) self.out_headerlist.append(("Set-Cookie", ck)) diff --git a/copyparty/svchub.py b/copyparty/svchub.py index ab02cbb3..929392f7 100644 --- a/copyparty/svchub.py +++ b/copyparty/svchub.py @@ -1439,7 +1439,7 @@ class SvcHub(object): self.log("root", "ssdp startup failed;\n" + min_ex(), 3) def reload(self, rescan_all_vols: bool, up2k: bool) -> str: - t = "config has been reloaded" + t = "users, volumes, and volflags have been reloaded" with self.reload_mutex: self.log("root", "reloading config") self.asrv.reload(9 if up2k else 4) @@ -1449,6 +1449,7 @@ class SvcHub(object): t += "; volumes are now reinitializing" else: self.log("root", "reload done") + t += "\n\nchanges to global options (if any) require a restart of copyparty to take effect" self.broker.reload() return t diff --git a/copyparty/th_srv.py b/copyparty/th_srv.py index 7d461579..9cff845a 100644 --- a/copyparty/th_srv.py +++ b/copyparty/th_srv.py @@ -6,6 +6,7 @@ import io import logging import os import re +import shlex import shutil import subprocess as sp import tempfile @@ -554,11 +555,12 @@ class ThumbSrv(object): conv_ok = True break except Exception as ex: + r321 = getattr(ex, "returncode", 0) == 321 msg = "%s could not create thumbnail of %r\n%s" - msg = msg % (fun.__name__, abspath, min_ex()) + msg = msg % (fun.__name__, abspath, ex if r321 else min_ex()) c: Union[str, int] = 1 if " 5000: txt = txt[:2500] + "...\nff: [...]\nff: ..." + txt[-2500:] - self.log(t + txt, c=c) + try: + zs = shlex.join([x.decode("utf-8", "replace") for x in cmd]) + except: + zs = "'" + (b"' '".join(cmd2)).decode("utf-8", "replace") + "'" + + self.log("%scmd: %s\n%s" % (t, zs, txt), c=c) raise sp.CalledProcessError(ret, (cmd[0], b"...", cmd[-1])) def conv_waves(self, abspath: str, tpath: str, fmt: str, vn: VFS) -> None: diff --git a/copyparty/up2k.py b/copyparty/up2k.py index 7d525fa5..4681c223 100644 --- a/copyparty/up2k.py +++ b/copyparty/up2k.py @@ -3260,6 +3260,21 @@ class Up2k(object): dst = djoin(cj["ptop"], cj["prel"], cj["name"]) vsrc = djoin(job["vtop"], job["prel"], job["name"]) vsrc = vsrc.replace("\\", "/") # just for prints anyways + if vfs.lim: + dst, cj["prel"] = vfs.lim.all( + cj["addr"], + cj["prel"], + cj["size"], + cj["ptop"], + djoin(cj["ptop"], cj["prel"]), + self.hub.broker, + reg, + "up2k._get_volsize", + ) + bos.makedirs(dst, vf=vfs.flags) + vfs.lim.nup(cj["addr"]) + vfs.lim.bup(cj["addr"], cj["size"]) + if "done" not in job: self.log("unfinished:\n %r\n %r" % (src, dst)) err = "partial upload exists at a different location; please resume uploading here instead:\n" diff --git a/copyparty/util.py b/copyparty/util.py index 110b49dd..13113199 100644 --- a/copyparty/util.py +++ b/copyparty/util.py @@ -3439,8 +3439,10 @@ def rmdirs_up(top: str, stop: str) -> tuple[list[str], list[str]]: return [top] + ok, ng -def unescape_cookie(orig: str) -> str: +def unescape_cookie(orig: str, name: str) -> str: # mw=idk; doot=qwe%2Crty%3Basd+fgh%2Bjkl%25zxc%26vbn # qwe,rty;asd fgh+jkl%zxc&vbn + if not name.startswith("cppw"): + orig = orig[:3] ret = [] esc = "" for ch in orig: diff --git a/copyparty/web/browser.js b/copyparty/web/browser.js index 5428f5c2..467d6035 100644 --- a/copyparty/web/browser.js +++ b/copyparty/web/browser.js @@ -332,8 +332,7 @@ if (1) "mt_ssvt": "volume threshold (0-255)\">vol", "mt_ssts": "active threshold (% of track, start)\">start", "mt_sste": "active threshold (% of track, end)\">end", - "mt_ssrt": "volume/speed ramp up/down time\">fade", - "mt_sssm": "playback speed multiplier\">ffwd", + "mt_sssm": "playback speed multiplier (range: 0.15 to 8)\">ffwd", "mb_play": "play", "mm_hashplay": "play this audio file?", @@ -2718,8 +2717,8 @@ var afilt = (function () { "drcv": [-24, 30, 12, 0.01, 0.25], "drch": ['tresh', 'knee', 'ratio', 'atk', 'rls'], "drck": ['threshold', 'knee', 'ratio', 'attack', 'release'], - "sscl": [L.mt_ssvt, L.mt_ssts, L.mt_sste, L.mt_ssrt, L.mt_sssm], - "sscv": [1, 5, 5, 5.0, 0.2], + "sscl": [L.mt_ssvt, L.mt_ssts, L.mt_sste, L.mt_sssm], + "sscv": [1, 5, 5, 5.0], "drcn": null, "filters": [], "filterskip": [], @@ -10042,8 +10041,8 @@ var mpss = (function() { vthresh: afilt.sscv[0], sthresh: afilt.sscv[1], etresh: afilt.sscv[2], - sspeed: afilt.sscv[3], - rspeed: afilt.sscv[4], + sspeed: clamp(afilt.sscv[3], 0.15, 8.0), + rspeed: 0.2, loopInterval: 25, }; return true; diff --git a/copyparty/web/tl/chi.js b/copyparty/web/tl/chi.js index d7a7d0e2..b4d434f8 100644 --- a/copyparty/web/tl/chi.js +++ b/copyparty/web/tl/chi.js @@ -325,7 +325,6 @@ Ls.chi = { "mt_ssvt": "音量阈值 (0-255)\">音量", //m "mt_ssts": "活动阈值 (曲目百分比,开始)\">开始", //m "mt_sste": "活动阈值 (曲目百分比,结束)\">结束", //m - "mt_ssrt": "音量/速度 渐变时间\">攻", //m "mt_sssm": "播放速度倍率\">快进", //m "mb_play": "播放", diff --git a/copyparty/web/tl/cze.js b/copyparty/web/tl/cze.js index 814abb97..07a8035d 100644 --- a/copyparty/web/tl/cze.js +++ b/copyparty/web/tl/cze.js @@ -329,7 +329,6 @@ Ls.cze = { "mt_ssvt": "prahová hlasitost (0-255)\">hl", //m "mt_ssts": "aktivní práh (% stopy, začátek)\">zač", //m "mt_sste": "aktivní práh (% stopy, konec)\">kon", //m - "mt_ssrt": "doba náběhu/útlumu hlasitosti/rychlosti\">fade", //m "mt_sssm": "násobič rychlosti přehrávání\">rych", //m "mb_play": "přehrát", diff --git a/copyparty/web/tl/deu.js b/copyparty/web/tl/deu.js index 3f1dfa21..3d9cb9ac 100644 --- a/copyparty/web/tl/deu.js +++ b/copyparty/web/tl/deu.js @@ -325,7 +325,6 @@ Ls.deu = { "mt_ssvt": "Lautstärkeschwelle (0-255)\">laut", //m "mt_ssts": "Aktiv-Schwelle (% Titel, Anfang)\">anf", //m "mt_sste": "Aktiv-Schwelle (% Titel, Ende)\">end", //m - "mt_ssrt": "Ein/Ausblendzeit Lautstärke/Geschwindigkeit\">fade", //m "mt_sssm": "Wiedergabegeschwindigkeits-Multiplikator\">vor", //m "mb_play": "Abspielen", diff --git a/copyparty/web/tl/epo.js b/copyparty/web/tl/epo.js index 3e09ce6b..ea3fcdf6 100644 --- a/copyparty/web/tl/epo.js +++ b/copyparty/web/tl/epo.js @@ -325,7 +325,6 @@ Ls.epo = { "mt_ssvt": "maksimuma laŭtnivelo por silentsaltado (0-255)\">laŭt", "mt_ssts": "komenca intervalo por silentsaltado (% de trakolongo)\">ek%", "mt_sste": "fina intervalo por silentsaltado (% de trakolongo)\">fin%", - "mt_ssrt": "tempo por laŭgrada ŝanĝo de laŭteco/rapideco dum silentsaltado\">ŝanĝ⏱️", "mt_sssm": "multiplikado de ludrapideco dum silentsaltado\">×rapid", "mb_play": "ludi", diff --git a/copyparty/web/tl/fin.js b/copyparty/web/tl/fin.js index cc90d9db..455d77b5 100644 --- a/copyparty/web/tl/fin.js +++ b/copyparty/web/tl/fin.js @@ -84,8 +84,8 @@ Ls.fin = { ["M", "sulje tekstitiedosto"], ["E", "muokkaa tekstitiedostoa"], ["S", "valitse tiedosto (leikkausta/kopiointia/uudelleennimeämistä varten)"], - ["Y", "lataa tekstitiedosto"], //m - ["⇧ J", "kaunista json"], //m + ["Y", "lataa tekstitiedosto"], + ["⇧ J", "muotoile/siisti json"], ] ], @@ -111,14 +111,14 @@ Ls.fin = { "gou": 'ylempi hakemisto">ylös', "gon": 'seuraava hakemisto">seur', "logout": "Kirjaudu ulos ", - "login": "Kirjaudu sisään", //m + "login": "Kirjaudu sisään", "access": " -oikeudet", "ot_close": "sulje alavalikko", "ot_search": "`etsi tiedostoja ominaisuuksien, tiedostopolun tai -nimen, musiikkitägien tai näiden yhdistelmän perusteella$N$N`foo bar` = täytyy sisältää sekä «foo» että «bar»,$N`foo -bar` = täytyy sisältää «foo» mutta ei «bar»,$N`^yana .opus$` = alkaa «yana» ja on «opus»-tiedosto$N`"try unite"` = sisältää täsmälleen «try unite»$N$Npäivämäärän muoto on iso-8601, kuten$N`2009-12-31` tai `2020-09-12 23:30:00`", "ot_unpost": "unpost: poista viimeaikaiset tai keskeytä keskeneräiset lataukset", "ot_bup": "bup: tiedostojen 'perus'lähetysohjelma, tukee jopa netscape 4.0", "ot_mkdir": "mkdir: luo uusi hakemisto", - "ot_md": "new-file: luo uusi tekstitiedosto", //m + "ot_md": "new-file: luo uusi tekstitiedosto", "ot_msg": "msg: lähetä viesti palvelinlokiin", "ot_mp": "mediasoittimen asetukset", "ot_cfg": "asetukset", @@ -127,7 +127,7 @@ Ls.fin = { "ot_noie": 'Suosittelemme käyttämään uudempaa selainta.', "ab_mkdir": "luo hakemisto", - "ab_mkdoc": "luo tekstitiedosto", //m + "ab_mkdoc": "luo tekstitiedosto", "ab_msg": "lähetä viesti palvelinlokiin", "ay_path": "siirry hakemistoihin", @@ -155,7 +155,7 @@ Ls.fin = { "ul_par": "rinnakkaislatausten lkm:", "ut_rand": "satunnaisgeneroidut tiedostonimet", "ut_u2ts": "kopioi viimeksi muokattu aikaleima$Ntiedostojärjestelmästäsi palvelimelle\">📅", - "ut_ow": "korvaa olemassa olevat tiedostot palvelimella?$N🛡️: ei koskaan (luo sen sijaan uuden tiedostonimen)$N🕒: korvaa jos palvelintiedosto on vanhempi kuin omasi$N♻️: korvaa aina jos tiedostot ovat erilaisia$N⏭️: ohita kaikki olemassa olevat tiedostot ehdottomasti", //m + "ut_ow": "korvaa olemassa olevat tiedostot palvelimella?$N🛡️: ei koskaan (luo sen sijaan uuden tiedostonimen)$N🕒: korvaa jos palvelintiedosto on vanhempi kuin omasi$N♻️: korvaa aina jos tiedostot ovat erilaisia$N⏭️: ohita kaikki olemassa olevat tiedostot ehdottomasti", "ut_mt": "jatka muiden tiedostojen tiivisteiden laskemista latauksen aikana$N$Nkannattanee poistaa käytöstä, mikäli prosessori tai kovalevy on vanhempaa mallia", "ut_ask": 'kysy vahvistusta ennen latauksen aloittamista">💭', "ut_pot": "paranna latausnopeutta hitailla laitteilla$Nvähentämällä käyttöliittymän monimutkaisuutta", @@ -206,7 +206,7 @@ Ls.fin = { "u_nav_b": 'TiedostojaYksi hakemisto', "cl_opts": "asetukset", - "cl_hfsz": "tiedostokoko", //m + "cl_hfsz": "tiedostokoko", "cl_themes": "teema", "cl_langs": "kieli", "cl_ziptype": "hakemiston pakkaustyyppi", @@ -220,14 +220,14 @@ Ls.fin = { "cl_reset": "palauta", "cl_hpick": "napauta sarakeotsikoita piilottaaksesi alla olevassa taulukossa", "cl_hcancel": "sarakkeiden piilotus peruttu", - "cl_rcm": "hiiren oikean painikkeen valikko", //m + "cl_rcm": "hiiren pikavalikko", "ct_grid": '田 kuvanäkymä', "ct_ttips": '◔ ◡ ◔">ℹ️ vihjelaatikot', "ct_thumb": 'valitse kuvakkeiden / pienoiskuvien välillä kuvanäkymässä $NPikanäppäin: T">🖼️ pienoiskuvat', "ct_csel": 'käytä CTRL ja SHIFT tiedostojen valintaan kuvanäkymässä">valitse', "ct_dsel": 'käytä aluevalintaa tiedostojen valintaan kuvanäkymässä">aluevalinta', - "ct_dl": 'pakota lataus (älä näytä upotettuna), kun tiedostoa napsautetaan">dl', //m + "ct_dl": 'pakota lataus (älä näytä upotettuna), kun tiedostoa klikataan">dl', "ct_ihop": 'kun kuvakatselin suljetaan, vieritä alas viimeksi katsottuun tiedostoon">g⮯', "ct_dots": 'näytä piilotetut tiedostot (jos palvelin sallii)">piilotiedostot', "ct_qdel": 'kysy vahvistusta vain kerran tiedostoja poistaessa">qdel', @@ -264,8 +264,8 @@ Ls.fin = { "cdt_lim": "tiedostojen enimmäismäärä näytettäväksi hakemistossa", "cdt_ask": "sivun lopussa, sen sijaan että lataa $Nautomaattisesti lisää tiedostoja, kysy mitä tehdä", "cdt_hsort": "`kuinka monta lajittelusääntöä (`,sorthref`) sisällyttää media-URL:eihin. Tämän asettaminen nollaan jättää myös huomioimatta media-linkeissä sisällytetyt lajittelusäännöt kun napsautat niitä", - "cdt_ren": "ota käyttöön mukautettu valikko, tavallinen valikko on käytettävissä painamalla shift ja napsauttamalla oikealla\">aktivoi", //m - "cdt_rdb": "näytä tavallinen oikean painikkeen valikko, kun mukautettu on jo auki ja oikeaa painiketta painetaan uudelleen\">x2", //m + "cdt_ren": "ota käyttöön mukautettu pikavalikko, tavallinen valikko on käytettävissä painamalla shift ja napsauttamalla hiiren oikeanpuoleisella painikkeella\">aktivoi", + "cdt_rdb": "näytä tavallinen pikavalikko, kun mukautettu on jo auki ja oikeanpuoleista painiketta painetaan uudelleen\">x2", "tt_entree": "näytä navigointipaneeli$NPikanäppäin: B", "tt_detree": "näytä linkkipolku$NPikanäppäin: B", @@ -283,7 +283,7 @@ Ls.fin = { "ml_tint": "sävy", "ml_eq": "taajuuskorjain", "ml_drc": "dynaaminen alueen kompressori", - "ml_ss": "ohita hiljaisuus", //m + "ml_ss": "ohita hiljaiset kohdat", "mt_loop": "toista samaa kappaletta\">🔁", "mt_one": "lopeta yhden toiston jälkeen\">1️⃣", @@ -312,8 +312,8 @@ Ls.fin = { "mt_c2owa": "opus-weba, iOS 17.5:lle ja uudemmille\">owa", "mt_c2caf": "opus-caf, iOS 11:lle - 17:lle\">caf", "mt_c2mp3": "käytä tätä erittäin vanhoissa laitteissa\">mp3", - "mt_c2flac": "paras äänenlaatu, mutta isot lataukset\">flac", //m - "mt_c2wav": "pakkaamaton toisto (vielä suurempi tiedosto)\">wav", //m + "mt_c2flac": "paras äänenlaatu, suuremmat latauskoot\">flac", + "mt_c2wav": "pakkaamaton toisto, suurimmat latauskoot\">wav", "mt_c2ok": "hienoa, hyvä valinta", "mt_c2nd": "tuo ei ole suositeltu formaatti laitteellesi, mutta tee miten lystäät", "mt_c2ng": "laitteesi ei näytä tukevan tätä formaattia, mutta yritetään nyt silti", @@ -321,12 +321,11 @@ Ls.fin = { "mt_tint": "taustan taso (0-100) liukupalkissa$Ntehden puskuroinnista vähemmän häiritsevän", "mt_eq": "`aktivoi taajuuskorjaimen ja vahvistussäätimen;$N$Nvahvistus `0` = normaali 100% äänenvoimakkuus (muokkaamaton)$N$Nleveys `1  ` = normaali stereo (muokkaamaton)$Nleveys `0.5` = 50% vasen-oikea ristisyöttö$Nleveys `0  ` = mono$N$Nvahvistus `-0.8` & leveys `10` = laulun poisto :^)$N$Nequalizerin käyttöönotto tekee saumattomista albumeista täysin saumattomia, joten jätä se päälle kaikilla arvoilla nollassa (paitsi leveys = 1) jos välität siitä", "mt_drc": "aktivoi dynaamisen alueen kompressorin; ottaa myös käyttöön taajuuskorjaimen tasapainottamaan spagettia, joten aseta kaikki EQ-kentät paitsi 'leveys' nollaan jos et halua sitä$N$Nalentaa äänenvoimakkuutta KYNNYS dB:n yläpuolella; jokaisesta SUHDE dB:stä KYNNYKSEN yli tulee 1 dB ulos, joten oletusarvot kynnys -24 ja suhde 12 tarkoittaa ettei sen pitäisi koskaan tulla kovempaa kuin -22 dB ja on turvallista nostaa equalizerin vahvistus 0.8:aan, tai jopa 1.8:aan ATK 0:lla ja valtavalla RLS:llä kuten 90 (toimii vain firefoxissa; RLS on max 1 muissa selaimissa)$N$N(katso wikipedia, he selittävät sen paljon paremmin)", - "mt_ss": "`ottaa käyttöön hiljaisuuden ohituksen; moninkertaistaa toistonopeuden `pik` lähellä kappaleen alkua/loppua, kun äänenvoimakkuus on alle `vol` ja sijainti on ensimmäisessä `alk`% tai viimeisessä `lop`%", //m - "mt_ssvt": "äänenvoimakkuuden kynnys (0-255)\">vol", //m - "mt_ssts": "aktiivikynnys (% kappale, alku)\">alk", //m - "mt_sste": "aktiivikynnys (% kappale, loppu)\">lop", //m - "mt_ssrt": "äänenvoim./nopeuden nousu/laskuaika\">häiv", //m - "mt_sssm": "toistonopeuden kerroin\">pik", //m + "mt_ss": "`ottaa käyttöön hiljaisuuden ohituksen; moninkertaistaa toistonopeuden `nop`:lla lähellä kappaleen alkua/loppua, kun äänenvoimakkuus on alle `vol` ja kappaleesta on kulunut vasta on `alk`% tai sitä on jäljellä enää `lop`%", + "mt_ssvt": "äänenvoimakkuuden kynnysarvo (0-255)\">vol", + "mt_ssts": "alkuraja (prosenttia kappaleen alusta)\">alk", + "mt_sste": "loppuraja (prosenttia kappaleen lopusta)\">lop", + "mt_sssm": "toistonopeuden kerroin (min. 0.15, max. 8))\">nop", "mb_play": "toista", "mm_hashplay": "soita tämä äänitiedosto?", @@ -343,7 +342,7 @@ Ls.fin = { "mm_eunk": "Tuntematon virhe", "mm_e404": "Kappaletta ei voitu toistaa; virhe 404: Tiedostoa ei löydy.", "mm_e403": "Kappaletta ei voitu toistaa; virhe 403: Pääsy kielletty.\n\nKokeile painaa F5 päivittääksesi, ehkä kirjauduit ulos", - "mm_e415": "Kappaletta ei voitu toistaa; virhe 415: Tiedoston muunnos epäonnistui; tarkista palvelinlokit.", //m + "mm_e415": "Kappaletta ei voitu toistaa; virhe 415: Tiedoston muunnos epäonnistui; tarkista palvelimen lokitiedostot.", "mm_e500": "Kappaletta ei voitu toistaa; virhe 500: Tarkista palvelinlokit.", "mm_e5xx": "Kappaletta ei voitu toistaa; palvelinvirhe ", "mm_nof": "ei löydy enempää äänitiedostoja lähistöltä", @@ -363,7 +362,7 @@ Ls.fin = { "f_anota": "vain {0} / {1} kohdetta valittiin;\nvalitaksesi koko hakemiston, vieritä ensin loppuun", "f_dls": 'nykyisen hakemiston tiedostolinkit on\nvaihdettu latauslinkeiksi', - "f_dl_nd": 'ohitetaan kansio (käytä zip/tar-latausta sen sijaan):\n', //m + "f_dl_nd": 'ohitetaan kansio (käytä sen sijaan zip/tar-latausta):\n', "f_partial": "Ladataksesi turvallisesti tiedoston joka on parhaillaan latautumassa, klikkaa tiedostoa jolla on sama nimi mutta ilman .PARTIAL päätettä. Paina PERUUTA tai Escape tehdäksesi tämän.\n\nOK / Enter painaminen sivuuttaa tämän varoituksen ja jatkaa .PARTIAL väliaikaistiedoston lataamista, mikä todennäköisesti antaa sinulle vioittunutta dataa.", @@ -432,16 +431,16 @@ Ls.fin = { "fcc_warn": 'kopioitiin {0} kohdetta leikepöydälle\n\nmutta: vain tämä selain-välilehti voi liittää ne\n(koska valinta on niin valtavan suuri)', "fp_apply": "käytä näitä nimiä", - "fp_skip": "ohita ristiriidat", //m + "fp_skip": "ohita ristiriitatilanteissa", "fp_ecut": "leikkaa tai kopioi ensin joitakin tiedostoja / hakemistoja liitettäväksi / siirrettäväksi\n\nhuom: voit leikata / liittää eri selain-välilehtien välillä", - "fp_ename": "{0} kohdetta ei voida siirtää tänne koska nimet ovat jo käytössä. Anna niille uudet nimet alla jatkaaksesi, tai tyhjennä nimi (\"ohita ristiriidat\") ohittaaksesi ne:", //m - "fcp_ename": "{0} kohdetta ei voida kopioida tänne koska nimet ovat jo käytössä. Anna niille uudet nimet alla jatkaaksesi, tai tyhjennä nimi (\"ohita ristiriidat\") ohittaaksesi ne:", //m + "fp_ename": "{0} kohdetta ei voida siirtää tänne koska nimet ovat jo käytössä. Anna niille uudet nimet alla jatkaaksesi, tai tyhjennä nimi (\"ohita ristiriitatilanteissa\") ohittaaksesi ne:", + "fcp_ename": "{0} kohdetta ei voida kopioida tänne koska nimet ovat jo käytössä. Anna niille uudet nimet alla jatkaaksesi, tai tyhjennä nimi (\"ohita ristiriitatilanteissa\") ohittaaksesi ne:", "fp_emore": "tiedostonimien törmäyksiä on vielä korjaamatta", "fp_ok": "siirto OK", "fcp_ok": "kopiointi OK", "fp_busy": "siirretään {0} kohdetta...\n\n{1}", "fcp_busy": "kopioidaan {0} kohdetta...\n\n{1}", - "fp_abrt": "keskeytetään...", //m + "fp_abrt": "keskeytetään...", "fp_err": "siirto epäonnistui:\n", "fcp_err": "kopiointi epäonnistui:\n", "fp_confirm": "siirrä nämä {0} kohdetta tänne?", @@ -454,8 +453,8 @@ Ls.fin = { "fcp_both_b": 'KopioiLähetä', "mk_noname": "kirjoita nimi vasemmalla olevaan tekstikenttään ennen kuin teet tuon :p", - "nmd_i1": "voit myös lisätä haluamasi tiedostopäätteen, esimerkiksi .md", //m - "nmd_i2": "voit luoda vain .{0}-tiedostoja, koska sinulla ei ole poistolupaa", //m + "nmd_i1": "voit myös lisätä haluamasi tiedostopäätteen, esimerkiksi .md", + "nmd_i2": "voit luoda vain .{0}-tiedostoja, koska sinulla ei ole “delete”-oikeutta", "tv_load": "Ladataan tekstidokumenttia:\n\n{0}\n\n{1}% ({2} / {3} Mt ladattu)", "tv_xe1": "tekstitiedoston lataaminen epäonnistui:\n\nvirhe ", @@ -466,7 +465,7 @@ Ls.fin = { "tvt_prev": "näytä edellinen dokumentti$NPikanäppäin: i\">⬆ edell", "tvt_next": "näytä seuraava dokumentti$NPikanäppäin: K\">⬇ seur", "tvt_sel": "valitse tiedosto   ( leikkausta / kopiointia / poistoa / ... varten )$NPikanäppäin: S\">val", - "tvt_j": "kaunista json$NPikanäppäin: shift-J\">j", //m + "tvt_j": "muotoile/siisti json$NPikanäppäin: shift-J\">j", "tvt_edit": "avaa tiedosto tekstieditorissa$NPikanäppäin: E\">✏️ muokkaa", "tvt_tail": "seuraa tiedoston muutoksia; näytä uudet rivit reaaliaikaisesti\">📡 seuraa", "tvt_wrap": "rivitys\">↵", @@ -651,25 +650,25 @@ Ls.fin = { "ur_um": "Valmis;\n{0} latausta OK,\n{1} latausta epäonnistui, pahoittelen", "ur_sm": "Valmis;\n{0} tiedostoa löytyi palvelimelta,\n{1} tiedostoa EI löytynyt palvelimelta", - "rc_opn": "avaa", //m - "rc_ply": "toista", //m - "rc_pla": "toista äänenä", //m - "rc_txt": "avaa tiedostoselaimessa", //m - "rc_md": "avaa tekstieditorissa", //m - "rc_dl": "Lataa", //m - "rc_zip": "Lataa arkistona", //m - "rc_cpl": "kopioi linkki", //m - "rc_del": "poista", //m - "rc_cut": "Leikkaa", //m - "rc_cpy": "kopioi", //m - "rc_pst": "Liitä", //m - "rc_rnm": "nimeä uudelleen", //m - "rc_nfo": "uusi kansio", //m - "rc_nfi": "uusi tiedosto", //m - "rc_sal": "valitse kaikki", //m - "rc_sin": "käännä valinta", //m - "rc_shf": "jaa tämä kansio", //m - "rc_shs": "jaa valinta", //m + "rc_opn": "avaa", + "rc_ply": "toista", + "rc_pla": "toista äänitiedostona", + "rc_txt": "avaa tekstinäkymässä", + "rc_md": "avaa markdown-näkymässä", + "rc_dl": "lataa", + "rc_zip": "lataa arkistona", + "rc_cpl": "kopioi linkki", + "rc_del": "poista", + "rc_cut": "leikkaa", + "rc_cpy": "kopioi", + "rc_pst": "liitä", + "rc_rnm": "nimeä uudelleen", + "rc_nfo": "uusi kansio", + "rc_nfi": "uusi tiedosto", + "rc_sal": "valitse kaikki", + "rc_sin": "käänteinen valinta", + "rc_shf": "jaa tämä kansio", + "rc_shs": "jaa valinta", "lang_set": "ladataanko sivu uudestaan kielen vaihtamiseksi?", @@ -689,11 +688,11 @@ Ls.fin = { "j1": "k304 katkaisee yhteytesi jokaisella HTTP 304:llä, mikä voi estää joitain bugisia välityspalvelimia jumittumasta/lopettamasta sivujen lataamista, mutta se myös vähentää suorituskykyä", "k1": "nollaa asetukset", "l1": "kirjaudu sisään:", - "ls3": "kirjaudu sisään", //m - "lu4": "käyttäjätunnus", //m - "lp4": "salasana", //m - "lo3": "kirjaa “{0}” ulos kaikkialta", //m - "lo2": "tämä lopettaa istunnon kaikissa selaimissa", //m + "ls3": "kirjaudu sisään", + "lu4": "käyttäjätunnus", + "lp4": "salasana", + "lo3": "kirjaa “{0}” ulos kaikkialta", + "lo2": "tämä lopettaa istunnon kaikissa selaimissa", "m1": "tervetuloa takaisin,", "n1": "404: ei löytynyt mitään  ┐( ´ -`)┌", "o1": 'tai ehkä sinulla ei vain ole käyttöoikeuksia? kokeile salasanaa tai mene kotiin', @@ -712,8 +711,8 @@ Ls.fin = { "ta1": "täytä ensin uusi salasana", "ta2": "toista vahvistaaksesi uuden salasanan:", "ta3": "löytyi kirjoitusvirhe; yritä uudelleen", - "nop": "VIRHE: Salasana ei voi olla tyhjä", //m - "nou": "VIRHE: Käyttäjänimi ja/tai salasana ei voi olla tyhjä", //m + "nop": "VIRHE: Salasana ei voi olla tyhjä", + "nou": "VIRHE: Käyttäjänimi ja/tai salasana ei voi olla tyhjä", "aa1": "saapuvat:", "ab1": "poista no304 käytöstä", "ac1": "ota no304 käyttöön", diff --git a/copyparty/web/tl/fra.js b/copyparty/web/tl/fra.js index e1b773b5..a1d2f52f 100644 --- a/copyparty/web/tl/fra.js +++ b/copyparty/web/tl/fra.js @@ -325,7 +325,6 @@ Ls.fra = { "mt_ssvt": "seuil de volume (0-255)\">vol", //m "mt_ssts": "seuil actif (% piste, début)\">deb", //m "mt_sste": "seuil actif (% piste, fin)\">fin", //m - "mt_ssrt": "temps de montée/descente volume/vitesse\">fondu", //m "mt_sssm": "multiplicateur de vitesse de lecture\">av", //m "mb_play": "lecture", diff --git a/copyparty/web/tl/grc.js b/copyparty/web/tl/grc.js index a95e2a2c..004ade1b 100644 --- a/copyparty/web/tl/grc.js +++ b/copyparty/web/tl/grc.js @@ -325,7 +325,6 @@ Ls.grc = { "mt_ssvt": "ὅριον φωνῆς (0-255)\">φων", //m "mt_ssts": "ἐνεργὸν ὅριον (% τροχιᾶς, ἀρχή)\">ἀρχ", //m "mt_sste": "ἐνεργὸν ὅριον (% τροχιᾶς, τέλος)\">τελ", //m - "mt_ssrt": "χρόνος αὔξησης/μείωσης φωνῆς/ταχύτητος\">σβέσις", //m "mt_sssm": "πολλαπλασιαστὴς ταχύτητος\">ταχ", //m "mb_play": "παίξε", diff --git a/copyparty/web/tl/hun.js b/copyparty/web/tl/hun.js index 70bafa70..f0db3dd2 100644 --- a/copyparty/web/tl/hun.js +++ b/copyparty/web/tl/hun.js @@ -326,7 +326,6 @@ Ls.hun = { "mt_ssvt": 'hangerő küszöb (0-255)">vol', "mt_ssts": 'aktív tartomány (%, eleje)">start', "mt_sste": 'aktív tartomány (%, vége)">end', - "mt_ssrt": 'hangosodási/halkulási idő">fade', "mt_sssm": 'lejátszási sebesség szorzó">ffwd', "mb_play": 'play', diff --git a/copyparty/web/tl/ita.js b/copyparty/web/tl/ita.js index 4530b4f1..08fb1e85 100644 --- a/copyparty/web/tl/ita.js +++ b/copyparty/web/tl/ita.js @@ -325,7 +325,6 @@ Ls.ita = { "mt_ssvt": "soglia volume (0-255)\">vol", //m "mt_ssts": "soglia attiva (% traccia, inizio)\">ini", //m "mt_sste": "soglia attiva (% traccia, fine)\">fin", //m - "mt_ssrt": "tempo rampa volume/velocità\">fade", //m "mt_sssm": "moltiplicatore velocità riproduzione\">av", //m "mb_play": "riproduci", diff --git a/copyparty/web/tl/jpn.js b/copyparty/web/tl/jpn.js index cabcd7b2..5d1e5889 100644 --- a/copyparty/web/tl/jpn.js +++ b/copyparty/web/tl/jpn.js @@ -325,7 +325,6 @@ Ls.jpn = { "mt_ssvt": "音量しきい値 (0-255)\">音", //m "mt_ssts": "有効しきい値 (トラック%, 開始)\">始", //m "mt_sste": "有効しきい値 (トラック%, 終了)\">終", //m - "mt_ssrt": "音量/速度 ランプ時間\">フェード", //m "mt_sssm": "再生速度倍率\">速", //m "mb_play": "再生", diff --git a/copyparty/web/tl/kor.js b/copyparty/web/tl/kor.js index bce748f1..98a73ee2 100644 --- a/copyparty/web/tl/kor.js +++ b/copyparty/web/tl/kor.js @@ -325,7 +325,6 @@ Ls.kor = { "mt_ssvt": "볼륨 임계값 (0-255)\">음", //m "mt_ssts": "활성 임계값 (% 트랙, 시작)\">시", //m "mt_sste": "활성 임계값 (% 트랙, 끝)\">끝", //m - "mt_ssrt": "볼륨/속도 램프 시간\">페이드", //m "mt_sssm": "재생 속도 배율\">고", //m "mb_play": "재생", diff --git a/copyparty/web/tl/nld.js b/copyparty/web/tl/nld.js index 76008443..85eeb0a6 100644 --- a/copyparty/web/tl/nld.js +++ b/copyparty/web/tl/nld.js @@ -325,7 +325,6 @@ Ls.nld = { "mt_ssvt": "Volumedrempel (0-255)\">vol", //m "mt_ssts": "Actieve drempel (% track, begin)\">beg", //m "mt_sste": "Actieve drempel (% track, einde)\">eind", //m - "mt_ssrt": "Op/aflooptijd volume/snelheid\">fade", //m "mt_sssm": "Vermenigvuldiger afspeelsnelheid\">vrs", //m "mb_play": "Afspelen", diff --git a/copyparty/web/tl/nno.js b/copyparty/web/tl/nno.js index 43af309e..c87db519 100644 --- a/copyparty/web/tl/nno.js +++ b/copyparty/web/tl/nno.js @@ -322,7 +322,6 @@ Ls.nno = { "mt_ssvt": "volumterskel (0-255)\">volum", "mt_ssts": "aktiv innanfor første % av songen\">start", "mt_sste": "aktiv innanfor siste % av songen\">slutt", - "mt_ssrt": "kor fort volum/tempo skal justerast\">fade", "mt_sssm": "avspelingshastigheitsmultiplikator\">ffwd", "mb_play": "lytt", diff --git a/copyparty/web/tl/nor.js b/copyparty/web/tl/nor.js index 1272606f..e1e191af 100644 --- a/copyparty/web/tl/nor.js +++ b/copyparty/web/tl/nor.js @@ -322,7 +322,6 @@ Ls.nor = { "mt_ssvt": "volumterskel (0-255)\">volum", "mt_ssts": "aktiv innenfor første % av sangen\">start", "mt_sste": "aktiv innenfor siste % av sangen\">slutt", - "mt_ssrt": "hvor fort volum/tempo skal justeres\">fade", "mt_sssm": "avspillingshastighetsmultiplikator\">ffwd", "mb_play": "lytt", diff --git a/copyparty/web/tl/pol.js b/copyparty/web/tl/pol.js index d8b2e621..c1d222e7 100644 --- a/copyparty/web/tl/pol.js +++ b/copyparty/web/tl/pol.js @@ -328,7 +328,6 @@ Ls.pol = { "mt_ssvt": "próg głośności (0-255)\">gł", //m "mt_ssts": "aktywny próg (% utworu, początek)\">pocz", //m "mt_sste": "aktywny próg (% utworu, koniec)\">kon", //m - "mt_ssrt": "czas narastania/opadania głośn./pręd.\">fade", //m "mt_sssm": "mnożnik prędkości odtwarzania\">szyb", //m "mb_play": "odtwórz", diff --git a/copyparty/web/tl/por.js b/copyparty/web/tl/por.js index 159c6233..b98d449d 100644 --- a/copyparty/web/tl/por.js +++ b/copyparty/web/tl/por.js @@ -325,7 +325,6 @@ Ls.por = { "mt_ssvt": "limiar de volume (0-255)\">vol", //m "mt_ssts": "limiar ativo (% faixa, início)\">ini", //m "mt_sste": "limiar ativo (% faixa, fim)\">fim", //m - "mt_ssrt": "tempo de rampa volume/velocidade\">fade", //m "mt_sssm": "multiplicador de velocidade de reprodução\">av", //m "mb_play": "reproduzir", diff --git a/copyparty/web/tl/rus.js b/copyparty/web/tl/rus.js index 9e1abc06..eff6bd55 100644 --- a/copyparty/web/tl/rus.js +++ b/copyparty/web/tl/rus.js @@ -325,7 +325,6 @@ Ls.rus = { "mt_ssvt": "порог громкости (0-255)\">гром", //m "mt_ssts": "активный порог (% трека, начало)\">нач", //m "mt_sste": "активный порог (% трека, конец)\">кон", //m - "mt_ssrt": "время нарастания/спада громк./скор.\">фейд", //m "mt_sssm": "множитель скорости воспроизведения\">уск", //m "mb_play": "играть", diff --git a/copyparty/web/tl/spa.js b/copyparty/web/tl/spa.js index 16013d4e..da4f5bb8 100644 --- a/copyparty/web/tl/spa.js +++ b/copyparty/web/tl/spa.js @@ -324,7 +324,6 @@ Ls.spa = { "mt_ssvt": "umbral de volumen (0-255)\">vol", //m "mt_ssts": "umbral activo (% pista, inicio)\">ini", //m "mt_sste": "umbral activo (% pista, fin)\">fin", //m - "mt_ssrt": "tiempo de rampa volumen/velocidad\">fund", //m "mt_sssm": "multiplicador de velocidad de reproducción\">av", //m "mb_play": "reproducir", diff --git a/copyparty/web/tl/swe.js b/copyparty/web/tl/swe.js index dc6f6cc2..ebb3d5b9 100644 --- a/copyparty/web/tl/swe.js +++ b/copyparty/web/tl/swe.js @@ -325,7 +325,6 @@ Ls.swe = { "mt_ssvt": "volymtröskel (0-255)\">vol", //m "mt_ssts": "aktiv tröskel (% spår, start)\">sta", //m "mt_sste": "aktiv tröskel (% spår, slut)\">slt", //m - "mt_ssrt": "upp/ned-rampningstid volym/hastighet\">fade", //m "mt_sssm": "uppspelningshastighetsmultiplikator\">sn", //m "mb_play": "play", diff --git a/copyparty/web/tl/tur.js b/copyparty/web/tl/tur.js index 74a16d96..1ec90f49 100644 --- a/copyparty/web/tl/tur.js +++ b/copyparty/web/tl/tur.js @@ -325,7 +325,6 @@ Ls.tur = { "mt_ssvt": "ses eşiği (0-255)\">ses", //m "mt_ssts": "etkin eşik (% parça, başlangıç)\">bas", //m "mt_sste": "etkin eşik (% parça, bitiş)\">son", //m - "mt_ssrt": "ses/hız rampa süresi\">fade", //m "mt_sssm": "oynatma hızı çarpanı\">ileri", //m "mb_play": "oynat", diff --git a/copyparty/web/tl/ukr.js b/copyparty/web/tl/ukr.js index c2ad6090..fb674ced 100644 --- a/copyparty/web/tl/ukr.js +++ b/copyparty/web/tl/ukr.js @@ -325,7 +325,6 @@ Ls.ukr = { "mt_ssvt": "поріг гучності (0-255)\">гуч", //m "mt_ssts": "активний поріг (% треку, початок)\">поч", //m "mt_sste": "активний поріг (% треку, кінець)\">кін", //m - "mt_ssrt": "час нарост./спаду гучн./швидк.\">фейд", //m "mt_sssm": "множник швидкості відтворення\">шв", //m "mb_play": "відтворити", diff --git a/copyparty/web/tl/vie.js b/copyparty/web/tl/vie.js index da1bebe4..053b4119 100644 --- a/copyparty/web/tl/vie.js +++ b/copyparty/web/tl/vie.js @@ -332,7 +332,6 @@ Ls.vie = { "mt_ssvt": "ngưỡng âm lượng (0-255)\">am", //m "mt_ssts": "ngưỡng hoạt động (% bài, đầu)\">dau", //m "mt_sste": "ngưỡng hoạt động (% bài, cuối)\">cuoi", //m - "mt_ssrt": "thời gian tăng/giảm âm lượng/tốc độ\">fade", //m "mt_sssm": "hệ số tốc độ phát\">nh", //m "mb_play": "phát", diff --git a/docs/bad-codecs.md b/docs/bad-codecs.md new file mode 100644 index 00000000..7aeffc66 --- /dev/null +++ b/docs/bad-codecs.md @@ -0,0 +1,53 @@ +due to legal reasons, the copyparty [docker image](https://github.com/9001/copyparty/blob/hovudstraum/scripts/docker) and [bootable flashdrive](https://a.ocv.me/pub/stuff/edcd001/enterprise-edition/) are not able to decode and display images and videos which were made using certain codecs + +* specifically, photos and videos taken with iphones will not work, and perhaps some samsung phones, idk +* this also includes thumbnails thereof + +I suggest you stop reading at this point, unless you want to share my frustration, in which case please do continue + + +## why hevc is not included + +the following is my understanding, which is probably wrong because [I anal](https://en.wikipedia.org/wiki/IANAL) + +* the h265 codec, also known as h.265 and hevc, is patent-encumbered and legally problematic to distribute; + * there are several patent-pools of patent-holders with conflicting and unclear requirements + * even FOSS is not exempt from demands of payment; https://en.wikipedia.org/wiki/High_Efficiency_Video_Coding#Provision_for_costless_software + * I have no idea how "number of sales" maps to FOSS, but copyparty doesn't have telemetry so it would be impossible to satisfy the requirements either way +* due to this, both chrome and firefox refuse to add a built-in decoder for hevc; https://caniuse.com/hevc + * and while I haven't discussed this with a lawyer, I presume the reason is they did +* most heif/heic images are hevc, meaning they are equally troublesome +* safari is the only webbrowser willing to decode and display heif/heic photos, for self-inflicted reasons https://caniuse.com/heif + +and anyways there's no reason to use hevc in the first place because [av1](https://en.wikipedia.org/wiki/AV1) gives higher quality at a smaller filesize, is entirely free, and avif (its heif counterpart) is widely supported across all browsers: https://caniuse.com/avif + + +## why only the docker and flashdrive images are affected + +supposedly, royalties is like a jigsaw puzzle, where whoever lays down the last piece wins the responsibility of dealing with that mess -- and because the docker-image has everything bundled as one big ball of software, that might(?) be a problem...idk, i anal + +so because ffmpeg is the component that handles everything regarding hevc, only the packages which include ffmpeg are affected, which means the docker-image and bootable-flashdrive-image + +if you use or install copyparty in any other way, then you are in charge of obtaining and providing an ffmpeg for copyparty to use, and thus nothing has changed + + +## how hevc support was removed + +the regular ffmpeg package from the alpinelinux repos was replaced with a [custom ffmpeg build](https://github.com/9001/copyparty/tree/hovudstraum/scripts/docker/base) where the hevc decoder was physically stripped out, meaning hevc is not just "disabled", but entirely removed from all official copyparty distributions + +oh and the aac support has also been tampered with; now only AAC-LC can be decoded, which is fine because that's like 99% of all aac files (nobody uses HE-AAC or AAC-LD), and LC-AAC has become royalty-free in all relevant parts of the world at this point + +and any traces of vvc was also stripped out because that codec was dead on arrival, unable to compete with av1 (and soon av2) + +the silver lining is that this has made the docker images *much* smaller; the `ac` image is now half the size -- it went from 67 to 35 MiB gzipped, from 195 to 99 MiB installed, which is nice + + +## how to enable hevc support + +all I can say is good luck; I legally cannot help you with that + +see, here's the fun part -- apparently I'm not allowed to assist with a technical explanation on how it could be done, because that would "facilitate access" as they call it?? but all copyparty does is call `ffmpeg` to generate the thumbnail; copyparty doesn't even know or care what "hevc" is; this is all purely on the ffmpeg side of things -- so technically none of this is even related to copyparty itself in the first place... ah whatever + +man I just wanna write software, I hate this + +pain peko diff --git a/docs/changelog.md b/docs/changelog.md index 22a97131..67389cdc 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,3 +1,88 @@ +▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ +# 2026-0225-0834 `v1.20.9` SECURITY: XSS fix + +## ⚠️ ATTN: this release fixes an XSS vulnerability + +[GHSA-62cr-6wp5-q43h](https://github.com/9001/copyparty/security/advisories/GHSA-62cr-6wp5-q43h) could let an attacker execute arbitrary JS by tricking you into clicking a malicious link 31b2801f + +## 🔧 other changes + +* webdav: [dav-port](https://copyparty.eu/cli/#g-dav-port) can be used as an alternative to [daw](https://copyparty.eu/cli/#g-daw) d21242fc + + + +▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ +# 2026-0222-1507 `v1.20.8` no265 + +## 🧪 new features + +* #1298 add Hungarian translation (thx @sonacl!) eefb181b f37c3b96 +* #1299 chown now accepts 4-digit values (thx @new-sashok724!) 5a7504fd + +## 🩹 bugfixes + +* audioplayer skip-silence: + * #1303 clamp ffwd to safe values (thx @icxes!) f5e70c7f + * fix crash on folderchange f1a433a6 + +## 🔧 other changes + +* due to [legal reasons](https://github.com/9001/copyparty/blob/hovudstraum/docs/bad-codecs.md), the [docker-images](https://github.com/9001/copyparty/blob/hovudstraum/scripts/docker) and [bootable flashdrive](https://a.ocv.me/pub/stuff/edcd001/enterprise-edition/) are now unable to create thumbnails of HEVC/h265 videos and heif/heic images 1bec91d1 + * this primarily means photos/videos taken with iphones (and maybe some samsung phones) + * on the bright side, this has made the docker-images much smaller; `ac` is now half the size it used to be, and `iv` / `dj` are each 97 MiB smaller + +## 🌠 fun facts + +* if you wanna see your car doing its best impression of a frictionless spherical cow, I can warmly (heh) recommend the icy snowcoated countryroads of viken this weekend + * goes oddly well with [sakuraburst - deconstructing nature](https://www.youtube.com/watch?v=MJjO-pwYpJg) + + + +▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ +# 2026-0214-2315 `v1.20.7` fika + +## 🧪 new features + +* now possible to upload/delete files while the filesystem-indexer is still busy d44ea245 0ca4c1bd + * global-option [fika](https://copyparty.eu/cli/#g-fika) decides which actions to allow while still indexing; default is upload+copy+delete + * full deduplication is only guaranteed if this option is set blank, as dupes are allowed while indexing +* #1266 browsers can request thumbnails as jxl images, and view jxl files in the gallery (thx @intelfx!) b2711e05 720c83b2 93ffc65c a65a30b1 a7a25deb 59de5e2c 16403d8c 48c10178 0e8913c2 + * only works in browsers which support jxl, which is FINALLY happening ([sure took a while](https://issues.chromium.org/issues/40168998)) + * some notes on memory/RAM usage though -- it is fine on Alpine Linux, so docker is also fine, just don't enable mimalloc + * jxl can be disabled with global-option [th-no-jxl](https://copyparty.eu/cli/#g-th-no-jxl) if necessary on baremetal deployments until libvips fixes this +* #1265 audioplayer can "skip silence" now (thx @icxes!) 66949989 +* #1287 opensearch support for opds (thx @philips!) 84e687a0 +* #1276 option [rw-edit](https://copyparty.eu/cli/#g-rw-edit) is the list of file-extensions that can be edited as textfiles with only permissions read+write (default is `md` like before); all other files still require read+write+delete 312f48e1 d6928380 +* #1288 option to customize the links copied when selecting files and pressing ctrl-c (thx @icxes!) e5d0a057 +* docker: add env-var [DI_PREPARTY](https://github.com/9001/copyparty/blob/hovudstraum/scripts/docker/devnotes.md#modding-on-the-fly) to run an arbitrary script during startup, for customizations and such bf01ca48 + +## 🩹 bugfixes + +* #1279 the textfile-viewer would refuse to load huge documents when hotlinked f02e9cf6 +* #1280 the custom rightclick-menu was enabled in the textfile viewer fc8a4b8e +* #1262 logtail now works on windows; would previously take an exclusive-lock on the monitored file, as windows does by default a368fc66 + +## 🔧 other changes + +* volumes are hidden from the treeview if the name starts with a dot 76041fdb +* #1277 `descript.ion` files no longer require the `e2d` and `e2t` options to be enabled 4cb4e820 +* chunked PUT-uploads are now terminated if they exceed a configured size limit dfadb5a7 +* #1282 improved compatibility with GraalPy (thx @vgskye!) e8609b87 +* #1292 #1296 updated Esperanto translation (thx @slashdevslashurandom!) 418bf2f9 914f84ce +* thumbnails: use libvips as fallback for rawpy 27ae2e1e + * libvips doesn't support .arw files (sony) yet, so still need rawpy +* make server config slightly easier: + * improve xff warnings 96aeb898 + * warn if config-values are quoted 598df44e + * lowercase headernames in configs fd096385 + +## 🌠 fun facts + +* the `fika` option sends the filesystem-indexer on [a coffee break](https://en.wikipedia.org/wiki/Coffee_in_Sweden#Fika) +* exci wants me to mention aoi yuuki here for some reason :^) so here's [gekisou gungnir](https://www.youtube.com/watch?v=feeFscLH6QE) + + + ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ # 2026-0131-2001 `v1.20.6` one safeguard too many diff --git a/scripts/docker/Dockerfile.ac b/scripts/docker/Dockerfile.ac index 96306140..ae9706f2 100644 --- a/scripts/docker/Dockerfile.ac +++ b/scripts/docker/Dockerfile.ac @@ -10,11 +10,9 @@ ENV XDG_CONFIG_HOME=/cfg RUN apk --no-cache add !pyc \ tzdata wget mimalloc2 mimalloc2-insecure \ py3-jinja2 py3-argon2-cffi py3-pyzmq \ - py3-openssl py3-paramiko py3-pillow \ - ffmpeg + py3-openssl py3-paramiko py3-pillow -COPY i/dist/copyparty-sfx.py innvikler.sh ./ -ADD base ./base +COPY i innvikler.sh ./ RUN ash innvikler.sh ac WORKDIR /state diff --git a/scripts/docker/Dockerfile.dj b/scripts/docker/Dockerfile.dj index 1d0eae5e..d78aa5ea 100644 --- a/scripts/docker/Dockerfile.dj +++ b/scripts/docker/Dockerfile.dj @@ -15,15 +15,15 @@ RUN apk add -U !pyc \ py3-jinja2 py3-argon2-cffi py3-pyzmq \ py3-openssl py3-paramiko py3-pillow \ py3-pip py3-cffi \ - ffmpeg \ py3-magic \ - vips-jxl vips-heif vips-poppler vips-magick \ + vips-jxl vips-poppler vips-magick \ py3-numpy fftw libsndfile \ vamp-sdk vamp-sdk-libs keyfinder-cli \ libraw py3-numpy \ && apk add -t .bd \ bash wget gcc g++ make cmake patchelf \ - python3-dev ffmpeg-dev fftw-dev libsndfile-dev \ + ffmpeg ffmpeg-dev \ + python3-dev fftw-dev libsndfile-dev \ py3-wheel py3-numpy-dev libffi-dev \ vamp-sdk-dev \ libraw-dev py3-numpy-dev cython \ @@ -35,8 +35,7 @@ RUN apk add -U !pyc \ && chmod 777 /root \ && ln -s /root/vamp /root/.local / -COPY i/dist/copyparty-sfx.py innvikler.sh ./ -ADD base ./base +COPY i innvikler.sh ./ RUN ash innvikler.sh dj WORKDIR /state diff --git a/scripts/docker/Dockerfile.im b/scripts/docker/Dockerfile.im index ff14be1f..4828520b 100644 --- a/scripts/docker/Dockerfile.im +++ b/scripts/docker/Dockerfile.im @@ -12,8 +12,7 @@ RUN apk --no-cache add !pyc \ py3-jinja2 py3-argon2-cffi \ py3-openssl py3-paramiko py3-pillow py3-mutagen -COPY i/dist/copyparty-sfx.py innvikler.sh ./ -ADD base ./base +COPY i innvikler.sh ./ RUN ash innvikler.sh im WORKDIR /state diff --git a/scripts/docker/Dockerfile.iv b/scripts/docker/Dockerfile.iv index 82bb324c..51360dd8 100644 --- a/scripts/docker/Dockerfile.iv +++ b/scripts/docker/Dockerfile.iv @@ -12,9 +12,8 @@ RUN apk add -U !pyc \ py3-jinja2 py3-argon2-cffi py3-pyzmq \ py3-openssl py3-paramiko py3-pillow \ py3-pip py3-cffi \ - ffmpeg \ py3-magic \ - vips-jxl vips-heif vips-poppler vips-magick \ + vips-jxl vips-poppler vips-magick \ libraw py3-numpy \ && apk add -t .bd \ bash wget gcc g++ make cmake patchelf \ @@ -25,8 +24,7 @@ RUN apk add -U !pyc \ && python3 -m pip install "$(wget -O- https://api.github.com/repos/letmaik/rawpy/releases/latest | awk -F\" '$2=="tarball_url"{print$4}')" \ && apk del py3-pip .bd -COPY i/dist/copyparty-sfx.py innvikler.sh ./ -ADD base ./base +COPY i innvikler.sh ./ RUN ash innvikler.sh iv WORKDIR /state diff --git a/scripts/docker/Dockerfile.min b/scripts/docker/Dockerfile.min index ec9aa121..ca7b718f 100644 --- a/scripts/docker/Dockerfile.min +++ b/scripts/docker/Dockerfile.min @@ -10,7 +10,7 @@ ENV XDG_CONFIG_HOME=/cfg RUN apk --no-cache add !pyc \ py3-jinja2 -COPY i/dist/copyparty-sfx.py innvikler.sh ./ +COPY i innvikler.sh ./ RUN ash innvikler.sh min WORKDIR /state diff --git a/scripts/docker/base/Dockerfile.zlibng b/scripts/docker/base/Dockerfile.zlibng index da270d0c..bddec9ab 100644 --- a/scripts/docker/base/Dockerfile.zlibng +++ b/scripts/docker/base/Dockerfile.zlibng @@ -2,4 +2,7 @@ FROM alpine:latest WORKDIR /z RUN apk add py3-pip make gcc musl-dev python3-dev -RUN pip wheel https://files.pythonhosted.org/packages/c4/a7/0b7673be5945071e99364a3ac1987b02fc1d416617e97f3e8816d275174e/zlib_ng-0.5.1.tar.gz + +RUN pip wheel https://files.pythonhosted.org/packages/46/7d/901c6e333fb031b5bfbd1532099200cf859f12aa83689be494eade6685ec/zlib_ng-1.0.0.tar.gz \ + && mkdir whl \ + && mv *.whl whl diff --git a/scripts/docker/base/Makefile b/scripts/docker/base/Makefile index 78eacd28..6e8b1dce 100644 --- a/scripts/docker/base/Makefile +++ b/scripts/docker/base/Makefile @@ -1,14 +1,26 @@ self := $(dir $(abspath $(lastword $(MAKEFILE_LIST)))) + all: +# build all outdated + bash verchk.sh + + +ff: +# legally comfy + /usr/bin/time ./build-no265.sh img + + +zlib: # build zlib-ng from source so we know how the sausage was made # (still only doing the archs which are officially supported/tested) podman build --arch amd64 -t localhost/cpp-zlibng-amd64:latest -f Dockerfile.zlibng . - podman run --arch amd64 --rm --log-driver=none -i localhost/cpp-zlibng-amd64:latest tar -cC/z . | tar -xv + podman run --arch amd64 --rm --log-driver=none -i localhost/cpp-zlibng-amd64:latest tar -cC/z whl | tar -xv podman build --arch arm64 -t localhost/cpp-zlibng-amd64:latest -f Dockerfile.zlibng . - podman run --arch arm64 --rm --log-driver=none -i localhost/cpp-zlibng-amd64:latest tar -cC/z . | tar -xv + podman run --arch arm64 --rm --log-driver=none -i localhost/cpp-zlibng-amd64:latest tar -cC/z whl | tar -xv + sh: @printf "\n\033[1;31mopening a shell in the most recently created docker image\033[0m\n" diff --git a/scripts/docker/base/arbeidspakke.sh b/scripts/docker/base/arbeidspakke.sh new file mode 100755 index 00000000..a191480c --- /dev/null +++ b/scripts/docker/base/arbeidspakke.sh @@ -0,0 +1,65 @@ +#!/bin/ash +set -e + +[ $1 = 1 ] && hub=1 + +uname -a +apk add alpine-sdk doas wget +echo permit nopass root > /etc/doas.d/u.conf +cp -pv /root/.abuild/*.pub /etc/apk/keys/ || abuild-keygen -ina + +## +## yeet h265 + +mkdir /ffmpeg +cd /ffmpeg +base=https://github.com/alpinelinux/aports/raw/refs/heads/3.23-stable/community/ffmpeg/ +wget ${base}APKBUILD +awk >APKBUILD <<'EOF' +prepare() { + default_prepare + tar -cC/opt/patch/ffmpeg . | tar -x + patch -p1 /dev/null && continue + err=1; echo ERROR: missing dependency: $x +done +[ $err ] && exit 1 + +for v in "$@"; do + [ "$v" = pull ] && pull=1 + [ "$v" = img ] && img=1 +done + +[ $# -gt 0 ] || { + echo "need list of commands, for example: pull img" + exit 1 +} + +wt() { + printf '\033]0;%s\033\\' "$*" + [ -z "$TMUX" ] || tmux renamew "$*" +} + +[ $pull ] && { + for a in $sarchs; do # arm/v6 + podman pull --arch=$a alpine:latest + done + + podman images --format "{{.ID}} {{.History}}" | + awk '/library\/alpine/{print$1}' | + while read id; do + tag=alpine-$(podman inspect $id | jq -r '.[]|.Architecture' | tr / -) + [ -e .tag-$tag ] && continue + touch .tag-$tag + echo tagging $tag + podman untag $id + podman tag $id $tag + done + rm .tag-* +} + +[ $img ] && { + mkdir -p "$self/b" + + # enable arm32 crossbuild from aarch64 (macbook or whatever) + [ $(uname -m) = aarch64 ] && [ ! -e /proc/sys/fs/binfmt_misc/qemu-arm ] && + echo ":qemu-arm:M:0:\x7f\x45\x4c\x46\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x28\x00:\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff:/usr/bin/qemu-arm-static:F" | + sudo tee >/dev/null /proc/sys/fs/binfmt_misc/register + + # kill abandoned builders + ps aux | awk '/bin\/qemu-[^-]+-static/{print$2}' | xargs -r kill -9 + + n=0; set -x + for a in $archs; do + n=$((n+1)); wt "$n/$a" + #[ $n -le 3 ] || continue + touch b/t.$a.1.$(date +%s) + + tar -c arbeidspakke.sh patch/ffmpeg | + time nice podman run \ + --rm -i --pull=never -v "$self/b:/root:z" localhost/alpine-$a \ + /bin/ash -c "cd /opt;tar -x;/bin/ash ./arbeidspakke.sh $n $a" 2>&1 | + tee b/log.$a + + touch b/t.$a.2.$(date +%s) + done + wt -;wt "" +} + +echo ok + +# just-no265 +# 4m18.77 x64 +# 4m22.81 386 +# 45m36.44 arm64 +# 34m31.22 ppc64le +# 50m01.04 s390x + +# golflympics +# 4:09 x86_64-hub +# 2:57 x86_64 +# 2:54 x86 +# 31:13 aarch64 +# 22:38 armv7 +# 32:17 s390x +# 24:27 ppc64le +# 2:00:35 summa summarum diff --git a/scripts/docker/base/patch/ffmpeg/aac-lc-only.patch b/scripts/docker/base/patch/ffmpeg/aac-lc-only.patch new file mode 100644 index 00000000..ad506b2f --- /dev/null +++ b/scripts/docker/base/patch/ffmpeg/aac-lc-only.patch @@ -0,0 +1,59 @@ +remove all advanced aac features, leaving only aac-lc which is +no longer patent-encumbered in any relevant parts of the world +( and 99% of all aac files are lc-aac anyways so that's fine ) + +diff --git a/libavcodec/aac/aacdec.c b/libavcodec/aac/aacdec.c +index b8d53036d4..054c46f84e 100644 +--- a/libavcodec/aac/aacdec.c ++++ b/libavcodec/aac/aacdec.c +@@ -880,9 +880,7 @@ static int decode_pce(AVCodecContext *avctx, MPEG4AudioConfig *m4ac, + */ + static int decode_ga_specific_config(AACDecContext *ac, AVCodecContext *avctx, +- GetBitContext *gb, +- int get_bit_alignment, +- MPEG4AudioConfig *m4ac, +- int channel_config) ++ GetBitContext *gb, int get_bit_alignment, MPEG4AudioConfig *m4ac, int channel_config) + { ++ if (m4ac->sbr > 0) return AVERROR_DECODER_NOT_FOUND; + int extension_flag, ret, ep_config, res_flags; + uint8_t layout_map[MAX_ELEM_ID*4][3]; +@@ -961,8 +959,7 @@ static int decode_ga_specific_config(AACDecContext *ac, AVCodecContext *avctx, + + static int decode_eld_specific_config(AACDecContext *ac, AVCodecContext *avctx, +- GetBitContext *gb, +- MPEG4AudioConfig *m4ac, +- int channel_config) ++ GetBitContext *gb, MPEG4AudioConfig *m4ac, int channel_config) + { ++ return AVERROR_DECODER_NOT_FOUND; // kill ELD support + int ret, ep_config, res_flags; + uint8_t layout_map[MAX_ELEM_ID*4][3]; +@@ -1070,5 +1067,4 @@ static int decode_audio_specific_config_gb(AACDecContext *ac, + case AOT_AAC_LTP: + case AOT_ER_AAC_LC: +- case AOT_ER_AAC_LD: + if ((ret = decode_ga_specific_config(ac, avctx, gb, get_bit_alignment, + &oc->m4ac, m4ac->chan_config)) < 0) +@@ -1948,4 +1944,5 @@ static int decode_extension_payload(AACDecContext *ac, GetBitContext *gb, int cn + crc_flag++; + case EXT_SBR_DATA: ++ return res; // kill HE/SBR support + if (!che) { + av_log(ac->avctx, AV_LOG_ERROR, "SBR was found before the first channel element.\n"); +@@ -2087,4 +2084,5 @@ static void spectral_to_sample(AACDecContext *ac, int samples) + } + if (ac->oc[1].m4ac.sbr > 0) { ++ exit(1); // kill HE/SBR support + ac->proc.sbr_apply(ac, che, type, + che->ch[0].output, +diff --git a/libavcodec/aacsbr_template.c b/libavcodec/aacsbr_template.c +index 31d2d844c4..b55f93752a 100644 +--- a/libavcodec/aacsbr_template.c ++++ b/libavcodec/aacsbr_template.c +@@ -639,4 +639,5 @@ static int read_sbr_grid(AACDecContext *ac, SpectralBandReplication *sbr, + GetBitContext *gb, SBRData *ch_data) + { ++ exit(1); // kill SBR support + int i; + int bs_pointer = 0; diff --git a/scripts/docker/base/patch/ffmpeg/libavcodec/aacps.c b/scripts/docker/base/patch/ffmpeg/libavcodec/aacps.c new file mode 100644 index 00000000..6eca3fbe --- /dev/null +++ b/scripts/docker/base/patch/ffmpeg/libavcodec/aacps.c @@ -0,0 +1,26 @@ +// just the signatures from the original file; all bodies/logic removed + +#include +#include "libavutil/common.h" +#include "libavutil/mathematics.h" +#include "libavutil/mem_internal.h" +#include "aacps.h" +#if USE_FIXED +#include "aacps_fixed_tablegen.h" +#else +#include "libavutil/internal.h" +#include "aacps_tablegen.h" +#endif /* USE_FIXED */ + +static void hybrid_analysis(PSDSPContext *dsp, INTFLOAT out[91][32][2], + INTFLOAT in[5][44][2], INTFLOAT L[2][38][64], + int is34, int len) {} + +static void hybrid_synthesis(PSDSPContext *dsp, INTFLOAT out[2][38][64], + INTFLOAT in[91][32][2], int is34, int len) {} + +static void decorrelation(PSContext *ps, INTFLOAT (*out)[32][2], const INTFLOAT (*s)[32][2], int is34) {} + +int AAC_RENAME(ff_ps_apply)(PSContext *ps, INTFLOAT L[2][38][64], INTFLOAT R[2][38][64], int top) { return 0; } + +av_cold void AAC_RENAME(ff_ps_init)(void) {} diff --git a/scripts/docker/base/patch/ffmpeg/libavcodec/aacsbr.c b/scripts/docker/base/patch/ffmpeg/libavcodec/aacsbr.c new file mode 100644 index 00000000..cf6548eb --- /dev/null +++ b/scripts/docker/base/patch/ffmpeg/libavcodec/aacsbr.c @@ -0,0 +1,41 @@ +// just the signatures from the original file; all bodies/logic removed + +#define USE_FIXED 0 + +#include "aac.h" +#include "sbr.h" +#include "aacsbr.h" +#include "aacsbrdata.h" +#include "aacps.h" +#include "sbrdsp.h" +#include "libavutil/internal.h" +#include "libavutil/intfloat.h" +#include "libavutil/libm.h" +#include "libavutil/avassert.h" +#include "libavutil/mem_internal.h" + +#include +#include +#include + +static void aacsbr_func_ptr_init(AACSBRContext *c); + +static void make_bands(int16_t* bands, int start, int stop, int num_bands) {} + +static void sbr_dequant(SpectralBandReplication *sbr, int id_aac) {} + +static void sbr_hf_inverse_filter(SBRDSPContext *dsp, + float (*alpha0)[2], float (*alpha1)[2], + const float X_low[32][40][2], int k0) {} + +static void sbr_chirp(SpectralBandReplication *sbr, SBRData *ch_data) {} + +static void sbr_gain_calc(SpectralBandReplication *sbr, + SBRData *ch_data, const int e_a[2]) {} + +static void sbr_hf_assemble(float Y1[38][64][2], + const float X_high[64][40][2], + SpectralBandReplication *sbr, SBRData *ch_data, + const int e_a[2]) {} + +#include "aacsbr_template.c" diff --git a/scripts/docker/base/patch/ffmpeg/libavcodec/aacsbr_fixed.c b/scripts/docker/base/patch/ffmpeg/libavcodec/aacsbr_fixed.c new file mode 100644 index 00000000..89071d21 --- /dev/null +++ b/scripts/docker/base/patch/ffmpeg/libavcodec/aacsbr_fixed.c @@ -0,0 +1,45 @@ +// just the signatures from the original file; all bodies/logic removed + +#define USE_FIXED 1 + +#include "aac.h" +#include "sbr.h" +#include "aacsbr.h" +#include "aacsbrdata.h" +#include "aacps.h" +#include "sbrdsp.h" +#include "libavutil/internal.h" +#include "libavutil/libm.h" +#include "libavutil/avassert.h" + +#include +#include +#include + +static void aacsbr_func_ptr_init(AACSBRContext *c); +static const int CONST_RECIP_LN2 = Q31(0.7); +static const int CONST_076923 = Q31(0.7); + +static const int fixed_log_table[] = {Q31(0)}; + +static int fixed_log(int x) {return 1;} + +static void make_bands(int16_t* bands, int start, int stop, int num_bands) {} + +static void sbr_dequant(SpectralBandReplication *sbr, int id_aac) {} + +static void sbr_hf_inverse_filter(SBRDSPContext *dsp, + int (*alpha0)[2], int (*alpha1)[2], + const int X_low[32][40][2], int k0) {} + +static void sbr_chirp(SpectralBandReplication *sbr, SBRData *ch_data) {} + +static void sbr_gain_calc(SpectralBandReplication *sbr, + SBRData *ch_data, const int e_a[2]) {} + +static void sbr_hf_assemble(int Y1[38][64][2], + const int X_high[64][40][2], + SpectralBandReplication *sbr, SBRData *ch_data, + const int e_a[2]) {} + +#include "aacsbr_template.c" diff --git a/scripts/docker/base/patch/ffmpeg/libavcodec/aacsbrdata.h b/scripts/docker/base/patch/ffmpeg/libavcodec/aacsbrdata.h new file mode 100644 index 00000000..016706bc --- /dev/null +++ b/scripts/docker/base/patch/ffmpeg/libavcodec/aacsbrdata.h @@ -0,0 +1,16 @@ +// just the signatures from the original file; all bodies/logic removed + +#ifndef AVCODEC_AACSBRDATA_H +#define AVCODEC_AACSBRDATA_H + +#include +#include "libavutil/mem_internal.h" +#include "aac_defines.h" + +static const int8_t sbr_offset[6][16] = {}; + +static const DECLARE_ALIGNED(32, INTFLOAT, sbr_qmf_window_ds)[320] = {}; + +static const DECLARE_ALIGNED(32, INTFLOAT, sbr_qmf_window_us)[640] = {}; + +#endif /* AVCODEC_AACSBRDATA_H */ diff --git a/scripts/docker/base/verchk.sh b/scripts/docker/base/verchk.sh new file mode 100755 index 00000000..a852f701 --- /dev/null +++ b/scripts/docker/base/verchk.sh @@ -0,0 +1,24 @@ +#!/bin/bash +set -e + +v=3.23 + +mkdir -p cver +rm -rf cver2 +mkdir cver2 +cd cver2 +curl \ + -Lo1 https://raw.githubusercontent.com/alpinelinux/aports/refs/heads/$v-stable/main/musl/APKBUILD \ + -Lo2 https://raw.githubusercontent.com/alpinelinux/aports/refs/heads/$v-stable/main/python3/APKBUILD \ + -Lo3 https://raw.githubusercontent.com/alpinelinux/aports/refs/heads/$v-stable/community/ffmpeg/APKBUILD \ + ; + +zlib= ff= +cmp 1 ../cver/1 || zlib=1 +cmp 2 ../cver/2 || zlib=1 +cmp 3 ../cver/3 || ff=1 +echo zlib=$zlib ff=$ff + +[ $zlib ] && { make zlib; cp -pv 1 2 ../cver/; } +[ $ff ] && { make ff; cp -pv 3 ../cver/; } +rm -rf cver2 diff --git a/scripts/docker/innvikler.sh b/scripts/docker/innvikler.sh index 922cd410..d5387d4f 100644 --- a/scripts/docker/innvikler.sh +++ b/scripts/docker/innvikler.sh @@ -14,15 +14,29 @@ ised() { tmv "$2" } +# use custom ffmpeg if relevant +echo $1 | grep -qE 'ac|iv|dj' && ( + cp -pv /z/packages/*.pub /etc/apk/keys/ + cd /z/packages/$(cat /etc/apk/arch) + apk add ./ffmpeg-*.apk + cd /z/test-aac + for f in *.m4a; do ffmpeg -v 0 -i $f ${f%.*}.flac || true; done + ls -1 *.flac | tee /dev/stderr | tr '\n' ' ' | grep -qE '^(lc.flac *)?$' || { + echo ERROR: incorrect aac decoder subset + exit 1 + } +) +rm -rf /z/packages /z/test-aac + # use zlib-ng if available -f=/z/base/zlib_ng-0.5.1-cp312-cp312-linux_$(cat /etc/apk/arch).whl +f=/z/whl/zlib_ng-0.5.1-cp312-cp312-linux_$(cat /etc/apk/arch).whl [ "$1" != min ] && [ -e $f ] && { apk add -t .bd !pyc py3-pip rm -f /usr/lib/python3*/EXTERNALLY-MANAGED pip install $f apk del .bd } -rm -rf /z/base +rm -rf /z/whl # cleanup for flavors with python build steps (dj/iv) rm -rf /var/cache/apk/* /root/.cache @@ -70,6 +84,11 @@ rm -rf \ /tmp/pe-* /z/copyparty-sfx.py \ ensurepip pydoc_data turtle.py turtledemo lib2to3 +cd /usr/lib/python3.*/site-packages +rm -rf \ + numpy/*/tests \ + /usr/share/mime/packages/freedesktop.org.xml + cd /usr/lib/python3.*/site-packages/copyparty/ rm stolen/surrogateescape.py iawk '/^[^ ]/{s=0}/^if not VENDORED:/{s=1}!s' qrkode.py diff --git a/scripts/docker/make.sh b/scripts/docker/make.sh index 3535fffa..c97d541d 100755 --- a/scripts/docker/make.sh +++ b/scripts/docker/make.sh @@ -1,5 +1,7 @@ #!/bin/bash set -e +self=$(cd -- "$(dirname "$BASH_SOURCE")"; pwd -P) +cd "$self" [ $(id -u) -eq 0 ] && { echo dont root @@ -78,6 +80,20 @@ filt= } [ $img ] && { + [ -e base/test-aac/lc.m4a ] || ( + echo building aac smoketest + mkdir -p base/test-aac + cd base/test-aac + ffmpeg -nostdin -y -f lavfi -i sine -ac 2 -t 1 a.wav && + fdkaac -m 3 -o lc.m4a a.wav && + fdkaac -m 2 -p 5 -o he.m4a a.wav && + fdkaac -m 1 -p 29 -o he2.m4a a.wav && + fdkaac -m 3 -p 23 -o ld.m4a a.wav && + fdkaac -m 3 -p 39 -o eld.m4a a.wav || + echo "nevermind, failed to build test files, cannot verify aac decoding" + rm -f a.wav + ) + fp=../../dist/copyparty-sfx.py [ -e $fp ] || { echo downloading copyparty-sfx.py ... @@ -96,7 +112,11 @@ filt= # grab deps rm -rf i err mkdir i - tar -cC../.. dist/copyparty-sfx.py bin/mtag | tar -xvCi + tar -cC "$self/base" whl test-aac \ + -C "$self/base/b" packages \ + -C "$self/../.." bin/mtag \ + -C dist copyparty-sfx.py \ + | tar -xvCi for i in $imgs; do podman rm copyparty-$i || true # old manifest diff --git a/scripts/rls.sh b/scripts/rls.sh index 048c6d14..a7cd9e7c 100755 --- a/scripts/rls.sh +++ b/scripts/rls.sh @@ -23,6 +23,7 @@ v=$1; shift printf '%s\n' "$v" | grep -qE '^[0-9\.]+$' || exit 1 grep -E "(${v//./, })" ../copyparty/__version__.py || exit 1 + make -C docker/base ./make-sfx.sh nopk gz ../dist/copyparty-sfx.py --version >/dev/null diff --git a/tests/test_dots.py b/tests/test_dots.py index 4f9d3a82..253cd690 100644 --- a/tests/test_dots.py +++ b/tests/test_dots.py @@ -15,7 +15,7 @@ from copyparty.httpcli import HttpCli from copyparty.u2idx import U2idx from copyparty.up2k import Up2k from tests import util as tu -from tests.util import Cfg +from tests.util import J2_ENV, Cfg try: from typing import Optional @@ -23,15 +23,16 @@ except: pass -def hdr(query, uname): - h = "GET /%s HTTP/1.1\r\nPW: %s\r\nConnection: close\r\n\r\n" - return (h % (query, uname)).encode("utf-8") +def hdr(query, uname, extra=""): + h = "GET /%s HTTP/1.1\r\nPW: %s\r\nConnection: close\r\n%s\r\n" + return (h % (query, uname, extra)).encode("utf-8") class TestDots(unittest.TestCase): def __init__(self, *a, **ka): super(TestDots, self).__init__(*a, **ka) self.is_dut = True + self.j2_brw = None def setUp(self): self.conn: Optional[tu.VHttpConn] = None @@ -49,6 +50,13 @@ class TestDots(unittest.TestCase): self.conn = None self.conn = tu.VHttpConn(self.args, self.asrv, self.log, b"") + if not self.j2_brw: + zs = os.path.dirname(__file__) + with open("%s/../copyparty/web/browser.html" % (zs,), "rb") as f: + zs = f.read().decode("utf-8") + self.j2_brw = J2_ENV.from_string(zs) + self.conn.hsrv.j2["browser"] = self.j2_brw + def test_dots(self): td = os.path.join(self.td, "vfs") os.mkdir(td) @@ -150,8 +158,10 @@ class TestDots(unittest.TestCase): os.chdir(td) vcfg = [] - for k in "dk dks dky fk fka dk,fk dks,fk".split(): - vcfg += ["{0}:{0}:r.,u1:g,u2:c,{0}".format(k)] + zs = "dk dks=12 dky fk fka dk,fk dks=12:c,fk" + for k2 in zs.split(): + k = k2.replace("=12", "").replace(":c,", ",") + vcfg += ["{0}:{0}:r.,u1:g,u2:c,{1}".format(k, k2)] zs = "%s/s1/s2" % (k,) os.makedirs(zs) @@ -220,6 +230,15 @@ class TestDots(unittest.TestCase): zs = self.curl("dks/%s&ls" % (s1), "u2")[1] self.assertIn('"s2/?k=', zs) + # parent key should not exist in response + self.assertNotIn(dk["dks"], zs) + # and not in html either + for ck in ("", "Cookie: js=y\r\n"): + zb = hdr("dks/%s" % (s1), "u2", ck) + zs = self.curl("", "", False, zb)[1] + self.assertNotIn(dk["dks"], zs) + self.assertIn('"s2/?k=', zs) + ## ## dks thumbs @@ -366,7 +385,7 @@ class TestDots(unittest.TestCase): zb = zs.encode("utf-8") hdr = "POST /%s HTTP/1.1\r\nPW: %s\r\nConnection: close\r\nContent-Type: multipart/form-data; boundary=XD\r\nContent-Length: %d\r\n\r\n" req = (hdr % (url, uname, len(zb))).encode("utf-8") + zb - h, b = self.curl("/" + url, uname, True, req) + h, b = self.curl("", "", True, req) tar = tarfile.open(fileobj=io.BytesIO(b), mode="r|").getnames() return " ".join(tar)