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
|
*.png binary
|
||||||
*.gif binary
|
*.gif binary
|
||||||
*.ico binary
|
|
||||||
*.gz 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
|
* [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
|
||||||
|
|
@ -138,7 +137,6 @@ built in Norway 🇳🇴 with contributions from [not-norway](https://github.com
|
||||||
* [client-side](#client-side) - when uploading files
|
* [client-side](#client-side) - when uploading files
|
||||||
* [security](#security) - there is a [discord server](https://discord.gg/25J8CdTT6G) with announcements
|
* [security](#security) - there is a [discord server](https://discord.gg/25J8CdTT6G) with announcements
|
||||||
* [gotchas](#gotchas) - behavior that might be unexpected
|
* [gotchas](#gotchas) - behavior that might be unexpected
|
||||||
* [csp nonce](#csp-nonce) - unauthorized javascript is blocked
|
|
||||||
* [cors](#cors) - cross-site request config
|
* [cors](#cors) - cross-site request config
|
||||||
* [filekeys](#filekeys) - prevent filename bruteforcing
|
* [filekeys](#filekeys) - prevent filename bruteforcing
|
||||||
* [dirkeys](#dirkeys) - share specific folders in a volume
|
* [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 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 +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
|
## upload rules
|
||||||
|
|
||||||
set upload rules using volflags, some examples:
|
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 :^)
|
* 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
|
## cors
|
||||||
|
|
||||||
cross-site request config
|
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("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")
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -285,7 +285,6 @@ in
|
||||||
|
|
||||||
environment = {
|
environment = {
|
||||||
PYTHONUNBUFFERED = "true";
|
PYTHONUNBUFFERED = "true";
|
||||||
NIX_SSL_CERT_FILE = "${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt";
|
|
||||||
XDG_CONFIG_HOME = externalStateDir;
|
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.
|
# 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.18"
|
||||||
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=("ea1347a2e30b4261282ac4609c9b844768a925eed717ac8af1ff87a8a326508e")
|
||||||
|
|
||||||
build() {
|
build() {
|
||||||
cd "${srcdir}/${pkgname}-${pkgver}/copyparty/web"
|
cd "${srcdir}/${pkgname}-${pkgver}/copyparty/web"
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
|
|
||||||
pkgname=copyparty
|
pkgname=copyparty
|
||||||
pkgver=1.20.20
|
pkgver=1.20.18
|
||||||
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=("ea1347a2e30b4261282ac4609c9b844768a925eed717ac8af1ff87a8a326508e")
|
||||||
|
|
||||||
build() {
|
build() {
|
||||||
cd "${srcdir}/${pkgname}-${pkgver}/copyparty/web"
|
cd "${srcdir}/${pkgname}-${pkgver}/copyparty/web"
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
{
|
{
|
||||||
"url": "https://github.com/9001/copyparty/releases/download/v1.20.20/copyparty-1.20.20.tar.gz",
|
"url": "https://github.com/9001/copyparty/releases/download/v1.20.18/copyparty-1.20.18.tar.gz",
|
||||||
"version": "1.20.20",
|
"version": "1.20.18",
|
||||||
"hash": "sha256-ox+eSkPlZgh2XhCDUxQHhNUBgcN+cF6c6O2fOJn1ffo="
|
"hash": "sha256-6hNHouMLQmEoKsRgnJuER2ipJe7XF6yK8f+HqKMmUI4="
|
||||||
}
|
}
|
||||||
|
|
@ -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"
|
||||||
|
|
|
||||||
|
|
@ -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,6 @@ 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-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 +1736,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 +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("--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)")
|
||||||
|
|
@ -1880,25 +1832,14 @@ def add_thumbnail(ap):
|
||||||
# https://github.com/libvips/libvips
|
# https://github.com/libvips/libvips
|
||||||
# https://stackoverflow.com/a/47612661
|
# https://stackoverflow.com/a/47612661
|
||||||
# ffmpeg -hide_banner -demuxers | awk '/^ D /{print$2}' | while IFS= read -r x; do ffmpeg -hide_banner -h demuxer=$x; done | grep -E '^Demuxer |extensions:'
|
# ffmpeg -hide_banner -demuxers | awk '/^ D /{print$2}' | while IFS= read -r x; do ffmpeg -hide_banner -h demuxer=$x; done | grep -E '^Demuxer |extensions:'
|
||||||
ap2.add_argument("--th-r-pil", metavar="T,T", type=u, default="avif,avifs,blp,bmp,cbz,dcx,dds,dib,emf,eps,epub,fits,flc,fli,fpx,gif,heic,heics,heif,heifs,icns,ico,im,j2p,j2k,jp2,jpeg,jpg,jpx,jxl,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-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-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-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", 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 +2077,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 +2113,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 +2216,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()
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
# coding: utf-8
|
# coding: utf-8
|
||||||
|
|
||||||
VERSION = (1, 20, 20)
|
VERSION = (1, 20, 19)
|
||||||
CODENAME = "sftp is fine too"
|
CODENAME = "sftp is fine too"
|
||||||
BUILD_DT = (2026, 8, 4)
|
BUILD_DT = (2026, 7, 27)
|
||||||
|
|
||||||
S_VERSION = ".".join(map(str, VERSION))
|
S_VERSION = ".".join(map(str, VERSION))
|
||||||
S_BUILD_DT = "{0:04d}-{1:02d}-{2:02d}".format(*BUILD_DT)
|
S_BUILD_DT = "{0:04d}-{1:02d}-{2:02d}".format(*BUILD_DT)
|
||||||
|
|
|
||||||
|
|
@ -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():
|
||||||
|
|
@ -2662,11 +2660,10 @@ class AuthSrv(object):
|
||||||
if head_s and not head_s.endswith("\n"):
|
if head_s and not head_s.endswith("\n"):
|
||||||
head_s += "\n"
|
head_s += "\n"
|
||||||
|
|
||||||
zs2 = "Content-Security-Policy: %s\r\n"
|
|
||||||
zs = vol.flags.get("csp_ui", "")
|
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", "")
|
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"
|
zs = "X-Content-Type-Options: nosniff\r\n"
|
||||||
if "norobots" in vol.flags:
|
if "norobots" in vol.flags:
|
||||||
|
|
@ -2764,8 +2761,7 @@ class AuthSrv(object):
|
||||||
|
|
||||||
for k in drop:
|
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)'
|
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)
|
vol.flags.pop(k)
|
||||||
|
|
||||||
zi = vol.flags.get("lifetime") or 0
|
zi = vol.flags.get("lifetime") or 0
|
||||||
|
|
@ -4081,11 +4077,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
|
||||||
|
|
|
||||||
|
|
@ -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)",
|
||||||
|
|
|
||||||
|
|
@ -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,9 +1657,7 @@ class HttpCli(object):
|
||||||
"expires": time.time() + self.args.wopi_ttl,
|
"expires": time.time() + self.args.wopi_ttl,
|
||||||
}
|
}
|
||||||
|
|
||||||
assert atoken and session # type: ignore # !rm
|
xml = "?"
|
||||||
|
|
||||||
xml = url = "?"
|
|
||||||
try:
|
try:
|
||||||
from .dxml import parse_xml
|
from .dxml import parse_xml
|
||||||
|
|
||||||
|
|
@ -1676,18 +1673,14 @@ class HttpCli(object):
|
||||||
|
|
||||||
uo_kw["context"] = ctx
|
uo_kw["context"] = ctx
|
||||||
|
|
||||||
wopi_urls = dict(x.lower().split("=", 1) for x in self.args.wopi_urls or [])
|
url = self.args.wopi_url.rstrip("/") + "/hosting/discovery"
|
||||||
url = wopi_urls.get(self.host.lower(), self.args.wopi_url).rstrip("/")
|
|
||||||
url += "/hosting/discovery"
|
|
||||||
buf = urlopen(url, **uo_kw).read()
|
buf = urlopen(url, **uo_kw).read()
|
||||||
xml = buf.decode("ascii", "replace").lower()
|
xml = buf.decode("ascii", "replace").lower()
|
||||||
enc = self.get_xml_enc(xml)
|
enc = self.get_xml_enc(xml)
|
||||||
xml = buf.decode(enc, "replace")
|
xml = buf.decode(enc, "replace")
|
||||||
xroot = parse_xml(xml)
|
xroot = parse_xml(xml)
|
||||||
ext = vpath.split(".")[-1]
|
ext = vpath.split(".")[-1]
|
||||||
url = xroot.find(
|
url = xroot.find(".//action[@ext='%s'][@urlsrc]" % (ext,)).get("urlsrc")
|
||||||
".//action[@ext='%s'][@name='edit'][@urlsrc]" % (ext,)
|
|
||||||
).get("urlsrc")
|
|
||||||
if not url.endswith(("?", "&")):
|
if not url.endswith(("?", "&")):
|
||||||
url += "&" if "?" in url else "?"
|
url += "&" if "?" in url else "?"
|
||||||
url += "WOPISrc="
|
url += "WOPISrc="
|
||||||
|
|
@ -1698,8 +1691,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(
|
||||||
|
|
@ -1707,7 +1699,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")
|
||||||
|
|
@ -7449,18 +7441,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
|
||||||
|
|
@ -7630,26 +7624,6 @@ class HttpCli(object):
|
||||||
(fe["sz"], fe["tags"][".files"]) = hit
|
(fe["sz"], fe["tags"][".files"]) = hit
|
||||||
except:
|
except:
|
||||||
pass # 404 or mojibake
|
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]
|
taglist = [k for k in lmte if k in tagset]
|
||||||
else:
|
else:
|
||||||
|
|
@ -7895,10 +7869,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
|
||||||
|
|
|
||||||
|
|
@ -194,12 +194,6 @@ def au_unpk(
|
||||||
log(t)
|
log(t)
|
||||||
fi = zf.open(using)
|
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":
|
elif pk == "epub":
|
||||||
fi = get_cover_from_epub(log, abspath)
|
fi = get_cover_from_epub(log, abspath)
|
||||||
assert fi # !rm
|
assert fi # !rm
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -352,7 +352,7 @@ RE_MEMAVAIL = re.compile("^MemAvailable:.* kB")
|
||||||
|
|
||||||
if PY2:
|
if PY2:
|
||||||
|
|
||||||
def umktrans(s1: str, s2: str):
|
def umktrans(s1, s2):
|
||||||
return {ord(c1): ord(c2) for c1, c2 in zip(s1, s2)}
|
return {ord(c1): ord(c2) for c1, c2 in zip(s1, s2)}
|
||||||
|
|
||||||
else:
|
else:
|
||||||
|
|
@ -2150,8 +2150,7 @@ class MultipartParser(object):
|
||||||
boundary = get_boundary(self.headers)
|
boundary = get_boundary(self.headers)
|
||||||
if boundary.startswith('"') and boundary.endswith('"'):
|
if boundary.startswith('"') and boundary.endswith('"'):
|
||||||
boundary = boundary[1:-1] # dillo uses quotes
|
boundary = boundary[1:-1] # dillo uses quotes
|
||||||
if len(boundary) > 72: # rfc-2046 <=70
|
self.log("boundary=%r" % (boundary,))
|
||||||
raise Pebkac(400, "boundary 2big: %d" % (len(boundary),))
|
|
||||||
|
|
||||||
# spec says there might be junk before the first boundary,
|
# spec says there might be junk before the first boundary,
|
||||||
# can't have the leading \r\n if that's not the case
|
# 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>
|
<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>
|
<a href="#" id="repl">π</a>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -46,7 +46,6 @@ if (1)
|
||||||
["ctrl-V", "paste (move/copy) here"],
|
["ctrl-V", "paste (move/copy) here"],
|
||||||
["Y", "download selected"],
|
["Y", "download selected"],
|
||||||
["F2", "rename selected"],
|
["F2", "rename selected"],
|
||||||
["F4", "update/refresh"],
|
|
||||||
|
|
||||||
"file-list-sel",
|
"file-list-sel",
|
||||||
["space", "toggle file selection"],
|
["space", "toggle file selection"],
|
||||||
|
|
@ -758,7 +757,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 +1245,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 +5693,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)),
|
||||||
|
|
@ -6439,9 +6438,6 @@ var ahotkeys = function (e) {
|
||||||
if (k == 'F2')
|
if (k == 'F2')
|
||||||
return fileman.rename();
|
return fileman.rename();
|
||||||
|
|
||||||
if (k == 'F4')
|
|
||||||
return treectl.goto();
|
|
||||||
|
|
||||||
if (!treectl.hidden && (!sh || !thegrid.en)) {
|
if (!treectl.hidden && (!sh || !thegrid.en)) {
|
||||||
if (kl == 'a')
|
if (kl == 'a')
|
||||||
return QS('#twig').click();
|
return QS('#twig').click();
|
||||||
|
|
@ -7696,7 +7692,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 +
|
||||||
|
|
@ -7790,7 +7786,6 @@ var treectl = (function () {
|
||||||
|
|
||||||
r.hydrate = function () {
|
r.hydrate = function () {
|
||||||
qsr('#bbsw');
|
qsr('#bbsw');
|
||||||
qsr('#js_bork');
|
|
||||||
srvinf = ebi('srv_info').innerHTML.slice(6, -7);
|
srvinf = ebi('srv_info').innerHTML.slice(6, -7);
|
||||||
if (ls0 === null) {
|
if (ls0 === null) {
|
||||||
r.ls_cb = showfile.addlinks;
|
r.ls_cb = showfile.addlinks;
|
||||||
|
|
@ -8626,7 +8621,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();
|
||||||
|
|
@ -8877,7 +8872,7 @@ var msel = (function () {
|
||||||
r.evsel = function (e, fun) {
|
r.evsel = function (e, fun) {
|
||||||
ev(e);
|
ev(e);
|
||||||
r.so = r.pr = null;
|
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++)
|
for (var a = 0, aa = trs.length; a < aa; a++)
|
||||||
clmod(trs[a], 'sel', fun);
|
clmod(trs[a], 'sel', fun);
|
||||||
r.selui();
|
r.selui();
|
||||||
|
|
@ -9337,7 +9332,6 @@ function sandbox(tgt, rules, allow, cls, html) {
|
||||||
fr.setAttribute('sandbox', rules ? 'allow-' + rules.replace(/ /g, ' allow-') : '');
|
fr.setAttribute('sandbox', rules ? 'allow-' + rules.replace(/ /g, ' allow-') : '');
|
||||||
fr.setAttribute('allow', allow);
|
fr.setAttribute('allow', allow);
|
||||||
fr.setAttribute('srcdoc', html);
|
fr.setAttribute('srcdoc', html);
|
||||||
tgt.innerHTML = '';
|
|
||||||
tgt.appendChild(fr);
|
tgt.appendChild(fr);
|
||||||
treectl.sb_msg = true;
|
treectl.sb_msg = true;
|
||||||
return true;
|
return true;
|
||||||
|
|
@ -10076,7 +10070,7 @@ function reload_browser() {
|
||||||
function sel_start(e) {
|
function sel_start(e) {
|
||||||
if (e.button !== 0 && e.type !== 'touchstart') return;
|
if (e.button !== 0 && e.type !== 'touchstart') return;
|
||||||
if (!thegrid.en || !treectl.dsel) 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'))
|
if (e.target.closest('#gfiles'))
|
||||||
ebi('gfiles').style.userSelect = "none";
|
ebi('gfiles').style.userSelect = "none";
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -39,7 +39,6 @@ Ls.chi = {
|
||||||
["ctrl-V", "粘贴(移动/复制)到此处"],
|
["ctrl-V", "粘贴(移动/复制)到此处"],
|
||||||
["Y", "下载选中项"],
|
["Y", "下载选中项"],
|
||||||
["F2", "重命名选中项"],
|
["F2", "重命名选中项"],
|
||||||
["F4", "刷新"], //m
|
|
||||||
|
|
||||||
"文件列表选择",
|
"文件列表选择",
|
||||||
["space", "切换文件选择"],
|
["space", "切换文件选择"],
|
||||||
|
|
|
||||||
|
|
@ -39,7 +39,6 @@ Ls.cze = {
|
||||||
["ctrl-V", "vložit (přesunout/kopírovat) zde"],
|
["ctrl-V", "vložit (přesunout/kopírovat) zde"],
|
||||||
["Y", "stáhnout vybrané"],
|
["Y", "stáhnout vybrané"],
|
||||||
["F2", "přejmenovat vybrané"],
|
["F2", "přejmenovat vybrané"],
|
||||||
["F4", "obnovit"], //m
|
|
||||||
|
|
||||||
"výběr souborů",
|
"výběr souborů",
|
||||||
["space", "přepnout výběr souboru"],
|
["space", "přepnout výběr souboru"],
|
||||||
|
|
|
||||||
|
|
@ -39,7 +39,6 @@ Ls.deu = {
|
||||||
["STRG-V", "Zwischenablage hier einfügen"],
|
["STRG-V", "Zwischenablage hier einfügen"],
|
||||||
["Y", "Auswahl herunterladen"],
|
["Y", "Auswahl herunterladen"],
|
||||||
["F2", "Auswahl umbenennen"],
|
["F2", "Auswahl umbenennen"],
|
||||||
["F4", "Aktualisieren"], //m
|
|
||||||
|
|
||||||
"file-list-sel",
|
"file-list-sel",
|
||||||
["LEER", "Dateiauswahl aktivieren"],
|
["LEER", "Dateiauswahl aktivieren"],
|
||||||
|
|
|
||||||
|
|
@ -39,7 +39,6 @@ Ls.epo = {
|
||||||
["stir-V", "alglui (movi/kopii) ĉi tien"],
|
["stir-V", "alglui (movi/kopii) ĉi tien"],
|
||||||
["Y", "elŝuti elektitajn"],
|
["Y", "elŝuti elektitajn"],
|
||||||
["F2", "alinomi elektitajn"],
|
["F2", "alinomi elektitajn"],
|
||||||
["F4", "refreŝigi"], //m
|
|
||||||
|
|
||||||
"file-list-sel",
|
"file-list-sel",
|
||||||
["spacoklavo", "baskuli elektadon de dosieroj"],
|
["spacoklavo", "baskuli elektadon de dosieroj"],
|
||||||
|
|
|
||||||
|
|
@ -39,7 +39,6 @@ Ls.fin = {
|
||||||
["ctrl-V", "siirrä tai kopioi tähän"],
|
["ctrl-V", "siirrä tai kopioi tähän"],
|
||||||
["Y", "lataa valitut"],
|
["Y", "lataa valitut"],
|
||||||
["F2", "uudelleennimeä valitut"],
|
["F2", "uudelleennimeä valitut"],
|
||||||
["F4", "päivitä"], //m
|
|
||||||
|
|
||||||
"file-list-sel",
|
"file-list-sel",
|
||||||
["space", "vaihda tiedostonvalintatilaan"],
|
["space", "vaihda tiedostonvalintatilaan"],
|
||||||
|
|
|
||||||
|
|
@ -39,7 +39,6 @@ Ls.fra = {
|
||||||
["ctrl-V", "coller (déplacer/copier) ici"],
|
["ctrl-V", "coller (déplacer/copier) ici"],
|
||||||
["Y", "télécharger la sélection"],
|
["Y", "télécharger la sélection"],
|
||||||
["F2", "renomer la sélection"],
|
["F2", "renomer la sélection"],
|
||||||
["F4", "actualiser"], //m
|
|
||||||
|
|
||||||
"file-list-sel",
|
"file-list-sel",
|
||||||
["Espace", "activer la sélection de fichiers"],
|
["Espace", "activer la sélection de fichiers"],
|
||||||
|
|
|
||||||
|
|
@ -39,7 +39,6 @@ Ls.grc = {
|
||||||
["ctrl-V", "επικόλληση (μετακίνηση/αντιγραφή) εδώ"],
|
["ctrl-V", "επικόλληση (μετακίνηση/αντιγραφή) εδώ"],
|
||||||
["Y", "λήψη επιλεγμένων"],
|
["Y", "λήψη επιλεγμένων"],
|
||||||
["F2", "μετονομασία επιλεγμένων"],
|
["F2", "μετονομασία επιλεγμένων"],
|
||||||
["F4", "ανανέωση"], //m
|
|
||||||
|
|
||||||
"λίστα αρχείων",
|
"λίστα αρχείων",
|
||||||
["space", "εναλλαγή επιλογής αρχείου"],
|
["space", "εναλλαγή επιλογής αρχείου"],
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,6 @@ Ls.hun = {
|
||||||
['ctrl-V', 'beillesztés ide'],
|
['ctrl-V', 'beillesztés ide'],
|
||||||
['Y', 'kijelöltek letöltése'],
|
['Y', 'kijelöltek letöltése'],
|
||||||
['F2', 'átnevezés'],
|
['F2', 'átnevezés'],
|
||||||
["F4", "frissítés"], //m
|
|
||||||
|
|
||||||
'kijelölés',
|
'kijelölés',
|
||||||
['space', 'fájl kijelölése'],
|
['space', 'fájl kijelölése'],
|
||||||
|
|
|
||||||
|
|
@ -39,7 +39,6 @@ Ls.ita = {
|
||||||
["ctrl-V", "incolla (sposta/copia) qui"],
|
["ctrl-V", "incolla (sposta/copia) qui"],
|
||||||
["Y", "scarica selezionati"],
|
["Y", "scarica selezionati"],
|
||||||
["F2", "rinomina selezionati"],
|
["F2", "rinomina selezionati"],
|
||||||
["F4", "aggiorna"], //m
|
|
||||||
|
|
||||||
"file-list-sel",
|
"file-list-sel",
|
||||||
["spazio", "alterna selezione file"],
|
["spazio", "alterna selezione file"],
|
||||||
|
|
|
||||||
|
|
@ -39,7 +39,6 @@ Ls.jpn = {
|
||||||
["ctrl-V", "ここに貼り付け(移動/コピー)"],
|
["ctrl-V", "ここに貼り付け(移動/コピー)"],
|
||||||
["Y", "選択した項目をダウンロード"],
|
["Y", "選択した項目をダウンロード"],
|
||||||
["F2", "選択した項目の名前を変更"],
|
["F2", "選択した項目の名前を変更"],
|
||||||
["F4", "更新"], //m
|
|
||||||
|
|
||||||
"file-list-sel",
|
"file-list-sel",
|
||||||
["space", "ファイル選択の切り替え"],
|
["space", "ファイル選択の切り替え"],
|
||||||
|
|
|
||||||
|
|
@ -39,7 +39,6 @@ Ls.kor = {
|
||||||
["ctrl-V", "여기에 붙여넣기 (이동/복사)"],
|
["ctrl-V", "여기에 붙여넣기 (이동/복사)"],
|
||||||
["Y", "선택 항목 다운로드"],
|
["Y", "선택 항목 다운로드"],
|
||||||
["F2", "선택 항목 이름 바꾸기"],
|
["F2", "선택 항목 이름 바꾸기"],
|
||||||
["F4", "새로 고침"], //m
|
|
||||||
|
|
||||||
"파일 목록 선택",
|
"파일 목록 선택",
|
||||||
["space", "파일 선택/해제"],
|
["space", "파일 선택/해제"],
|
||||||
|
|
|
||||||
|
|
@ -39,7 +39,6 @@ Ls.nld = {
|
||||||
["ctrl-V", "Hier plakken (verplaatsen/kopieëren)"],
|
["ctrl-V", "Hier plakken (verplaatsen/kopieëren)"],
|
||||||
["Y", "Download geselecteerde"],
|
["Y", "Download geselecteerde"],
|
||||||
["F2", "Hernoem geselecteerde"],
|
["F2", "Hernoem geselecteerde"],
|
||||||
["F4", "vernieuwen"], //m
|
|
||||||
|
|
||||||
"bestand-lijst-selectie",
|
"bestand-lijst-selectie",
|
||||||
["space", "wissel bestand selectie"],
|
["space", "wissel bestand selectie"],
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,6 @@ Ls.nno = {
|
||||||
["ctrl-V", "lim inn (flytt/kopiér)"],
|
["ctrl-V", "lim inn (flytt/kopiér)"],
|
||||||
["Y", "last ned valde"],
|
["Y", "last ned valde"],
|
||||||
["F2", "endre namn på valde"],
|
["F2", "endre namn på valde"],
|
||||||
["F4", "oppdater"],
|
|
||||||
|
|
||||||
"filmarkering",
|
"filmarkering",
|
||||||
["space", "markér fil"],
|
["space", "markér fil"],
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,6 @@ Ls.nor = {
|
||||||
["ctrl-V", "lim inn (flytt/kopiér)"],
|
["ctrl-V", "lim inn (flytt/kopiér)"],
|
||||||
["Y", "last ned valgte"],
|
["Y", "last ned valgte"],
|
||||||
["F2", "endre navn på valgte"],
|
["F2", "endre navn på valgte"],
|
||||||
["F4", "oppdater"],
|
|
||||||
|
|
||||||
"filmarkering",
|
"filmarkering",
|
||||||
["space", "marker fil"],
|
["space", "marker fil"],
|
||||||
|
|
|
||||||
|
|
@ -39,7 +39,6 @@ Ls.pol = {
|
||||||
["ctrl-V", "wklej (przenieś/skopiuj) tutaj"],
|
["ctrl-V", "wklej (przenieś/skopiuj) tutaj"],
|
||||||
["Y", "pobierz zaznaczone"],
|
["Y", "pobierz zaznaczone"],
|
||||||
["F2", "zmień nazwę zaznaczonych"],
|
["F2", "zmień nazwę zaznaczonych"],
|
||||||
["F4", "odśwież"], //m
|
|
||||||
|
|
||||||
"file-list-sel",
|
"file-list-sel",
|
||||||
["spacja", "przełącz zaznaczanie plików"],
|
["spacja", "przełącz zaznaczanie plików"],
|
||||||
|
|
|
||||||
|
|
@ -39,7 +39,6 @@ Ls.por = {
|
||||||
["ctrl-V", "colar (mover/copiar) aqui"],
|
["ctrl-V", "colar (mover/copiar) aqui"],
|
||||||
["Y", "baixar selecionado"],
|
["Y", "baixar selecionado"],
|
||||||
["F2", "renomear selecionado"],
|
["F2", "renomear selecionado"],
|
||||||
["F4", "atualizar"], //m
|
|
||||||
|
|
||||||
"seleção de lista de arquivos",
|
"seleção de lista de arquivos",
|
||||||
["espaço", "alternar seleção de arquivo"],
|
["espaço", "alternar seleção de arquivo"],
|
||||||
|
|
|
||||||
|
|
@ -39,7 +39,6 @@ Ls.rus = {
|
||||||
["ctrl-V", "вставить (переместить/копировать) сюда"],
|
["ctrl-V", "вставить (переместить/копировать) сюда"],
|
||||||
["Y", "скачать выделенное"],
|
["Y", "скачать выделенное"],
|
||||||
["F2", "переименовать выделенное"],
|
["F2", "переименовать выделенное"],
|
||||||
["F4", "обновить"], //m
|
|
||||||
|
|
||||||
"выделение файлов",
|
"выделение файлов",
|
||||||
["пробел", "выделить/снять выделение с текущего файла"],
|
["пробел", "выделить/снять выделение с текущего файла"],
|
||||||
|
|
|
||||||
|
|
@ -39,7 +39,6 @@ Ls.spa = {
|
||||||
["ctrl-V", "pegar (mover/copiar) aquí"],
|
["ctrl-V", "pegar (mover/copiar) aquí"],
|
||||||
["Y", "descargar seleccionados"],
|
["Y", "descargar seleccionados"],
|
||||||
["F2", "renombrar seleccionados"],
|
["F2", "renombrar seleccionados"],
|
||||||
["F4", "actualizar"], //m
|
|
||||||
|
|
||||||
"selección en lista de archivos",
|
"selección en lista de archivos",
|
||||||
["space", "alternar selección de archivo"],
|
["space", "alternar selección de archivo"],
|
||||||
|
|
|
||||||
|
|
@ -39,7 +39,6 @@ Ls.swe = {
|
||||||
["ctrl-V", "klistra in (kopiera/flytta) hit"],
|
["ctrl-V", "klistra in (kopiera/flytta) hit"],
|
||||||
["Y", "ladda ner urval"],
|
["Y", "ladda ner urval"],
|
||||||
["F2", "byt namn på urval"],
|
["F2", "byt namn på urval"],
|
||||||
["F4", "uppdatera"], //m
|
|
||||||
|
|
||||||
"välja filer",
|
"välja filer",
|
||||||
["Blanksteg", "växla val av fil"],
|
["Blanksteg", "växla val av fil"],
|
||||||
|
|
|
||||||
|
|
@ -39,7 +39,6 @@ Ls.tur = {
|
||||||
["ctrl-V", "buraya yapıştır (taşı/kopyala)"],
|
["ctrl-V", "buraya yapıştır (taşı/kopyala)"],
|
||||||
["Y", "seçileni indir"],
|
["Y", "seçileni indir"],
|
||||||
["F2", "seçileni yeniden adlandır"],
|
["F2", "seçileni yeniden adlandır"],
|
||||||
["F4", "yenile"], //m
|
|
||||||
|
|
||||||
"dosya yönetimi seçimleri",
|
"dosya yönetimi seçimleri",
|
||||||
["boşluk", "seçimi değiştir"],
|
["boşluk", "seçimi değiştir"],
|
||||||
|
|
|
||||||
|
|
@ -39,7 +39,6 @@ Ls.ukr = {
|
||||||
["ctrl-V", "вставити (перемістити/копіювати) сюди"],
|
["ctrl-V", "вставити (перемістити/копіювати) сюди"],
|
||||||
["Y", "завантажити вибране"],
|
["Y", "завантажити вибране"],
|
||||||
["F2", "перейменувати вибране"],
|
["F2", "перейменувати вибране"],
|
||||||
["F4", "оновити"], //m
|
|
||||||
|
|
||||||
"вибір файлів у списку",
|
"вибір файлів у списку",
|
||||||
["space", "перемкнути вибір файлу"],
|
["space", "перемкнути вибір файлу"],
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,6 @@ Ls.vie = {
|
||||||
["ctrl-V", "dán (di chuyển/sao chép) tại đây"],
|
["ctrl-V", "dán (di chuyển/sao chép) tại đây"],
|
||||||
["Y", "tải xuống mục đã chọn"],
|
["Y", "tải xuống mục đã chọn"],
|
||||||
["F2", "đổi tên mục đã chọn"],
|
["F2", "đổi tên mục đã chọn"],
|
||||||
["F4", "làm mới"], //m
|
|
||||||
|
|
||||||
"file-list-sel",
|
"file-list-sel",
|
||||||
["space", "chuyển đổi chọn tệp"],
|
["space", "chuyển đổi chọn tệp"],
|
||||||
|
|
|
||||||
|
|
@ -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),
|
||||||
|
|
@ -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),
|
var pvis = new U2pvis("bz", '#u2cards', uc, st),
|
||||||
donut = new Donut(uc, st);
|
donut = new Donut(uc, st);
|
||||||
|
|
||||||
|
|
@ -2525,7 +2512,6 @@ function up2k_init(subtle) {
|
||||||
keepalive = t.keepalive,
|
keepalive = t.keepalive,
|
||||||
me = Date.now();
|
me = Date.now();
|
||||||
|
|
||||||
while (apop(st.todo.handshake, t));
|
|
||||||
if (t.done)
|
if (t.done)
|
||||||
return console.log('done; skip hs', t.name, t);
|
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);
|
cbd.push(a == cdr_idx ? cdr_sz : chunksize);
|
||||||
}
|
}
|
||||||
|
|
||||||
unqueue_up(t);
|
t.postlist = [];
|
||||||
t.wark = response.wark;
|
t.wark = response.wark;
|
||||||
var missing = response.hash;
|
var missing = response.hash;
|
||||||
for (var a = 0; a < missing.length; a++) {
|
for (var a = 0; a < missing.length; a++) {
|
||||||
|
|
@ -2682,7 +2668,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++) {
|
||||||
|
|
@ -2871,8 +2857,9 @@ function up2k_init(subtle) {
|
||||||
if (upf.sprs)
|
if (upf.sprs)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
if (upq_has(st.busy.upload, upf))
|
for (var a = 0, aa = st.busy.upload.length; a < aa; a++)
|
||||||
return false;
|
if (st.busy.upload[a].nfile == upt.nfile)
|
||||||
|
return false;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
@ -2883,8 +2870,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);
|
||||||
|
|
@ -2933,7 +2919,8 @@ function up2k_init(subtle) {
|
||||||
var txt = unpre((xhr.response && xhr.response.err) || xhr.responseText);
|
var txt = unpre((xhr.response && xhr.response.err) || xhr.responseText);
|
||||||
if (txt.indexOf('upload blocked by x') + 1) {
|
if (txt.indexOf('upload blocked by x') + 1) {
|
||||||
apop(st.busy.upload, upt);
|
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, 1, "ERROR");
|
||||||
pvis.seth(t.n, 2, txt.split(/\n/)[0]);
|
pvis.seth(t.n, 2, txt.split(/\n/)[0]);
|
||||||
pvis.move(t.n, 'ng');
|
pvis.move(t.n, 'ng');
|
||||||
|
|
@ -2956,13 +2943,8 @@ function up2k_init(subtle) {
|
||||||
st.etac.u++;
|
st.etac.u++;
|
||||||
st.etac.t++;
|
st.etac.t++;
|
||||||
}
|
}
|
||||||
else if (xhr.status == 413) {
|
else if (txt.indexOf('already got that') + 1 ||
|
||||||
modal.die('bad server config; see "u2sz" in readme');
|
txt.indexOf('already being written') + 1) {
|
||||||
}
|
|
||||||
else if (txt.indexOf('already got that') + 1) {
|
|
||||||
unqueue_up(t);
|
|
||||||
}
|
|
||||||
else if (txt.indexOf('already being written') + 1) {
|
|
||||||
t.nojoin = t.nojoin || t.postlist.length;
|
t.nojoin = t.nojoin || t.postlist.length;
|
||||||
console.log("ignoring dupe-segment with backoff", t.nojoin, t.name, t);
|
console.log("ignoring dupe-segment with backoff", t.nojoin, t.name, t);
|
||||||
if (!toast.visible && st.todo.upload.length < 4)
|
if (!toast.visible && st.todo.upload.length < 4)
|
||||||
|
|
@ -2970,10 +2952,6 @@ function up2k_init(subtle) {
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
xhrchk(xhr, L.u_cuerr2.format(snpart, Math.ceil(t.size / chunksize), esc(t.name)), "404, target folder not found (???)", "warn", t);
|
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);
|
chill(t);
|
||||||
}
|
}
|
||||||
orz2(xhr);
|
orz2(xhr);
|
||||||
|
|
@ -2982,12 +2960,10 @@ function up2k_init(subtle) {
|
||||||
apop(st.busy.upload, upt);
|
apop(st.busy.upload, upt);
|
||||||
for (var a = pcar; a <= pcdr; a++)
|
for (var a = pcar; a <= pcdr; a++)
|
||||||
apop(t.postlist, a);
|
apop(t.postlist, a);
|
||||||
if (!t.postlist.length && !upq_has(st.busy.upload, t)) {
|
if (!t.postlist.length) {
|
||||||
t.t_uploaded = Date.now();
|
t.t_uploaded = Date.now();
|
||||||
if (!has(st.busy.handshake, t)) {
|
pvis.seth(t.n, 1, 'verifying');
|
||||||
pvis.seth(t.n, 1, 'verifying');
|
st.todo.handshake.unshift(t);
|
||||||
st.todo.handshake.unshift(t);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
tasker();
|
tasker();
|
||||||
}
|
}
|
||||||
|
|
@ -3022,7 +2998,6 @@ function up2k_init(subtle) {
|
||||||
if (!toast.visible)
|
if (!toast.visible)
|
||||||
toast.warn(9.98, L.u_cuerr.format(snpart, Math.ceil(t.size / chunksize), esc(t.name)), t);
|
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
|
t.nojoin = t.nojoin || t.postlist.length; // maybe rproxy postsize limit
|
||||||
console.log('chunkpit onerror,', t.name, t);
|
console.log('chunkpit onerror,', t.name, t);
|
||||||
orz2(xhr);
|
orz2(xhr);
|
||||||
|
|
@ -3171,7 +3146,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);
|
||||||
|
|
@ -3408,7 +3383,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) {
|
||||||
|
|
@ -3479,9 +3454,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";
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
@ -1206,10 +1199,8 @@ function has(haystack, needle) {
|
||||||
|
|
||||||
function apop(arr, v) {
|
function apop(arr, v) {
|
||||||
var ofs = arr.indexOf(v);
|
var ofs = arr.indexOf(v);
|
||||||
if (ofs !== -1) {
|
if (ofs !== -1)
|
||||||
arr.splice(ofs, 1);
|
arr.splice(ofs, 1);
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1475,7 +1466,7 @@ function Debounce(delay) {
|
||||||
};
|
};
|
||||||
|
|
||||||
r.rm = function (fun) {
|
r.rm = function (fun) {
|
||||||
while (apop(r.q, fun));
|
apop(r.q, fun);
|
||||||
};
|
};
|
||||||
|
|
||||||
r.run = function () {
|
r.run = function () {
|
||||||
|
|
@ -1535,7 +1526,7 @@ var timer = (function () {
|
||||||
};
|
};
|
||||||
|
|
||||||
r.rm = function (fun) {
|
r.rm = function (fun) {
|
||||||
while (apop(r.q, fun));
|
apop(r.q, fun);
|
||||||
};
|
};
|
||||||
|
|
||||||
var doevents = function () {
|
var doevents = function () {
|
||||||
|
|
@ -2028,12 +2019,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);
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
|
|
||||||
|
|
@ -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
|
# 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
|
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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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/;
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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 |
|
|
@ -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 |
|
|
@ -71,7 +71,6 @@ Ls.hmn = {
|
||||||
["ctrl-V", "paste (move/copy) here"],
|
["ctrl-V", "paste (move/copy) here"],
|
||||||
["Y", "download selected"],
|
["Y", "download selected"],
|
||||||
["F2", "rename selected"],
|
["F2", "rename selected"],
|
||||||
["F4", "update/refresh"],
|
|
||||||
|
|
||||||
"file-list-sel",
|
"file-list-sel",
|
||||||
["space", "toggle file selection"],
|
["space", "toggle file selection"],
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue