Compare commits

...

6 Commits

Author SHA1 Message Date
ed
4dca1cf8f4 v1.18.4 2025-07-25 18:41:05 +00:00
ed
edba7fffd3 add landmarks (#182) 2025-07-25 18:35:28 +00:00
ed
21a96bcfe8 add quickdelete option; closes #183
togglebutton in the ui switches between 2 (off/default) and
1 (on/quick) confirmations; global-option `--qdel` sets the default

setting `--qdel=0` changes the togglebutton to switch
between 1 (off/default) confirmations and 0 (on)

in other words, when the ui-button is enabled, it
always reduces the number of confirmations by one
2025-07-25 18:31:49 +00:00
ed
2d322dd48e fix unpost in new shares 2025-07-25 15:12:05 +00:00
ed
df6d4df4f8 fix filekeys on windows 2025-07-24 23:07:04 +00:00
ed
5aa893973c update pkgs to 1.18.3 2025-07-21 23:30:16 +00:00
14 changed files with 127 additions and 23 deletions

View File

@@ -1605,7 +1605,7 @@ config file example:
w: * # anyone can upload here w: * # anyone can upload here
rw: ed # only user "ed" can read-write rw: ed # only user "ed" can read-write
flags: flags:
e2ds: # filesystem indexing is required for many of these: e2ds # filesystem indexing is required for many of these:
sz: 1k-3m # accept upload only if filesize in this range sz: 1k-3m # accept upload only if filesize in this range
df: 4g # free disk space cannot go lower than this df: 4g # free disk space cannot go lower than this
vmaxb: 1g # volume can never exceed 1 GiB vmaxb: 1g # volume can never exceed 1 GiB
@@ -1662,6 +1662,8 @@ this can instead be kept in a single place using the `--hist` argument, or the `
by default, the per-volume `up2k.db` sqlite3-database for `-e2d` and `-e2t` is stored next to the thumbnails according to the `--hist` option, but the global-option `--dbpath` and/or volflag `dbpath` can be used to put the database somewhere else by default, the per-volume `up2k.db` sqlite3-database for `-e2d` and `-e2t` is stored next to the thumbnails according to the `--hist` option, but the global-option `--dbpath` and/or volflag `dbpath` can be used to put the database somewhere else
if your storage backend is unreliable (NFS or bad HDDs), you can specify one or more "landmarks" to look for before doing anything database-related. A landmark is a file which is always expected to exist inside the volume. This avoids spurious filesystem rescans in the event of an outage. One line per landmark (see example below)
note: note:
* putting the hist-folders on an SSD is strongly recommended for performance * putting the hist-folders on an SSD is strongly recommended for performance
* markdown edits are always stored in a local `.hist` subdirectory * markdown edits are always stored in a local `.hist` subdirectory
@@ -1679,6 +1681,8 @@ config file example:
flags: flags:
hist: - # restore the default (/mnt/nas/pics/.hist/) hist: - # restore the default (/mnt/nas/pics/.hist/)
hist: /mnt/nas/cache/pics/ # can be absolute path hist: /mnt/nas/cache/pics/ # can be absolute path
landmark: me.jpg # /mnt/nas/pics/me.jpg must be readable to enable db
landmark: info/a.txt^=ok # and this textfile must start with "ok"
``` ```

View File

@@ -1,6 +1,6 @@
# Maintainer: icxes <dev.null@need.moe> # Maintainer: icxes <dev.null@need.moe>
pkgname=copyparty pkgname=copyparty
pkgver="1.18.2" pkgver="1.18.3"
pkgrel=1 pkgrel=1
pkgdesc="File server with accelerated resumable uploads, dedup, WebDAV, FTP, TFTP, zeroconf, media indexer, thumbnails++" pkgdesc="File server with accelerated resumable uploads, dedup, WebDAV, FTP, TFTP, zeroconf, media indexer, thumbnails++"
arch=("any") arch=("any")
@@ -22,7 +22,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=("7bcd6fad7c1612a29320e9eb5e8f1bf3852c35df03a04ecb348536028ce31a37") sha256sums=("aa12f4779cf5c014cc9503798ac63872dac840ca91ddf122daa6befb4c883d48")
build() { build() {
cd "${srcdir}/${pkgname}-${pkgver}" cd "${srcdir}/${pkgname}-${pkgver}"

View File

@@ -1,5 +1,5 @@
{ {
"url": "https://github.com/9001/copyparty/releases/download/v1.18.2/copyparty-sfx.py", "url": "https://github.com/9001/copyparty/releases/download/v1.18.3/copyparty-sfx.py",
"version": "1.18.2", "version": "1.18.3",
"hash": "sha256-l6rZlZG7y25ds23ZqBA6BjzYY8WLTMAmfsGoWeYt0mQ=" "hash": "sha256-INqErls4gyhBAlDlY1vfNboKrrqHmeiyB+RAuuYRISQ="
} }

View File

@@ -1550,6 +1550,7 @@ def add_ui(ap, retry):
ap2.add_argument("--nsort", action="store_true", help="default-enable natural sort of filenames with leading numbers (volflag=nsort)") ap2.add_argument("--nsort", action="store_true", help="default-enable natural sort of filenames with leading numbers (volflag=nsort)")
ap2.add_argument("--hsortn", metavar="N", type=int, default=2, help="number of sorting rules to include in media URLs by default (volflag=hsortn)") ap2.add_argument("--hsortn", metavar="N", type=int, default=2, help="number of sorting rules to include in media URLs by default (volflag=hsortn)")
ap2.add_argument("--see-dots", action="store_true", help="default-enable seeing dotfiles; only takes effect if user has the necessary permissions") ap2.add_argument("--see-dots", action="store_true", help="default-enable seeing dotfiles; only takes effect if user has the necessary permissions")
ap2.add_argument("--qdel", metavar="LVL", type=int, default=2, help="number of confirmations to show when deleting files (2/1/0)")
ap2.add_argument("--unlist", metavar="REGEX", type=u, default="", help="don't show files matching \033[33mREGEX\033[0m in file list. Purely cosmetic! Does not affect API calls, just the browser. Example: [\033[32m\\.(js|css)$\033[0m] (volflag=unlist)") ap2.add_argument("--unlist", metavar="REGEX", type=u, default="", help="don't show files matching \033[33mREGEX\033[0m in file list. Purely cosmetic! Does not affect API calls, just the browser. Example: [\033[32m\\.(js|css)$\033[0m] (volflag=unlist)")
ap2.add_argument("--favico", metavar="TXT", type=u, default="c 000 none" if retry else "🎉 000 none", help="\033[33mfavicon-text\033[0m [ \033[33mforeground\033[0m [ \033[33mbackground\033[0m ] ], set blank to disable") ap2.add_argument("--favico", metavar="TXT", type=u, default="c 000 none" if retry else "🎉 000 none", help="\033[33mfavicon-text\033[0m [ \033[33mforeground\033[0m [ \033[33mbackground\033[0m ] ], set blank to disable")
ap2.add_argument("--ext-th", metavar="E=VP", type=u, action="append", help="use thumbnail-image \033[33mVP\033[0m for file-extension \033[33mE\033[0m, example: [\033[32mexe=/.res/exe.png\033[0m] (volflag=ext_th)") ap2.add_argument("--ext-th", metavar="E=VP", type=u, action="append", help="use thumbnail-image \033[33mVP\033[0m for file-extension \033[33mE\033[0m, example: [\033[32mexe=/.res/exe.png\033[0m] (volflag=ext_th)")

View File

@@ -1,8 +1,8 @@
# coding: utf-8 # coding: utf-8
VERSION = (1, 18, 3) VERSION = (1, 18, 4)
CODENAME = "logtail" CODENAME = "logtail"
BUILD_DT = (2025, 7, 21) BUILD_DT = (2025, 7, 25)
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)

View File

@@ -386,20 +386,20 @@ class VFS(object):
self.adot: dict[str, list[str]] = {} self.adot: dict[str, list[str]] = {}
self.js_ls = {} self.js_ls = {}
self.js_htm = "" self.js_htm = ""
self.all_vols: dict[str, VFS] = {} # flattened recursive
self.all_nodes: dict[str, VFS] = {} # also jumpvols/shares
if realpath: if realpath:
rp = realpath + ("" if realpath.endswith(os.sep) else os.sep) rp = realpath + ("" if realpath.endswith(os.sep) else os.sep)
vp = vpath + ("/" if vpath else "") vp = vpath + ("/" if vpath else "")
self.histpath = os.path.join(realpath, ".hist") # db / thumbcache self.histpath = os.path.join(realpath, ".hist") # db / thumbcache
self.dbpath = self.histpath self.dbpath = self.histpath
self.all_vols = {vpath: self} # flattened recursive self.all_vols[vpath] = self
self.all_nodes = {vpath: self} # also jumpvols/shares self.all_nodes[vpath] = self
self.all_aps = [(rp, [self])] self.all_aps = [(rp, [self])]
self.all_vps = [(vp, self)] self.all_vps = [(vp, self)]
else: else:
self.histpath = self.dbpath = "" self.histpath = self.dbpath = ""
self.all_vols = {}
self.all_nodes = {}
self.all_aps = [] self.all_aps = []
self.all_vps = [] self.all_vps = []
@@ -868,6 +868,53 @@ class VFS(object):
return self return self
def check_landmarks(self) -> bool:
if self.dbv:
return True
vps = self.flags.get("landmark") or []
if not vps:
return True
failed = ""
for vp in vps:
if "^=" in vp:
vp, zs = vp.split("^=", 1)
expect = zs.encode("utf-8")
else:
expect = b""
if self.log:
t = "checking [/%s] landmark [%s]"
self.log("vfs", t % (self.vpath, vp), 6)
ap = "?"
try:
ap = self.canonical(vp)
with open(ap, "rb") as f:
buf = f.read(4096)
if not buf.startswith(expect):
t = "file [%s] does not start with the expected bytes %s"
failed = t % (ap, expect)
break
except Exception as ex:
t = "%r while trying to read [%s] => [%s]"
failed = t % (ex, vp, ap)
break
if not failed:
return True
if self.log:
t = "WARNING: landmark verification failed; %s; will now disable up2k database for volume [/%s]"
self.log("vfs", t % (failed, self.vpath), 3)
for rm in "e2d e2t e2v".split():
self.flags = {k: v for k, v in self.flags.items() if not k.startswith(rm)}
self.flags["d2d"] = True
self.flags["d2t"] = True
return False
if WINDOWS: if WINDOWS:
re_vol = re.compile(r"^([a-zA-Z]:[\\/][^:]*|[^:]*):([^:]*):(.*)$") re_vol = re.compile(r"^([a-zA-Z]:[\\/][^:]*|[^:]*):([^:]*):(.*)$")
@@ -1501,7 +1548,7 @@ class AuthSrv(object):
flags[name] = True flags[name] = True
return return
zs = "ext_th mtp on403 on404 xbu xau xiu xbc xac xbr xar xbd xad xm xban" zs = "ext_th landmark mtp on403 on404 xbu xau xiu xbc xac xbr xar xbd xad xm xban"
if name not in zs.split(): if name not in zs.split():
if value is True: if value is True:
t = "└─add volflag [{}] = {} ({})" t = "└─add volflag [{}] = {} ({})"
@@ -2237,6 +2284,8 @@ class AuthSrv(object):
t = "WARNING: volume [/%s]: invalid value specified for ext-th: %s" t = "WARNING: volume [/%s]: invalid value specified for ext-th: %s"
self.log(t % (vol.vpath, etv), 3) self.log(t % (vol.vpath, etv), 3)
vol.check_landmarks()
# d2d drops all database features for a volume # d2d drops all database features for a volume
for grp, rm in [["d2d", "e2d"], ["d2t", "e2t"], ["d2d", "e2v"]]: for grp, rm in [["d2d", "e2d"], ["d2t", "e2t"], ["d2d", "e2v"]]:
if not vol.flags.get(grp, False): if not vol.flags.get(grp, False):
@@ -2670,6 +2719,7 @@ class AuthSrv(object):
"def_hcols": list(vf.get("mth") or []), "def_hcols": list(vf.get("mth") or []),
"unlist0": vf.get("unlist") or "", "unlist0": vf.get("unlist") or "",
"see_dots": self.args.see_dots, "see_dots": self.args.see_dots,
"dqdel": self.args.qdel,
"dgrid": "grid" in vf, "dgrid": "grid" in vf,
"dgsel": "gsel" in vf, "dgsel": "gsel" in vf,
"dnsort": "nsort" in vf, "dnsort": "nsort" in vf,

View File

@@ -222,6 +222,7 @@ flagcats = {
"d2d": "disables all database stuff, overrides -e2*", "d2d": "disables all database stuff, overrides -e2*",
"hist=/tmp/cdb": "puts thumbnails and indexes at that location", "hist=/tmp/cdb": "puts thumbnails and indexes at that location",
"dbpath=/tmp/cdb": "puts indexes at that location", "dbpath=/tmp/cdb": "puts indexes at that location",
"landmark=foo": "disable db if file foo doesn't exist",
"scan=60": "scan for new files every 60sec, same as --re-maxage", "scan=60": "scan for new files every 60sec, same as --re-maxage",
"nohash=\\.iso$": "skips hashing file contents if path matches *.iso", "nohash=\\.iso$": "skips hashing file contents if path matches *.iso",
"noidx=\\.iso$": "fully ignores the contents at paths matching *.iso", "noidx=\\.iso$": "fully ignores the contents at paths matching *.iso",

View File

@@ -5573,7 +5573,7 @@ class HttpCli(object):
db.commit() db.commit()
db.close() db.close()
self.conn.hsrv.broker.ask("reload", False, False).get() self.conn.hsrv.broker.ask("reload", False, True).get()
self.redirect("", "?idp") self.redirect("", "?idp")
return True return True
@@ -5657,7 +5657,7 @@ class HttpCli(object):
cur.connection.commit() cur.connection.commit()
if reload: if reload:
self.conn.hsrv.broker.ask("reload", False, False).get() self.conn.hsrv.broker.ask("reload", False, True).get()
self.conn.hsrv.broker.ask("up2k.wake_rescanner").get() self.conn.hsrv.broker.ask("up2k.wake_rescanner").get()
self.redirect("", "?shares") self.redirect("", "?shares")
@@ -5749,7 +5749,7 @@ class HttpCli(object):
cur.execute(q, (skey, fn)) cur.execute(q, (skey, fn))
cur.connection.commit() cur.connection.commit()
self.conn.hsrv.broker.ask("reload", False, False).get() self.conn.hsrv.broker.ask("reload", False, True).get()
self.conn.hsrv.broker.ask("up2k.wake_rescanner").get() self.conn.hsrv.broker.ask("up2k.wake_rescanner").get()
fn = quotep(fns[0]) if len(fns) == 1 else "" fn = quotep(fns[0]) if len(fns) == 1 else ""

View File

@@ -1379,6 +1379,10 @@ class Up2k(object):
t = "volume /%s at [%s] is empty; will not be indexed as this could be due to an offline filesystem" t = "volume /%s at [%s] is empty; will not be indexed as this could be due to an offline filesystem"
self.log(t % (vol.vpath, rtop), 6) self.log(t % (vol.vpath, rtop), 6)
return True, False return True, False
if not vol.check_landmarks():
t = "volume /%s at [%s] will not be indexed due to bad landmarks"
self.log(t % (vol.vpath, rtop), 6)
return True, False
n_add, _, _ = self._build_dir( n_add, _, _ = self._build_dir(
db, db,

View File

@@ -1974,7 +1974,7 @@ def rand_name(fdir: str, fn: str, rnd: int) -> str:
return fn return fn
def gen_filekey(alg: int, salt: str, fspath: str, fsize: int, inode: int) -> str: def _gen_filekey(alg: int, salt: str, fspath: str, fsize: int, inode: int) -> str:
if alg == 1: if alg == 1:
zs = "%s %s %s %s" % (salt, fspath, fsize, inode) zs = "%s %s %s %s" % (salt, fspath, fsize, inode)
else: else:
@@ -1984,6 +1984,13 @@ def gen_filekey(alg: int, salt: str, fspath: str, fsize: int, inode: int) -> str
return ub64enc(hashlib.sha512(zb).digest()).decode("ascii") return ub64enc(hashlib.sha512(zb).digest()).decode("ascii")
def _gen_filekey_w(alg: int, salt: str, fspath: str, fsize: int, inode: int) -> str:
return _gen_filekey(alg, salt, fspath.replace("/", "\\"), fsize, inode)
gen_filekey = _gen_filekey_w if ANYWIN else _gen_filekey
def gen_filekey_dbg( def gen_filekey_dbg(
alg: int, alg: int,
salt: str, salt: str,

View File

@@ -226,6 +226,7 @@ var Ls = {
"ct_csel": 'use CTRL and SHIFT for file selection in grid-view">sel', "ct_csel": 'use CTRL and SHIFT for file selection in grid-view">sel',
"ct_ihop": 'when the image viewer is closed, scroll down to the last viewed file">g⮯', "ct_ihop": 'when the image viewer is closed, scroll down to the last viewed file">g⮯',
"ct_dots": 'show hidden files (if server permits)">dotfiles', "ct_dots": 'show hidden files (if server permits)">dotfiles',
"ct_qdel": 'when deleting files, only ask for confirmation once">qdel',
"ct_dir1st": 'sort folders before files">📁 first', "ct_dir1st": 'sort folders before files">📁 first',
"ct_nsort": 'natural sort (for filenames with leading digits)">nsort', "ct_nsort": 'natural sort (for filenames with leading digits)">nsort',
"ct_readme": 'show README.md in folder listings">📜 readme', "ct_readme": 'show README.md in folder listings">📜 readme',
@@ -850,6 +851,7 @@ var Ls = {
"ct_csel": 'bruk tastene CTRL og SHIFT for markering av filer i ikonvisning">merk', "ct_csel": 'bruk tastene CTRL og SHIFT for markering av filer i ikonvisning">merk',
"ct_ihop": 'bla ned til sist viste bilde når bildeviseren lukkes">g⮯', "ct_ihop": 'bla ned til sist viste bilde når bildeviseren lukkes">g⮯',
"ct_dots": 'vis skjulte filer (gitt at serveren tillater det)">.synlig', "ct_dots": 'vis skjulte filer (gitt at serveren tillater det)">.synlig',
"ct_qdel": 'sletteknappen spør bare én gang om bekreftelse">hurtig🗑',
"ct_dir1st": 'sorter slik at mapper kommer foran filer">📁 først', "ct_dir1st": 'sorter slik at mapper kommer foran filer">📁 først',
"ct_nsort": 'naturlig sortering (forstår tall i filnavn)">nsort', "ct_nsort": 'naturlig sortering (forstår tall i filnavn)">nsort',
"ct_readme": 'vis README.md nedenfor filene">📜 readme', "ct_readme": 'vis README.md nedenfor filene">📜 readme',
@@ -1474,6 +1476,7 @@ var Ls = {
"ct_csel": '在网格视图中使用 CTRL 和 SHIFT 进行文件选择">CTRL', "ct_csel": '在网格视图中使用 CTRL 和 SHIFT 进行文件选择">CTRL',
"ct_ihop": '当图像查看器关闭时,滚动到最后查看的文件">滚动', "ct_ihop": '当图像查看器关闭时,滚动到最后查看的文件">滚动',
"ct_dots": '显示隐藏文件(如果服务器允许)">隐藏文件', "ct_dots": '显示隐藏文件(如果服务器允许)">隐藏文件',
"ct_qdel": '删除文件时,只需确认一次">快删', //m
"ct_dir1st": '在文件之前排序文件夹">📁 排序', "ct_dir1st": '在文件之前排序文件夹">📁 排序',
"ct_nsort": '正确排序以数字开头的文件名">数字排序', //m "ct_nsort": '正确排序以数字开头的文件名">数字排序', //m
"ct_readme": '在文件夹列表中显示 README.md">📜 readme', "ct_readme": '在文件夹列表中显示 README.md">📜 readme',
@@ -2090,6 +2093,7 @@ ebi('op_cfg').innerHTML = (
' <a id="csel" class="tgl btn" href="#" tt="' + L.ct_csel + '</a>\n' + ' <a id="csel" class="tgl btn" href="#" tt="' + L.ct_csel + '</a>\n' +
' <a id="ihop" class="tgl btn" href="#" tt="' + L.ct_ihop + '</a>\n' + ' <a id="ihop" class="tgl btn" href="#" tt="' + L.ct_ihop + '</a>\n' +
' <a id="dotfiles" class="tgl btn" href="#" tt="' + L.ct_dots + '</a>\n' + ' <a id="dotfiles" class="tgl btn" href="#" tt="' + L.ct_dots + '</a>\n' +
' <a id="qdel" class="tgl btn" href="#" tt="' + L.ct_qdel + '</a>\n' +
' <a id="dir1st" class="tgl btn" href="#" tt="' + L.ct_dir1st + '</a>\n' + ' <a id="dir1st" class="tgl btn" href="#" tt="' + L.ct_dir1st + '</a>\n' +
' <a id="nsort" class="tgl btn" href="#" tt="' + L.ct_nsort + '</a>\n' + ' <a id="nsort" class="tgl btn" href="#" tt="' + L.ct_nsort + '</a>\n' +
' <a id="ireadme" class="tgl btn" href="#" tt="' + L.ct_readme + '</a>\n' + ' <a id="ireadme" class="tgl btn" href="#" tt="' + L.ct_readme + '</a>\n' +
@@ -5453,7 +5457,16 @@ var fileman = (function () {
deleter(); deleter();
} }
var asks = r.qdel ? 1 : 2;
if (dqdel === 0)
asks -= 1;
if (!asks)
return deleter();
modal.confirm('<h6 style="color:#900">' + L.danger + '</h6>\n<b>' + L.fd_warn1.format(vps.length) + '</b><ul>' + uricom_adec(vps, true).join('') + '</ul>', function () { modal.confirm('<h6 style="color:#900">' + L.danger + '</h6>\n<b>' + L.fd_warn1.format(vps.length) + '</b><ul>' + uricom_adec(vps, true).join('') + '</ul>', function () {
if (asks === 1)
return deleter();
modal.confirm(L.fd_warn2, deleter, null); modal.confirm(L.fd_warn2, deleter, null);
}, null); }, null);
}; };
@@ -5814,6 +5827,8 @@ var fileman = (function () {
r.bus.onmessage(); r.bus.onmessage();
}; };
bcfg_bind(r, 'qdel', 'qdel', dqdel == 1);
bren.onclick = r.rename; bren.onclick = r.rename;
bdel.onclick = r.delete; bdel.onclick = r.delete;
bcut.onclick = r.cut; bcut.onclick = r.cut;

View File

@@ -1,3 +1,24 @@
▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
# 2025-0721-2307 `v1.18.3` drop the umask
## 🧪 new features
* #181 the default chmod (unix-permissions) of new files and folders can now be changed 9921c43e
* `--chmod-d` or volflag `chmod_d` sets directory permissions; default is 755
* `--chmod-f` or volflag `chmod_f` sets file permissions; default is usually 644 (OS-defined)
* see `--help-chmod` which explains the numbers
## 🩹 bugfixes
* #179 couldn't combine `--shr` (shares) and `--xvol` (symlink-guard) 0f0f8d90
* #180 gallery buttons could still be clicked when faded-out 8c32b0e7
* rss-feeds were slightly busted when combined with rp-loc (location-based proxying) 56d3bcf5
* music-playback within search-results no longer jumps into the next folder at end-of-list 9bc4c5d2
* video-playback on iOS now behaves like on all other platforms 78605d9a
* (it would force-switch into fullscreen because that's their default)
▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
# 2025-0707-1419 `v1.18.2` idp-vol persistence # 2025-0707-1419 `v1.18.2` idp-vol persistence

View File

@@ -121,7 +121,7 @@ var tl_browser = {
"file-manager", "file-manager",
["G", "toggle list / grid view"], ["G", "toggle list / grid view"],
["T", "toggle thumbnails / icons"], ["T", "toggle thumbnails / icons"],
["🡅 A/D", "thumbnail size"], [" A/D", "thumbnail size"],
["ctrl-K", "delete selected"], ["ctrl-K", "delete selected"],
["ctrl-X", "cut selection to clipboard"], ["ctrl-X", "cut selection to clipboard"],
["ctrl-C", "copy selection to clipboard"], ["ctrl-C", "copy selection to clipboard"],
@@ -131,9 +131,9 @@ var tl_browser = {
"file-list-sel", "file-list-sel",
["space", "toggle file selection"], ["space", "toggle file selection"],
["🡑/🡓", "move selection cursor"], ["↑/↓", "move selection cursor"],
["ctrl 🡑/🡓", "move cursor and viewport"], ["ctrl ↑/↓", "move cursor and viewport"],
["🡅 🡑/🡓", "select prev/next file"], ["⇧ ↑/↓", "select prev/next file"],
["ctrl-A", "select all files / folders"], ["ctrl-A", "select all files / folders"],
], [ ], [
"navigation", "navigation",
@@ -156,7 +156,7 @@ var tl_browser = {
["Home/End", "first/last pic"], ["Home/End", "first/last pic"],
["F", "fullscreen"], ["F", "fullscreen"],
["R", "rotate clockwise"], ["R", "rotate clockwise"],
["🡅 R", "rotate ccw"], [" R", "rotate ccw"],
["S", "select pic"], ["S", "select pic"],
["Y", "download pic"], ["Y", "download pic"],
], [ ], [
@@ -312,6 +312,7 @@ var tl_browser = {
"ct_csel": 'use CTRL and SHIFT for file selection in grid-view">sel', "ct_csel": 'use CTRL and SHIFT for file selection in grid-view">sel',
"ct_ihop": 'when the image viewer is closed, scroll down to the last viewed file">g⮯', "ct_ihop": 'when the image viewer is closed, scroll down to the last viewed file">g⮯',
"ct_dots": 'show hidden files (if server permits)">dotfiles', "ct_dots": 'show hidden files (if server permits)">dotfiles',
"ct_qdel": 'when deleting files, only ask for confirmation once">qdel',
"ct_dir1st": 'sort folders before files">📁 first', "ct_dir1st": 'sort folders before files">📁 first',
"ct_nsort": 'natural sort (for filenames with leading digits)">nsort', "ct_nsort": 'natural sort (for filenames with leading digits)">nsort',
"ct_readme": 'show README.md in folder listings">📜 readme', "ct_readme": 'show README.md in folder listings">📜 readme',

View File

@@ -152,7 +152,7 @@ class Cfg(Namespace):
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" 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"
ka.update(**{k: None for k in ex.split()}) ka.update(**{k: None for k in ex.split()})
ex = "hash_mt hsortn safe_dedup srch_time tail_fd tail_rate u2abort u2j u2sz" ex = "hash_mt hsortn qdel safe_dedup srch_time tail_fd tail_rate u2abort u2j u2sz"
ka.update(**{k: 1 for k in ex.split()}) ka.update(**{k: 1 for k in ex.split()})
ex = "au_vol dl_list mtab_age reg_cap s_thead s_tbody tail_tmax tail_who th_convt ups_who zip_who" ex = "au_vol dl_list mtab_age reg_cap s_thead s_tbody tail_tmax tail_who th_convt ups_who zip_who"