mirror of
https://github.com/9001/copyparty.git
synced 2025-10-03 01:39:26 +02:00
Compare commits
11 commits
5996a58b20
...
e1ea9852c6
Author | SHA1 | Date | |
---|---|---|---|
![]() |
e1ea9852c6 | ||
![]() |
2ee9c80d3b | ||
![]() |
4b2ff3a196 | ||
![]() |
538a205ce4 | ||
![]() |
6559152882 | ||
![]() |
669b10754d | ||
![]() |
478f1c764e | ||
![]() |
a043d7cfb6 | ||
![]() |
ee5f31908f | ||
![]() |
35326a6fb8 | ||
![]() |
59a0122179 |
21 changed files with 173 additions and 48 deletions
|
@ -1353,7 +1353,7 @@ general usage:
|
|||
on macos, connect from finder:
|
||||
* [Go] -> [Connect to Server...] -> http://192.168.123.1:3923/
|
||||
|
||||
in order to grant full write-access to webdav clients, the volflag `daw` must be set and the account must also have delete-access (otherwise the client won't be allowed to replace the contents of existing files, which is how webdav works)
|
||||
to upload or edit files with WebDAV clients, enable the `daw` volflag (because most WebDAV clients expect this) and give your account the delete-permission. This avoids getting several copies of the same file on the server. HOWEVER: This will also make all PUT-uploads overwrite existing files if the user has delete-access, so use with caution.
|
||||
|
||||
> note: if you have enabled [IdP authentication](#identity-providers) then that may cause issues for some/most webdav clients; see [the webdav section in the IdP docs](https://github.com/9001/copyparty/blob/hovudstraum/docs/idp.md#connecting-webdav-clients)
|
||||
|
||||
|
@ -3023,7 +3023,7 @@ first install one of the following:
|
|||
and then copypaste the following command into `a-Shell`:
|
||||
|
||||
```sh
|
||||
curl https://github.com/9001/copyparty/raw/refs/heads/hovudstraum/contrib/setup-ashell.sh | sh
|
||||
curl -L https://github.com/9001/copyparty/raw/refs/heads/hovudstraum/contrib/setup-ashell.sh | sh
|
||||
```
|
||||
|
||||
what this does:
|
||||
|
|
|
@ -7,6 +7,12 @@
|
|||
* works on windows, linux and macos
|
||||
* assumes `copyparty-sfx.py` was renamed to `copyparty.py` in the same folder as `copyparty.bat`
|
||||
|
||||
### [`setup-ashell.sh`](setup-ashell.sh)
|
||||
* run copyparty on an iPhone/iPad using [a-Shell](https://holzschu.github.io/a-Shell_iOS/)
|
||||
* not very useful due to limitations in iOS:
|
||||
* not able to share all of your phone's storage
|
||||
* cannot run in the background
|
||||
|
||||
### [`index.html`](index.html)
|
||||
* drop-in redirect from an httpd to copyparty
|
||||
* assumes the webserver and copyparty is running on the same server/IP
|
||||
|
|
|
@ -52,6 +52,7 @@ let
|
|||
${mkSection "global" cfg.settings}
|
||||
${cfg.globalExtraConfig}
|
||||
${mkSection "accounts" (accountsWithPlaceholders cfg.accounts)}
|
||||
${mkSection "groups" cfg.groups}
|
||||
${concatStringsSep "\n" (mapAttrsToList mkVolume cfg.volumes)}
|
||||
'';
|
||||
|
||||
|
@ -167,6 +168,19 @@ in
|
|||
'';
|
||||
};
|
||||
|
||||
groups = mkOption {
|
||||
type = types.attrsOf (types.listOf types.str);
|
||||
description = ''
|
||||
A set of copyparty groups to create and the users that should be part of each group.
|
||||
'';
|
||||
default = { };
|
||||
example = literalExpression ''
|
||||
{
|
||||
group_name = [ "user1" "user2" ];
|
||||
};
|
||||
'';
|
||||
};
|
||||
|
||||
volumes = mkOption {
|
||||
type = types.attrsOf (
|
||||
types.submodule (
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
# NOTE: You generally shouldn't use this PKGBUILD on Arch, as it is mainly for testing purposes. Install copyparty using pacman instead.
|
||||
|
||||
pkgname=copyparty
|
||||
pkgver="1.19.9"
|
||||
pkgver="1.19.10"
|
||||
pkgrel=1
|
||||
pkgdesc="File server with accelerated resumable uploads, dedup, WebDAV, FTP, TFTP, zeroconf, media indexer, thumbnails++"
|
||||
arch=("any")
|
||||
|
@ -23,7 +23,7 @@ optdepends=("ffmpeg: thumbnails for videos, images (slower) and audio, music tag
|
|||
)
|
||||
source=("https://github.com/9001/${pkgname}/releases/download/v${pkgver}/${pkgname}-${pkgver}.tar.gz")
|
||||
backup=("etc/${pkgname}/copyparty.conf" )
|
||||
sha256sums=("b7d0d66ff94ca4505b06d46526dd2f7c75b2a10e4de828afe0df4f60f9f07d63")
|
||||
sha256sums=("0b786b9e1d7b64fa8e1ea8fab119c84add4efeef32504cbbd01217ebead6bec0")
|
||||
|
||||
build() {
|
||||
cd "${srcdir}/${pkgname}-${pkgver}/copyparty/web"
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
|
||||
pkgname=copyparty
|
||||
pkgver=1.19.9
|
||||
pkgver=1.19.10
|
||||
pkgrel=1
|
||||
pkgdesc="File server with accelerated resumable uploads, dedup, WebDAV, FTP, TFTP, zeroconf, media indexer, thumbnails++"
|
||||
arch=("any")
|
||||
|
@ -20,7 +20,7 @@ optdepends=("ffmpeg: thumbnails for videos, images (slower) and audio, music tag
|
|||
)
|
||||
source=("https://github.com/9001/${pkgname}/releases/download/v${pkgver}/${pkgname}-${pkgver}.tar.gz")
|
||||
backup=("/etc/${pkgname}.d/init" )
|
||||
sha256sums=("b7d0d66ff94ca4505b06d46526dd2f7c75b2a10e4de828afe0df4f60f9f07d63")
|
||||
sha256sums=("0b786b9e1d7b64fa8e1ea8fab119c84add4efeef32504cbbd01217ebead6bec0")
|
||||
|
||||
build() {
|
||||
cd "${srcdir}/${pkgname}-${pkgver}/copyparty/web"
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"url": "https://github.com/9001/copyparty/releases/download/v1.19.9/copyparty-1.19.9.tar.gz",
|
||||
"version": "1.19.9",
|
||||
"hash": "sha256-t9DWb/lMpFBbBtRlJt0vfHWyoQ5N6Civ4N9PYPnwfWM="
|
||||
"url": "https://github.com/9001/copyparty/releases/download/v1.19.10/copyparty-1.19.10.tar.gz",
|
||||
"version": "1.19.10",
|
||||
"hash": "sha256-C3hrnh17ZPqOHqj6sRnISt1O/u8yUEy70BIX6+rWvsA="
|
||||
}
|
|
@ -6,7 +6,7 @@
|
|||
# https://apps.apple.com/us/app/a-shell/id1473805438
|
||||
#
|
||||
# step 2: copypaste the following command into a-Shell:
|
||||
# curl https://github.com/9001/copyparty/raw/refs/heads/hovudstraum/contrib/setup-ashell.sh
|
||||
# curl -L https://github.com/9001/copyparty/raw/refs/heads/hovudstraum/contrib/setup-ashell.sh
|
||||
#
|
||||
# step 3: launch copyparty with this command: cpp
|
||||
#
|
||||
|
|
|
@ -1220,6 +1220,7 @@ def add_upload(ap):
|
|||
ap2.add_argument("--chmod-d", metavar="UGO", type=u, default="755", help="unix file permissions to use when creating directories; see --help-chmod. Examples: [\033[32m755\033[0m] = owner-RW + all-R, [\033[32m777\033[0m] = full-yolo (volflag=chmod_d)")
|
||||
ap2.add_argument("--uid", metavar="N", type=int, default=-1, help="unix user-id to chown new files/folders to; default = -1 = do-not-change (volflag=uid)")
|
||||
ap2.add_argument("--gid", metavar="N", type=int, default=-1, help="unix group-id to chown new files/folders to; default = -1 = do-not-change (volflag=gid)")
|
||||
ap2.add_argument("--wram", action="store_true", help="allow uploading even if a volume is inside a ramdisk, meaning that all data will be lost on the next server reboot (volflag=wram)")
|
||||
ap2.add_argument("--dedup", action="store_true", help="enable symlink-based upload deduplication (volflag=dedup)")
|
||||
ap2.add_argument("--safe-dedup", metavar="N", type=int, default=50, help="how careful to be when deduplicating files; [\033[32m1\033[0m] = just verify the filesize, [\033[32m50\033[0m] = verify file contents have not been altered (volflag=safededup)")
|
||||
ap2.add_argument("--hardlink", action="store_true", help="enable hardlink-based dedup; will fallback on symlinks when that is impossible (across filesystems) (volflag=hardlink)")
|
||||
|
@ -1577,6 +1578,9 @@ def add_logging(ap):
|
|||
ap2.add_argument("--ihead", metavar="HEADER", type=u, action='append', help="print request \033[33mHEADER\033[0m; [\033[32m*\033[0m]=all")
|
||||
ap2.add_argument("--ohead", metavar="HEADER", type=u, action='append', help="print response \033[33mHEADER\033[0m; [\033[32m*\033[0m]=all")
|
||||
ap2.add_argument("--lf-url", metavar="RE", type=u, default=r"^/\.cpr/|[?&]th=[wjp]|/\.(_|ql_|DS_Store$|localized$)", help="dont log URLs matching regex \033[33mRE\033[0m")
|
||||
ap2.add_argument("--scan-st-r", metavar="SEC", type=float, default=0.1, help="fs-indexing: wait \033[33mSEC\033[0m between each status-message")
|
||||
ap2.add_argument("--scan-pr-r", metavar="SEC", type=float, default=10, help="fs-indexing: wait \033[33mSEC\033[0m between each 'progress:' message")
|
||||
ap2.add_argument("--scan-pr-s", metavar="MiB", type=float, default=1, help="fs-indexing: say 'file: <name>' when a file larger than \033[33mMiB\033[0m is about to be hashed")
|
||||
|
||||
|
||||
def add_admin(ap):
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
# coding: utf-8
|
||||
|
||||
VERSION = (1, 19, 9)
|
||||
VERSION = (1, 19, 10)
|
||||
CODENAME = "usernames"
|
||||
BUILD_DT = (2025, 9, 15)
|
||||
BUILD_DT = (2025, 9, 19)
|
||||
|
||||
S_VERSION = ".".join(map(str, VERSION))
|
||||
S_BUILD_DT = "{0:04d}-{1:02d}-{2:02d}".format(*BUILD_DT)
|
||||
|
|
|
@ -12,6 +12,7 @@ import queue
|
|||
from .__init__ import ANYWIN
|
||||
from .authsrv import AuthSrv
|
||||
from .broker_util import BrokerCli, ExceptionalQueue, NotExQueue
|
||||
from .fsutil import ramdisk_chk
|
||||
from .httpsrv import HttpSrv
|
||||
from .util import FAKE_MP, Daemon, HMaccas
|
||||
|
||||
|
@ -56,6 +57,7 @@ class MpWorker(BrokerCli):
|
|||
|
||||
# starting to look like a good idea
|
||||
self.asrv = AuthSrv(args, None, False)
|
||||
ramdisk_chk(self.asrv)
|
||||
|
||||
# instantiate all services here (TODO: inheritance?)
|
||||
self.iphash = HMaccas(os.path.join(self.args.E.cfg, "iphash"), 8)
|
||||
|
@ -99,6 +101,7 @@ class MpWorker(BrokerCli):
|
|||
if dest == "reload":
|
||||
self.logw("mpw.asrv reloading")
|
||||
self.asrv.reload()
|
||||
ramdisk_chk(self.asrv)
|
||||
self.logw("mpw.asrv reloaded")
|
||||
continue
|
||||
|
||||
|
|
|
@ -57,6 +57,7 @@ def vf_bmap() -> dict[str, str]:
|
|||
"rmagic",
|
||||
"rss",
|
||||
"wo_up_readme",
|
||||
"wram",
|
||||
"xdev",
|
||||
"xlink",
|
||||
"xvol",
|
||||
|
@ -187,6 +188,7 @@ flagcats = {
|
|||
"chmod_f=644": "unix-permission for new files",
|
||||
"uid=573": "change owner of new files/folders to unix-user 573",
|
||||
"gid=999": "change owner of new files/folders to unix-group 999",
|
||||
"wram": "allow uploading into ramdisks",
|
||||
"sparse": "force use of sparse files, mainly for s3-backed storage",
|
||||
"nosparse": "deny use of sparse files, mainly for slow storage",
|
||||
"daw": "enable full WebDAV write support (dangerous);\nPUT-operations will now \033[1;31mOVERWRITE\033[0;35m existing files",
|
||||
|
|
|
@ -7,7 +7,7 @@ import re
|
|||
import time
|
||||
|
||||
from .__init__ import ANYWIN, MACOS
|
||||
from .authsrv import AXS, VFS
|
||||
from .authsrv import AXS, VFS, AuthSrv
|
||||
from .bos import bos
|
||||
from .util import chkcmd, min_ex, undot
|
||||
|
||||
|
@ -18,22 +18,25 @@ if True: # pylint: disable=using-constant-test
|
|||
|
||||
|
||||
class Fstab(object):
|
||||
def __init__(self, log: "RootLogger", args: argparse.Namespace):
|
||||
def __init__(self, log: "RootLogger", args: argparse.Namespace, verbose: bool):
|
||||
self.log_func = log
|
||||
self.verbose = verbose
|
||||
|
||||
self.warned = False
|
||||
self.trusted = False
|
||||
self.tab: Optional[VFS] = None
|
||||
self.oldtab: Optional[VFS] = None
|
||||
self.srctab = "a"
|
||||
self.cache: dict[str, str] = {}
|
||||
self.cache: dict[str, tuple[str, str]] = {}
|
||||
self.age = 0.0
|
||||
self.maxage = args.mtab_age
|
||||
|
||||
def log(self, msg: str, c: Union[int, str] = 0) -> None:
|
||||
if not c or self.verbose:
|
||||
return
|
||||
self.log_func("fstab", msg, c)
|
||||
|
||||
def get(self, path: str) -> str:
|
||||
def get(self, path: str) -> tuple[str, str]:
|
||||
now = time.time()
|
||||
if now - self.age > self.maxage or len(self.cache) > 9000:
|
||||
self.age = now
|
||||
|
@ -41,6 +44,7 @@ class Fstab(object):
|
|||
self.tab = None
|
||||
self.cache = {}
|
||||
|
||||
mp = ""
|
||||
fs = "ext4"
|
||||
msg = "failed to determine filesystem at %r; assuming %s\n%s"
|
||||
|
||||
|
@ -50,7 +54,7 @@ class Fstab(object):
|
|||
path = self._winpath(path)
|
||||
except:
|
||||
self.log(msg % (path, fs, min_ex()), 3)
|
||||
return fs
|
||||
return fs, ""
|
||||
|
||||
path = undot(path)
|
||||
try:
|
||||
|
@ -59,14 +63,14 @@ class Fstab(object):
|
|||
pass
|
||||
|
||||
try:
|
||||
fs = self.get_w32(path) if ANYWIN else self.get_unix(path)
|
||||
fs, mp = self.get_w32(path) if ANYWIN else self.get_unix(path)
|
||||
except:
|
||||
self.log(msg % (path, fs, min_ex()), 3)
|
||||
|
||||
fs = fs.lower()
|
||||
self.cache[path] = fs
|
||||
self.log("found %s at %r" % (fs, path))
|
||||
return fs
|
||||
self.cache[path] = (fs, mp)
|
||||
self.log("found %s at %r, %r" % (fs, mp, path))
|
||||
return fs, mp
|
||||
|
||||
def _winpath(self, path: str) -> str:
|
||||
# try to combine volume-label + st_dev (vsn)
|
||||
|
@ -81,34 +85,49 @@ class Fstab(object):
|
|||
self.tab = VFS(self.log_func, "idk", "/", "/", AXS(), {})
|
||||
self.trusted = False
|
||||
|
||||
def build_tab(self) -> None:
|
||||
self.log("inspecting mtab for changes")
|
||||
|
||||
def _from_sp_mount(self) -> dict[str, str]:
|
||||
sptn = r"^.*? on (.*) type ([^ ]+) \(.*"
|
||||
if MACOS:
|
||||
sptn = r"^.*? on (.*) \(([^ ]+), .*"
|
||||
|
||||
ptn = re.compile(sptn)
|
||||
so, _ = chkcmd(["mount"])
|
||||
tab1: list[tuple[str, str]] = []
|
||||
atab = []
|
||||
dtab: dict[str, str] = {}
|
||||
for ln in so.split("\n"):
|
||||
m = ptn.match(ln)
|
||||
if not m:
|
||||
continue
|
||||
|
||||
zs1, zs2 = m.groups()
|
||||
tab1.append((str(zs1), str(zs2)))
|
||||
atab.append(ln)
|
||||
dtab[str(zs1)] = str(zs2)
|
||||
|
||||
return dtab
|
||||
|
||||
def _from_proc(self) -> dict[str, str]:
|
||||
ret: dict[str, str] = {}
|
||||
with open("/proc/self/mounts", "rb", 262144) as f:
|
||||
src = f.read(262144).decode("utf-8", "replace").split("\n")
|
||||
for zsl in [x.split(" ") for x in src]:
|
||||
if len(zsl) < 3:
|
||||
continue
|
||||
zs = zsl[1]
|
||||
zs = zs.replace("\\011", "\t").replace("\\040", " ").replace("\\134", "\\")
|
||||
ret[zs] = zsl[2]
|
||||
return ret
|
||||
|
||||
def build_tab(self) -> None:
|
||||
self.log("inspecting mtab for changes")
|
||||
dtab = self._from_sp_mount() if MACOS else self._from_proc()
|
||||
|
||||
# keep empirically-correct values if mounttab unchanged
|
||||
srctab = "\n".join(sorted(atab))
|
||||
srctab = str(sorted(dtab.items()))
|
||||
if srctab == self.srctab:
|
||||
self.tab = self.oldtab
|
||||
return
|
||||
|
||||
self.log("mtab has changed; reevaluating support for sparse files")
|
||||
|
||||
tab1 = list(dtab.items())
|
||||
tab1.sort(key=lambda x: (len(x[0]), x[0]))
|
||||
path1, fs1 = tab1[0]
|
||||
tab = VFS(self.log_func, fs1, path1, path1, AXS(), {})
|
||||
|
@ -146,7 +165,7 @@ class Fstab(object):
|
|||
vn.realpath = ptn.sub(nval, vn.realpath)
|
||||
visit.extend(list(vn.nodes.values()))
|
||||
|
||||
def get_unix(self, path: str) -> str:
|
||||
def get_unix(self, path: str) -> tuple[str, str]:
|
||||
if not self.tab:
|
||||
try:
|
||||
self.build_tab()
|
||||
|
@ -155,20 +174,44 @@ class Fstab(object):
|
|||
# prisonparty or other restrictive environment
|
||||
if not self.warned:
|
||||
self.warned = True
|
||||
self.log("failed to build tab:\n{}".format(min_ex()), 3)
|
||||
t = "failed to associate fs-mounts with the VFS (this is fine):\n%s"
|
||||
self.log(t % (min_ex(),), 6)
|
||||
self.build_fallback()
|
||||
|
||||
assert self.tab # !rm
|
||||
ret = self.tab._find(path)[0]
|
||||
if self.trusted or path == ret.vpath:
|
||||
return ret.realpath.split("/")[0]
|
||||
return ret.realpath.split("/")[0], ret.vpath
|
||||
else:
|
||||
return "idk"
|
||||
return "idk", ""
|
||||
|
||||
def get_w32(self, path: str) -> str:
|
||||
def get_w32(self, path: str) -> tuple[str, str]:
|
||||
if not self.tab:
|
||||
self.build_fallback()
|
||||
|
||||
assert self.tab # !rm
|
||||
ret = self.tab._find(path)[0]
|
||||
return ret.realpath
|
||||
return ret.realpath, ""
|
||||
|
||||
|
||||
def ramdisk_chk(asrv: AuthSrv) -> None:
|
||||
# should have been in authsrv but that's a circular import
|
||||
mods = []
|
||||
ramfs = ("tmpfs", "overlay")
|
||||
log = asrv.log_func or print
|
||||
fstab = Fstab(log, asrv.args, False)
|
||||
for vn in asrv.vfs.all_nodes.values():
|
||||
if not vn.axs.uwrite or "wram" in vn.flags:
|
||||
continue
|
||||
ap = vn.realpath
|
||||
if not ap or os.path.isfile(ap):
|
||||
continue
|
||||
fs, mp = fstab.get(ap)
|
||||
mp = "/" + mp.strip("/")
|
||||
if fs == "tmpfs" or (mp == "/" and fs in ramfs):
|
||||
mods.append((vn.vpath, ap, fs, mp))
|
||||
vn.axs.uwrite.clear()
|
||||
if mods:
|
||||
t = "WARNING: write-access was removed from the following volumes because they are not mapped to an actual HDD for storage! All uploaded data would live in RAM only, and all uploaded files would be LOST on next reboot. To allow uploading and ignore this hazard, enable the 'wram' option (global/volflag). List of affected volumes:"
|
||||
t2 = ["\n volume=[/%s], abspath=%r, type=%s, root=%r" % x for x in mods]
|
||||
log("vfs", t + "".join(t2) + "\n", 1)
|
||||
|
|
|
@ -433,9 +433,8 @@ def _get_cover_from_epub2(
|
|||
) -> Optional[str]:
|
||||
# <meta name="cover" content="id-to-cover-image"> in <metadata>, then
|
||||
# <item> in <manifest>
|
||||
cover_id = package_root.find("./metadata/meta[@name='cover']", package_ns).get(
|
||||
"content"
|
||||
)
|
||||
xn = package_root.find("./metadata/meta[@name='cover']", package_ns)
|
||||
cover_id = xn.get("content") if xn is not None else None
|
||||
|
||||
if not cover_id:
|
||||
return None
|
||||
|
|
|
@ -30,6 +30,7 @@ from .__init__ import ANYWIN, EXE, MACOS, PY2, TYPE_CHECKING, E, EnvParams, unic
|
|||
from .authsrv import BAD_CFG, AuthSrv, n_du_who, n_ver_who
|
||||
from .bos import bos
|
||||
from .cert import ensure_cert
|
||||
from .fsutil import ramdisk_chk
|
||||
from .mtag import HAVE_FFMPEG, HAVE_FFPROBE, HAVE_MUTAGEN
|
||||
from .pwhash import HAVE_ARGON2
|
||||
from .tcpsrv import TcpSrv
|
||||
|
@ -310,6 +311,7 @@ class SvcHub(object):
|
|||
|
||||
# initiate all services to manage
|
||||
self.asrv = AuthSrv(self.args, self.log, dargs=self.dargs)
|
||||
ramdisk_chk(self.asrv)
|
||||
|
||||
if args.cgen:
|
||||
self.asrv.cgen()
|
||||
|
@ -1359,6 +1361,7 @@ class SvcHub(object):
|
|||
with self.reload_mutex:
|
||||
self.log("root", "reloading config")
|
||||
self.asrv.reload(9 if up2k else 4)
|
||||
ramdisk_chk(self.asrv)
|
||||
if up2k:
|
||||
self.up2k.reload(rescan_all_vols)
|
||||
t += "; volumes are now reinitializing"
|
||||
|
|
|
@ -213,7 +213,7 @@ class Up2k(object):
|
|||
t = "could not initialize sqlite3, will use in-memory registry only"
|
||||
self.log(t, 3)
|
||||
|
||||
self.fstab = Fstab(self.log_func, self.args)
|
||||
self.fstab = Fstab(self.log_func, self.args, True)
|
||||
self.gen_fk = self._gen_fk if self.args.log_fk else gen_filekey
|
||||
|
||||
if self.args.hash_mt < 2:
|
||||
|
@ -1499,6 +1499,7 @@ class Up2k(object):
|
|||
|
||||
th_cvd = self.args.th_coversd
|
||||
th_cvds = self.args.th_coversd_set
|
||||
scan_pr_s = self.args.scan_pr_s
|
||||
|
||||
assert self.pp and self.mem_cur # !rm
|
||||
self.pp.msg = "a%d %s" % (self.pp.n, cdir)
|
||||
|
@ -1711,7 +1712,7 @@ class Up2k(object):
|
|||
if nohash or not sz:
|
||||
wark = up2k_wark_from_metadata(self.salt, sz, lmod, rd, fn)
|
||||
else:
|
||||
if sz > 1024 * 1024:
|
||||
if sz > 1024 * 1024 * scan_pr_s:
|
||||
self.log("file: %r" % (abspath,))
|
||||
|
||||
try:
|
||||
|
@ -3006,7 +3007,7 @@ class Up2k(object):
|
|||
|
||||
# check if filesystem supports sparse files;
|
||||
# refuse out-of-order / multithreaded uploading if sprs False
|
||||
sprs = self.fstab.get(pdir) != "ng"
|
||||
sprs = self.fstab.get(pdir)[0] != "ng"
|
||||
|
||||
if True:
|
||||
jcur = self.cur.get(ptop)
|
||||
|
@ -5179,7 +5180,7 @@ class Up2k(object):
|
|||
sprs = False
|
||||
|
||||
if not ANYWIN and sprs and sz > 1024 * 1024:
|
||||
fs = self.fstab.get(pdir)
|
||||
fs, mnt = self.fstab.get(pdir)
|
||||
if fs == "ok":
|
||||
pass
|
||||
elif "nosparse" in vf:
|
||||
|
|
|
@ -546,6 +546,8 @@ def py_desc() -> str:
|
|||
ofs = py_ver.find(".final.")
|
||||
if ofs > 0:
|
||||
py_ver = py_ver[:ofs]
|
||||
if "free-threading" in sys.version:
|
||||
py_ver += "t"
|
||||
|
||||
host_os = platform.system()
|
||||
compiler = platform.python_compiler().split("http")[0]
|
||||
|
@ -1121,16 +1123,18 @@ class ProgressPrinter(threading.Thread):
|
|||
sigblock()
|
||||
tp = 0
|
||||
msg = None
|
||||
no_stdout = self.args.q
|
||||
slp_pr = self.args.scan_pr_r
|
||||
slp_ps = min(slp_pr, self.args.scan_st_r)
|
||||
no_stdout = self.args.q or slp_pr == slp_ps
|
||||
fmt = " {}\033[K\r" if VT100 else " {} $\r"
|
||||
while not self.end:
|
||||
time.sleep(0.1)
|
||||
time.sleep(slp_ps)
|
||||
if msg == self.msg or self.end:
|
||||
continue
|
||||
|
||||
msg = self.msg
|
||||
now = time.time()
|
||||
if msg and now - tp > 10:
|
||||
if msg and now - tp >= slp_pr:
|
||||
tp = now
|
||||
self.log("progress: %r" % (msg,), 6)
|
||||
|
||||
|
|
|
@ -1,3 +1,39 @@
|
|||
▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
|
||||
# 2025-0915-0019 `v1.19.9` case-sensitivity, give or take
|
||||
|
||||
## 🧪 new features
|
||||
|
||||
* #781 case-sensitive behavior is now simulated on Windows/Macos/Fat32/NTFS 8b66874b
|
||||
* avoids some of the scary issues associated with case-insensitive filesystems
|
||||
* unfortunately this is expensive and may be **noticeably slower in large folders;** disable the safeguard with `casechk: n` if you know you don't need it
|
||||
* #789 case-insensitive search for unicode filenames/paths (thx @km-clay!) e2aa8fc1 ecd18adc
|
||||
* default-disabled because it is somewhat expensive; enable with global-option `srch-icase`
|
||||
* [CB-1](https://codeberg.org/9001/copyparty/issues/1) add `--qr-stdout` and `--qr-stderr` to show qr-code even with `-q` d7887f3d
|
||||
|
||||
## 🩹 bugfixes
|
||||
|
||||
* #775 the basic-uploader didn't accept empty files 25749b4b
|
||||
* opt-out from index.html with `?v` did not work as documented 3d09bec1
|
||||
* Windows: dedup could get rejected by the filesystem if the origin file had a timestamp from the cambrian era e09f3c9e
|
||||
* webdav would incorrectly return an error for Depth:0 on an unmapped root 3a2381ff
|
||||
* markdown-editor would waste another http roundtrip on certain documents 14b7e514
|
||||
* `--help` didn't render if terminal was non-UTF8 3f454927
|
||||
|
||||
## 🔧 other changes
|
||||
|
||||
* #788 fixed a hotkey typo in the imageviewer (thx @tkroo!) 5c1a43c7
|
||||
* #778 improved polish translation (thx @daimond113!) 52438bcc
|
||||
* #798 debian: fixed an issue in the systemd script (thx @Beethoven-n, and congrats on commit number 4000!) dfd9e007
|
||||
* media-tag `conductor` is no longer mapped to `circle` (album-artist) 9c9e4057
|
||||
* "download-selection-as-zip" now produces a better filename, `sel-FOLDERNAME.zip` instead of `FIRSTFILE.zip` 8f587627
|
||||
* detect and warn if IdP volumes are misconfigured in a particular way 83bd1974
|
||||
|
||||
## 🌠 fun facts
|
||||
|
||||
* the themesong of this release is [KO3 - Give it up?](https://www.youtube.com/watch?v=8w_na7HAppU) because that's what the car mechanic got to enjoy when i forgot to unplug the flashdrive before handing in the shitbox for service
|
||||
|
||||
|
||||
|
||||
▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
|
||||
# 2025-0907-2300 `v1.19.8` SECURITY: fix single-file shares
|
||||
|
||||
|
|
|
@ -17,6 +17,7 @@ uname -s | grep NT-10 && w10=1 || w7=1
|
|||
[ $w7 ] && [ -e up2k.sh ] && [ ! "$1" ] && ./up2k.sh
|
||||
|
||||
[ $w7 ] && pyv=37 || pyv=313
|
||||
[ $w7 ] && sfx=en || sfx=sfx
|
||||
esuf=
|
||||
[ $w7 ] && [ $m = 32 ] && esuf=32
|
||||
[ $w7 ] && [ $m = 64 ] && esuf=-winpe64
|
||||
|
@ -33,8 +34,16 @@ dl https://192.168.123.1:3923/cpp/scripts/pyinstaller/loader.ico
|
|||
dl https://192.168.123.1:3923/cpp/scripts/pyinstaller/loader.py
|
||||
dl https://192.168.123.1:3923/cpp/scripts/pyinstaller/loader.rc
|
||||
|
||||
[ $sfx = en ] && {
|
||||
dl https://192.168.123.1:3923/cpp/dist/copyparty-en.py
|
||||
|
||||
st_en=$(cat copyparty-en.py | awk '/^STAMP = [0-9]+/{print$3;exit}') 2>/dev/null
|
||||
st_sfx=$(cat copyparty-sfx.py | awk '/^STAMP = [0-9]+/{print$3;exit}') 2>/dev/null
|
||||
[ $st_en ] && [ $st_en -ge $st_sfx ] || sfx=sfx
|
||||
}
|
||||
|
||||
rm -rf $TEMP/pe-copyparty*
|
||||
python copyparty-sfx.py --version
|
||||
python copyparty-$sfx.py --version
|
||||
|
||||
rm -rf mods; mkdir mods
|
||||
cp -pR $TEMP/pe-copyparty/{copyparty,partftpy}/ $TEMP/pe-copyparty/{ftp,j2}/* mods/
|
||||
|
|
BIN
scripts/pyinstaller/up2k.ico
Normal file
BIN
scripts/pyinstaller/up2k.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.4 KiB |
|
@ -102,6 +102,7 @@ def tc1(vflags):
|
|||
"-p4321",
|
||||
"-e2dsa",
|
||||
"-e2tsr",
|
||||
"--wram",
|
||||
"--ban-403=no",
|
||||
"--dbd=yolo",
|
||||
"--no-mutagen",
|
||||
|
|
|
@ -146,7 +146,7 @@ class Cfg(Namespace):
|
|||
ex = "allow_flac allow_wav chpw cookie_lax daw dav_auth dav_mac dav_rt e2d e2ds e2dsa e2t e2ts e2tsr e2v e2vu e2vp early_ban ed emp exp force_js getmod grid gsel hardlink hardlink_only ih ihead localtime log_badxml magic md_no_br nid nih no_acode no_athumb no_bauth no_clone no_cp no_dav no_db_ip no_del no_dirsz no_dupe no_fnugg no_lifetime no_logues no_mv no_pipe no_poll no_readme no_robots no_sb_md no_sb_lg no_scandir no_tail no_tarcmp no_thumb no_vthumb no_u2abrt no_zip nrand nsort nw og og_no_head og_s_title ohead q rand re_dirsz reflink rmagic rss smb srch_dbg srch_excl srch_icase stats uqe usernames vague_403 vc ver wo_up_readme write_uplog xdev xlink xvol zipmaxu zs"
|
||||
ka.update(**{k: False for k in ex.split()})
|
||||
|
||||
ex = "dav_inf dedup dotpart dotsrch hook_v no_dhash no_fastboot no_fpool no_htp no_rescan no_sendfile no_ses no_snap no_up_list no_voldump re_dhash see_dots plain_ip"
|
||||
ex = "dav_inf dedup dotpart dotsrch hook_v no_dhash no_fastboot no_fpool no_htp no_rescan no_sendfile no_ses no_snap no_up_list no_voldump wram re_dhash see_dots plain_ip"
|
||||
ka.update(**{k: True for k in ex.split()})
|
||||
|
||||
ex = "ah_cli ah_gen css_browser dbpath hist ipu js_browser js_other mime mimes no_forget no_hash no_idx nonsus_urls og_tpl og_ua ua_nodoc ua_nozip"
|
||||
|
@ -155,7 +155,7 @@ class Cfg(Namespace):
|
|||
ex = "gid uid"
|
||||
ka.update(**{k: -1 for k in ex.split()})
|
||||
|
||||
ex = "hash_mt hsortn qdel safe_dedup srch_time tail_fd tail_rate th_spec_p u2abort u2j u2sz unp_who"
|
||||
ex = "hash_mt hsortn qdel safe_dedup scan_pr_r scan_pr_s scan_st_r srch_time tail_fd tail_rate th_spec_p u2abort u2j u2sz unp_who"
|
||||
ka.update(**{k: 1 for k in ex.split()})
|
||||
|
||||
ex = "ac_convt au_vol dl_list du_iwho mtab_age reg_cap s_thead s_tbody tail_tmax tail_who th_convt ups_who ver_iwho zip_who"
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue