Compare commits

..

No commits in common. "hovudstraum" and "v1.20.20" have entirely different histories.

30 changed files with 119 additions and 440 deletions

1
.gitattributes vendored
View file

@ -4,5 +4,4 @@
*.png binary *.png binary
*.gif binary *.gif binary
*.ico binary
*.gz binary *.gz binary

View file

@ -84,7 +84,6 @@ built in Norway 🇳🇴 with contributions from [not-norway](https://github.com
* [exclude-patterns](#exclude-patterns) - to save some time * [exclude-patterns](#exclude-patterns) - to save some time
* [filesystem guards](#filesystem-guards) - avoid traversing into other filesystems * [filesystem guards](#filesystem-guards) - avoid traversing into other filesystems
* [periodic rescan](#periodic-rescan) - filesystem monitoring * [periodic rescan](#periodic-rescan) - filesystem monitoring
* [u2sz](#u2sz) - configure upload-chunking
* [upload rules](#upload-rules) - set upload rules using volflags * [upload rules](#upload-rules) - set upload rules using volflags
* [compress uploads](#compress-uploads) - files can be autocompressed on upload * [compress uploads](#compress-uploads) - files can be autocompressed on upload
* [chmod and chown](#chmod-and-chown) - per-volume filesystem-permissions and ownership * [chmod and chown](#chmod-and-chown) - per-volume filesystem-permissions and ownership
@ -923,7 +922,7 @@ note that since up2k has to read each file twice, `[🎈] bup` can *theoreticall
if you are resuming a massive upload and want to skip hashing the files which already finished, you can enable `turbo` in the `[⚙️] config` tab, but please read the tooltip on that button if you are resuming a massive upload and want to skip hashing the files which already finished, you can enable `turbo` in the `[⚙️] config` tab, but please read the tooltip on that button
if the server is behind a proxy which imposes a request-size limit, you can configure up2k to sneak below the limit with server-option [--u2sz](#u2sz) if the server is behind a proxy which imposes a request-size limit, you can configure up2k to sneak below the limit with server-option `--u2sz` (the default is 96 MiB to support Cloudflare)
if you want to replace existing files on the server with new uploads by default, run with `--u2ow 2` (only works if users have the delete-permission, and can still be disabled with `🛡️` in the UI) if you want to replace existing files on the server with new uploads by default, run with `--u2ow 2` (only works if users have the delete-permission, and can still be disabled with `🛡️` in the UI)
@ -1827,23 +1826,6 @@ config file example:
``` ```
## u2sz
configure upload-chunking; the default `64,96` is optimal for nearly every scenario, however:
* if you are seeing the message `bad server config; see "u2sz" in readme` then you have a proxy/CDN/whatever with a `max-request-body-size` which is too small, and you must either increase the limit in the reverseproxy config to at least 96 MiB (recommended) or reduce the target chunksize in copyparty (continue reading)
the global-option `--u2sz` is two numbers, `target,max` MiB of each chunk to send, aiming for `target` MiB and allowing up-to-and-including `max` MiB [depending on filesize](./docs/devnotes.md#list-of-chunk-sizes), and if you only specify one number then it sets that for both
cloudflare max is 96 MiB, but if you have a reverseproxy (or CDN/WAF/whatever) which is more restrictive, then try some of the following:
* `u2sz: 32` is still acceptable performance in most cases
* `u2sz: 8` is the lowest recommended value; anything lower will reduce performance over long physical distances and/or shitty routes
* `u2sz: 1` is the absolute minimum, and will be slow in many cases
the setting can also be changed in the web-UI; the textbox next to `date-chk` in the settings tab
## upload rules ## upload rules
set upload rules using volflags, some examples: set upload rules using volflags, some examples:

View file

@ -1,54 +0,0 @@
# thumbnail extractors
extract (or generate) thumbnails for custom file formats or override default thumbnail extraction for standard media files
## usage
* load plugins with `--th-extract baz=~/dev/copyparty/bin/thumbs/randomcolor.py`
* `baz` is the file extension (may be comma-separated list of extensions)
* add multiple different extractor plugins by repeating the `--th-extract` argument
## api
### in
each plugin must define a function `main(abspath, **kwargs)`:
* `abspath` is the path to a file to extract thumbnail for
* `kwargs` currently receives these keyword arguments:
* `vn` is the VFS which contains the requested file
* `th_srv` is an instance of [copyparty/th_srv](https://github.com/9001/copyparty/blob/hovudstraum/copyparty/th_srv.py)
### out
the `main()` function must
* return `None` or raise an `Exception` in case it is unable to extract thumbnail
* return tuple `fmt, stream, offset, whence, size` otherwise:
* `fmt: str` format (filename extension) of the thumbnail; should be one of the image formats supported by copyparty (see `--th-r-*` options)
* `stream: IO[bytes]` binary file-like object supporting `seek()`, `read()`, `close()` methods; should contain extracted thumbnail image
* `offset: int, whence: int` arguments to `stream.seek()`; copyparty will call `stream.seek(offset, whence)` once before starting reading the stream contents
* `size: int | None` number of bytes to read from the `stream`; if `size` is negative or `None`, copyparty will read until the end of the stream
### notes
it is possible to extract or generate custom thumbnails for standard media files too,
e.g. run copyparty with `--th-extract mp3,m4a,aac,flac,opus=~/dev/copyparty/bin/thumbs/my_custom_mp3_th_extractor.py` and do your magic with audio file thumbnails
in case copyparty doesn't like the `fmt` option, it will `close()` the `stream` without ever calling `seek()` or `read()`
if your scenario involves heavy computation or i/o, it is better to defer the heavy parts until the first `seek()` or `read()` to avoid redundant work in case the result gets rejected based on `fmt` value
if you provide custom `stream` object:
copyparty performs buffered reading, so expect multiple `read()` calls, respect `size` argument in `read(size)` calls, and maintain correct seek position
## examples
* [randomcolor](https://github.com/9001/copyparty/blob/hovudstraum/bin/thumbs/randomcolor.py) generates a random .gif image
## some other known plugins seen on the internets
* [fpkg_thumb](https://github.com/kamaeff/copyparty-dumb-fpkgi-handler/blob/master/fpkg_thumb.py) extracts cover images from playstation4 software installation packages ("pkg" and "fpkg" files)

View file

@ -1,29 +0,0 @@
import io
import os
import struct
from base64 import b64decode
def create_gif() -> bytes:
head = r"R0lGODdhIAAgAIAB"
tail = r"AAD/LAAAAAAgACAAAAIehI+py+0Po5y02ouz3rz7D4biSJbmiabqyrbuC5sFADs="
#color = struct.pack(">L", random.randrange(0xffffff)) # wow, way too stable
color = b"\x00" + os.urandom(3)
return b64decode(head) + color + b64decode(tail)
def main(abspath, **ka) -> Optional[tuple[str, IO[bytes], int, int, Optional[int]]]:
print("hello from randomcolor.py pretending to extract a thumb from " + abspath)
# need to return a file-like object
gif_bytes = create_gif()
file_obj = io.BytesIO(gif_bytes)
# where copyparty should seek to in the "file" to start reading from
seek_offset = 0
seek_whence = 0
filesize = None # auto; read until end
# "gif" is the file-extension to decode the file as
return "gif", file_obj, seek_offset, seek_whence, filesize

View file

@ -1576,7 +1576,7 @@ NOTE: if server has --usernames enabled, then password is "username:password"
ap.add_argument("url", type=unicode, help="server url, including destination folder") ap.add_argument("url", type=unicode, help="server url, including destination folder")
ap.add_argument("files", type=files_decoder, nargs="+", help="files and/or folders to process") ap.add_argument("files", type=files_decoder, nargs="+", help="files and/or folders to process")
ap.add_argument("-v", action="store_true", help="verbose") ap.add_argument("-v", action="store_true", help="verbose")
ap.add_argument("-a", metavar="PASSWD", default="", help="password (or $filepath) for copyparty (is sent in header 'PW'), default is env.var U2C_PW") ap.add_argument("-a", metavar="PASSWD", default="", help="password (or $filepath) for copyparty (is sent in header 'PW')")
ap.add_argument("--ba", metavar="PASSWD", default="", help="password (or $filepath) for basic-auth (usually not necessary)") ap.add_argument("--ba", metavar="PASSWD", default="", help="password (or $filepath) for basic-auth (usually not necessary)")
ap.add_argument("-s", action="store_true", help="file-search (disables upload)") ap.add_argument("-s", action="store_true", help="file-search (disables upload)")
ap.add_argument("-x", type=unicode, metavar="REGEX", action="append", help="skip file if filesystem-abspath matches REGEX (option can be repeated), example: '.*/\\.hist/.*'") ap.add_argument("-x", type=unicode, metavar="REGEX", action="append", help="skip file if filesystem-abspath matches REGEX (option can be repeated), example: '.*/\\.hist/.*'")
@ -1729,9 +1729,6 @@ NOTE: if server has --usernames enabled, then password is "username:password"
with open(zs[1:], "rb") as f: with open(zs[1:], "rb") as f:
setattr(ar, k, f.read().decode("utf-8").strip()) setattr(ar, k, f.read().decode("utf-8").strip())
if not ar.a:
ar.a = os.environ.get("U2C_PW", "")
if ar.ba: if ar.ba:
ar.ba = "Basic " + base64.b64encode(ar.ba.encode("utf-8")).decode("utf-8") ar.ba = "Basic " + base64.b64encode(ar.ba.encode("utf-8")).decode("utf-8")

View file

@ -17,12 +17,8 @@ defaults
listen foo1 listen foo1
bind *:8081 bind *:8081
http-request add-header X-Forwarded-Proto https if { ssl_fc }
http-request add-header X-Forwarded-Proto http unless { ssl_fc }
server srv1 127.0.0.1:3923 maxconn 512 server srv1 127.0.0.1:3923 maxconn 512
listen foo2 listen foo2
bind *:8082 bind *:8082
http-request add-header X-Forwarded-Proto https if { ssl_fc }
http-request add-header X-Forwarded-Proto http unless { ssl_fc }
server srv1 /dev/shm/party.sock maxconn 512 server srv1 /dev/shm/party.sock maxconn 512

View file

@ -3,7 +3,7 @@
# NOTE: You generally shouldn't use this PKGBUILD on Arch, as it is mainly for testing purposes. Install copyparty using pacman instead. # 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.20" pkgver="1.20.19"
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=("a31f9e4a43e56608765e108353140784d50181c37e705e9ce8ed9f3899f57dfa") sha256sums=("834d97d637575cb9b2d53052790b6512f462b3241dff489cf641539ef624fd6c")
build() { build() {
cd "${srcdir}/${pkgname}-${pkgver}/copyparty/web" cd "${srcdir}/${pkgname}-${pkgver}/copyparty/web"

View file

@ -2,7 +2,7 @@
pkgname=copyparty pkgname=copyparty
pkgver=1.20.20 pkgver=1.20.19
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=("a31f9e4a43e56608765e108353140784d50181c37e705e9ce8ed9f3899f57dfa") sha256sums=("834d97d637575cb9b2d53052790b6512f462b3241dff489cf641539ef624fd6c")
build() { build() {
cd "${srcdir}/${pkgname}-${pkgver}/copyparty/web" cd "${srcdir}/${pkgname}-${pkgver}/copyparty/web"

View file

@ -1,5 +1,5 @@
{ {
"url": "https://github.com/9001/copyparty/releases/download/v1.20.20/copyparty-1.20.20.tar.gz", "url": "https://github.com/9001/copyparty/releases/download/v1.20.19/copyparty-1.20.19.tar.gz",
"version": "1.20.20", "version": "1.20.19",
"hash": "sha256-ox+eSkPlZgh2XhCDUxQHhNUBgcN+cF6c6O2fOJn1ffo=" "hash": "sha256-g02X1jdXXLmy1TBSeQtlEvRisyQd/0ic9kFTnvYk/Ww="
} }

View file

@ -40,9 +40,6 @@ VT100 = "--ansi" in sys.argv or (
) )
# introduced in anniversary update # introduced in anniversary update
zs = os.environ.get("PRTY_CONFIG", "")
CFG_DEF = [zs] if zs else []
ANYWIN = WINDOWS or sys.platform in ["msys", "cygwin"] ANYWIN = WINDOWS or sys.platform in ["msys", "cygwin"]
MACOS = platform.system() == "Darwin" MACOS = platform.system() == "Darwin"

View file

@ -23,7 +23,6 @@ import uuid
from .__init__ import ( from .__init__ import (
ANYWIN, ANYWIN,
CFG_DEF,
CORES, CORES,
EXE, EXE,
MACOS, MACOS,
@ -64,7 +63,6 @@ from .util import (
URL_BUG, URL_BUG,
URL_PRJ, URL_PRJ,
Daemon, Daemon,
absreal,
align_tab, align_tab,
b64enc, b64enc,
ctypes, ctypes,
@ -107,6 +105,10 @@ except:
u = unicode u = unicode
zsid = uuid.uuid4().urn[4:] zsid = uuid.uuid4().urn[4:]
CFG_DEF = [os.environ.get("PRTY_CONFIG", "")]
if not CFG_DEF[0]:
CFG_DEF.pop()
FULL_HELP = os.environ.get("PRTY_FULL_HELP", "") FULL_HELP = os.environ.get("PRTY_FULL_HELP", "")
@ -777,54 +779,6 @@ def get_sects():
).rstrip() ).rstrip()
+ build_flags_desc(), + build_flags_desc(),
], ],
[
"thumb-ex",
"extract thumbnails from custom file formats with python scripts",
dedent(
"""
copyparty is able to extract thumbnails from various media formats:
images, audio, video, ebooks
if you want to extract thumbnails from unsupported file formats,
you can load a plugin, which extracts image data from custom formats
load the plugin using --args; for example \033[36m
--tx-extract iso,mdf,dmg=~/party-thumb-extractors/th_diskimg.py
\033[0m
the file must define the function \033[35mmain(abspath, **kwargs)\033[0m:
\033[35mabspath\033[0m: path to the file to extract thumbnail from
\033[35mkwargs\033[0m currently have:
\033[35mvn\033[0m: the VFS which contains the requested file
\033[35mrth_srv\033[0m: the copyparty ThumbSrv instance
if `main` couldn't extract a thumbnail, it must return None;
otherwise the return value must be a tuple:
\033[35mreturn fmt, stream, offset, whence, size\033[0m
> \033[32mfmt\033[0m: thumbnail format, should be one of the formats
supported by copyparty; e.g. "png", "jpg"
> \033[32mstream\033[0m: binary file-like object containing the thumbnail,
supporting seek(), read(), and close() methods
> \033[32moffset, whence\033[0m: arguments to seek() method;
stream.seek(offset, whence) will be called once before copyparty
starts reading from the stream
> \033[32msize\033[0m: number of bytes to read ftom the stream;
if negative, copyparty will read through the end of the stream;
you can also supply extractors for standard formats to override default
thumbnail extraction behaviour; for example \033[36m
--tx-extract mp3=~/party-thumb-extractors/th_mp3.py
\033[0mwill let you extract or generate custom thumbnails for mp3 files
\033[1;35mPS!\033[0m the folder that contains the python file should ideally
not contain many other python files, and especially nothing
with filenames that overlap with modules used by copyparty
"""
),
],
[ [
"handlers", "handlers",
"use plugins to handle certain events", "use plugins to handle certain events",
@ -1384,7 +1338,7 @@ def add_upload(ap):
ap2.add_argument("--turbo", metavar="LVL", type=int, default=0, help="configure turbo-mode in up2k client; [\033[32m-1\033[0m] = forbidden/always-off, [\033[32m0\033[0m] = default-off and warn if enabled, [\033[32m1\033[0m] = default-off, [\033[32m2\033[0m] = on, [\033[32m3\033[0m] = on and disable datecheck") ap2.add_argument("--turbo", metavar="LVL", type=int, default=0, help="configure turbo-mode in up2k client; [\033[32m-1\033[0m] = forbidden/always-off, [\033[32m0\033[0m] = default-off and warn if enabled, [\033[32m1\033[0m] = default-off, [\033[32m2\033[0m] = on, [\033[32m3\033[0m] = on and disable datecheck")
ap2.add_argument("--nosubtle", metavar="N", type=int, default=0, help="when to use a wasm-hasher instead of the browser's builtin; faster on chrome, but buggy in older chrome versions. [\033[32m0\033[0m] = only when necessary (non-https), [\033[32m1\033[0m] = always (all browsers), [\033[32m2\033[0m] = always on chrome/firefox, [\033[32m3\033[0m] = always on chrome, [\033[32mN\033[0m] = chrome-version N and newer (recommendation: 137)") ap2.add_argument("--nosubtle", metavar="N", type=int, default=0, help="when to use a wasm-hasher instead of the browser's builtin; faster on chrome, but buggy in older chrome versions. [\033[32m0\033[0m] = only when necessary (non-https), [\033[32m1\033[0m] = always (all browsers), [\033[32m2\033[0m] = always on chrome/firefox, [\033[32m3\033[0m] = always on chrome, [\033[32mN\033[0m] = chrome-version N and newer (recommendation: 137)")
ap2.add_argument("--u2j", metavar="JOBS", type=int, default=2, help="web-client: number of file chunks to upload in parallel; 1 or 2 is good when latency is low (same-country), 2~4 for android-clients, 2~6 for cross-atlantic. Max is 6 in most browsers. Big values increase network-speed but may reduce HDD-speed") ap2.add_argument("--u2j", metavar="JOBS", type=int, default=2, help="web-client: number of file chunks to upload in parallel; 1 or 2 is good when latency is low (same-country), 2~4 for android-clients, 2~6 for cross-atlantic. Max is 6 in most browsers. Big values increase network-speed but may reduce HDD-speed")
ap2.add_argument("--u2sz", metavar="N,N", type=u, default="64,96", help="web-client: default upload chunksize in MiB, either \033[33mtarget,max\033[0m or just \033[33mtarget\033[0m (sets both); this is \033[33mtarget\033[0m in the web-UI. Each HTTP POST will aim for \033[33mtarget\033[0m and never exceed \033[33mmax\033[0m. Cloudflare max is 96. Minimum is [\033[32m1\033[0m] and will be slow. Lowest recommended is [\033[32m8\033[0m]. Big values are good for cross-atlantic but may increase HDD fragmentation on some FS. Disable this optimization with [\033[32mno\033[0m]") ap2.add_argument("--u2sz", metavar="N,N,N", type=u, default="1,64,96", help="web-client: default upload chunksize (MiB); sets \033[33mmin,default,max\033[0m in the settings gui. Each HTTP POST will aim for \033[33mdefault\033[0m, and never exceed \033[33mmax\033[0m. Cloudflare max is 96. Big values are good for cross-atlantic but may increase HDD fragmentation on some FS. Disable this optimization with [\033[32m1,1,1\033[0m]")
ap2.add_argument("--u2ow", metavar="NUM", type=int, default=0, help="web-client: default setting for when to replace/overwrite existing files; [\033[32m0\033[0m]=never, [\033[32m1\033[0m]=if-client-newer, [\033[32m2\033[0m]=always (volflag=u2ow)") ap2.add_argument("--u2ow", metavar="NUM", type=int, default=0, help="web-client: default setting for when to replace/overwrite existing files; [\033[32m0\033[0m]=never, [\033[32m1\033[0m]=if-client-newer, [\033[32m2\033[0m]=always (volflag=u2ow)")
ap2.add_argument("--u2sort", metavar="TXT", type=u, default="s", help="upload order; [\033[32ms\033[0m]=smallest-first, [\033[32mn\033[0m]=alphabetical, [\033[32mfs\033[0m]=force-s, [\033[32mfn\033[0m]=force-n -- alphabetical is a bit slower on fiber/LAN but makes it easier to eyeball if everything went fine") ap2.add_argument("--u2sort", metavar="TXT", type=u, default="s", help="upload order; [\033[32ms\033[0m]=smallest-first, [\033[32mn\033[0m]=alphabetical, [\033[32mfs\033[0m]=force-s, [\033[32mfn\033[0m]=force-n -- alphabetical is a bit slower on fiber/LAN but makes it easier to eyeball if everything went fine")
ap2.add_argument("--write-uplog", action="store_true", help="write POST reports to textfiles in working-directory") ap2.add_argument("--write-uplog", action="store_true", help="write POST reports to textfiles in working-directory")
@ -1633,7 +1587,7 @@ def add_wopi(ap):
ap2.add_argument("--wopi", action="store_true", help="enable integration with office suites using WOPI") ap2.add_argument("--wopi", action="store_true", help="enable integration with office suites using WOPI")
ap2.add_argument("--wopi-api", metavar="URL", type=u, default="", help="URL that the WOPI-client should use to communicate with copyparty; default is same as user's webbrowser. Example: [\033[32mhttps://party.example.com/\033[0m]") ap2.add_argument("--wopi-api", metavar="URL", type=u, default="", help="URL that the WOPI-client should use to communicate with copyparty; default is same as user's webbrowser. Example: [\033[32mhttps://party.example.com/\033[0m]")
ap2.add_argument("--wopi-url", metavar="URL", type=u, default="", help="URL to your WOPI client; the host of e.g. Collabora Online. Example: [\033[32mhttps://code.example.com/\033[0m]") ap2.add_argument("--wopi-url", metavar="URL", type=u, default="", help="URL to your WOPI client; the host of e.g. Collabora Online. Example: [\033[32mhttps://code.example.com/\033[0m]")
ap2.add_argument("--wopi-urls", metavar="H=U", type=u, action="append", help="\033[34mREPEATABLE:\033[0m maps http \033[33mH\033[0mOST copyparty being accessed by to specific WOPI client instance \033[33mU\033[0mRL; falls back to \033[33m--wopi-url\033[0m; examples: [\033[32mparty.public.com=https://office.public.com/\033[0m], [\033[32mparty.internal.net:8443=https://office.internal.net:8443/\033[0m]") ap2.add_argument("--wopi-urls", metavar="HOST=URL", type=u, action="append", help="\033[34mREPEATABLE:\033[0m maps http \033[33mHOST\033[0m copyparty being accessed by to specific WOPI client instance \033[33mURL\033[0m; falls back to \033[33m--wopi-url\033[0m; examples: [\033[32mparty.public.com=https://office.public.com/\033[0m], [\033[32mparty.internal.net:8443=https://office.internal.net:8443/\033[0m]")
ap2.add_argument("--wopi-crt", metavar="TXT", type=u, default="", help="if \033[33m--wopi-url\033[0m is selfsigned: path to ca.pem or cert.pem to expect/verify (can be [\033[32mno\033[0m] for full-yolo)") ap2.add_argument("--wopi-crt", metavar="TXT", type=u, default="", help="if \033[33m--wopi-url\033[0m is selfsigned: path to ca.pem or cert.pem to expect/verify (can be [\033[32mno\033[0m] for full-yolo)")
ap2.add_argument("--wopi-crt-icn", action="store_true", help="if \033[33m--wopi-url\033[0m is selfsigned: ignore the CN (server ip/name) in cert") ap2.add_argument("--wopi-crt-icn", action="store_true", help="if \033[33m--wopi-url\033[0m is selfsigned: ignore the CN (server ip/name) in cert")
ap2.add_argument("--wopi-ttl", metavar="SEC", type=int, default=1800, help="session lifetime; allow editing for this many seconds (default is 30 min)") ap2.add_argument("--wopi-ttl", metavar="SEC", type=int, default=1800, help="session lifetime; allow editing for this many seconds (default is 30 min)")
@ -1783,7 +1737,7 @@ def add_safety(ap):
ap2.add_argument("--use-bwrap", metavar="TXT", type=u, default="n", help=argparse.SUPPRESS) ap2.add_argument("--use-bwrap", metavar="TXT", type=u, default="n", help=argparse.SUPPRESS)
def add_salt(ap, fk_salt, dk_salt, ah_salt, wopi_salt): def add_salt(ap, fk_salt, dk_salt, ah_salt):
ap2 = ap.add_argument_group("salting options") ap2 = ap.add_argument_group("salting options")
ap2.add_argument("--ah-alg", metavar="ALG", type=u, default="none", help="account-pw hashing algorithm; one of these, best to worst: \033[32margon2 scrypt sha2 none\033[0m (each optionally followed by alg-specific comma-sep. config)") ap2.add_argument("--ah-alg", metavar="ALG", type=u, default="none", help="account-pw hashing algorithm; one of these, best to worst: \033[32margon2 scrypt sha2 none\033[0m (each optionally followed by alg-specific comma-sep. config)")
ap2.add_argument("--ah-salt", metavar="SALT", type=u, default=ah_salt, help="account-pw salt; ignored if \033[33m--ah-alg\033[0m is none (default)") ap2.add_argument("--ah-salt", metavar="SALT", type=u, default=ah_salt, help="account-pw salt; ignored if \033[33m--ah-alg\033[0m is none (default)")
@ -1792,7 +1746,6 @@ def add_salt(ap, fk_salt, dk_salt, ah_salt, wopi_salt):
ap2.add_argument("--fk-salt", metavar="SALT", type=u, default=fk_salt, help="per-file accesskey salt; used to generate unpredictable URLs for hidden files") ap2.add_argument("--fk-salt", metavar="SALT", type=u, default=fk_salt, help="per-file accesskey salt; used to generate unpredictable URLs for hidden files")
ap2.add_argument("--dk-salt", metavar="SALT", type=u, default=dk_salt, help="per-directory accesskey salt; used to generate unpredictable URLs to share folders with users who only have the 'get' permission") ap2.add_argument("--dk-salt", metavar="SALT", type=u, default=dk_salt, help="per-directory accesskey salt; used to generate unpredictable URLs to share folders with users who only have the 'get' permission")
ap2.add_argument("--warksalt", metavar="SALT", type=u, default="hunter2", help="up2k file-hash salt; serves no purpose, no reason to change this (but delete all databases if you do)") ap2.add_argument("--warksalt", metavar="SALT", type=u, default="hunter2", help="up2k file-hash salt; serves no purpose, no reason to change this (but delete all databases if you do)")
ap2.add_argument("--wopi-salt", metavar="TXT", type=u, default=wopi_salt, help="WOPI file ID salt; used by \033[33m--wopi\033[0m for persistent file IDs")
ap2.add_argument("--show-ah-salt", action="store_true", help="on startup, print the effective value of \033[33m--ah-salt\033[0m (the autogenerated value in $XDG_CONFIG_HOME unless otherwise specified)") ap2.add_argument("--show-ah-salt", action="store_true", help="on startup, print the effective value of \033[33m--ah-salt\033[0m (the autogenerated value in $XDG_CONFIG_HOME unless otherwise specified)")
ap2.add_argument("--show-fk-salt", action="store_true", help="on startup, print the effective value of \033[33m--fk-salt\033[0m (the autogenerated value in $XDG_CONFIG_HOME unless otherwise specified)") ap2.add_argument("--show-fk-salt", action="store_true", help="on startup, print the effective value of \033[33m--fk-salt\033[0m (the autogenerated value in $XDG_CONFIG_HOME unless otherwise specified)")
ap2.add_argument("--show-dk-salt", action="store_true", help="on startup, print the effective value of \033[33m--dk-salt\033[0m (the autogenerated value in $XDG_CONFIG_HOME unless otherwise specified)") ap2.add_argument("--show-dk-salt", action="store_true", help="on startup, print the effective value of \033[33m--dk-salt\033[0m (the autogenerated value in $XDG_CONFIG_HOME unless otherwise specified)")
@ -1886,19 +1839,8 @@ def add_thumbnail(ap):
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,kra,ora,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,kra,ora,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,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-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-pil-add", metavar="T", type=u, default="", help="add or remove image formats to decode using pillow listed in \033[33m--th-r-pil\033[0m; example: [\033[32mfb2,pdf,-psd,-bmp\033[0m] (add fb2 and pdf, remove psd and bmp)")
ap2.add_argument("--th-vips-add", metavar="T", type=u, default="", help="add or remove formats; same as \033[33m--th-pil-add\033[0m but for pyvips")
ap2.add_argument("--th-raw-add", metavar="T", type=u, default="", help="add or remove formats; same as \033[33m--th-pil-add\033[0m but for rawpy/libraw")
ap2.add_argument("--th-ffi-add", metavar="T", type=u, default="", help="add or remove formats; same as \033[33m--th-pil-add\033[0m but for ffmpeg images")
ap2.add_argument("--th-ffv-add", metavar="T", type=u, default="", help="add or remove formats; same as \033[33m--th-pil-add\033[0m but for ffmpeg videos")
ap2.add_argument("--th-ffa-add", metavar="T", type=u, default="", help="add or remove formats; same as \033[33m--th-pil-add\033[0m but for ffmpeg audio")
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, kra=png.kra, ora=png.ora", 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, kra=png.kra, ora=png.ora", help="audio/image formats to decompress before passing to ffmpeg")
ap2.add_argument("--th-extract", metavar="T", type=u, action="append", help="\033[34mREPEATABLE:\033[0m list of file extensions to thumbnail using a custom plugin (a python script); example: [\033[32mmdf,iso,dmg=/thumbs/diskimg.py\033[0m], see --help-thumb-ex")
ap2.add_argument("--th-extr-sz", metavar="M", type=int, default=16, help="max num megabytes to allow \033[33m--th-extract\033[0m plugins to extract from each file")
ap2.add_argument("--hot-th-extr", action="store_true", help="recompile extractors on each thumbnail extraction -- expensive but convenient when hacking on stuff")
def add_transcoding(ap): def add_transcoding(ap):
@ -2136,7 +2078,6 @@ def run_argparse(
fk_salt = get_salt("fk", 18) fk_salt = get_salt("fk", 18)
dk_salt = get_salt("dk", 30) dk_salt = get_salt("dk", 30)
ah_salt = get_salt("ah", 18) ah_salt = get_salt("ah", 18)
wopi_salt = get_salt("wopi", 18)
# alpine peaks at 5 threads for some reason, # alpine peaks at 5 threads for some reason,
# all others scale past that (but try to avoid SMT), # all others scale past that (but try to avoid SMT),
@ -2173,7 +2114,7 @@ def run_argparse(
add_opds(ap) add_opds(ap)
add_wopi(ap) add_wopi(ap)
add_safety(ap) add_safety(ap)
add_salt(ap, fk_salt, dk_salt, ah_salt, wopi_salt) add_salt(ap, fk_salt, dk_salt, ah_salt)
add_optouts(ap) add_optouts(ap)
add_shutdown(ap) add_shutdown(ap)
add_yolo(ap) add_yolo(ap)
@ -2276,9 +2217,6 @@ def main(argv: Optional[list[str]] = None) -> None:
argv.pop(n) argv.pop(n)
break break
if CFG_DEF:
CFG_DEF[0] = absreal(CFG_DEF[0])
ensure_locale() ensure_locale()
ensure_webdeps() ensure_webdeps()

View file

@ -13,7 +13,7 @@ import threading
import time import time
from datetime import datetime from datetime import datetime
from .__init__ import ANYWIN, CFG_DEF, MACOS, PY2, TYPE_CHECKING, WINDOWS, E from .__init__ import ANYWIN, MACOS, PY2, TYPE_CHECKING, WINDOWS, E
from .bos import bos from .bos import bos
from .cfg import flagdescs, permdescs, vf_bmap, vf_cmap, vf_vmap from .cfg import flagdescs, permdescs, vf_bmap, vf_cmap, vf_vmap
from .pwhash import PWHash from .pwhash import PWHash
@ -25,7 +25,6 @@ from .util import (
FN_EMB, FN_EMB,
HAVE_SQLITE3, HAVE_SQLITE3,
IMPLICATIONS, IMPLICATIONS,
LOG,
META_NOBOTS, META_NOBOTS,
MIMES, MIMES,
SQLITE_VER, SQLITE_VER,
@ -2379,7 +2378,6 @@ class AuthSrv(object):
vol.flags["dvthumb"] = True vol.flags["dvthumb"] = True
vol.flags["dathumb"] = True vol.flags["dathumb"] = True
vol.flags["dithumb"] = True vol.flags["dithumb"] = True
vol.flags["dethumb"] = True
have_fk = False have_fk = False
for vol in vfs.all_nodes.values(): for vol in vfs.all_nodes.values():
@ -4081,11 +4079,6 @@ def expand_config_file(
if fp2 in ipath: if fp2 in ipath:
continue continue
if CFG_DEF and CFG_DEF[0] == fp2:
t = "ignoring PRTY_CONFIG because it was also autodiscovered by %s -> %s"
LOG[0]("root", t % (ipath, fp))
continue
expand_config_file(log, shenvexp, ret, fp2, ipath) expand_config_file(log, shenvexp, ret, fp2, ipath)
return return

View file

@ -316,7 +316,6 @@ flagcats = {
"dvthumb": "disables video thumbnails", "dvthumb": "disables video thumbnails",
"dathumb": "disables audio thumbnails (spectrograms)", "dathumb": "disables audio thumbnails (spectrograms)",
"dithumb": "disables image thumbnails", "dithumb": "disables image thumbnails",
"dethumb": "disables custom thumbnails (--th-extract)",
"pngquant": "compress audio waveforms 33% better", "pngquant": "compress audio waveforms 33% better",
"thsize": "thumbnail res; WxH", "thsize": "thumbnail res; WxH",
"crop": "center-cropping (y/n/fy/fn)", "crop": "center-cropping (y/n/fy/fn)",

View file

@ -1649,8 +1649,7 @@ class HttpCli(object):
raise Pebkac(500, "too many wopi sessions") raise Pebkac(500, "too many wopi sessions")
if not found: if not found:
atoken = ub64enc(os.urandom(18)).decode("ascii") # 18 = 144b = 24c atoken = ub64enc(os.urandom(18)).decode("ascii") # 18 = 144b = 24c
zb = (vpath + self.args.wopi_salt).encode("utf-8", "replace") file_id = ub64enc(os.urandom(15)).decode("ascii") # 15 = 120b = 20c
file_id = ub64enc(hashlib.sha512(zb).digest()[:15]).decode("ascii")
wopi_files[atoken] = session = { wopi_files[atoken] = session = {
"vp": vpath, "vp": vpath,
"uname": self.uname, "uname": self.uname,
@ -1658,7 +1657,7 @@ class HttpCli(object):
"expires": time.time() + self.args.wopi_ttl, "expires": time.time() + self.args.wopi_ttl,
} }
xml = url = "?" xml = "?"
try: try:
from .dxml import parse_xml from .dxml import parse_xml
@ -1696,8 +1695,7 @@ class HttpCli(object):
url += quotep(zs + "/wopi/files/" + session["file_id"]) url += quotep(zs + "/wopi/files/" + session["file_id"])
except: except:
del wopi_files[atoken] # dont reuse an atoken wopi-client doesnt like del wopi_files[atoken] # dont reuse an atoken wopi-client doesnt like
t = "reading WOPI-client response from %s failed; %s\n%s" self.log("reading WOPI-client response failed; %s\n%s" % (min_ex(), xml), 3)
self.log(t % (url, min_ex(), xml), 3)
raise Pebkac(500, "wopi error (see fileserver log)") raise Pebkac(500, "wopi error (see fileserver log)")
html = self.j2s( html = self.j2s(
@ -1705,7 +1703,7 @@ class HttpCli(object):
title=self.uparam["wopi"], title=self.uparam["wopi"],
url=url, url=url,
atoken=atoken, atoken=atoken,
ttl=int(session["expires"] * 1000), ttl=session["expires"],
).encode("utf-8", "replace") ).encode("utf-8", "replace")
self.reply(html, 200, "text/html; charset=utf-8") self.reply(html, 200, "text/html; charset=utf-8")
@ -7447,18 +7445,20 @@ class HttpCli(object):
dirs = [] dirs = []
files = [] files = []
ptn_hr = RE_HR ptn_hr = RE_HR
use_abs_url = is_opds or (
base = "" vpath and not is_ls and not is_js and not self.trailing_slash
if is_opds or (vpath and not (is_ls or is_js or self.trailing_slash)): )
if is_opds:
base = self.args.SRS
if vpath:
base += vpath + "/"
else:
base = "/" + vpath + "/"
for fn in ls_names: for fn in ls_names:
href = base + fn base = ""
href = fn
if use_abs_url:
if is_opds:
base = self.args.SRS
if vpath:
base += vpath + "/"
else:
base = "/" + vpath + "/"
href = base + fn
if fn in vfs_virt: if fn in vfs_virt:
fspath = vfs_virt[fn].realpath fspath = vfs_virt[fn].realpath
@ -7893,10 +7893,6 @@ class HttpCli(object):
ext in self.thumbcli.fmt_pil ext in self.thumbcli.fmt_pil
or ext in self.thumbcli.fmt_vips or ext in self.thumbcli.fmt_vips
or ext in self.thumbcli.fmt_ffi or ext in self.thumbcli.fmt_ffi
or (
ext in self.thumbcli.thumbable
and ext not in self.thumbcli.thumbable_native
)
) )
is_vid = ext in self.thumbcli.fmt_ffv is_vid = ext in self.thumbcli.fmt_ffv
is_au = ext in self.thumbcli.fmt_ffa is_au = ext in self.thumbcli.fmt_ffa

View file

@ -133,19 +133,6 @@ VER_SHARES_DB = 2
CVE_SEVS = {"low": 1, "medium": 2, "moderate": 2, "high": 3, "critical": 4} CVE_SEVS = {"low": 1, "medium": 2, "moderate": 2, "high": 3, "critical": 4}
def _build_th_fset(f: str, fadd: str) -> set[str]:
fset = {x for x in f.replace(" ", "").split(",")}
rmset = {""}
for x in fadd.replace(" ", "").split(","):
if x.startswith("-"):
rmset.add(x.lstrip("-"))
else:
fset.add(x)
return fset - rmset
class SvcHub(object): class SvcHub(object):
""" """
Hosts all services which cannot be parallelized due to reliance on monolithic resources. Hosts all services which cannot be parallelized due to reliance on monolithic resources.
@ -454,20 +441,6 @@ class SvcHub(object):
zlss = [x.strip().lower().split("=", 1) for x in args.au_unpk.split(",")] zlss = [x.strip().lower().split("=", 1) for x in args.au_unpk.split(",")]
args.au_unpk = {x[0]: x[1] for x in zlss} args.au_unpk = {x[0]: x[1] for x in zlss}
args.th_r_pil = _build_th_fset(args.th_r_pil, args.th_pil_add)
args.th_r_vips = _build_th_fset(args.th_r_vips, args.th_vips_add)
args.th_r_raw = _build_th_fset(args.th_r_raw, args.th_raw_add)
args.th_r_ffi = _build_th_fset(args.th_r_ffi, args.th_ffi_add)
args.th_r_ffv = _build_th_fset(args.th_r_ffv, args.th_ffv_add)
args.th_r_ffa = _build_th_fset(args.th_r_ffa, args.th_ffa_add)
th_extract = args.th_extract or []
args.th_extract = {}
for arg in th_extract:
exts, script = arg.split("=", 1)
for ext in exts.split(","):
args.th_extract[ext.strip().lower()] = script
self.args.th_dec = list(decs.keys()) self.args.th_dec = list(decs.keys())
self.thumbsrv = None self.thumbsrv = None
want_ff = False want_ff = False
@ -505,7 +478,7 @@ class SvcHub(object):
t = "invalid mp3 transcoding quality [%s] specified; only supports [0] to disable, a CBR value such as [192k], or a CQ/CRF value such as [v2]" t = "invalid mp3 transcoding quality [%s] specified; only supports [0] to disable, a CBR value such as [192k], or a CQ/CRF value such as [v2]"
raise Exception(t % (args.q_mp3,)) raise Exception(t % (args.q_mp3,))
else: else:
zss = args.th_r_ffa | args.th_r_ffv zss = set(args.th_r_ffa.split(",") + args.th_r_ffv.split(","))
args.au_unpk = { args.au_unpk = {
k: v for k, v in args.au_unpk.items() if v.split(".")[0] not in zss k: v for k, v in args.au_unpk.items() if v.split(".")[0] not in zss
} }
@ -1154,10 +1127,6 @@ class SvcHub(object):
t = "not listening on any ip-addresses (only unix-sockets and/or FDs); cannot enable zeroconf/mdns/ssdp as requested" t = "not listening on any ip-addresses (only unix-sockets and/or FDs); cannot enable zeroconf/mdns/ssdp as requested"
self.log("root", t, 3) self.log("root", t, 3)
if al.wopi and not al.wopi_url and not al.wopi_urls:
self.log("root", "disabling wopi; need either wopi-url or wopi-urls", 1)
al.wopi = False
if self.args.wopi or not self.args.no_dav: if self.args.wopi or not self.args.no_dav:
from .dxml import DXML_OK from .dxml import DXML_OK
@ -1414,20 +1383,18 @@ class SvcHub(object):
self.args.name_html = zs self.args.name_html = zs
zs = al.u2sz zs = al.u2sz
if zs in ("no", "1,1,1"):
zs = "0"
zsl = [x.strip() for x in zs.split(",")] zsl = [x.strip() for x in zs.split(",")]
if len(zsl) not in (1, 2): if len(zsl) not in (1, 3):
t = "invalid --u2sz; must be either one number, or a comma-separated list of two numbers (target,max)" t = "invalid --u2sz; must be either one number, or a comma-separated list of three numbers (min,default,max)"
raise Exception(t) raise Exception(t)
if len(zsl) < 2: if len(zsl) < 3:
zsl = [zs, zs] zsl = ["1", zs, zs]
zi2 = 0 zi2 = 1
for zs in zsl: for zs in zsl:
zi = int(zs) zi = int(zs)
# arbitrary constraint (anything above 2 GiB is probably unintended) # arbitrary constraint (anything above 2 GiB is probably unintended)
if zi < 0 or zi > 2047: if zi < 1 or zi > 2047:
raise Exception("invalid --u2sz; minimum is 0, max is 2047") raise Exception("invalid --u2sz; minimum is 1, max is 2047")
if zi < zi2: if zi < zi2:
raise Exception("invalid --u2sz; values must be equal or ascending") raise Exception("invalid --u2sz; values must be equal or ascending")
zi2 = zi zi2 = zi

View file

@ -34,7 +34,6 @@ class ThumbCli(object):
self.cooldown = Cooldown(self.args.th_poke) if self.args.th_poke else None self.cooldown = Cooldown(self.args.th_poke) if self.args.th_poke else None
self.thumbable = c["thumbable"] self.thumbable = c["thumbable"]
self.thumbable_native = c["thumbable_native"]
self.fmt_pil = c["pil"] self.fmt_pil = c["pil"]
self.fmt_vips = c["vips"] self.fmt_vips = c["vips"]
self.fmt_raw = c["raw"] self.fmt_raw = c["raw"]
@ -60,10 +59,6 @@ class ThumbCli(object):
if is_vid and "dvthumb" in dbv.flags: if is_vid and "dvthumb" in dbv.flags:
return None return None
is_custom = ext not in self.thumbable_native
if is_custom and "dethumb" in dbv.flags:
return None
want_opus = fmt in EXTS_AC want_opus = fmt in EXTS_AC
is_au = ext in self.fmt_ffa is_au = ext in self.fmt_ffa
is_vau = want_opus and ext in self.fmt_ffv is_vau = want_opus and ext in self.fmt_ffv
@ -81,7 +76,7 @@ class ThumbCli(object):
elif want_opus: elif want_opus:
return None return None
is_img = not is_vid and not is_au and not is_custom is_img = not is_vid and not is_au
if is_img and "dithumb" in dbv.flags: if is_img and "dithumb" in dbv.flags:
return None return None

View file

@ -38,7 +38,6 @@ from .util import (
afsenc, afsenc,
atomic_move, atomic_move,
fsenc, fsenc,
loadpy,
min_ex, min_ex,
runcmd, runcmd,
statdir, statdir,
@ -324,12 +323,24 @@ class ThumbSrv(object):
if self.args.th_clean: if self.args.th_clean:
Daemon(self.cleaner, "thumb.cln") Daemon(self.cleaner, "thumb.cln")
self.fmt_pil = self.args.th_r_pil (
self.fmt_vips = self.args.th_r_vips self.fmt_pil,
self.fmt_raw = self.args.th_r_raw self.fmt_vips,
self.fmt_ffi = self.args.th_r_ffi self.fmt_raw,
self.fmt_ffv = self.args.th_r_ffv self.fmt_ffi,
self.fmt_ffa = self.args.th_r_ffa self.fmt_ffv,
self.fmt_ffa,
) = [
set(y.split(","))
for y in [
self.args.th_r_pil,
self.args.th_r_vips,
self.args.th_r_raw,
self.args.th_r_ffi,
self.args.th_r_ffv,
self.args.th_r_ffa,
]
]
if not H_PIL_HEIF: if not H_PIL_HEIF:
for f in "heif heifs heic heics".split(" "): for f in "heif heifs heic heics".split(" "):
@ -348,26 +359,23 @@ class ThumbSrv(object):
self.fmt_pil.discard(f) self.fmt_pil.discard(f)
self.thumbable: set[str] = set() self.thumbable: set[str] = set()
self.thumbable_native: set[str] = set()
self._build_thumbable() self._build_thumbable()
def _build_thumbable(self) -> None: def _build_thumbable(self) -> None:
self.thumbable_native.clear() self.thumbable.clear()
if "pil" in self.args.th_dec: if "pil" in self.args.th_dec:
self.thumbable_native |= self.fmt_pil self.thumbable |= self.fmt_pil
if "vips" in self.args.th_dec: if "vips" in self.args.th_dec:
self.thumbable_native |= self.fmt_vips self.thumbable |= self.fmt_vips
if "raw" in self.args.th_dec: if "raw" in self.args.th_dec:
self.thumbable_native |= self.fmt_raw self.thumbable |= self.fmt_raw
if "ff" in self.args.th_dec: if "ff" in self.args.th_dec:
for zss in [self.fmt_ffi, self.fmt_ffv, self.fmt_ffa]: for zss in [self.fmt_ffi, self.fmt_ffv, self.fmt_ffa]:
self.thumbable_native |= zss self.thumbable |= zss
self.thumbable = self.thumbable_native | set(self.args.th_extract)
def _log(self, msg: str, c: Union[int, str] = 0) -> None: def _log(self, msg: str, c: Union[int, str] = 0) -> None:
self.log_func("thumb", msg, c) self.log_func("thumb", msg, c)
@ -459,7 +467,6 @@ class ThumbSrv(object):
def getcfg(self) -> dict[str, set[str]]: def getcfg(self) -> dict[str, set[str]]:
return { return {
"thumbable": self.thumbable, "thumbable": self.thumbable,
"thumbable_native": self.thumbable_native,
"pil": self.fmt_pil, "pil": self.fmt_pil,
"vips": self.fmt_vips, "vips": self.fmt_vips,
"raw": self.fmt_raw, "raw": self.fmt_raw,
@ -542,36 +549,19 @@ class ThumbSrv(object):
png_ok = False png_ok = False
funs = [] funs = []
tex = tpath.rsplit(".", 1)[-1]
want_mp3 = tex == "mp3"
want_opus = tex in ("opus", "owa", "caf")
want_flac = tex == "flac"
want_wav = tex == "wav"
want_png = tex == "png"
want_au = want_mp3 or want_opus or want_flac or want_wav
ap_extr = abspath
if (
ext in self.args.th_extract
and not want_au
and not want_png
and "dethumb" not in vn.flags
):
ap_extr = self.run_extractor(self.args.th_extract[ext], abspath, vn)
if ap_extr:
ext = ap_extr.rsplit(".", 1)[-1]
if ext in self.args.au_unpk: if ext in self.args.au_unpk:
ap_unpk = au_unpk(self.log, self.args.au_unpk, ap_extr, vn) ap_unpk = au_unpk(self.log, self.args.au_unpk, abspath, vn)
elif ext in self.thumbable_native:
ap_unpk = ap_extr
else: else:
ap_unpk = "" ap_unpk = abspath
if ap_extr and ap_extr != ap_unpk and ap_extr != abspath:
wunlink(self.log, ap_extr, vn.flags)
if ap_unpk and not bos.path.exists(tpath): if ap_unpk and not bos.path.exists(tpath):
tex = tpath.rsplit(".", 1)[-1]
want_mp3 = tex == "mp3"
want_opus = tex in ("opus", "owa", "caf")
want_flac = tex == "flac"
want_wav = tex == "wav"
want_png = tex == "png"
want_au = want_mp3 or want_opus or want_flac or want_wav
for lib in self.args.th_dec: for lib in self.args.th_dec:
can_au = lib == "ff" and ( can_au = lib == "ff" and (
ext in self.fmt_ffa or ext in self.fmt_ffv ext in self.fmt_ffa or ext in self.fmt_ffv
@ -679,66 +669,6 @@ class ThumbSrv(object):
with self.mutex: with self.mutex:
self.nthr -= 1 self.nthr -= 1
def run_extractor(self, extr: str, abspath: str, vn: VFS) -> str:
try:
mod = loadpy(extr, self.args.hot_th_extr)
except Exception as ex:
self.log("extractor import failed; " + min_ex(), 1)
return ""
fd = 0
ret = ""
stream = None
try:
res = mod.main(abspath, vn=vn, th_srv=self)
if not res:
t = "extractor %r returned no data for file %r"
self.log(t % (extr, abspath))
return ""
outext, stream, offset, whence, size = res
if (
outext not in self.thumbable_native
or outext in self.fmt_ffa
or outext in self.fmt_ffv
):
t = "extractor %r returned unsupported format %r"
self.log(t % (extr, outext))
return ""
remains = size if size is not None and size >= 0 else -1
stream.seek(offset, whence)
fd, ret = tempfile.mkstemp("." + outext)
bufsz = rsz = self.args.iobuf
maxsz = self.args.th_extr_sz * 1048576
outsz = 0
with os.fdopen(fd, "wb", rsz) as f:
fd = 0
while True:
if remains < rsz and remains >= 0:
bufsz = remains
buf = stream.read(bufsz)
if not buf:
break
remains -= len(buf)
outsz += len(buf)
if outsz >= maxsz:
raise Exception("too large")
f.write(buf)
return ret
except Exception as e:
if fd:
os.close(fd)
if ret:
wunlink(self.log, ret, vn.flags)
t = "failed to extract thumbnail from %r: %s"
self.log(t % (abspath, min_ex()))
return ""
finally:
if stream:
stream.close()
def fancy_pillow(self, im: "Image.Image", fmt: str, vn: VFS) -> "Image.Image": def fancy_pillow(self, im: "Image.Image", fmt: str, vn: VFS) -> "Image.Image":
# exif_transpose is expensive (loads full image + unconditional copy) # exif_transpose is expensive (loads full image + unconditional copy)
res = self.getres(vn, fmt) res = self.getres(vn, fmt)

View file

@ -758,7 +758,7 @@ if (window.glang && navigator.languages && !/\bcplng=/.test(document.cookie))
if (window.stop) if (window.stop)
window.stop(); window.stop();
document.body.innerHTML = 'Loading ' + n; document.body.innerHTML = 'Loading ' + n;
setck("cplng=" + n, relod); setck("cplng=" + n, location.reload.bind(location));
crashed = true; crashed = true;
throw 1; throw 1;
} }
@ -1246,8 +1246,8 @@ check_image_support('jxl', "data:image/jxl;base64,/woIAAAMABKIAgC4AF3lEgA=");
var img_re = APPLE ? var img_re = APPLE ?
/\.(a?png|avif|bmp|gif|hei[cf]s?|jpe?g|jfif|svg|webp|webm|mkv|mp4|m4v|mov|svg)(\?|$)/i : /\.(a?png|avif|bmp|gif|hei[cf]s?|jpe?g|jfif|svg|webp|webm|mkv|mp4|m4v|mov)(\?|$)/i :
/\.(a?png|avif|bmp|gif|jpe?g|jfif|svg|webp|webm|mkv|mp4|m4v|mov|svg)(\?|$)/i; /\.(a?png|avif|bmp|gif|jpe?g|jfif|svg|webp|webm|mkv|mp4|m4v|mov)(\?|$)/i;
var wopi_set = !window.have_wopi ? null : var wopi_set = !window.have_wopi ? null :
new Set('odt fodt ott doc docx dotx rtf odm ods fods ots xls xlsx odp fodp otp ppt pptx ppsx odg fodg otg odf'.split(' ')); new Set('odt fodt ott doc docx dotx rtf odm ods fods ots xls xlsx odp fodp otp ppt pptx ppsx odg fodg otg odf'.split(' '));
@ -5694,8 +5694,8 @@ var thegrid = (function () {
fid = oth.getAttribute('id'), fid = oth.getAttribute('id'),
aplay = ebi('a' + fid), aplay = ebi('a' + fid),
atext = ebi('t' + fid), atext = ebi('t' + fid),
is_txt = atext && !/\.ts$/.test(href) && showfile.getlang(href),
is_img = img_re.test(href), is_img = img_re.test(href),
is_txt = atext && !is_img && !/\.ts$/.test(href) && showfile.getlang(href),
is_dir = href.endsWith('/'), is_dir = href.endsWith('/'),
is_srch = !!ebi('unsearch'), is_srch = !!ebi('unsearch'),
in_tree = is_dir && treectl.find(oth.textContent.slice(0, -1)), in_tree = is_dir && treectl.find(oth.textContent.slice(0, -1)),
@ -7696,7 +7696,7 @@ var treectl = (function () {
if (wopi_set && wopi_set.has(tn.ext)) if (wopi_set && wopi_set.has(tn.ext))
tn.lead = '<a href="?wopi=' + bhref + tn.lead = '<a href="?wopi=' + bhref +
'" rel="nofollow" target="_blank" name="' + hname + '">📄</a>'; '" rel="nofollow" name="' + hname + '">📄</a>';
if (tn.lead == '-') if (tn.lead == '-')
tn.lead = '<a href="?doc=' + bhref + '" id="t' + id + tn.lead = '<a href="?doc=' + bhref + '" id="t' + id +
@ -8626,7 +8626,7 @@ var setfszf = (function () {
setck('cplng=' + lang); setck('cplng=' + lang);
freshen(); freshen();
var t = L.tt == 'English' ? '' : Ls.eng.lang_set; var t = L.tt == 'English' ? '' : Ls.eng.lang_set;
modal.confirm(L.lang_set + "\n\n" + t, relod, null); modal.confirm(L.lang_set + "\n\n" + t, location.reload.bind(location), null);
} }
freshen(); freshen();

View file

@ -325,9 +325,6 @@ var modpoll = new Modpoll();
window.onbeforeunload = function (e) { window.onbeforeunload = function (e) {
if (window.gtfo)
return;
if ((ebi("save").className + '').indexOf('disabled') >= 0) if ((ebi("save").className + '').indexOf('disabled') >= 0)
return; //nice (todo) return; //nice (todo)

View file

@ -888,8 +888,9 @@ function up2k_init(subtle) {
setmsg(suggest_up2k, 'msg'); setmsg(suggest_up2k, 'msg');
var u2szs = u2sz.split(','), var u2szs = u2sz.split(','),
u2sz_tgt = parseInt(u2szs[0]), u2sz_min = parseInt(u2szs[0]),
u2sz_max = parseInt(u2szs[1]); u2sz_tgt = parseInt(u2szs[1]),
u2sz_max = parseInt(u2szs[2]);
var parallel_uploads = ebi('nthread').value = icfg_get('nthread', u2j), var parallel_uploads = ebi('nthread').value = icfg_get('nthread', u2j),
stitch_tgt = ebi('u2szg').value = icfg_get('u2sz', u2sz_tgt), stitch_tgt = ebi('u2szg').value = icfg_get('u2sz', u2sz_tgt),
@ -2676,7 +2677,7 @@ function up2k_init(subtle) {
ccs += stp; stp *= 2; ccs += stp; stp *= 2;
} }
ocs = Math.floor(ocs / 1024 / 1024); ocs = Math.floor(ocs / 1024 / 1024);
t.stitch_sz = Math.min(ocs, stitch_tgt || 1); t.stitch_sz = Math.min(ocs, stitch_tgt);
} }
for (var a = 0; a < t.postlist.length; a++) { for (var a = 0; a < t.postlist.length; a++) {
@ -2878,8 +2879,7 @@ function up2k_init(subtle) {
nparts = upt.nparts, nparts = upt.nparts,
pcar = nparts[0], pcar = nparts[0],
pcdr = nparts[nparts.length - 1], pcdr = nparts[nparts.length - 1],
maxmsz = stitch_tgt == u2sz_tgt ? u2sz_max : stitch_tgt, maxsz = (u2sz_max > 1 ? u2sz_max : 2040) * 1024 * 1024;
maxsz = (maxmsz > 0 ? maxmsz : 2040) * 1024 * 1024;
if (t.done) if (t.done)
return console.log('done; skip chunk', t.name, t); return console.log('done; skip chunk', t.name, t);
@ -2951,9 +2951,6 @@ function up2k_init(subtle) {
st.etac.u++; st.etac.u++;
st.etac.t++; st.etac.t++;
} }
else if (xhr.status == 413) {
modal.die('bad server config; see "u2sz" in readme');
}
else if (txt.indexOf('already got that') + 1) { else if (txt.indexOf('already got that') + 1) {
unqueue_up(t); unqueue_up(t);
} }
@ -3166,7 +3163,7 @@ function up2k_init(subtle) {
var el = ebi('u2szg'), n = parseInt(el.value); var el = ebi('u2szg'), n = parseInt(el.value);
stitch_tgt = n = ( stitch_tgt = n = (
isNaN(n) ? u2sz_tgt : isNaN(n) ? u2sz_tgt :
n < 0 ? 0 : n < u2sz_min ? u2sz_min :
n > u2sz_max ? u2sz_max : n n > u2sz_max ? u2sz_max : n
); );
if (n == u2sz_tgt) sdrop('u2sz'); else swrite('u2sz', n); if (n == u2sz_tgt) sdrop('u2sz'); else swrite('u2sz', n);
@ -3403,7 +3400,7 @@ function up2k_init(subtle) {
function set_nosubtle(v) { function set_nosubtle(v) {
if (!WebAssembly) if (!WebAssembly)
return toast.err(10, L.u_nowork); return toast.err(10, L.u_nowork);
modal.confirm(L.lang_set, relod, null); modal.confirm(L.lang_set, location.reload.bind(location), null);
} }
function set_upnag(en) { function set_upnag(en) {
@ -3474,9 +3471,6 @@ function up2k_init(subtle) {
function warn_uploader_busy(e) { function warn_uploader_busy(e) {
if (window.gtfo)
return;
e.preventDefault(); e.preventDefault();
e.returnValue = ''; e.returnValue = '';
return "upload in progress, click abort and use the file-tree to navigate instead"; return "upload in progress, click abort and use the file-tree to navigate instead";

View file

@ -108,13 +108,6 @@ function qsr(sel) {
} }
function relod() {
crashed = 1;
window.gtfo = 1;
location.href = location.href;
}
// error handler for mobile devices // error handler for mobile devices
function esc(txt) { function esc(txt) {
return txt.replace(/[&"<>]/g, function (c) { return txt.replace(/[&"<>]/g, function (c) {
@ -296,7 +289,7 @@ function vis_exh(msg, url, lineNo, columnNo, error) {
var x = ebi('exh_wipecfg'); var x = ebi('exh_wipecfg');
if (x) x.onclick = function () { if (x) x.onclick = function () {
localStorage.clear(); localStorage.clear();
relod(); location.reload();
}; };
x = ebi('exh_ignex'); if (x) x.onclick = ignex; x = ebi('exh_ignex'); if (x) x.onclick = ignex;
x = ebi('exh_ignexa'); if (x) x.onclick = ignexa; x = ebi('exh_ignexa'); if (x) x.onclick = ignexa;
@ -2028,12 +2021,6 @@ var modal = (function () {
q.shift()(); q.shift()();
} }
r.die = function (html) {
crashed = 1;
if (r.busy)
r.hide();
_alert(lf2br(html), relod);
};
r.alert = function (html, cb, fun) { r.alert = function (html, cb, fun) {
q.push(function () { q.push(function () {
_alert(lf2br(html), cb, fun); _alert(lf2br(html), cb, fun);

View file

@ -27,7 +27,7 @@ body {
<div style="display: none"> <div style="display: none">
<form action="{{ url }}" enctype="multipart/form-data" method="post" target="w" id="submit-form"> <form action="{{ url }}" enctype="multipart/form-data" method="post" target="w" id="submit-form">
<input name="access_token" value="{{ atoken }}" type="hidden" id="access-token"/> <input name="access_token" value="{{ atoken }}" type="hidden" id="access-token"/>
<input name="access_token_ttl" value="{{ ttl }}" type="hidden" id="access-token_ttl"/> <input name="access_token_ttl" value="{{ ttl }}000" type="hidden" id="access-token_ttl"/>
<input type="submit" value="" /> <input type="submit" value="" />
</form> </form>
</div> </div>

View file

@ -15,7 +15,7 @@ command -v gsort >/dev/null && {
cd ../dist cd ../dist
kwds='-bind -accounts -auth -auth-ord -flags -thumb-ex -handlers -hooks -idp -urlform -exp -rlo -ls -dbd -chmod -pwhash -zm' kwds='-bind -accounts -auth -auth-ord -flags -handlers -hooks -idp -urlform -exp -rlo -ls -dbd -chmod -pwhash -zm'
export PRTY_FULL_HELP=1 export PRTY_FULL_HELP=1
export PRTY_NO_PARAMIKO=1 export PRTY_NO_PARAMIKO=1

View file

@ -74,7 +74,7 @@ def cnv(src):
continue continue
if '">uuid:' in ln: if '">uuid:' in ln:
ln = re.sub(r">uuid:[0-9a-f-]{36}<", ">autogenerated<", ln) ln = re.sub(r">uuid:[0-9a-f-]{36}<", ">autogenerated<", ln)
if "-salt " in ln and ("-salt SALT" in ln or "-salt TXT" in ln): if "-salt SALT" in ln:
in_salt = 3 in_salt = 3
if in_salt: if in_salt:
in_salt -= 1 in_salt -= 1

View file

@ -10,8 +10,8 @@ command -v gsort >/dev/null && {
( ( HLPTXT=CAT && cat || xsel -ob ) | sed -r ' ( ( HLPTXT=CAT && cat || xsel -ob ) | sed -r '
s`/home/ed/`~/`; s`/home/ed/`~/`;
s/uuid:[0-9a-f-]{36}/autogenerated/; s/uuid:[0-9a-f-]{36}/autogenerated/;
s/(-salt (SALT|TXT).*default: )[0-9a-zA-Z/+]{24}\)/\124-character-autogenerated)/; s/(-salt SALT.*default: )[0-9a-zA-Z/+]{24}\)/\124-character-autogenerated)/;
s/(-salt (SALT|TXT).*default: )[0-9a-zA-Z/+]{40}\)/\140-character-autogenerated)/; s/(-salt SALT.*default: )[0-9a-zA-Z/+]{40}\)/\140-character-autogenerated)/;
s/(--name TXT.*default: )[^)]+/\1hostname/; s/(--name TXT.*default: )[^)]+/\1hostname/;
s/(--hash-mt CORES.*default: )[0-9]+/\1numCores if 5 or less/; s/(--hash-mt CORES.*default: )[0-9]+/\1numCores if 5 or less/;
s/(--mtag-mt|th-mt)( CORES.*default: )[0-9]+/\1\2numCores/; s/(--mtag-mt|th-mt)( CORES.*default: )[0-9]+/\1\2numCores/;

View file

@ -16,7 +16,7 @@ uname -s | grep WOW64 && m=64 || m=32
uname -s | grep NT-10 && w10=1 || w7=1 uname -s | grep NT-10 && w10=1 || w7=1
[ $w7 ] && [ -e up2k.sh ] && [ ! "$1" ] && ./up2k.sh [ $w7 ] && [ -e up2k.sh ] && [ ! "$1" ] && ./up2k.sh
[ $w7 ] && pyv=37 || pyv=314 [ $w7 ] && pyv=37 || pyv=313
[ $w7 ] && sfx=en || sfx=sfx [ $w7 ] && sfx=en || sfx=sfx
esuf= esuf=
[ $w7 ] && [ $m = 32 ] && esuf=32 [ $w7 ] && [ $m = 32 ] && esuf=32
@ -100,14 +100,9 @@ excl=(
) )
[ $w10 ] && excl+=( [ $w10 ] && excl+=(
_pyrepl _pyrepl
bz2 # 51k
compression.zstd # 195k
concurrent.interpreters # 12k
distutils distutils
setuptools setuptools
ftplib # 23k
PIL._avif PIL._avif
PIL._imagingft # 861k (freetype)
PIL.ImageQt PIL.ImageQt
PIL.ImageShow PIL.ImageShow
PIL.ImageTk PIL.ImageTk

View file

@ -1,7 +1,7 @@
76e42806c52319081201d9fd9101827d36d9dbdd34e04f8a48736f2e3b37d68e79e8f52e43260d3eaaa46fe149d95b54ef1355af24361d0bf190dced93cd77bb altgraph-0.17.5-py2.py3-none-any.whl f117016b1e6a7d7e745db30d3e67f1acf7957c443a0dd301b6c5e10b8368f2aa4db6be9782d2d3f84beadd139bfeef4982e40f21ca5d9065cb794eeb0e473e82 altgraph-0.17.4-py2.py3-none-any.whl
17ce52ba50692a9d964f57a23ac163fb74c77fdeb2ca988a6d439ae1fe91955ff43730c073af97a7b3223093ffea3479a996b9b50ee7fba0869247a56f74baa6 pefile-2023.2.7-py3-none-any.whl 17ce52ba50692a9d964f57a23ac163fb74c77fdeb2ca988a6d439ae1fe91955ff43730c073af97a7b3223093ffea3479a996b9b50ee7fba0869247a56f74baa6 pefile-2023.2.7-py3-none-any.whl
b297ff66ec50cf5a1abcf07d6ac949644c5150ba094ffac974c5d27c81574c3e97ed814a47547f4b03a4c83ea0fb8f026433fca06a3f08e32742dc5c024f3d07 pywin32_ctypes-0.2.3-py3-none-any.whl b297ff66ec50cf5a1abcf07d6ac949644c5150ba094ffac974c5d27c81574c3e97ed814a47547f4b03a4c83ea0fb8f026433fca06a3f08e32742dc5c024f3d07 pywin32_ctypes-0.2.3-py3-none-any.whl
76724b04d6fdfadb46f27b6fd5753429d597642d1035ff7bf52fe1b6f5bc18f129567b738a40436adf03afd3a38708dc268de3c1b5d4ef9a89968335805ead72 upx-5.2.0-win32.zip fd88d00c7231f9fcbe0fb9037217b6ae5dcb5b06a98be56243400542671a32f003d594b2e1852f77d316e9327ad4c643321e1f9f3ea20cc8500ac5bba8ae170e upx-5.0.2-win32.zip
# win7 # win7
d9d30ff960365307833425e340bf0a642fd54e35155f18ff1d9746bc1ecf8ecb864aad24948735e8fd994503b659dc8968e6ba337eeb4568217ebdf97dd292dd Git-2.46.2-32-bit.exe d9d30ff960365307833425e340bf0a642fd54e35155f18ff1d9746bc1ecf8ecb864aad24948735e8fd994503b659dc8968e6ba337eeb4568217ebdf97dd292dd Git-2.46.2-32-bit.exe
3253e86471e6f9fa85bfdb7684cd2f964ed6e35c6a4db87f81cca157c049bef43e66dfcae1e037b2fb904567b1e028aaeefe8983ba3255105df787406d2aa71e en_windows_7_professional_with_sp1_x86_dvd_u_677056.iso 3253e86471e6f9fa85bfdb7684cd2f964ed6e35c6a4db87f81cca157c049bef43e66dfcae1e037b2fb904567b1e028aaeefe8983ba3255105df787406d2aa71e en_windows_7_professional_with_sp1_x86_dvd_u_677056.iso
@ -21,15 +21,15 @@ ea73aa54cc6d5db20dfb127e54562dabf890e4cd6171a91b10a51af2bcfc76e1d64cbdce4546df2d
479a63e14586ab2f2228208116fc149ed8ee7b1e4ff360754f5bda4bf765c61af2e04b5ef123976623d04df4976b7886e0445647269da81436bd0a7b5671d361 windows6.1-kb2533623-x86.msu 479a63e14586ab2f2228208116fc149ed8ee7b1e4ff360754f5bda4bf765c61af2e04b5ef123976623d04df4976b7886e0445647269da81436bd0a7b5671d361 windows6.1-kb2533623-x86.msu
ac96786e5d35882e0c5b724794329c9125c2b86ae7847f17acfc49f0d294312c6afc1c3f248655de3f0ccb4ca426d7957d02ba702f4a15e9fcd7e2c314e72c19 zipp-3.15.0-py3-none-any.whl ac96786e5d35882e0c5b724794329c9125c2b86ae7847f17acfc49f0d294312c6afc1c3f248655de3f0ccb4ca426d7957d02ba702f4a15e9fcd7e2c314e72c19 zipp-3.15.0-py3-none-any.whl
# win10 # win10
ef723f34ce8c0c1f8ef9eb7746f77e4ab6129aa7c7dcf501bee56f522ff2c57ff013f9a69db141ad04c9d1b90b4f1bbd22b6028218e3ae68eb4efec861d63f65 Git-2.55.0.3-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
4f13070cc9241fa342deab4ebfac360565030580ff77b6e5f1951a64627621e5da4abfd30e1e46ca8bae2bb7dd4ff98141aff424142c9629a5876a61283962e5 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
52adad30ac608695a6345468144048e5134696313ab775ef72f41ca082f8fa19953e2b9924fb5970f829bf9188183fe616b3942c4705eeddfe4ce223a5940186 markupsafe-3.0.3-cp314-cp314-win_amd64.whl 00731cfdd9d5c12efef04a7161c90c1e5ed1dc4677aa88a1d4054aff836f3430df4da5262ed4289c21637358a9e10e5df16f76743cbf5a29bb3a44b146c19cf3 MarkupSafe-3.0.2-cp313-cp313-win_amd64.whl
c7668214758cb79a6ba0f7dbffb4b1b6f7cbf98d8ab6cbe77ab42c891916de9339bf17e3de3c3f2d20f6065b49f97c6d521419cc0a2b6c6b8940cf4317172d71 mutagen-1.48.1-py3-none-any.whl 8a6e2b13a2ec4ef914a5d62aad3db6464d45e525a82e07f6051ed10474eae959069e165dba011aefb8207cdfd55391d73d6f06362c7eb247b08763106709526e mutagen-1.47.0-py3-none-any.whl
e9492eedaac7cfddde6d84389ef7df86c68275e8f29d838fc431d9bf379401bf6d82ec70ac02461995982613c80592cd8de74c16a3654d7d8fdc06f96057d6e7 packaging-26.3-py3-none-any.whl a726fb46cce24f781fc8b55a3e6dea0a884ebc3b2b400ea74aa02333699f4955a5dc1e2ec5927ac72f35a624401f3f3b442882ba1cc4cadaf9c88558b5b8bdae packaging-25.0-py3-none-any.whl
8ea9946d8484a6de833a97d3cd545c2b67475207944856281cffb2c513ab14bf50e2b9d31d7cca8f50a6ca8b6711fb8e45cbadabd566cab28f7d58ee058dc650 pillow-12.3.0-cp314-cp314-win_amd64.whl efcb9da8e6f50cb5ea8e1d41e631e89b6f852b68a2568619a6ec151267e7fac94aba1e36317341c4fc5df822215b7ab9dd6b33cfb738e1b8423d4f0f6cff9525 pillow-12.3.0-cp313-cp313-win_amd64.whl
6f7ed5dc47ade9ad56f6f0499bc5d8eb485bd454d47189133297fa5aa633cf70dad18656cd673318c664f96097c39003d94d9c9703c8337d62840b4564695a04 pyinstaller-6.21.0-py3-none-win_amd64.whl b9b98714dfca6fa80b0b3f222965724d63be9c54d19435d1fe768e07016913d6db8d6e043fcb185b55a9bd6fe370a80cf961814fc096046a5f4640d99ed575ef pyinstaller-6.15.0-py3-none-win_amd64.whl
0a7c1ae7063a2ee46a1c5c057fe73838d7dc5c59800e56037b3fb9dbd1c363915375f989160e44f21436e1071713682bb14473aa8112128beb670f5b110dc8e2 pyinstaller_hooks_contrib-2026.6-py3-none-any.whl cad0f7cf39de691813b1d4abc7d33f8bda99a87d9c5886039b814752e8690364150da26fb61b3e28d5698ff57a90e6dcd619ed2b64b04f72b5aadb75e201bdb0 pyinstaller_hooks_contrib-2025.8-py3-none-any.whl
a4adbefaa216571ade7fda561ed3404c762ff03029696ce1745fd4e23b066cb49b1fef99792ce3a594a04f9260a2f05a9e2f851bcc83ed4404f00ce05709c56a python-3.14.7-amd64.exe d0d23fd4ae8900764df664803844155994b253ed449b9ed54ea020fbfa7aaea8800ca1928e4a7f26b9278613f8bce27ef9a424cfcc4ca15551c7e4e1b78f3991 python-3.13.14-amd64.exe
bfda5996cdf908edf971df38421ed154e2f2c58f38731ad3be22a88e78c933c64efd35fbc6daf82845dc0b59b5211e925038b430749b2c851887096578c68219 setuptools-83.0.0-py3-none-any.whl 2a0420f7faaa33d2132b82895a8282688030e939db0225ad8abb95a47bdb87b45318f10985fc3cee271a9121441c1526caa363d7f2e4a4b18b1a674068766e87 setuptools-80.9.0-py3-none-any.whl

Binary file not shown.

Before

Width:  |  Height:  |  Size: 555 B

After

Width:  |  Height:  |  Size: 554 B

View file

@ -29,21 +29,21 @@ uname -s | grep NT-10 && w10=1 || {
w7=1; uname -s | grep WOW64 && w7x64=1 || w7x32=1 w7=1; uname -s | grep WOW64 && w7x64=1 || w7x32=1
} }
fns=( fns=(
altgraph-0.17.5-py2.py3-none-any.whl altgraph-0.17.4-py2.py3-none-any.whl
pefile-2023.2.7-py3-none-any.whl pefile-2023.2.7-py3-none-any.whl
pywin32_ctypes-0.2.3-py3-none-any.whl pywin32_ctypes-0.2.3-py3-none-any.whl
upx-5.2.0-win32.zip upx-5.0.2-win32.zip
) )
[ $w10 ] && fns+=( [ $w10 ] && fns+=(
jinja2-3.1.6-py3-none-any.whl jinja2-3.1.6-py3-none-any.whl
markupsafe-3.0.3-cp314-cp314-win_amd64.whl MarkupSafe-3.0.2-cp313-cp313-win_amd64.whl
mutagen-1.48.1-py3-none-any.whl mutagen-1.47.0-py3-none-any.whl
packaging-26.3-py3-none-any.whl packaging-25.0-py3-none-any.whl
pillow-12.3.0-cp314-cp314-win_amd64.whl pillow-12.3.0-cp313-cp313-win_amd64.whl
pyinstaller-6.21.0-py3-none-win_amd64.whl pyinstaller-6.15.0-py3-none-win_amd64.whl
pyinstaller_hooks_contrib-2026.6-py3-none-any.whl pyinstaller_hooks_contrib-2025.8-py3-none-any.whl
python-3.14.7-amd64.exe python-3.13.14-amd64.exe
setuptools-83.0.0-py3-none-any.whl setuptools-80.9.0-py3-none-any.whl
) )
[ $w7 ] && fns+=( [ $w7 ] && fns+=(
future-1.0.0-py3-none-any.whl future-1.0.0-py3-none-any.whl
@ -83,7 +83,7 @@ close and reopen git-bash so python is in PATH
===[ copy-paste into git-bash ]================================ ===[ copy-paste into git-bash ]================================
uname -s | grep NT-10 && w10=1 || w7=1 uname -s | grep NT-10 && w10=1 || w7=1
[ $w7 ] && pyv=37 || pyv=314 [ $w7 ] && pyv=37 || pyv=313
appd=$(cygpath.exe "$APPDATA") appd=$(cygpath.exe "$APPDATA")
cd ~/Downloads && cd ~/Downloads &&
yes | unzip upx-*-win32.zip && yes | unzip upx-*-win32.zip &&
@ -91,7 +91,7 @@ mv upx-*/upx.exe . &&
python -m ensurepip && python -m ensurepip &&
{ [ $w10 ] || python -m pip install --user -U pip-*.whl; } && { [ $w10 ] || python -m pip install --user -U pip-*.whl; } &&
python -m pip install --user -U packaging-*.whl && python -m pip install --user -U packaging-*.whl &&
{ [ $w7 ] || python -m pip install --user -U {setuptools,mutagen,pillow,jinja2,markupsafe}-*.whl; } && { [ $w7 ] || python -m pip install --user -U {setuptools,mutagen,pillow,jinja2,MarkupSafe}-*.whl; } &&
{ [ $w10 ] || python -m pip install --user -U future-*.whl importlib_metadata-*.whl typing_extensions-*.whl zipp-*.whl; } && { [ $w10 ] || python -m pip install --user -U future-*.whl importlib_metadata-*.whl typing_extensions-*.whl zipp-*.whl; } &&
python -m pip install --user -U pyinstaller-*.whl pefile-*.whl pywin32_ctypes-*.whl pyinstaller_hooks_contrib-*.whl altgraph-*.whl && python -m pip install --user -U pyinstaller-*.whl pefile-*.whl pywin32_ctypes-*.whl pyinstaller_hooks_contrib-*.whl altgraph-*.whl &&
sed -ri 's/--lzma/--best/' $appd/Python/Python$pyv/site-packages/pyinstaller/building/utils.py && sed -ri 's/--lzma/--best/' $appd/Python/Python$pyv/site-packages/pyinstaller/building/utils.py &&

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB