mirror of
https://github.com/9001/copyparty.git
synced 2026-08-16 15:23:08 -06:00
Compare commits
No commits in common. "hovudstraum" and "v1.20.19" have entirely different histories.
hovudstrau
...
v1.20.19
1
.gitattributes
vendored
1
.gitattributes
vendored
|
|
@ -4,5 +4,4 @@
|
|||
|
||||
*.png binary
|
||||
*.gif binary
|
||||
*.ico binary
|
||||
*.gz binary
|
||||
|
|
|
|||
38
README.md
38
README.md
|
|
@ -84,7 +84,6 @@ built in Norway 🇳🇴 with contributions from [not-norway](https://github.com
|
|||
* [exclude-patterns](#exclude-patterns) - to save some time
|
||||
* [filesystem guards](#filesystem-guards) - avoid traversing into other filesystems
|
||||
* [periodic rescan](#periodic-rescan) - filesystem monitoring
|
||||
* [u2sz](#u2sz) - configure upload-chunking
|
||||
* [upload rules](#upload-rules) - set upload rules using volflags
|
||||
* [compress uploads](#compress-uploads) - files can be autocompressed on upload
|
||||
* [chmod and chown](#chmod-and-chown) - per-volume filesystem-permissions and ownership
|
||||
|
|
@ -138,7 +137,6 @@ built in Norway 🇳🇴 with contributions from [not-norway](https://github.com
|
|||
* [client-side](#client-side) - when uploading files
|
||||
* [security](#security) - there is a [discord server](https://discord.gg/25J8CdTT6G) with announcements
|
||||
* [gotchas](#gotchas) - behavior that might be unexpected
|
||||
* [csp nonce](#csp-nonce) - unauthorized javascript is blocked
|
||||
* [cors](#cors) - cross-site request config
|
||||
* [filekeys](#filekeys) - prevent filename bruteforcing
|
||||
* [dirkeys](#dirkeys) - share specific folders in a volume
|
||||
|
|
@ -923,7 +921,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 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)
|
||||
|
||||
|
|
@ -1827,23 +1825,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
|
||||
|
||||
set upload rules using volflags, some examples:
|
||||
|
|
@ -3126,23 +3107,6 @@ behavior that might be unexpected
|
|||
* or eliminate the problem entirely by only giving write-access to trustworthy people :^)
|
||||
|
||||
|
||||
## csp nonce
|
||||
|
||||
unauthorized javascript is blocked by means of the default [content-security-policy](https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/CSP) which enforces "script nonces", to help prevent XSS and similar
|
||||
|
||||
usually this Just Works and is correctly configured by default; you can stop reading this section and move on, nothing to see here
|
||||
|
||||
but if you are the unlucky 0.1% with a hosting-setup funky enough for this to not work as intended, then you will also be seeing this message: `Looks like javascript is broken; see "csp nonce" in the readme`
|
||||
|
||||
there is two possible reasons why this might happen:
|
||||
|
||||
1. your browser is simply too old to understand the javascript, which is not likely, because just about every browser is supported, even internet explorer 11
|
||||
|
||||
2. something in your hosting-setup is tampering with the responses from copyparty and destroying this security feature; the culprit is probably a misconfigured reverseproxy, a CDN, a WAF, or some other mitm, but a popular offender is **"cloudflare rocket loader"**
|
||||
|
||||
you can try to **temporarily** set global-options `--csp-ui no` and `--csp-dl no` and restart copyparty to check whether it is due to reason #2. Remember to refresh the webbrowser with CTRL-SHIFT-R to check properly. If that makes the problem go away, then turn them back off (**never** keep them enabled), restart copyparty, open the `Network` tab in the webbrowser, and refresh the website; you should see the `content-security-policy` header including a nonce, for example `nonce-BxcJbvdu1z3S/ycPBjVWuA==` which should also appear inside the HTML of that response like this: `<script nonce="BxcJbvdu1z3S/ycPBjVWuA==">` -- if it doesn't then that's the problem; something in your hosting stack is dangerously misconfigured
|
||||
|
||||
|
||||
## cors
|
||||
|
||||
cross-site request config
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
@ -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
|
||||
|
|
@ -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("files", type=files_decoder, nargs="+", help="files and/or folders to process")
|
||||
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("-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/.*'")
|
||||
|
|
@ -1729,9 +1729,6 @@ NOTE: if server has --usernames enabled, then password is "username:password"
|
|||
with open(zs[1:], "rb") as f:
|
||||
setattr(ar, k, f.read().decode("utf-8").strip())
|
||||
|
||||
if not ar.a:
|
||||
ar.a = os.environ.get("U2C_PW", "")
|
||||
|
||||
if ar.ba:
|
||||
ar.ba = "Basic " + base64.b64encode(ar.ba.encode("utf-8")).decode("utf-8")
|
||||
|
||||
|
|
|
|||
|
|
@ -17,12 +17,8 @@ defaults
|
|||
|
||||
listen foo1
|
||||
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
|
||||
|
||||
listen foo2
|
||||
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
|
||||
|
|
|
|||
|
|
@ -285,7 +285,6 @@ in
|
|||
|
||||
environment = {
|
||||
PYTHONUNBUFFERED = "true";
|
||||
NIX_SSL_CERT_FILE = "${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt";
|
||||
XDG_CONFIG_HOME = externalStateDir;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
# NOTE: You generally shouldn't use this PKGBUILD on Arch, as it is mainly for testing purposes. Install copyparty using pacman instead.
|
||||
|
||||
pkgname=copyparty
|
||||
pkgver="1.20.20"
|
||||
pkgver="1.20.18"
|
||||
pkgrel=1
|
||||
pkgdesc="File server with accelerated resumable uploads, dedup, WebDAV, SFTP, FTP, TFTP, zeroconf, media indexer, thumbnails++"
|
||||
arch=("any")
|
||||
|
|
@ -24,7 +24,7 @@ optdepends=("ffmpeg: thumbnails for videos, images (slower) and audio, music tag
|
|||
)
|
||||
source=("https://github.com/9001/${pkgname}/releases/download/v${pkgver}/${pkgname}-${pkgver}.tar.gz")
|
||||
backup=("etc/${pkgname}/copyparty.conf" )
|
||||
sha256sums=("a31f9e4a43e56608765e108353140784d50181c37e705e9ce8ed9f3899f57dfa")
|
||||
sha256sums=("ea1347a2e30b4261282ac4609c9b844768a925eed717ac8af1ff87a8a326508e")
|
||||
|
||||
build() {
|
||||
cd "${srcdir}/${pkgname}-${pkgver}/copyparty/web"
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
|
||||
pkgname=copyparty
|
||||
pkgver=1.20.20
|
||||
pkgver=1.20.18
|
||||
pkgrel=1
|
||||
pkgdesc="File server with accelerated resumable uploads, dedup, WebDAV, SFTP, FTP, TFTP, zeroconf, media indexer, thumbnails++"
|
||||
arch=("any")
|
||||
|
|
@ -21,7 +21,7 @@ optdepends=("ffmpeg: thumbnails for videos, images (slower) and audio, music tag
|
|||
)
|
||||
source=("https://github.com/9001/${pkgname}/releases/download/v${pkgver}/${pkgname}-${pkgver}.tar.gz")
|
||||
backup=("/etc/${pkgname}.d/init" )
|
||||
sha256sums=("a31f9e4a43e56608765e108353140784d50181c37e705e9ce8ed9f3899f57dfa")
|
||||
sha256sums=("ea1347a2e30b4261282ac4609c9b844768a925eed717ac8af1ff87a8a326508e")
|
||||
|
||||
build() {
|
||||
cd "${srcdir}/${pkgname}-${pkgver}/copyparty/web"
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"url": "https://github.com/9001/copyparty/releases/download/v1.20.20/copyparty-1.20.20.tar.gz",
|
||||
"version": "1.20.20",
|
||||
"hash": "sha256-ox+eSkPlZgh2XhCDUxQHhNUBgcN+cF6c6O2fOJn1ffo="
|
||||
"url": "https://github.com/9001/copyparty/releases/download/v1.20.18/copyparty-1.20.18.tar.gz",
|
||||
"version": "1.20.18",
|
||||
"hash": "sha256-6hNHouMLQmEoKsRgnJuER2ipJe7XF6yK8f+HqKMmUI4="
|
||||
}
|
||||
|
|
@ -40,9 +40,6 @@ VT100 = "--ansi" in sys.argv or (
|
|||
)
|
||||
# introduced in anniversary update
|
||||
|
||||
zs = os.environ.get("PRTY_CONFIG", "")
|
||||
CFG_DEF = [zs] if zs else []
|
||||
|
||||
ANYWIN = WINDOWS or sys.platform in ["msys", "cygwin"]
|
||||
|
||||
MACOS = platform.system() == "Darwin"
|
||||
|
|
|
|||
|
|
@ -23,7 +23,6 @@ import uuid
|
|||
|
||||
from .__init__ import (
|
||||
ANYWIN,
|
||||
CFG_DEF,
|
||||
CORES,
|
||||
EXE,
|
||||
MACOS,
|
||||
|
|
@ -64,7 +63,6 @@ from .util import (
|
|||
URL_BUG,
|
||||
URL_PRJ,
|
||||
Daemon,
|
||||
absreal,
|
||||
align_tab,
|
||||
b64enc,
|
||||
ctypes,
|
||||
|
|
@ -107,6 +105,10 @@ except:
|
|||
u = unicode
|
||||
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", "")
|
||||
|
||||
|
||||
|
|
@ -777,54 +779,6 @@ def get_sects():
|
|||
).rstrip()
|
||||
+ 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",
|
||||
"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("--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("--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("--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")
|
||||
|
|
@ -1633,7 +1587,6 @@ def add_wopi(ap):
|
|||
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-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-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-ttl", metavar="SEC", type=int, default=1800, help="session lifetime; allow editing for this many seconds (default is 30 min)")
|
||||
|
|
@ -1783,7 +1736,7 @@ def add_safety(ap):
|
|||
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.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)")
|
||||
|
|
@ -1792,7 +1745,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("--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("--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-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)")
|
||||
|
|
@ -1880,25 +1832,14 @@ def add_thumbnail(ap):
|
|||
# https://github.com/libvips/libvips
|
||||
# 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:'
|
||||
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,kra,ora,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,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,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,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,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-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("--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")
|
||||
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")
|
||||
|
||||
|
||||
def add_transcoding(ap):
|
||||
|
|
@ -2136,7 +2077,6 @@ def run_argparse(
|
|||
fk_salt = get_salt("fk", 18)
|
||||
dk_salt = get_salt("dk", 30)
|
||||
ah_salt = get_salt("ah", 18)
|
||||
wopi_salt = get_salt("wopi", 18)
|
||||
|
||||
# alpine peaks at 5 threads for some reason,
|
||||
# all others scale past that (but try to avoid SMT),
|
||||
|
|
@ -2173,7 +2113,7 @@ def run_argparse(
|
|||
add_opds(ap)
|
||||
add_wopi(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_shutdown(ap)
|
||||
add_yolo(ap)
|
||||
|
|
@ -2276,9 +2216,6 @@ def main(argv: Optional[list[str]] = None) -> None:
|
|||
argv.pop(n)
|
||||
break
|
||||
|
||||
if CFG_DEF:
|
||||
CFG_DEF[0] = absreal(CFG_DEF[0])
|
||||
|
||||
ensure_locale()
|
||||
|
||||
ensure_webdeps()
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
# coding: utf-8
|
||||
|
||||
VERSION = (1, 20, 20)
|
||||
VERSION = (1, 20, 19)
|
||||
CODENAME = "sftp is fine too"
|
||||
BUILD_DT = (2026, 8, 4)
|
||||
BUILD_DT = (2026, 7, 27)
|
||||
|
||||
S_VERSION = ".".join(map(str, VERSION))
|
||||
S_BUILD_DT = "{0:04d}-{1:02d}-{2:02d}".format(*BUILD_DT)
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ import threading
|
|||
import time
|
||||
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 .cfg import flagdescs, permdescs, vf_bmap, vf_cmap, vf_vmap
|
||||
from .pwhash import PWHash
|
||||
|
|
@ -25,7 +25,6 @@ from .util import (
|
|||
FN_EMB,
|
||||
HAVE_SQLITE3,
|
||||
IMPLICATIONS,
|
||||
LOG,
|
||||
META_NOBOTS,
|
||||
MIMES,
|
||||
SQLITE_VER,
|
||||
|
|
@ -2379,7 +2378,6 @@ class AuthSrv(object):
|
|||
vol.flags["dvthumb"] = True
|
||||
vol.flags["dathumb"] = True
|
||||
vol.flags["dithumb"] = True
|
||||
vol.flags["dethumb"] = True
|
||||
|
||||
have_fk = False
|
||||
for vol in vfs.all_nodes.values():
|
||||
|
|
@ -2662,11 +2660,10 @@ class AuthSrv(object):
|
|||
if head_s and not head_s.endswith("\n"):
|
||||
head_s += "\n"
|
||||
|
||||
zs2 = "Content-Security-Policy: %s\r\n"
|
||||
zs = vol.flags.get("csp_ui", "")
|
||||
csp_ui = zs2 % (zs,) if zs.lower() not in ("", "no") else ""
|
||||
csp_ui = "Content-Security-Policy: %s\r\n" % (zs,) if zs else ""
|
||||
zs = vol.flags.get("csp_dl", "")
|
||||
csp_dl = zs2 % (zs,) if zs.lower() not in ("", "no") else ""
|
||||
csp_dl = "Content-Security-Policy: %s\r\n" % (zs,) if zs else ""
|
||||
|
||||
zs = "X-Content-Type-Options: nosniff\r\n"
|
||||
if "norobots" in vol.flags:
|
||||
|
|
@ -2764,8 +2761,7 @@ class AuthSrv(object):
|
|||
|
||||
for k in drop:
|
||||
t = 'cannot enable [%s] for volume "/%s" because this requires one of the following: e2d / e2ds / e2dsa (either as volflag or global-option)'
|
||||
if not (enshare and vp.startswith(shrs)):
|
||||
self.log(t % (k, vol.vpath), 1)
|
||||
self.log(t % (k, vol.vpath), 1)
|
||||
vol.flags.pop(k)
|
||||
|
||||
zi = vol.flags.get("lifetime") or 0
|
||||
|
|
@ -4081,11 +4077,6 @@ def expand_config_file(
|
|||
if fp2 in ipath:
|
||||
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)
|
||||
|
||||
return
|
||||
|
|
|
|||
|
|
@ -316,7 +316,6 @@ flagcats = {
|
|||
"dvthumb": "disables video thumbnails",
|
||||
"dathumb": "disables audio thumbnails (spectrograms)",
|
||||
"dithumb": "disables image thumbnails",
|
||||
"dethumb": "disables custom thumbnails (--th-extract)",
|
||||
"pngquant": "compress audio waveforms 33% better",
|
||||
"thsize": "thumbnail res; WxH",
|
||||
"crop": "center-cropping (y/n/fy/fn)",
|
||||
|
|
|
|||
|
|
@ -1649,8 +1649,7 @@ class HttpCli(object):
|
|||
raise Pebkac(500, "too many wopi sessions")
|
||||
if not found:
|
||||
atoken = ub64enc(os.urandom(18)).decode("ascii") # 18 = 144b = 24c
|
||||
zb = (vpath + self.args.wopi_salt).encode("utf-8", "replace")
|
||||
file_id = ub64enc(hashlib.sha512(zb).digest()[:15]).decode("ascii")
|
||||
file_id = ub64enc(os.urandom(15)).decode("ascii") # 15 = 120b = 20c
|
||||
wopi_files[atoken] = session = {
|
||||
"vp": vpath,
|
||||
"uname": self.uname,
|
||||
|
|
@ -1658,9 +1657,7 @@ class HttpCli(object):
|
|||
"expires": time.time() + self.args.wopi_ttl,
|
||||
}
|
||||
|
||||
assert atoken and session # type: ignore # !rm
|
||||
|
||||
xml = url = "?"
|
||||
xml = "?"
|
||||
try:
|
||||
from .dxml import parse_xml
|
||||
|
||||
|
|
@ -1676,18 +1673,14 @@ class HttpCli(object):
|
|||
|
||||
uo_kw["context"] = ctx
|
||||
|
||||
wopi_urls = dict(x.lower().split("=", 1) for x in self.args.wopi_urls or [])
|
||||
url = wopi_urls.get(self.host.lower(), self.args.wopi_url).rstrip("/")
|
||||
url += "/hosting/discovery"
|
||||
url = self.args.wopi_url.rstrip("/") + "/hosting/discovery"
|
||||
buf = urlopen(url, **uo_kw).read()
|
||||
xml = buf.decode("ascii", "replace").lower()
|
||||
enc = self.get_xml_enc(xml)
|
||||
xml = buf.decode(enc, "replace")
|
||||
xroot = parse_xml(xml)
|
||||
ext = vpath.split(".")[-1]
|
||||
url = xroot.find(
|
||||
".//action[@ext='%s'][@name='edit'][@urlsrc]" % (ext,)
|
||||
).get("urlsrc")
|
||||
url = xroot.find(".//action[@ext='%s'][@urlsrc]" % (ext,)).get("urlsrc")
|
||||
if not url.endswith(("?", "&")):
|
||||
url += "&" if "?" in url else "?"
|
||||
url += "WOPISrc="
|
||||
|
|
@ -1698,8 +1691,7 @@ class HttpCli(object):
|
|||
url += quotep(zs + "/wopi/files/" + session["file_id"])
|
||||
except:
|
||||
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(t % (url, min_ex(), xml), 3)
|
||||
self.log("reading WOPI-client response failed; %s\n%s" % (min_ex(), xml), 3)
|
||||
raise Pebkac(500, "wopi error (see fileserver log)")
|
||||
|
||||
html = self.j2s(
|
||||
|
|
@ -1707,7 +1699,7 @@ class HttpCli(object):
|
|||
title=self.uparam["wopi"],
|
||||
url=url,
|
||||
atoken=atoken,
|
||||
ttl=int(session["expires"] * 1000),
|
||||
ttl=session["expires"],
|
||||
).encode("utf-8", "replace")
|
||||
|
||||
self.reply(html, 200, "text/html; charset=utf-8")
|
||||
|
|
@ -7449,18 +7441,20 @@ class HttpCli(object):
|
|||
dirs = []
|
||||
files = []
|
||||
ptn_hr = RE_HR
|
||||
|
||||
base = ""
|
||||
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 + "/"
|
||||
|
||||
use_abs_url = is_opds or (
|
||||
vpath and not is_ls and not is_js and not self.trailing_slash
|
||||
)
|
||||
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:
|
||||
fspath = vfs_virt[fn].realpath
|
||||
|
|
@ -7630,26 +7624,6 @@ class HttpCli(object):
|
|||
(fe["sz"], fe["tags"][".files"]) = hit
|
||||
except:
|
||||
pass # 404 or mojibake
|
||||
if vfs_virt:
|
||||
q = "select sz, nf from ds where rd='' limit 1"
|
||||
try:
|
||||
for fe in [x for x in dirs if x["name"] in vfs_virt]:
|
||||
if ".files" not in fe["tags"]:
|
||||
fe["tags"][".files"] = 0
|
||||
vols = [vn.nodes[fe["name"]]]
|
||||
while vols:
|
||||
vn2 = vols.pop()
|
||||
if self.uname not in vn2.axs.uread:
|
||||
continue
|
||||
vols += list(vn2.nodes.values())
|
||||
if vn2.dbv not in (vn2, None):
|
||||
continue
|
||||
hit = idx.get_cur(vn2).execute(q).fetchone()
|
||||
if hit:
|
||||
fe["sz"] += hit[0]
|
||||
fe["tags"][".files"] += hit[1]
|
||||
except:
|
||||
pass
|
||||
|
||||
taglist = [k for k in lmte if k in tagset]
|
||||
else:
|
||||
|
|
@ -7895,10 +7869,6 @@ class HttpCli(object):
|
|||
ext in self.thumbcli.fmt_pil
|
||||
or ext in self.thumbcli.fmt_vips
|
||||
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_au = ext in self.thumbcli.fmt_ffa
|
||||
|
|
|
|||
|
|
@ -194,12 +194,6 @@ def au_unpk(
|
|||
log(t)
|
||||
fi = zf.open(using)
|
||||
|
||||
elif pk == "kra" or pk == "ora":
|
||||
import zipfile
|
||||
|
||||
zf = zipfile.ZipFile(abspath, "r")
|
||||
fi = zf.open("mergedimage.png")
|
||||
|
||||
elif pk == "epub":
|
||||
fi = get_cover_from_epub(log, abspath)
|
||||
assert fi # !rm
|
||||
|
|
|
|||
|
|
@ -133,19 +133,6 @@ VER_SHARES_DB = 2
|
|||
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):
|
||||
"""
|
||||
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(",")]
|
||||
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.thumbsrv = None
|
||||
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]"
|
||||
raise Exception(t % (args.q_mp3,))
|
||||
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 = {
|
||||
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"
|
||||
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:
|
||||
from .dxml import DXML_OK
|
||||
|
||||
|
|
@ -1414,20 +1383,18 @@ class SvcHub(object):
|
|||
self.args.name_html = zs
|
||||
|
||||
zs = al.u2sz
|
||||
if zs in ("no", "1,1,1"):
|
||||
zs = "0"
|
||||
zsl = [x.strip() for x in zs.split(",")]
|
||||
if len(zsl) not in (1, 2):
|
||||
t = "invalid --u2sz; must be either one number, or a comma-separated list of two numbers (target,max)"
|
||||
if len(zsl) not in (1, 3):
|
||||
t = "invalid --u2sz; must be either one number, or a comma-separated list of three numbers (min,default,max)"
|
||||
raise Exception(t)
|
||||
if len(zsl) < 2:
|
||||
zsl = [zs, zs]
|
||||
zi2 = 0
|
||||
if len(zsl) < 3:
|
||||
zsl = ["1", zs, zs]
|
||||
zi2 = 1
|
||||
for zs in zsl:
|
||||
zi = int(zs)
|
||||
# arbitrary constraint (anything above 2 GiB is probably unintended)
|
||||
if zi < 0 or zi > 2047:
|
||||
raise Exception("invalid --u2sz; minimum is 0, max is 2047")
|
||||
if zi < 1 or zi > 2047:
|
||||
raise Exception("invalid --u2sz; minimum is 1, max is 2047")
|
||||
if zi < zi2:
|
||||
raise Exception("invalid --u2sz; values must be equal or ascending")
|
||||
zi2 = zi
|
||||
|
|
|
|||
|
|
@ -34,7 +34,6 @@ class ThumbCli(object):
|
|||
self.cooldown = Cooldown(self.args.th_poke) if self.args.th_poke else None
|
||||
|
||||
self.thumbable = c["thumbable"]
|
||||
self.thumbable_native = c["thumbable_native"]
|
||||
self.fmt_pil = c["pil"]
|
||||
self.fmt_vips = c["vips"]
|
||||
self.fmt_raw = c["raw"]
|
||||
|
|
@ -60,10 +59,6 @@ class ThumbCli(object):
|
|||
if is_vid and "dvthumb" in dbv.flags:
|
||||
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
|
||||
is_au = ext in self.fmt_ffa
|
||||
is_vau = want_opus and ext in self.fmt_ffv
|
||||
|
|
@ -81,7 +76,7 @@ class ThumbCli(object):
|
|||
elif want_opus:
|
||||
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:
|
||||
return None
|
||||
|
||||
|
|
|
|||
|
|
@ -38,7 +38,6 @@ from .util import (
|
|||
afsenc,
|
||||
atomic_move,
|
||||
fsenc,
|
||||
loadpy,
|
||||
min_ex,
|
||||
runcmd,
|
||||
statdir,
|
||||
|
|
@ -324,12 +323,24 @@ class ThumbSrv(object):
|
|||
if self.args.th_clean:
|
||||
Daemon(self.cleaner, "thumb.cln")
|
||||
|
||||
self.fmt_pil = self.args.th_r_pil
|
||||
self.fmt_vips = self.args.th_r_vips
|
||||
self.fmt_raw = self.args.th_r_raw
|
||||
self.fmt_ffi = self.args.th_r_ffi
|
||||
self.fmt_ffv = self.args.th_r_ffv
|
||||
self.fmt_ffa = self.args.th_r_ffa
|
||||
(
|
||||
self.fmt_pil,
|
||||
self.fmt_vips,
|
||||
self.fmt_raw,
|
||||
self.fmt_ffi,
|
||||
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:
|
||||
for f in "heif heifs heic heics".split(" "):
|
||||
|
|
@ -348,26 +359,23 @@ class ThumbSrv(object):
|
|||
self.fmt_pil.discard(f)
|
||||
|
||||
self.thumbable: set[str] = set()
|
||||
self.thumbable_native: set[str] = set()
|
||||
self._build_thumbable()
|
||||
|
||||
def _build_thumbable(self) -> None:
|
||||
self.thumbable_native.clear()
|
||||
self.thumbable.clear()
|
||||
|
||||
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:
|
||||
self.thumbable_native |= self.fmt_vips
|
||||
self.thumbable |= self.fmt_vips
|
||||
|
||||
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:
|
||||
for zss in [self.fmt_ffi, self.fmt_ffv, self.fmt_ffa]:
|
||||
self.thumbable_native |= zss
|
||||
|
||||
self.thumbable = self.thumbable_native | set(self.args.th_extract)
|
||||
self.thumbable |= zss
|
||||
|
||||
def _log(self, msg: str, c: Union[int, str] = 0) -> None:
|
||||
self.log_func("thumb", msg, c)
|
||||
|
|
@ -459,7 +467,6 @@ class ThumbSrv(object):
|
|||
def getcfg(self) -> dict[str, set[str]]:
|
||||
return {
|
||||
"thumbable": self.thumbable,
|
||||
"thumbable_native": self.thumbable_native,
|
||||
"pil": self.fmt_pil,
|
||||
"vips": self.fmt_vips,
|
||||
"raw": self.fmt_raw,
|
||||
|
|
@ -542,36 +549,19 @@ class ThumbSrv(object):
|
|||
png_ok = False
|
||||
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:
|
||||
ap_unpk = au_unpk(self.log, self.args.au_unpk, ap_extr, vn)
|
||||
elif ext in self.thumbable_native:
|
||||
ap_unpk = ap_extr
|
||||
ap_unpk = au_unpk(self.log, self.args.au_unpk, abspath, vn)
|
||||
else:
|
||||
ap_unpk = ""
|
||||
|
||||
if ap_extr and ap_extr != ap_unpk and ap_extr != abspath:
|
||||
wunlink(self.log, ap_extr, vn.flags)
|
||||
ap_unpk = abspath
|
||||
|
||||
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:
|
||||
can_au = lib == "ff" and (
|
||||
ext in self.fmt_ffa or ext in self.fmt_ffv
|
||||
|
|
@ -679,66 +669,6 @@ class ThumbSrv(object):
|
|||
with self.mutex:
|
||||
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":
|
||||
# exif_transpose is expensive (loads full image + unconditional copy)
|
||||
res = self.getres(vn, fmt)
|
||||
|
|
|
|||
|
|
@ -352,7 +352,7 @@ RE_MEMAVAIL = re.compile("^MemAvailable:.* kB")
|
|||
|
||||
if PY2:
|
||||
|
||||
def umktrans(s1: str, s2: str):
|
||||
def umktrans(s1, s2):
|
||||
return {ord(c1): ord(c2) for c1, c2 in zip(s1, s2)}
|
||||
|
||||
else:
|
||||
|
|
@ -2150,8 +2150,7 @@ class MultipartParser(object):
|
|||
boundary = get_boundary(self.headers)
|
||||
if boundary.startswith('"') and boundary.endswith('"'):
|
||||
boundary = boundary[1:-1] # dillo uses quotes
|
||||
if len(boundary) > 72: # rfc-2046 <=70
|
||||
raise Pebkac(400, "boundary 2big: %d" % (len(boundary),))
|
||||
self.log("boundary=%r" % (boundary,))
|
||||
|
||||
# spec says there might be junk before the first boundary,
|
||||
# can't have the leading \r\n if that's not the case
|
||||
|
|
|
|||
|
|
@ -121,8 +121,6 @@
|
|||
|
||||
<h2 id="wfp"><a href="{{ r }}/?h" id="goh">control-panel</a></h2>
|
||||
|
||||
<p id="js_bork">Looks like javascript is broken; see "csp nonce" section in readme</p>
|
||||
|
||||
<a href="#" id="repl">π</a>
|
||||
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -46,7 +46,6 @@ if (1)
|
|||
["ctrl-V", "paste (move/copy) here"],
|
||||
["Y", "download selected"],
|
||||
["F2", "rename selected"],
|
||||
["F4", "update/refresh"],
|
||||
|
||||
"file-list-sel",
|
||||
["space", "toggle file selection"],
|
||||
|
|
@ -758,7 +757,7 @@ if (window.glang && navigator.languages && !/\bcplng=/.test(document.cookie))
|
|||
if (window.stop)
|
||||
window.stop();
|
||||
document.body.innerHTML = 'Loading ' + n;
|
||||
setck("cplng=" + n, relod);
|
||||
setck("cplng=" + n, location.reload.bind(location));
|
||||
crashed = true;
|
||||
throw 1;
|
||||
}
|
||||
|
|
@ -1246,8 +1245,8 @@ check_image_support('jxl', "data:image/jxl;base64,/woIAAAMABKIAgC4AF3lEgA=");
|
|||
|
||||
|
||||
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|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)(\?|$)/i;
|
||||
|
||||
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(' '));
|
||||
|
|
@ -5694,8 +5693,8 @@ var thegrid = (function () {
|
|||
fid = oth.getAttribute('id'),
|
||||
aplay = ebi('a' + fid),
|
||||
atext = ebi('t' + fid),
|
||||
is_txt = atext && !/\.ts$/.test(href) && showfile.getlang(href),
|
||||
is_img = img_re.test(href),
|
||||
is_txt = atext && !is_img && !/\.ts$/.test(href) && showfile.getlang(href),
|
||||
is_dir = href.endsWith('/'),
|
||||
is_srch = !!ebi('unsearch'),
|
||||
in_tree = is_dir && treectl.find(oth.textContent.slice(0, -1)),
|
||||
|
|
@ -6439,9 +6438,6 @@ var ahotkeys = function (e) {
|
|||
if (k == 'F2')
|
||||
return fileman.rename();
|
||||
|
||||
if (k == 'F4')
|
||||
return treectl.goto();
|
||||
|
||||
if (!treectl.hidden && (!sh || !thegrid.en)) {
|
||||
if (kl == 'a')
|
||||
return QS('#twig').click();
|
||||
|
|
@ -7696,7 +7692,7 @@ var treectl = (function () {
|
|||
|
||||
if (wopi_set && wopi_set.has(tn.ext))
|
||||
tn.lead = '<a href="?wopi=' + bhref +
|
||||
'" rel="nofollow" target="_blank" name="' + hname + '">📄</a>';
|
||||
'" rel="nofollow" name="' + hname + '">📄</a>';
|
||||
|
||||
if (tn.lead == '-')
|
||||
tn.lead = '<a href="?doc=' + bhref + '" id="t' + id +
|
||||
|
|
@ -7790,7 +7786,6 @@ var treectl = (function () {
|
|||
|
||||
r.hydrate = function () {
|
||||
qsr('#bbsw');
|
||||
qsr('#js_bork');
|
||||
srvinf = ebi('srv_info').innerHTML.slice(6, -7);
|
||||
if (ls0 === null) {
|
||||
r.ls_cb = showfile.addlinks;
|
||||
|
|
@ -8626,7 +8621,7 @@ var setfszf = (function () {
|
|||
setck('cplng=' + lang);
|
||||
freshen();
|
||||
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();
|
||||
|
|
@ -8877,7 +8872,7 @@ var msel = (function () {
|
|||
r.evsel = function (e, fun) {
|
||||
ev(e);
|
||||
r.so = r.pr = null;
|
||||
var trs = QSA('#files tbody tr' + (ebi('unsearch') ? ':not(.srch_hdr)' : ''));
|
||||
var trs = QSA('#files tbody tr');
|
||||
for (var a = 0, aa = trs.length; a < aa; a++)
|
||||
clmod(trs[a], 'sel', fun);
|
||||
r.selui();
|
||||
|
|
@ -9337,7 +9332,6 @@ function sandbox(tgt, rules, allow, cls, html) {
|
|||
fr.setAttribute('sandbox', rules ? 'allow-' + rules.replace(/ /g, ' allow-') : '');
|
||||
fr.setAttribute('allow', allow);
|
||||
fr.setAttribute('srcdoc', html);
|
||||
tgt.innerHTML = '';
|
||||
tgt.appendChild(fr);
|
||||
treectl.sb_msg = true;
|
||||
return true;
|
||||
|
|
@ -10076,7 +10070,7 @@ function reload_browser() {
|
|||
function sel_start(e) {
|
||||
if (e.button !== 0 && e.type !== 'touchstart') return;
|
||||
if (!thegrid.en || !treectl.dsel) return;
|
||||
if (e.target.closest('#widget,#ops,.opview,.doc,.bbox-open')) return;
|
||||
if (e.target.closest('#widget,#ops,.opview,.doc')) return;
|
||||
|
||||
if (e.target.closest('#gfiles'))
|
||||
ebi('gfiles').style.userSelect = "none";
|
||||
|
|
|
|||
|
|
@ -325,9 +325,6 @@ var modpoll = new Modpoll();
|
|||
|
||||
|
||||
window.onbeforeunload = function (e) {
|
||||
if (window.gtfo)
|
||||
return;
|
||||
|
||||
if ((ebi("save").className + '').indexOf('disabled') >= 0)
|
||||
return; //nice (todo)
|
||||
|
||||
|
|
|
|||
|
|
@ -39,7 +39,6 @@ Ls.chi = {
|
|||
["ctrl-V", "粘贴(移动/复制)到此处"],
|
||||
["Y", "下载选中项"],
|
||||
["F2", "重命名选中项"],
|
||||
["F4", "刷新"], //m
|
||||
|
||||
"文件列表选择",
|
||||
["space", "切换文件选择"],
|
||||
|
|
|
|||
|
|
@ -39,7 +39,6 @@ Ls.cze = {
|
|||
["ctrl-V", "vložit (přesunout/kopírovat) zde"],
|
||||
["Y", "stáhnout vybrané"],
|
||||
["F2", "přejmenovat vybrané"],
|
||||
["F4", "obnovit"], //m
|
||||
|
||||
"výběr souborů",
|
||||
["space", "přepnout výběr souboru"],
|
||||
|
|
|
|||
|
|
@ -39,7 +39,6 @@ Ls.deu = {
|
|||
["STRG-V", "Zwischenablage hier einfügen"],
|
||||
["Y", "Auswahl herunterladen"],
|
||||
["F2", "Auswahl umbenennen"],
|
||||
["F4", "Aktualisieren"], //m
|
||||
|
||||
"file-list-sel",
|
||||
["LEER", "Dateiauswahl aktivieren"],
|
||||
|
|
|
|||
|
|
@ -39,7 +39,6 @@ Ls.epo = {
|
|||
["stir-V", "alglui (movi/kopii) ĉi tien"],
|
||||
["Y", "elŝuti elektitajn"],
|
||||
["F2", "alinomi elektitajn"],
|
||||
["F4", "refreŝigi"], //m
|
||||
|
||||
"file-list-sel",
|
||||
["spacoklavo", "baskuli elektadon de dosieroj"],
|
||||
|
|
|
|||
|
|
@ -39,7 +39,6 @@ Ls.fin = {
|
|||
["ctrl-V", "siirrä tai kopioi tähän"],
|
||||
["Y", "lataa valitut"],
|
||||
["F2", "uudelleennimeä valitut"],
|
||||
["F4", "päivitä"], //m
|
||||
|
||||
"file-list-sel",
|
||||
["space", "vaihda tiedostonvalintatilaan"],
|
||||
|
|
|
|||
|
|
@ -39,7 +39,6 @@ Ls.fra = {
|
|||
["ctrl-V", "coller (déplacer/copier) ici"],
|
||||
["Y", "télécharger la sélection"],
|
||||
["F2", "renomer la sélection"],
|
||||
["F4", "actualiser"], //m
|
||||
|
||||
"file-list-sel",
|
||||
["Espace", "activer la sélection de fichiers"],
|
||||
|
|
|
|||
|
|
@ -39,7 +39,6 @@ Ls.grc = {
|
|||
["ctrl-V", "επικόλληση (μετακίνηση/αντιγραφή) εδώ"],
|
||||
["Y", "λήψη επιλεγμένων"],
|
||||
["F2", "μετονομασία επιλεγμένων"],
|
||||
["F4", "ανανέωση"], //m
|
||||
|
||||
"λίστα αρχείων",
|
||||
["space", "εναλλαγή επιλογής αρχείου"],
|
||||
|
|
|
|||
|
|
@ -36,7 +36,6 @@ Ls.hun = {
|
|||
['ctrl-V', 'beillesztés ide'],
|
||||
['Y', 'kijelöltek letöltése'],
|
||||
['F2', 'átnevezés'],
|
||||
["F4", "frissítés"], //m
|
||||
|
||||
'kijelölés',
|
||||
['space', 'fájl kijelölése'],
|
||||
|
|
|
|||
|
|
@ -39,7 +39,6 @@ Ls.ita = {
|
|||
["ctrl-V", "incolla (sposta/copia) qui"],
|
||||
["Y", "scarica selezionati"],
|
||||
["F2", "rinomina selezionati"],
|
||||
["F4", "aggiorna"], //m
|
||||
|
||||
"file-list-sel",
|
||||
["spazio", "alterna selezione file"],
|
||||
|
|
|
|||
|
|
@ -39,7 +39,6 @@ Ls.jpn = {
|
|||
["ctrl-V", "ここに貼り付け(移動/コピー)"],
|
||||
["Y", "選択した項目をダウンロード"],
|
||||
["F2", "選択した項目の名前を変更"],
|
||||
["F4", "更新"], //m
|
||||
|
||||
"file-list-sel",
|
||||
["space", "ファイル選択の切り替え"],
|
||||
|
|
|
|||
|
|
@ -39,7 +39,6 @@ Ls.kor = {
|
|||
["ctrl-V", "여기에 붙여넣기 (이동/복사)"],
|
||||
["Y", "선택 항목 다운로드"],
|
||||
["F2", "선택 항목 이름 바꾸기"],
|
||||
["F4", "새로 고침"], //m
|
||||
|
||||
"파일 목록 선택",
|
||||
["space", "파일 선택/해제"],
|
||||
|
|
|
|||
|
|
@ -39,7 +39,6 @@ Ls.nld = {
|
|||
["ctrl-V", "Hier plakken (verplaatsen/kopieëren)"],
|
||||
["Y", "Download geselecteerde"],
|
||||
["F2", "Hernoem geselecteerde"],
|
||||
["F4", "vernieuwen"], //m
|
||||
|
||||
"bestand-lijst-selectie",
|
||||
["space", "wissel bestand selectie"],
|
||||
|
|
|
|||
|
|
@ -36,7 +36,6 @@ Ls.nno = {
|
|||
["ctrl-V", "lim inn (flytt/kopiér)"],
|
||||
["Y", "last ned valde"],
|
||||
["F2", "endre namn på valde"],
|
||||
["F4", "oppdater"],
|
||||
|
||||
"filmarkering",
|
||||
["space", "markér fil"],
|
||||
|
|
|
|||
|
|
@ -36,7 +36,6 @@ Ls.nor = {
|
|||
["ctrl-V", "lim inn (flytt/kopiér)"],
|
||||
["Y", "last ned valgte"],
|
||||
["F2", "endre navn på valgte"],
|
||||
["F4", "oppdater"],
|
||||
|
||||
"filmarkering",
|
||||
["space", "marker fil"],
|
||||
|
|
|
|||
|
|
@ -39,7 +39,6 @@ Ls.pol = {
|
|||
["ctrl-V", "wklej (przenieś/skopiuj) tutaj"],
|
||||
["Y", "pobierz zaznaczone"],
|
||||
["F2", "zmień nazwę zaznaczonych"],
|
||||
["F4", "odśwież"], //m
|
||||
|
||||
"file-list-sel",
|
||||
["spacja", "przełącz zaznaczanie plików"],
|
||||
|
|
|
|||
|
|
@ -39,7 +39,6 @@ Ls.por = {
|
|||
["ctrl-V", "colar (mover/copiar) aqui"],
|
||||
["Y", "baixar selecionado"],
|
||||
["F2", "renomear selecionado"],
|
||||
["F4", "atualizar"], //m
|
||||
|
||||
"seleção de lista de arquivos",
|
||||
["espaço", "alternar seleção de arquivo"],
|
||||
|
|
|
|||
|
|
@ -39,7 +39,6 @@ Ls.rus = {
|
|||
["ctrl-V", "вставить (переместить/копировать) сюда"],
|
||||
["Y", "скачать выделенное"],
|
||||
["F2", "переименовать выделенное"],
|
||||
["F4", "обновить"], //m
|
||||
|
||||
"выделение файлов",
|
||||
["пробел", "выделить/снять выделение с текущего файла"],
|
||||
|
|
|
|||
|
|
@ -39,7 +39,6 @@ Ls.spa = {
|
|||
["ctrl-V", "pegar (mover/copiar) aquí"],
|
||||
["Y", "descargar seleccionados"],
|
||||
["F2", "renombrar seleccionados"],
|
||||
["F4", "actualizar"], //m
|
||||
|
||||
"selección en lista de archivos",
|
||||
["space", "alternar selección de archivo"],
|
||||
|
|
|
|||
|
|
@ -39,7 +39,6 @@ Ls.swe = {
|
|||
["ctrl-V", "klistra in (kopiera/flytta) hit"],
|
||||
["Y", "ladda ner urval"],
|
||||
["F2", "byt namn på urval"],
|
||||
["F4", "uppdatera"], //m
|
||||
|
||||
"välja filer",
|
||||
["Blanksteg", "växla val av fil"],
|
||||
|
|
|
|||
|
|
@ -39,7 +39,6 @@ Ls.tur = {
|
|||
["ctrl-V", "buraya yapıştır (taşı/kopyala)"],
|
||||
["Y", "seçileni indir"],
|
||||
["F2", "seçileni yeniden adlandır"],
|
||||
["F4", "yenile"], //m
|
||||
|
||||
"dosya yönetimi seçimleri",
|
||||
["boşluk", "seçimi değiştir"],
|
||||
|
|
|
|||
|
|
@ -39,7 +39,6 @@ Ls.ukr = {
|
|||
["ctrl-V", "вставити (перемістити/копіювати) сюди"],
|
||||
["Y", "завантажити вибране"],
|
||||
["F2", "перейменувати вибране"],
|
||||
["F4", "оновити"], //m
|
||||
|
||||
"вибір файлів у списку",
|
||||
["space", "перемкнути вибір файлу"],
|
||||
|
|
|
|||
|
|
@ -36,7 +36,6 @@ Ls.vie = {
|
|||
["ctrl-V", "dán (di chuyển/sao chép) tại đây"],
|
||||
["Y", "tải xuống mục đã chọn"],
|
||||
["F2", "đổi tên mục đã chọn"],
|
||||
["F4", "làm mới"], //m
|
||||
|
||||
"file-list-sel",
|
||||
["space", "chuyển đổi chọn tệp"],
|
||||
|
|
|
|||
|
|
@ -888,8 +888,9 @@ function up2k_init(subtle) {
|
|||
setmsg(suggest_up2k, 'msg');
|
||||
|
||||
var u2szs = u2sz.split(','),
|
||||
u2sz_tgt = parseInt(u2szs[0]),
|
||||
u2sz_max = parseInt(u2szs[1]);
|
||||
u2sz_min = parseInt(u2szs[0]),
|
||||
u2sz_tgt = parseInt(u2szs[1]),
|
||||
u2sz_max = parseInt(u2szs[2]);
|
||||
|
||||
var parallel_uploads = ebi('nthread').value = icfg_get('nthread', u2j),
|
||||
stitch_tgt = ebi('u2szg').value = icfg_get('u2sz', u2sz_tgt),
|
||||
|
|
@ -997,20 +998,6 @@ function up2k_init(subtle) {
|
|||
});
|
||||
}
|
||||
|
||||
function unqueue_up(t) {
|
||||
t.postlist = [];
|
||||
var arr = st.todo.upload;
|
||||
for (var a = arr.length - 1; a >= 0; a--)
|
||||
if (arr[a].nfile == t.n)
|
||||
arr.splice(a, 1);
|
||||
}
|
||||
|
||||
function upq_has(q, t) {
|
||||
for (var a = 0, aa = q.length; a < aa; a++)
|
||||
if (q[a].nfile == t.n)
|
||||
return true;
|
||||
}
|
||||
|
||||
var pvis = new U2pvis("bz", '#u2cards', uc, st),
|
||||
donut = new Donut(uc, st);
|
||||
|
||||
|
|
@ -2525,7 +2512,6 @@ function up2k_init(subtle) {
|
|||
keepalive = t.keepalive,
|
||||
me = Date.now();
|
||||
|
||||
while (apop(st.todo.handshake, t));
|
||||
if (t.done)
|
||||
return console.log('done; skip hs', t.name, t);
|
||||
|
||||
|
|
@ -2644,7 +2630,7 @@ function up2k_init(subtle) {
|
|||
cbd.push(a == cdr_idx ? cdr_sz : chunksize);
|
||||
}
|
||||
|
||||
unqueue_up(t);
|
||||
t.postlist = [];
|
||||
t.wark = response.wark;
|
||||
var missing = response.hash;
|
||||
for (var a = 0; a < missing.length; a++) {
|
||||
|
|
@ -2682,7 +2668,7 @@ function up2k_init(subtle) {
|
|||
ccs += stp; stp *= 2;
|
||||
}
|
||||
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++) {
|
||||
|
|
@ -2871,8 +2857,9 @@ function up2k_init(subtle) {
|
|||
if (upf.sprs)
|
||||
return true;
|
||||
|
||||
if (upq_has(st.busy.upload, upf))
|
||||
return false;
|
||||
for (var a = 0, aa = st.busy.upload.length; a < aa; a++)
|
||||
if (st.busy.upload[a].nfile == upt.nfile)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
@ -2883,8 +2870,7 @@ function up2k_init(subtle) {
|
|||
nparts = upt.nparts,
|
||||
pcar = nparts[0],
|
||||
pcdr = nparts[nparts.length - 1],
|
||||
maxmsz = stitch_tgt == u2sz_tgt ? u2sz_max : stitch_tgt,
|
||||
maxsz = (maxmsz > 0 ? maxmsz : 2040) * 1024 * 1024;
|
||||
maxsz = (u2sz_max > 1 ? u2sz_max : 2040) * 1024 * 1024;
|
||||
|
||||
if (t.done)
|
||||
return console.log('done; skip chunk', t.name, t);
|
||||
|
|
@ -2933,7 +2919,8 @@ function up2k_init(subtle) {
|
|||
var txt = unpre((xhr.response && xhr.response.err) || xhr.responseText);
|
||||
if (txt.indexOf('upload blocked by x') + 1) {
|
||||
apop(st.busy.upload, upt);
|
||||
unqueue_up(t);
|
||||
for (var a = pcar; a <= pcdr; a++)
|
||||
apop(t.postlist, a);
|
||||
pvis.seth(t.n, 1, "ERROR");
|
||||
pvis.seth(t.n, 2, txt.split(/\n/)[0]);
|
||||
pvis.move(t.n, 'ng');
|
||||
|
|
@ -2956,13 +2943,8 @@ function up2k_init(subtle) {
|
|||
st.etac.u++;
|
||||
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) {
|
||||
unqueue_up(t);
|
||||
}
|
||||
else if (txt.indexOf('already being written') + 1) {
|
||||
else if (txt.indexOf('already got that') + 1 ||
|
||||
txt.indexOf('already being written') + 1) {
|
||||
t.nojoin = t.nojoin || t.postlist.length;
|
||||
console.log("ignoring dupe-segment with backoff", t.nojoin, t.name, t);
|
||||
if (!toast.visible && st.todo.upload.length < 4)
|
||||
|
|
@ -2970,10 +2952,6 @@ function up2k_init(subtle) {
|
|||
}
|
||||
else {
|
||||
xhrchk(xhr, L.u_cuerr2.format(snpart, Math.ceil(t.size / chunksize), esc(t.name)), "404, target folder not found (???)", "warn", t);
|
||||
if (txt.indexOf('unknown wark') == 0) {
|
||||
t.cooldown = t.coolmul = 0;
|
||||
unqueue_up(t);
|
||||
}
|
||||
chill(t);
|
||||
}
|
||||
orz2(xhr);
|
||||
|
|
@ -2982,12 +2960,10 @@ function up2k_init(subtle) {
|
|||
apop(st.busy.upload, upt);
|
||||
for (var a = pcar; a <= pcdr; a++)
|
||||
apop(t.postlist, a);
|
||||
if (!t.postlist.length && !upq_has(st.busy.upload, t)) {
|
||||
if (!t.postlist.length) {
|
||||
t.t_uploaded = Date.now();
|
||||
if (!has(st.busy.handshake, t)) {
|
||||
pvis.seth(t.n, 1, 'verifying');
|
||||
st.todo.handshake.unshift(t);
|
||||
}
|
||||
pvis.seth(t.n, 1, 'verifying');
|
||||
st.todo.handshake.unshift(t);
|
||||
}
|
||||
tasker();
|
||||
}
|
||||
|
|
@ -3022,7 +2998,6 @@ function up2k_init(subtle) {
|
|||
if (!toast.visible)
|
||||
toast.warn(9.98, L.u_cuerr.format(snpart, Math.ceil(t.size / chunksize), esc(t.name)), t);
|
||||
|
||||
unqueue_up(t); // maybe unknown wark (ff drops undrained rsp)
|
||||
t.nojoin = t.nojoin || t.postlist.length; // maybe rproxy postsize limit
|
||||
console.log('chunkpit onerror,', t.name, t);
|
||||
orz2(xhr);
|
||||
|
|
@ -3171,7 +3146,7 @@ function up2k_init(subtle) {
|
|||
var el = ebi('u2szg'), n = parseInt(el.value);
|
||||
stitch_tgt = n = (
|
||||
isNaN(n) ? u2sz_tgt :
|
||||
n < 0 ? 0 :
|
||||
n < u2sz_min ? u2sz_min :
|
||||
n > u2sz_max ? u2sz_max : n
|
||||
);
|
||||
if (n == u2sz_tgt) sdrop('u2sz'); else swrite('u2sz', n);
|
||||
|
|
@ -3408,7 +3383,7 @@ function up2k_init(subtle) {
|
|||
function set_nosubtle(v) {
|
||||
if (!WebAssembly)
|
||||
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) {
|
||||
|
|
@ -3479,9 +3454,6 @@ function up2k_init(subtle) {
|
|||
|
||||
|
||||
function warn_uploader_busy(e) {
|
||||
if (window.gtfo)
|
||||
return;
|
||||
|
||||
e.preventDefault();
|
||||
e.returnValue = '';
|
||||
return "upload in progress, click abort and use the file-tree to navigate instead";
|
||||
|
|
|
|||
|
|
@ -108,13 +108,6 @@ function qsr(sel) {
|
|||
}
|
||||
|
||||
|
||||
function relod() {
|
||||
crashed = 1;
|
||||
window.gtfo = 1;
|
||||
location.href = location.href;
|
||||
}
|
||||
|
||||
|
||||
// error handler for mobile devices
|
||||
function esc(txt) {
|
||||
return txt.replace(/[&"<>]/g, function (c) {
|
||||
|
|
@ -296,7 +289,7 @@ function vis_exh(msg, url, lineNo, columnNo, error) {
|
|||
var x = ebi('exh_wipecfg');
|
||||
if (x) x.onclick = function () {
|
||||
localStorage.clear();
|
||||
relod();
|
||||
location.reload();
|
||||
};
|
||||
x = ebi('exh_ignex'); if (x) x.onclick = ignex;
|
||||
x = ebi('exh_ignexa'); if (x) x.onclick = ignexa;
|
||||
|
|
@ -1206,10 +1199,8 @@ function has(haystack, needle) {
|
|||
|
||||
function apop(arr, v) {
|
||||
var ofs = arr.indexOf(v);
|
||||
if (ofs !== -1) {
|
||||
if (ofs !== -1)
|
||||
arr.splice(ofs, 1);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -1475,7 +1466,7 @@ function Debounce(delay) {
|
|||
};
|
||||
|
||||
r.rm = function (fun) {
|
||||
while (apop(r.q, fun));
|
||||
apop(r.q, fun);
|
||||
};
|
||||
|
||||
r.run = function () {
|
||||
|
|
@ -1535,7 +1526,7 @@ var timer = (function () {
|
|||
};
|
||||
|
||||
r.rm = function (fun) {
|
||||
while (apop(r.q, fun));
|
||||
apop(r.q, fun);
|
||||
};
|
||||
|
||||
var doevents = function () {
|
||||
|
|
@ -2028,12 +2019,6 @@ var modal = (function () {
|
|||
q.shift()();
|
||||
}
|
||||
|
||||
r.die = function (html) {
|
||||
crashed = 1;
|
||||
if (r.busy)
|
||||
r.hide();
|
||||
_alert(lf2br(html), relod);
|
||||
};
|
||||
r.alert = function (html, cb, fun) {
|
||||
q.push(function () {
|
||||
_alert(lf2br(html), cb, fun);
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ body {
|
|||
<div style="display: none">
|
||||
<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_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="" />
|
||||
</form>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,52 +1,3 @@
|
|||
▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
|
||||
# 2026-0727-2202 `v1.20.19` SECURITY: fix FTP upload outside volume
|
||||
|
||||
## ⚠️ ATTN: this release fixes a vulnerability in FTP and FTPS (not SFTP)
|
||||
|
||||
the FTP-server (default-disabled) would allow uploading to any folder that the copyparty process had permission to write to, but with certain limitations; see [GHSA-phv8-wgjp-g4p9](https://github.com/9001/copyparty/security/advisories/GHSA-phv8-wgjp-g4p9)
|
||||
|
||||
## recent important news
|
||||
|
||||
* [v1.20.19 (2026-07-27)](https://github.com/9001/copyparty/releases/tag/v1.20.19) fixed an FTP-server vuln (upload outside defined volumes)
|
||||
* [v1.20.17 (2026-07-06)](https://github.com/9001/copyparty/releases/tag/v1.20.17) fixed a vuln when a volume has both filekeys and dirkeys enabled
|
||||
* [v1.20.17 (2026-07-06)](https://github.com/9001/copyparty/releases/tag/v1.20.17) introduced csp nonces, possibly breaking some javascript-based plugins
|
||||
|
||||
## 🧪 new features
|
||||
|
||||
* #1495 wopi integration -- edit office documents in the web-ui (thx @brandon-doornbos!) d57bb0c7 10db4236
|
||||
* currently only works with collabora online as wopi client #1574 and there's no docs/examples #1575
|
||||
* thumbnails: use embedded cover-image in videos when available 59524018
|
||||
* thumbnails: folder-thumbs can be disabled with `th-covers: no` (volflag or global) 856fada3
|
||||
* #1555 macos: add [--srch-nfkc](https://copyparty.eu/cli/#g-srch-nfkc) to fix searching for filenames / paths in CJK languages a7c99094
|
||||
* reduces search performance to around 30% when enabled
|
||||
* hooks: `xbr` / `xar` did not include old/new abspath as parameters; now they do c122e103
|
||||
|
||||
## 🩹 bugfixes
|
||||
|
||||
* ftp: fix [GHSA-phv8-wgjp-g4p9](https://github.com/9001/copyparty/security/advisories/GHSA-phv8-wgjp-g4p9) b331bb1c
|
||||
* #1563 moving files between volumes could fail depending on OS and underlying filesystem c70dc7ac
|
||||
* fix drag-drop uploading in certain glitchy KDE environments (thx @tilse!) daf144af
|
||||
* hooks: `xiu` crashed if the fork-flag was set (thx @stackxp!) aa862353
|
||||
* hooks: `xau` without json-flag would be given the wark (file hash) instead of the abspath bae77b90
|
||||
* shares: fix markdown-viewer (`?v`) inside shares 6a9437b7
|
||||
* ftp: fix logging from `xbu` hooks 9912a951
|
||||
* fix slow boot if a volume had lots of files in its toplevel folder cdb474c8
|
||||
* python2.7: fix multithreaded file-hashing 0f2040c3
|
||||
|
||||
## 🔧 other changes
|
||||
|
||||
* #1556 the libvips thumbnailer was demoted to last-fallback due to frequently using excessive amounts of ram ed0be42a
|
||||
* if [-lo](https://copyparty.eu/cli/#g-lo) points to an existing file, it will now be appended to instead of overwritten depending on [--rlo](https://copyparty.eu/cli/#g-help-rlo) b6abc33f
|
||||
* #1530 nixos: the nix package now uses `ffmpeg-headless` instead of `ffmpeg-full` (thx @nyakase!) fface524
|
||||
* slightly longer session cookie (was 20, now 24 chars) 537a99df
|
||||
* Windows-specific:
|
||||
* add a warning regarding the risks of DLL-hijacking when relevant bb40804f
|
||||
* fix some trivial PATH-related footguns cea97ac6
|
||||
* faster creation of sparse files bc45299b
|
||||
* fix detection of filesystem characteristics 6226858b
|
||||
|
||||
|
||||
|
||||
▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
|
||||
# 2026-0709-2254 `v1.20.18` bwrap off
|
||||
|
||||
|
|
|
|||
|
|
@ -1,54 +0,0 @@
|
|||
drop tcp mid-chunk and force browser to re-hs before it can be retried
|
||||
|
||||
ok with subchunks as of writing; 6thr, u2sz=1
|
||||
303M file: --s-wr-slp 0.02 --iobuf 131072
|
||||
33.5M file: --s-wr-slp 0.1 --iobuf 131072
|
||||
|
||||
--- a/copyparty/httpcli.py
|
||||
+++ b/copyparty/httpcli.py
|
||||
@@ -234,2 +234,3 @@ ZIP_XCODE_L = _build_zip_xcode()
|
||||
ZIP_XCODE_S = set(ZIP_XCODE_L)
|
||||
+hcblk = ""
|
||||
|
||||
@@ -3154,2 +3155,3 @@ class HttpCli(object):
|
||||
def handle_post_json(self) -> bool:
|
||||
+ global hcblk
|
||||
try:
|
||||
@@ -3248,2 +3250,3 @@ class HttpCli(object):
|
||||
# not to protect u2fh, but to prevent handshakes while files are closing
|
||||
+ hcblk = ""
|
||||
with self.u2mutex:
|
||||
@@ -3335,2 +3338,3 @@ class HttpCli(object):
|
||||
def handle_post_binary(self) -> bool:
|
||||
+ global hcblk
|
||||
try:
|
||||
@@ -3452,2 +3456,4 @@ class HttpCli(object):
|
||||
)
|
||||
+ if chash == hcblk:
|
||||
+ raise Exception("fhdsafhad re")
|
||||
post_sz, _, sha_b64 = hashcopy(
|
||||
@@ -3513,3 +3519,6 @@ class HttpCli(object):
|
||||
self.u2fh.put(path, f)
|
||||
- except:
|
||||
+ except Exception as ex:
|
||||
+ if "fhdsafhad" in str(ex):
|
||||
+ hcblk = chashes[0]
|
||||
+ self.sr.s.close()
|
||||
# maybe busted handle (eg. disk went full)
|
||||
--- a/copyparty/util.py
|
||||
+++ b/copyparty/util.py
|
||||
@@ -3414,2 +3414,3 @@ def eol_conv(
|
||||
|
||||
+hcn = 0
|
||||
def hashcopy(
|
||||
@@ -3421,2 +3422,4 @@ def hashcopy(
|
||||
) -> tuple[int, str, str]:
|
||||
+ global hcn
|
||||
+ print(hcn)
|
||||
if not hashobj:
|
||||
@@ -3429,2 +3432,5 @@ def hashcopy(
|
||||
|
||||
+ hcn += 1
|
||||
+ if hcn in (24, 121, 300, 600, 900):
|
||||
+ raise Exception("fhdsafhad " + str(tlen))
|
||||
hashobj.update(buf)
|
||||
|
|
@ -15,7 +15,7 @@ command -v gsort >/dev/null && {
|
|||
|
||||
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_NO_PARAMIKO=1
|
||||
|
|
|
|||
|
|
@ -74,7 +74,7 @@ def cnv(src):
|
|||
continue
|
||||
if '">uuid:' in 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
|
||||
if in_salt:
|
||||
in_salt -= 1
|
||||
|
|
|
|||
|
|
@ -10,8 +10,8 @@ command -v gsort >/dev/null && {
|
|||
( ( HLPTXT=CAT && cat || xsel -ob ) | sed -r '
|
||||
s`/home/ed/`~/`;
|
||||
s/uuid:[0-9a-f-]{36}/autogenerated/;
|
||||
s/(-salt (SALT|TXT).*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/+]{24}\)/\124-character-autogenerated)/;
|
||||
s/(-salt SALT.*default: )[0-9a-zA-Z/+]{40}\)/\140-character-autogenerated)/;
|
||||
s/(--name TXT.*default: )[^)]+/\1hostname/;
|
||||
s/(--hash-mt CORES.*default: )[0-9]+/\1numCores if 5 or less/;
|
||||
s/(--mtag-mt|th-mt)( CORES.*default: )[0-9]+/\1\2numCores/;
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ uname -s | grep WOW64 && m=64 || m=32
|
|||
uname -s | grep NT-10 && w10=1 || w7=1
|
||||
[ $w7 ] && [ -e up2k.sh ] && [ ! "$1" ] && ./up2k.sh
|
||||
|
||||
[ $w7 ] && pyv=37 || pyv=314
|
||||
[ $w7 ] && pyv=37 || pyv=313
|
||||
[ $w7 ] && sfx=en || sfx=sfx
|
||||
esuf=
|
||||
[ $w7 ] && [ $m = 32 ] && esuf=32
|
||||
|
|
@ -100,14 +100,9 @@ excl=(
|
|||
)
|
||||
[ $w10 ] && excl+=(
|
||||
_pyrepl
|
||||
bz2 # 51k
|
||||
compression.zstd # 195k
|
||||
concurrent.interpreters # 12k
|
||||
distutils
|
||||
setuptools
|
||||
ftplib # 23k
|
||||
PIL._avif
|
||||
PIL._imagingft # 861k (freetype)
|
||||
PIL.ImageQt
|
||||
PIL.ImageShow
|
||||
PIL.ImageTk
|
||||
|
|
|
|||
|
|
@ -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
|
||||
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
|
||||
d9d30ff960365307833425e340bf0a642fd54e35155f18ff1d9746bc1ecf8ecb864aad24948735e8fd994503b659dc8968e6ba337eeb4568217ebdf97dd292dd Git-2.46.2-32-bit.exe
|
||||
3253e86471e6f9fa85bfdb7684cd2f964ed6e35c6a4db87f81cca157c049bef43e66dfcae1e037b2fb904567b1e028aaeefe8983ba3255105df787406d2aa71e en_windows_7_professional_with_sp1_x86_dvd_u_677056.iso
|
||||
|
|
@ -21,15 +21,15 @@ ea73aa54cc6d5db20dfb127e54562dabf890e4cd6171a91b10a51af2bcfc76e1d64cbdce4546df2d
|
|||
479a63e14586ab2f2228208116fc149ed8ee7b1e4ff360754f5bda4bf765c61af2e04b5ef123976623d04df4976b7886e0445647269da81436bd0a7b5671d361 windows6.1-kb2533623-x86.msu
|
||||
ac96786e5d35882e0c5b724794329c9125c2b86ae7847f17acfc49f0d294312c6afc1c3f248655de3f0ccb4ca426d7957d02ba702f4a15e9fcd7e2c314e72c19 zipp-3.15.0-py3-none-any.whl
|
||||
# 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
|
||||
4f13070cc9241fa342deab4ebfac360565030580ff77b6e5f1951a64627621e5da4abfd30e1e46ca8bae2bb7dd4ff98141aff424142c9629a5876a61283962e5 virtio-win-0.1.285.iso
|
||||
9a7f40edc6f9209a2acd23793f3cbd6213c94f36064048cb8bf6eb04f1bdb2c2fe991cb09f77fe8b13e5cd85c618ef23573e79813b2fef899ab2f290cd129779 jinja2-3.1.6-py3-none-any.whl
|
||||
52adad30ac608695a6345468144048e5134696313ab775ef72f41ca082f8fa19953e2b9924fb5970f829bf9188183fe616b3942c4705eeddfe4ce223a5940186 markupsafe-3.0.3-cp314-cp314-win_amd64.whl
|
||||
c7668214758cb79a6ba0f7dbffb4b1b6f7cbf98d8ab6cbe77ab42c891916de9339bf17e3de3c3f2d20f6065b49f97c6d521419cc0a2b6c6b8940cf4317172d71 mutagen-1.48.1-py3-none-any.whl
|
||||
e9492eedaac7cfddde6d84389ef7df86c68275e8f29d838fc431d9bf379401bf6d82ec70ac02461995982613c80592cd8de74c16a3654d7d8fdc06f96057d6e7 packaging-26.3-py3-none-any.whl
|
||||
8ea9946d8484a6de833a97d3cd545c2b67475207944856281cffb2c513ab14bf50e2b9d31d7cca8f50a6ca8b6711fb8e45cbadabd566cab28f7d58ee058dc650 pillow-12.3.0-cp314-cp314-win_amd64.whl
|
||||
6f7ed5dc47ade9ad56f6f0499bc5d8eb485bd454d47189133297fa5aa633cf70dad18656cd673318c664f96097c39003d94d9c9703c8337d62840b4564695a04 pyinstaller-6.21.0-py3-none-win_amd64.whl
|
||||
0a7c1ae7063a2ee46a1c5c057fe73838d7dc5c59800e56037b3fb9dbd1c363915375f989160e44f21436e1071713682bb14473aa8112128beb670f5b110dc8e2 pyinstaller_hooks_contrib-2026.6-py3-none-any.whl
|
||||
a4adbefaa216571ade7fda561ed3404c762ff03029696ce1745fd4e23b066cb49b1fef99792ce3a594a04f9260a2f05a9e2f851bcc83ed4404f00ce05709c56a python-3.14.7-amd64.exe
|
||||
bfda5996cdf908edf971df38421ed154e2f2c58f38731ad3be22a88e78c933c64efd35fbc6daf82845dc0b59b5211e925038b430749b2c851887096578c68219 setuptools-83.0.0-py3-none-any.whl
|
||||
00731cfdd9d5c12efef04a7161c90c1e5ed1dc4677aa88a1d4054aff836f3430df4da5262ed4289c21637358a9e10e5df16f76743cbf5a29bb3a44b146c19cf3 MarkupSafe-3.0.2-cp313-cp313-win_amd64.whl
|
||||
8a6e2b13a2ec4ef914a5d62aad3db6464d45e525a82e07f6051ed10474eae959069e165dba011aefb8207cdfd55391d73d6f06362c7eb247b08763106709526e mutagen-1.47.0-py3-none-any.whl
|
||||
a726fb46cce24f781fc8b55a3e6dea0a884ebc3b2b400ea74aa02333699f4955a5dc1e2ec5927ac72f35a624401f3f3b442882ba1cc4cadaf9c88558b5b8bdae packaging-25.0-py3-none-any.whl
|
||||
efcb9da8e6f50cb5ea8e1d41e631e89b6f852b68a2568619a6ec151267e7fac94aba1e36317341c4fc5df822215b7ab9dd6b33cfb738e1b8423d4f0f6cff9525 pillow-12.3.0-cp313-cp313-win_amd64.whl
|
||||
b9b98714dfca6fa80b0b3f222965724d63be9c54d19435d1fe768e07016913d6db8d6e043fcb185b55a9bd6fe370a80cf961814fc096046a5f4640d99ed575ef pyinstaller-6.15.0-py3-none-win_amd64.whl
|
||||
cad0f7cf39de691813b1d4abc7d33f8bda99a87d9c5886039b814752e8690364150da26fb61b3e28d5698ff57a90e6dcd619ed2b64b04f72b5aadb75e201bdb0 pyinstaller_hooks_contrib-2025.8-py3-none-any.whl
|
||||
d0d23fd4ae8900764df664803844155994b253ed449b9ed54ea020fbfa7aaea8800ca1928e4a7f26b9278613f8bce27ef9a424cfcc4ca15551c7e4e1b78f3991 python-3.13.14-amd64.exe
|
||||
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 |
|
|
@ -29,21 +29,21 @@ uname -s | grep NT-10 && w10=1 || {
|
|||
w7=1; uname -s | grep WOW64 && w7x64=1 || w7x32=1
|
||||
}
|
||||
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
|
||||
pywin32_ctypes-0.2.3-py3-none-any.whl
|
||||
upx-5.2.0-win32.zip
|
||||
upx-5.0.2-win32.zip
|
||||
)
|
||||
[ $w10 ] && fns+=(
|
||||
jinja2-3.1.6-py3-none-any.whl
|
||||
markupsafe-3.0.3-cp314-cp314-win_amd64.whl
|
||||
mutagen-1.48.1-py3-none-any.whl
|
||||
packaging-26.3-py3-none-any.whl
|
||||
pillow-12.3.0-cp314-cp314-win_amd64.whl
|
||||
pyinstaller-6.21.0-py3-none-win_amd64.whl
|
||||
pyinstaller_hooks_contrib-2026.6-py3-none-any.whl
|
||||
python-3.14.7-amd64.exe
|
||||
setuptools-83.0.0-py3-none-any.whl
|
||||
MarkupSafe-3.0.2-cp313-cp313-win_amd64.whl
|
||||
mutagen-1.47.0-py3-none-any.whl
|
||||
packaging-25.0-py3-none-any.whl
|
||||
pillow-12.3.0-cp313-cp313-win_amd64.whl
|
||||
pyinstaller-6.15.0-py3-none-win_amd64.whl
|
||||
pyinstaller_hooks_contrib-2025.8-py3-none-any.whl
|
||||
python-3.13.14-amd64.exe
|
||||
setuptools-80.9.0-py3-none-any.whl
|
||||
)
|
||||
[ $w7 ] && fns+=(
|
||||
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 ]================================
|
||||
uname -s | grep NT-10 && w10=1 || w7=1
|
||||
[ $w7 ] && pyv=37 || pyv=314
|
||||
[ $w7 ] && pyv=37 || pyv=313
|
||||
appd=$(cygpath.exe "$APPDATA")
|
||||
cd ~/Downloads &&
|
||||
yes | unzip upx-*-win32.zip &&
|
||||
|
|
@ -91,7 +91,7 @@ mv upx-*/upx.exe . &&
|
|||
python -m ensurepip &&
|
||||
{ [ $w10 ] || python -m pip install --user -U pip-*.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; } &&
|
||||
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 &&
|
||||
|
|
|
|||
Binary file not shown.
|
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.4 KiB |
|
|
@ -71,7 +71,6 @@ Ls.hmn = {
|
|||
["ctrl-V", "paste (move/copy) here"],
|
||||
["Y", "download selected"],
|
||||
["F2", "rename selected"],
|
||||
["F4", "update/refresh"],
|
||||
|
||||
"file-list-sel",
|
||||
["space", "toggle file selection"],
|
||||
|
|
|
|||
Loading…
Reference in a new issue