mirror of
https://github.com/9001/copyparty.git
synced 2026-06-18 20:22:27 -06:00
Compare commits
48 commits
v1.20.14
...
hovudstrau
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d33d11321f | ||
|
|
e017b1bc6e | ||
|
|
efa43f891b | ||
|
|
90639de984 | ||
|
|
5dbff4af41 | ||
|
|
5beecd6622 | ||
|
|
5a8949d595 | ||
|
|
22c3a3dd46 | ||
|
|
a00bc93fe1 | ||
|
|
a48afe87b9 | ||
|
|
c3eb9ecec2 | ||
|
|
6e75faa623 | ||
|
|
06af60276b | ||
|
|
cc80ecc341 | ||
|
|
ce33a88e25 | ||
|
|
62dc833273 | ||
|
|
f0f6933b4e | ||
|
|
e32718303c | ||
|
|
c7b80acd0d | ||
|
|
cc5420a324 | ||
|
|
c28aa08b35 | ||
|
|
6183540c61 | ||
|
|
5e806ec124 | ||
|
|
9068ec6a8e | ||
|
|
27031f73be | ||
|
|
926c6e814d | ||
|
|
4e9ad781b6 | ||
|
|
f4f97b6cc3 | ||
|
|
f23ec5d9f8 | ||
|
|
348b4bb5c7 | ||
|
|
2c778e0828 | ||
|
|
7d81b9e837 | ||
|
|
ca406472f4 | ||
|
|
30b23c6ae8 | ||
|
|
9b0268970c | ||
|
|
8c201b844e | ||
|
|
b2401ff15a | ||
|
|
1f4246c6bb | ||
|
|
83dc20f33e | ||
|
|
f432ef6d51 | ||
|
|
d7eb556cdd | ||
|
|
0bb80e9294 | ||
|
|
3b53a228b0 | ||
|
|
139ef1851e | ||
|
|
2a4c82c742 | ||
|
|
b009c585a6 | ||
|
|
da6e2ddca9 | ||
|
|
6e25d648a9 |
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -35,6 +35,7 @@ scripts/docker/base/test-aac/
|
||||||
scripts/docker/base/whl/
|
scripts/docker/base/whl/
|
||||||
scripts/docker/i/
|
scripts/docker/i/
|
||||||
scripts/deps-docker/uncomment.py
|
scripts/deps-docker/uncomment.py
|
||||||
|
scripts/deps-docker/unhint.py
|
||||||
contrib/package/arch/pkg/
|
contrib/package/arch/pkg/
|
||||||
contrib/package/arch/src/
|
contrib/package/arch/src/
|
||||||
|
|
||||||
|
|
|
||||||
37
README.md
37
README.md
|
|
@ -242,6 +242,11 @@ you may also want these, especially on servers:
|
||||||
* [nixos module](#nixos-module) to run copyparty on NixOS hosts
|
* [nixos module](#nixos-module) to run copyparty on NixOS hosts
|
||||||
* [contrib/nginx/copyparty.conf](contrib/nginx/copyparty.conf) to [reverse-proxy](#reverse-proxy) behind nginx (for better https)
|
* [contrib/nginx/copyparty.conf](contrib/nginx/copyparty.conf) to [reverse-proxy](#reverse-proxy) behind nginx (for better https)
|
||||||
|
|
||||||
|
because the following environment variables are commonly used in service-scripts, they are understood by copyparty:
|
||||||
|
|
||||||
|
* `NOTIFY_SOCKET` as provided by systemd with service type=notify (see systemd/copyparty.service above)
|
||||||
|
* `S6_NOTIFY_FD` for s6/dinit [`ready-notification = pipevar:S6_NOTIFY_FD`](https://skarnet.org/software/s6/notifywhenup.html)
|
||||||
|
|
||||||
and remember to open the ports you want; here's a complete example including every feature copyparty has to offer:
|
and remember to open the ports you want; here's a complete example including every feature copyparty has to offer:
|
||||||
```
|
```
|
||||||
firewall-cmd --permanent --add-port={80,443,3921,3922,3923,3945,3990}/tcp # --zone=libvirt
|
firewall-cmd --permanent --add-port={80,443,3921,3922,3923,3945,3990}/tcp # --zone=libvirt
|
||||||
|
|
@ -297,7 +302,7 @@ also see [comparison to similar software](./docs/versus.md)
|
||||||
* ☑ realtime streaming of growing files (logfiles and such)
|
* ☑ realtime streaming of growing files (logfiles and such)
|
||||||
* ☑ [thumbnails](#thumbnails)
|
* ☑ [thumbnails](#thumbnails)
|
||||||
* ☑ ...of images using Pillow, pyvips, or FFmpeg
|
* ☑ ...of images using Pillow, pyvips, or FFmpeg
|
||||||
* ☑ ...of RAW images using rawpy
|
* ☑ ...of RAW images using libraw-dcraw_emu or rawpy
|
||||||
* ☑ ...of videos using FFmpeg
|
* ☑ ...of videos using FFmpeg
|
||||||
* ☑ ...of audio (spectrograms) using FFmpeg
|
* ☑ ...of audio (spectrograms) using FFmpeg
|
||||||
* ☑ cache eviction (max-age; maybe max-size eventually)
|
* ☑ cache eviction (max-age; maybe max-size eventually)
|
||||||
|
|
@ -831,6 +836,7 @@ you can also zip a selection of files or folders by clicking them in the browser
|
||||||
|
|
||||||
cool trick: download a folder by appending url-params `?tar&opus` or `?tar&mp3` to transcode all audio files (except aac|m4a|mp3|ogg|opus|wma) to opus/mp3 before they're added to the archive
|
cool trick: download a folder by appending url-params `?tar&opus` or `?tar&mp3` to transcode all audio files (except aac|m4a|mp3|ogg|opus|wma) to opus/mp3 before they're added to the archive
|
||||||
* super useful if you're 5 minutes away from takeoff and realize you don't have any music on your phone but your server only has flac files and downloading those will burn through all your data + there wouldn't be enough time anyways
|
* super useful if you're 5 minutes away from takeoff and realize you don't have any music on your phone but your server only has flac files and downloading those will burn through all your data + there wouldn't be enough time anyways
|
||||||
|
* and url-param `&name=foo` changes the name of the toplevel folder in the archive to `foo`, and just `&name` removes the folder entirely
|
||||||
* and url-param `&nodot` skips dotfiles/dotfolders; they are included by default if your account has permission to see them
|
* and url-param `&nodot` skips dotfiles/dotfolders; they are included by default if your account has permission to see them
|
||||||
* and url-params `&j` / `&w` produce jpeg/webm thumbnails/spectrograms instead of the original audio/video/images (`&p` for audio waveforms)
|
* and url-params `&j` / `&w` produce jpeg/webm thumbnails/spectrograms instead of the original audio/video/images (`&p` for audio waveforms)
|
||||||
* can also be used to pregenerate thumbnails; combine with `--th-maxage=9999999` or `--th-clean=0`
|
* can also be used to pregenerate thumbnails; combine with `--th-maxage=9999999` or `--th-clean=0`
|
||||||
|
|
@ -1181,7 +1187,7 @@ open the `[🎺]` media-player-settings tab to configure it,
|
||||||
* `[flac]` converts `flac` and `wav` files into opus (if supported by browser) or mp3
|
* `[flac]` converts `flac` and `wav` files into opus (if supported by browser) or mp3
|
||||||
* `[aac]` converts `aac` and `m4a` files into opus (if supported by browser) or mp3
|
* `[aac]` converts `aac` and `m4a` files into opus (if supported by browser) or mp3
|
||||||
* `[oth]` converts all other known formats into opus (if supported by browser) or mp3
|
* `[oth]` converts all other known formats into opus (if supported by browser) or mp3
|
||||||
* `aac|ac3|aif|aiff|alac|alaw|amr|ape|au|dfpwm|dts|flac|gsm|it|m4a|m4b|m4r|mo3|mod|mp2|mp3|mpc|mptm|mt2|mulaw|ogg|okt|opus|ra|s3m|tak|tta|ulaw|wav|wma|wv|xm|xpk`
|
* `aac|ac3|aif|aiff|alac|alaw|amr|ape|au|dfpwm|dts|flac|gsm|it|m4a|m4b|m4r|mka|mo3|mod|mp2|mp3|mpc|mptm|mt2|mulaw|ogg|okt|opus|ra|s3m|tak|tta|ulaw|wav|wma|wv|xm|xpk`
|
||||||
* "transcode to":
|
* "transcode to":
|
||||||
* `[opus]` produces an `opus` whenever transcoding is necessary (the best choice on Android and PCs)
|
* `[opus]` produces an `opus` whenever transcoding is necessary (the best choice on Android and PCs)
|
||||||
* `[awo]` is `opus` in a `weba` file, good for iPhones (iOS 17.5 and newer) but Apple is still fixing some state-confusion bugs as of iOS 18.2.1
|
* `[awo]` is `opus` in a `weba` file, good for iPhones (iOS 17.5 and newer) but Apple is still fixing some state-confusion bugs as of iOS 18.2.1
|
||||||
|
|
@ -1331,9 +1337,15 @@ using arguments or config files, or a mix of both:
|
||||||
|
|
||||||
sleep better at night by telling copyparty to periodically check whether your version has a [known vulnerability](https://github.com/9001/copyparty/security/advisories)
|
sleep better at night by telling copyparty to periodically check whether your version has a [known vulnerability](https://github.com/9001/copyparty/security/advisories)
|
||||||
|
|
||||||
this feature can be enabled by setting the global-option `--vc-url` to one of the following URLs; all of them provide the same information, so which one you choose is whatever
|
this feature can be enabled by setting the global-option `--vc-url` to one of the following URLs; choose what severity level you want to be notified for:
|
||||||
* `https://api.copyparty.eu/advisories`
|
* `https://api.copyparty.eu/advisories-panic` -- only really bad stuff, the "UPGRADE NOW" kind
|
||||||
* `https://api.github.com/repos/9001/copyparty/security-advisories?per_page=9`
|
* `https://api.copyparty.eu/advisories` -- everything important / noteworthy, "upgrade when you can"
|
||||||
|
* `https://api.copyparty.eu/advisories-all` -- *everything*, including stuff that's unlikely to affect anyone
|
||||||
|
* `https://api.github.com/repos/9001/copyparty/security-advisories?per_page=9` -- same as `advisories-all`
|
||||||
|
|
||||||
|
note that `https://api.copyparty.eu/advisories` may (for example) skip some advisories rated `High` but include some `Low`; that's because an easily-reachable `Low` in a default-enabled feature is more severe than a `High` which is a theoretical bug in a contrived use of a fringe feature, but the CVE calculator would still classify that as `High`
|
||||||
|
|
||||||
|
if you want to use the github advisory feed but only care about advisories rated `medium`/`moderate` or higher, then global-option `--vc-sev medium` does that, but see previous paragraph
|
||||||
|
|
||||||
> to see what happens when a bad version is detected, try `--vc-url https://api.copyparty.eu/advisories-test`
|
> to see what happens when a bad version is detected, try `--vc-url https://api.copyparty.eu/advisories-test`
|
||||||
|
|
||||||
|
|
@ -1349,6 +1361,7 @@ config file example:
|
||||||
vc-url: https://api.copyparty.eu/advisories
|
vc-url: https://api.copyparty.eu/advisories
|
||||||
vc-age: 3 # how many hours to wait between each check
|
vc-age: 3 # how many hours to wait between each check
|
||||||
vc-exit # emergency-exit if current version is vulnerable
|
vc-exit # emergency-exit if current version is vulnerable
|
||||||
|
vc-sev: medium # only care about severity 'Medium'/'Moderate' or higher (github-only; don't use this with api.copyparty.eu)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1757,7 +1770,7 @@ avoid traversing into other filesystems using `--xdev` / volflag `:c,xdev`, ski
|
||||||
|
|
||||||
and/or you can `--xvol` / `:c,xvol` to ignore all symlinks leaving the volume's top directory, but still allow bind-mounts pointing elsewhere
|
and/or you can `--xvol` / `:c,xvol` to ignore all symlinks leaving the volume's top directory, but still allow bind-mounts pointing elsewhere
|
||||||
|
|
||||||
* symlinks are permitted with `xvol` if they point into another volume where the user has the same level of access
|
* symlinks are permitted with `xvol` if they point into another volume where the user also has some sort of access, keeping permissions from outer/initial volume
|
||||||
|
|
||||||
these options will reduce performance; unlikely worst-case estimates are 14% reduction for directory listings, 35% for download-as-tar
|
these options will reduce performance; unlikely worst-case estimates are 14% reduction for directory listings, 35% for download-as-tar
|
||||||
|
|
||||||
|
|
@ -2321,9 +2334,7 @@ if you want to change the fonts, see [./docs/rice/](./docs/rice/)
|
||||||
|
|
||||||
become a *real* webserver which people can access by just going to your IP or domain without specifying a port
|
become a *real* webserver which people can access by just going to your IP or domain without specifying a port
|
||||||
|
|
||||||
**if you're on windows,** then you just need to add the commandline argument `-p 80,443` and you're done! nice
|
**if you're on windows or macos,** then you just need to add the commandline argument `-p 80,443` and you're done! nice
|
||||||
|
|
||||||
**if you're on macos,** sorry, I don't know
|
|
||||||
|
|
||||||
**if you're on Linux,** you have the following 4 options:
|
**if you're on Linux,** you have the following 4 options:
|
||||||
|
|
||||||
|
|
@ -3203,7 +3214,7 @@ enable [thumbnails](#thumbnails) of...
|
||||||
* **HEIF pictures:** `pyvips` or `ffmpeg` or `pillow-heif`
|
* **HEIF pictures:** `pyvips` or `ffmpeg` or `pillow-heif`
|
||||||
* **AVIF pictures:** `pyvips` or `ffmpeg` or `pillow-avif-plugin` or pillow v11.3+
|
* **AVIF pictures:** `pyvips` or `ffmpeg` or `pillow-avif-plugin` or pillow v11.3+
|
||||||
* **JPEG XL pictures:** `pyvips` or `ffmpeg`
|
* **JPEG XL pictures:** `pyvips` or `ffmpeg`
|
||||||
* **RAW images:** `rawpy`, plus one of `pyvips` or `Pillow` (for some formats)
|
* **RAW photos:** either `libraw dcraw_emu` or `rawpy`, plus either `pyvips` or `Pillow`
|
||||||
|
|
||||||
enable sending [zeromq messages](#zeromq) from event-hooks: `pyzmq`
|
enable sending [zeromq messages](#zeromq) from event-hooks: `pyzmq`
|
||||||
|
|
||||||
|
|
@ -3215,6 +3226,8 @@ enable [smb](#smb-server) support (**not** recommended): `impacket==0.13.0`
|
||||||
|
|
||||||
to install FFmpeg on Windows, grab [a recent build](https://www.gyan.dev/ffmpeg/builds/ffmpeg-git-full.7z) -- you need `ffmpeg.exe` and `ffprobe.exe` from inside the `bin` folder; copy them into `C:\Windows\System32` or any other folder that's in your `%PATH%`
|
to install FFmpeg on Windows, grab [a recent build](https://www.gyan.dev/ffmpeg/builds/ffmpeg-git-full.7z) -- you need `ffmpeg.exe` and `ffprobe.exe` from inside the `bin` folder; copy them into `C:\Windows\System32` or any other folder that's in your `%PATH%`
|
||||||
|
|
||||||
|
if your ffmpeg/ffprobe binaries have nonstandard names -- such as `ffmpeg8` (macports) -- set environment variables `PRTY_FFMPEG_BIN` and `PRTY_FFPROBE_BIN` to the corret name (or full path)
|
||||||
|
|
||||||
|
|
||||||
### dependency chickenbits
|
### dependency chickenbits
|
||||||
|
|
||||||
|
|
@ -3229,6 +3242,7 @@ set any of the following environment variables to disable its associated optiona
|
||||||
| -------------------- | ------------ |
|
| -------------------- | ------------ |
|
||||||
| `PRTY_NO_ARGON2` | disable argon2-cffi password hashing |
|
| `PRTY_NO_ARGON2` | disable argon2-cffi password hashing |
|
||||||
| `PRTY_NO_CFSSL` | never attempt to generate self-signed certificates using [cfssl](https://github.com/cloudflare/cfssl) |
|
| `PRTY_NO_CFSSL` | never attempt to generate self-signed certificates using [cfssl](https://github.com/cloudflare/cfssl) |
|
||||||
|
| `PRTY_NO_DCRAW` | disable all [libraw](https://www.libraw.org/homepage)-based thumbnail support for RAW images |
|
||||||
| `PRTY_NO_FFMPEG` | **audio transcoding** goes byebye, **thumbnailing** must be handled by Pillow/libvips |
|
| `PRTY_NO_FFMPEG` | **audio transcoding** goes byebye, **thumbnailing** must be handled by Pillow/libvips |
|
||||||
| `PRTY_NO_FFPROBE` | **audio transcoding** goes byebye, **thumbnailing** must be handled by Pillow/libvips, **metadata-scanning** must be handled by mutagen |
|
| `PRTY_NO_FFPROBE` | **audio transcoding** goes byebye, **thumbnailing** must be handled by Pillow/libvips, **metadata-scanning** must be handled by mutagen |
|
||||||
| `PRTY_NO_MAGIC` | do not use [magic](https://pypi.org/project/python-magic/) for filetype detection |
|
| `PRTY_NO_MAGIC` | do not use [magic](https://pypi.org/project/python-magic/) for filetype detection |
|
||||||
|
|
@ -3243,7 +3257,8 @@ set any of the following environment variables to disable its associated optiona
|
||||||
| `PRTY_NO_PIL_WEBP` | disable use of native webp support in Pillow |
|
| `PRTY_NO_PIL_WEBP` | disable use of native webp support in Pillow |
|
||||||
| `PRTY_NO_PSUTIL` | do not use [psutil](https://pypi.org/project/psutil/) for reaping stuck hooks and plugins on Windows |
|
| `PRTY_NO_PSUTIL` | do not use [psutil](https://pypi.org/project/psutil/) for reaping stuck hooks and plugins on Windows |
|
||||||
| `PRTY_NO_PYFTPD` | disable ftp(s) server ([pyftpdlib](https://pypi.org/project/pyftpdlib/)-based) |
|
| `PRTY_NO_PYFTPD` | disable ftp(s) server ([pyftpdlib](https://pypi.org/project/pyftpdlib/)-based) |
|
||||||
| `PRTY_NO_RAW` | disable all [rawpy](https://pypi.org/project/rawpy/)-based thumbnail support for RAW images |
|
| `PRTY_NO_RAW` | same as `PRTY_NO_DCRAW` plus `PRTY_NO_RAWPY` |
|
||||||
|
| `PRTY_NO_RAWPY` | disable all [rawpy](https://pypi.org/project/rawpy/)-based thumbnail support for RAW images |
|
||||||
| `PRTY_NO_VIPS` | disable all [libvips](https://pypi.org/project/pyvips/)-based thumbnail support; will fallback to Pillow or ffmpeg |
|
| `PRTY_NO_VIPS` | disable all [libvips](https://pypi.org/project/pyvips/)-based thumbnail support; will fallback to Pillow or ffmpeg |
|
||||||
|
|
||||||
example: `PRTY_NO_PIL=1 python3 copyparty-sfx.py`
|
example: `PRTY_NO_PIL=1 python3 copyparty-sfx.py`
|
||||||
|
|
|
||||||
|
|
@ -26,12 +26,12 @@ and consider using [../docs/rclone.md](../docs/rclone.md) instead; usually a bit
|
||||||
## to run this on windows:
|
## to run this on windows:
|
||||||
* install [winfsp](https://github.com/billziss-gh/winfsp/releases/latest) and [python 3](https://www.python.org/downloads/)
|
* install [winfsp](https://github.com/billziss-gh/winfsp/releases/latest) and [python 3](https://www.python.org/downloads/)
|
||||||
* [x] add python 3.x to PATH (it asks during install)
|
* [x] add python 3.x to PATH (it asks during install)
|
||||||
* `python -m pip install --user fusepy` (or grab a copy of `fuse.py` from the `connect` page on your copyparty, and keep it in the same folder)
|
* `python -m pip install --user mfusepy` (or grab a copy of `mfusepy.py` from the `connect` page on your copyparty, and keep it in the same folder)
|
||||||
* `python ./partyfuse.py n: http://192.168.1.69:3923/`
|
* `python ./partyfuse.py n: http://192.168.1.69:3923/`
|
||||||
|
|
||||||
10% faster in [msys2](https://www.msys2.org/), 700% faster if debug prints are enabled:
|
10% faster in [msys2](https://www.msys2.org/), 700% faster if debug prints are enabled:
|
||||||
* `pacman -S mingw64/mingw-w64-x86_64-python{,-pip}`
|
* `pacman -S mingw64/mingw-w64-x86_64-python{,-pip}`
|
||||||
* `/mingw64/bin/python3 -m pip install --user fusepy`
|
* `/mingw64/bin/python3 -m pip install --user mfusepy`
|
||||||
* `/mingw64/bin/python3 ./partyfuse.py [...]`
|
* `/mingw64/bin/python3 ./partyfuse.py [...]`
|
||||||
|
|
||||||
you could replace winfsp with [dokan](https://github.com/dokan-dev/dokany/releases/latest), let me know if you [figure out how](https://github.com/dokan-dev/dokany/wiki/FUSE)
|
you could replace winfsp with [dokan](https://github.com/dokan-dev/dokany/releases/latest), let me know if you [figure out how](https://github.com/dokan-dev/dokany/wiki/FUSE)
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@ usage:
|
||||||
python partyfuse.py http://192.168.1.69:3923/ ./music
|
python partyfuse.py http://192.168.1.69:3923/ ./music
|
||||||
|
|
||||||
dependencies:
|
dependencies:
|
||||||
python3 -m pip install --user fusepy # or grab it from the connect page
|
python3 -m pip install --user mfusepy # or grab it from the connect page
|
||||||
+ on Linux: sudo apk add fuse
|
+ on Linux: sudo apk add fuse
|
||||||
+ on Macos: https://osxfuse.github.io/
|
+ on Macos: https://osxfuse.github.io/
|
||||||
+ on Windows: https://github.com/billziss-gh/winfsp/releases/latest
|
+ on Windows: https://github.com/billziss-gh/winfsp/releases/latest
|
||||||
|
|
@ -91,9 +91,14 @@ info = dbg = nullfun
|
||||||
is_dbg = False
|
is_dbg = False
|
||||||
|
|
||||||
|
|
||||||
|
try:
|
||||||
|
from mfusepy import FUSE, FuseOSError, Operations
|
||||||
|
except:
|
||||||
try:
|
try:
|
||||||
from fuse import FUSE, FuseOSError, Operations
|
from fuse import FUSE, FuseOSError, Operations
|
||||||
except:
|
except:
|
||||||
|
FUSE = None
|
||||||
|
|
||||||
if WINDOWS:
|
if WINDOWS:
|
||||||
libfuse = "install https://github.com/billziss-gh/winfsp/releases/latest"
|
libfuse = "install https://github.com/billziss-gh/winfsp/releases/latest"
|
||||||
elif MACOS:
|
elif MACOS:
|
||||||
|
|
@ -102,10 +107,11 @@ except:
|
||||||
libfuse = "apt install libfuse2\n modprobe fuse"
|
libfuse = "apt install libfuse2\n modprobe fuse"
|
||||||
|
|
||||||
m = """\033[33m
|
m = """\033[33m
|
||||||
could not import fuse; these may help:
|
could not import mfusepy; these may help:
|
||||||
{} -m pip install --user fusepy
|
{} -m pip install --user mfusepy
|
||||||
{}
|
{}
|
||||||
\033[0m"""
|
\033[0m"""
|
||||||
|
if not FUSE:
|
||||||
print(m.format(sys.executable, libfuse))
|
print(m.format(sys.executable, libfuse))
|
||||||
raise
|
raise
|
||||||
|
|
||||||
|
|
@ -143,10 +149,10 @@ def fancy_log(fmt, *a):
|
||||||
|
|
||||||
|
|
||||||
def register_wtf8():
|
def register_wtf8():
|
||||||
def wtf8_enc(text):
|
def wtf8_enc(text, errors=""):
|
||||||
return str(text).encode("utf-8", "surrogateescape"), len(text)
|
return str(text).encode("utf-8", "surrogateescape"), len(text)
|
||||||
|
|
||||||
def wtf8_dec(binary):
|
def wtf8_dec(binary, errors=""):
|
||||||
return bytes(binary).decode("utf-8", "surrogateescape"), len(binary)
|
return bytes(binary).decode("utf-8", "surrogateescape"), len(binary)
|
||||||
|
|
||||||
def wtf8_search(encoding_name):
|
def wtf8_search(encoding_name):
|
||||||
|
|
|
||||||
34
bin/u2c.py
34
bin/u2c.py
|
|
@ -1,8 +1,8 @@
|
||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
from __future__ import print_function, unicode_literals
|
from __future__ import print_function, unicode_literals
|
||||||
|
|
||||||
S_VERSION = "2.20"
|
S_VERSION = "2.22"
|
||||||
S_BUILD_DT = "2026-04-22"
|
S_BUILD_DT = "2026-06-13"
|
||||||
|
|
||||||
"""
|
"""
|
||||||
u2c.py: upload to copyparty
|
u2c.py: upload to copyparty
|
||||||
|
|
@ -733,16 +733,22 @@ def up2k_chunksize(filesize):
|
||||||
stepsize *= mul
|
stepsize *= mul
|
||||||
|
|
||||||
|
|
||||||
# mostly from copyparty/up2k.py
|
|
||||||
def get_hashlist(file, pcb, mth):
|
def get_hashlist(file, pcb, mth):
|
||||||
# type: (File, Any, Any) -> None
|
# 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`"""
|
"""generates the up2k hashlist from file contents, inserts it into `file`"""
|
||||||
|
|
||||||
chunk_sz = up2k_chunksize(file.size)
|
chunk_sz = up2k_chunksize(file.size)
|
||||||
file_rem = file.size
|
file_rem = file.size
|
||||||
file_ofs = 0
|
file_ofs = 0
|
||||||
ret = []
|
ret = []
|
||||||
with open(file.abs, "rb", 512 * 1024) as f:
|
if True:
|
||||||
t0 = time.time()
|
t0 = time.time()
|
||||||
|
|
||||||
if mth and file.size >= 1024 * 512:
|
if mth and file.size >= 1024 * 512:
|
||||||
|
|
@ -778,6 +784,18 @@ def get_hashlist(file, pcb, mth):
|
||||||
file.kchunks[k] = [v1, v2]
|
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):
|
def printlink(ar, purl, name, fk):
|
||||||
if not name:
|
if not name:
|
||||||
url = purl # srch
|
url = purl # srch
|
||||||
|
|
@ -1311,7 +1329,8 @@ class Ctl(object):
|
||||||
if self.ar.jw:
|
if self.ar.jw:
|
||||||
print("%s %s" % (wark, vp))
|
print("%s %s" % (wark, vp))
|
||||||
else:
|
else:
|
||||||
zd = datetime.datetime.fromtimestamp(max(0, file.lmod), UTC)
|
tsdt = datetime.datetime.fromtimestamp
|
||||||
|
zd = tsdt(max(0, min(2 << 36, file.lmod)), UTC)
|
||||||
dt = "%04d-%02d-%02d %02d:%02d:%02d" % (
|
dt = "%04d-%02d-%02d %02d:%02d:%02d" % (
|
||||||
zd.year,
|
zd.year,
|
||||||
zd.month,
|
zd.month,
|
||||||
|
|
@ -1586,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("--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("--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("--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 = app.add_argument_group("performance tweaks")
|
||||||
ap.add_argument("-j", type=int, metavar="CONNS", default=2, help="parallel connections")
|
ap.add_argument("-j", type=int, metavar="CONNS", default=2, help="parallel connections")
|
||||||
|
|
@ -1616,6 +1636,10 @@ NOTE: if server has --usernames enabled, then password is "username:password"
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
if ar.stdin:
|
||||||
|
wark_stdin(ar)
|
||||||
|
return
|
||||||
|
|
||||||
# msys2 doesn't uncygpath absolute paths with whitespace
|
# msys2 doesn't uncygpath absolute paths with whitespace
|
||||||
if not VT100:
|
if not VT100:
|
||||||
zsl = []
|
zsl = []
|
||||||
|
|
|
||||||
|
|
@ -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.
|
# NOTE: You generally shouldn't use this PKGBUILD on Arch, as it is mainly for testing purposes. Install copyparty using pacman instead.
|
||||||
|
|
||||||
pkgname=copyparty
|
pkgname=copyparty
|
||||||
pkgver="1.20.13"
|
pkgver="1.20.16"
|
||||||
pkgrel=1
|
pkgrel=1
|
||||||
pkgdesc="File server with accelerated resumable uploads, dedup, WebDAV, SFTP, FTP, TFTP, zeroconf, media indexer, thumbnails++"
|
pkgdesc="File server with accelerated resumable uploads, dedup, WebDAV, SFTP, FTP, TFTP, zeroconf, media indexer, thumbnails++"
|
||||||
arch=("any")
|
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")
|
source=("https://github.com/9001/${pkgname}/releases/download/v${pkgver}/${pkgname}-${pkgver}.tar.gz")
|
||||||
backup=("etc/${pkgname}/copyparty.conf" )
|
backup=("etc/${pkgname}/copyparty.conf" )
|
||||||
sha256sums=("b2af9250f7ef97a5df26df412ee082c6d2be0f0cd31d579b4fbb6aa2f3e5c271")
|
sha256sums=("625f95d65d95cdd6898510518d013905e6766c7d2ae0ea9ae7d5dec96e89e02d")
|
||||||
|
|
||||||
build() {
|
build() {
|
||||||
cd "${srcdir}/${pkgname}-${pkgver}/copyparty/web"
|
cd "${srcdir}/${pkgname}-${pkgver}/copyparty/web"
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
|
|
||||||
pkgname=copyparty
|
pkgname=copyparty
|
||||||
pkgver=1.20.13
|
pkgver=1.20.16
|
||||||
pkgrel=1
|
pkgrel=1
|
||||||
pkgdesc="File server with accelerated resumable uploads, dedup, WebDAV, SFTP, FTP, TFTP, zeroconf, media indexer, thumbnails++"
|
pkgdesc="File server with accelerated resumable uploads, dedup, WebDAV, SFTP, FTP, TFTP, zeroconf, media indexer, thumbnails++"
|
||||||
arch=("any")
|
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")
|
source=("https://github.com/9001/${pkgname}/releases/download/v${pkgver}/${pkgname}-${pkgver}.tar.gz")
|
||||||
backup=("/etc/${pkgname}.d/init" )
|
backup=("/etc/${pkgname}.d/init" )
|
||||||
sha256sums=("b2af9250f7ef97a5df26df412ee082c6d2be0f0cd31d579b4fbb6aa2f3e5c271")
|
sha256sums=("625f95d65d95cdd6898510518d013905e6766c7d2ae0ea9ae7d5dec96e89e02d")
|
||||||
|
|
||||||
build() {
|
build() {
|
||||||
cd "${srcdir}/${pkgname}-${pkgver}/copyparty/web"
|
cd "${srcdir}/${pkgname}-${pkgver}/copyparty/web"
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
{
|
{
|
||||||
"url": "https://github.com/9001/copyparty/releases/download/v1.20.13/copyparty-1.20.13.tar.gz",
|
"url": "https://github.com/9001/copyparty/releases/download/v1.20.16/copyparty-1.20.16.tar.gz",
|
||||||
"version": "1.20.13",
|
"version": "1.20.16",
|
||||||
"hash": "sha256-sq+SUPfvl6XfJt9BLuCCxtK+DwzTHVebT7tqovPlwnE="
|
"hash": "sha256-Yl+V1l2VzdaJhRBRjQE5BeZ2bH0q4Oqa59XeyW6J4C0="
|
||||||
}
|
}
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
#
|
#
|
||||||
# PROVIDE: copyparty
|
# PROVIDE: copyparty
|
||||||
# REQUIRE: networking
|
# REQUIRE: networking DAEMON FILESYSTEMS mountd
|
||||||
# KEYWORD:
|
# KEYWORD:
|
||||||
|
|
||||||
. /etc/rc.subr
|
. /etc/rc.subr
|
||||||
|
|
|
||||||
|
|
@ -77,7 +77,7 @@ web/deps/busy.mp3
|
||||||
web/deps/easymde.css
|
web/deps/easymde.css
|
||||||
web/deps/easymde.js
|
web/deps/easymde.js
|
||||||
web/deps/marked.js
|
web/deps/marked.js
|
||||||
web/deps/fuse.py
|
web/deps/mfusepy.py
|
||||||
web/deps/mini-fa.css
|
web/deps/mini-fa.css
|
||||||
web/deps/mini-fa.woff
|
web/deps/mini-fa.woff
|
||||||
web/deps/prism.css
|
web/deps/prism.css
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,7 @@ from .__init__ import (
|
||||||
MACOS,
|
MACOS,
|
||||||
PY2,
|
PY2,
|
||||||
PY36,
|
PY36,
|
||||||
|
UNIX,
|
||||||
VT100,
|
VT100,
|
||||||
WINDOWS,
|
WINDOWS,
|
||||||
E,
|
E,
|
||||||
|
|
@ -45,6 +46,7 @@ from .util import (
|
||||||
DEF_EXP,
|
DEF_EXP,
|
||||||
DEF_MTE,
|
DEF_MTE,
|
||||||
DEF_MTH,
|
DEF_MTH,
|
||||||
|
HAVE_BWRAP,
|
||||||
HAVE_IPV6,
|
HAVE_IPV6,
|
||||||
IMPLICATIONS,
|
IMPLICATIONS,
|
||||||
JINJA_VER,
|
JINJA_VER,
|
||||||
|
|
@ -1234,8 +1236,10 @@ def add_general(ap, nc, srvname):
|
||||||
ap2.add_argument("--mimes", action="store_true", help="list default mimetype mapping and exit")
|
ap2.add_argument("--mimes", action="store_true", help="list default mimetype mapping and exit")
|
||||||
ap2.add_argument("--rmagic", action="store_true", help="do expensive analysis to improve accuracy of returned mimetypes; will make file-downloads, rss, and webdav slower (volflag=rmagic)")
|
ap2.add_argument("--rmagic", action="store_true", help="do expensive analysis to improve accuracy of returned mimetypes; will make file-downloads, rss, and webdav slower (volflag=rmagic)")
|
||||||
ap2.add_argument("-j", metavar="CORES", type=int, default=1, help="num cpu-cores for uploads/downloads (0=all); keeping the default is almost always best")
|
ap2.add_argument("-j", metavar="CORES", type=int, default=1, help="num cpu-cores for uploads/downloads (0=all); keeping the default is almost always best")
|
||||||
|
ap2.add_argument("--reload-sig", metavar="S", type=u, default=("" if ANYWIN else "USR1"), help="reload server config when unix-signal \033[33mS\033[0m is received; examples: [\033[32mSIGUSR1\033[0m], [\033[32mUSR1\033[0m], [\033[32m10\033[0m]")
|
||||||
ap2.add_argument("--vc-url", metavar="URL", type=u, default="", help="URL to check for vulnerable versions (default-disabled)")
|
ap2.add_argument("--vc-url", metavar="URL", type=u, default="", help="URL to check for vulnerable versions (default-disabled)")
|
||||||
ap2.add_argument("--vc-age", metavar="HOURS", type=int, default=3, help="how many hours to wait between vulnerability checks")
|
ap2.add_argument("--vc-age", metavar="HOURS", type=int, default=3, help="how many hours to wait between vulnerability checks")
|
||||||
|
ap2.add_argument("--vc-sev", metavar="LEVEL", type=u, default="low", help="minimum severity to care about; one of these: \033[32mlow medium high critical\033[0m")
|
||||||
ap2.add_argument("--vc-exit", action="store_true", help="panic and exit if current version is vulnerable")
|
ap2.add_argument("--vc-exit", action="store_true", help="panic and exit if current version is vulnerable")
|
||||||
ap2.add_argument("--license", action="store_true", help="show licenses and exit")
|
ap2.add_argument("--license", action="store_true", help="show licenses and exit")
|
||||||
ap2.add_argument("--version", action="store_true", help="show versions and exit")
|
ap2.add_argument("--version", action="store_true", help="show versions and exit")
|
||||||
|
|
@ -1641,12 +1645,35 @@ def add_optouts(ap):
|
||||||
|
|
||||||
|
|
||||||
def add_safety(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 = 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("-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")
|
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")
|
||||||
ap2.add_argument("-sss", action="store_true", help="further increase safety: Enable logging to disk, scan for dangerous symlinks.\n └─Alias of\033[32m -ss --no-dav --no-logues --no-readme -lo=cpp-%%Y-%%m%%d-%%H%%M%%S.txt.xz --ls=**,*,ln,p,r")
|
ap2.add_argument("-sss", action="store_true", help="further increase safety: Enable logging to disk, scan for dangerous symlinks.\n └─Alias of\033[32m -ss --no-dav --no-logues --no-readme -lo=cpp-%%Y-%%m%%d-%%H%%M%%S.txt.xz --ls=**,*,ln,p,r")
|
||||||
ap2.add_argument("--ls", metavar="U[,V[,F]]", type=u, default="", help="do a sanity/safety check of all volumes on startup; arguments \033[33mUSER\033[0m,\033[33mVOL\033[0m,\033[33mFLAGS\033[0m (see \033[33m--help-ls\033[0m); example [\033[32m**,*,ln,p,r\033[0m]")
|
ap2.add_argument("--ls", metavar="U[,V[,F]]", type=u, default="", help="do a sanity/safety check of all volumes on startup; arguments \033[33mUSER\033[0m,\033[33mVOL\033[0m,\033[33mFLAGS\033[0m (see \033[33m--help-ls\033[0m); example [\033[32m**,*,ln,p,r\033[0m]")
|
||||||
ap2.add_argument("--xvol", action="store_true", help="never follow symlinks leaving the volume root, unless the link is into another volume where the user has similar access (volflag=xvol)")
|
ap2.add_argument("--xvol", action="store_true", help="never follow symlinks leaving the volume root, unless the link is into another volume where the user also has access (keeps permissions from the outer/initial volume) (volflag=xvol)")
|
||||||
ap2.add_argument("--xdev", action="store_true", help="stay within the filesystem of the volume root; do not descend into other devices (symlink or bind-mount to another HDD, ...) (volflag=xdev)")
|
ap2.add_argument("--xdev", action="store_true", help="stay within the filesystem of the volume root; do not descend into other devices (symlink or bind-mount to another HDD, ...) (volflag=xdev)")
|
||||||
ap2.add_argument("--vol-nospawn", action="store_true", help="if a volume's folder does not exist on the HDD, then do not create it (continue with warning) (volflag=nospawn)")
|
ap2.add_argument("--vol-nospawn", action="store_true", help="if a volume's folder does not exist on the HDD, then do not create it (continue with warning) (volflag=nospawn)")
|
||||||
ap2.add_argument("--vol-or-crash", action="store_true", help="if a volume's folder does not exist on the HDD, then burst into flames (volflag=assert_root)")
|
ap2.add_argument("--vol-or-crash", action="store_true", help="if a volume's folder does not exist on the HDD, then burst into flames (volflag=assert_root)")
|
||||||
|
|
@ -1677,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("--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("--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)")
|
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):
|
def add_salt(ap, fk_salt, dk_salt, ah_salt):
|
||||||
|
|
@ -1706,6 +1735,7 @@ def add_logging(ap):
|
||||||
ap2.add_argument("-lo", metavar="PATH", type=u, default="", help="logfile; use .txt for plaintext or .xz for compressed. Example: \033[32mcpp-%%Y-%%m%%d-%%H%%M%%S.txt.xz\033[0m (NB: some errors may appear on STDOUT only)")
|
ap2.add_argument("-lo", metavar="PATH", type=u, default="", help="logfile; use .txt for plaintext or .xz for compressed. Example: \033[32mcpp-%%Y-%%m%%d-%%H%%M%%S.txt.xz\033[0m (NB: some errors may appear on STDOUT only)")
|
||||||
ap2.add_argument("--flo", metavar="N", type=int, default=1, help="log format for \033[33m-lo\033[0m; [\033[32m1\033[0m]=classic/colors, [\033[32m2\033[0m]=no-color")
|
ap2.add_argument("--flo", metavar="N", type=int, default=1, help="log format for \033[33m-lo\033[0m; [\033[32m1\033[0m]=classic/colors, [\033[32m2\033[0m]=no-color")
|
||||||
ap2.add_argument("--rlo", metavar="TXT", type=u, default=".1", help="logrotate counter format; see \033[33m--help-rlo\033[0m")
|
ap2.add_argument("--rlo", metavar="TXT", type=u, default=".1", help="logrotate counter format; see \033[33m--help-rlo\033[0m")
|
||||||
|
ap2.add_argument("--logrot-sig", metavar="S", type=u, default="", help="immediately logrotate when unix-signal \033[33mS\033[0m is received; examples: [\033[32mSIGHUP\033[0m], [\033[32mHUP\033[0m], [\033[32m1\033[0m]")
|
||||||
ap2.add_argument("--no-ansi", action="store_true", default=not VT100, help="disable colors; same as environment-variable NO_COLOR")
|
ap2.add_argument("--no-ansi", action="store_true", default=not VT100, help="disable colors; same as environment-variable NO_COLOR")
|
||||||
ap2.add_argument("--ansi", action="store_true", help="force colors; overrides environment-variable NO_COLOR")
|
ap2.add_argument("--ansi", action="store_true", help="force colors; overrides environment-variable NO_COLOR")
|
||||||
ap2.add_argument("--no-logflush", action="store_true", help="don't flush the logfile after each write; tiny bit faster")
|
ap2.add_argument("--no-logflush", action="store_true", help="don't flush the logfile after each write; tiny bit faster")
|
||||||
|
|
@ -1769,16 +1799,17 @@ def add_thumbnail(ap):
|
||||||
ap2.add_argument("--th-pre-rl", metavar="SEC", type=int, default=30, help="while pregen is running, ratelimit the thumbnailer logger to one message every \033[33mSEC\033[0m seconds (only works with \033[33m-j1\033[0m); set 0 to disable ratelimit")
|
ap2.add_argument("--th-pre-rl", metavar="SEC", type=int, default=30, help="while pregen is running, ratelimit the thumbnailer logger to one message every \033[33mSEC\033[0m seconds (only works with \033[33m-j1\033[0m); set 0 to disable ratelimit")
|
||||||
ap2.add_argument("--th-covers", metavar="N,N", type=u, default="folder.png,folder.jpg,cover.png,cover.jpg", help="folder thumbnails to stat/look for; enabling \033[33m-e2d\033[0m will make these case-insensitive, and try them as dotfiles (.folder.jpg), and also automatically select thumbnails for all folders that contain pics, even if none match this pattern")
|
ap2.add_argument("--th-covers", metavar="N,N", type=u, default="folder.png,folder.jpg,cover.png,cover.jpg", help="folder thumbnails to stat/look for; enabling \033[33m-e2d\033[0m will make these case-insensitive, and try them as dotfiles (.folder.jpg), and also automatically select thumbnails for all folders that contain pics, even if none match this pattern")
|
||||||
ap2.add_argument("--th-spec-p", metavar="N", type=u, default=1, help="for music, do spectrograms or embedded coverart? [\033[32m0\033[0m]=only-art, [\033[32m1\033[0m]=prefer-art, [\033[32m2\033[0m]=only-spec")
|
ap2.add_argument("--th-spec-p", metavar="N", type=u, default=1, help="for music, do spectrograms or embedded coverart? [\033[32m0\033[0m]=only-art, [\033[32m1\033[0m]=prefer-art, [\033[32m2\033[0m]=only-spec")
|
||||||
|
ap2.add_argument("--th-spec-fl", action="store_true", help="generate spectrograms with logarithmic frequency scale instead of linear")
|
||||||
# https://pillow.readthedocs.io/en/stable/handbook/image-file-formats.html
|
# https://pillow.readthedocs.io/en/stable/handbook/image-file-formats.html
|
||||||
# https://github.com/libvips/libvips
|
# https://github.com/libvips/libvips
|
||||||
# https://stackoverflow.com/a/47612661
|
# https://stackoverflow.com/a/47612661
|
||||||
# ffmpeg -hide_banner -demuxers | awk '/^ D /{print$2}' | while IFS= read -r x; do ffmpeg -hide_banner -h demuxer=$x; done | grep -E '^Demuxer |extensions:'
|
# ffmpeg -hide_banner -demuxers | awk '/^ D /{print$2}' | while IFS= read -r x; do ffmpeg -hide_banner -h demuxer=$x; done | grep -E '^Demuxer |extensions:'
|
||||||
ap2.add_argument("--th-r-pil", metavar="T,T", type=u, default="avif,avifs,blp,bmp,cbz,dcx,dds,dib,emf,eps,epub,fits,flc,fli,fpx,gif,heic,heics,heif,heifs,icns,ico,im,j2p,j2k,jp2,jpeg,jpg,jpx,jxl,pbm,pcx,pgm,png,pnm,ppm,psd,qoi,sgi,spi,tga,tif,tiff,webp,wmf,xbm,xpm", help="image formats to decode using pillow")
|
ap2.add_argument("--th-r-pil", metavar="T,T", type=u, default="avif,avifs,blp,bmp,cbz,dcx,dds,dib,emf,eps,epub,fits,flc,fli,fpx,gif,heic,heics,heif,heifs,icns,ico,im,j2p,j2k,jp2,jpeg,jpg,jpx,jxl,pbm,pcx,pgm,png,pnm,ppm,psd,qoi,sgi,spi,tga,tif,tiff,webp,wmf,xbm,xpm", help="image formats to decode using pillow")
|
||||||
ap2.add_argument("--th-r-vips", metavar="T,T", type=u, default="3fr,avif,cr2,cr3,crw,dcr,dng,erf,exr,fit,fits,fts,gif,hdr,heic,heics,heif,heifs,jp2,jpeg,jpg,jpx,jxl,k25,mdc,mef,mrw,nef,nii,pfm,pgm,png,ppm,raf,raw,sr2,srf,svg,tif,tiff,webp,x3f", help="image formats to decode using pyvips")
|
ap2.add_argument("--th-r-vips", metavar="T,T", type=u, default="3fr,arw,avif,cr2,cr3,crw,dcr,dng,erf,exr,fit,fits,fts,gif,hdr,heic,heics,heif,heifs,jp2,jpeg,jpg,jpx,jxl,k25,kdc,mdc,mef,mrw,nef,nii,nrw,orf,pfm,pgm,png,ppm,raf,raw,rw2,sr2,srf,srw,svg,tif,tiff,webp,x3f", help="image formats to decode using pyvips")
|
||||||
ap2.add_argument("--th-r-raw", metavar="T,T", type=u, default="3fr,arw,cr2,cr3,crw,dcr,dng,erf,k25,kdc,mdc,mef,mos,mrw,nef,nrw,orf,pef,raf,raw,sr2,srf,srw,x3f", help="image formats to decode using rawpy")
|
ap2.add_argument("--th-r-raw", metavar="T,T", type=u, default="3fr,arw,cr2,cr3,crw,dcr,dng,erf,k25,kdc,mdc,mef,mos,mrw,nef,nrw,orf,pef,raf,raw,rw2,sr2,srf,srw,x3f", help="image formats to decode using rawpy (if available) or libraw's dcraw_emu")
|
||||||
ap2.add_argument("--th-r-ffi", metavar="T,T", type=u, default="apng,avif,avifs,bmp,cbz,dds,dib,epub,fit,fits,fts,gif,hdr,heic,heics,heif,heifs,icns,ico,jp2,jpeg,jpg,jpx,jxl,pbm,pcx,pfm,pgm,png,pnm,ppm,psd,qoi,sgi,tga,tif,tiff,webp,xbm,xpm", help="image formats to decode using ffmpeg")
|
ap2.add_argument("--th-r-ffi", metavar="T,T", type=u, default="apng,avif,avifs,bmp,cbz,dds,dib,epub,fit,fits,fts,gif,hdr,heic,heics,heif,heifs,icns,ico,jp2,jpeg,jpg,jpx,jxl,pbm,pcx,pfm,pgm,png,pnm,ppm,psd,qoi,sgi,tga,tif,tiff,webp,xbm,xpm", help="image formats to decode using ffmpeg")
|
||||||
ap2.add_argument("--th-r-ffv", metavar="T,T", type=u, default="3gp,asf,av1,avc,avi,flv,h264,h265,hevc,m4v,mjpeg,mjpg,mkv,mov,mp4,mpeg,mpeg2,mpegts,mpg,mpg2,mts,nut,ogm,ogv,rm,ts,vob,webm,wmv", help="video formats to decode using ffmpeg")
|
ap2.add_argument("--th-r-ffv", metavar="T,T", type=u, default="3gp,asf,av1,avc,avi,flv,h264,h265,hevc,m4v,mjpeg,mjpg,mkv,mov,mp4,mpeg,mpeg2,mpegts,mpg,mpg2,mts,nut,ogm,ogv,rm,ts,vob,webm,wmv", help="video formats to decode using ffmpeg")
|
||||||
ap2.add_argument("--th-r-ffa", metavar="T,T", type=u, default="aac,ac3,aif,aiff,alac,alaw,amr,apac,ape,au,bcstm,bfstm,brstm,bonk,dfpwm,dts,flac,gsm,ilbc,it,itgz,itxz,itz,m4a,m4b,m4r,mdgz,mdxz,mdz,mo3,mod,mp2,mp3,mpc,mptm,mt2,mulaw,oga,ogg,okt,opus,ra,s3m,s3gz,s3xz,s3z,tak,tta,ulaw,wav,wma,wv,xm,xmgz,xmxz,xmz,xpk", help="audio formats to decode using ffmpeg")
|
ap2.add_argument("--th-r-ffa", metavar="T,T", type=u, default="aac,ac3,aif,aiff,alac,alaw,amr,apac,ape,au,bcstm,bfstm,brstm,bonk,dfpwm,dts,flac,gsm,ilbc,it,itgz,itxz,itz,m4a,m4b,m4r,mdgz,mdxz,mdz,mka,mo3,mod,mp2,mp3,mpc,mptm,mt2,mulaw,oga,ogg,okt,opus,ra,s3m,s3gz,s3xz,s3z,tak,tta,ulaw,wav,wma,wv,xm,xmgz,xmxz,xmz,xpk", help="audio formats to decode using ffmpeg")
|
||||||
ap2.add_argument("--th-spec-cnv", metavar="T", type=u, default="it,itgz,itxz,itz,mdgz,mdxz,mdz,mo3,mod,s3m,s3gz,s3xz,s3z,xm,xmgz,xmxz,xmz,xpk", help="audio formats which provoke https://trac.ffmpeg.org/ticket/10797 (huge ram usage for s3xmodit spectrograms)")
|
ap2.add_argument("--th-spec-cnv", metavar="T", type=u, default="it,itgz,itxz,itz,mdgz,mdxz,mdz,mo3,mod,s3m,s3gz,s3xz,s3z,xm,xmgz,xmxz,xmz,xpk", help="audio formats which provoke https://trac.ffmpeg.org/ticket/10797 (huge ram usage for s3xmodit spectrograms)")
|
||||||
ap2.add_argument("--au-unpk", metavar="E=F.C", type=u, default="mdz=mod.zip, mdgz=mod.gz, mdxz=mod.xz, s3z=s3m.zip, s3gz=s3m.gz, s3xz=s3m.xz, xmz=xm.zip, xmgz=xm.gz, xmxz=xm.xz, itz=it.zip, itgz=it.gz, itxz=it.xz, cbz=jpg.cbz, epub=jpg.epub", help="audio/image formats to decompress before passing to ffmpeg")
|
ap2.add_argument("--au-unpk", metavar="E=F.C", type=u, default="mdz=mod.zip, mdgz=mod.gz, mdxz=mod.xz, s3z=s3m.zip, s3gz=s3m.gz, s3xz=s3m.xz, xmz=xm.zip, xmgz=xm.gz, xmxz=xm.xz, itz=it.zip, itgz=it.gz, itxz=it.xz, cbz=jpg.cbz, epub=jpg.epub", help="audio/image formats to decompress before passing to ffmpeg")
|
||||||
|
|
||||||
|
|
@ -1979,10 +2010,15 @@ def add_debug(ap):
|
||||||
ap2.add_argument("--no-scandir", action="store_true", help="kernel-bug workaround: disable scandir; do a listdir + stat on each file instead")
|
ap2.add_argument("--no-scandir", action="store_true", help="kernel-bug workaround: disable scandir; do a listdir + stat on each file instead")
|
||||||
ap2.add_argument("--no-fastboot", action="store_true", help="wait for initial filesystem indexing before accepting client requests")
|
ap2.add_argument("--no-fastboot", action="store_true", help="wait for initial filesystem indexing before accepting client requests")
|
||||||
ap2.add_argument("--no-htp", action="store_true", help="disable httpserver threadpool, create threads as-needed instead")
|
ap2.add_argument("--no-htp", action="store_true", help="disable httpserver threadpool, create threads as-needed instead")
|
||||||
|
if ANYWIN or sys.version_info < (3, 7):
|
||||||
|
ap2.add_argument("--sig-thr", action="store_true", default=True, help=argparse.SUPPRESS)
|
||||||
|
else:
|
||||||
|
ap2.add_argument("--sig-thr", action="store_true", help="start separate thread for OS-signals (try this if CTRL-C is busted)")
|
||||||
ap2.add_argument("--rm-sck", action="store_true", help="when listening on unix-sockets, do a basic delete+bind instead of the default atomic bind")
|
ap2.add_argument("--rm-sck", action="store_true", help="when listening on unix-sockets, do a basic delete+bind instead of the default atomic bind")
|
||||||
ap2.add_argument("--srch-dbg", action="store_true", help="explain search processing, and do some extra expensive sanity checks")
|
ap2.add_argument("--srch-dbg", action="store_true", help="explain search processing, and do some extra expensive sanity checks")
|
||||||
ap2.add_argument("--rclone-mdns", action="store_true", help="use mdns-domain instead of server-ip on /?hc")
|
ap2.add_argument("--rclone-mdns", action="store_true", help="use mdns-domain instead of server-ip on /?hc")
|
||||||
ap2.add_argument("--stackmon", metavar="P,S", type=u, default="", help="write stacktrace to \033[33mP\033[0math every \033[33mS\033[0m second, for example --stackmon=\033[32m./st/%%Y-%%m/%%d/%%H%%M.xz,60")
|
ap2.add_argument("--stackmon", metavar="P,S", type=u, default="", help="write stacktrace to \033[33mP\033[0math every \033[33mS\033[0m second, for example --stackmon=\033[32m./st/%%Y-%%m/%%d/%%H%%M.xz,60")
|
||||||
|
ap2.add_argument("--stack-sig", metavar="S", type=u, default="", help="show stacktrace when unix-signal \033[33mS\033[0m is received; examples: [\033[32mSIGUSR2\033[0m], [\033[32mUSR2\033[0m], [\033[32m12\033[0m]")
|
||||||
ap2.add_argument("--log-thrs", metavar="SEC", type=float, default=0.0, help="list active threads every \033[33mSEC\033[0m")
|
ap2.add_argument("--log-thrs", metavar="SEC", type=float, default=0.0, help="list active threads every \033[33mSEC\033[0m")
|
||||||
ap2.add_argument("--log-fk", metavar="REGEX", type=u, default="", help="log filekey params for files where path matches \033[33mREGEX\033[0m; [\033[32m.\033[0m] (a single dot) = all files")
|
ap2.add_argument("--log-fk", metavar="REGEX", type=u, default="", help="log filekey params for files where path matches \033[33mREGEX\033[0m; [\033[32m.\033[0m] (a single dot) = all files")
|
||||||
ap2.add_argument("--bak-flips", action="store_true", help="[up2k] if a client uploads a bitflipped/corrupted chunk, store a copy according to \033[33m--bf-nc\033[0m and \033[33m--bf-dir\033[0m")
|
ap2.add_argument("--bak-flips", action="store_true", help="[up2k] if a client uploads a bitflipped/corrupted chunk, store a copy according to \033[33m--bf-nc\033[0m and \033[33m--bf-dir\033[0m")
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
# coding: utf-8
|
# coding: utf-8
|
||||||
|
|
||||||
VERSION = (1, 20, 14)
|
VERSION = (1, 20, 16)
|
||||||
CODENAME = "sftp is fine too"
|
CODENAME = "sftp is fine too"
|
||||||
BUILD_DT = (2026, 4, 24)
|
BUILD_DT = (2026, 5, 26)
|
||||||
|
|
||||||
S_VERSION = ".".join(map(str, VERSION))
|
S_VERSION = ".".join(map(str, VERSION))
|
||||||
S_BUILD_DT = "{0:04d}-{1:02d}-{2:02d}".format(*BUILD_DT)
|
S_BUILD_DT = "{0:04d}-{1:02d}-{2:02d}".format(*BUILD_DT)
|
||||||
|
|
|
||||||
|
|
@ -627,6 +627,9 @@ class VFS(object):
|
||||||
t = "%s has no %s in %r => %r => %r"
|
t = "%s has no %s in %r => %r => %r"
|
||||||
self.log("vfs", t % (uname, msg, vpath, cvpath, ap), 6)
|
self.log("vfs", t % (uname, msg, vpath, cvpath, ap), 6)
|
||||||
|
|
||||||
|
if not err:
|
||||||
|
return None
|
||||||
|
|
||||||
t = "you don't have %s-access in %r or below %r"
|
t = "you don't have %s-access in %r or below %r"
|
||||||
raise Pebkac(err, t % (msg, "/" + cvpath, "/" + vn.vpath))
|
raise Pebkac(err, t % (msg, "/" + cvpath, "/" + vn.vpath))
|
||||||
|
|
||||||
|
|
@ -858,7 +861,7 @@ class VFS(object):
|
||||||
for le in vfs_ls:
|
for le in vfs_ls:
|
||||||
ap = absreal(os.path.join(fsroot, le[0]))
|
ap = absreal(os.path.join(fsroot, le[0]))
|
||||||
vn2 = self.chk_ap(ap)
|
vn2 = self.chk_ap(ap)
|
||||||
if not vn2 or not vn2.get("", uname, True, False):
|
if not vn2 or not vn2.get("", uname, True, False, err=0):
|
||||||
rm1.append(le)
|
rm1.append(le)
|
||||||
_ = [vfs_ls.remove(x) for x in rm1] # type: ignore
|
_ = [vfs_ls.remove(x) for x in rm1] # type: ignore
|
||||||
|
|
||||||
|
|
@ -900,20 +903,14 @@ class VFS(object):
|
||||||
|
|
||||||
def zipgen(
|
def zipgen(
|
||||||
self,
|
self,
|
||||||
vpath: str,
|
folder: str,
|
||||||
vrem: str,
|
vrem: str,
|
||||||
flt: set[str],
|
flt: set[str],
|
||||||
uname: str,
|
uname: str,
|
||||||
dirs: bool,
|
dirs: bool,
|
||||||
dots: int,
|
dots: int,
|
||||||
scandir: bool,
|
scandir: bool,
|
||||||
wrap: bool = True,
|
|
||||||
) -> Generator[dict[str, Any], None, None]:
|
) -> Generator[dict[str, Any], None, None]:
|
||||||
|
|
||||||
# if multiselect: add all items to archive root
|
|
||||||
# if single folder: the folder itself is the top-level item
|
|
||||||
folder = "" if flt or not wrap else (vpath.split("/")[-1].lstrip(".") or "top")
|
|
||||||
|
|
||||||
g = self.walk(folder, vrem, [], uname, [[True, False]], dots, scandir, False)
|
g = self.walk(folder, vrem, [], uname, [[True, False]], dots, scandir, False)
|
||||||
for _, _, vpath, apath, files, rd, vd in g:
|
for _, _, vpath, apath, files, rd, vd in g:
|
||||||
if flt:
|
if flt:
|
||||||
|
|
@ -1663,7 +1660,8 @@ class AuthSrv(object):
|
||||||
for alias, mapping in [
|
for alias, mapping in [
|
||||||
("h", "gh"),
|
("h", "gh"),
|
||||||
("G", "gG"),
|
("G", "gG"),
|
||||||
("A", "rwmda.A"),
|
("r", "g"),
|
||||||
|
("A", "rgwmda.A"),
|
||||||
]:
|
]:
|
||||||
expanded = ""
|
expanded = ""
|
||||||
for ch in mapping:
|
for ch in mapping:
|
||||||
|
|
@ -2330,7 +2328,7 @@ class AuthSrv(object):
|
||||||
zs = vol.flags.get("rotf")
|
zs = vol.flags.get("rotf")
|
||||||
if zs:
|
if zs:
|
||||||
use = True
|
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")
|
zs = vol.flags.get("maxn")
|
||||||
if zs:
|
if zs:
|
||||||
|
|
@ -3081,10 +3079,10 @@ class AuthSrv(object):
|
||||||
pwds.extend([x.split(":", 1)[1] for x in pwds if ":" in x])
|
pwds.extend([x.split(":", 1)[1] for x in pwds if ":" in x])
|
||||||
if pwds:
|
if pwds:
|
||||||
if self.ah.on:
|
if self.ah.on:
|
||||||
zs = r"(\[H\] %s:.*|[?&]%s=)([^&]+)"
|
zs = r"(\[[HO]\] %s:.*|[?&]%s=)([^&]+)"
|
||||||
zs = zs % (self.args.pw_hdr, self.args.pw_urlp)
|
zs = zs % (self.args.pw_hdr, self.args.pw_urlp)
|
||||||
else:
|
else:
|
||||||
zs = r"(\[H\] %s:.*|=)(" % (self.args.pw_hdr,)
|
zs = r"(\[[HO]\] %s:.*|=)(" % (self.args.pw_hdr,)
|
||||||
zs += "|".join(pwds) + r")([]&; ]|$)"
|
zs += "|".join(pwds) + r")([]&; ]|$)"
|
||||||
|
|
||||||
self.re_pwd = re.compile(zs)
|
self.re_pwd = re.compile(zs)
|
||||||
|
|
@ -3113,7 +3111,13 @@ class AuthSrv(object):
|
||||||
|
|
||||||
try:
|
try:
|
||||||
s_vfs, s_rem = vfs.get(
|
s_vfs, s_rem = vfs.get(
|
||||||
s_vp, s_un, "r" in s_pr, "w" in s_pr, "m" in s_pr, "d" in s_pr
|
s_vp,
|
||||||
|
s_un,
|
||||||
|
"r" in s_pr,
|
||||||
|
"w" in s_pr,
|
||||||
|
"m" in s_pr,
|
||||||
|
"d" in s_pr,
|
||||||
|
"g" in s_pr,
|
||||||
)
|
)
|
||||||
except Exception as ex:
|
except Exception as ex:
|
||||||
t = "removing share [%s] by [%s] to [%s] due to %r"
|
t = "removing share [%s] by [%s] to [%s] due to %r"
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@ if True: # pylint: disable=using-constant-test
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from .httpsrv import HttpSrv
|
from .httpsrv import HttpSrv
|
||||||
|
from .svchub import SvcHub
|
||||||
|
|
||||||
|
|
||||||
class ExceptionalQueue(Queue, object):
|
class ExceptionalQueue(Queue, object):
|
||||||
|
|
@ -50,6 +51,7 @@ class BrokerCli(object):
|
||||||
for example resolving httpconn.* in httpcli -- see lines tagged #mypy404
|
for example resolving httpconn.* in httpcli -- see lines tagged #mypy404
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
hub: "SvcHub"
|
||||||
log: "RootLogger"
|
log: "RootLogger"
|
||||||
args: argparse.Namespace
|
args: argparse.Namespace
|
||||||
asrv: AuthSrv
|
asrv: AuthSrv
|
||||||
|
|
|
||||||
|
|
@ -123,6 +123,7 @@ from .util import (
|
||||||
unescape_cookie,
|
unescape_cookie,
|
||||||
unquotep,
|
unquotep,
|
||||||
vjoin,
|
vjoin,
|
||||||
|
vjoins,
|
||||||
vol_san,
|
vol_san,
|
||||||
vroots,
|
vroots,
|
||||||
vsplit,
|
vsplit,
|
||||||
|
|
@ -1823,14 +1824,13 @@ class HttpCli(object):
|
||||||
# because lstat=true would not recurse into subfolders
|
# because lstat=true would not recurse into subfolders
|
||||||
# and this is a rare case where we actually want that
|
# and this is a rare case where we actually want that
|
||||||
fgen = vn.zipgen(
|
fgen = vn.zipgen(
|
||||||
rem,
|
"",
|
||||||
rem,
|
rem,
|
||||||
set(),
|
set(),
|
||||||
self.uname,
|
self.uname,
|
||||||
True,
|
True,
|
||||||
1,
|
1,
|
||||||
not self.args.no_scandir,
|
not self.args.no_scandir,
|
||||||
wrap=False,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
elif depth == "0":
|
elif depth == "0":
|
||||||
|
|
@ -1920,7 +1920,7 @@ class HttpCli(object):
|
||||||
df = {}
|
df = {}
|
||||||
|
|
||||||
fgen = itertools.chain([topdir], fgen)
|
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)
|
chunksz = 0x7FF8 # preferred by nginx or cf (dunno which)
|
||||||
|
|
||||||
|
|
@ -5163,6 +5163,16 @@ class HttpCli(object):
|
||||||
if items:
|
if items:
|
||||||
fn = "sel-" + fn
|
fn = "sel-" + fn
|
||||||
|
|
||||||
|
if "name" in self.ouparam:
|
||||||
|
# user-selected name for toplevel folder, or blank for none
|
||||||
|
vpath = undot(self.ouparam["name"])
|
||||||
|
elif items:
|
||||||
|
# multiselect; add all items to archive root
|
||||||
|
vpath = ""
|
||||||
|
else:
|
||||||
|
# single folder; the folder itself is the top-level item
|
||||||
|
vpath = vpath.split("/")[-1].lstrip(".") or "top"
|
||||||
|
|
||||||
if vn.flags.get("zipmax") and not (
|
if vn.flags.get("zipmax") and not (
|
||||||
vn.flags.get("zipmaxu") and self.uname != "*"
|
vn.flags.get("zipmaxu") and self.uname != "*"
|
||||||
):
|
):
|
||||||
|
|
@ -5209,7 +5219,7 @@ class HttpCli(object):
|
||||||
|
|
||||||
if cfmt:
|
if cfmt:
|
||||||
self.log("transcoding to [{}]".format(cfmt))
|
self.log("transcoding to [{}]".format(cfmt))
|
||||||
fgen = gfilter(fgen, self.thumbcli, self.uname, vpath, cfmt)
|
fgen = gfilter(fgen, self.thumbcli, self.uname, self.vpath, vpath, cfmt)
|
||||||
|
|
||||||
now = time.time()
|
now = time.time()
|
||||||
self.dl_id = "%s:%s" % (self.ip, self.addr[1])
|
self.dl_id = "%s:%s" % (self.ip, self.addr[1])
|
||||||
|
|
@ -6443,6 +6453,7 @@ class HttpCli(object):
|
||||||
s_wr = "write" in req["perms"]
|
s_wr = "write" in req["perms"]
|
||||||
s_get = "get" in req["perms"]
|
s_get = "get" in req["perms"]
|
||||||
s_dot = "dot" in req["perms"]
|
s_dot = "dot" in req["perms"]
|
||||||
|
# will_read, will_write, will_move, will_del, will_get
|
||||||
s_axs = [s_rd, s_wr, False, False, s_get]
|
s_axs = [s_rd, s_wr, False, False, s_get]
|
||||||
s_axsd = s_axs + [s_dot]
|
s_axsd = s_axs + [s_dot]
|
||||||
|
|
||||||
|
|
@ -7186,17 +7197,18 @@ class HttpCli(object):
|
||||||
dirs = []
|
dirs = []
|
||||||
files = []
|
files = []
|
||||||
ptn_hr = RE_HR
|
ptn_hr = RE_HR
|
||||||
use_abs_url = (
|
use_abs_url = is_opds or (
|
||||||
not is_opds
|
vpath and not is_ls and not is_js and not self.trailing_slash
|
||||||
and not is_ls
|
|
||||||
and not is_js
|
|
||||||
and not self.trailing_slash
|
|
||||||
and vpath
|
|
||||||
)
|
)
|
||||||
for fn in ls_names:
|
for fn in ls_names:
|
||||||
base = ""
|
base = ""
|
||||||
href = fn
|
href = fn
|
||||||
if use_abs_url:
|
if use_abs_url:
|
||||||
|
if is_opds:
|
||||||
|
base = self.args.SRS
|
||||||
|
if vpath:
|
||||||
|
base += vpath + "/"
|
||||||
|
else:
|
||||||
base = "/" + vpath + "/"
|
base = "/" + vpath + "/"
|
||||||
href = base + fn
|
href = base + fn
|
||||||
|
|
||||||
|
|
@ -7237,7 +7249,7 @@ class HttpCli(object):
|
||||||
margin = "-"
|
margin = "-"
|
||||||
|
|
||||||
sz = inf.st_size
|
sz = inf.st_size
|
||||||
zd = datetime.fromtimestamp(max(0, linf.st_mtime), UTC)
|
zd = datetime.fromtimestamp(max(0, min(2 << 36, linf.st_mtime)), UTC)
|
||||||
dt = "%04d-%02d-%02d %02d:%02d:%02d" % (
|
dt = "%04d-%02d-%02d %02d:%02d:%02d" % (
|
||||||
zd.year,
|
zd.year,
|
||||||
zd.month,
|
zd.month,
|
||||||
|
|
@ -7458,11 +7470,11 @@ class HttpCli(object):
|
||||||
if doctxt is not None:
|
if doctxt is not None:
|
||||||
j2a["doc"] = doctxt
|
j2a["doc"] = doctxt
|
||||||
|
|
||||||
|
dirs.sort(key=itemgetter("name"))
|
||||||
|
|
||||||
for d in dirs:
|
for d in dirs:
|
||||||
d["name"] += "/"
|
d["name"] += "/"
|
||||||
|
|
||||||
dirs.sort(key=itemgetter("name"))
|
|
||||||
|
|
||||||
if is_opds:
|
if is_opds:
|
||||||
# OpenSearch Description format requires a full-qualified URL and a "Short Name" under 16 characters
|
# OpenSearch Description format requires a full-qualified URL and a "Short Name" under 16 characters
|
||||||
# which will be the longname truncated in the template.
|
# which will be the longname truncated in the template.
|
||||||
|
|
@ -7527,17 +7539,26 @@ class HttpCli(object):
|
||||||
]
|
]
|
||||||
|
|
||||||
j2a["opds_osd"] = "%s%s?opds&osd" % (self.args.SRS, quotep(vpath))
|
j2a["opds_osd"] = "%s%s?opds&osd" % (self.args.SRS, quotep(vpath))
|
||||||
|
j2a["opds_id"] = uuid.uuid5(uuid.NAMESPACE_URL, vpath + "/").urn
|
||||||
|
j2a["opds_title"] = (
|
||||||
|
(vpath.rsplit("/", 1)[-1] + "/") if vpath else self.args.bname
|
||||||
|
)
|
||||||
for item in dirs:
|
for item in dirs:
|
||||||
href = item["href"]
|
href = item["href"]
|
||||||
href += ("&" if "?" in href else "?") + "opds"
|
href += ("&" if "?" in href else "?") + "opds"
|
||||||
item["href"] = href
|
item["href"] = href
|
||||||
|
item["opds_id"] = uuid.uuid5(
|
||||||
|
uuid.NAMESPACE_URL, "%s/%s" % (vpath, item["name"])
|
||||||
|
).urn
|
||||||
item["iso8601"] = "%sZ" % (item["dt"].replace(" ", "T"),)
|
item["iso8601"] = "%sZ" % (item["dt"].replace(" ", "T"),)
|
||||||
|
|
||||||
for item in files:
|
for item in files:
|
||||||
href = item["href"]
|
href = item["href"]
|
||||||
href += ("&" if "?" in href else "?") + "dl"
|
href += ("&" if "?" in href else "?") + "dl"
|
||||||
item["href"] = href
|
item["href"] = href
|
||||||
|
item["opds_id"] = uuid.uuid5(
|
||||||
|
uuid.NAMESPACE_URL, "%s/%s" % (vpath, item["name"])
|
||||||
|
).urn
|
||||||
item["iso8601"] = "%sZ" % (item["dt"].replace(" ", "T"),)
|
item["iso8601"] = "%sZ" % (item["dt"].replace(" ", "T"),)
|
||||||
|
|
||||||
if "rmagic" in self.vn.flags:
|
if "rmagic" in self.vn.flags:
|
||||||
|
|
|
||||||
|
|
@ -700,7 +700,7 @@ class HttpSrv(object):
|
||||||
if not fmts:
|
if not fmts:
|
||||||
continue
|
continue
|
||||||
log("starting for volume /%s" % (vn.vpath,), 6)
|
log("starting for volume /%s" % (vn.vpath,), 6)
|
||||||
g = vn.walk("x", "/", [], LEELOO_DALLAS, [[True]], 2, scandir, False, False)
|
g = vn.walk("", "/", [], LEELOO_DALLAS, [[True]], 2, scandir, False, False)
|
||||||
g = gfilter2(g, self, vn.vpath, fmts.split(","))
|
g = gfilter2(g, self, vn.vpath, fmts.split(","))
|
||||||
for f in g:
|
for f in g:
|
||||||
nfiles += 1
|
nfiles += 1
|
||||||
|
|
|
||||||
|
|
@ -46,24 +46,35 @@ except:
|
||||||
HAVE_MUTAGEN = False
|
HAVE_MUTAGEN = False
|
||||||
|
|
||||||
|
|
||||||
def have_ff(scmd: str) -> bool:
|
def have_ff(name: str) -> bytes:
|
||||||
if ANYWIN:
|
uname = name.upper()
|
||||||
|
if os.environ.get("PRTY_NO_" + uname):
|
||||||
|
return b""
|
||||||
|
|
||||||
|
ebin = os.environ.get("PRTY_%s_BIN" % (uname,))
|
||||||
|
try:
|
||||||
|
scmd = (ebin or name).decode("utf-8")
|
||||||
|
except:
|
||||||
|
scmd: str = ebin or name
|
||||||
|
|
||||||
|
if ANYWIN and not ebin:
|
||||||
scmd += ".exe"
|
scmd += ".exe"
|
||||||
|
|
||||||
if PY2:
|
if PY2:
|
||||||
print("# checking {}".format(scmd))
|
print("# checking %s" % (scmd,))
|
||||||
acmd = (scmd + " -version").encode("ascii").split(b" ")
|
bcmd = scmd.encode("utf-8")
|
||||||
try:
|
try:
|
||||||
sp.Popen(acmd, stdout=sp.PIPE, stderr=sp.PIPE).communicate()
|
sp.Popen([bcmd, b"-version"], stdout=sp.PIPE, stderr=sp.PIPE).communicate()
|
||||||
return True
|
return bcmd
|
||||||
except:
|
except:
|
||||||
return False
|
return b""
|
||||||
else:
|
else:
|
||||||
return bool(shutil.which(scmd))
|
return (shutil.which(scmd) or "").encode("utf-8")
|
||||||
|
|
||||||
|
|
||||||
HAVE_FFMPEG = not os.environ.get("PRTY_NO_FFMPEG") and have_ff("ffmpeg")
|
HAVE_FFMPEG = have_ff("ffmpeg")
|
||||||
HAVE_FFPROBE = not os.environ.get("PRTY_NO_FFPROBE") and have_ff("ffprobe")
|
HAVE_FFPROBE = have_ff("ffprobe")
|
||||||
|
TH_BWRAP = []
|
||||||
|
|
||||||
CBZ_PICS = set("png jpg jpeg gif bmp tga tif tiff webp avif jxl".split())
|
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")
|
CBZ_01 = re.compile(r"(^|[^0-9v])0+[01]\b")
|
||||||
|
|
@ -214,17 +225,28 @@ def au_unpk(
|
||||||
return abspath
|
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(
|
def ffprobe(
|
||||||
abspath: str, timeout: int = 60
|
abspath: str, timeout: int = 60
|
||||||
) -> tuple[dict[str, tuple[int, Any]], dict[str, list[Any]], list[Any], dict[str, Any]]:
|
) -> tuple[dict[str, tuple[int, Any]], dict[str, list[Any]], list[Any], dict[str, Any]]:
|
||||||
# ffprobe -hide_banner -show_streams -show_format --
|
# ffprobe -hide_banner -show_streams -show_format --
|
||||||
cmd = [
|
bap = fsenc(abspath)
|
||||||
b"ffprobe",
|
cmd = bwrap(HAVE_FFPROBE, bap, b"") + [
|
||||||
b"-hide_banner",
|
b"-hide_banner",
|
||||||
b"-show_streams",
|
b"-show_streams",
|
||||||
b"-show_format",
|
b"-show_format",
|
||||||
b"--",
|
b"--",
|
||||||
fsenc(abspath),
|
bap,
|
||||||
]
|
]
|
||||||
rc, so, se = runcmd(cmd, timeout=timeout, nice=True, oom=200)
|
rc, so, se = runcmd(cmd, timeout=timeout, nice=True, oom=200)
|
||||||
retchk(rc, cmd, se)
|
retchk(rc, cmd, se)
|
||||||
|
|
|
||||||
|
|
@ -61,6 +61,7 @@ def gfilter(
|
||||||
thumbcli: ThumbCli,
|
thumbcli: ThumbCli,
|
||||||
uname: str,
|
uname: str,
|
||||||
vtop: str,
|
vtop: str,
|
||||||
|
vname: str,
|
||||||
fmt: str,
|
fmt: str,
|
||||||
) -> Generator[dict[str, Any], None, None]:
|
) -> Generator[dict[str, Any], None, None]:
|
||||||
from concurrent.futures import ThreadPoolExecutor
|
from concurrent.futures import ThreadPoolExecutor
|
||||||
|
|
@ -70,7 +71,7 @@ def gfilter(
|
||||||
_pools[tp] = 1
|
_pools[tp] = 1
|
||||||
try:
|
try:
|
||||||
for f in fgen:
|
for f in fgen:
|
||||||
task = tp.submit(enthumb, thumbcli, uname, vtop, f, fmt)
|
task = tp.submit(enthumb, thumbcli, uname, vtop, vname, f, fmt)
|
||||||
pend.append((task, f))
|
pend.append((task, f))
|
||||||
if pend[0][0].done() or len(pend) > CORES * 4:
|
if pend[0][0].done() or len(pend) > CORES * 4:
|
||||||
task, f = pend.pop(0)
|
task, f = pend.pop(0)
|
||||||
|
|
@ -130,7 +131,7 @@ def gfilter2(
|
||||||
try:
|
try:
|
||||||
f = {"vp": vp, "st": fi[1]}
|
f = {"vp": vp, "st": fi[1]}
|
||||||
task = tp.submit(
|
task = tp.submit(
|
||||||
enthumb, hsrv.thumbcli, LEELOO_DALLAS, vtop, f, fmt
|
enthumb, hsrv.thumbcli, LEELOO_DALLAS, vtop, "", f, fmt
|
||||||
)
|
)
|
||||||
pend.append((task, f))
|
pend.append((task, f))
|
||||||
if pend[0][0].done() or len(pend) > CORES * 4:
|
if pend[0][0].done() or len(pend) > CORES * 4:
|
||||||
|
|
@ -152,14 +153,17 @@ def gfilter2(
|
||||||
|
|
||||||
|
|
||||||
def enthumb(
|
def enthumb(
|
||||||
thumbcli: ThumbCli, uname: str, vtop: str, f: dict[str, Any], fmt: str
|
thumbcli: ThumbCli, uname: str, vtop: str, vname: str, f: dict[str, Any], fmt: str
|
||||||
) -> dict[str, Any]:
|
) -> dict[str, Any]:
|
||||||
rem = f["vp"]
|
rem = f["vp"]
|
||||||
ext = rem.rsplit(".", 1)[-1].lower()
|
ext = rem.rsplit(".", 1)[-1].lower()
|
||||||
if (fmt == "mp3" and ext == "mp3") or (fmt == "opus" and ext in TAR_NO_OPUS):
|
if (fmt == "mp3" and ext == "mp3") or (fmt == "opus" and ext in TAR_NO_OPUS):
|
||||||
raise Exception()
|
raise Exception()
|
||||||
|
|
||||||
|
if vname:
|
||||||
vp = vjoin(vtop, rem.split("/", 1)[1])
|
vp = vjoin(vtop, rem.split("/", 1)[1])
|
||||||
|
else:
|
||||||
|
vp = vjoin(vtop, rem)
|
||||||
vn, rem = thumbcli.asrv.vfs.get(vp, uname, True, False)
|
vn, rem = thumbcli.asrv.vfs.get(vp, uname, True, False)
|
||||||
dbv, vrem = vn.get_dbv(rem)
|
dbv, vrem = vn.get_dbv(rem)
|
||||||
thp = thumbcli.get(dbv, vrem, f["st"].st_mtime, fmt)
|
thp = thumbcli.get(dbv, vrem, f["st"].st_mtime, fmt)
|
||||||
|
|
|
||||||
|
|
@ -27,13 +27,23 @@ if True: # pylint: disable=using-constant-test
|
||||||
import typing
|
import typing
|
||||||
from typing import Any, Optional, Union
|
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 .__version__ import S_VERSION, VERSION
|
||||||
from .authsrv import BAD_CFG, AuthSrv, derive_args, n_du_who, n_ver_who
|
from .authsrv import BAD_CFG, AuthSrv, derive_args, n_du_who, n_ver_who
|
||||||
from .bos import bos
|
from .bos import bos
|
||||||
from .cert import ensure_cert
|
from .cert import ensure_cert
|
||||||
from .fsutil import ramdisk_chk
|
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 .pwhash import HAVE_ARGON2
|
||||||
from .sutil import close_pools as sutil_close_pools
|
from .sutil import close_pools as sutil_close_pools
|
||||||
from .tcpsrv import TcpSrv
|
from .tcpsrv import TcpSrv
|
||||||
|
|
@ -41,19 +51,20 @@ from .th_srv import (
|
||||||
H_PIL_AVIF,
|
H_PIL_AVIF,
|
||||||
H_PIL_HEIF,
|
H_PIL_HEIF,
|
||||||
H_PIL_WEBP,
|
H_PIL_WEBP,
|
||||||
HAVE_FFMPEG,
|
HAVE_DCRAW,
|
||||||
HAVE_FFPROBE,
|
|
||||||
HAVE_PIL,
|
HAVE_PIL,
|
||||||
HAVE_RAW,
|
HAVE_RAWPY,
|
||||||
HAVE_VIPS,
|
HAVE_VIPS,
|
||||||
ThumbSrv,
|
ThumbSrv,
|
||||||
)
|
)
|
||||||
from .up2k import Up2k
|
from .up2k import Up2k
|
||||||
from .util import (
|
from .util import (
|
||||||
|
BLOCK_SIGS,
|
||||||
DEF_EXP,
|
DEF_EXP,
|
||||||
DEF_MTE,
|
DEF_MTE,
|
||||||
DEF_MTH,
|
DEF_MTH,
|
||||||
FFMPEG_URL,
|
FFMPEG_URL,
|
||||||
|
HAVE_BWRAP,
|
||||||
HAVE_PSUTIL,
|
HAVE_PSUTIL,
|
||||||
HAVE_SQLITE3,
|
HAVE_SQLITE3,
|
||||||
HAVE_ZMQ,
|
HAVE_ZMQ,
|
||||||
|
|
@ -81,6 +92,7 @@ from .util import (
|
||||||
odfusion,
|
odfusion,
|
||||||
pybin,
|
pybin,
|
||||||
read_utf8,
|
read_utf8,
|
||||||
|
signame2int,
|
||||||
start_log_thrs,
|
start_log_thrs,
|
||||||
start_stackmon,
|
start_stackmon,
|
||||||
termsize,
|
termsize,
|
||||||
|
|
@ -106,11 +118,19 @@ if PY2:
|
||||||
else:
|
else:
|
||||||
from urllib.request import Request, urlopen
|
from urllib.request import Request, urlopen
|
||||||
|
|
||||||
|
try:
|
||||||
|
from queue import SimpleQueue
|
||||||
|
except:
|
||||||
|
# yuul b. alwright
|
||||||
|
from queue import Queue as SimpleQueue
|
||||||
|
|
||||||
|
|
||||||
VER_IDP_DB = 1
|
VER_IDP_DB = 1
|
||||||
VER_SESSION_DB = 1
|
VER_SESSION_DB = 1
|
||||||
VER_SHARES_DB = 2
|
VER_SHARES_DB = 2
|
||||||
|
|
||||||
|
CVE_SEVS = {"low": 1, "medium": 2, "moderate": 2, "high": 3, "critical": 4}
|
||||||
|
|
||||||
|
|
||||||
class SvcHub(object):
|
class SvcHub(object):
|
||||||
"""
|
"""
|
||||||
|
|
@ -139,13 +159,9 @@ class SvcHub(object):
|
||||||
self.logf: Optional[typing.TextIO] = None
|
self.logf: Optional[typing.TextIO] = None
|
||||||
self.logf_base_fn = ""
|
self.logf_base_fn = ""
|
||||||
self.is_dut = False # running in unittest; always False
|
self.is_dut = False # running in unittest; always False
|
||||||
self.stop_req = False
|
|
||||||
self.stopping = False
|
self.stopping = False
|
||||||
self.stopped = False
|
self.stopped = False
|
||||||
self.reload_req = False
|
|
||||||
self.reload_mutex = threading.Lock()
|
self.reload_mutex = threading.Lock()
|
||||||
self.stop_cond = threading.Condition()
|
|
||||||
self.nsigs = 3
|
|
||||||
self.retcode = 0
|
self.retcode = 0
|
||||||
self.httpsrv_up = 0
|
self.httpsrv_up = 0
|
||||||
self.qr_tsz = None
|
self.qr_tsz = None
|
||||||
|
|
@ -155,6 +171,12 @@ class SvcHub(object):
|
||||||
self.cmon = 0
|
self.cmon = 0
|
||||||
self.tstack = 0.0
|
self.tstack = 0.0
|
||||||
|
|
||||||
|
self.sig_logrot = -999
|
||||||
|
self.sig_reload = -999
|
||||||
|
self.sig_stack = -999
|
||||||
|
self.nsigs = 7
|
||||||
|
self.sig = SimpleQueue()
|
||||||
|
|
||||||
self.iphash = HMaccas(os.path.join(self.E.cfg, "iphash"), 8)
|
self.iphash = HMaccas(os.path.join(self.E.cfg, "iphash"), 8)
|
||||||
|
|
||||||
if args.sss or args.s >= 3:
|
if args.sss or args.s >= 3:
|
||||||
|
|
@ -288,6 +310,9 @@ class SvcHub(object):
|
||||||
self.log("root", "vc-age too low for copyparty.eu; will use 3 hours")
|
self.log("root", "vc-age too low for copyparty.eu; will use 3 hours")
|
||||||
args.vc_age = zi
|
args.vc_age = zi
|
||||||
|
|
||||||
|
if args.vc_sev and args.vc_sev not in CVE_SEVS:
|
||||||
|
self.log("root", "vc-sev %r invalid; will use 'low'" % (args.vc_sev,), 3)
|
||||||
|
|
||||||
zs = ""
|
zs = ""
|
||||||
if args.th_ram_max < 0.22:
|
if args.th_ram_max < 0.22:
|
||||||
zs = "generate thumbnails"
|
zs = "generate thumbnails"
|
||||||
|
|
@ -398,7 +423,7 @@ class SvcHub(object):
|
||||||
decs.pop("vips", None)
|
decs.pop("vips", None)
|
||||||
if not HAVE_PIL:
|
if not HAVE_PIL:
|
||||||
decs.pop("pil", None)
|
decs.pop("pil", None)
|
||||||
if not HAVE_RAW:
|
if not HAVE_RAWPY and not HAVE_DCRAW:
|
||||||
decs.pop("raw", None)
|
decs.pop("raw", None)
|
||||||
if not HAVE_FFMPEG or not HAVE_FFPROBE:
|
if not HAVE_FFMPEG or not HAVE_FFPROBE:
|
||||||
decs.pop("ff", None)
|
decs.pop("ff", None)
|
||||||
|
|
@ -985,12 +1010,20 @@ class SvcHub(object):
|
||||||
def after_httpsrv_up(self) -> None:
|
def after_httpsrv_up(self) -> None:
|
||||||
self.up2k.init_vols()
|
self.up2k.init_vols()
|
||||||
|
|
||||||
Daemon(self.sd_notify, "sd-notify")
|
zb = os.environ.get("NOTIFY_SOCKET")
|
||||||
|
if zb:
|
||||||
|
Daemon(self.sd_notify, "sd-notify", (zb,))
|
||||||
|
|
||||||
|
zb = os.environ.get("S6_NOTIFY_FD")
|
||||||
|
if zb:
|
||||||
|
Daemon(self.s6_notify, "s6-notify", (zb,))
|
||||||
|
|
||||||
def _feature_test(self) -> None:
|
def _feature_test(self) -> None:
|
||||||
fok = []
|
fok = []
|
||||||
fng = []
|
fng = []
|
||||||
t_ff = "transcode audio, create spectrograms, video thumbnails"
|
t_ff = "transcode audio, create spectrograms, video thumbnails"
|
||||||
|
|
||||||
|
# fmt: off
|
||||||
to_check = [
|
to_check = [
|
||||||
(HAVE_SQLITE3, "sqlite", "sessions and file/media indexing"),
|
(HAVE_SQLITE3, "sqlite", "sessions and file/media indexing"),
|
||||||
(HAVE_PIL, "pillow", "image thumbnails (plenty fast)"),
|
(HAVE_PIL, "pillow", "image thumbnails (plenty fast)"),
|
||||||
|
|
@ -998,23 +1031,29 @@ class SvcHub(object):
|
||||||
(H_PIL_WEBP, "pillow-webp", "create thumbnails as webp files"),
|
(H_PIL_WEBP, "pillow-webp", "create thumbnails as webp files"),
|
||||||
(HAVE_FFMPEG, "ffmpeg", t_ff + ", good-but-slow image thumbnails"),
|
(HAVE_FFMPEG, "ffmpeg", t_ff + ", good-but-slow image thumbnails"),
|
||||||
(HAVE_FFPROBE, "ffprobe", t_ff + ", read audio/media tags"),
|
(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_MUTAGEN, "mutagen", "read audio tags (ffprobe is better but slower)"),
|
||||||
(HAVE_ARGON2, "argon2", "secure password hashing (advanced users only)"),
|
(HAVE_ARGON2, "argon2", "secure password hashing (advanced users only)"),
|
||||||
(HAVE_ZMQ, "pyzmq", "send zeromq messages from event-hooks"),
|
(HAVE_ZMQ, "pyzmq", "send zeromq messages from event-hooks"),
|
||||||
(H_PIL_HEIF, "pillow-heif", "read .heif pics with pillow (rarely useful)"),
|
(H_PIL_HEIF, "pillow-heif", "read .heif pics with pillow (rarely useful)"),
|
||||||
(H_PIL_AVIF, "pillow-avif", "read .avif pics with pillow (rarely useful)"),
|
(H_PIL_AVIF, "pillow-avif", "read .avif pics with pillow (rarely useful)"),
|
||||||
(HAVE_RAW, "rawpy", "read RAW images"),
|
(HAVE_RAWPY, "rawpy", "read RAW images"),
|
||||||
]
|
(HAVE_DCRAW, "libraw", "read RAW images"),
|
||||||
if ANYWIN:
|
(HAVE_PSUTIL, "psutil", "improved plugin cleanup (rarely useful)", ANYWIN),
|
||||||
to_check += [
|
|
||||||
(HAVE_PSUTIL, "psutil", "improved plugin cleanup (rarely useful)")
|
|
||||||
]
|
]
|
||||||
|
# fmt: on
|
||||||
|
|
||||||
verbose = self.args.deps
|
verbose = self.args.deps
|
||||||
if verbose:
|
if verbose:
|
||||||
self.log("dependencies", "")
|
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 = fok if have else fng
|
||||||
lst.append((feat, what))
|
lst.append((feat, what))
|
||||||
if verbose:
|
if verbose:
|
||||||
|
|
@ -1181,6 +1220,15 @@ class SvcHub(object):
|
||||||
vsa = [x.upper() for x in vsa if x]
|
vsa = [x.upper() for x in vsa if x]
|
||||||
setattr(al, k + "_set", set(vsa))
|
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"
|
zs = "dav_ua1 lf_url sus_urls nonsus_urls ua_nodav ua_nodoc ua_nozip"
|
||||||
for k in zs.split(" "):
|
for k in zs.split(" "):
|
||||||
vs = getattr(al, k)
|
vs = getattr(al, k)
|
||||||
|
|
@ -1443,31 +1491,44 @@ class SvcHub(object):
|
||||||
|
|
||||||
sigs = [signal.SIGINT, signal.SIGTERM]
|
sigs = [signal.SIGINT, signal.SIGTERM]
|
||||||
if not ANYWIN:
|
if not ANYWIN:
|
||||||
sigs.append(signal.SIGUSR1)
|
sigs.append(signal.SIGHUP)
|
||||||
|
|
||||||
|
for (opt, mem) in (
|
||||||
|
("logrot_sig", "sig_logrot"),
|
||||||
|
("reload_sig", "sig_reload"),
|
||||||
|
("stack_sig", "sig_stack"),
|
||||||
|
):
|
||||||
|
zs = getattr(self.args, opt)
|
||||||
|
if not zs:
|
||||||
|
continue
|
||||||
|
zi = signame2int(zs)
|
||||||
|
setattr(self, mem, zi)
|
||||||
|
try:
|
||||||
|
sigs.append(signal.Signals(zi))
|
||||||
|
except:
|
||||||
|
t = "using unknown signal %r as %s"
|
||||||
|
self.log("root", t % (zi, mem), 3)
|
||||||
|
sigs.append(zi)
|
||||||
|
|
||||||
for sig in sigs:
|
for sig in sigs:
|
||||||
signal.signal(sig, self.signal_handler)
|
signal.signal(sig, self.signal_handler)
|
||||||
|
if sig not in BLOCK_SIGS and BLOCK_SIGS:
|
||||||
|
BLOCK_SIGS.append(sig)
|
||||||
|
|
||||||
# macos hangs after shutdown on sigterm with while-sleep,
|
if self.args.sig_thr:
|
||||||
# windows cannot ^c stop_cond (and win10 does the macos thing but winxp is fine??)
|
Daemon(self._signal_thr, "svchub-sig")
|
||||||
# linux is fine with both,
|
|
||||||
# never lucky
|
|
||||||
if ANYWIN:
|
|
||||||
# msys-python probably fine but >msys-python
|
|
||||||
Daemon(self.stop_thr, "svchub-sig")
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
while not self.stop_req:
|
while not self.stopping:
|
||||||
time.sleep(1)
|
time.sleep(1)
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
self.shutdown()
|
|
||||||
# cant join; eats signals on win10
|
# cant join; eats signals on win10
|
||||||
while not self.stopped:
|
while not self.stopped:
|
||||||
time.sleep(0.1)
|
time.sleep(0.1)
|
||||||
else:
|
else:
|
||||||
self.stop_thr()
|
self._signal_thr()
|
||||||
|
|
||||||
def start_zeroconf(self) -> None:
|
def start_zeroconf(self) -> None:
|
||||||
self.zc_ngen += 1
|
self.zc_ngen += 1
|
||||||
|
|
@ -1525,17 +1586,6 @@ class SvcHub(object):
|
||||||
self.asrv.load_sessions(True)
|
self.asrv.load_sessions(True)
|
||||||
self.broker.reload_sessions()
|
self.broker.reload_sessions()
|
||||||
|
|
||||||
def stop_thr(self) -> None:
|
|
||||||
while not self.stop_req:
|
|
||||||
with self.stop_cond:
|
|
||||||
self.stop_cond.wait(9001)
|
|
||||||
|
|
||||||
if self.reload_req:
|
|
||||||
self.reload_req = False
|
|
||||||
self.reload(True, True)
|
|
||||||
|
|
||||||
self.shutdown()
|
|
||||||
|
|
||||||
def kill9(self, delay: float = 0.0) -> None:
|
def kill9(self, delay: float = 0.0) -> None:
|
||||||
if delay > 0.01:
|
if delay > 0.01:
|
||||||
time.sleep(delay)
|
time.sleep(delay)
|
||||||
|
|
@ -1548,26 +1598,42 @@ class SvcHub(object):
|
||||||
os.kill(os.getpid(), signal.SIGKILL)
|
os.kill(os.getpid(), signal.SIGKILL)
|
||||||
|
|
||||||
def signal_handler(self, sig: int, frame: Optional[FrameType]) -> None:
|
def signal_handler(self, sig: int, frame: Optional[FrameType]) -> None:
|
||||||
if self.stopping:
|
if sig in (signal.SIGINT, signal.SIGTERM):
|
||||||
if self.nsigs <= 0:
|
self.nsigs -= 1
|
||||||
|
|
||||||
|
if self.nsigs == 0:
|
||||||
try:
|
try:
|
||||||
threading.Thread(target=self.pr, args=("OMBO BREAKER",)).start()
|
threading.Thread(target=self.pr, args=("OMBO BREAKER",)).start()
|
||||||
time.sleep(0.1)
|
time.sleep(0.1)
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
if self.nsigs <= 0:
|
||||||
self.kill9()
|
self.kill9()
|
||||||
else:
|
|
||||||
self.nsigs -= 1
|
|
||||||
return
|
|
||||||
|
|
||||||
if not ANYWIN and sig == signal.SIGUSR1:
|
self.sig.put(sig)
|
||||||
self.reload_req = True
|
|
||||||
else:
|
|
||||||
self.stop_req = True
|
|
||||||
|
|
||||||
with self.stop_cond:
|
def _signal_thr(self) -> None:
|
||||||
self.stop_cond.notify_all()
|
while not self.stopping:
|
||||||
|
sig = self.sig.get()
|
||||||
|
self._signal_handler(sig)
|
||||||
|
|
||||||
|
def _signal_handler(self, sig: int) -> None:
|
||||||
|
if sig == self.sig_logrot:
|
||||||
|
self.log("root", "signal: logrotate")
|
||||||
|
dt = datetime.now(self.tz)
|
||||||
|
self.logf_base_fn = "\t"
|
||||||
|
self._set_next_day(dt)
|
||||||
|
|
||||||
|
elif sig == self.sig_reload:
|
||||||
|
self.log("root", "signal: reload")
|
||||||
|
self.reload(True, True)
|
||||||
|
|
||||||
|
elif sig == self.sig_stack:
|
||||||
|
self.log("root", "signal: stack%s" % (alltrace(),))
|
||||||
|
|
||||||
|
else:
|
||||||
|
self.shutdown()
|
||||||
|
|
||||||
def shutdown(self) -> None:
|
def shutdown(self) -> None:
|
||||||
if self.stopping:
|
if self.stopping:
|
||||||
|
|
@ -1575,10 +1641,8 @@ class SvcHub(object):
|
||||||
|
|
||||||
# start_log_thrs(print, 0.1, 1)
|
# start_log_thrs(print, 0.1, 1)
|
||||||
|
|
||||||
|
self.nsigs = 3
|
||||||
self.stopping = True
|
self.stopping = True
|
||||||
self.stop_req = True
|
|
||||||
with self.stop_cond:
|
|
||||||
self.stop_cond.notify_all()
|
|
||||||
|
|
||||||
ret = 1
|
ret = 1
|
||||||
try:
|
try:
|
||||||
|
|
@ -1878,12 +1942,8 @@ class SvcHub(object):
|
||||||
self.log("svchub", "cannot efficiently use multiple CPU cores")
|
self.log("svchub", "cannot efficiently use multiple CPU cores")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def sd_notify(self) -> None:
|
def sd_notify(self, zb: bytes) -> None:
|
||||||
try:
|
try:
|
||||||
zb = os.environ.get("NOTIFY_SOCKET")
|
|
||||||
if not zb:
|
|
||||||
return
|
|
||||||
|
|
||||||
addr = unicode(zb)
|
addr = unicode(zb)
|
||||||
if addr.startswith("@"):
|
if addr.startswith("@"):
|
||||||
addr = "\0" + addr[1:]
|
addr = "\0" + addr[1:]
|
||||||
|
|
@ -1895,7 +1955,19 @@ class SvcHub(object):
|
||||||
sck.connect(addr)
|
sck.connect(addr)
|
||||||
sck.sendall(b"READY=1")
|
sck.sendall(b"READY=1")
|
||||||
except:
|
except:
|
||||||
self.log("sd_notify", min_ex())
|
t = "NOTIFY_SOCKET=%s:\n%s"
|
||||||
|
self.log("sd-notify", t % (zb, min_ex()), 1)
|
||||||
|
|
||||||
|
def s6_notify(self, zb: bytes) -> None:
|
||||||
|
try:
|
||||||
|
fd = int(zb)
|
||||||
|
if fd < 3:
|
||||||
|
raise Exception("value < 3")
|
||||||
|
os.write(fd, b"\n")
|
||||||
|
os.close(fd)
|
||||||
|
except:
|
||||||
|
t = "S6_NOTIFY_FD=%s:\n%s"
|
||||||
|
self.log("s6-notify", t % (zb, min_ex()), 1)
|
||||||
|
|
||||||
def log_stacks(self) -> None:
|
def log_stacks(self) -> None:
|
||||||
td = time.time() - self.tstack
|
td = time.time() - self.tstack
|
||||||
|
|
@ -1914,6 +1986,7 @@ class SvcHub(object):
|
||||||
next_chk = 0
|
next_chk = 0
|
||||||
# self.args.vc_age = 2 / 60
|
# self.args.vc_age = 2 / 60
|
||||||
fpath = os.path.join(self.E.cfg, "vuln_advisory.json")
|
fpath = os.path.join(self.E.cfg, "vuln_advisory.json")
|
||||||
|
minsev = CVE_SEVS.get(self.args.vc_sev, 0)
|
||||||
while not self.stopping:
|
while not self.stopping:
|
||||||
now = time.time()
|
now = time.time()
|
||||||
if now < next_chk:
|
if now < next_chk:
|
||||||
|
|
@ -1957,10 +2030,13 @@ class SvcHub(object):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
sver = "0.1"
|
||||||
advisories = json.loads(jtxt)
|
advisories = json.loads(jtxt)
|
||||||
for adv in advisories:
|
for adv in advisories:
|
||||||
if adv.get("state") == "closed":
|
if adv.get("state") == "closed":
|
||||||
continue
|
continue
|
||||||
|
if CVE_SEVS.get(adv.get("severity"), 9) < minsev:
|
||||||
|
continue
|
||||||
vuln = {}
|
vuln = {}
|
||||||
for x in adv["vulnerabilities"]:
|
for x in adv["vulnerabilities"]:
|
||||||
if x["package"]["name"].lower() == "copyparty":
|
if x["package"]["name"].lower() == "copyparty":
|
||||||
|
|
@ -1978,7 +2054,6 @@ class SvcHub(object):
|
||||||
if self.args.vc_exit:
|
if self.args.vc_exit:
|
||||||
self.sigterm()
|
self.sigterm()
|
||||||
return
|
return
|
||||||
else:
|
|
||||||
t = "%sok; v%s and newer is safe"
|
t = "%sok; v%s and newer is safe"
|
||||||
self.log("ver-chk", t % (src, sver), 2)
|
self.log("ver-chk", t % (src, sver), 2)
|
||||||
next_chk = time.time() + self.args.vc_age * 3600 - age
|
next_chk = time.time() + self.args.vc_age * 3600 - age
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ from queue import Queue
|
||||||
from .__init__ import ANYWIN, PY2, TYPE_CHECKING, unicode
|
from .__init__ import ANYWIN, PY2, TYPE_CHECKING, unicode
|
||||||
from .authsrv import VFS
|
from .authsrv import VFS
|
||||||
from .bos import bos
|
from .bos import bos
|
||||||
from .mtag import HAVE_FFMPEG, HAVE_FFPROBE, au_unpk, ffprobe
|
from .mtag import HAVE_FFMPEG, HAVE_FFPROBE, au_unpk, bwrap, ffprobe, have_ff
|
||||||
from .util import BytesIO # type: ignore
|
from .util import BytesIO # type: ignore
|
||||||
from .util import (
|
from .util import (
|
||||||
FFMPEG_URL,
|
FFMPEG_URL,
|
||||||
|
|
@ -201,16 +201,22 @@ except Exception as e:
|
||||||
logging.warning("libvips found, but failed to load: " + str(e))
|
logging.warning("libvips found, but failed to load: " + str(e))
|
||||||
|
|
||||||
|
|
||||||
|
PRTY_NO_RAW = os.environ.get("PRTY_NO_RAW")
|
||||||
|
PRTY_NO_RAWPY = PRTY_NO_RAW or os.environ.get("PRTY_NO_RAWPY")
|
||||||
|
PRTY_NO_DCRAW = PRTY_NO_RAW or os.environ.get("PRTY_NO_DCRAW")
|
||||||
try:
|
try:
|
||||||
if os.environ.get("PRTY_NO_RAW"):
|
if PRTY_NO_RAWPY:
|
||||||
raise Exception()
|
raise Exception()
|
||||||
|
|
||||||
HAVE_RAW = True
|
HAVE_RAWPY = True
|
||||||
import rawpy
|
import rawpy
|
||||||
|
|
||||||
logging.getLogger("rawpy").setLevel(logging.WARNING)
|
logging.getLogger("rawpy").setLevel(logging.WARNING)
|
||||||
except:
|
except:
|
||||||
HAVE_RAW = False
|
HAVE_RAWPY = False
|
||||||
|
|
||||||
|
|
||||||
|
HAVE_DCRAW = not PRTY_NO_DCRAW and have_ff("dcraw_emu")
|
||||||
|
|
||||||
|
|
||||||
th_dir_cache = {}
|
th_dir_cache = {}
|
||||||
|
|
@ -224,11 +230,6 @@ def thumb_path(histpath: str, rem: str, mtime: float, fmt: str, ffa: set[str]) -
|
||||||
if not rd:
|
if not rd:
|
||||||
rd = "\ntop"
|
rd = "\ntop"
|
||||||
|
|
||||||
# spectrograms are never cropped; strip fullsize flag
|
|
||||||
ext = rem.split(".")[-1].lower()
|
|
||||||
if ext in ffa and fmt[:2] in ("wf", "jf", "xf"):
|
|
||||||
fmt = fmt.replace("f", "")
|
|
||||||
|
|
||||||
dcache = th_dir_cache
|
dcache = th_dir_cache
|
||||||
rd_key = rd + "\n" + fmt
|
rd_key = rd + "\n" + fmt
|
||||||
rd = dcache.get(rd_key)
|
rd = dcache.get(rd_key)
|
||||||
|
|
@ -307,6 +308,8 @@ class ThumbSrv(object):
|
||||||
if ANYWIN and self.args.no_acode:
|
if ANYWIN and self.args.no_acode:
|
||||||
self.log("download FFmpeg to fix it:\033[0m " + FFMPEG_URL, 3)
|
self.log("download FFmpeg to fix it:\033[0m " + FFMPEG_URL, 3)
|
||||||
|
|
||||||
|
self.conv_raw = self._conv_rawpy if HAVE_RAWPY else self._conv_dcraw
|
||||||
|
|
||||||
if self.args.th_clean:
|
if self.args.th_clean:
|
||||||
Daemon(self.cleaner, "thumb.cln")
|
Daemon(self.cleaner, "thumb.cln")
|
||||||
|
|
||||||
|
|
@ -467,6 +470,11 @@ class ThumbSrv(object):
|
||||||
zs = "th_dec th_no_webp th_no_jpg"
|
zs = "th_dec th_no_webp th_no_jpg"
|
||||||
for zs in zs.split(" "):
|
for zs in zs.split(" "):
|
||||||
ret.append("%s(%s)\n" % (zs, getattr(self.args, zs)))
|
ret.append("%s(%s)\n" % (zs, getattr(self.args, zs)))
|
||||||
|
zs = "th_spec_fl"
|
||||||
|
for zs in zs.split(" "):
|
||||||
|
v = getattr(self.args, zs)
|
||||||
|
if v:
|
||||||
|
ret.append("%s(%s)\n" % (zs, v))
|
||||||
zs = "th_qv th_qvx thsize th_spec_p convt"
|
zs = "th_qv th_qvx thsize th_spec_p convt"
|
||||||
for zs in zs.split(" "):
|
for zs in zs.split(" "):
|
||||||
ret.append("%s(%s)\n" % (zs, vn.flags.get(zs)))
|
ret.append("%s(%s)\n" % (zs, vn.flags.get(zs)))
|
||||||
|
|
@ -753,8 +761,39 @@ class ThumbSrv(object):
|
||||||
|
|
||||||
self.conv_image_vips(_loader, tpath, fmt, vn)
|
self.conv_image_vips(_loader, tpath, fmt, vn)
|
||||||
|
|
||||||
def conv_raw(self, abspath: str, tpath: str, fmt: str, vn: VFS) -> None:
|
def _conv_dcraw(self, abspath: str, tpath: str, fmt: str, vn: VFS) -> None:
|
||||||
self.wait4ram(0.2, tpath)
|
self.wait4ram(0.6, tpath)
|
||||||
|
bap = fsenc(abspath)
|
||||||
|
# fmt: off
|
||||||
|
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
|
||||||
|
bap,
|
||||||
|
]
|
||||||
|
# fmt: on
|
||||||
|
p = sp.Popen(cmd, stdout=sp.PIPE)
|
||||||
|
try:
|
||||||
|
if HAVE_PIL:
|
||||||
|
self.conv_image_pil(Image.open(p.stdout), tpath, fmt, vn)
|
||||||
|
elif HAVE_VIPS:
|
||||||
|
ppm, _ = p.communicate(timeout=vn.flags["convt"])
|
||||||
|
|
||||||
|
def _loader(w: int, kw: dict) -> Any:
|
||||||
|
return pyvips.Image.thumbnail_buffer(ppm, w, **kw)
|
||||||
|
|
||||||
|
self.conv_image_vips(_loader, tpath, fmt, vn)
|
||||||
|
else:
|
||||||
|
raise Exception(
|
||||||
|
"either pil or vips is needed to process embedded bitmap thumbnails in raw files"
|
||||||
|
)
|
||||||
|
finally:
|
||||||
|
if p and p.poll() is None:
|
||||||
|
p.kill()
|
||||||
|
|
||||||
|
def _conv_rawpy(self, abspath: str, tpath: str, fmt: str, vn: VFS) -> None:
|
||||||
|
self.wait4ram(0.6, tpath)
|
||||||
with rawpy.imread(abspath) as raw:
|
with rawpy.imread(abspath) as raw:
|
||||||
thumb = raw.extract_thumb()
|
thumb = raw.extract_thumb()
|
||||||
if thumb.format == rawpy.ThumbFormat.JPEG and tpath.endswith(".jpg"):
|
if thumb.format == rawpy.ThumbFormat.JPEG and tpath.endswith(".jpg"):
|
||||||
|
|
@ -819,16 +858,15 @@ class ThumbSrv(object):
|
||||||
|
|
||||||
res = self.getres(vn, fmt)
|
res = self.getres(vn, fmt)
|
||||||
bscale = scale.format(*list(res)).encode("utf-8")
|
bscale = scale.format(*list(res)).encode("utf-8")
|
||||||
|
bap_in = fsenc(abspath)
|
||||||
|
bap_out = fsenc(tpath)
|
||||||
# fmt: off
|
# fmt: off
|
||||||
cmd = [
|
cmd = bwrap(HAVE_FFMPEG, bap_in, bap_out) + [
|
||||||
b"ffmpeg",
|
|
||||||
b"-nostdin",
|
b"-nostdin",
|
||||||
b"-v", b"error",
|
b"-v", b"error",
|
||||||
b"-hide_banner"
|
b"-hide_banner"
|
||||||
]
|
] + seek + [
|
||||||
cmd += seek
|
b"-i", bap_in,
|
||||||
cmd += [
|
|
||||||
b"-i", fsenc(abspath),
|
|
||||||
b"-map", imap,
|
b"-map", imap,
|
||||||
b"-vf", bscale,
|
b"-vf", bscale,
|
||||||
b"-frames:v", b"1",
|
b"-frames:v", b"1",
|
||||||
|
|
@ -836,15 +874,15 @@ class ThumbSrv(object):
|
||||||
]
|
]
|
||||||
# fmt: on
|
# 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:
|
def _ffmpeg_im_o(self, tpath: bytes, vn: VFS, cmd: list[bytes]) -> None:
|
||||||
if tpath.endswith(".jpg"):
|
if tpath.endswith(b".jpg"):
|
||||||
cmd += [
|
cmd += [
|
||||||
b"-q:v",
|
b"-q:v",
|
||||||
FF_JPG_Q[vn.flags["th_qv"] // 5], # default=??
|
FF_JPG_Q[vn.flags["th_qv"] // 5], # default=??
|
||||||
]
|
]
|
||||||
elif tpath.endswith(".jxl"):
|
elif tpath.endswith(b".jxl"):
|
||||||
cmd += [
|
cmd += [
|
||||||
b"-q:v",
|
b"-q:v",
|
||||||
unicode(vn.flags["th_qvx"]).encode("ascii"), # default=??
|
unicode(vn.flags["th_qvx"]).encode("ascii"), # default=??
|
||||||
|
|
@ -859,7 +897,7 @@ class ThumbSrv(object):
|
||||||
b"6", # default=4, 0=fast, 6=max
|
b"6", # default=4, 0=fast, 6=max
|
||||||
]
|
]
|
||||||
|
|
||||||
cmd += [fsenc(tpath)]
|
cmd.append(tpath)
|
||||||
self._run_ff(cmd, vn, "convt")
|
self._run_ff(cmd, vn, "convt")
|
||||||
|
|
||||||
def _run_ff(self, cmd: list[bytes], vn: VFS, kto: str, oom: int = 400) -> None:
|
def _run_ff(self, cmd: list[bytes], vn: VFS, kto: str, oom: int = 400) -> None:
|
||||||
|
|
@ -959,20 +997,21 @@ class ThumbSrv(object):
|
||||||
b",showwavespic=s=2048x64:colors=white"
|
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
|
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
|
# fmt: off
|
||||||
cmd = [
|
cmd = bwrap(HAVE_FFMPEG, bap_in, bap_out) + [
|
||||||
b"ffmpeg",
|
|
||||||
b"-nostdin",
|
b"-nostdin",
|
||||||
b"-v", b"error",
|
b"-v", b"error",
|
||||||
b"-hide_banner",
|
b"-hide_banner",
|
||||||
b"-i", fsenc(abspath),
|
b"-i", bap_in,
|
||||||
b"-filter_complex", flt,
|
b"-filter_complex", flt,
|
||||||
b"-frames:v", b"1",
|
b"-frames:v", b"1",
|
||||||
]
|
]
|
||||||
# fmt: on
|
# fmt: on
|
||||||
|
|
||||||
cmd += [fsenc(tpath)]
|
cmd.append(bap_out)
|
||||||
self._run_ff(cmd, vn, "convt")
|
self._run_ff(cmd, vn, "convt")
|
||||||
|
|
||||||
if "pngquant" in vn.flags:
|
if "pngquant" in vn.flags:
|
||||||
|
|
@ -1039,28 +1078,32 @@ class ThumbSrv(object):
|
||||||
except:
|
except:
|
||||||
self.untemp[tpath] = [infile]
|
self.untemp[tpath] = [infile]
|
||||||
|
|
||||||
|
bap_in = fsenc(abspath)
|
||||||
|
bap_out = fsenc(infile)
|
||||||
|
|
||||||
# fmt: off
|
# fmt: off
|
||||||
cmd = [
|
cmd = bwrap(HAVE_FFMPEG, bap_in, bap_out) + [
|
||||||
b"ffmpeg",
|
|
||||||
b"-nostdin",
|
b"-nostdin",
|
||||||
b"-v", b"error",
|
b"-v", b"error",
|
||||||
b"-hide_banner",
|
b"-hide_banner",
|
||||||
b"-i", fsenc(abspath),
|
b"-i", bap_in,
|
||||||
b"-map", b"0:a:0",
|
b"-map", b"0:a:0",
|
||||||
b"-ac", b"1",
|
b"-ac", b"1",
|
||||||
b"-ar", b"48000",
|
b"-ar", b"48000",
|
||||||
b"-sample_fmt", b"s16",
|
b"-sample_fmt", b"s16",
|
||||||
b"-t", b"900",
|
b"-t", b"900",
|
||||||
b"-y", fsenc(infile),
|
b"-y", bap_out,
|
||||||
]
|
]
|
||||||
# fmt: on
|
# fmt: on
|
||||||
self._run_ff(cmd, vn, "convt")
|
self._run_ff(cmd, vn, "convt")
|
||||||
|
|
||||||
|
fscale = ":fscale=log" if self.args.th_spec_fl else ""
|
||||||
|
|
||||||
fc = "[0:a:0]aresample=48000{},showspectrumpic=s="
|
fc = "[0:a:0]aresample=48000{},showspectrumpic=s="
|
||||||
if "3" in fmt:
|
if "3" in fmt:
|
||||||
fc += "1280x1024,crop=1420:1056:70:48[o]"
|
fc += "1280x1024%s,crop=1420:1056:70:48[o]" % fscale
|
||||||
else:
|
else:
|
||||||
fc += "640x512,crop=780:544:70:48[o]"
|
fc += "640x512%s,crop=780:544:70:48[o]" % fscale
|
||||||
|
|
||||||
if self.args.th_ff_swr:
|
if self.args.th_ff_swr:
|
||||||
fco = ":filter_size=128:cutoff=0.877"
|
fco = ":filter_size=128:cutoff=0.877"
|
||||||
|
|
@ -1069,20 +1112,22 @@ class ThumbSrv(object):
|
||||||
|
|
||||||
fc = fc.format(fco)
|
fc = fc.format(fco)
|
||||||
|
|
||||||
|
bap_in = fsenc(infile)
|
||||||
|
bap_out = fsenc(tpath)
|
||||||
|
|
||||||
# fmt: off
|
# fmt: off
|
||||||
cmd = [
|
cmd = bwrap(HAVE_FFMPEG, bap_in, bap_out) + [
|
||||||
b"ffmpeg",
|
|
||||||
b"-nostdin",
|
b"-nostdin",
|
||||||
b"-v", b"error",
|
b"-v", b"error",
|
||||||
b"-hide_banner",
|
b"-hide_banner",
|
||||||
b"-i", fsenc(infile),
|
b"-i", bap_in,
|
||||||
b"-filter_complex", fc.encode("utf-8"),
|
b"-filter_complex", fc.encode("utf-8"),
|
||||||
b"-map", b"[o]",
|
b"-map", b"[o]",
|
||||||
b"-frames:v", b"1",
|
b"-frames:v", b"1",
|
||||||
]
|
]
|
||||||
# fmt: on
|
# 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:
|
def conv_mp3(self, abspath: str, tpath: str, fmt: str, vn: VFS) -> None:
|
||||||
quality = self.args.q_mp3.lower()
|
quality = self.args.q_mp3.lower()
|
||||||
|
|
@ -1101,24 +1146,26 @@ class ThumbSrv(object):
|
||||||
qk = b"-q:a"
|
qk = b"-q:a"
|
||||||
qv = quality[1:].encode("ascii")
|
qv = quality[1:].encode("ascii")
|
||||||
|
|
||||||
|
bap_in = fsenc(abspath)
|
||||||
|
bap_out = fsenc(tpath)
|
||||||
|
|
||||||
# extremely conservative choices for output format
|
# extremely conservative choices for output format
|
||||||
# (always 2ch 44k1) because if a device is old enough
|
# (always 2ch 44k1) because if a device is old enough
|
||||||
# to not support opus then it's probably also super picky
|
# to not support opus then it's probably also super picky
|
||||||
|
|
||||||
# fmt: off
|
# fmt: off
|
||||||
cmd = [
|
cmd = bwrap(HAVE_FFMPEG, bap_in, bap_out) + [
|
||||||
b"ffmpeg",
|
|
||||||
b"-nostdin",
|
b"-nostdin",
|
||||||
b"-v", b"error",
|
b"-v", b"error",
|
||||||
b"-hide_banner",
|
b"-hide_banner",
|
||||||
b"-i", fsenc(abspath),
|
b"-i", bap_in,
|
||||||
] + self.big_tags(rawtags) + [
|
] + self.big_tags(rawtags) + [
|
||||||
b"-map", b"0:a:0",
|
b"-map", b"0:a:0",
|
||||||
b"-ar", b"44100",
|
b"-ar", b"44100",
|
||||||
b"-ac", b"2",
|
b"-ac", b"2",
|
||||||
b"-c:a", b"libmp3lame",
|
b"-c:a", b"libmp3lame",
|
||||||
qk, qv,
|
qk, qv,
|
||||||
fsenc(tpath)
|
bap_out,
|
||||||
]
|
]
|
||||||
# fmt: on
|
# fmt: on
|
||||||
self._run_ff(cmd, vn, "aconvt", oom=300)
|
self._run_ff(cmd, vn, "aconvt", oom=300)
|
||||||
|
|
@ -1134,16 +1181,18 @@ class ThumbSrv(object):
|
||||||
|
|
||||||
self.log("conv2 flac", 6)
|
self.log("conv2 flac", 6)
|
||||||
|
|
||||||
|
bap_in = fsenc(abspath)
|
||||||
|
bap_out = fsenc(tpath)
|
||||||
|
|
||||||
# fmt: off
|
# fmt: off
|
||||||
cmd = [
|
cmd = bwrap(HAVE_FFMPEG, bap_in, bap_out) + [
|
||||||
b"ffmpeg",
|
|
||||||
b"-nostdin",
|
b"-nostdin",
|
||||||
b"-v", b"error",
|
b"-v", b"error",
|
||||||
b"-hide_banner",
|
b"-hide_banner",
|
||||||
b"-i", fsenc(abspath),
|
b"-i", bap_in,
|
||||||
b"-map", b"0:a:0",
|
b"-map", b"0:a:0",
|
||||||
b"-c:a", b"flac",
|
b"-c:a", b"flac",
|
||||||
fsenc(tpath)
|
bap_out,
|
||||||
]
|
]
|
||||||
# fmt: on
|
# fmt: on
|
||||||
self._run_ff(cmd, vn, "aconvt", oom=300)
|
self._run_ff(cmd, vn, "aconvt", oom=300)
|
||||||
|
|
@ -1169,16 +1218,18 @@ class ThumbSrv(object):
|
||||||
|
|
||||||
self.log("conv2 wav", 6)
|
self.log("conv2 wav", 6)
|
||||||
|
|
||||||
|
bap_in = fsenc(abspath)
|
||||||
|
bap_out = fsenc(tpath)
|
||||||
|
|
||||||
# fmt: off
|
# fmt: off
|
||||||
cmd = [
|
cmd = bwrap(HAVE_FFMPEG, bap_in, bap_out) + [
|
||||||
b"ffmpeg",
|
|
||||||
b"-nostdin",
|
b"-nostdin",
|
||||||
b"-v", b"error",
|
b"-v", b"error",
|
||||||
b"-hide_banner",
|
b"-hide_banner",
|
||||||
b"-i", fsenc(abspath),
|
b"-i", bap_in,
|
||||||
b"-map", b"0:a:0",
|
b"-map", b"0:a:0",
|
||||||
b"-c:a", codec,
|
b"-c:a", codec,
|
||||||
fsenc(tpath)
|
bap_out,
|
||||||
]
|
]
|
||||||
# fmt: on
|
# fmt: on
|
||||||
self._run_ff(cmd, vn, "aconvt", oom=300)
|
self._run_ff(cmd, vn, "aconvt", oom=300)
|
||||||
|
|
@ -1230,19 +1281,21 @@ class ThumbSrv(object):
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
bap_in = fsenc(abspath)
|
||||||
|
bap_out = fsenc(tpath)
|
||||||
|
|
||||||
# fmt: off
|
# fmt: off
|
||||||
cmd = [
|
cmd = bwrap(HAVE_FFMPEG, bap_in, bap_out) + [
|
||||||
b"ffmpeg",
|
|
||||||
b"-nostdin",
|
b"-nostdin",
|
||||||
b"-v", b"error",
|
b"-v", b"error",
|
||||||
b"-hide_banner",
|
b"-hide_banner",
|
||||||
b"-i", fsenc(abspath),
|
b"-i", bap_in,
|
||||||
] + tagset + [
|
] + tagset + [
|
||||||
b"-map", b"0:a:0",
|
b"-map", b"0:a:0",
|
||||||
b"-ac", ac,
|
b"-ac", ac,
|
||||||
] + benc + [
|
] + benc + [
|
||||||
b"-f", container,
|
b"-f", container,
|
||||||
fsenc(tpath)
|
bap_out,
|
||||||
]
|
]
|
||||||
# fmt: on
|
# fmt: on
|
||||||
self._run_ff(cmd, vn, "aconvt", oom=300)
|
self._run_ff(cmd, vn, "aconvt", oom=300)
|
||||||
|
|
@ -1271,19 +1324,21 @@ class ThumbSrv(object):
|
||||||
self.log("conv2 caf-tmp [%s]" % (enc,), 6)
|
self.log("conv2 caf-tmp [%s]" % (enc,), 6)
|
||||||
benc = enc.encode("ascii").split(b" ")
|
benc = enc.encode("ascii").split(b" ")
|
||||||
|
|
||||||
|
bap_in = fsenc(abspath)
|
||||||
|
bap_out = fsenc(tmp_opus)
|
||||||
|
|
||||||
# fmt: off
|
# fmt: off
|
||||||
cmd = [
|
cmd = bwrap(HAVE_FFMPEG, bap_in, bap_out) + [
|
||||||
b"ffmpeg",
|
|
||||||
b"-nostdin",
|
b"-nostdin",
|
||||||
b"-v", b"error",
|
b"-v", b"error",
|
||||||
b"-hide_banner",
|
b"-hide_banner",
|
||||||
b"-i", fsenc(abspath),
|
b"-i", bap_in,
|
||||||
b"-map_metadata", b"-1",
|
b"-map_metadata", b"-1",
|
||||||
b"-map", b"0:a:0",
|
b"-map", b"0:a:0",
|
||||||
b"-ac", b"2",
|
b"-ac", b"2",
|
||||||
] + benc + [
|
] + benc + [
|
||||||
b"-f", b"opus",
|
b"-f", b"opus",
|
||||||
fsenc(tmp_opus)
|
bap_out,
|
||||||
]
|
]
|
||||||
# fmt: on
|
# fmt: on
|
||||||
self._run_ff(cmd, vn, "aconvt", oom=300)
|
self._run_ff(cmd, vn, "aconvt", oom=300)
|
||||||
|
|
@ -1297,20 +1352,21 @@ class ThumbSrv(object):
|
||||||
if dur < 20 or sz < 256 * 1024:
|
if dur < 20 or sz < 256 * 1024:
|
||||||
zs = bq.decode("ascii")
|
zs = bq.decode("ascii")
|
||||||
self.log("conv2 caf-transcode; dur=%d sz=%d q=%s" % (dur, sz, zs), 6)
|
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
|
# fmt: off
|
||||||
cmd = [
|
cmd = bwrap(HAVE_FFMPEG, bap_in, bap_out) + [
|
||||||
b"ffmpeg",
|
|
||||||
b"-nostdin",
|
b"-nostdin",
|
||||||
b"-v", b"error",
|
b"-v", b"error",
|
||||||
b"-hide_banner",
|
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"-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"-map_metadata", b"-1",
|
||||||
b"-ac", b"2",
|
b"-ac", b"2",
|
||||||
b"-c:a", b"libopus",
|
b"-c:a", b"libopus",
|
||||||
b"-b:a", bq,
|
b"-b:a", bq,
|
||||||
b"-f", b"caf",
|
b"-f", b"caf",
|
||||||
fsenc(tpath)
|
bap_out,
|
||||||
]
|
]
|
||||||
# fmt: on
|
# fmt: on
|
||||||
self._run_ff(cmd, vn, "aconvt", oom=300)
|
self._run_ff(cmd, vn, "aconvt", oom=300)
|
||||||
|
|
@ -1318,18 +1374,19 @@ class ThumbSrv(object):
|
||||||
else:
|
else:
|
||||||
# simple remux should be safe
|
# simple remux should be safe
|
||||||
self.log("conv2 caf-remux; dur=%d sz=%d" % (dur, sz), 6)
|
self.log("conv2 caf-remux; dur=%d sz=%d" % (dur, sz), 6)
|
||||||
|
bap_in = fsenc(tmp_opus)
|
||||||
|
bap_out = fsenc(tpath)
|
||||||
# fmt: off
|
# fmt: off
|
||||||
cmd = [
|
cmd = bwrap(HAVE_FFMPEG, bap_in, bap_out) + [
|
||||||
b"ffmpeg",
|
|
||||||
b"-nostdin",
|
b"-nostdin",
|
||||||
b"-v", b"error",
|
b"-v", b"error",
|
||||||
b"-hide_banner",
|
b"-hide_banner",
|
||||||
b"-i", fsenc(tmp_opus),
|
b"-i", bap_in,
|
||||||
b"-map_metadata", b"-1",
|
b"-map_metadata", b"-1",
|
||||||
b"-map", b"0:a:0",
|
b"-map", b"0:a:0",
|
||||||
b"-c:a", b"copy",
|
b"-c:a", b"copy",
|
||||||
b"-f", b"caf",
|
b"-f", b"caf",
|
||||||
fsenc(tpath)
|
bap_out,
|
||||||
]
|
]
|
||||||
# fmt: on
|
# fmt: on
|
||||||
self._run_ff(cmd, vn, "aconvt", oom=300)
|
self._run_ff(cmd, vn, "aconvt", oom=300)
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@ from .util import (
|
||||||
quotep,
|
quotep,
|
||||||
s3dec,
|
s3dec,
|
||||||
vjoin,
|
vjoin,
|
||||||
|
vjoins,
|
||||||
)
|
)
|
||||||
|
|
||||||
if HAVE_SQLITE3:
|
if HAVE_SQLITE3:
|
||||||
|
|
@ -437,7 +438,7 @@ class U2idx(object):
|
||||||
if rd.startswith("//") or fn.startswith("//"):
|
if rd.startswith("//") or fn.startswith("//"):
|
||||||
rd, fn = s3dec(rd, fn)
|
rd, fn = s3dec(rd, fn)
|
||||||
|
|
||||||
vp = vjoin(vjoin(vtop, rd), fn)
|
vp = vjoins(vtop, rd, fn)
|
||||||
|
|
||||||
if vp in seen_rps:
|
if vp in seen_rps:
|
||||||
continue
|
continue
|
||||||
|
|
|
||||||
|
|
@ -70,6 +70,7 @@ from .util import (
|
||||||
ub64enc,
|
ub64enc,
|
||||||
unhumanize,
|
unhumanize,
|
||||||
vjoin,
|
vjoin,
|
||||||
|
vjoins,
|
||||||
vsplit,
|
vsplit,
|
||||||
w8b64dec,
|
w8b64dec,
|
||||||
w8b64enc,
|
w8b64enc,
|
||||||
|
|
@ -102,7 +103,7 @@ ICV_EXTS = set(zsg.split(","))
|
||||||
zsg = "3gp,asf,av1,avc,avi,flv,m4v,mjpeg,mjpg,mkv,mov,mp4,mpeg,mpeg2,mpegts,mpg,mpg2,mts,nut,ogm,ogv,rm,vob,webm,wmv"
|
zsg = "3gp,asf,av1,avc,avi,flv,m4v,mjpeg,mjpg,mkv,mov,mp4,mpeg,mpeg2,mpegts,mpg,mpg2,mts,nut,ogm,ogv,rm,vob,webm,wmv"
|
||||||
VCV_EXTS = set(zsg.split(","))
|
VCV_EXTS = set(zsg.split(","))
|
||||||
|
|
||||||
zsg = "aif,aiff,alac,ape,flac,m4a,m4b,m4r,mp3,oga,ogg,opus,tak,tta,wav,wma,wv,cbz,epub"
|
zsg = "aif,aiff,alac,ape,flac,m4a,m4b,m4r,mka,mp3,oga,ogg,opus,tak,tta,wav,wma,wv,cbz,epub"
|
||||||
ACV_EXTS = set(zsg.split(","))
|
ACV_EXTS = set(zsg.split(","))
|
||||||
|
|
||||||
zsg = "nohash noidx xdev xvol"
|
zsg = "nohash noidx xdev xvol"
|
||||||
|
|
@ -3037,7 +3038,7 @@ class Up2k(object):
|
||||||
raise Pebkac(500, "too many xbu relocs, giving up")
|
raise Pebkac(500, "too many xbu relocs, giving up")
|
||||||
|
|
||||||
ptop = cj["ptop"]
|
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:
|
if ptop not in self.registry:
|
||||||
raise Pebkac(410, "location unavailable")
|
raise Pebkac(410, "location unavailable")
|
||||||
|
|
||||||
|
|
@ -3077,7 +3078,7 @@ class Up2k(object):
|
||||||
)
|
)
|
||||||
|
|
||||||
zi = cj["lmod"]
|
zi = cj["lmod"]
|
||||||
bad_mt = zi <= 0 or zi > 0xAAAAAAAA
|
bad_mt = zi <= 0 or zi > (2 << 36)
|
||||||
if bad_mt or vfs.flags.get("up_ts", "") == "fu":
|
if bad_mt or vfs.flags.get("up_ts", "") == "fu":
|
||||||
# force upload time rather than last-modified
|
# force upload time rather than last-modified
|
||||||
cj["lmod"] = int(time.time())
|
cj["lmod"] = int(time.time())
|
||||||
|
|
@ -3190,7 +3191,7 @@ class Up2k(object):
|
||||||
c2 = None
|
c2 = None
|
||||||
for cur, dp_dir, dp_fn in lost:
|
for cur, dp_dir, dp_fn in lost:
|
||||||
t = "forgetting desynced db entry: %r"
|
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"])
|
self.db_rm(cur, vfs.flags, dp_dir, dp_fn, cj["size"])
|
||||||
if c2 and c2 != cur:
|
if c2 and c2 != cur:
|
||||||
c2.connection.commit()
|
c2.connection.commit()
|
||||||
|
|
@ -3280,7 +3281,10 @@ class Up2k(object):
|
||||||
vfs.lim.nup(cj["addr"])
|
vfs.lim.nup(cj["addr"])
|
||||||
vfs.lim.bup(cj["addr"], cj["size"])
|
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))
|
self.log("unfinished:\n %r\n %r" % (src, dst))
|
||||||
err = "partial upload exists at a different location; please resume uploading here instead:\n"
|
err = "partial upload exists at a different location; please resume uploading here instead:\n"
|
||||||
err += "/" + quotep(vsrc) + " "
|
err += "/" + quotep(vsrc) + " "
|
||||||
|
|
@ -3370,9 +3374,9 @@ class Up2k(object):
|
||||||
x = pathmod(self.vfs, dst, vp, hr["reloc"])
|
x = pathmod(self.vfs, dst, vp, hr["reloc"])
|
||||||
if x:
|
if x:
|
||||||
ud1 = (vfs.vpath, job["prel"], job["name"])
|
ud1 = (vfs.vpath, job["prel"], job["name"])
|
||||||
|
job["rvp0"] = vjoins(*ud1)
|
||||||
pdir, _, job["name"], (vfs, rem) = x
|
pdir, _, job["name"], (vfs, rem) = x
|
||||||
dst = os.path.join(pdir, job["name"])
|
dst = os.path.join(pdir, job["name"])
|
||||||
job["vcfg"] = vfs.flags
|
|
||||||
job["ptop"] = vfs.realpath
|
job["ptop"] = vfs.realpath
|
||||||
job["vtop"] = vfs.vpath
|
job["vtop"] = vfs.vpath
|
||||||
job["prel"] = rem
|
job["prel"] = rem
|
||||||
|
|
@ -3380,8 +3384,19 @@ class Up2k(object):
|
||||||
ud2 = (vfs.vpath, job["prel"], job["name"])
|
ud2 = (vfs.vpath, job["prel"], job["name"])
|
||||||
if ud1 != ud2:
|
if ud1 != ud2:
|
||||||
# print(json.dumps(job, sort_keys=True, indent=4))
|
# print(json.dumps(job, sort_keys=True, indent=4))
|
||||||
|
job["vcfg"] = vfs.flags
|
||||||
job["hash"] = cj["hash"]
|
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)
|
return self._handle_json(job, depth + 1)
|
||||||
|
|
||||||
job["name"] = self._untaken(pdir, job, now)
|
job["name"] = self._untaken(pdir, job, now)
|
||||||
|
|
@ -5263,15 +5278,25 @@ class Up2k(object):
|
||||||
x = pathmod(self.vfs, ap_chk, vp_chk, hr["reloc"])
|
x = pathmod(self.vfs, ap_chk, vp_chk, hr["reloc"])
|
||||||
if x:
|
if x:
|
||||||
ud1 = (vfs.vpath, job["prel"], job["name"])
|
ud1 = (vfs.vpath, job["prel"], job["name"])
|
||||||
|
job["rvp0"] = vjoins(*ud1)
|
||||||
pdir, _, job["name"], (vfs, rem) = x
|
pdir, _, job["name"], (vfs, rem) = x
|
||||||
job["vcfg"] = vf = vfs.flags
|
vf = vfs.flags
|
||||||
job["ptop"] = vfs.realpath
|
job["ptop"] = vfs.realpath
|
||||||
job["vtop"] = vfs.vpath
|
job["vtop"] = vfs.vpath
|
||||||
job["prel"] = rem
|
job["prel"] = rem
|
||||||
job["name"] = sanitize_fn(job["name"])
|
job["name"] = sanitize_fn(job["name"])
|
||||||
ud2 = (vfs.vpath, job["prel"], job["name"])
|
ud2 = (vfs.vpath, job["prel"], job["name"])
|
||||||
if ud1 != ud2:
|
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)
|
return self._handle_json(job, depth + 1)
|
||||||
|
|
||||||
job["name"] = self._untaken(pdir, job, job["t0"])
|
job["name"] = self._untaken(pdir, job, job["t0"])
|
||||||
|
|
|
||||||
|
|
@ -326,7 +326,14 @@ except:
|
||||||
BITNESS = struct.calcsize("P") * 8
|
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]")
|
RE_ANSI = re.compile("\033\\[[^mK]*[mK]")
|
||||||
|
|
@ -516,7 +523,7 @@ image heics=heic-sequence heifs=heif-sequence hdr=vnd.radiance svg=svg+xml
|
||||||
image arw=x-sony-arw cr2=x-canon-cr2 crw=x-canon-crw dcr=x-kodak-dcr dng=x-adobe-dng erf=x-epson-erf
|
image arw=x-sony-arw cr2=x-canon-cr2 crw=x-canon-crw dcr=x-kodak-dcr dng=x-adobe-dng erf=x-epson-erf
|
||||||
image k25=x-kodak-k25 kdc=x-kodak-kdc mrw=x-minolta-mrw nef=x-nikon-nef orf=x-olympus-orf
|
image k25=x-kodak-k25 kdc=x-kodak-kdc mrw=x-minolta-mrw nef=x-nikon-nef orf=x-olympus-orf
|
||||||
image pef=x-pentax-pef raf=x-fuji-raf raw=x-panasonic-raw sr2=x-sony-sr2 srf=x-sony-srf x3f=x-sigma-x3f
|
image pef=x-pentax-pef raf=x-fuji-raf raw=x-panasonic-raw sr2=x-sony-sr2 srf=x-sony-srf x3f=x-sigma-x3f
|
||||||
audio caf=x-caf mp3=mpeg m4a=mp4 m4b=mp4 m4r=mp4 mid=midi mpc=musepack aif=aiff au=basic qcp=qcelp
|
audio caf=x-caf mp3=mpeg m4a=mp4 m4b=mp4 m4r=mp4 mid=midi mka=x-matroska mpc=musepack aif=aiff au=basic qcp=qcelp
|
||||||
video mkv=x-matroska mov=quicktime avi=x-msvideo m4v=x-m4v ts=mp2t
|
video mkv=x-matroska mov=quicktime avi=x-msvideo m4v=x-m4v ts=mp2t
|
||||||
video asf=x-ms-asf flv=x-flv 3gp=3gpp 3g2=3gpp2 rmvb=vnd.rn-realmedia-vbr
|
video asf=x-ms-asf flv=x-flv 3gp=3gpp 3g2=3gpp2 rmvb=vnd.rn-realmedia-vbr
|
||||||
font ttc=collection
|
font ttc=collection
|
||||||
|
|
@ -650,6 +657,14 @@ if EXE:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
try:
|
||||||
|
if PY2 or ANYWIN:
|
||||||
|
raise Exception()
|
||||||
|
HAVE_BWRAP = shutil.which("bwrap")
|
||||||
|
except:
|
||||||
|
HAVE_BWRAP = ""
|
||||||
|
|
||||||
|
|
||||||
def py_desc() -> str:
|
def py_desc() -> str:
|
||||||
interp = platform.python_implementation()
|
interp = platform.python_implementation()
|
||||||
py_ver = ".".join([str(x) for x in sys.version_info])
|
py_ver = ".".join([str(x) for x in sys.version_info])
|
||||||
|
|
@ -832,10 +847,8 @@ class Daemon(threading.Thread):
|
||||||
self.start()
|
self.start()
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
if CAN_SIGMASK:
|
if BLOCK_SIGS:
|
||||||
signal.pthread_sigmask(
|
signal.pthread_sigmask(signal.SIG_BLOCK, BLOCK_SIGS)
|
||||||
signal.SIG_BLOCK, [signal.SIGINT, signal.SIGTERM, signal.SIGUSR1]
|
|
||||||
)
|
|
||||||
|
|
||||||
self.fun(*self.a, **self.ka)
|
self.fun(*self.a, **self.ka)
|
||||||
|
|
||||||
|
|
@ -1635,6 +1648,16 @@ def expand_osenv_cs(txt) -> str:
|
||||||
raise Exception(t)
|
raise Exception(t)
|
||||||
|
|
||||||
|
|
||||||
|
def signame2int(txt: str) -> int:
|
||||||
|
try:
|
||||||
|
return int(txt)
|
||||||
|
except:
|
||||||
|
txt = txt.upper()
|
||||||
|
if not txt.startswith("SIG"):
|
||||||
|
txt = "SIG" + txt
|
||||||
|
return int(getattr(signal, txt))
|
||||||
|
|
||||||
|
|
||||||
def rice_tid() -> str:
|
def rice_tid() -> str:
|
||||||
tid = threading.current_thread().ident
|
tid = threading.current_thread().ident
|
||||||
c = sunpack(b"B" * 5, spack(b">Q", tid)[-5:])
|
c = sunpack(b"B" * 5, spack(b">Q", tid)[-5:])
|
||||||
|
|
@ -1768,9 +1791,7 @@ def log_thrs(log: Callable[[str, str, int], None], ival: float, name: str) -> No
|
||||||
|
|
||||||
|
|
||||||
def _sigblock():
|
def _sigblock():
|
||||||
signal.pthread_sigmask(
|
signal.pthread_sigmask(signal.SIG_BLOCK, BLOCK_SIGS)
|
||||||
signal.SIG_BLOCK, [signal.SIGINT, signal.SIGTERM, signal.SIGUSR1]
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
sigblock = _sigblock if CAN_SIGMASK else noop
|
sigblock = _sigblock if CAN_SIGMASK else noop
|
||||||
|
|
@ -2666,6 +2687,10 @@ def vjoin(rd: str, fn: str) -> str:
|
||||||
return rd or fn
|
return rd or fn
|
||||||
|
|
||||||
|
|
||||||
|
def vjoins(*a: str) -> str:
|
||||||
|
return "/".join([x for x in a if x])
|
||||||
|
|
||||||
|
|
||||||
# url-join
|
# url-join
|
||||||
def ujoin(rd: str, fn: str) -> str:
|
def ujoin(rd: str, fn: str) -> str:
|
||||||
if rd and fn:
|
if rd and fn:
|
||||||
|
|
|
||||||
|
|
@ -1701,7 +1701,7 @@ mpl.init_ac2();
|
||||||
var re_m3u = /\.(m3u8?)$/i;
|
var re_m3u = /\.(m3u8?)$/i;
|
||||||
var re_au_native = (can_ogg || have_acode) ? /\.(aac|flac|m4[abr]|mp3|oga|ogg|opus|wav)$/i : /\.(aac|flac|m4[abr]|mp3|wav)$/i,
|
var re_au_native = (can_ogg || have_acode) ? /\.(aac|flac|m4[abr]|mp3|oga|ogg|opus|wav)$/i : /\.(aac|flac|m4[abr]|mp3|wav)$/i,
|
||||||
re_au_vid = /\.(3gp|asf|avi|flv|m4v|mkv|mov|mp4|mpeg|mpeg2|mpegts|mpg|mpg2|nut|ogm|ogv|rm|ts|vob|webm|wmv)$/i,
|
re_au_vid = /\.(3gp|asf|avi|flv|m4v|mkv|mov|mp4|mpeg|mpeg2|mpegts|mpg|mpg2|nut|ogm|ogv|rm|ts|vob|webm|wmv)$/i,
|
||||||
re_au_all = /\.(aac|ac3|aif|aiff|alac|alaw|amr|ape|au|b[cfr]stm|dfpwm|dts|flac|gsm|it|itgz|itxz|itz|m4[abr]|mdgz|mdxz|mdz|mo3|mod|mp2|mp3|mpc|mptm|mt2|mulaw|oga|ogg|okt|opus|ra|s3m|s3gz|s3xz|s3z|tak|tta|ulaw|wav|wma|wv|xm|xmgz|xmxz|xmz|xpk|3gp|asf|avi|flv|m4v|mkv|mov|mp4|mpeg|mpeg2|mpegts|mpg|mpg2|nut|ogm|ogv|rm|ts|vob|webm|wmv)$/i;
|
re_au_all = /\.(aac|ac3|aif|aiff|alac|alaw|amr|ape|au|b[cfr]stm|dfpwm|dts|flac|gsm|it|itgz|itxz|itz|m4[abr]|mdgz|mdxz|mdz|mka|mo3|mod|mp2|mp3|mpc|mptm|mt2|mulaw|oga|ogg|okt|opus|ra|s3m|s3gz|s3xz|s3z|tak|tta|ulaw|wav|wma|wv|xm|xmgz|xmxz|xmz|xpk|3gp|asf|avi|flv|m4v|mkv|mov|mp4|mpeg|mpeg2|mpegts|mpg|mpg2|nut|ogm|ogv|rm|ts|vob|webm|wmv)$/i;
|
||||||
|
|
||||||
|
|
||||||
// extract songs + add play column
|
// extract songs + add play column
|
||||||
|
|
@ -3760,7 +3760,7 @@ function sortfiles(nodes) {
|
||||||
if ((v + '').indexOf('<a ') === 0)
|
if ((v + '').indexOf('<a ') === 0)
|
||||||
v = v.split('>')[1];
|
v = v.split('>')[1];
|
||||||
else if (name == "href" && v)
|
else if (name == "href" && v)
|
||||||
v = uricom_dec(v);
|
v = uri2txt(v, true);
|
||||||
|
|
||||||
nodes[b]._sv = v
|
nodes[b]._sv = v
|
||||||
}
|
}
|
||||||
|
|
@ -4145,6 +4145,8 @@ var fileman = (function () {
|
||||||
if (this.textContent == 'write-only')
|
if (this.textContent == 'write-only')
|
||||||
for (var a = 0; a < pbtns.length; a++)
|
for (var a = 0; a < pbtns.length; a++)
|
||||||
clmod(pbtns[a], 'on', pbtns[a].textContent == 'write');
|
clmod(pbtns[a], 'on', pbtns[a].textContent == 'write');
|
||||||
|
if (this.textContent == 'get' && clgot(this, 'on') && has(perms, 'read'))
|
||||||
|
clmod(pbtns[0], 'on');
|
||||||
}
|
}
|
||||||
clmod(pbtns[0], 'on', 1);
|
clmod(pbtns[0], 'on', 1);
|
||||||
|
|
||||||
|
|
@ -7871,7 +7873,7 @@ var treectl = (function () {
|
||||||
delete res['a'];
|
delete res['a'];
|
||||||
var keys = Object.keys(res);
|
var keys = Object.keys(res);
|
||||||
for (var a = 0; a < keys.length; a++)
|
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)
|
if (ENATSORT)
|
||||||
keys.sort(function (a, b) { return NATSORT.compare(a[0], b[0]); });
|
keys.sort(function (a, b) { return NATSORT.compare(a[0], b[0]); });
|
||||||
|
|
@ -8033,12 +8035,17 @@ function apply_perms(res) {
|
||||||
a.style.display = '';
|
a.style.display = '';
|
||||||
tt.att(QS('#ops'));
|
tt.att(QS('#ops'));
|
||||||
|
|
||||||
|
var v_perms = perms.slice(0);
|
||||||
|
var have_read = has(perms, 'read');
|
||||||
|
if (have_read)
|
||||||
|
apop(v_perms, 'get');
|
||||||
|
|
||||||
for (var a = 0; a < chk.length; a++)
|
for (var a = 0; a < chk.length; a++)
|
||||||
if (has(perms, chk[a]))
|
if (has(v_perms, chk[a]))
|
||||||
axs.push(chk[a].slice(0, 1).toUpperCase() + chk[a].slice(1));
|
axs.push(chk[a].slice(0, 1).toUpperCase() + chk[a].slice(1));
|
||||||
|
|
||||||
axs = axs.join('-');
|
axs = axs.join('-');
|
||||||
if (perms.length == 1) {
|
if (v_perms.length == 1) {
|
||||||
aclass = ' class="warn">';
|
aclass = ' class="warn">';
|
||||||
axs += '-Only';
|
axs += '-Only';
|
||||||
}
|
}
|
||||||
|
|
@ -8080,7 +8087,6 @@ function apply_perms(res) {
|
||||||
document.body.setAttribute('perms', perms.join(' '));
|
document.body.setAttribute('perms', perms.join(' '));
|
||||||
|
|
||||||
var have_write = has(perms, "write"),
|
var have_write = has(perms, "write"),
|
||||||
have_read = has(perms, "read"),
|
|
||||||
de = document.documentElement,
|
de = document.documentElement,
|
||||||
tds = QSA('#u2conf td');
|
tds = QSA('#u2conf td');
|
||||||
|
|
||||||
|
|
@ -8979,6 +8985,13 @@ var msel = (function () {
|
||||||
return;
|
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);
|
xhrchk(this, L.fd_xe1, L.fd_xe2);
|
||||||
|
|
||||||
if (this.status !== 201) {
|
if (this.status !== 201) {
|
||||||
|
|
@ -10030,7 +10043,7 @@ function reload_browser() {
|
||||||
if (e.target.closest('#widget,#ops,.opview,.doc')) return;
|
if (e.target.closest('#widget,#ops,.opview,.doc')) return;
|
||||||
|
|
||||||
if (e.target.closest('#gfiles'))
|
if (e.target.closest('#gfiles'))
|
||||||
ebi('gfiles').style.userSelect = "none"
|
ebi('gfiles').style.userSelect = "none";
|
||||||
|
|
||||||
var pos = getpp(e);
|
var pos = getpp(e);
|
||||||
startx = pos.x;
|
startx = pos.x;
|
||||||
|
|
@ -10071,7 +10084,9 @@ function reload_browser() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!dragging && dist > mvthresh && !window.getSelection().toString()) {
|
if (!dragging && dist > mvthresh && !window.getSelection().toString()) {
|
||||||
if (fwrap = e.target.closest('#wrap'))
|
if (e.target instanceof Element)
|
||||||
|
fwrap = e.target.closest('#wrap');
|
||||||
|
if (fwrap)
|
||||||
fwrap.style.userSelect = 'none';
|
fwrap.style.userSelect = 'none';
|
||||||
else return;
|
else return;
|
||||||
start_drag();
|
start_drag();
|
||||||
|
|
@ -10115,7 +10130,8 @@ function reload_browser() {
|
||||||
|
|
||||||
window.addEventListener('dragstart', function(e) {
|
window.addEventListener('dragstart', function(e) {
|
||||||
if (treectl.dsel && (is_selma || dragging)) {
|
if (treectl.dsel && (is_selma || dragging)) {
|
||||||
e.preventDefault();
|
if (!QS('body.bbox-open'))
|
||||||
|
ev(e);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,13 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<feed xmlns="http://www.w3.org/2005/Atom">
|
<feed xmlns="http://www.w3.org/2005/Atom">
|
||||||
|
<id>{{ opds_id }}</id>
|
||||||
|
<title>{{ opds_title | e }}</title>
|
||||||
<link rel="search"
|
<link rel="search"
|
||||||
href="{{ opds_osd | e }}"
|
href="{{ opds_osd | e }}"
|
||||||
type="application/opensearchdescription+xml"/>
|
type="application/opensearchdescription+xml"/>
|
||||||
{%- for d in dirs %}
|
{%- for d in dirs %}
|
||||||
<entry>
|
<entry>
|
||||||
|
<id>{{ d.opds_id }}</id>
|
||||||
<title>{{ d.name | e }}</title>
|
<title>{{ d.name | e }}</title>
|
||||||
<link rel="subsection"
|
<link rel="subsection"
|
||||||
href="{{ d.href | e }}"
|
href="{{ d.href | e }}"
|
||||||
|
|
@ -14,6 +17,7 @@
|
||||||
{%- endfor %}
|
{%- endfor %}
|
||||||
{%- for f in files %}
|
{%- for f in files %}
|
||||||
<entry>
|
<entry>
|
||||||
|
<id>{{ f.opds_id }}</id>
|
||||||
<title>{{ f.name | e }}</title>
|
<title>{{ f.name | e }}</title>
|
||||||
<updated>{{ f.iso8601 }}</updated>
|
<updated>{{ f.iso8601 }}</updated>
|
||||||
<link rel="http://opds-spec.org/acquisition"
|
<link rel="http://opds-spec.org/acquisition"
|
||||||
|
|
|
||||||
|
|
@ -233,7 +233,7 @@
|
||||||
<h1>partyfuse</h1>
|
<h1>partyfuse</h1>
|
||||||
<p>
|
<p>
|
||||||
<a href="{{ r }}/.cpr/a/partyfuse.py">partyfuse.py</a> -- fast, read-only,
|
<a href="{{ r }}/.cpr/a/partyfuse.py">partyfuse.py</a> -- fast, read-only,
|
||||||
needs <a href="{{ r }}/.cpr/w/deps/fuse.py">fuse.py</a> in the same folder,
|
needs <a href="{{ r }}/.cpr/w/deps/mfusepy.py">mfusepy.py</a> in the same folder,
|
||||||
<span class="os win">needs <a href="https://winfsp.dev/rel/">winfsp</a></span>
|
<span class="os win">needs <a href="https://winfsp.dev/rel/">winfsp</a></span>
|
||||||
<span class="os lin">doesn't need root</span>
|
<span class="os lin">doesn't need root</span>
|
||||||
</p>
|
</p>
|
||||||
|
|
|
||||||
|
|
@ -877,6 +877,16 @@ function url_enc(txt) {
|
||||||
return ret.join('/');
|
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) {
|
function uricom_dec(txt) {
|
||||||
try {
|
try {
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,63 @@
|
||||||
|
▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
|
||||||
|
# 2026-0424-2222 `v1.20.14` autolocalization
|
||||||
|
|
||||||
|
## 🧪 new features
|
||||||
|
|
||||||
|
* #1410 #376 #1224 new option `--glang` to autoselect UI-translation based on webbrowser's language (thx @stackxp!) ec3e0e7e
|
||||||
|
* #1407 #1384 option to automatically switch between list-view and grid-view depending on folder contents (thx @icxes!) 822fa718 660ed7a9 961a2737
|
||||||
|
* #1447 audioplayer can now play bcstm / bfstm / brstm files (nintendo 3ds/wii bgm) 3a9ff67a
|
||||||
|
* #1389 add 1000-based filesize-units in addition to 1024-based 43773f2c
|
||||||
|
* #1395 [reloc-by-wark](https://github.com/9001/copyparty/tree/hovudstraum/bin/hooks#more-upload-stuff), a pair of hooks to rename incoming uploads to a hash of the file contents 1e7de5d1
|
||||||
|
* option [--rlo](https://copyparty.eu/cli/#rlo-help-page) to change the logrotate-counter for [-lo](https://copyparty.eu/cli/#g-lo) 8b986888
|
||||||
|
* add `--certkey` to specify certificate and key as separate files 8c7cdf85
|
||||||
|
* but the built-in HTTPS server should [still not be trusted](https://github.com/9001/copyparty/#https)
|
||||||
|
* config-files can now use OS environment-variables anywhere in the `[global]` config section cbd82b65 e52bbed8
|
||||||
|
* by default, only the syntax `${VAR}` is supported, not `$VAR` or `%VAR%`
|
||||||
|
* previously, a small handful of global-options already supported this (`c lo hist dbpath ssl_log`), but they also supported the `$VAR` syntax, which is no longer the case
|
||||||
|
* if the old `$VAR` syntax is detected, copyparty will crash on startup, suggesting the following remedies (choose one!) in the log:
|
||||||
|
1. update the config-value to the new `${VAR}` syntax (recommended)
|
||||||
|
2. allow the old syntax with global-option `--env-expand 1` (risky)
|
||||||
|
3. ignore the old syntax and only expand the new syntax with global-option `--env-expand 2`
|
||||||
|
4. disable all environment-variable expansions with `PRTY_NO_ENVEXPAND=1`
|
||||||
|
|
||||||
|
## 🩹 bugfixes
|
||||||
|
|
||||||
|
* #1437 webdav clients can now PROPFIND a file with `depth: infinite` which at least [webdav4](https://github.com/skshetry/webdav4) does e00f2b46
|
||||||
|
* #1392 navigating into a subfolder using a `dks` [dirkey](https://github.com/9001/copyparty/#dirkeys) (default-disabled) could fail 228c3dfa
|
||||||
|
* #1446 #1330 #1362 fix some small edgecases with the rightclick-menu (thx @icxes!) 874e0e7a
|
||||||
|
* #1403 #1396 audioplayer: fix ui-crash when folder contains an m3u-file and sort-order is changed during playback (thx @icxes!) 198f631a
|
||||||
|
* #1428 #1427 when `--magic` was enabled, nameless uploads of textfiles would get the file-extension `.ssa` instead of `.txt` (thx @Scotsguy!) ed516ddc
|
||||||
|
* #1449 on some filesystems, the tail/follow function would spam the log with `reopened at byte XXX` 81730189
|
||||||
|
* #1401 on windows, a spec-violating basic-upload could delay that upload by a few seconds 6fb1287e
|
||||||
|
* on macOS, u2c would clear the terminal on exit, even with `-ns` 238887c7
|
||||||
|
* audio-files in a videofile trenchcoat did not thumbnail correctly 1066dc39
|
||||||
|
|
||||||
|
## 🔧 other changes
|
||||||
|
|
||||||
|
* #1387 added gentoo packaging (thx @mid-kid!) fb5384f4
|
||||||
|
* #1425 improved FreeBSD / OpenBSD support (thx @chilledfrogs!) f5613187 745d82fa
|
||||||
|
* #1352 new handler: [fail2ban](https://github.com/9001/copyparty/blob/hovudstraum/bin/handlers/404-to-fail2ban.py) (thx @Lomaiin!) 26e663d1
|
||||||
|
* improve errormessage when the server's OS-HDD blips out of existence d1517d0c
|
||||||
|
* #1439 improve IPv6 autoban IP-range (thx @SnowSquire!) f6dc1e29
|
||||||
|
* ensure opus transcodes will at most have 2 audio channels (stereo) b31f2902
|
||||||
|
* #1417 smb-server: probably add IPv6 support a5d859d2
|
||||||
|
* `--list-nics` and `--list-ips` to show autodetected network-adapters and IPs 8d4363d1
|
||||||
|
* docs:
|
||||||
|
* nixos module-override example (thx @Scotsguy!) 0b16e875
|
||||||
|
* make it even more obvious that `--allow-csrf` is a bad idea 9a724b01
|
||||||
|
* mention `--urlform get` to disable message-to-serverlog ac05b4f1
|
||||||
|
* readme: improve [shadowing](https://github.com/9001/copyparty#shadowing) phrasing 003c68d0
|
||||||
|
* [devnotes](https://github.com/9001/copyparty/blob/hovudstraum/docs/devnotes.md#dependencies): explain the vendored dependencies 971f8ef9
|
||||||
|
|
||||||
|
## 🌠 fun facts
|
||||||
|
|
||||||
|
* this release includes [code](https://github.com/9001/copyparty/commit/cbd82b65) written at [abs(unit)](https://a.ocv.me/pub/g/nerd-stuff/abs-unit.jpg)
|
||||||
|
* btw that pdp had an IPv6 lease and browsed the internet :^)
|
||||||
|
* hasn't connected to copyparty though (yet...)
|
||||||
|
* this release was powered by [一体いつから (TaKo Hardcore bootleg)](https://soundcloud.com/takomusiccc/tako-hardcore-bootleg) followed by [Fighting My Way (YUPPUN Hardcore Remix)](https://soundcloud.com/yuppun/fightingmyway) (shd is a good dj)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
|
▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
|
||||||
# 2026-0323-0328 `v1.20.13` dothidden
|
# 2026-0323-0328 `v1.20.13` dothidden
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -374,7 +374,7 @@ some third-party code has been vendored into the git repo; some for convenience,
|
||||||
* `dnslib` (MIT) may be deleted and replaced with a systemwide install of the original [dnslib](https://github.com/paulc/dnslib/), HOWEVER:
|
* `dnslib` (MIT) may be deleted and replaced with a systemwide install of the original [dnslib](https://github.com/paulc/dnslib/), HOWEVER:
|
||||||
* will cause problems for mDNS in some network environments; 6c1cf68bca7376c6291c3cfe710ebd5bd5ed3e6c + 94d1924fa97e5faaf1ebfd85cae73faebcb89fa1
|
* will cause problems for mDNS in some network environments; 6c1cf68bca7376c6291c3cfe710ebd5bd5ed3e6c + 94d1924fa97e5faaf1ebfd85cae73faebcb89fa1
|
||||||
|
|
||||||
* inside the folder `/copyparty/web/deps` (only in distributed archives/builds) is [fuse.py](https://github.com/fusepy/fusepy/blob/master/fuse.py), to make it downloadable from the connect-page on the web-ui
|
* inside the folder `/copyparty/web/deps` (only in distributed archives/builds) is [mfusepy.py](https://github.com/mxmlnkn/mfusepy/blob/master/mfusepy.py) (sizegolfed, no important changes), to make it downloadable from the connect-page on the web-ui
|
||||||
|
|
||||||
* inside the folder `/copyparty/web` (only in distributed archives/builds) is a collection of javascript libraries (produced by [deps-docker](https://github.com/9001/copyparty/tree/hovudstraum/scripts/deps-docker)) which are used clientside by the web-UI:
|
* inside the folder `/copyparty/web` (only in distributed archives/builds) is a collection of javascript libraries (produced by [deps-docker](https://github.com/9001/copyparty/tree/hovudstraum/scripts/deps-docker)) which are used clientside by the web-UI:
|
||||||
* [marked.js](https://github.com/markedjs/marked/releases) (MIT) powers the markdown editor, and has been [patched](https://github.com/9001/copyparty/blob/hovudstraum/scripts/deps-docker/marked-ln.patch) to include the line-numbers of each input line, to enable scroll-sync between the editor and the preview-pane. This patch is [not strictly necessary anymore](https://github.com/markedjs/marked/issues/2134) but I haven't gotten around to making the change yet
|
* [marked.js](https://github.com/markedjs/marked/releases) (MIT) powers the markdown editor, and has been [patched](https://github.com/9001/copyparty/blob/hovudstraum/scripts/deps-docker/marked-ln.patch) to include the line-numbers of each input line, to enable scroll-sync between the editor and the preview-pane. This patch is [not strictly necessary anymore](https://github.com/markedjs/marked/issues/2134) but I haven't gotten around to making the change yet
|
||||||
|
|
@ -447,6 +447,12 @@ build the sfx using any of the following examples:
|
||||||
./scripts/make-sfx.sh gz no-cm # gzip-compressed + no fancy markdown editor
|
./scripts/make-sfx.sh gz no-cm # gzip-compressed + no fancy markdown editor
|
||||||
```
|
```
|
||||||
|
|
||||||
|
on macos, you need to download several GNU utilities before building:
|
||||||
|
|
||||||
|
```zsh
|
||||||
|
brew install gsed gnu-tar findutils coreutils
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
## build from release tarball
|
## build from release tarball
|
||||||
|
|
||||||
|
|
@ -494,9 +500,13 @@ to get started, first `cd` into the `scripts` folder
|
||||||
* if you want to build a pypi package, now run `./make-pypi-release.sh d`
|
* if you want to build a pypi package, now run `./make-pypi-release.sh d`
|
||||||
|
|
||||||
* if you want to build a docker-image, you have two options:
|
* if you want to build a docker-image, you have two options:
|
||||||
* if you want to use podman to build all docker-images for all supported architectures, now run `(cd docker; ./make.sh hclean; ./make.sh hclean pull img)`
|
* if you want to use podman to build all docker-images for all supported architectures, now run `(cd docker; make -C base; ./make.sh hclean; ./make.sh hclean pull img)`
|
||||||
* if you want to use docker to build all docker-images for your native architecture, now run `sudo make -C docker`
|
* if you want to use docker to build just the `ac` docker-image for your native architecture, now run `sudo make -C docker`
|
||||||
|
* to use docker to build something other than `ac`, list the image variants you want; `sudo make -C docker min im ac iv dj`
|
||||||
* if you want to do something else, please take a look at `docker/make.sh` or `docker/Makefile` for inspiration
|
* if you want to do something else, please take a look at `docker/make.sh` or `docker/Makefile` for inspiration
|
||||||
|
* beware of the following:
|
||||||
|
* if you build with docker, you get the [stock alpine-provided ffmpeg](https://github.com/9001/copyparty/blob/hovudstraum/docs/bad-codecs.md), which makes the docker-image almost twice as big
|
||||||
|
* if you build with podman (make.sh) you get the copyparty-official legally-comfy custom ffmpeg, but the first build takes about 2-3 hours longer
|
||||||
|
|
||||||
* if you want to build the windows exe, first grab some snacks and a beer, [you'll need it](https://github.com/9001/copyparty/tree/hovudstraum/scripts/pyinstaller)
|
* if you want to build the windows exe, first grab some snacks and a beer, [you'll need it](https://github.com/9001/copyparty/tree/hovudstraum/scripts/pyinstaller)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,7 @@ https://github.com/ahupp/python-magic/
|
||||||
C: 2001-2014 Adam Hupp
|
C: 2001-2014 Adam Hupp
|
||||||
L: MIT
|
L: MIT
|
||||||
|
|
||||||
https://github.com/fusepy/fusepy
|
https://github.com/mxmlnkn/mfusepy
|
||||||
C: 2012 Giorgos Verigakis
|
C: 2012 Giorgos Verigakis
|
||||||
L: ISC
|
L: ISC
|
||||||
|
|
||||||
|
|
|
||||||
64
docs/th-raw.txt
Normal file
64
docs/th-raw.txt
Normal file
|
|
@ -0,0 +1,64 @@
|
||||||
|
FS=/home/ed/Pictures/rawsamples-ch # https://rawsamples.ch/index.php/en/ (the 7z)
|
||||||
|
|
||||||
|
find $FS -type f | sed -r 's/(.*)\.(.*)/\2 \1.\2/' | sort | tr '[:upper:]' '[:lower:]' | uniq -cw16 | sort -n | awk '{printf"%s ",$2}'
|
||||||
|
FMTS="dcr erf mdc mef ppm sr2 srf mos pdf 3fr tiff nrw kdc tif srw x3f mrw pef dng raw raf arw crw orf nef cr2 rw2 jpg"
|
||||||
|
|
||||||
|
for w in $FMTS ; do grep -E "th-r-.*\b$w\b" ~/dev/copyparty/copyparty/__main__.py || echo "$w"; done
|
||||||
|
missing rw2;
|
||||||
|
FMTS_CPP=3fr,arw,cr2,cr3,crw,dcr,dng,erf,k25,kdc,mdc,mef,mos,mrw,nef,nrw,orf,pef,raf,raw,sr2,srf,srw,x3f,rw2
|
||||||
|
|
||||||
|
rm -rf $FS/.hist
|
||||||
|
time podman run --rm -it -v $FS:/w copyparty/iv -v /w::r --exit=thgen --th-pregen=j
|
||||||
|
find $FS/.hist/th/ -iname '*.jpg' | wc -l
|
||||||
|
|
||||||
|
371 0m5.200s 3s
|
||||||
|
458 0m5.512s 3s with --th-r-raw=$FMTS_CPP
|
||||||
|
443 0m3.967s 2s with --th-r-raw=$FMTS_CPP --th-dec=raw
|
||||||
|
|
||||||
|
t0=$(date +%s); for f in $FMTS ; do rm -rf $FS/.hist
|
||||||
|
podman run --rm -it -v $FS:/w copyparty/iv -v /w::r --exit=thgen --th-pregen=j --th-dec=raw --th-r-raw=$f -q >/dev/null 2>&1
|
||||||
|
printf '%s ' $(find $FS/.hist/th/ -size +0 -iname '*.jpg' | wc -l)
|
||||||
|
done;t=$(date +%s);echo $((t-t0))
|
||||||
|
|
||||||
|
95f 55s --th-dec=ff
|
||||||
|
397f 51s --th-dec=raw (rawpy)
|
||||||
|
153f 51s --th-dec=vips (no-magick)
|
||||||
|
|
||||||
|
# swithc to persistent for messingaround
|
||||||
|
podman run --rm -it -v $FS:/w --entrypoint /bin/ash copyparty/iv
|
||||||
|
apk update
|
||||||
|
apk upgrade -lai
|
||||||
|
|
||||||
|
t0=$(date +%s); for f in $FMTS ; do rm -rf $FS/.hist
|
||||||
|
podman exec -it d171470581ab python3 -m copyparty -v /w::r --exit=thgen --th-pregen=j --th-dec=vips --th-r-vips=$f -q >/dev/null 2>&1
|
||||||
|
printf '%s ' $(find $FS/.hist/th/ -size +0 -iname '*.jpg' | wc -l)
|
||||||
|
done;t=$(date +%s);echo $((t-t0))
|
||||||
|
# equivalent results
|
||||||
|
|
||||||
|
apk add imagemagick; t0=$(date +%s); rm -rf /w/.hist/ ; for f in $FMTS ; do rm -f /*.jpg; n=0; find /w -type f -iname "*.$f" | while IFS= read -r x; do magick "$x" -scale 320x /$n.jpg >/dev/null 2>&1 ; [ -s /$n.jpg ] || rm -f /$n.jpg; n=$((n+1)); done; echo -n "$(ls -1 / | grep -F .jpg | wc -l) "; done; t=$(date +%s); echo $((t-t0))
|
||||||
|
|
||||||
|
apk add libraw-tools; t0=$(date +%s); rm -rf /w/.hist/ ; for f in $FMTS ; do rm -f /*.jpg; n=0; find /w -type f -iname "*.$f" | while IFS= read -r x; do [ $(dcraw_emu -h -o 1 -s 0 -Z - "$x" 2>/dev/null | wc -c) -gt 1024 ] && touch /$n.jpg; n=$((n+1)); done; echo -n "$(ls -1 / | grep -F .jpg | wc -l) "; done; t=$(date +%s); echo $((t-t0))
|
||||||
|
|
||||||
|
dcr erf mdc mef ppm sr2 srf mos pdf 3fr tiff nrw kdc tif srw x3f mrw pef dng raw raf arw crw orf nef cr2 rw2 jpg
|
||||||
|
d e m m p s s m p 3 t n k t s x m p d r r a c o n c r j
|
||||||
|
c r d e p r r o d f i r d i r 3 r e n a a r r r e r w p
|
||||||
|
r f c f m 2 f s f r f w c f w f w f g w f w w f f 2 2 g
|
||||||
|
-----------------------------------------------------------------
|
||||||
|
0 0 0 0 1 1 0 0 0 0 3 0 2 6 0 0 0 6 17 0 0 0 0 0 54 0 0 5 = 95, 55s --th-dec=ff
|
||||||
|
1 1 0 1 0 1 1 2 0 3 0 4 5 6 7 0 8 17 16 2 30 31 17 45 56 56 87 0 =397, 51s --th-dec=raw ## rawpy
|
||||||
|
1 1 1 1 0 1 1 2 0 3 0 4 5 6 6 0 9 17 18 24 30 31 34 45 56 57 87 0 =440, 87s dcraw_emu
|
||||||
|
1 1 1 1 1 1 1 2 0 3 6 4 5 6 6 0 9 17 18 24 30 31 34 45 56 57 87 5 =452, 226s magick-cmd
|
||||||
|
0 1 0 1 1 0 0 0 2 3 3 0 3 6 0 0 0 0 17 0 0 0 0 0 56 55 0 5 =153, 51s --th-dec=vips ## stock
|
||||||
|
0 1 0 1 1 1 1 0 2 3 3 4 5 6 6 0 8 0 18 11 30 30 18 39 56 55 87 5 =391, 151s vips + apk add imagemagick imagemagick-raw
|
||||||
|
0 1 0 1 1 0 0 0 2 3 3 0 3 6 0 0 8 0 17 11 30 0 18 39 56 55 87 5 =346, 128s vips + apk del imagemagick (just imagemagick-raw)
|
||||||
|
|
||||||
|
xsel -o | tr ' ' '\n' | awk '!$0{next} {t+=$1} END{print t}'
|
||||||
|
|
||||||
|
apk add time
|
||||||
|
rm -rf /w/.hist; time python3 -m copyparty -v /w::r --exit=thgen --th-pregen=j --th-dec=raw --th-r-raw=$FMTS_CPP ; find /w/.hist/ -iname '*.jpg' -size +0 | wc -l
|
||||||
|
|
||||||
|
391f, 0:03.77elapsed 227264maxresident # rawpy+vips with-embedded-thumbs
|
||||||
|
391f, 0:04.79elapsed 467036maxresident # rawpy+pillow with-embedded-thumbs
|
||||||
|
434f, 0:29.67elapsed 307724maxresident # dcraw+vips
|
||||||
|
434f, 0:29.34elapsed 327980maxresident # dcraw+pillow
|
||||||
|
374f, 1:49.70elapsed 4574768maxresident # vips+imagemagick lol lmao
|
||||||
|
|
@ -2,7 +2,7 @@ FROM alpine:3.23
|
||||||
WORKDIR /z
|
WORKDIR /z
|
||||||
ENV ver_hashwasm=4.12.0 \
|
ENV ver_hashwasm=4.12.0 \
|
||||||
ver_marked=4.3.0 \
|
ver_marked=4.3.0 \
|
||||||
ver_dompf=3.4.1 \
|
ver_dompf=3.4.6 \
|
||||||
ver_mde=2.18.0 \
|
ver_mde=2.18.0 \
|
||||||
ver_codemirror=5.65.18 \
|
ver_codemirror=5.65.18 \
|
||||||
ver_fontawesome=5.13.0 \
|
ver_fontawesome=5.13.0 \
|
||||||
|
|
@ -21,11 +21,14 @@ ENV ver_hashwasm=4.12.0 \
|
||||||
# download;
|
# download;
|
||||||
# the scp url is regular latin from https://fonts.googleapis.com/css2?family=Source+Code+Pro&display=swap
|
# the scp url is regular latin from https://fonts.googleapis.com/css2?family=Source+Code+Pro&display=swap
|
||||||
RUN mkdir -p /z/dist/no-pk \
|
RUN mkdir -p /z/dist/no-pk \
|
||||||
|
&& wget https://ocv.me/dev/fonts/orbitron.woff2 -O /z/dist/no-pk/orbitron.woff2 \
|
||||||
|
&& sha512sum /z/dist/no-pk/orbitron.woff2 | tee /dev/stderr | grep -q b36a69a1b483ca735a3e7d026cdc5e993b8359bbf69b05deadc369fa759f88fe6087bbbbc0be51e62daf034c090df7ee096e5b07884c494aebf56db0e2bc53f7 \
|
||||||
&& wget https://fonts.gstatic.com/s/sourcecodepro/v11/HI_SiYsKILxRpg3hIP6sJ7fM7PqlPevW.woff2 -O scp.woff2 \
|
&& wget https://fonts.gstatic.com/s/sourcecodepro/v11/HI_SiYsKILxRpg3hIP6sJ7fM7PqlPevW.woff2 -O scp.woff2 \
|
||||||
&& apk add \
|
&& apk add \
|
||||||
bash brotli cmake make g++ git gzip lame npm patch pigz \
|
bash brotli cmake make g++ git gzip lame npm patch pigz \
|
||||||
python3 python3-dev py3-brotli sox tar unzip wget \
|
python3 python3-dev py3-pip py3-brotli sox tar unzip wget \
|
||||||
&& rm -f /usr/lib/python3*/EXTERNALLY-MANAGED \
|
&& rm -f /usr/lib/python3*/EXTERNALLY-MANAGED \
|
||||||
|
&& pip install strip_hints \
|
||||||
&& wget https://github.com/markedjs/marked/archive/v$ver_marked.tar.gz -O marked.tgz \
|
&& wget https://github.com/markedjs/marked/archive/v$ver_marked.tar.gz -O marked.tgz \
|
||||||
&& wget https://github.com/Ionaru/easy-markdown-editor/archive/$ver_mde.tar.gz -O mde.tgz \
|
&& wget https://github.com/Ionaru/easy-markdown-editor/archive/$ver_mde.tar.gz -O mde.tgz \
|
||||||
&& wget https://github.com/codemirror/codemirror5/archive/$ver_codemirror.tar.gz -O codemirror.tgz \
|
&& wget https://github.com/codemirror/codemirror5/archive/$ver_codemirror.tar.gz -O codemirror.tgz \
|
||||||
|
|
@ -34,7 +37,7 @@ RUN mkdir -p /z/dist/no-pk \
|
||||||
&& wget https://github.com/google/zopfli/archive/zopfli-$ver_zopfli.tar.gz -O zopfli.tgz \
|
&& wget https://github.com/google/zopfli/archive/zopfli-$ver_zopfli.tar.gz -O zopfli.tgz \
|
||||||
&& wget https://github.com/Daninet/hash-wasm/releases/download/v$ver_hashwasm/hash-wasm@$ver_hashwasm.zip -O hash-wasm.zip \
|
&& wget https://github.com/Daninet/hash-wasm/releases/download/v$ver_hashwasm/hash-wasm@$ver_hashwasm.zip -O hash-wasm.zip \
|
||||||
&& wget https://github.com/PrismJS/prism/archive/refs/tags/v$ver_prism.tar.gz -O prism.tgz \
|
&& wget https://github.com/PrismJS/prism/archive/refs/tags/v$ver_prism.tar.gz -O prism.tgz \
|
||||||
&& wget https://files.pythonhosted.org/packages/04/0b/4506cb2e831cea4b0214d3625430e921faaa05a7fb520458c75a2dbd2152/fusepy-3.0.1.tar.gz -O fusepy.tgz \
|
&& wget https://files.pythonhosted.org/packages/91/47/746287c8962274f73ee25edb3840d80899464bfffbe2c435424c2d60a071/mfusepy-3.1.1.tar.gz -O mfusepy.tgz \
|
||||||
&& (mkdir hash-wasm \
|
&& (mkdir hash-wasm \
|
||||||
&& cd hash-wasm \
|
&& cd hash-wasm \
|
||||||
&& unzip ../hash-wasm.zip) \
|
&& unzip ../hash-wasm.zip) \
|
||||||
|
|
@ -51,7 +54,7 @@ RUN mkdir -p /z/dist/no-pk \
|
||||||
&& npm i gulp-cli -g ) \
|
&& npm i gulp-cli -g ) \
|
||||||
&& tar --no-same-owner -xf dompurify.tgz \
|
&& tar --no-same-owner -xf dompurify.tgz \
|
||||||
&& tar --no-same-owner -xf prism.tgz \
|
&& tar --no-same-owner -xf prism.tgz \
|
||||||
&& tar --no-same-owner -xf fusepy.tgz \
|
&& tar --no-same-owner -xf mfusepy.tgz \
|
||||||
&& unzip fontawesome.zip \
|
&& unzip fontawesome.zip \
|
||||||
&& tar --no-same-owner -xf zopfli.tgz
|
&& tar --no-same-owner -xf zopfli.tgz
|
||||||
|
|
||||||
|
|
@ -148,16 +151,23 @@ RUN cd /z/dist \
|
||||||
&& rmdir no-pk
|
&& rmdir no-pk
|
||||||
|
|
||||||
|
|
||||||
# build fusepy
|
# build mfusepy -- just sizegolfing for the sfx, mfusepy.py works fine as-is
|
||||||
COPY uncomment.py /z
|
COPY uncomment.py unhint.py /z
|
||||||
RUN mv /z/fusepy-3.0.1/fuse.py /z/dist/f1 \
|
RUN mv /z/mfusepy-3.1.1/mfusepy.py /z/dist/ \
|
||||||
&& cd /z/dist \
|
&& cd /z/dist \
|
||||||
|
&& python3 /z/unhint.py \
|
||||||
|
&& rm -f uh \
|
||||||
|
&& mv mfusepy.py f1 \
|
||||||
&& python3 /z/uncomment.py f1 \
|
&& python3 /z/uncomment.py f1 \
|
||||||
&& sed -ri '/self.__critical_exception = e/d' f1 \
|
&& sed -ri '/self.__critical_exception/d; /^from (typing|collections.abc) import/d' f1 \
|
||||||
&& awk '/^log =/{s=0} !s; /^from traceback im/{s=1;print"from functools import partial";print"basestring = str"}' <f1 >f2 \
|
&& sed -ri '/^(FieldsEntry|BitFieldsEntry|ReadDirResult) =/d' f1 \
|
||||||
&& awk '/LoggingMixIn:/{exit} --s<0;/self.use_ns = getattr/{s=7}' <f2 >f1 \
|
&& awk "/if TYPE_CHECKING:/{s=1;sub(/TYPE_CHECKING/,"1");print}!s;/else:/{s=0}" <f1 >f2 \
|
||||||
&& awk "/if _machine =/{s=0} /'(mips|ppc|ppc64)'/{s=1} !s" <f1 >f2 \
|
&& awk '/^def _nullable_dummy_function/{print"class Operations:\n pass";exit};1' <f2 >f1 \
|
||||||
&& rm f1 && mv f2 fuse.py
|
&& awk '/^# Note that/{s=3} --s<0; /self.use_ns = getattr/{s=16}' <f1 >f2 \
|
||||||
|
&& awk "/if _machine =/{s=0} /_machine == '(mips|ppc|ppc64)'/{s=1} !s" <f2 >f1 \
|
||||||
|
&& awk '/else:/{s=0} /elif _system == .NetBSD/{s=1} !s' <f1 >f2 \
|
||||||
|
&& awk '/^if _system == .NetBSD_False/{s=1;print"if 0:\n pass"} /^elif/{s=0} !s' <f2 >f1 \
|
||||||
|
&& rm f2 && mv f1 mfusepy.py
|
||||||
|
|
||||||
|
|
||||||
# git diff -U2 --no-index marked-1.1.0-orig/ marked-1.1.0-edit/ -U2 | sed -r '/^index /d;s`^(diff --git a/)[^/]+/(.* b/)[^/]+/`\1\2`; s`^(---|\+\+\+) ([ab]/)[^/]+/`\1 \2`' > ../dev/copyparty/scripts/deps-docker/marked-ln.patch
|
# git diff -U2 --no-index marked-1.1.0-orig/ marked-1.1.0-edit/ -U2 | sed -r '/^index /d;s`^(diff --git a/)[^/]+/(.* b/)[^/]+/`\1\2`; s`^(---|\+\+\+) ([ab]/)[^/]+/`\1 \2`' > ../dev/copyparty/scripts/deps-docker/marked-ln.patch
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ vend := $(self)/../../copyparty/web/deps
|
||||||
|
|
||||||
all:
|
all:
|
||||||
cp -pv ../uncomment.py .
|
cp -pv ../uncomment.py .
|
||||||
|
cp -pv ../strip_hints/a.py unhint.py
|
||||||
|
|
||||||
docker build -t build-copyparty-deps .
|
docker build -t build-copyparty-deps .
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -114,12 +114,14 @@ diff -wNarU2 codemirror-5.65.1-orig/src/input/ContentEditableInput.js codemirror
|
||||||
+ /*
|
+ /*
|
||||||
let order = getOrder(line, cm.doc.direction), side = "left"
|
let order = getOrder(line, cm.doc.direction), side = "left"
|
||||||
if (order) {
|
if (order) {
|
||||||
@@ -405,4 +406,5 @@
|
@@ -405,5 +406,6 @@
|
||||||
side = partPos % 2 ? "right" : "left"
|
side = partPos % 2 ? "right" : "left"
|
||||||
}
|
}
|
||||||
|
- let result = nodeAndOffsetInLineMap(info.map, pos.ch, side)
|
||||||
+ */
|
+ */
|
||||||
let result = nodeAndOffsetInLineMap(info.map, pos.ch, side)
|
+ let result = nodeAndOffsetInLineMap(info.map, pos.ch, "left")
|
||||||
result.offset = result.collapse == "right" ? result.end : result.start
|
result.offset = result.collapse == "right" ? result.end : result.start
|
||||||
|
return result
|
||||||
diff -wNarU2 codemirror-5.65.1-orig/src/input/movement.js codemirror-5.65.1/src/input/movement.js
|
diff -wNarU2 codemirror-5.65.1-orig/src/input/movement.js codemirror-5.65.1/src/input/movement.js
|
||||||
--- codemirror-5.65.1-orig/src/input/movement.js 2022-01-20 13:06:23.000000000 +0100
|
--- codemirror-5.65.1-orig/src/input/movement.js 2022-01-20 13:06:23.000000000 +0100
|
||||||
+++ codemirror-5.65.1/src/input/movement.js 2022-02-09 22:50:18.145862052 +0100
|
+++ codemirror-5.65.1/src/input/movement.js 2022-02-09 22:50:18.145862052 +0100
|
||||||
|
|
|
||||||
|
|
@ -6,9 +6,10 @@ LABEL org.opencontainers.image.url="https://github.com/9001/copyparty" \
|
||||||
org.opencontainers.image.title="copyparty-ac" \
|
org.opencontainers.image.title="copyparty-ac" \
|
||||||
org.opencontainers.image.description="copyparty with Pillow and FFmpeg (image/audio/video thumbnails, audio transcoding, media tags)"
|
org.opencontainers.image.description="copyparty with Pillow and FFmpeg (image/audio/video thumbnails, audio transcoding, media tags)"
|
||||||
ENV XDG_CONFIG_HOME=/cfg
|
ENV XDG_CONFIG_HOME=/cfg
|
||||||
|
ARG ADD_PKG=""
|
||||||
|
|
||||||
RUN apk --no-cache add !pyc \
|
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-jinja2 py3-argon2-cffi py3-pyzmq \
|
||||||
py3-openssl py3-paramiko py3-pillow
|
py3-openssl py3-paramiko py3-pillow
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,30 +6,29 @@ LABEL org.opencontainers.image.url="https://github.com/9001/copyparty" \
|
||||||
org.opencontainers.image.title="copyparty-dj" \
|
org.opencontainers.image.title="copyparty-dj" \
|
||||||
org.opencontainers.image.description="copyparty with all optional dependencies, including musical key / bpm detection"
|
org.opencontainers.image.description="copyparty with all optional dependencies, including musical key / bpm detection"
|
||||||
ENV XDG_CONFIG_HOME=/cfg
|
ENV XDG_CONFIG_HOME=/cfg
|
||||||
|
ARG ADD_PKG=""
|
||||||
|
|
||||||
COPY i/bin/mtag/install-deps.sh ./
|
COPY i/bin/mtag/install-deps.sh ./
|
||||||
COPY i/bin/mtag/audio-bpm.py /mtag/
|
COPY i/bin/mtag/audio-bpm.py /mtag/
|
||||||
COPY i/bin/mtag/audio-key.py /mtag/
|
COPY i/bin/mtag/audio-key.py /mtag/
|
||||||
RUN apk add -U !pyc \
|
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-jinja2 py3-argon2-cffi py3-pyzmq \
|
||||||
py3-openssl py3-paramiko py3-pillow \
|
py3-openssl py3-paramiko py3-pillow \
|
||||||
py3-pip py3-cffi \
|
py3-pip \
|
||||||
py3-magic \
|
py3-magic \
|
||||||
vips-jxl vips-poppler vips-magick \
|
vips-jxl vips-poppler vips-magick \
|
||||||
py3-numpy fftw libsndfile \
|
py3-numpy fftw libsndfile \
|
||||||
vamp-sdk vamp-sdk-libs keyfinder-cli \
|
vamp-sdk vamp-sdk-libs keyfinder-cli \
|
||||||
libraw py3-numpy \
|
libraw-tools \
|
||||||
&& apk add -t .bd \
|
&& apk add -t .bd \
|
||||||
bash wget gcc g++ make cmake patchelf \
|
bash wget gcc g++ make cmake patchelf \
|
||||||
ffmpeg ffmpeg-dev \
|
ffmpeg ffmpeg-dev \
|
||||||
python3-dev fftw-dev libsndfile-dev \
|
python3-dev fftw-dev libsndfile-dev \
|
||||||
py3-wheel py3-numpy-dev libffi-dev \
|
py3-wheel py3-numpy-dev \
|
||||||
vamp-sdk-dev \
|
vamp-sdk-dev \
|
||||||
libraw-dev py3-numpy-dev cython \
|
|
||||||
&& rm -f /usr/lib/python3*/EXTERNALLY-MANAGED \
|
&& rm -f /usr/lib/python3*/EXTERNALLY-MANAGED \
|
||||||
&& python3 -m pip install pyvips \
|
&& python3 -m pip install pyvips --no-build-isolation \
|
||||||
&& python3 -m pip install "$(wget -O- https://api.github.com/repos/letmaik/rawpy/releases/latest | awk -F\" '$2=="tarball_url"{print$4}')" \
|
|
||||||
&& bash install-deps.sh \
|
&& bash install-deps.sh \
|
||||||
&& apk del py3-pip .bd \
|
&& apk del py3-pip .bd \
|
||||||
&& chmod 777 /root \
|
&& chmod 777 /root \
|
||||||
|
|
|
||||||
|
|
@ -6,8 +6,9 @@ LABEL org.opencontainers.image.url="https://github.com/9001/copyparty" \
|
||||||
org.opencontainers.image.title="copyparty-im" \
|
org.opencontainers.image.title="copyparty-im" \
|
||||||
org.opencontainers.image.description="copyparty with Pillow and Mutagen (image thumbnails, media tags)"
|
org.opencontainers.image.description="copyparty with Pillow and Mutagen (image thumbnails, media tags)"
|
||||||
ENV XDG_CONFIG_HOME=/cfg
|
ENV XDG_CONFIG_HOME=/cfg
|
||||||
|
ARG ADD_PKG=""
|
||||||
|
|
||||||
RUN apk --no-cache add !pyc \
|
RUN apk --no-cache add !pyc ${ADD_PKG} \
|
||||||
tzdata wget mimalloc2 mimalloc2-insecure \
|
tzdata wget mimalloc2 mimalloc2-insecure \
|
||||||
py3-jinja2 py3-argon2-cffi \
|
py3-jinja2 py3-argon2-cffi \
|
||||||
py3-openssl py3-paramiko py3-pillow py3-mutagen
|
py3-openssl py3-paramiko py3-pillow py3-mutagen
|
||||||
|
|
|
||||||
|
|
@ -6,23 +6,18 @@ LABEL org.opencontainers.image.url="https://github.com/9001/copyparty" \
|
||||||
org.opencontainers.image.title="copyparty-iv" \
|
org.opencontainers.image.title="copyparty-iv" \
|
||||||
org.opencontainers.image.description="copyparty with Pillow, FFmpeg, libvips (image/audio/video thumbnails, audio transcoding, media tags)"
|
org.opencontainers.image.description="copyparty with Pillow, FFmpeg, libvips (image/audio/video thumbnails, audio transcoding, media tags)"
|
||||||
ENV XDG_CONFIG_HOME=/cfg
|
ENV XDG_CONFIG_HOME=/cfg
|
||||||
|
ARG ADD_PKG=""
|
||||||
|
|
||||||
RUN apk add -U !pyc \
|
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-jinja2 py3-argon2-cffi py3-pyzmq \
|
||||||
py3-openssl py3-paramiko py3-pillow \
|
py3-openssl py3-paramiko py3-pillow \
|
||||||
py3-pip py3-cffi \
|
py3-pip \
|
||||||
py3-magic \
|
py3-magic \
|
||||||
vips-jxl vips-poppler vips-magick \
|
vips-jxl vips-poppler vips-magick \
|
||||||
libraw py3-numpy \
|
libraw-tools \
|
||||||
&& apk add -t .bd \
|
|
||||||
bash wget gcc g++ make cmake patchelf \
|
|
||||||
python3-dev py3-wheel libffi-dev \
|
|
||||||
libraw-dev py3-numpy-dev cython \
|
|
||||||
&& rm -f /usr/lib/python3*/EXTERNALLY-MANAGED \
|
&& rm -f /usr/lib/python3*/EXTERNALLY-MANAGED \
|
||||||
&& python3 -m pip install pyvips \
|
&& python3 -m pip install pyvips --no-build-isolation
|
||||||
&& 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 innvikler.sh ./
|
COPY i innvikler.sh ./
|
||||||
RUN ash innvikler.sh iv
|
RUN ash innvikler.sh iv
|
||||||
|
|
|
||||||
|
|
@ -6,8 +6,9 @@ LABEL org.opencontainers.image.url="https://github.com/9001/copyparty" \
|
||||||
org.opencontainers.image.title="copyparty-min" \
|
org.opencontainers.image.title="copyparty-min" \
|
||||||
org.opencontainers.image.description="just copyparty, no thumbnails / media tags / audio transcoding"
|
org.opencontainers.image.description="just copyparty, no thumbnails / media tags / audio transcoding"
|
||||||
ENV XDG_CONFIG_HOME=/cfg
|
ENV XDG_CONFIG_HOME=/cfg
|
||||||
|
ARG ADD_PKG=""
|
||||||
|
|
||||||
RUN apk --no-cache add !pyc \
|
RUN apk --no-cache add !pyc ${ADD_PKG} \
|
||||||
py3-jinja2
|
py3-jinja2
|
||||||
|
|
||||||
COPY i innvikler.sh ./
|
COPY i innvikler.sh ./
|
||||||
|
|
|
||||||
|
|
@ -1,57 +1,32 @@
|
||||||
self := $(dir $(abspath $(lastword $(MAKEFILE_LIST))))
|
self := $(dir $(abspath $(lastword $(MAKEFILE_LIST))))
|
||||||
|
|
||||||
all:
|
.PHONY: i min im ac iv dj clean hclean purge sh
|
||||||
-service docker start
|
|
||||||
-systemctl start docker
|
|
||||||
|
|
||||||
|
all: ac
|
||||||
|
|
||||||
|
i:
|
||||||
rm -rf i
|
rm -rf i
|
||||||
mkdir i
|
mkdir i
|
||||||
tar -cC../.. dist/copyparty-sfx.py bin/mtag | tar -xvCi
|
tar -cC../../dist copyparty-sfx.py | tar -xvCi
|
||||||
|
tar -cC../.. bin/mtag | tar -xvCi
|
||||||
|
# # use stock ffmpeg because ./base/Makefile requires podman;
|
||||||
|
# # beware: https://github.com/9001/copyparty/blob/hovudstraum/docs/bad-codecs.md
|
||||||
|
touch i/stock_ffmpeg
|
||||||
|
|
||||||
|
min: i
|
||||||
docker build -t copyparty/min:latest -f Dockerfile.min .
|
docker build -t copyparty/min:latest -f Dockerfile.min .
|
||||||
echo 'scale=1;'`docker save copyparty/min:latest | pigz -c | wc -c`/1024/1024 | bc
|
|
||||||
|
|
||||||
# docker build -t copyparty/min-pip:latest -f Dockerfile.min.pip .
|
|
||||||
# echo 'scale=1;'`docker save copyparty/min-pip:latest | pigz -c | wc -c`/1024/1024 | bc
|
|
||||||
|
|
||||||
|
im: i
|
||||||
docker build -t copyparty/im:latest -f Dockerfile.im .
|
docker build -t copyparty/im:latest -f Dockerfile.im .
|
||||||
echo 'scale=1;'`docker save copyparty/im:latest | pigz -c | wc -c`/1024/1024 | bc
|
|
||||||
|
|
||||||
docker build -t copyparty/iv:latest -f Dockerfile.iv .
|
ac: i
|
||||||
echo 'scale=1;'`docker save copyparty/iv:latest | pigz -c | wc -c`/1024/1024 | bc
|
docker build -t copyparty/ac:latest -f Dockerfile.ac --build-arg ADD_PKG=ffmpeg .
|
||||||
|
|
||||||
docker build -t copyparty/ac:latest -f Dockerfile.ac .
|
iv: i
|
||||||
echo 'scale=1;'`docker save copyparty/ac:latest | pigz -c | wc -c`/1024/1024 | bc
|
docker build -t copyparty/iv:latest -f Dockerfile.iv --build-arg ADD_PKG=ffmpeg .
|
||||||
|
|
||||||
docker build -t copyparty/dj:latest -f Dockerfile.dj .
|
dj: i
|
||||||
echo 'scale=1;'`docker save copyparty/dj:latest | pigz -c | wc -c`/1024/1024 | bc
|
docker build -t copyparty/dj:latest -f Dockerfile.dj --build-arg ADD_PKG=ffmpeg .
|
||||||
|
|
||||||
docker image ls
|
|
||||||
|
|
||||||
min:
|
|
||||||
rm -rf i
|
|
||||||
mkdir i
|
|
||||||
tar -cC../.. dist/copyparty-sfx.py bin/mtag | tar -xvCi
|
|
||||||
|
|
||||||
podman build --squash --pull=always -t copyparty/min:latest -f Dockerfile.min .
|
|
||||||
echo 'scale=1;'`podman save copyparty/min:latest | pigz -c | wc -c`/1024/1024 | bc
|
|
||||||
|
|
||||||
push:
|
|
||||||
docker push copyparty/min
|
|
||||||
docker push copyparty/im
|
|
||||||
docker push copyparty/iv
|
|
||||||
docker push copyparty/ac
|
|
||||||
docker push copyparty/dj
|
|
||||||
docker image tag copyparty/min:latest ghcr.io/9001/copyparty-min:latest
|
|
||||||
docker image tag copyparty/im:latest ghcr.io/9001/copyparty-im:latest
|
|
||||||
docker image tag copyparty/iv:latest ghcr.io/9001/copyparty-iv:latest
|
|
||||||
docker image tag copyparty/ac:latest ghcr.io/9001/copyparty-ac:latest
|
|
||||||
docker image tag copyparty/dj:latest ghcr.io/9001/copyparty-dj:latest
|
|
||||||
docker push ghcr.io/9001/copyparty-min:latest
|
|
||||||
docker push ghcr.io/9001/copyparty-im:latest
|
|
||||||
docker push ghcr.io/9001/copyparty-iv:latest
|
|
||||||
docker push ghcr.io/9001/copyparty-ac:latest
|
|
||||||
docker push ghcr.io/9001/copyparty-dj:latest
|
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
-docker kill `docker ps -q`
|
-docker kill `docker ps -q`
|
||||||
|
|
@ -66,7 +41,7 @@ hclean:
|
||||||
purge:
|
purge:
|
||||||
-docker kill `docker ps -q`
|
-docker kill `docker ps -q`
|
||||||
-docker rm `docker ps -qa`
|
-docker rm `docker ps -qa`
|
||||||
-docker rmi `docker images -qa`
|
-docker rmi `docker images -qa` -f
|
||||||
|
|
||||||
sh:
|
sh:
|
||||||
@printf "\n\033[1;31mopening a shell in the most recently created docker image\033[0m\n"
|
@printf "\n\033[1;31mopening a shell in the most recently created docker image\033[0m\n"
|
||||||
|
|
|
||||||
|
|
@ -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
|
[`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`
|
| this architecture | can run these editions |
|
||||||
* `dj` doesn't run on `ppc64le`, `s390x`, `armhf`
|
| ------------------------------ | ----------------------------- |
|
||||||
* `iv` doesn't run on `ppc64le`, `s390x`
|
| `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
|
> NOTE: the following editions are unfinished experiments, and not published anywhere: djd djf djff dju
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ all:
|
||||||
|
|
||||||
ff:
|
ff:
|
||||||
# legally comfy
|
# legally comfy
|
||||||
/usr/bin/time ./build-no265.sh img
|
/usr/bin/time ./build-no265.sh pull img
|
||||||
|
|
||||||
|
|
||||||
zlib:
|
zlib:
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,13 @@
|
||||||
#!/bin/ash
|
#!/bin/ash
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
|
AVER=3.24
|
||||||
|
|
||||||
[ $1 = 1 ] && hub=1
|
[ $1 = 1 ] && hub=1
|
||||||
|
|
||||||
uname -a
|
uname -a
|
||||||
|
apk update
|
||||||
|
apk upgrade -al
|
||||||
apk add alpine-sdk doas wget
|
apk add alpine-sdk doas wget
|
||||||
echo permit nopass root > /etc/doas.d/u.conf
|
echo permit nopass root > /etc/doas.d/u.conf
|
||||||
cp -pv /root/.abuild/*.pub /etc/apk/keys/ || abuild-keygen -ina
|
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
|
mkdir /ffmpeg
|
||||||
cd /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
|
wget ${base}APKBUILD
|
||||||
awk <APKBUILD -vb="$base" '/"/{o=0}/^source=/{o=1;next}o{print b $1}' | wget -i-
|
awk <APKBUILD -vb="$base" '/"/{o=0}/^source=/{o=1;next}o{print b $1}' | wget -i-
|
||||||
cp -pv APKBUILD /root/
|
cp -pv APKBUILD /root/
|
||||||
|
|
@ -29,16 +33,23 @@ prepare() {
|
||||||
default_prepare
|
default_prepare
|
||||||
tar -cC/opt/patch/ffmpeg . | tar -x
|
tar -cC/opt/patch/ffmpeg . | tar -x
|
||||||
patch -p1 <aac-lc-only.patch
|
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
|
EOF
|
||||||
|
|
||||||
##
|
##
|
||||||
## shrink-ray
|
## 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)
|
# `- 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
|
sed -ri 's/\bffplay$//; s/\bsdl2-dev\b//' APKBUILD
|
||||||
|
|
||||||
##
|
##
|
||||||
|
|
@ -48,10 +59,12 @@ sed -ri 's/\bffplay$//; s/\bsdl2-dev\b//' APKBUILD
|
||||||
sed -ri 's/--enable-(ladspa|lv2|vaapi|vulkan)/--disable-\1/' APKBUILD
|
sed -ri 's/--enable-(ladspa|lv2|vaapi|vulkan)/--disable-\1/' APKBUILD
|
||||||
sed -ri 's/--enable-lib(aom|ass|drm|fontconfig|freetype|fribidi|harfbuzz|pulse|rist|srt|ssh|v4l2|vidstab|x264|xvid|zimg|vpl)/--disable-lib\1/' APKBUILD
|
sed -ri 's/--enable-lib(aom|ass|drm|fontconfig|freetype|fribidi|harfbuzz|pulse|rist|srt|ssh|v4l2|vidstab|x264|xvid|zimg|vpl)/--disable-lib\1/' APKBUILD
|
||||||
sed -ri 's/\b(v4l-utils|libvpx)-dev\b//' APKBUILD # (try to) drop v4l2_m2m, and use builtin vp8/vp9 instead of libvpx for decode
|
sed -ri 's/\b(v4l-utils|libvpx)-dev\b//' APKBUILD # (try to) drop v4l2_m2m, and use builtin vp8/vp9 instead of libvpx for decode
|
||||||
sed -ri 's/(--disable-vulkan)/\1 --disable-devices --disable-hwaccels --disable-encoders --enable-encoder=flac --enable-encoder=libjxl --enable-encoder=libmp3lame --enable-encoder=libopus --enable-encoder=libwebp --enable-encoder=mjpeg --enable-encoder=pcm_s16le --enable-encoder=pcm_s16le_planar --enable-encoder=png --enable-encoder=rawvideo --enable-encoder=vnull --enable-encoder=wrapped_avframe --disable-muxers --enable-muxer=aiff --enable-muxer=apng --enable-muxer=caf --enable-muxer=ffmetadata --enable-muxer=fifo --enable-muxer=flac --enable-muxer=image2 --enable-muxer=image2pipe --enable-muxer=matroska --enable-muxer=matroska_audio --enable-muxer=mjpeg --enable-muxer=mp3 --enable-muxer=null --enable-muxer=opus --enable-muxer=pcm_s16le --enable-muxer=wav --enable-muxer=webm --enable-muxer=webp --enable-muxer=yuv4mpegpipe --disable-filters --enable-filter=anoisesrc --enable-filter=asplit --enable-filter=amerge --enable-filter=amix --enable-filter=aresample --enable-filter=crop --enable-filter=showspectrumpic --enable-filter=showwavespic --enable-filter=convolution --enable-filter=volume --enable-filter=compand --enable-filter=setsar --enable-filter=scale --disable-decoder=av1 --disable-hwaccel=v4l2_m2m --disable-decoder=h263_v4l2m2m --disable-decoder=h264_v4l2m2m --disable-decoder=mpeg1_v4l2m2m --disable-decoder=mpeg2_v4l2m2m --disable-decoder=mpeg4_v4l2m2m --disable-decoder=vc1_v4l2m2m --disable-decoder=vp8_v4l2m2m --disable-decoder=vp9_v4l2m2m --disable-decoder=subrip --disable-decoder=srt --disable-decoder=pgssub --disable-decoder=cc_dec --disable-decoder=dvdsub --disable-decoder=dvbsub --disable-decoder=ssa --disable-decoder=ass --disable-decoder=opus /' APKBUILD
|
sed -ri 's/(--disable-vulkan)/\1 --disable-devices --disable-hwaccels --disable-encoders --enable-encoder=flac --enable-encoder=libjxl --enable-encoder=libmp3lame --enable-encoder=libopus --enable-encoder=libwebp --enable-encoder=mjpeg --enable-encoder=pcm_f32le --enable-encoder=pcm_s16le --enable-encoder=pcm_s16le_planar --enable-encoder=png --enable-encoder=rawvideo --enable-encoder=vnull --enable-encoder=wrapped_avframe --disable-muxers --enable-muxer=aiff --enable-muxer=apng --enable-muxer=caf --enable-muxer=ffmetadata --enable-muxer=fifo --enable-muxer=flac --enable-muxer=image2 --enable-muxer=image2pipe --enable-muxer=matroska --enable-muxer=matroska_audio --enable-muxer=mjpeg --enable-muxer=mp3 --enable-muxer=null --enable-muxer=opus --enable-muxer=pcm_f32le --enable-muxer=pcm_s16le --enable-muxer=wav --enable-muxer=webm --enable-muxer=webp --enable-muxer=yuv4mpegpipe --disable-filters --enable-filter=anoisesrc --enable-filter=asplit --enable-filter=amerge --enable-filter=amix --enable-filter=aresample --enable-filter=crop --enable-filter=showspectrumpic --enable-filter=showwavespic --enable-filter=convolution --enable-filter=volume --enable-filter=compand --enable-filter=setsar --enable-filter=scale --disable-decoder=av1 --disable-hwaccel=v4l2_m2m --disable-decoder=h263_v4l2m2m --disable-decoder=h264_v4l2m2m --disable-decoder=mpeg1_v4l2m2m --disable-decoder=mpeg2_v4l2m2m --disable-decoder=mpeg4_v4l2m2m --disable-decoder=vc1_v4l2m2m --disable-decoder=vp8_v4l2m2m --disable-decoder=vp9_v4l2m2m --disable-decoder=subrip --disable-decoder=srt --disable-decoder=pgssub --disable-decoder=cc_dec --disable-decoder=dvdsub --disable-decoder=dvbsub --disable-decoder=ssa --disable-decoder=ass --disable-decoder=opus /' APKBUILD
|
||||||
# `- s/av1/libdav1d/; s/libvorbis/vorbis/; s/opus/libopus/; libvorbis and mpg123 gets pulled in by openmpt
|
# `- 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)
|
p=/root/packages/$(abuild -A)
|
||||||
rm -rf $p
|
rm -rf $p
|
||||||
abuild -FrcK
|
abuild -FrcK
|
||||||
|
|
@ -59,6 +72,7 @@ abuild -FrcK
|
||||||
mkdir $p/ex
|
mkdir $p/ex
|
||||||
mv $p/ffmpeg-d* $p/ex # dbg,dev,doc
|
mv $p/ffmpeg-d* $p/ex # dbg,dev,doc
|
||||||
cp -pv src/ffmpeg-*/ffbuild/config.log $p/
|
cp -pv src/ffmpeg-*/ffbuild/config.log $p/
|
||||||
|
#tar -cz src > $p/.tar
|
||||||
|
|
||||||
[ $hub ] && rm -rf $p.hub && mv $p $p.hub
|
[ $hub ] && rm -rf $p.hub && mv $p $p.hub
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
|
AVER=3.24
|
||||||
|
|
||||||
[ $(id -u) -eq 0 ] && {
|
[ $(id -u) -eq 0 ] && {
|
||||||
echo dont root
|
echo dont root
|
||||||
exit 1
|
exit 1
|
||||||
|
|
@ -34,8 +36,8 @@ wt() {
|
||||||
}
|
}
|
||||||
|
|
||||||
[ $pull ] && {
|
[ $pull ] && {
|
||||||
for a in $sarchs; do # arm/v6
|
for a in $sarchs; do
|
||||||
podman pull --arch=$a alpine:latest
|
podman pull --arch=$a alpine:$AVER
|
||||||
done
|
done
|
||||||
|
|
||||||
podman images --format "{{.ID}} {{.History}}" |
|
podman images --format "{{.ID}} {{.History}}" |
|
||||||
|
|
@ -62,19 +64,20 @@ wt() {
|
||||||
# kill abandoned builders
|
# kill abandoned builders
|
||||||
ps aux | awk '/bin\/qemu-[^-]+-static/{print$2}' | xargs -r kill -9
|
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
|
for a in $archs; do
|
||||||
n=$((n+1)); wt "$n/$a"
|
n=$((n+1)); wt "$n/$a"
|
||||||
#[ $n -le 3 ] || continue
|
#[ $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 |
|
tar -c arbeidspakke.sh patch/ffmpeg |
|
||||||
time nice podman run \
|
time nice podman run \
|
||||||
--rm -i --pull=never -v "$self/b:/root:z" localhost/alpine-$a \
|
--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 |
|
/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
|
done
|
||||||
wt -;wt ""
|
wt -;wt ""
|
||||||
}
|
}
|
||||||
|
|
@ -89,11 +92,23 @@ echo ok
|
||||||
# 50m01.04 s390x
|
# 50m01.04 s390x
|
||||||
|
|
||||||
# golflympics
|
# golflympics
|
||||||
# 4:09 x86_64-hub
|
# 3:48 x86_64-hub
|
||||||
# 2:57 x86_64
|
# 2:46 x86_64
|
||||||
# 2:54 x86
|
# 2:24 x86
|
||||||
# 31:13 aarch64
|
# 28:50 aarch64
|
||||||
# 22:38 armv7
|
# 21:34 armv7
|
||||||
# 32:17 s390x
|
# 31:13 s390x
|
||||||
# 24:27 ppc64le
|
# 22:50 ppc64le
|
||||||
# 2:00:35 summa summarum
|
# 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
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,19 @@
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
v=3.23
|
AVER=3.24
|
||||||
|
|
||||||
|
v=$AVER-stable
|
||||||
|
#v=master
|
||||||
|
|
||||||
mkdir -p cver
|
mkdir -p cver
|
||||||
rm -rf cver2
|
rm -rf cver2
|
||||||
mkdir cver2
|
mkdir cver2
|
||||||
cd cver2
|
cd cver2
|
||||||
curl \
|
curl \
|
||||||
-Lo1 https://raw.githubusercontent.com/alpinelinux/aports/refs/heads/$v-stable/main/musl/APKBUILD \
|
-Lo1 https://raw.githubusercontent.com/alpinelinux/aports/refs/heads/$v/main/musl/APKBUILD \
|
||||||
-Lo2 https://raw.githubusercontent.com/alpinelinux/aports/refs/heads/$v-stable/main/python3/APKBUILD \
|
-Lo2 https://raw.githubusercontent.com/alpinelinux/aports/refs/heads/$v/main/python3/APKBUILD \
|
||||||
-Lo3 https://raw.githubusercontent.com/alpinelinux/aports/refs/heads/$v-stable/community/ffmpeg/APKBUILD \
|
-Lo3 https://raw.githubusercontent.com/alpinelinux/aports/refs/heads/$v/community/ffmpeg/APKBUILD \
|
||||||
;
|
;
|
||||||
|
|
||||||
zlib= ff=
|
zlib= ff=
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@ ised() {
|
||||||
}
|
}
|
||||||
|
|
||||||
# use custom ffmpeg if relevant
|
# use custom ffmpeg if relevant
|
||||||
echo $1 | grep -qE 'ac|iv|dj' && (
|
echo $1 | grep -qE 'ac|iv|dj' && [ ! -e "/z/stock_ffmpeg" ] && (
|
||||||
cp -pv /z/packages/*.pub /etc/apk/keys/
|
cp -pv /z/packages/*.pub /etc/apk/keys/
|
||||||
cd /z/packages/$(cat /etc/apk/arch)
|
cd /z/packages/$(cat /etc/apk/arch)
|
||||||
apk add ./ffmpeg-*.apk
|
apk add ./ffmpeg-*.apk
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ imgs="dj iv min im ac"
|
||||||
dhub_order="iv dj min im ac"
|
dhub_order="iv dj min im ac"
|
||||||
ghcr_order="ac im min dj iv"
|
ghcr_order="ac im min dj iv"
|
||||||
ngs=(
|
ngs=(
|
||||||
iv-{ppc64le,s390x}
|
iv-{ppc64le,s390x,arm}
|
||||||
dj-{ppc64le,s390x,arm}
|
dj-{ppc64le,s390x,arm}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -511,6 +511,7 @@ unhelpg() {
|
||||||
[ $no_hl ] &&
|
[ $no_hl ] &&
|
||||||
rm -rf copyparty/web/deps/prism*
|
rm -rf copyparty/web/deps/prism*
|
||||||
|
|
||||||
|
rm -f copyparty/web/deps/orbitron.woff2 # todo:uiv15
|
||||||
[ $no_fnt ] && {
|
[ $no_fnt ] && {
|
||||||
rm -f copyparty/web/deps/scp.woff2
|
rm -f copyparty/web/deps/scp.woff2
|
||||||
f=copyparty/web/ui.css
|
f=copyparty/web/ui.css
|
||||||
|
|
|
||||||
|
|
@ -23,12 +23,12 @@ ac96786e5d35882e0c5b724794329c9125c2b86ae7847f17acfc49f0d294312c6afc1c3f248655de
|
||||||
# win10
|
# win10
|
||||||
f4b4e330995ebe96c0bd06e16e5b26062ece9473f06d369775aa68eab261dedcf32dfdd159acaa227502bbf9fa2cd8bbe57cddb89fc6f2196fef7a9ed5a80ae9 Git-2.51.0-64-bit.exe
|
f4b4e330995ebe96c0bd06e16e5b26062ece9473f06d369775aa68eab261dedcf32dfdd159acaa227502bbf9fa2cd8bbe57cddb89fc6f2196fef7a9ed5a80ae9 Git-2.51.0-64-bit.exe
|
||||||
0a2cd4cadf0395f0374974cd2bc2407e5cc65c111275acdffb6ecc5a2026eee9e1bb3da528b35c7f0ff4b64563a74857d5c2149051e281cc09ebd0d1968be9aa en-us_windows_10_enterprise_ltsc_2021_x64_dvd_d289cf96.iso
|
0a2cd4cadf0395f0374974cd2bc2407e5cc65c111275acdffb6ecc5a2026eee9e1bb3da528b35c7f0ff4b64563a74857d5c2149051e281cc09ebd0d1968be9aa en-us_windows_10_enterprise_ltsc_2021_x64_dvd_d289cf96.iso
|
||||||
16cc0c58b5df6c7040893089f3eb29c074aed61d76dae6cd628d8a89a05f6223ac5d7f3f709a12417c147594a87a94cc808d1e04a6f1e407cc41f7c9f47790d1 virtio-win-0.1.285.iso
|
4f13070cc9241fa342deab4ebfac360565030580ff77b6e5f1951a64627621e5da4abfd30e1e46ca8bae2bb7dd4ff98141aff424142c9629a5876a61283962e5 virtio-win-0.1.285.iso
|
||||||
9a7f40edc6f9209a2acd23793f3cbd6213c94f36064048cb8bf6eb04f1bdb2c2fe991cb09f77fe8b13e5cd85c618ef23573e79813b2fef899ab2f290cd129779 jinja2-3.1.6-py3-none-any.whl
|
9a7f40edc6f9209a2acd23793f3cbd6213c94f36064048cb8bf6eb04f1bdb2c2fe991cb09f77fe8b13e5cd85c618ef23573e79813b2fef899ab2f290cd129779 jinja2-3.1.6-py3-none-any.whl
|
||||||
00731cfdd9d5c12efef04a7161c90c1e5ed1dc4677aa88a1d4054aff836f3430df4da5262ed4289c21637358a9e10e5df16f76743cbf5a29bb3a44b146c19cf3 MarkupSafe-3.0.2-cp313-cp313-win_amd64.whl
|
00731cfdd9d5c12efef04a7161c90c1e5ed1dc4677aa88a1d4054aff836f3430df4da5262ed4289c21637358a9e10e5df16f76743cbf5a29bb3a44b146c19cf3 MarkupSafe-3.0.2-cp313-cp313-win_amd64.whl
|
||||||
8a6e2b13a2ec4ef914a5d62aad3db6464d45e525a82e07f6051ed10474eae959069e165dba011aefb8207cdfd55391d73d6f06362c7eb247b08763106709526e mutagen-1.47.0-py3-none-any.whl
|
8a6e2b13a2ec4ef914a5d62aad3db6464d45e525a82e07f6051ed10474eae959069e165dba011aefb8207cdfd55391d73d6f06362c7eb247b08763106709526e mutagen-1.47.0-py3-none-any.whl
|
||||||
a726fb46cce24f781fc8b55a3e6dea0a884ebc3b2b400ea74aa02333699f4955a5dc1e2ec5927ac72f35a624401f3f3b442882ba1cc4cadaf9c88558b5b8bdae packaging-25.0-py3-none-any.whl
|
a726fb46cce24f781fc8b55a3e6dea0a884ebc3b2b400ea74aa02333699f4955a5dc1e2ec5927ac72f35a624401f3f3b442882ba1cc4cadaf9c88558b5b8bdae packaging-25.0-py3-none-any.whl
|
||||||
efc712162da7fb005c8869a7612d2f4983d2d073ec79e16a58e7bf1fcd01c88b1cc26656f0893c68edd2294be7c3990db2f6bd77e7e3f2613539d57994b6a033 pillow-12.1.1-cp313-cp313-win_amd64.whl
|
5459cfe12d953ed37c481a992e3509536b7997fbd1bb77158d3465d86d3d57af9a16fd4d695374fe6ed30cbb12ac90a2de3000dd92897ddf8bdcfc3e3de831bd pillow-12.2.0-cp313-cp313-win_amd64.whl
|
||||||
b9b98714dfca6fa80b0b3f222965724d63be9c54d19435d1fe768e07016913d6db8d6e043fcb185b55a9bd6fe370a80cf961814fc096046a5f4640d99ed575ef pyinstaller-6.15.0-py3-none-win_amd64.whl
|
b9b98714dfca6fa80b0b3f222965724d63be9c54d19435d1fe768e07016913d6db8d6e043fcb185b55a9bd6fe370a80cf961814fc096046a5f4640d99ed575ef pyinstaller-6.15.0-py3-none-win_amd64.whl
|
||||||
cad0f7cf39de691813b1d4abc7d33f8bda99a87d9c5886039b814752e8690364150da26fb61b3e28d5698ff57a90e6dcd619ed2b64b04f72b5aadb75e201bdb0 pyinstaller_hooks_contrib-2025.8-py3-none-any.whl
|
cad0f7cf39de691813b1d4abc7d33f8bda99a87d9c5886039b814752e8690364150da26fb61b3e28d5698ff57a90e6dcd619ed2b64b04f72b5aadb75e201bdb0 pyinstaller_hooks_contrib-2025.8-py3-none-any.whl
|
||||||
368ea2da3e3bfe765a37c62227e84774853aaabce6954475fa45c873e5547cb5346ca03a0f6a0789af369285bb3464881fed0275a19066913d9d396d5d9b9947 python-3.13.13-amd64.exe
|
368ea2da3e3bfe765a37c62227e84774853aaabce6954475fa45c873e5547cb5346ca03a0f6a0789af369285bb3464881fed0275a19066913d9d396d5d9b9947 python-3.13.13-amd64.exe
|
||||||
|
|
|
||||||
|
|
@ -39,7 +39,7 @@ fns=(
|
||||||
MarkupSafe-3.0.2-cp313-cp313-win_amd64.whl
|
MarkupSafe-3.0.2-cp313-cp313-win_amd64.whl
|
||||||
mutagen-1.47.0-py3-none-any.whl
|
mutagen-1.47.0-py3-none-any.whl
|
||||||
packaging-25.0-py3-none-any.whl
|
packaging-25.0-py3-none-any.whl
|
||||||
pillow-12.1.1-cp313-cp313-win_amd64.whl
|
pillow-12.2.0-cp313-cp313-win_amd64.whl
|
||||||
pyinstaller-6.15.0-py3-none-win_amd64.whl
|
pyinstaller-6.15.0-py3-none-win_amd64.whl
|
||||||
pyinstaller_hooks_contrib-2025.8-py3-none-any.whl
|
pyinstaller_hooks_contrib-2025.8-py3-none-any.whl
|
||||||
python-3.13.13-amd64.exe
|
python-3.13.13-amd64.exe
|
||||||
|
|
|
||||||
|
|
@ -80,7 +80,7 @@ copyparty/web/deps/__init__.py,
|
||||||
copyparty/web/deps/busy.mp3,
|
copyparty/web/deps/busy.mp3,
|
||||||
copyparty/web/deps/easymde.css,
|
copyparty/web/deps/easymde.css,
|
||||||
copyparty/web/deps/easymde.js,
|
copyparty/web/deps/easymde.js,
|
||||||
copyparty/web/deps/fuse.py,
|
copyparty/web/deps/mfusepy.py,
|
||||||
copyparty/web/deps/marked.js,
|
copyparty/web/deps/marked.js,
|
||||||
copyparty/web/deps/mini-fa.css,
|
copyparty/web/deps/mini-fa.css,
|
||||||
copyparty/web/deps/mini-fa.woff,
|
copyparty/web/deps/mini-fa.woff,
|
||||||
|
|
|
||||||
|
|
@ -38,7 +38,10 @@ class TestVFS(unittest.TestCase):
|
||||||
unpacked.append(list(sorted(getattr(axs, k))))
|
unpacked.append(list(sorted(getattr(axs, k))))
|
||||||
|
|
||||||
pad = len(unpacked) - len(expected)
|
pad = len(unpacked) - len(expected)
|
||||||
self.assertEqual(unpacked, expected + [[]] * pad)
|
want = expected + [[]] * pad
|
||||||
|
if want[0] and not want[4]:
|
||||||
|
want[4] = want[0]
|
||||||
|
self.assertEqual(unpacked, want)
|
||||||
|
|
||||||
def assertAxsAt(self, au, vp, expected):
|
def assertAxsAt(self, au, vp, expected):
|
||||||
vn = self.nav(au, vp)
|
vn = self.nav(au, vp)
|
||||||
|
|
|
||||||
|
|
@ -188,8 +188,8 @@ class TestVFS(unittest.TestCase):
|
||||||
self.assertAxs(n.axs.uread, ["*", "k"])
|
self.assertAxs(n.axs.uread, ["*", "k"])
|
||||||
self.assertAxs(n.axs.uwrite, [])
|
self.assertAxs(n.axs.uwrite, [])
|
||||||
perm_na = (False, False, False, False, False, False, False, False, False)
|
perm_na = (False, False, False, False, False, False, False, False, False)
|
||||||
perm_rw = (True, True, False, False, False, False, False, False, False)
|
perm_rw = (True, True, False, False, True, False, False, False, False)
|
||||||
perm_ro = (True, False, False, False, False, False, False, False, False)
|
perm_ro = (True, False, False, False, True, False, False, False, False)
|
||||||
self.assertEqual(vfs.can_access("/", "*"), perm_na)
|
self.assertEqual(vfs.can_access("/", "*"), perm_na)
|
||||||
self.assertEqual(vfs.can_access("/", "k"), perm_rw)
|
self.assertEqual(vfs.can_access("/", "k"), perm_rw)
|
||||||
self.assertEqual(vfs.can_access("/a", "*"), perm_ro)
|
self.assertEqual(vfs.can_access("/a", "*"), perm_ro)
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue