Compare commits

..

13 commits

Author SHA1 Message Date
NecRaul d33d11321f
fix directory sort-order (closes #1119) (#1528);
folders sorted incorrectly due to the trailing slash,
for example `a/` vs. `a b/`
2026-06-16 22:25:08 +00:00
NecRaul e017b1bc6e fix(rotf): use global rotf_tz as volume fallback 2026-06-16 20:40:37 +02:00
ed efa43f891b ffmpeg bwrap sandbox 2026-06-13 21:26:09 +00:00
ed 90639de984 u2c: hash stdin to wark 2026-06-13 11:47:47 +00:00
ed 5dbff4af41 js: mkdir: cd if existed; closes #1512 2026-06-13 11:45:21 +00:00
ed 5beecd6622 fix xbu reloc resume;
both up2k.js and u2c.py would ignore the "partial upload exists"
assuming it was self-inflicted, skipping the file
2026-06-13 11:20:13 +00:00
ed 5a8949d595 vjoins 2026-06-13 11:15:20 +00:00
ed 22c3a3dd46 pop job.vcfg; fixes up2k._snap_reg 2026-06-13 10:39:26 +00:00
ed a00bc93fe1 bsd: signal masking 2026-06-12 23:37:12 +00:00
ed a48afe87b9 docker: alpine 3.24 2026-06-12 21:47:04 +00:00
ed c3eb9ecec2 docs: docker archs 2026-06-12 21:29:35 +00:00
ed 6e75faa623 docker: drop arm32 iv;
deadlock on startup, probably bad cffi
2026-05-26 19:16:31 +00:00
ed 06af60276b update pkgs to 1.20.16 2026-05-26 18:49:07 +00:00
24 changed files with 331 additions and 129 deletions

View file

@ -1,8 +1,8 @@
#!/usr/bin/env python3
from __future__ import print_function, unicode_literals
S_VERSION = "2.21"
S_BUILD_DT = "2026-05-25"
S_VERSION = "2.22"
S_BUILD_DT = "2026-06-13"
"""
u2c.py: upload to copyparty
@ -733,16 +733,22 @@ def up2k_chunksize(filesize):
stepsize *= mul
# mostly from copyparty/up2k.py
def get_hashlist(file, pcb, mth):
# type: (File, Any, Any) -> None
with open(file.abs, "rb", 512 * 1024) as f:
get_hashlist_f(file, pcb, mth, f)
# mostly from copyparty/up2k.py
def get_hashlist_f(file, pcb, mth, f):
# type: (File, Any, Any, Any) -> None
"""generates the up2k hashlist from file contents, inserts it into `file`"""
chunk_sz = up2k_chunksize(file.size)
file_rem = file.size
file_ofs = 0
ret = []
with open(file.abs, "rb", 512 * 1024) as f:
if True:
t0 = time.time()
if mth and file.size >= 1024 * 512:
@ -778,6 +784,18 @@ def get_hashlist(file, pcb, mth):
file.kchunks[k] = [v1, v2]
def wark_stdin(ar):
file = File(b"", b"", int(ar.files[0]), 0)
get_hashlist_f(file, None, False, sys.stdin if PY2 else sys.stdin.buffer)
if ar.chs:
zsl = ["%s %d %d" % (zsii[0], n, zsii[1]) for n, zsii in enumerate(file.cids)]
print("chs:\n" + "\n".join(zsl))
zsl = [ar.wsalt, str(file.size)] + [x[0] for x in file.cids]
zb = hashlib.sha512("\n".join(zsl).encode("utf-8")).digest()[:33]
wark = ub64enc(zb).decode("utf-8")
print(wark)
def printlink(ar, purl, name, fk):
if not name:
url = purl # srch
@ -1587,6 +1605,7 @@ NOTE: if server has --usernames enabled, then password is "username:password"
ap.add_argument("--wsalt", type=unicode, metavar="S", default="hunter2", help="salt to use when creating warks; must match server config")
ap.add_argument("--chs", action="store_true", help="verbose (print the hash/offset of each chunk in each file)")
ap.add_argument("--jw", action="store_true", help="just identifier+filepath, not mtime/size too")
ap.add_argument("--stdin", action="store_true", help="calculate file-ID of stdin; u2c.py --stdin - $LEN < a.mkv")
ap = app.add_argument_group("performance tweaks")
ap.add_argument("-j", type=int, metavar="CONNS", default=2, help="parallel connections")
@ -1617,6 +1636,10 @@ NOTE: if server has --usernames enabled, then password is "username:password"
except:
pass
if ar.stdin:
wark_stdin(ar)
return
# msys2 doesn't uncygpath absolute paths with whitespace
if not VT100:
zsl = []

View file

@ -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.14"
pkgver="1.20.16"
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=("8783dc8390be17673d306f424e7a28dd9f9b4fce005e35734d30c1b296707c12")
sha256sums=("625f95d65d95cdd6898510518d013905e6766c7d2ae0ea9ae7d5dec96e89e02d")
build() {
cd "${srcdir}/${pkgname}-${pkgver}/copyparty/web"

View file

@ -2,7 +2,7 @@
pkgname=copyparty
pkgver=1.20.14
pkgver=1.20.16
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=("8783dc8390be17673d306f424e7a28dd9f9b4fce005e35734d30c1b296707c12")
sha256sums=("625f95d65d95cdd6898510518d013905e6766c7d2ae0ea9ae7d5dec96e89e02d")
build() {
cd "${srcdir}/${pkgname}-${pkgver}/copyparty/web"

View file

@ -1,5 +1,5 @@
{
"url": "https://github.com/9001/copyparty/releases/download/v1.20.14/copyparty-1.20.14.tar.gz",
"version": "1.20.14",
"hash": "sha256-h4Pcg5C+F2c9MG9CTnoo3Z+bT84AXjVzTTDBspZwfBI="
"url": "https://github.com/9001/copyparty/releases/download/v1.20.16/copyparty-1.20.16.tar.gz",
"version": "1.20.16",
"hash": "sha256-Yl+V1l2VzdaJhRBRjQE5BeZ2bH0q4Oqa59XeyW6J4C0="
}

View file

@ -28,6 +28,7 @@ from .__init__ import (
MACOS,
PY2,
PY36,
UNIX,
VT100,
WINDOWS,
E,
@ -45,6 +46,7 @@ from .util import (
DEF_EXP,
DEF_MTE,
DEF_MTH,
HAVE_BWRAP,
HAVE_IPV6,
IMPLICATIONS,
JINJA_VER,
@ -1643,6 +1645,29 @@ def add_optouts(ap):
def add_safety(ap):
th_bwrap = ""
if HAVE_BWRAP:
zsl = [
"bwrap",
"--proc /proc",
"--tmpfs /tmp",
"--tmpfs /var",
"--tmpfs /run",
"--dev-bind /dev/null /dev/null",
"--dev-bind /dev/random /dev/random",
"--dev-bind /dev/urandom /dev/urandom",
"--chdir /tmp",
"--clearenv",
"--unshare-all",
"--cap-drop ALL",
"--die-with-parent",
"--new-session",
]
for d in ("/lib", "/lib64", "/usr/lib", "/usr/lib64"):
if os.path.isdir(d):
zsl.append(" --ro-bind %s %s" % (d, d))
th_bwrap = " ".join(zsl)
ap2 = ap.add_argument_group("safety options")
ap2.add_argument("-s", action="count", default=0, help="increase safety: Disable thumbnails / potentially dangerous software (ffmpeg/pillow/vips), hide partial uploads, avoid crawlers.\n └─Alias of\033[32m --dotpart --no-thumb --no-mtag-ff --no-robots --force-js")
ap2.add_argument("-ss", action="store_true", help="further increase safety: Prevent js-injection, accidental move/delete, broken symlinks, webdav requires login, 404 on 403, ban on excessive 404s.\n └─Alias of\033[32m -s --no-html --no-readme --no-logues --unpost=0 --no-del --no-mv --reflink --dav-auth --vague-403 -nih")
@ -1679,6 +1704,8 @@ def add_safety(ap):
ap2.add_argument("--loris", metavar="B", type=int, default=60, help="if a client maxes out the server connection limit without sending headers, ban it for \033[33mB\033[0m minutes; disable with [\033[32m0\033[0m]")
ap2.add_argument("--acao", metavar="V[,V]", type=u, default="*", help="Access-Control-Allow-Origin; list of origins (domains/IPs without port) to accept requests from; [\033[32mhttps://1.2.3.4\033[0m]. Default [\033[32m*\033[0m] allows requests from all sites but removes cookies and http-auth; only ?pw=hunter2 survives")
ap2.add_argument("--acam", metavar="V[,V]", type=u, default="GET,HEAD", help="Access-Control-Allow-Methods; list of methods to accept from offsite ('*' behaves like \033[33m--acao\033[0m's description)")
if not ANYWIN and not UNIX:
ap2.add_argument("--th-bwrap", metavar="CMD", type=u, default=th_bwrap, help="optional bwrap sandbox command for FFmpeg and dcraw (Linux-only)")
def add_salt(ap, fk_salt, dk_salt, ah_salt):

View file

@ -2328,7 +2328,7 @@ class AuthSrv(object):
zs = vol.flags.get("rotf")
if zs:
use = True
lim.set_rotf(zs, vol.flags.get("rotf_tz") or "UTC")
lim.set_rotf(zs, vol.flags.get("rotf_tz", self.args.rotf_tz) or "UTC")
zs = vol.flags.get("maxn")
if zs:

View file

@ -123,6 +123,7 @@ from .util import (
unescape_cookie,
unquotep,
vjoin,
vjoins,
vol_san,
vroots,
vsplit,
@ -1919,7 +1920,7 @@ class HttpCli(object):
df = {}
fgen = itertools.chain([topdir], fgen)
vtop = vjoin(self.args.R, vjoin(vn.vpath, rem))
vtop = vjoins(self.args.R, vn.vpath, rem)
chunksz = 0x7FF8 # preferred by nginx or cf (dunno which)
@ -7469,11 +7470,11 @@ class HttpCli(object):
if doctxt is not None:
j2a["doc"] = doctxt
dirs.sort(key=itemgetter("name"))
for d in dirs:
d["name"] += "/"
dirs.sort(key=itemgetter("name"))
if is_opds:
# OpenSearch Description format requires a full-qualified URL and a "Short Name" under 16 characters
# which will be the longname truncated in the template.

View file

@ -74,6 +74,7 @@ def have_ff(name: str) -> bytes:
HAVE_FFMPEG = have_ff("ffmpeg")
HAVE_FFPROBE = have_ff("ffprobe")
TH_BWRAP = []
CBZ_PICS = set("png jpg jpeg gif bmp tga tif tiff webp avif jxl".split())
CBZ_01 = re.compile(r"(^|[^0-9v])0+[01]\b")
@ -224,17 +225,28 @@ def au_unpk(
return abspath
def bwrap(prog: bytes, ap_in: bytes, ap_out: bytes) -> list[bytes]:
if not TH_BWRAP:
return [prog]
ret = TH_BWRAP + [b"--ro-bind", prog, prog, b"--ro-bind", ap_in, ap_in]
if ap_out:
zs = ap_out.rsplit(b"/", 1)[0]
ret += [b"--bind", zs, zs]
ret.append(prog)
return ret
def ffprobe(
abspath: str, timeout: int = 60
) -> tuple[dict[str, tuple[int, Any]], dict[str, list[Any]], list[Any], dict[str, Any]]:
# ffprobe -hide_banner -show_streams -show_format --
cmd = [
HAVE_FFPROBE,
bap = fsenc(abspath)
cmd = bwrap(HAVE_FFPROBE, bap, b"") + [
b"-hide_banner",
b"-show_streams",
b"-show_format",
b"--",
fsenc(abspath),
bap,
]
rc, so, se = runcmd(cmd, timeout=timeout, nice=True, oom=200)
retchk(rc, cmd, se)

View file

@ -27,13 +27,23 @@ if True: # pylint: disable=using-constant-test
import typing
from typing import Any, Optional, Union
from .__init__ import ANYWIN, EXE, MACOS, PY2, TYPE_CHECKING, E, EnvParams, unicode
from .__init__ import (
ANYWIN,
EXE,
MACOS,
PY2,
TYPE_CHECKING,
UNIX,
E,
EnvParams,
unicode,
)
from .__version__ import S_VERSION, VERSION
from .authsrv import BAD_CFG, AuthSrv, derive_args, n_du_who, n_ver_who
from .bos import bos
from .cert import ensure_cert
from .fsutil import ramdisk_chk
from .mtag import HAVE_FFMPEG, HAVE_FFPROBE, HAVE_MUTAGEN
from .mtag import HAVE_FFMPEG, HAVE_FFPROBE, HAVE_MUTAGEN, TH_BWRAP
from .pwhash import HAVE_ARGON2
from .sutil import close_pools as sutil_close_pools
from .tcpsrv import TcpSrv
@ -42,8 +52,6 @@ from .th_srv import (
H_PIL_HEIF,
H_PIL_WEBP,
HAVE_DCRAW,
HAVE_FFMPEG,
HAVE_FFPROBE,
HAVE_PIL,
HAVE_RAWPY,
HAVE_VIPS,
@ -51,10 +59,12 @@ from .th_srv import (
)
from .up2k import Up2k
from .util import (
BLOCK_SIGS,
DEF_EXP,
DEF_MTE,
DEF_MTH,
FFMPEG_URL,
HAVE_BWRAP,
HAVE_PSUTIL,
HAVE_SQLITE3,
HAVE_ZMQ,
@ -1012,6 +1022,8 @@ class SvcHub(object):
fok = []
fng = []
t_ff = "transcode audio, create spectrograms, video thumbnails"
# fmt: off
to_check = [
(HAVE_SQLITE3, "sqlite", "sessions and file/media indexing"),
(HAVE_PIL, "pillow", "image thumbnails (plenty fast)"),
@ -1019,6 +1031,7 @@ class SvcHub(object):
(H_PIL_WEBP, "pillow-webp", "create thumbnails as webp files"),
(HAVE_FFMPEG, "ffmpeg", t_ff + ", good-but-slow image thumbnails"),
(HAVE_FFPROBE, "ffprobe", t_ff + ", read audio/media tags"),
(HAVE_BWRAP, "bwrap", "sandbox to make ffmpeg less dangerous", not ANYWIN and not UNIX),
(HAVE_MUTAGEN, "mutagen", "read audio tags (ffprobe is better but slower)"),
(HAVE_ARGON2, "argon2", "secure password hashing (advanced users only)"),
(HAVE_ZMQ, "pyzmq", "send zeromq messages from event-hooks"),
@ -1026,17 +1039,21 @@ class SvcHub(object):
(H_PIL_AVIF, "pillow-avif", "read .avif pics with pillow (rarely useful)"),
(HAVE_RAWPY, "rawpy", "read RAW images"),
(HAVE_DCRAW, "libraw", "read RAW images"),
(HAVE_PSUTIL, "psutil", "improved plugin cleanup (rarely useful)", ANYWIN),
]
if ANYWIN:
to_check += [
(HAVE_PSUTIL, "psutil", "improved plugin cleanup (rarely useful)")
]
# fmt: on
verbose = self.args.deps
if verbose:
self.log("dependencies", "")
for have, feat, what in to_check:
for zc in to_check:
try:
have, feat, what = zc
except:
have, feat, what, zb = zc
if not zb:
continue
lst = fok if have else fng
lst.append((feat, what))
if verbose:
@ -1203,6 +1220,15 @@ class SvcHub(object):
vsa = [x.upper() for x in vsa if x]
setattr(al, k + "_set", set(vsa))
zs = "th_bwrap"
for k in zs.split(" "):
zsl = [x for x in str(getattr(al, k)).split(" ") if x]
zbl = [x.encode("ascii", "replace") for x in zsl]
setattr(al, k + "_s", zsl)
setattr(al, k + "_b", zbl)
TH_BWRAP[:] = al.th_bwrap_b
zs = "dav_ua1 lf_url sus_urls nonsus_urls ua_nodav ua_nodoc ua_nozip"
for k in zs.split(" "):
vs = getattr(al, k)
@ -1486,6 +1512,8 @@ class SvcHub(object):
for sig in sigs:
signal.signal(sig, self.signal_handler)
if sig not in BLOCK_SIGS and BLOCK_SIGS:
BLOCK_SIGS.append(sig)
if self.args.sig_thr:
Daemon(self._signal_thr, "svchub-sig")

View file

@ -18,7 +18,7 @@ from queue import Queue
from .__init__ import ANYWIN, PY2, TYPE_CHECKING, unicode
from .authsrv import VFS
from .bos import bos
from .mtag import HAVE_FFMPEG, HAVE_FFPROBE, au_unpk, ffprobe, have_ff
from .mtag import HAVE_FFMPEG, HAVE_FFPROBE, au_unpk, bwrap, ffprobe, have_ff
from .util import BytesIO # type: ignore
from .util import (
FFMPEG_URL,
@ -763,14 +763,14 @@ class ThumbSrv(object):
def _conv_dcraw(self, abspath: str, tpath: str, fmt: str, vn: VFS) -> None:
self.wait4ram(0.6, tpath)
bap = fsenc(abspath)
# fmt: off
cmd = [
b"dcraw_emu",
cmd = bwrap(HAVE_DCRAW, bap, b"") + [
b"-h", # halfsize
b"-o", b"1", # srgb
b"-s", b"0", # first frame
b"-Z", b"-", # to stdout
fsenc(abspath),
bap,
]
# fmt: on
p = sp.Popen(cmd, stdout=sp.PIPE)
@ -858,16 +858,15 @@ class ThumbSrv(object):
res = self.getres(vn, fmt)
bscale = scale.format(*list(res)).encode("utf-8")
bap_in = fsenc(abspath)
bap_out = fsenc(tpath)
# fmt: off
cmd = [
HAVE_FFMPEG,
cmd = bwrap(HAVE_FFMPEG, bap_in, bap_out) + [
b"-nostdin",
b"-v", b"error",
b"-hide_banner"
]
cmd += seek
cmd += [
b"-i", fsenc(abspath),
] + seek + [
b"-i", bap_in,
b"-map", imap,
b"-vf", bscale,
b"-frames:v", b"1",
@ -875,15 +874,15 @@ class ThumbSrv(object):
]
# fmt: on
self._ffmpeg_im_o(tpath, vn, cmd)
self._ffmpeg_im_o(bap_out, vn, cmd)
def _ffmpeg_im_o(self, tpath: str, vn: VFS, cmd: list[bytes]) -> None:
if tpath.endswith(".jpg"):
def _ffmpeg_im_o(self, tpath: bytes, vn: VFS, cmd: list[bytes]) -> None:
if tpath.endswith(b".jpg"):
cmd += [
b"-q:v",
FF_JPG_Q[vn.flags["th_qv"] // 5], # default=??
]
elif tpath.endswith(".jxl"):
elif tpath.endswith(b".jxl"):
cmd += [
b"-q:v",
unicode(vn.flags["th_qvx"]).encode("ascii"), # default=??
@ -898,7 +897,7 @@ class ThumbSrv(object):
b"6", # default=4, 0=fast, 6=max
]
cmd += [fsenc(tpath)]
cmd.append(tpath)
self._run_ff(cmd, vn, "convt")
def _run_ff(self, cmd: list[bytes], vn: VFS, kto: str, oom: int = 400) -> None:
@ -998,20 +997,21 @@ class ThumbSrv(object):
b",showwavespic=s=2048x64:colors=white"
b",convolution=1 1 1 1 1 1 1 1 1:1 1 1 1 1 1 1 1 1:1 1 1 1 1 1 1 1 1:1 -1 1 -1 5 -1 1 -1 1" # idk what im doing but it looks ok
)
bap_in = fsenc(abspath)
bap_out = fsenc(tpath)
# fmt: off
cmd = [
HAVE_FFMPEG,
cmd = bwrap(HAVE_FFMPEG, bap_in, bap_out) + [
b"-nostdin",
b"-v", b"error",
b"-hide_banner",
b"-i", fsenc(abspath),
b"-i", bap_in,
b"-filter_complex", flt,
b"-frames:v", b"1",
]
# fmt: on
cmd += [fsenc(tpath)]
cmd.append(bap_out)
self._run_ff(cmd, vn, "convt")
if "pngquant" in vn.flags:
@ -1078,19 +1078,21 @@ class ThumbSrv(object):
except:
self.untemp[tpath] = [infile]
bap_in = fsenc(abspath)
bap_out = fsenc(infile)
# fmt: off
cmd = [
HAVE_FFMPEG,
cmd = bwrap(HAVE_FFMPEG, bap_in, bap_out) + [
b"-nostdin",
b"-v", b"error",
b"-hide_banner",
b"-i", fsenc(abspath),
b"-i", bap_in,
b"-map", b"0:a:0",
b"-ac", b"1",
b"-ar", b"48000",
b"-sample_fmt", b"s16",
b"-t", b"900",
b"-y", fsenc(infile),
b"-y", bap_out,
]
# fmt: on
self._run_ff(cmd, vn, "convt")
@ -1110,20 +1112,22 @@ class ThumbSrv(object):
fc = fc.format(fco)
bap_in = fsenc(infile)
bap_out = fsenc(tpath)
# fmt: off
cmd = [
HAVE_FFMPEG,
cmd = bwrap(HAVE_FFMPEG, bap_in, bap_out) + [
b"-nostdin",
b"-v", b"error",
b"-hide_banner",
b"-i", fsenc(infile),
b"-i", bap_in,
b"-filter_complex", fc.encode("utf-8"),
b"-map", b"[o]",
b"-frames:v", b"1",
]
# fmt: on
self._ffmpeg_im_o(tpath, vn, cmd)
self._ffmpeg_im_o(bap_out, vn, cmd)
def conv_mp3(self, abspath: str, tpath: str, fmt: str, vn: VFS) -> None:
quality = self.args.q_mp3.lower()
@ -1142,24 +1146,26 @@ class ThumbSrv(object):
qk = b"-q:a"
qv = quality[1:].encode("ascii")
bap_in = fsenc(abspath)
bap_out = fsenc(tpath)
# extremely conservative choices for output format
# (always 2ch 44k1) because if a device is old enough
# to not support opus then it's probably also super picky
# fmt: off
cmd = [
HAVE_FFMPEG,
cmd = bwrap(HAVE_FFMPEG, bap_in, bap_out) + [
b"-nostdin",
b"-v", b"error",
b"-hide_banner",
b"-i", fsenc(abspath),
b"-i", bap_in,
] + self.big_tags(rawtags) + [
b"-map", b"0:a:0",
b"-ar", b"44100",
b"-ac", b"2",
b"-c:a", b"libmp3lame",
qk, qv,
fsenc(tpath)
bap_out,
]
# fmt: on
self._run_ff(cmd, vn, "aconvt", oom=300)
@ -1175,16 +1181,18 @@ class ThumbSrv(object):
self.log("conv2 flac", 6)
bap_in = fsenc(abspath)
bap_out = fsenc(tpath)
# fmt: off
cmd = [
HAVE_FFMPEG,
cmd = bwrap(HAVE_FFMPEG, bap_in, bap_out) + [
b"-nostdin",
b"-v", b"error",
b"-hide_banner",
b"-i", fsenc(abspath),
b"-i", bap_in,
b"-map", b"0:a:0",
b"-c:a", b"flac",
fsenc(tpath)
bap_out,
]
# fmt: on
self._run_ff(cmd, vn, "aconvt", oom=300)
@ -1210,16 +1218,18 @@ class ThumbSrv(object):
self.log("conv2 wav", 6)
bap_in = fsenc(abspath)
bap_out = fsenc(tpath)
# fmt: off
cmd = [
HAVE_FFMPEG,
cmd = bwrap(HAVE_FFMPEG, bap_in, bap_out) + [
b"-nostdin",
b"-v", b"error",
b"-hide_banner",
b"-i", fsenc(abspath),
b"-i", bap_in,
b"-map", b"0:a:0",
b"-c:a", codec,
fsenc(tpath)
bap_out,
]
# fmt: on
self._run_ff(cmd, vn, "aconvt", oom=300)
@ -1271,19 +1281,21 @@ class ThumbSrv(object):
except:
pass
bap_in = fsenc(abspath)
bap_out = fsenc(tpath)
# fmt: off
cmd = [
HAVE_FFMPEG,
cmd = bwrap(HAVE_FFMPEG, bap_in, bap_out) + [
b"-nostdin",
b"-v", b"error",
b"-hide_banner",
b"-i", fsenc(abspath),
b"-i", bap_in,
] + tagset + [
b"-map", b"0:a:0",
b"-ac", ac,
] + benc + [
b"-f", container,
fsenc(tpath)
bap_out,
]
# fmt: on
self._run_ff(cmd, vn, "aconvt", oom=300)
@ -1312,19 +1324,21 @@ class ThumbSrv(object):
self.log("conv2 caf-tmp [%s]" % (enc,), 6)
benc = enc.encode("ascii").split(b" ")
bap_in = fsenc(abspath)
bap_out = fsenc(tmp_opus)
# fmt: off
cmd = [
HAVE_FFMPEG,
cmd = bwrap(HAVE_FFMPEG, bap_in, bap_out) + [
b"-nostdin",
b"-v", b"error",
b"-hide_banner",
b"-i", fsenc(abspath),
b"-i", bap_in,
b"-map_metadata", b"-1",
b"-map", b"0:a:0",
b"-ac", b"2",
] + benc + [
b"-f", b"opus",
fsenc(tmp_opus)
bap_out,
]
# fmt: on
self._run_ff(cmd, vn, "aconvt", oom=300)
@ -1338,20 +1352,21 @@ class ThumbSrv(object):
if dur < 20 or sz < 256 * 1024:
zs = bq.decode("ascii")
self.log("conv2 caf-transcode; dur=%d sz=%d q=%s" % (dur, sz, zs), 6)
bap_in = fsenc(abspath)
bap_out = fsenc(tpath)
# fmt: off
cmd = [
HAVE_FFMPEG,
cmd = bwrap(HAVE_FFMPEG, bap_in, bap_out) + [
b"-nostdin",
b"-v", b"error",
b"-hide_banner",
b"-i", fsenc(abspath),
b"-i", bap_in,
b"-filter_complex", b"anoisesrc=a=0.001:d=7:c=pink,asplit[l][r]; [l][r]amerge[s]; [0:a:0][s]amix",
b"-map_metadata", b"-1",
b"-ac", b"2",
b"-c:a", b"libopus",
b"-b:a", bq,
b"-f", b"caf",
fsenc(tpath)
bap_out,
]
# fmt: on
self._run_ff(cmd, vn, "aconvt", oom=300)
@ -1359,18 +1374,19 @@ class ThumbSrv(object):
else:
# simple remux should be safe
self.log("conv2 caf-remux; dur=%d sz=%d" % (dur, sz), 6)
bap_in = fsenc(tmp_opus)
bap_out = fsenc(tpath)
# fmt: off
cmd = [
HAVE_FFMPEG,
cmd = bwrap(HAVE_FFMPEG, bap_in, bap_out) + [
b"-nostdin",
b"-v", b"error",
b"-hide_banner",
b"-i", fsenc(tmp_opus),
b"-i", bap_in,
b"-map_metadata", b"-1",
b"-map", b"0:a:0",
b"-c:a", b"copy",
b"-f", b"caf",
fsenc(tpath)
bap_out,
]
# fmt: on
self._run_ff(cmd, vn, "aconvt", oom=300)

View file

@ -22,6 +22,7 @@ from .util import (
quotep,
s3dec,
vjoin,
vjoins,
)
if HAVE_SQLITE3:
@ -437,7 +438,7 @@ class U2idx(object):
if rd.startswith("//") or fn.startswith("//"):
rd, fn = s3dec(rd, fn)
vp = vjoin(vjoin(vtop, rd), fn)
vp = vjoins(vtop, rd, fn)
if vp in seen_rps:
continue

View file

@ -70,6 +70,7 @@ from .util import (
ub64enc,
unhumanize,
vjoin,
vjoins,
vsplit,
w8b64dec,
w8b64enc,
@ -3037,7 +3038,7 @@ class Up2k(object):
raise Pebkac(500, "too many xbu relocs, giving up")
ptop = cj["ptop"]
if not self.register_vpath(ptop, cj["vcfg"]):
if not self.register_vpath(ptop, cj.pop("vcfg")):
if ptop not in self.registry:
raise Pebkac(410, "location unavailable")
@ -3190,7 +3191,7 @@ class Up2k(object):
c2 = None
for cur, dp_dir, dp_fn in lost:
t = "forgetting desynced db entry: %r"
self.log(t % ("/" + vjoin(vjoin(vfs.vpath, dp_dir), dp_fn)))
self.log(t % ("/" + vjoins(vfs.vpath, dp_dir, dp_fn)))
self.db_rm(cur, vfs.flags, dp_dir, dp_fn, cj["size"])
if c2 and c2 != cur:
c2.connection.commit()
@ -3280,7 +3281,10 @@ class Up2k(object):
vfs.lim.nup(cj["addr"])
vfs.lim.bup(cj["addr"], cj["size"])
if "done" not in job:
if "rvp0" in job and wark in reg:
# xbu reloc; accept wrong path
job["addr"] = cj["addr"]
elif "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"
err += "/" + quotep(vsrc) + " "
@ -3370,9 +3374,9 @@ class Up2k(object):
x = pathmod(self.vfs, dst, vp, hr["reloc"])
if x:
ud1 = (vfs.vpath, job["prel"], job["name"])
job["rvp0"] = vjoins(*ud1)
pdir, _, job["name"], (vfs, rem) = x
dst = os.path.join(pdir, job["name"])
job["vcfg"] = vfs.flags
job["ptop"] = vfs.realpath
job["vtop"] = vfs.vpath
job["prel"] = rem
@ -3380,8 +3384,19 @@ class Up2k(object):
ud2 = (vfs.vpath, job["prel"], job["name"])
if ud1 != ud2:
# print(json.dumps(job, sort_keys=True, indent=4))
job["vcfg"] = vfs.flags
job["hash"] = cj["hash"]
self.log("xbu reloc1:%d..." % (depth,), 6)
t = "xbu reloc1=%d ptop=%r vtop=%r prel=%r name=%r"
t = t % (
depth,
job["ptop"],
job["vtop"],
job["prel"],
job["name"],
)
self.log(t, 6)
zs = djoin(job["ptop"], job["prel"])
bos.makedirs(zs, vf=vfs.flags)
return self._handle_json(job, depth + 1)
job["name"] = self._untaken(pdir, job, now)
@ -5263,15 +5278,25 @@ class Up2k(object):
x = pathmod(self.vfs, ap_chk, vp_chk, hr["reloc"])
if x:
ud1 = (vfs.vpath, job["prel"], job["name"])
job["rvp0"] = vjoins(*ud1)
pdir, _, job["name"], (vfs, rem) = x
job["vcfg"] = vf = vfs.flags
vf = vfs.flags
job["ptop"] = vfs.realpath
job["vtop"] = vfs.vpath
job["prel"] = rem
job["name"] = sanitize_fn(job["name"])
ud2 = (vfs.vpath, job["prel"], job["name"])
if ud1 != ud2:
self.log("xbu reloc2:%d..." % (depth,), 6)
job["vcfg"] = vf
t = "xbu reloc2=%d ptop=%r vtop=%r prel=%r name=%r" % (
depth,
job["ptop"],
job["vtop"],
job["prel"],
job["name"],
)
self.log(t, 6)
bos.makedirs(djoin(job["ptop"], job["prel"]), vf=vf)
return self._handle_json(job, depth + 1)
job["name"] = self._untaken(pdir, job, job["t0"])

View file

@ -326,7 +326,14 @@ except:
BITNESS = struct.calcsize("P") * 8
CAN_SIGMASK = not (ANYWIN or PY2 or GRAAL)
try:
if ANYWIN or PY2 or GRAAL or not hasattr(signal, "pthread_sigmask"):
raise Exception()
BLOCK_SIGS = [signal.SIGINT, signal.SIGTERM, signal.SIGHUP, signal.SIGUSR1]
CAN_SIGMASK = True
except:
BLOCK_SIGS = []
CAN_SIGMASK = False
RE_ANSI = re.compile("\033\\[[^mK]*[mK]")
@ -650,6 +657,14 @@ if EXE:
pass
try:
if PY2 or ANYWIN:
raise Exception()
HAVE_BWRAP = shutil.which("bwrap")
except:
HAVE_BWRAP = ""
def py_desc() -> str:
interp = platform.python_implementation()
py_ver = ".".join([str(x) for x in sys.version_info])
@ -832,10 +847,8 @@ class Daemon(threading.Thread):
self.start()
def run(self):
if CAN_SIGMASK:
signal.pthread_sigmask(
signal.SIG_BLOCK, [signal.SIGINT, signal.SIGTERM, signal.SIGUSR1]
)
if BLOCK_SIGS:
signal.pthread_sigmask(signal.SIG_BLOCK, BLOCK_SIGS)
self.fun(*self.a, **self.ka)
@ -1778,9 +1791,7 @@ def log_thrs(log: Callable[[str, str, int], None], ival: float, name: str) -> No
def _sigblock():
signal.pthread_sigmask(
signal.SIG_BLOCK, [signal.SIGINT, signal.SIGTERM, signal.SIGUSR1]
)
signal.pthread_sigmask(signal.SIG_BLOCK, BLOCK_SIGS)
sigblock = _sigblock if CAN_SIGMASK else noop
@ -2676,6 +2687,10 @@ def vjoin(rd: str, fn: str) -> str:
return rd or fn
def vjoins(*a: str) -> str:
return "/".join([x for x in a if x])
# url-join
def ujoin(rd: str, fn: str) -> str:
if rd and fn:

View file

@ -3760,7 +3760,7 @@ function sortfiles(nodes) {
if ((v + '').indexOf('<a ') === 0)
v = v.split('>')[1];
else if (name == "href" && v)
v = uricom_dec(v);
v = uri2txt(v, true);
nodes[b]._sv = v
}
@ -7873,7 +7873,7 @@ var treectl = (function () {
delete res['a'];
var keys = Object.keys(res);
for (var a = 0; a < keys.length; a++)
keys[a] = [uricom_dec(keys[a]), keys[a]];
keys[a] = [uri2txt(keys[a]), keys[a]];
if (ENATSORT)
keys.sort(function (a, b) { return NATSORT.compare(a[0], b[0]); });
@ -8985,6 +8985,13 @@ var msel = (function () {
return;
}
if (this.status == 405) {
tb.value = '';
sf.textContent = 'already existed';
treectl.goto(this.vp + uricom_enc(this.dn) + '/', true);
return tree_scrollto();
}
xhrchk(this, L.fd_xe1, L.fd_xe2);
if (this.status !== 201) {

View file

@ -877,6 +877,16 @@ function url_enc(txt) {
return ret.join('/');
}
function uri2txt(txt, unslash) {
try {
txt = decodeURIComponent(txt.split('?')[0]);
}
catch (ex) {
console.log("ucd-err [" + txt + "]");
}
return unslash ? txt.replace(/\/$/, '') : txt;
}
function uricom_dec(txt) {
try {

View file

@ -9,7 +9,7 @@ ENV XDG_CONFIG_HOME=/cfg
ARG ADD_PKG=""
RUN apk --no-cache add !pyc ${ADD_PKG} \
tzdata wget mimalloc2 mimalloc2-insecure \
tzdata wget mimalloc2 mimalloc2-insecure bubblewrap \
py3-jinja2 py3-argon2-cffi py3-pyzmq \
py3-openssl py3-paramiko py3-pillow

View file

@ -12,7 +12,7 @@ COPY i/bin/mtag/install-deps.sh ./
COPY i/bin/mtag/audio-bpm.py /mtag/
COPY i/bin/mtag/audio-key.py /mtag/
RUN apk add -U !pyc ${ADD_PKG} \
tzdata wget mimalloc2 mimalloc2-insecure \
tzdata wget mimalloc2 mimalloc2-insecure bubblewrap \
py3-jinja2 py3-argon2-cffi py3-pyzmq \
py3-openssl py3-paramiko py3-pillow \
py3-pip \

View file

@ -9,7 +9,7 @@ ENV XDG_CONFIG_HOME=/cfg
ARG ADD_PKG=""
RUN apk add -U !pyc ${ADD_PKG} \
tzdata wget mimalloc2 mimalloc2-insecure \
tzdata wget mimalloc2 mimalloc2-insecure bubblewrap \
py3-jinja2 py3-argon2-cffi py3-pyzmq \
py3-openssl py3-paramiko py3-pillow \
py3-pip \

View file

@ -54,9 +54,14 @@ with image size after installation and when gzipped
[`ac` is recommended](https://hub.docker.com/r/copyparty/ac) since the additional features available in `iv` and `dj` are rarely useful
most editions support `x86`, `x86_64`, `armhf`, `aarch64`, `ppc64le`, `s390x`
* `dj` doesn't run on `ppc64le`, `s390x`, `armhf`
* `iv` doesn't run on `ppc64le`, `s390x`
| this architecture | can run these editions |
| ------------------------------ | ----------------------------- |
| `x86` aka `i386` aka `386` | `min`, `im`, `ac`, `iv`, `dj` |
| `x86_64` aka `x64` aka `amd64` | `min`, `im`, `ac`, `iv`, `dj` |
| `AArch64` aka `arm64/v8` | `min`, `im`, `ac`, `iv`, `dj` |
| `arm32` aka `arm/v7` | `min`, `im`, `ac` |
| `ppc64le` aka `PowerPC` | `min`, `im`, `ac` |
| `s390x` aka `IBM mainframe` | `min`, `im`, `ac` |
> NOTE: the following editions are unfinished experiments, and not published anywhere: djd djf djff dju

View file

@ -8,7 +8,7 @@ all:
ff:
# legally comfy
/usr/bin/time ./build-no265.sh img
/usr/bin/time ./build-no265.sh pull img
zlib:

View file

@ -1,9 +1,13 @@
#!/bin/ash
set -e
AVER=3.24
[ $1 = 1 ] && hub=1
uname -a
apk update
apk upgrade -al
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
@ -13,7 +17,7 @@ cp -pv /root/.abuild/*.pub /etc/apk/keys/ || abuild-keygen -ina
mkdir /ffmpeg
cd /ffmpeg
base=https://github.com/alpinelinux/aports/raw/refs/heads/3.23-stable/community/ffmpeg/
base=https://github.com/alpinelinux/aports/raw/refs/heads/$AVER-stable/community/ffmpeg/
wget ${base}APKBUILD
awk <APKBUILD -vb="$base" '/"/{o=0}/^source=/{o=1;next}o{print b $1}' | wget -i-
cp -pv APKBUILD /root/
@ -29,16 +33,23 @@ prepare() {
default_prepare
tar -cC/opt/patch/ffmpeg . | tar -x
patch -p1 <aac-lc-only.patch
awk >t <libavcodec/aac/aacdec_tab.c '/^[^ \t]/{o=0} /^(static|const).*( sbr_|_hcod)/{o=1} !o{print;next} {gsub(/\{ *-?[0-9]+, *-?[0-9]+ *\}/, "{ 1, 1 }")}1'
mv t libavcodec/aac/aacdec_tab.c
# invent the missing disable-option for this crap
sed -ri 's/(^v4l2_m2m_deps=")/\1videotoolbox /' configure
}
EOF
##
## shrink-ray
sed -ri 's/--enable-lib(bluray|placebo|rav1e|shaderc)/--disable-lib\1/; s/--enable-(vdpau)/--disable-\1/; s/\b(rav1e|shaderc)-dev//; s/\blib(bluray|placebo|vdpau|xfixes)-dev\b//' APKBUILD
sed -ri 's/--enable-lib(bluray|dvdnav|dvdread|placebo|rav1e|shaderc)/--disable-lib\1/; s/--enable-(vdpau)/--disable-\1/; s/\b(rav1e|shaderc)-dev//; s/\blib(bluray|placebo|vdpau|xfixes)-dev\b//' APKBUILD
# `- rm placebo+shaderc to drop spirv-tools (1.7 MiB apk)
sed -ri 's/--enable-libxcb/--disable-libxcb --disable-indev=xcbgrab --disable-ffplay --disable-encoder=opus /' APKBUILD
sed -ri 's/--enable-libxcb/--disable-libxcb --disable-indev=xcbgrab --disable-ffplay --disable-encoder=opus --disable-decoder=metasound --disable-decoder=twinvq/' APKBUILD
# `- metasound+twinvq = +450 KiB apk
sed -ri 's/\bffplay$//; s/\bsdl2-dev\b//' APKBUILD
##
@ -52,6 +63,8 @@ sed -ri 's/(--disable-vulkan)/\1 --disable-devices --disable-hwaccels --disable-
# `- s/av1/libdav1d/; s/libvorbis/vorbis/; s/opus/libopus/; libvorbis and mpg123 gets pulled in by openmpt
}
[ $1 -gt 1 ] && sed -ri 's/(--disable-libxcb )/\1--disable-doc --disable-htmlpages --disable-manpages --disable-podpages --disable-txtpages /' APKBUILD
p=/root/packages/$(abuild -A)
rm -rf $p
abuild -FrcK
@ -59,6 +72,7 @@ abuild -FrcK
mkdir $p/ex
mv $p/ffmpeg-d* $p/ex # dbg,dev,doc
cp -pv src/ffmpeg-*/ffbuild/config.log $p/
#tar -cz src > $p/.tar
[ $hub ] && rm -rf $p.hub && mv $p $p.hub

View file

@ -1,6 +1,8 @@
#!/bin/bash
set -e
AVER=3.24
[ $(id -u) -eq 0 ] && {
echo dont root
exit 1
@ -34,8 +36,8 @@ wt() {
}
[ $pull ] && {
for a in $sarchs; do # arm/v6
podman pull --arch=$a alpine:latest
for a in $sarchs; do
podman pull --arch=$a alpine:$AVER
done
podman images --format "{{.ID}} {{.History}}" |
@ -62,19 +64,20 @@ wt() {
# kill abandoned builders
ps aux | awk '/bin\/qemu-[^-]+-static/{print$2}' | xargs -r kill -9
n=0; set -x
n=0; set -xo pipefail
for a in $archs; do
n=$((n+1)); wt "$n/$a"
#[ $n -le 3 ] || continue
touch b/t.$a.1.$(date +%s)
touch b/t.$n.$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
awk '{getline a<"/proc/uptime";close("/proc/uptime");sub(/ .*/,"",a);printf"%.2f %s\n",a-p,$0;p=a}' |
tee b/log.$n.$a
touch b/t.$a.2.$(date +%s)
touch b/t.$n.$a.2.$(date +%s)
done
wt -;wt ""
}
@ -89,11 +92,23 @@ echo ok
# 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
# 3:48 x86_64-hub
# 2:46 x86_64
# 2:24 x86
# 28:50 aarch64
# 21:34 armv7
# 31:13 s390x
# 22:50 ppc64le
# 1:53:25 summa summarum
# for a in version muxers demuxers devices decoders encoders filters pix_fmts layouts sample_fmts ; do ffmpeg -hide_banner -$a; done | nc 192.168.123.1 4321
# v=3.24-stable
# echo -n https://dl-cdn.alpinelinux.org/v${v%-*}/releases/x86_64/ >aver
# curl -s $(cat aver)latest-releases.yaml | awk '/alpine-minirootfs-3.*gz$/{print$2;exit}' | grep ... >> aver
# podman import $(cat aver) a324
# f(){ p=/sys/fs;for w in cgroup user.slice user-1000.slice user@1000.service user.slice ;do p="$p/$w";echo $1>"$p/cgroup.subtree_control";done;}
# f +cpuset
# time nice podman run --cpuset-cpus=1 \
# grep -E '^[^0].*' -B2 -A1 log.1.amd64 # offbyone, whatever, just eyeball it
# f -cpuset

View file

@ -1,16 +1,19 @@
#!/bin/bash
set -e
v=3.23
AVER=3.24
v=$AVER-stable
#v=master
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 \
-Lo1 https://raw.githubusercontent.com/alpinelinux/aports/refs/heads/$v/main/musl/APKBUILD \
-Lo2 https://raw.githubusercontent.com/alpinelinux/aports/refs/heads/$v/main/python3/APKBUILD \
-Lo3 https://raw.githubusercontent.com/alpinelinux/aports/refs/heads/$v/community/ffmpeg/APKBUILD \
;
zlib= ff=

View file

@ -16,7 +16,7 @@ imgs="dj iv min im ac"
dhub_order="iv dj min im ac"
ghcr_order="ac im min dj iv"
ngs=(
iv-{ppc64le,s390x}
iv-{ppc64le,s390x,arm}
dj-{ppc64le,s390x,arm}
)