mirror of
https://github.com/9001/copyparty.git
synced 2026-06-18 20:22:27 -06:00
Merge remote-tracking branch 'upstream/HEAD' into uiv1.5
This commit is contained in:
commit
715e8ada11
31
README.md
31
README.md
|
|
@ -120,6 +120,7 @@ built in Norway 🇳🇴 with contributions from [not-norway](https://github.com
|
|||
* [packages](#packages) - the party might be closer than you think
|
||||
* [arch package](#arch-package) - `pacman -S copyparty` (in [arch linux extra](https://archlinux.org/packages/extra/any/copyparty/))
|
||||
* [fedora package](#fedora-package) - does not exist yet
|
||||
* [gentoo ::guru package](#gentoo-guru-package) - `emerge www-servers/copyparty::guru` (in [::guru](https://wiki.gentoo.org/wiki/Project:GURU))
|
||||
* [homebrew formulae](#homebrew-formulae) - `brew install copyparty ffmpeg`
|
||||
* [nix package](#nix-package) - `nix profile install github:9001/copyparty`
|
||||
* [nixos module](#nixos-module)
|
||||
|
|
@ -183,7 +184,7 @@ enable thumbnails (images/audio/video), media indexing, and audio transcoding by
|
|||
* **Alpine:** `apk add py3-pillow ffmpeg`
|
||||
* **Debian:** `apt install --no-install-recommends python3-pil ffmpeg`
|
||||
* **Fedora:** rpmfusion + `dnf install python3-pillow ffmpeg --allowerasing`
|
||||
* **FreeBSD:** `pkg install py39-sqlite3 py39-pillow ffmpeg`
|
||||
* **FreeBSD:** `pkg install py311-sqlite3 py311-pillow ffmpeg`
|
||||
* **MacOS:** `port install py-Pillow ffmpeg`
|
||||
* **MacOS** (alternative): `brew install pillow ffmpeg`
|
||||
* **Windows:** `python -m pip install --user -U Pillow`
|
||||
|
|
@ -607,10 +608,12 @@ and if you want to use config files instead of commandline args (good!) then her
|
|||
|
||||
hiding specific subfolders by mounting another volume on top of them
|
||||
|
||||
for example `-v /mnt::r -v /var/empty:web/certs:r` mounts the server folder `/mnt` as the webroot, but another volume is mounted at `/web/certs` -- so visitors can only see the contents of `/mnt` and `/mnt/web` (at URLs `/` and `/web`), but not `/mnt/web/certs` because URL `/web/certs` is mapped to `/var/empty`
|
||||
for example `-v /mnt::r -v /var/empty:web/certs:` (note: no permissions) mounts the server folder `/mnt` as the webroot, but another volume is mounted at `/web/certs` -- so visitors can only see the contents of `/mnt` and `/mnt/web` (at URLs `/` and `/web`), but not `/mnt/web/certs` because URL `/web/certs` is mapped to `/var/empty`
|
||||
|
||||
the example config file right above this section may explain this better; the first volume `/` is mapped to `/srv` which means http://127.0.0.1:3923/music would try to read `/srv/music` on the server filesystem, but since there's another volume at `/music` mapped to `/mnt/music` then it'll go to `/mnt/music` instead
|
||||
|
||||
so, to shadow a file/folder, define a volume but leave out the `accs:` section
|
||||
|
||||
> ℹ️ this also works for single files, because files can also be volumes
|
||||
|
||||
|
||||
|
|
@ -2539,6 +2542,7 @@ buggy feature? rip it out by setting any of the following environment variables
|
|||
| -------------------- | ------------ |
|
||||
| `PRTY_NO_CTYPES` | do not use features from external libraries such as kernel32 |
|
||||
| `PRTY_NO_DB_LOCK` | do not lock session/shares-databases for exclusive access |
|
||||
| `PRTY_NO_ENVEXPAND` | do not expand environment-variables in configs and args |
|
||||
| `PRTY_NO_IFADDR` | disable ip/nic discovery by poking into your OS with ctypes |
|
||||
| `PRTY_NO_IMPRESO` | do not try to load js/css files using `importlib.resources` |
|
||||
| `PRTY_NO_IPV6` | disable some ipv6 support (should not be necessary since windows 2000) |
|
||||
|
|
@ -2585,6 +2589,23 @@ after installing, start either the system service or the user service and naviga
|
|||
does not exist yet; there are rumours that it is being packaged! keep an eye on this space...
|
||||
|
||||
|
||||
## gentoo ::guru package
|
||||
|
||||
`emerge www-servers/copyparty::guru` (in [::guru](https://wiki.gentoo.org/wiki/Project:GURU))
|
||||
|
||||
but first enable the `::guru` repo;
|
||||
|
||||
```bash
|
||||
emerge -an app-eselect/eselect-repository
|
||||
eselect repository enable guru
|
||||
emerge --sync guru
|
||||
```
|
||||
|
||||
to start the service as a user:
|
||||
* OpenRC: `rc-service -U copyparty start && rc-update -U add copyparty default`
|
||||
* systemd: [todo]
|
||||
|
||||
|
||||
## homebrew formulae
|
||||
|
||||
`brew install copyparty ffmpeg` -- https://formulae.brew.sh/formula/copyparty
|
||||
|
|
@ -2729,6 +2750,12 @@ services.copyparty = {
|
|||
};
|
||||
# you may increase the open file limit for the process
|
||||
openFilesLimit = 8192;
|
||||
|
||||
# override the package used by the module to add dependencies, e.g. for hooks
|
||||
package = pkgs.copyparty.override {
|
||||
# provides exiftool for bin/hooks/image-noexif.py
|
||||
extraPackages = [ pkgs.exiftool ];
|
||||
};
|
||||
};
|
||||
```
|
||||
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
# and copyparty replaces %Y-%m%d with Year-MonthDay, so the
|
||||
# full path will be something like /var/log/copyparty/2023-1130.txt
|
||||
# (note: enable compression by adding .xz at the end)
|
||||
# q, lo: $LOGS_DIRECTORY/%Y-%m%d.log
|
||||
# q, lo: ${LOGS_DIRECTORY}/%Y-%m%d.log
|
||||
|
||||
# p: 80,443,3923 # listen on 80/443 as well (requires CAP_NET_BIND_SERVICE)
|
||||
# i: 127.0.0.1 # only allow connections from localhost (reverse-proxies)
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@
|
|||
# and copyparty replaces %Y-%m%d with Year-MonthDay, so the
|
||||
# full path will be something like /var/log/copyparty/2023-1130.txt
|
||||
# (note: enable compression by adding .xz at the end)
|
||||
q, lo: $LOGS_DIRECTORY/%Y-%m%d.log
|
||||
q, lo: ${LOGS_DIRECTORY}/%Y-%m%d.log
|
||||
|
||||
# enable version-checker by uncommenting one of the 'vc-url' lines below; this will
|
||||
# periodically check if your copyparty version has a known security vulnerability,
|
||||
|
|
|
|||
|
|
@ -44,6 +44,14 @@ ANYWIN = WINDOWS or sys.platform in ["msys", "cygwin"]
|
|||
|
||||
MACOS = platform.system() == "Darwin"
|
||||
|
||||
FREEBSD = platform.system() == "FreeBSD"
|
||||
|
||||
OPENBSD = platform.system() == "OpenBSD"
|
||||
|
||||
ANYBSD = FREEBSD or OPENBSD
|
||||
|
||||
UNIX = MACOS or ANYBSD
|
||||
|
||||
GRAAL = platform.python_implementation() == "GraalVM"
|
||||
|
||||
EXE = bool(getattr(sys, "frozen", False))
|
||||
|
|
|
|||
|
|
@ -65,6 +65,10 @@ from .util import (
|
|||
b64enc,
|
||||
ctypes,
|
||||
dedent,
|
||||
expand_osenv_c,
|
||||
expand_osenv_cs,
|
||||
expand_osenv_noop,
|
||||
expand_osenv_s,
|
||||
has_resource,
|
||||
load_resource,
|
||||
min_ex,
|
||||
|
|
@ -427,9 +431,22 @@ def configure_ssl_ciphers(al: argparse.Namespace) -> None:
|
|||
sys.exit(0)
|
||||
|
||||
|
||||
def expand_cvars(argv) -> list[str]:
|
||||
n = 0
|
||||
for v in argv:
|
||||
if "=" in v:
|
||||
a, b = v.split("=", 1)
|
||||
v = "%s=%s" % (a, os.path.expanduser(expand_osenv_c(b)))
|
||||
else:
|
||||
v = os.path.expanduser(expand_osenv_c(v))
|
||||
argv[n] = v
|
||||
n += 1
|
||||
return argv
|
||||
|
||||
|
||||
def args_from_cfg(cfg_path: str) -> list[str]:
|
||||
lines: list[str] = []
|
||||
expand_config_file(None, lines, cfg_path, "")
|
||||
expand_config_file(None, expand_osenv_c, lines, cfg_path, "")
|
||||
lines = upgrade_cfg_fmt(None, argparse.Namespace(vc=False), lines, "")
|
||||
|
||||
ret: list[str] = []
|
||||
|
|
@ -453,10 +470,12 @@ def args_from_cfg(cfg_path: str) -> list[str]:
|
|||
else:
|
||||
ret.append(prefix + k + "=" + v)
|
||||
|
||||
return ret
|
||||
return expand_cvars(ret)
|
||||
|
||||
|
||||
def expand_cfg(argv) -> list[str]:
|
||||
argv = expand_cvars(argv)
|
||||
|
||||
if CFG_DEF:
|
||||
supp = args_from_cfg(CFG_DEF[0])
|
||||
argv = argv[:1] + supp + argv[1:]
|
||||
|
|
@ -1197,6 +1216,7 @@ def add_general(ap, nc, srvname):
|
|||
ap2.add_argument("--name-url", metavar="TXT", type=u, help="URL for server name hyperlink (displayed topleft in browser)")
|
||||
ap2.add_argument("--name-html", type=u, help=argparse.SUPPRESS)
|
||||
ap2.add_argument("--site", metavar="URL", type=u, default="", help="public URL to assume when creating links; example: [\033[32mhttps://example.com/\033[0m]")
|
||||
ap2.add_argument("--env-expand", metavar="N", type=int, default=-1, help="syntax to expect for environment-variables to expand in config-files; [\033[32m0\033[0m]=disable, [\033[32m1\033[0m]=$VAR (old syntax (scary)), [\033[32m2\033[0m]=${VAR} (new syntax (recommended))")
|
||||
ap2.add_argument("--mime", metavar="EXT=MIME", type=u, action="append", help="\033[34mREPEATABLE:\033[0m map file \033[33mEXT\033[0mension to \033[33mMIME\033[0mtype, for example [\033[32mjpg=image/jpeg\033[0m]")
|
||||
ap2.add_argument("--mimes", action="store_true", help="list default mimetype mapping and exit")
|
||||
ap2.add_argument("--rmagic", action="store_true", help="do expensive analysis to improve accuracy of returned mimetypes; will make file-downloads, rss, and webdav slower (volflag=rmagic)")
|
||||
|
|
@ -1520,6 +1540,7 @@ def add_smb(ap):
|
|||
ap2.add_argument("--smb-nwa-1", action="store_true", help="truncate directory listings to 64kB (~400 files); avoids impacket-0.11 bug, fixes impacket-0.12 performance")
|
||||
ap2.add_argument("--smb-nwa-2", action="store_true", help="disable impacket workaround for filecopy globs")
|
||||
ap2.add_argument("--smba", action="store_true", help="small performance boost: disable per-account permissions, enables account coalescing instead (if one user has write/delete-access, then everyone does)")
|
||||
ap2.add_argument("--smb6", action="store_true", help="enable IPv6")
|
||||
ap2.add_argument("--smbv", action="store_true", help="verbose")
|
||||
ap2.add_argument("--smbvv", action="store_true", help="verboser")
|
||||
ap2.add_argument("--smbvvv", action="store_true", help="verbosest")
|
||||
|
|
@ -1740,7 +1761,7 @@ def add_thumbnail(ap):
|
|||
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,sr2,srf,srw,x3f", help="image formats to decode using rawpy")
|
||||
ap2.add_argument("--th-r-ffi", metavar="T,T", type=u, default="apng,avif,avifs,bmp,cbz,dds,dib,epub,fit,fits,fts,gif,hdr,heic,heics,heif,heifs,icns,ico,jp2,jpeg,jpg,jpx,jxl,pbm,pcx,pfm,pgm,png,pnm,ppm,psd,qoi,sgi,tga,tif,tiff,webp,xbm,xpm", help="image formats to decode using ffmpeg")
|
||||
ap2.add_argument("--th-r-ffv", metavar="T,T", type=u, default="3gp,asf,av1,avc,avi,flv,h264,h265,hevc,m4v,mjpeg,mjpg,mkv,mov,mp4,mpeg,mpeg2,mpegts,mpg,mpg2,mts,nut,ogm,ogv,rm,ts,vob,webm,wmv", help="video formats to decode using ffmpeg")
|
||||
ap2.add_argument("--th-r-ffa", metavar="T,T", type=u, default="aac,ac3,aif,aiff,alac,alaw,amr,apac,ape,au,bonk,dfpwm,dts,flac,gsm,ilbc,it,itgz,itxz,itz,m4a,m4b,m4r,mdgz,mdxz,mdz,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,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-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", help="audio/image formats to decompress before passing to ffmpeg")
|
||||
|
||||
|
|
@ -1868,8 +1889,10 @@ def add_ui(ap, retry: int):
|
|||
ap2.add_argument("--gsel", action="store_true", help="select files in grid by ctrl-click (volflag=gsel)")
|
||||
ap2.add_argument("--localtime", action="store_true", help="default to local timezone instead of UTC")
|
||||
ap2.add_argument("--ui-filesz", metavar="FMT", type=u, default="4", help="default filesize format; one of these: 0, 1, 2, 2c, 3, 3c, 4, 4c, 5, 5c, fuzzy (see UI)")
|
||||
ap2.add_argument("--gauto", metavar="PERCENT", type=int, default=0, help="switch to gridview if more than \033[33mPERCENT\033[0m of files are pics/vids; 0=disabled")
|
||||
ap2.add_argument("--rcm", metavar="TXT", default="yy", help="rightclick-menu; two yes/no options: 1st y/n is enable-custom-menu, 2nd y/n is enable-double")
|
||||
ap2.add_argument("--lang", metavar="LANG", type=u, default="eng", help="language, for example \033[32meng\033[0m / \033[32mnor\033[0m / ...")
|
||||
ap2.add_argument("--glang", action="store_true", help="guess the browser's default language, otherwise fall back to \033[33m--lang\033[0m")
|
||||
ap2.add_argument("--theme", metavar="NUM", type=int, default=0, help="default theme to use (0..%d)" % (THEMES - 1,))
|
||||
ap2.add_argument("--themes", metavar="NUM", type=int, default=THEMES, help="number of themes installed")
|
||||
ap2.add_argument("--au-vol", metavar="0-100", type=int, default=50, choices=range(0, 101), help="default audio/video volume percent")
|
||||
|
|
@ -2176,6 +2199,15 @@ def main(argv: Optional[list[str]] = None) -> None:
|
|||
|
||||
quotecheck(al)
|
||||
|
||||
if al.env_expand == 2:
|
||||
al.shenvexp = expand_osenv_c
|
||||
elif al.env_expand == 1:
|
||||
al.shenvexp = expand_osenv_s
|
||||
elif al.env_expand == 0:
|
||||
al.shenvexp = expand_osenv_noop
|
||||
else:
|
||||
al.shenvexp = expand_osenv_cs
|
||||
|
||||
if al.chdir:
|
||||
os.chdir(al.chdir)
|
||||
|
||||
|
|
|
|||
|
|
@ -56,7 +56,7 @@ if HAVE_SQLITE3:
|
|||
if True: # pylint: disable=using-constant-test
|
||||
from collections.abc import Iterable
|
||||
|
||||
from typing import Any, Generator, Optional, Sequence, Union
|
||||
from typing import Any, Callable, Generator, Optional, Sequence, Union
|
||||
|
||||
from .util import NamedLogger, RootLogger
|
||||
|
||||
|
|
@ -541,13 +541,11 @@ class VFS(object):
|
|||
|
||||
hist = flags.get("hist")
|
||||
if hist and hist != "-":
|
||||
zs = "{}/{}".format(hist.rstrip("/"), name)
|
||||
flags["hist"] = os.path.expandvars(os.path.expanduser(zs))
|
||||
flags["hist"] = "%s/%s" % (hist.rstrip("/"), name)
|
||||
|
||||
dbp = flags.get("dbpath")
|
||||
if dbp and dbp != "-":
|
||||
zs = "{}/{}".format(dbp.rstrip("/"), name)
|
||||
flags["dbpath"] = os.path.expandvars(os.path.expanduser(zs))
|
||||
flags["dbpath"] = "%s/%s" % (dbp.rstrip("/"), name)
|
||||
|
||||
return flags
|
||||
|
||||
|
|
@ -1279,7 +1277,7 @@ class AuthSrv(object):
|
|||
daxs: dict[str, AXS],
|
||||
mflags: dict[str, dict[str, Any]],
|
||||
) -> tuple[str, str]:
|
||||
src = os.path.expandvars(os.path.expanduser(src))
|
||||
src = os.path.expanduser(self.args.shenvexp(src))
|
||||
src = absreal(src)
|
||||
dst = dst.strip("/")
|
||||
|
||||
|
|
@ -1372,7 +1370,7 @@ class AuthSrv(object):
|
|||
) -> None:
|
||||
self.line_ctr = 0
|
||||
|
||||
expand_config_file(self.log, cfg_lines, fp, "")
|
||||
expand_config_file(self.log, self.args.shenvexp, cfg_lines, fp, "")
|
||||
if self.args.vc:
|
||||
lns = ["{:4}: {}".format(n, s) for n, s in enumerate(cfg_lines, 1)]
|
||||
self.log("expanded config file (unprocessed):\n" + "\n".join(lns))
|
||||
|
|
@ -2174,7 +2172,7 @@ class AuthSrv(object):
|
|||
if vflag == "-":
|
||||
pass
|
||||
elif vflag:
|
||||
vflag = os.path.expandvars(os.path.expanduser(vflag))
|
||||
vflag = os.path.expanduser(self.args.shenvexp(vflag))
|
||||
vol.histpath = vol.dbpath = uncyg(vflag) if WINDOWS else vflag
|
||||
elif self.args.hist:
|
||||
for nch in range(len(hid)):
|
||||
|
|
@ -2209,7 +2207,7 @@ class AuthSrv(object):
|
|||
if vflag == "-":
|
||||
pass
|
||||
elif vflag:
|
||||
vflag = os.path.expandvars(os.path.expanduser(vflag))
|
||||
vflag = os.path.expanduser(self.args.shenvexp(vflag))
|
||||
vol.dbpath = uncyg(vflag) if WINDOWS else vflag
|
||||
elif self.args.dbpath:
|
||||
for nch in range(len(hid)):
|
||||
|
|
@ -3258,6 +3256,7 @@ class AuthSrv(object):
|
|||
"idxh": int(self.args.ih),
|
||||
"dutc": not self.args.localtime,
|
||||
"dfszf": self.args.ui_filesz.strip("-"),
|
||||
"dgauto": self.args.gauto,
|
||||
"themes": self.args.themes,
|
||||
"turbolvl": self.args.turbo,
|
||||
"nosubtle": self.args.nosubtle,
|
||||
|
|
@ -3273,7 +3272,7 @@ class AuthSrv(object):
|
|||
for zs in zs.split():
|
||||
if vf.get(zs):
|
||||
js_htm[zs] = 1
|
||||
zs = "notooltips"
|
||||
zs = "glang notooltips"
|
||||
for zs in zs.split():
|
||||
if getattr(self.args, zs, False):
|
||||
js_htm[zs] = 1
|
||||
|
|
@ -3963,10 +3962,14 @@ def split_cfg_ln(ln: str) -> dict[str, Any]:
|
|||
|
||||
|
||||
def expand_config_file(
|
||||
log: Optional["NamedLogger"], ret: list[str], fp: str, ipath: str
|
||||
log: Optional["NamedLogger"],
|
||||
shenvexp: "Callable[[str], str]",
|
||||
ret: list[str],
|
||||
fp: str,
|
||||
ipath: str,
|
||||
) -> None:
|
||||
"""expand all % file includes"""
|
||||
fp = absreal(fp)
|
||||
fp = absreal(os.path.expanduser(shenvexp(fp)))
|
||||
if len(ipath.split(" -> ")) > 64:
|
||||
raise Exception("hit max depth of 64 includes")
|
||||
|
||||
|
|
@ -3997,7 +4000,7 @@ def expand_config_file(
|
|||
if fp2 in ipath:
|
||||
continue
|
||||
|
||||
expand_config_file(log, ret, fp2, ipath)
|
||||
expand_config_file(log, shenvexp, ret, fp2, ipath)
|
||||
|
||||
return
|
||||
|
||||
|
|
@ -4022,7 +4025,7 @@ def expand_config_file(
|
|||
fp2 = ln[1:].strip()
|
||||
fp2 = os.path.join(os.path.dirname(fp), fp2)
|
||||
ofs = len(ret)
|
||||
expand_config_file(log, ret, fp2, ipath)
|
||||
expand_config_file(log, shenvexp, ret, fp2, ipath)
|
||||
for n in range(ofs, len(ret)):
|
||||
ret[n] = pad + ret[n]
|
||||
continue
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import os
|
|||
import re
|
||||
import time
|
||||
|
||||
from .__init__ import ANYWIN, MACOS
|
||||
from .__init__ import ANYWIN, FREEBSD, MACOS, UNIX
|
||||
from .authsrv import AXS, VFS, AuthSrv
|
||||
from .bos import bos
|
||||
from .util import chkcmd, json_hesc, min_ex, undot
|
||||
|
|
@ -88,7 +88,7 @@ class Fstab(object):
|
|||
|
||||
def _from_sp_mount(self) -> dict[str, str]:
|
||||
sptn = r"^.*? on (.*) type ([^ ]+) \(.*"
|
||||
if MACOS:
|
||||
if MACOS or FREEBSD:
|
||||
sptn = r"^.*? on (.*) \(([^ ]+), .*"
|
||||
|
||||
ptn = re.compile(sptn)
|
||||
|
|
@ -118,7 +118,7 @@ class Fstab(object):
|
|||
|
||||
def build_tab(self) -> None:
|
||||
self.log("inspecting mtab for changes")
|
||||
dtab = self._from_sp_mount() if MACOS else self._from_proc()
|
||||
dtab = self._from_sp_mount() if UNIX else self._from_proc()
|
||||
|
||||
# keep empirically-correct values if mounttab unchanged
|
||||
srctab = str(sorted(dtab.items()))
|
||||
|
|
@ -130,7 +130,7 @@ class Fstab(object):
|
|||
|
||||
try:
|
||||
fuses = [mp for mp, fs in dtab.items() if fs == "fuseblk"]
|
||||
if not fuses or MACOS:
|
||||
if not fuses or UNIX:
|
||||
raise Exception()
|
||||
try:
|
||||
so, _ = chkcmd(["lsblk", "-nrfo", "FSTYPE,MOUNTPOINT"]) # centos6
|
||||
|
|
|
|||
|
|
@ -154,7 +154,7 @@ _ = (argparse, threading)
|
|||
|
||||
USED4SEC = {"usedforsecurity": False} if sys.version_info > (3, 9) else {}
|
||||
|
||||
ALL_COOKIES = "k304 no304 js idxh dots cppwd cppws".split()
|
||||
ALL_COOKIES = "cplng cppwd cppws dots idxh js k304 no304".split()
|
||||
|
||||
BADXFF = " due to dangerous misconfiguration (the http-header specified by --xff-hdr was received from an untrusted reverse-proxy)"
|
||||
BADXFF2 = ". Some copyparty features are now disabled as a safety measure.\n\n\n"
|
||||
|
|
@ -1800,7 +1800,11 @@ class HttpCli(object):
|
|||
topdir = {"vp": "", "st": st}
|
||||
fgen: Iterable[dict[str, Any]] = []
|
||||
|
||||
depth = self.headers.get("depth", "infinity").lower()
|
||||
if stat.S_ISDIR(st.st_mode):
|
||||
depth = self.headers.get("depth", "infinity").lower()
|
||||
else:
|
||||
depth = "0"
|
||||
|
||||
if depth == "infinity":
|
||||
# allow depth:0 from unmapped root, but require read-axs otherwise
|
||||
if not self.can_read and (self.vpath or self.asrv.vfs.realpath):
|
||||
|
|
@ -1809,12 +1813,6 @@ class HttpCli(object):
|
|||
self.log(t, 3)
|
||||
raise Pebkac(401, t)
|
||||
|
||||
if not stat.S_ISDIR(topdir["st"].st_mode):
|
||||
t = "depth:infinity can only be used on folders; %r is 0o%o"
|
||||
t = t % ("/" + self.vpath, topdir["st"])
|
||||
self.log(t, 3)
|
||||
raise Pebkac(400, t)
|
||||
|
||||
if not self.args.dav_inf:
|
||||
self.log("client wants --dav-inf", 3)
|
||||
zb = b'<?xml version="1.0" encoding="utf-8"?>\n<D:error xmlns:D="DAV:"><D:propfind-finite-depth/></D:error>'
|
||||
|
|
@ -1835,7 +1833,7 @@ class HttpCli(object):
|
|||
wrap=False,
|
||||
)
|
||||
|
||||
elif depth == "0" or not stat.S_ISDIR(st.st_mode):
|
||||
elif depth == "0":
|
||||
if depth == "0" and not self.vpath and not vn.realpath:
|
||||
# rootless server; give dummy listing
|
||||
self.can_read = True
|
||||
|
|
@ -3721,12 +3719,12 @@ class HttpCli(object):
|
|||
|
||||
fdir = fdir_base
|
||||
fname = sanitize_fn(p_file or "")
|
||||
abspath = os.path.join(fdir, fname)
|
||||
suffix = "-%.6f-%s" % (time.time(), dip)
|
||||
if p_file and not nullwrite:
|
||||
if rnd:
|
||||
fname = rand_name(fdir, fname, rnd)
|
||||
|
||||
abspath = os.path.join(fdir, fname)
|
||||
open_args = {"fdir": fdir, "suffix": suffix, "vf": vfs.flags}
|
||||
|
||||
if "replace" in self.uparam or "replace" in self.headers:
|
||||
|
|
@ -3744,7 +3742,7 @@ class HttpCli(object):
|
|||
tnam = fname = os.devnull
|
||||
fdir = abspath = ""
|
||||
|
||||
if xbu:
|
||||
if xbu and abspath:
|
||||
at = time.time() - lifetime
|
||||
hr = runhook(
|
||||
self.log,
|
||||
|
|
@ -3792,7 +3790,7 @@ class HttpCli(object):
|
|||
else:
|
||||
open_args["fdir"] = fdir
|
||||
|
||||
if p_file and not nullwrite:
|
||||
if abspath:
|
||||
bos.makedirs(fdir, vf=vfs.flags)
|
||||
|
||||
# reserve destination filename
|
||||
|
|
@ -3830,6 +3828,14 @@ class HttpCli(object):
|
|||
finally:
|
||||
f.close()
|
||||
|
||||
self.conn.nbyte += sz
|
||||
if not abspath:
|
||||
files.append(
|
||||
(sz, sha_hex, sha_b64, p_file or "(discarded)", fname, "")
|
||||
)
|
||||
tabspath = ""
|
||||
continue
|
||||
|
||||
if lim:
|
||||
lim.nup(self.ip)
|
||||
lim.bup(self.ip, sz)
|
||||
|
|
@ -3840,15 +3846,12 @@ class HttpCli(object):
|
|||
lim.chk_bup(self.ip)
|
||||
lim.chk_nup(self.ip)
|
||||
except:
|
||||
if not nullwrite:
|
||||
wunlink(self.log, tabspath, vfs.flags)
|
||||
wunlink(self.log, abspath, vfs.flags)
|
||||
wunlink(self.log, tabspath, vfs.flags)
|
||||
wunlink(self.log, abspath, vfs.flags)
|
||||
fname = os.devnull
|
||||
raise
|
||||
|
||||
if not nullwrite:
|
||||
atomic_move(self.log, tabspath, abspath, vfs.flags)
|
||||
|
||||
atomic_move(self.log, tabspath, abspath, vfs.flags)
|
||||
tabspath = ""
|
||||
|
||||
at = time.time() - lifetime
|
||||
|
|
@ -3903,9 +3906,7 @@ class HttpCli(object):
|
|||
abspath = ap2
|
||||
sz = bos.path.getsize(abspath)
|
||||
|
||||
files.append(
|
||||
(sz, sha_hex, sha_b64, p_file or "(discarded)", fname, abspath)
|
||||
)
|
||||
files.append((sz, sha_hex, sha_b64, p_file, fname, abspath))
|
||||
dbv, vrem = vfs.get_dbv(rem)
|
||||
self.conn.hsrv.broker.say(
|
||||
"up2k.hash_file",
|
||||
|
|
@ -3919,7 +3920,6 @@ class HttpCli(object):
|
|||
self.uname,
|
||||
True,
|
||||
)
|
||||
self.conn.nbyte += sz
|
||||
|
||||
except Pebkac:
|
||||
self.parser.drop()
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ from .__init__ import TYPE_CHECKING, EnvParams
|
|||
from .authsrv import AuthSrv # typechk
|
||||
from .httpcli import HttpCli
|
||||
from .u2idx import U2idx
|
||||
from .util import HMaccas, NetMap, shut_socket
|
||||
from .util import HMaccas, NetMap, min_ex, shut_socket
|
||||
|
||||
if True: # pylint: disable=using-constant-test
|
||||
from typing import Optional, Pattern, Union
|
||||
|
|
@ -194,12 +194,12 @@ class HttpConn(object):
|
|||
except Exception as ex:
|
||||
em = str(ex)
|
||||
|
||||
if "ALERT_CERTIFICATE_UNKNOWN" in em:
|
||||
# android-chrome keeps doing this
|
||||
pass
|
||||
if "ALERT_" in em:
|
||||
self.log("client refused our TLS cert or config: " + em, c=6)
|
||||
|
||||
else:
|
||||
self.log("handshake\033[0m " + em, c=5)
|
||||
t = "https-handshake failed, probably due to client:\n"
|
||||
self.log(t + min_ex(), c=5)
|
||||
|
||||
return
|
||||
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ from .util import (
|
|||
FFMPEG_URL,
|
||||
REKOBO_LKEY,
|
||||
VF_CAREFUL,
|
||||
expand_osenv_c,
|
||||
fsenc,
|
||||
gzip,
|
||||
min_ex,
|
||||
|
|
@ -86,7 +87,7 @@ class MParser(object):
|
|||
|
||||
while True:
|
||||
try:
|
||||
bp = os.path.expanduser(args)
|
||||
bp = os.path.expanduser(expand_osenv_c(args))
|
||||
if WINDOWS:
|
||||
bp = uncyg(bp)
|
||||
|
||||
|
|
@ -216,6 +217,7 @@ def au_unpk(
|
|||
def ffprobe(
|
||||
abspath: str, timeout: int = 60
|
||||
) -> tuple[dict[str, tuple[int, Any]], dict[str, list[Any]], list[Any], dict[str, Any]]:
|
||||
# ffprobe -hide_banner -show_streams -show_format --
|
||||
cmd = [
|
||||
b"ffprobe",
|
||||
b"-hide_banner",
|
||||
|
|
|
|||
|
|
@ -89,13 +89,15 @@ class SMB(object):
|
|||
smbserver.isInFileJail = self._is_in_file_jail
|
||||
self._disarm()
|
||||
|
||||
ip = next((x for x in self.args.smb_i if ":" not in x), None)
|
||||
zs = " " if self.args.smb6 else ":"
|
||||
ip = next((x for x in self.args.smb_i if zs not in x), None)
|
||||
if not ip:
|
||||
self.log("smb", "IPv6 not supported for SMB; listening on 0.0.0.0", 3)
|
||||
self.log("smb", "IPv6 not enabled with --smb6; listening on 0.0.0.0", 3)
|
||||
ip = "0.0.0.0"
|
||||
|
||||
port = int(self.args.smb_port)
|
||||
srv = smbserver.SimpleSMBServer(listenAddress=ip, listenPort=port)
|
||||
kw = {"ipv6": True} if ":" in ip else {}
|
||||
srv = smbserver.SimpleSMBServer(listenAddress=ip, listenPort=port, **kw)
|
||||
try:
|
||||
if self.accs:
|
||||
srv.setAuthCallback(self._auth_cb)
|
||||
|
|
@ -121,6 +123,7 @@ class SMB(object):
|
|||
|
||||
self.srv = srv
|
||||
self.stop = srv.stop
|
||||
ip = "[%s]" % (ip,) if kw else ip
|
||||
self.log("smb", "listening @ {}:{}".format(ip, port))
|
||||
|
||||
def nlog(self, msg: str, c: Union[int, str] = 0) -> None:
|
||||
|
|
|
|||
|
|
@ -1133,17 +1133,23 @@ class SvcHub(object):
|
|||
al.th_coversd_set = set(al.th_coversd)
|
||||
|
||||
for k in "c".split(" "):
|
||||
if self.args.env_expand in (0, 2):
|
||||
break
|
||||
|
||||
vl = getattr(al, k)
|
||||
if not vl:
|
||||
continue
|
||||
|
||||
vl = [os.path.expandvars(os.path.expanduser(x)) for x in vl]
|
||||
vl = [os.path.expanduser(self.args.shenvexp(x)) for x in vl]
|
||||
setattr(al, k, vl)
|
||||
|
||||
for k in "lo hist dbpath ssl_log".split(" "):
|
||||
if self.args.env_expand in (0, 2):
|
||||
break
|
||||
|
||||
vs = getattr(al, k)
|
||||
if vs:
|
||||
vs = os.path.expandvars(os.path.expanduser(vs))
|
||||
vs = os.path.expanduser(self.args.shenvexp(vs))
|
||||
setattr(al, k, vs)
|
||||
|
||||
for k in "idp_adm stats_u".split(" "):
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import socket
|
|||
import sys
|
||||
import time
|
||||
|
||||
from .__init__ import ANYWIN, PY2, TYPE_CHECKING, unicode
|
||||
from .__init__ import ANYWIN, OPENBSD, PY2, TYPE_CHECKING, UNIX, unicode
|
||||
from .cert import gencert
|
||||
from .qrkode import QrCode, qr2png, qr2svg, qr2txt, qrgen
|
||||
from .util import (
|
||||
|
|
@ -21,6 +21,7 @@ from .util import (
|
|||
VF_CAREFUL,
|
||||
Netdev,
|
||||
atomic_move,
|
||||
chkcmd,
|
||||
get_adapters,
|
||||
min_ex,
|
||||
sunpack,
|
||||
|
|
@ -510,6 +511,13 @@ class TcpSrv(object):
|
|||
return eps
|
||||
|
||||
def _extdevs_nix(self) -> Generator[str, None, None]:
|
||||
if UNIX:
|
||||
so, _ = chkcmd(["netstat", "-nrf", "inet"])
|
||||
for ln in so.split("\n"):
|
||||
if not ln.startswith("default"):
|
||||
continue
|
||||
yield ln.split()[7] if OPENBSD else ln.split()[3]
|
||||
return
|
||||
with open("/proc/net/route", "rb") as f:
|
||||
next(f)
|
||||
for ln in f:
|
||||
|
|
|
|||
|
|
@ -1219,6 +1219,13 @@ class ThumbSrv(object):
|
|||
self.log("conv2 %s [%s]" % (container, enc), 6)
|
||||
benc = enc.encode("ascii").split(b" ")
|
||||
|
||||
ac = b"2"
|
||||
try:
|
||||
if tags["chs"][1] in ("mono", "1", "1.0"):
|
||||
ac = b"1"
|
||||
except:
|
||||
pass
|
||||
|
||||
# fmt: off
|
||||
cmd = [
|
||||
b"ffmpeg",
|
||||
|
|
@ -1228,6 +1235,7 @@ class ThumbSrv(object):
|
|||
b"-i", fsenc(abspath),
|
||||
] + tagset + [
|
||||
b"-map", b"0:a:0",
|
||||
b"-ac", ac,
|
||||
] + benc + [
|
||||
b"-f", container,
|
||||
fsenc(tpath)
|
||||
|
|
@ -1268,6 +1276,7 @@ class ThumbSrv(object):
|
|||
b"-i", fsenc(abspath),
|
||||
b"-map_metadata", b"-1",
|
||||
b"-map", b"0:a:0",
|
||||
b"-ac", b"2",
|
||||
] + benc + [
|
||||
b"-f", b"opus",
|
||||
fsenc(tmp_opus)
|
||||
|
|
|
|||
|
|
@ -491,13 +491,12 @@ font woff woff2 otf ttf
|
|||
for v in vs.strip().split():
|
||||
MIMES[v] = "{}/{}".format(k, v)
|
||||
|
||||
for ln in """text md=plain txt=plain js=javascript
|
||||
for ln in """text md=plain js=javascript ass=plain ssa=plain txt=plain
|
||||
application 7z=x-7z-compressed tar=x-tar bz2=x-bzip2 gz=gzip rar=x-rar-compressed zst=zstd xz=x-xz lz=lzip cpio=x-cpio
|
||||
application msi=x-ms-installer cab=vnd.ms-cab-compressed rpm=x-rpm crx=x-chrome-extension
|
||||
application epub=epub+zip mobi=x-mobipocket-ebook lit=x-ms-reader rss=rss+xml atom=atom+xml torrent=x-bittorrent
|
||||
application p7s=pkcs7-signature dcm=dicom shx=vnd.shx shp=vnd.shp dbf=x-dbf gml=gml+xml gpx=gpx+xml amf=x-amf
|
||||
application swf=x-shockwave-flash m3u=vnd.apple.mpegurl db3=vnd.sqlite3 sqlite=vnd.sqlite3
|
||||
text ass=plain ssa=plain
|
||||
image jpg=jpeg xpm=x-xpixmap psd=vnd.adobe.photoshop jpf=jpx tif=tiff ico=x-icon djvu=vnd.djvu
|
||||
image heics=heic-sequence heifs=heif-sequence hdr=vnd.radiance svg=svg+xml
|
||||
image arw=x-sony-arw cr2=x-canon-cr2 crw=x-canon-crw dcr=x-kodak-dcr dng=x-adobe-dng erf=x-epson-erf
|
||||
|
|
@ -1502,8 +1501,7 @@ class Garda(object):
|
|||
return 0, ip
|
||||
|
||||
if ":" in ip:
|
||||
# assume /64 clients; drop 4 groups
|
||||
ip = IPv6Address(ip).exploded[:-20]
|
||||
ip = ipnorm(ip)
|
||||
|
||||
if prev and self.uniq:
|
||||
if self.prev.get(ip) == prev:
|
||||
|
|
@ -1564,6 +1562,43 @@ def dedent(txt: str) -> str:
|
|||
return "\n".join([ln[pad:] for ln in lns])
|
||||
|
||||
|
||||
def expand_osenv_noop(txt) -> str:
|
||||
return txt
|
||||
|
||||
|
||||
def _expand_osenv_c(txt) -> str:
|
||||
if "${" not in txt:
|
||||
return txt
|
||||
zsl = txt.split("${")
|
||||
ret = zsl[0]
|
||||
for v in zsl[1:]:
|
||||
if "}" not in v:
|
||||
raise Exception("missing '}' after %r in config-value %r" % (v, txt))
|
||||
a, b = v.split("}", 1)
|
||||
try:
|
||||
ret += os.environ[a] + b
|
||||
except:
|
||||
raise Exception("env-var %r not defined; config-value %r" % (a, txt))
|
||||
return ret
|
||||
|
||||
|
||||
if os.environ.get("PRTY_NO_ENVEXPAND"):
|
||||
expand_osenv_c = expand_osenv_noop
|
||||
expand_osenv_s = expand_osenv_noop
|
||||
else:
|
||||
expand_osenv_c = _expand_osenv_c
|
||||
expand_osenv_s = os.path.expandvars
|
||||
|
||||
|
||||
def expand_osenv_cs(txt) -> str:
|
||||
a = expand_osenv_c(txt)
|
||||
b = expand_osenv_s(txt)
|
||||
if a == b:
|
||||
return a
|
||||
t = "config-value %r is using the old syntax for environment-variables; choose one of the following options:\noption 1: update the config-value to the new syntax, ${VAR} instead of $VAR or %%VAR%%\noption 2: tell copyparty to allow the old syntax with global-option --env-expand 1 (risky)\noption 3: tell copyparty to only use the new syntax (and not expand this variable) with global-option --env-expand 2\noption 4: disable all environment-variable expansions with PRTY_NO_ENVEXPAND=1 or global-option --env-expand 0"
|
||||
raise Exception(t % (txt,))
|
||||
|
||||
|
||||
def rice_tid() -> str:
|
||||
tid = threading.current_thread().ident
|
||||
c = sunpack(b"B" * 5, spack(b">Q", tid)[-5:])
|
||||
|
|
@ -2446,8 +2481,8 @@ def odfusion(
|
|||
|
||||
def ipnorm(ip: str) -> str:
|
||||
if ":" in ip:
|
||||
# assume /64 clients; drop 4 groups
|
||||
return IPv6Address(ip).exploded[:-20]
|
||||
# assume /56 clients; drop final 72 bits
|
||||
return str(IPv6Network(ip + "/56", strict=False).network_address)
|
||||
|
||||
return ip
|
||||
|
||||
|
|
@ -3849,7 +3884,7 @@ def _parsehook(
|
|||
|
||||
argv = cmd.split(",") if "," in cmd else [cmd]
|
||||
|
||||
argv[0] = os.path.expandvars(os.path.expanduser(argv[0]))
|
||||
argv[0] = os.path.expanduser(expand_osenv_c(argv[0]))
|
||||
|
||||
return areq, chk, imp, fork, sin, jtxt, wait, sp_ka, argv
|
||||
|
||||
|
|
@ -4192,7 +4227,7 @@ def loadpy(ap: str, hot: bool) -> Any:
|
|||
depending on what other inconveniently named files happen
|
||||
to be in the same folder
|
||||
"""
|
||||
ap = os.path.expandvars(os.path.expanduser(ap))
|
||||
ap = os.path.expanduser(expand_osenv_c(ap))
|
||||
mdir, mfile = os.path.split(absreal(ap))
|
||||
mname = mfile.rsplit(".", 1)[0]
|
||||
sys.path.insert(0, mdir)
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
var J_BRW = 1;
|
||||
|
||||
if (window.rw_edit === undefined)
|
||||
if (window.dgauto === undefined)
|
||||
alert('FATAL ERROR: receiving stale data from the server; this may be due to a broken reverse-proxy (stuck cache). Try restarting copyparty and press CTRL-SHIFT-R in the browser');
|
||||
|
||||
var XHR = XMLHttpRequest;
|
||||
|
|
@ -232,6 +232,7 @@ if (1)
|
|||
"cl_hpick": "tap on column headers to hide in the table below",
|
||||
"cl_hcancel": "column hiding aborted",
|
||||
"cl_rcm": "right-click menu",
|
||||
"cl_gauto": "autogrid",
|
||||
|
||||
"ct_grid": '田 the grid',
|
||||
"ct_ttips": '◔ ◡ ◔">ℹ️ tooltips',
|
||||
|
|
@ -287,6 +288,8 @@ if (1)
|
|||
"tt_dynt": "autogrow as tree expands",
|
||||
"tt_wrap": "word wrap",
|
||||
"tt_hover": "reveal overflowing lines on hover$N( breaks scrolling unless mouse $N cursor is in the left gutter )",
|
||||
"tt_gauto": "display as grid or list depending on folder contents",
|
||||
"tt_gathr": "use grid if this percentage of files are pics/vids",
|
||||
|
||||
"ml_pmode": "at end of folder...",
|
||||
"ml_btns": "cmds",
|
||||
|
|
@ -720,6 +723,54 @@ var L = Ls[lang] || Ls.eng, LANGS = [];
|
|||
for (var a = 0; a < LANGN.length; a++)
|
||||
LANGS.push(LANGN[a][0]);
|
||||
|
||||
if (window.glang && navigator.languages && !/\bcplng=/.test(document.cookie))
|
||||
(function() {
|
||||
var lmap = [
|
||||
["eng", /^en/i],
|
||||
["nor", /^n[ob]/i],
|
||||
["chi", /^zh-cn/i],
|
||||
["cze", /^cs/i],
|
||||
["deu", /^de/i],
|
||||
["epo", /^eo/i],
|
||||
["fin", /^fi/i],
|
||||
["fra", /^fr/i],
|
||||
["grc", /^el/i],
|
||||
["hun", /^hu/i],
|
||||
["ita", /^it/i],
|
||||
["jpn", /^ja/i],
|
||||
["kor", /^ko/i],
|
||||
["nld", /^nl/i],
|
||||
["nno", /^nn/i],
|
||||
["pol", /^pl/i],
|
||||
["por", /^pt/i],
|
||||
["rus", /^ru/i],
|
||||
["spa", /^es/i],
|
||||
["swe", /^sv/i],
|
||||
["tur", /^tr/i],
|
||||
["ukr", /^uk/i],
|
||||
["vie", /^vi/i],
|
||||
];
|
||||
for (var a = 0; a < navigator.languages.length; a++) {
|
||||
for (var b = 0; b < lmap.length; b++) {
|
||||
var n = lmap[b][0];
|
||||
if (!lmap[b][1].test(navigator.languages[a]) || !has(LANGS, n))
|
||||
continue;
|
||||
|
||||
if (Ls[n]) {
|
||||
lang = n;
|
||||
L = Ls[n];
|
||||
return;
|
||||
}
|
||||
if (window.stop)
|
||||
window.stop();
|
||||
document.body.innerHTML = 'Loading ' + n;
|
||||
setck("cplng=" + n, location.reload.bind(location));
|
||||
crashed = true;
|
||||
throw 1;
|
||||
}
|
||||
}
|
||||
})();
|
||||
|
||||
|
||||
function langtest() {
|
||||
var n = LANGS.length - 1;
|
||||
|
|
@ -728,7 +779,9 @@ function langtest() {
|
|||
}
|
||||
function langtest2() {
|
||||
for (var a = 0; a < LANGS.length; a++) {
|
||||
if (!Ls[LANGS[a]]) continue;
|
||||
for (var b = a + 1; b < LANGS.length; b++) {
|
||||
if (!Ls[LANGS[b]]) continue;
|
||||
var i1 = Object.keys(Ls[LANGS[a]]).length > Object.keys(Ls[LANGS[b]]).length ? a : b,
|
||||
i2 = i1 == a ? b : a,
|
||||
t1 = Ls[LANGS[i1]],
|
||||
|
|
@ -742,6 +795,7 @@ for (var a = 0; a < LANGS.length; a++) {
|
|||
}
|
||||
}
|
||||
}
|
||||
langtest2();
|
||||
|
||||
|
||||
|
||||
|
|
@ -809,12 +863,7 @@ function mktemp(is_dir) {
|
|||
sendit(input.value);
|
||||
// Chrome blurs elements when calling remove for some reason
|
||||
input.onblur = null;
|
||||
try{
|
||||
row.remove();
|
||||
}
|
||||
catch(e){
|
||||
console.log(e);
|
||||
}
|
||||
row.remove();
|
||||
};
|
||||
input.onkeydown = function(e) {
|
||||
if (e.key == "Enter")
|
||||
|
|
@ -1172,6 +1221,13 @@ ebi('op_cfg').innerHTML = (
|
|||
'</div>\n' +
|
||||
(!MOBILE ? '<div><h3 id="h_mouse">🖱️ ' + L.cl_rcm + '</h3><div><a id="rcm_en" class="tgl btn" tt="' + L.cdt_ren + '</a><a id="rcm_db" class="tgl btn" tt="' + L.cdt_rdb + '</a></div></div>' : '') +
|
||||
'<div>\n' +
|
||||
' <h3 id="h_gauto">🅰️ ' + L.cl_gauto + '</h3>\n' +
|
||||
' <div>\n' +
|
||||
' <a id="gauto" class="tgl btn" href="#" tt="' + L.tt_gauto + '">' + L.enable + '</a>\n' +
|
||||
' <input type="text" id="ga_thresh" value="" ' + NOAC + ' style="width:1.5em" tt="' + L.tt_gathr + '" />' +
|
||||
' </div>\n' +
|
||||
'</div>\n' +
|
||||
'<div>\n' +
|
||||
' <h3 id="h_filesize">🔢 ' + L.cl_hfsz + '</h3>\n' +
|
||||
' <div><select id="fszfmt">\n' +
|
||||
' <option value="0">0 ┃ 1234567</option>\n' +
|
||||
|
|
@ -1501,9 +1557,7 @@ onresize100.add(read_sbw, true);
|
|||
|
||||
|
||||
function check_image_support(format, uri) {
|
||||
var cached
|
||||
= window['have_' + format]
|
||||
= sread('have_' + format);
|
||||
var cached = window['have_' + format] = sread('have_' + format);
|
||||
if (cached !== null)
|
||||
return;
|
||||
|
||||
|
|
@ -1978,7 +2032,7 @@ mpl.init_ac2();
|
|||
var re_m3u = /\.(m3u8?)$/i;
|
||||
var re_au_native = (can_ogg || have_acode) ? /\.(aac|flac|m4[abr]|mp3|oga|ogg|opus|wav)$/i : /\.(aac|flac|m4[abr]|mp3|wav)$/i,
|
||||
re_au_vid = /\.(3gp|asf|avi|flv|m4v|mkv|mov|mp4|mpeg|mpeg2|mpegts|mpg|mpg2|nut|ogm|ogv|rm|ts|vob|webm|wmv)$/i,
|
||||
re_au_all = /\.(aac|ac3|aif|aiff|alac|alaw|amr|ape|au|dfpwm|dts|flac|gsm|it|itgz|itxz|itz|m4[abr]|mdgz|mdxz|mdz|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|3gp|asf|avi|flv|m4v|mkv|mov|mp4|mpeg|mpeg2|mpegts|mpg|mpg2|nut|ogm|ogv|rm|ts|vob|webm|wmv)$/i;
|
||||
re_au_all = /\.(aac|ac3|aif|aiff|alac|alaw|amr|ape|au|b[cfr]stm|dfpwm|dts|flac|gsm|it|itgz|itxz|itz|m4[abr]|mdgz|mdxz|mdz|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|3gp|asf|avi|flv|m4v|mkv|mov|mp4|mpeg|mpeg2|mpegts|mpg|mpg2|nut|ogm|ogv|rm|ts|vob|webm|wmv)$/i;
|
||||
|
||||
|
||||
// extract songs + add play column
|
||||
|
|
@ -2059,7 +2113,9 @@ function MPlayer() {
|
|||
if (!tid || tid.indexOf('af-') !== 0)
|
||||
continue;
|
||||
|
||||
order.push(tid.slice(1));
|
||||
tid = tid.slice(1);
|
||||
if (r.tracks[tid])
|
||||
order.push(tid);
|
||||
}
|
||||
r.order = order;
|
||||
r.shuffle();
|
||||
|
|
@ -5735,8 +5791,8 @@ var showfile = (function () {
|
|||
Prism.highlightElement(el);
|
||||
}
|
||||
catch (ex) { }
|
||||
btn.setAttribute('download', ebi('docname').innerHTML);
|
||||
btn.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(jt));
|
||||
btn.setAttribute('download', ebi('docname').innerHTML);
|
||||
btn.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(jt));
|
||||
};
|
||||
|
||||
r.mktree = function () {
|
||||
|
|
@ -5963,6 +6019,16 @@ var thegrid = (function () {
|
|||
r.setvis();
|
||||
};
|
||||
|
||||
r.autogrid = function (res) {
|
||||
var ni = 0;
|
||||
var nf = res.files.length;
|
||||
for (var a = 0; a < nf; a++)
|
||||
if (img_re.test('.' + res.files[a].ext))
|
||||
ni++;
|
||||
if (nf)
|
||||
thegrid.en = 100 * ni / nf >= r.gathr;
|
||||
};
|
||||
|
||||
function setln(v) {
|
||||
if (v) {
|
||||
r.ln += v;
|
||||
|
|
@ -6075,11 +6141,10 @@ var thegrid = (function () {
|
|||
var ths = QSA('#ggrid>a');
|
||||
|
||||
for (var a = 0, aa = ths.length; a < aa; a++) {
|
||||
var _ref = ebi(ths[a].getAttribute('ref'));
|
||||
if(_ref == null)
|
||||
var ref = ths[a].getAttribute('ref');
|
||||
if (!ref)
|
||||
continue;
|
||||
var tr = _ref.closest('tr'),
|
||||
cl = tr.className || '';
|
||||
var cl = ebi(ref).closest('tr').className || '';
|
||||
|
||||
if (noq_href(ths[a]).endsWith('/'))
|
||||
cl += ' dir';
|
||||
|
|
@ -6188,7 +6253,7 @@ var thegrid = (function () {
|
|||
ihref = addq(ihref, 'th=' + (
|
||||
have_jxl ? 'x' :
|
||||
have_webp ? 'w' :
|
||||
'j'
|
||||
'j'
|
||||
));
|
||||
if (!r.crop)
|
||||
ihref += 'f';
|
||||
|
|
@ -6355,6 +6420,18 @@ var thegrid = (function () {
|
|||
vbar.onresize();
|
||||
});
|
||||
|
||||
bcfg_bind(r, 'gaen', 'gauto', !!dgauto, function(v) {
|
||||
if (r.en && sread("griden") != 1) {
|
||||
r.en = false;
|
||||
r.setvis(true);
|
||||
}
|
||||
});
|
||||
ebi('ga_thresh').value = r.gathr = icfg_get('ga_thresh', dgauto || 70);
|
||||
ebi('ga_thresh').oninput = function (e) {
|
||||
var n = parseInt(this.value);
|
||||
swrite('ga_thresh', r.gathr = (isNum(n) ? n : 0) || 70);
|
||||
};
|
||||
|
||||
ebi('wtgrid').onclick =
|
||||
ebi('gridicon_template').onclick =
|
||||
ebi('listicon_template').onclick =
|
||||
|
|
@ -6602,6 +6679,9 @@ var ahotkeys = function (e) {
|
|||
return ebi('griden').click();
|
||||
}
|
||||
|
||||
if (aet == 'input')
|
||||
return;
|
||||
|
||||
var in_ftab = (aet == 'tr' || aet == 'td') && ae.closest('#files');
|
||||
if (in_ftab) {
|
||||
var d = '', rem = 0;
|
||||
|
|
@ -8183,6 +8263,9 @@ var treectl = (function () {
|
|||
}
|
||||
}
|
||||
|
||||
if (thegrid.gaen && sread('griden') != 1)
|
||||
thegrid.autogrid(res);
|
||||
|
||||
if (url) setTimeout(asdf, 1); else asdf();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -221,6 +221,7 @@ Ls.chi = {
|
|||
"cl_hpick": "在下方文件列表中点击某列表头即可从表中隐去该列",
|
||||
"cl_hcancel": "列隐藏操作已中止",
|
||||
"cl_rcm": "右键菜单",
|
||||
"cl_gauto": "自动网格", //m
|
||||
|
||||
"ct_grid": '田 网格',
|
||||
"ct_ttips": '◔ ◡ ◔">ℹ️ 提示',
|
||||
|
|
@ -275,6 +276,8 @@ Ls.chi = {
|
|||
"tt_dynt": "自动随着目录树展开而变宽",
|
||||
"tt_wrap": "自动换行",
|
||||
"tt_hover": "悬停时完整显示出写不下的文字$N(启用后,鼠标光标只有$N 位于左边线上才滚得动)",
|
||||
"tt_gauto": "根据文件夹内容以网格或列表显示", //m
|
||||
"tt_gathr": "当此比例的文件为图片/视频时使用网格", //m
|
||||
|
||||
"ml_pmode": "文件夹播完后",
|
||||
"ml_btns": "命令",
|
||||
|
|
|
|||
|
|
@ -225,6 +225,7 @@ Ls.cze = {
|
|||
"cl_hpick": "klepněte na záhlaví sloupců pro skrytí v tabulce níže",
|
||||
"cl_hcancel": "skrývání sloupců zrušeno",
|
||||
"cl_rcm": "kontextová nabídka", //m
|
||||
"cl_gauto": "auto mřížka", //m
|
||||
|
||||
"ct_grid": '田 mřížka',
|
||||
"ct_ttips": '◔ ◡ ◔">ℹ️ nápovědy',
|
||||
|
|
@ -279,6 +280,8 @@ Ls.cze = {
|
|||
"tt_dynt": "automaticky rozrůstat jak se strom rozšiřuje",
|
||||
"tt_wrap": "zalomení řádků",
|
||||
"tt_hover": "odhalit přetékající řádky při najetí$N( ruší posun pokud kurzor myši $N není v levém okraji )",
|
||||
"tt_gauto": "zobrazit jako mřížku nebo seznam podle obsahu složky", //m
|
||||
"tt_gathr": "použít mřížku, pokud toto procento souborů tvoří obrázky/videa", //m
|
||||
|
||||
"ml_pmode": "na konci složky...",
|
||||
"ml_btns": "příkazy",
|
||||
|
|
|
|||
|
|
@ -221,6 +221,7 @@ Ls.deu = {
|
|||
"cl_hpick": "zum Verstecken, tippe auf Spaltenüberschriften in der Tabelle unten",
|
||||
"cl_hcancel": "Spaltenbearbeitung abgebrochen",
|
||||
"cl_rcm": "Rechtsklick-Menü",
|
||||
"cl_gauto": "auto-raster", //m
|
||||
|
||||
"ct_grid": '田 Das Raster™',
|
||||
"ct_ttips": '◔ ◡ ◔">ℹ️ Tooltips',
|
||||
|
|
@ -275,6 +276,8 @@ Ls.deu = {
|
|||
"tt_dynt": "autom. wachsen wenn Baum wächst",
|
||||
"tt_wrap": "Zeilenumbruch",
|
||||
"tt_hover": "Beim Hovern überlange Zeilen anzeigen$N(Scrollen funktioniert nicht ausser $N Cursor ist im linken Gutter)",
|
||||
"tt_gauto": "je nach ordnerinhalt als raster oder liste anzeigen", //m
|
||||
"tt_gathr": "raster verwenden, wenn dieser prozentsatz der dateien bilder/videos sind", //m
|
||||
|
||||
"ml_pmode": "am Ende des Ordners...",
|
||||
"ml_btns": "cmds",
|
||||
|
|
|
|||
|
|
@ -221,6 +221,7 @@ Ls.epo = {
|
|||
"cl_hpick": "alklaki la kapojn de kolumnoj por kasi en la suban tabelon",
|
||||
"cl_hcancel": "kaŝado de kolumno nuligita",
|
||||
"cl_rcm": "dekstra-klaka menuo",
|
||||
"cl_gauto": "aŭto田",
|
||||
|
||||
"ct_grid": '田 krado',
|
||||
"ct_ttips": '◔ ◡ ◔">ℹ️ ŝpruchelpiloj',
|
||||
|
|
@ -275,6 +276,8 @@ Ls.epo = {
|
|||
"tt_dynt": "aŭtomate pligrandigi panelon",
|
||||
"tt_wrap": "linifaldo",
|
||||
"tt_hover": "montri kompletajn nomojn sur musumo$N( paneas rulumadon, se la kursoro de muso $N ne estas en la maldekstra malplenaĵo )",
|
||||
"tt_gauto": "montri kiel krado aŭ listo laŭ dosieruja enhavo",
|
||||
"tt_gathr": "uzi kradon se ĉi tiu elcento da dosieroj estas bildoj/filmetoj",
|
||||
|
||||
"ml_pmode": "je la fino de dosierujo...",
|
||||
"ml_btns": "komandoj",
|
||||
|
|
|
|||
|
|
@ -221,6 +221,7 @@ Ls.fin = {
|
|||
"cl_hpick": "napauta sarakeotsikoita piilottaaksesi alla olevassa taulukossa",
|
||||
"cl_hcancel": "sarakkeiden piilotus peruttu",
|
||||
"cl_rcm": "hiiren pikavalikko",
|
||||
"cl_gauto": "auto田", //m
|
||||
|
||||
"ct_grid": '田 kuvanäkymä',
|
||||
"ct_ttips": '◔ ◡ ◔">ℹ️ vihjelaatikot',
|
||||
|
|
@ -275,6 +276,8 @@ Ls.fin = {
|
|||
"tt_dynt": "kasvata automaattisesti hakemistosyvyyden kasvaessa",
|
||||
"tt_wrap": "rivitys",
|
||||
"tt_hover": "paljasta ylivuotavat rivit leijutettaessa$N( rikkoo vierityksen ellei hiiri $N ole vasemmassa marginaalissa )",
|
||||
"tt_gauto": "näytä ruudukkona tai listana kansion sisällön mukaan", //m
|
||||
"tt_gathr": "käytä ruudukkoa jos tämä prosentti tiedostoista on kuvia/videoita", //m
|
||||
|
||||
"ml_pmode": "hakemiston lopussa...",
|
||||
"ml_btns": "komennot",
|
||||
|
|
|
|||
|
|
@ -221,6 +221,7 @@ Ls.fra = {
|
|||
"cl_hpick": "cliquez sur les en-têtes de colonnes pour les masquer dans le tableau ci-dessous",
|
||||
"cl_hcancel": "masquage des colonnes annulé",
|
||||
"cl_rcm": "menu contextuel", //m
|
||||
"cl_gauto": "auto-grille", //m
|
||||
|
||||
"ct_grid": '田 grille',
|
||||
"ct_ttips": '◔ ◡ ◔">ℹ️ infobulles',
|
||||
|
|
@ -275,6 +276,8 @@ Ls.fra = {
|
|||
"tt_dynt": "croissance automatique à mesure que l'arborescence s'étend",
|
||||
"tt_wrap": "retour à la ligne",
|
||||
"tt_hover": "révéler les lignes débordantes au survol$N( interrompt le défilement à moins que le curseur de la souris ne soit dans la gouttière gauche )",
|
||||
"tt_gauto": "afficher en grille ou liste selon le contenu du dossier", //m
|
||||
"tt_gathr": "utiliser la grille si ce pourcentage de fichiers sont des images/vidéos", //m
|
||||
|
||||
"ml_pmode": "à la fin du dossier…",
|
||||
"ml_btns": "cmds",
|
||||
|
|
|
|||
|
|
@ -221,6 +221,7 @@ Ls.grc = {
|
|||
"cl_hpick": "πάτησε στις κεφαλίδες στηλών για να τις κρύψεις στον πίνακα παρακάτω",
|
||||
"cl_hcancel": "η απόκρυψη στηλών ακυρώθηκε",
|
||||
"cl_rcm": "μενού δεξιού κλικ", //m
|
||||
"cl_gauto": "αυτόματο田", //m
|
||||
|
||||
"ct_grid": '田 το πλέγμα',
|
||||
"ct_ttips": '◔ ◡ ◔">ℹ️ συμβουλές εργαλείων',
|
||||
|
|
@ -275,6 +276,8 @@ Ls.grc = {
|
|||
"tt_dynt": "αυτόματη επέκταση καθώς επεκτείνεται το δέντρο διαδρομών",
|
||||
"tt_wrap": "αναδίπλωση λέξεων",
|
||||
"tt_hover": "αποκάλυψη των γραμμών που ξεπερνούν το πλάτος με το ποντίκι πάνω τους$N( σπάει το scroll εκτός αν το ποντίκι $N είναι στην αριστερή στήλη )",
|
||||
"tt_gauto": "εμφάνιση ως πλέγμα ή λίστα ανάλογα με τα περιεχόμενα του φακέλου", //m
|
||||
"tt_gathr": "χρήση πλέγματος αν αυτό το ποσοστό αρχείων είναι εικόνες/βίντεο", //m
|
||||
|
||||
"ml_pmode": "στο τέλος του φακέλου...",
|
||||
"ml_btns": "εντολές",
|
||||
|
|
|
|||
|
|
@ -222,6 +222,7 @@ Ls.hun = {
|
|||
"cl_hpick": 'kattints az oszlopfejlécre az elrejtéshez',
|
||||
"cl_hcancel": 'elrejtés megszakítva',
|
||||
"cl_rcm": 'jobb-klikkes menü',
|
||||
"cl_gauto": "auto田", //m
|
||||
|
||||
"ct_grid": '田 rács nézet',
|
||||
"ct_ttips": '◔ ◡ ◔">ℹ️ segítő szövegek',
|
||||
|
|
@ -276,6 +277,8 @@ Ls.hun = {
|
|||
"tt_dynt": 'automatikus méretezés nyitáskor',
|
||||
"tt_wrap": 'sortörés',
|
||||
"tt_hover": 'túl hosszú sorok mutatása rámutatáskor',
|
||||
"tt_gauto": "megjelenítés rácsban vagy listában a mappa tartalmától függően", //m
|
||||
"tt_gathr": "rács használata, ha a fájlok ezen százaléka kép/videó", //m
|
||||
|
||||
"ml_pmode": 'mappa végén...',
|
||||
"ml_btns": 'gombok',
|
||||
|
|
|
|||
|
|
@ -221,6 +221,7 @@ Ls.ita = {
|
|||
"cl_hpick": "tocca le intestazioni delle colonne per nascondere nella tabella sottostante",
|
||||
"cl_hcancel": "nascondere colonne annullato",
|
||||
"cl_rcm": "menu contestuale", //m
|
||||
"cl_gauto": "auto田", //m
|
||||
|
||||
"ct_grid": '田 griglia',
|
||||
"ct_ttips": '◔ ◡ ◔">ℹ️ tooltip',
|
||||
|
|
@ -275,6 +276,8 @@ Ls.ita = {
|
|||
"tt_dynt": "crescita automatica mentre l'albero si espande",
|
||||
"tt_wrap": "a capo parola",
|
||||
"tt_hover": "rivela righe che traboccano al passaggio del mouse$N( interrompe lo scorrimento a meno che il cursore $N del mouse non sia nella grondaia sinistra )",
|
||||
"tt_gauto": "mostra come griglia o lista in base al contenuto della cartella", //m
|
||||
"tt_gathr": "usa la griglia se questa percentuale di file sono immagini/video", //m
|
||||
|
||||
"ml_pmode": "alla fine della cartella...",
|
||||
"ml_btns": "comandi",
|
||||
|
|
|
|||
|
|
@ -221,6 +221,7 @@ Ls.jpn = {
|
|||
"cl_hpick": "下の表で非表示にするには列ヘッダーをタップします",
|
||||
"cl_hcancel": "列の非表示を解除",
|
||||
"cl_rcm": "右クリックメニュー",
|
||||
"cl_gauto": "自動グリッド", //m
|
||||
|
||||
"ct_grid": '田 グリッド',
|
||||
"ct_ttips": '◔ ◡ ◔">ℹ️ ツールチップ',
|
||||
|
|
@ -275,6 +276,8 @@ Ls.jpn = {
|
|||
"tt_dynt": "ツリーが拡大するにつれて自動的に増加",
|
||||
"tt_wrap": "単語の折り返し",
|
||||
"tt_hover": "ホバーすると溢れた線を表示する$N( マウスを押さない限りスクロールが中断されます $N カーソルは左余白です )",
|
||||
"tt_gauto": "フォルダー内容に応じてグリッドまたはリスト表示", //m
|
||||
"tt_gathr": "この割合のファイルが画像/動画ならグリッドを使用", //m
|
||||
|
||||
"ml_pmode": "フォルダの末尾...",
|
||||
"ml_btns": "コマンド",
|
||||
|
|
|
|||
|
|
@ -221,6 +221,7 @@ Ls.kor = {
|
|||
"cl_hpick": "아래 테이블에서 숨기고 싶은 열의 헤더를 탭하세요",
|
||||
"cl_hcancel": "열 숨기기가 중단되었습니다",
|
||||
"cl_rcm": "우클릭 메뉴", //m
|
||||
"cl_gauto": "자동 田", //m
|
||||
|
||||
"ct_grid": "田 그리드",
|
||||
"ct_ttips": '◔ ◡ ◔">ℹ️ 도움말',
|
||||
|
|
@ -275,6 +276,8 @@ Ls.kor = {
|
|||
"tt_dynt": "트리가 확장될 때 자동으로 너비 증가",
|
||||
"tt_wrap": "자동 줄 바꿈",
|
||||
"tt_hover": "마우스를 올리면 넘어가는 줄 표시$N(마우스 커서가 왼쪽 여백에$N 있지 않으면 스크롤이 깨짐)",
|
||||
"tt_gauto": "폴더 내용에 따라 그리드 또는 목록으로 표시", //m
|
||||
"tt_gathr": "파일 중 이 비율이 이미지/동영상이면 그리드 사용", //m
|
||||
|
||||
"ml_pmode": "폴더 끝에서...",
|
||||
"ml_btns": "명령",
|
||||
|
|
|
|||
|
|
@ -221,6 +221,7 @@ Ls.nld = {
|
|||
"cl_hpick": "Tik op de kolomkoppen om ze in de onderstaande tabel te verbergen",
|
||||
"cl_hcancel": "Kolumn verbergen geannuleerd",
|
||||
"cl_rcm": "Rechtermuisknopmenu", //m
|
||||
"cl_gauto": "auto田", //m
|
||||
|
||||
"ct_grid": '田 grid',
|
||||
"ct_ttips": '◔ ◡ ◔">ℹ️ tooltips',
|
||||
|
|
@ -275,6 +276,8 @@ Ls.nld = {
|
|||
"tt_dynt": "Automatisch groeien naarmate de directoryboom zich uitbreidt",
|
||||
"tt_wrap": "Automatische terugloop",
|
||||
"tt_hover": "Laat overlopenden lijnen zien bij zweven$N(stopt het scrollen tenzij de muis in de linker gedeelte van het scherm is)",
|
||||
"tt_gauto": "weergeven als grid of lijst afhankelijk van mapinhoud", //m
|
||||
"tt_gathr": "gebruik grid als dit percentage bestanden afbeeldingen/video's zijn", //m
|
||||
|
||||
"ml_pmode": "Aan het einde van de map...",
|
||||
"ml_btns": "Cmds",
|
||||
|
|
|
|||
|
|
@ -218,6 +218,7 @@ Ls.nno = {
|
|||
"cl_hpick": "klikk på overskrifta åt kolonnene du ønskjer å skjule i tabellen nedanfor",
|
||||
"cl_hcancel": "kolonne-skjuling avbrote",
|
||||
"cl_rcm": "høgreklikkmeny",
|
||||
"cl_gauto": "auto田",
|
||||
|
||||
"ct_grid": '田 ikon',
|
||||
"ct_ttips": 'vis hjelpetekst ved å holde musa over ting">ℹ️ tips',
|
||||
|
|
@ -272,6 +273,8 @@ Ls.nno = {
|
|||
"tt_dynt": "øk bredda på panelet ettersom treet utvider seg",
|
||||
"tt_wrap": "linjebryting",
|
||||
"tt_hover": "vis heile mappenamnet når musepeikaren treff mappa$N( gjer diverre at scrollhjulet fusker dersom musepeikaren ikkje finn seg i grøfta )",
|
||||
"tt_gauto": "byt visingsmodus (liste/ikon) avhengig av mappeinnhald",
|
||||
"tt_gathr": "vis som ikon når denne prosentdelen er bilete/videoar",
|
||||
|
||||
"ml_pmode": "ved enden av mappa",
|
||||
"ml_btns": "knapper",
|
||||
|
|
|
|||
|
|
@ -218,6 +218,7 @@ Ls.nor = {
|
|||
"cl_hpick": "klikk på overskriften til kolonnene du ønsker å skjule i tabellen nedenfor",
|
||||
"cl_hcancel": "kolonne-skjuling avbrutt",
|
||||
"cl_rcm": "høyreklikkmeny",
|
||||
"cl_gauto": "auto田",
|
||||
|
||||
"ct_grid": '田 ikoner',
|
||||
"ct_ttips": 'vis hjelpetekst ved å holde musen over ting">ℹ️ tips',
|
||||
|
|
@ -272,6 +273,8 @@ Ls.nor = {
|
|||
"tt_dynt": "øk bredden på panelet ettersom treet utvider seg",
|
||||
"tt_wrap": "linjebryting",
|
||||
"tt_hover": "vis hele mappenavnet når musepekeren treffer mappen$N( gjør dessverre at scrollhjulet fusker dersom musepekeren ikke befinner seg i grøfta )",
|
||||
"tt_gauto": "bytt visningsmodus (liste/ikoner) avhengig av mappeinnhold",
|
||||
"tt_gathr": "vis som ikoner når denne prosentandelen er bilder/videoer",
|
||||
|
||||
"ml_pmode": "ved enden av mappen",
|
||||
"ml_btns": "knapper",
|
||||
|
|
|
|||
|
|
@ -224,6 +224,7 @@ Ls.pol = {
|
|||
"cl_hpick": "kliknij nagłówki kolumn, aby ukryć je w tabeli niżej",
|
||||
"cl_hcancel": "ukrywanie kolumn przerwane",
|
||||
"cl_rcm": "menu kontekstowe", //m
|
||||
"cl_gauto": "auto田", //m
|
||||
|
||||
"ct_grid": '田 siatka',
|
||||
"ct_ttips": '◔ ◡ ◔">ℹ️ podpowiedzi',
|
||||
|
|
@ -278,6 +279,8 @@ Ls.pol = {
|
|||
"tt_dynt": "rozszerzaj panel wraz z drzewem",
|
||||
"tt_wrap": "zawijaj tekst",
|
||||
"tt_hover": "pokazuj za długie linie po najechaniu kursorem$N( psuje przewijanie gdy $N kursor nie jest w lewym marginesie )",
|
||||
"tt_gauto": "wyświetl jako siatkę lub listę w zależności od zawartości folderu", //m
|
||||
"tt_gathr": "użyj siatki, jeśli ten procent plików to obrazy/wideo", //m
|
||||
|
||||
"ml_pmode": "na końcu folderu...",
|
||||
"ml_btns": "komendy",
|
||||
|
|
|
|||
|
|
@ -221,6 +221,7 @@ Ls.por = {
|
|||
"cl_hpick": "toque nos cabeçalhos das colunas para ocultá-los na tabela abaixo",
|
||||
"cl_hcancel": "ocultar coluna abortado",
|
||||
"cl_rcm": "menu de clique direito",
|
||||
"cl_gauto": "auto田", //m
|
||||
|
||||
"ct_grid": '田 a grade',
|
||||
"ct_ttips": '◔ ◡ ◔">ℹ️ dicas de ferramentas',
|
||||
|
|
@ -275,6 +276,8 @@ Ls.por = {
|
|||
"tt_dynt": "crescer automaticamente à medida que a árvore se expande",
|
||||
"tt_wrap": "quebra de linha",
|
||||
"tt_hover": "revelar linhas transbordando ao passar o mouse$N( quebra a rolagem a menos que o cursor do mouse $N esteja na margem esquerda )",
|
||||
"tt_gauto": "exibir como grade ou lista dependendo do conteúdo da pasta", //m
|
||||
"tt_gathr": "usar grade se esta porcentagem de arquivos for imagens/vídeos", //m
|
||||
|
||||
"ml_pmode": "ao final da pasta...",
|
||||
"ml_btns": "comandos",
|
||||
|
|
|
|||
|
|
@ -221,6 +221,7 @@ Ls.rus = {
|
|||
"cl_hpick": "нажмите на заголовки столбцов, чтобы скрыть их в таблице ниже",
|
||||
"cl_hcancel": "скрытие столбца отменено",
|
||||
"cl_rcm": "контекстное меню", //m
|
||||
"cl_gauto": "авто田", //m
|
||||
|
||||
"ct_grid": '田 сетка',
|
||||
"ct_ttips": '◔ ◡ ◔">ℹ️ подсказки',
|
||||
|
|
@ -275,6 +276,8 @@ Ls.rus = {
|
|||
"tt_dynt": "автоматическое расширение панели",
|
||||
"tt_wrap": "перенос слов",
|
||||
"tt_hover": "раскрывать обрезанные строки при наведении$N( ломает скроллинг, если $N курсор не в пустоте слева )",
|
||||
"tt_gauto": "показывать как сетку или список в зависимости от содержимого папки", //m
|
||||
"tt_gathr": "использовать сетку, если этот процент файлов — изображения/видео", //m
|
||||
|
||||
"ml_pmode": "в конце папки...",
|
||||
"ml_btns": "команды",
|
||||
|
|
|
|||
|
|
@ -220,6 +220,7 @@ Ls.spa = {
|
|||
"cl_hpick": "toca en las cabeceras de columna para ocultarlas en la tabla de abajo",
|
||||
"cl_hcancel": "ocultación de columna cancelada",
|
||||
"cl_rcm": "menú contextual", //m
|
||||
"cl_gauto": "auto田", //m
|
||||
|
||||
"ct_grid": '田 cuadrícula',
|
||||
"ct_ttips": '◔ ◡ ◔">ℹ️ tooltips',
|
||||
|
|
@ -274,6 +275,8 @@ Ls.spa = {
|
|||
"tt_dynt": "crecimiento automático a medida que el árbol se expande",
|
||||
"tt_wrap": "ajuste de línea",
|
||||
"tt_hover": "revelar líneas que se desbordan al pasar el ratón$N( rompe el desplazamiento a menos que el $N cursor esté en el margen izquierdo )",
|
||||
"tt_gauto": "mostrar como cuadrícula o lista según el contenido de la carpeta", //m
|
||||
"tt_gathr": "usar cuadrícula si este porcentaje de archivos son imágenes/videos", //m
|
||||
|
||||
"ml_pmode": "al final de la carpeta...",
|
||||
"ml_btns": "acciones",
|
||||
|
|
|
|||
|
|
@ -221,6 +221,7 @@ Ls.swe = {
|
|||
"cl_hpick": "tryck på en kolumntitel för att dölja den i filvyn",
|
||||
"cl_hcancel": "kolumndöljning avbruten",
|
||||
"cl_rcm": "högerklicksmeny", //m
|
||||
"cl_gauto": "auto田", //m
|
||||
|
||||
"ct_grid": '田 rutnätet',
|
||||
"ct_ttips": '◔ ◡ ◔">ℹ️ tips',
|
||||
|
|
@ -275,6 +276,8 @@ Ls.swe = {
|
|||
"tt_dynt": "väx vyn när trädet expanderar",
|
||||
"tt_wrap": "automatisk radbrytning",
|
||||
"tt_hover": "visa överlånga rader när muspekaren hovrar över dem$N( skrollhjulet fungerar ej såvida inte pekaren$Nstår till vänster )",
|
||||
"tt_gauto": "visa som rutnät eller lista beroende på mappens innehåll", //m
|
||||
"tt_gathr": "använd rutnät om denna andel filer är bilder/videor", //m
|
||||
|
||||
"ml_pmode": "vid mappens slut...",
|
||||
"ml_btns": "komm.",
|
||||
|
|
|
|||
|
|
@ -221,6 +221,7 @@ Ls.tur = {
|
|||
"cl_hpick": "aşağıdaki tabloda gizlemek için sütun başlıklarına dokunun",
|
||||
"cl_hcancel": "sütun gizleme iptal edildi",
|
||||
"cl_rcm": "sağ tık menüsü", //m
|
||||
"cl_gauto": "otomatik田", //m
|
||||
|
||||
"ct_grid": '田 ızgara',
|
||||
"ct_ttips": '◔ ◡ ◔">ℹ️ ipuçları',
|
||||
|
|
@ -275,6 +276,8 @@ Ls.tur = {
|
|||
"tt_dynt": "ağaç genişledikçe otomatik büyüt",
|
||||
"tt_wrap": "kelime sarma",
|
||||
"tt_hover": "fare ile üzerine gelindiğinde taşan satırları göster$N( fare imleci sol kenarda değilse kaydırmayı bozar )",
|
||||
"tt_gauto": "klasör içeriğine bağlı olarak ızgara veya liste olarak göster", //m
|
||||
"tt_gathr": "dosyaların bu yüzdesi resim/video ise ızgara kullan", //m
|
||||
|
||||
"ml_pmode": "klasör sonunda...",
|
||||
"ml_btns": "komutlar",
|
||||
|
|
|
|||
|
|
@ -221,6 +221,7 @@ Ls.ukr = {
|
|||
"cl_hpick": "натисніть на заголовки стовпців, щоб приховати їх у таблиці нижче",
|
||||
"cl_hcancel": "приховання стовпців скасовано",
|
||||
"cl_rcm": "контекстне меню", //m
|
||||
"cl_gauto": "авто田", //m
|
||||
|
||||
"ct_grid": '田 сітка',
|
||||
"ct_ttips": '◔ ◡ ◔">ℹ️ підказки',
|
||||
|
|
@ -275,6 +276,8 @@ Ls.ukr = {
|
|||
"tt_dynt": "автоматично збільшуватися при розширенні дерева",
|
||||
"tt_wrap": "перенесення слів",
|
||||
"tt_hover": "показувати переповнені рядки при наведенні$N( порушує прокрутку, якщо курсор $N миші не знаходиться в лівому відступі )",
|
||||
"tt_gauto": "показувати як сітку або список залежно від вмісту папки", //m
|
||||
"tt_gathr": "використовувати сітку, якщо цей відсоток файлів — зображення/відео", //m
|
||||
|
||||
"ml_pmode": "в кінці папки...",
|
||||
"ml_btns": "команди",
|
||||
|
|
|
|||
|
|
@ -221,6 +221,7 @@ Ls.vie = {
|
|||
"cl_hpick": "chạm vào tiêu đề cột để ẩn trong bảng bên dưới",
|
||||
"cl_hcancel": "đã hủy việc ẩn cột",
|
||||
"cl_rcm": "menu chuột phải", //m
|
||||
"cl_gauto": "lưới tự động", //m
|
||||
|
||||
// settings / tuỳ chọn
|
||||
"ct_grid": '田 chế độ lưới',
|
||||
|
|
@ -279,6 +280,8 @@ Ls.vie = {
|
|||
"tt_dynt": "tự mở rộng khi cây mở rộng",
|
||||
"tt_wrap": "ngắt dòng",
|
||||
"tt_hover": "hiện thị dòng tràn khi rê chuột$N( không cuộn được nếu $N con trỏ chuột nằm ngoài cột trái )",
|
||||
"tt_gauto": "hiển thị dạng lưới hoặc danh sách tùy theo nội dung thư mục", //m
|
||||
"tt_gathr": "dùng lưới nếu tỷ lệ tệp này là ảnh/video", //m
|
||||
|
||||
"ml_pmode": "ở cuối thư mục...",
|
||||
"ml_btns": "lệnh",
|
||||
|
|
|
|||
|
|
@ -359,6 +359,48 @@ for the `re`pack to work, first run one of the sfx'es once to unpack it
|
|||
**note:** you can also just download and run [/scripts/copyparty-repack.sh](https://github.com/9001/copyparty/blob/hovudstraum/scripts/copyparty-repack.sh) -- this will grab the latest copyparty release from github and do a few repacks; works on linux/macos (and windows with msys2 or WSL)
|
||||
|
||||
|
||||
# dependencies
|
||||
|
||||
## vendored dependencies
|
||||
|
||||
some third-party code has been vendored into the git repo; some for convenience, some because they have been lightly hacked to fit copyparty's usecase better:
|
||||
|
||||
* inside the folder [/copyparty/stolen](https://github.com/9001/copyparty/tree/hovudstraum/copyparty/stolen) is python-libraries which runs on the serverside:
|
||||
* `surrogateescape.py` (BSD2) can be removed; only needed for python2 support
|
||||
* `qrcodegen.py` (MIT) can be removed and replaced with a systemwide install of the original [qrcodegen.py](https://github.com/nayuki/QR-Code-generator/blob/daa3114/python/qrcodegen.py);
|
||||
* modifications: removed code/features that copyparty does not need/use
|
||||
* `ifaddr` (BSD2) can be removed and replaced with a systemwide install of the original [ifaddr](https://github.com/ifaddr/ifaddr);
|
||||
* modifications: support python2, support s390x / irix32 / graal
|
||||
* `dnslib` (MIT) may be deleted and replaced with a systemwide install of the original [dnslib](https://github.com/paulc/dnslib/), HOWEVER:
|
||||
* will cause problems for mDNS in some network environments; 6c1cf68bca7376c6291c3cfe710ebd5bd5ed3e6c + 94d1924fa97e5faaf1ebfd85cae73faebcb89fa1
|
||||
|
||||
* inside the folder `/copyparty/web/deps` (only in distributed archives/builds) is [fuse.py](https://github.com/fusepy/fusepy/blob/master/fuse.py), to make it downloadable from the connect-page on the web-ui
|
||||
|
||||
* inside the folder `/copyparty/web` (only in distributed archives/builds) is a collection of javascript libraries (produced by [deps-docker](https://github.com/9001/copyparty/tree/hovudstraum/scripts/deps-docker)) which are used clientside by the web-UI:
|
||||
* [marked.js](https://github.com/markedjs/marked/releases) (MIT) powers the markdown editor, and has been [patched](https://github.com/9001/copyparty/blob/hovudstraum/scripts/deps-docker/marked-ln.patch) to include the line-numbers of each input line, to enable scroll-sync between the editor and the preview-pane. This patch is [not strictly necessary anymore](https://github.com/markedjs/marked/issues/2134) but I haven't gotten around to making the change yet
|
||||
* [easyMDE](https://github.com/Ionaru/easy-markdown-editor/) (MIT), the alternative markdown editor, has the same [patch](https://github.com/9001/copyparty/blob/hovudstraum/scripts/deps-docker/easymde-ln.patch) to enable scroll-sync, and also some [size-golfing](https://github.com/9001/copyparty/blob/hovudstraum/scripts/deps-docker/easymde.patch)
|
||||
* [codemirror5](https://github.com/codemirror/codemirror5/) (MIT) has no noteworthy changes, and has only been [size-golfed](https://github.com/9001/copyparty/blob/hovudstraum/scripts/deps-docker/codemirror.patch), could have been used as-is
|
||||
* [DOMPurify](https://github.com/cure53/DOMPurify) (Apache2) is used as-is
|
||||
* [hash-wasm](https://github.com/Daninet/hash-wasm/) (MIT) is used entirely as-is
|
||||
* [asmcrypto.js](https://github.com/openpgpjs/asmcrypto.js/) (MIT) is abandoned software, and used almost as-is (slightly golfed for size); it is probably fine to exclude/remove this, since it will only break support for uploading from really old browsers (IE10/IE11) using up2k (the "fancy uploader")
|
||||
* [prism.js](https://github.com/PrismJS/prism/) (MIT) is built with a [selection of languages](https://github.com/9001/copyparty/blob/hovudstraum/scripts/deps-docker/genprism.py); there is an assumption about the exact subset of languages elsewhere in copyparty, but there shouldn't be any big consequences of replacing it with a different build if that exists in Fedora
|
||||
* an old version of [SourceCodePro](https://github.com/adobe-fonts/source-code-pro) (OFL-1.1), is size-reduced to [only the necessary characters](https://github.com/9001/copyparty/blob/41ed559faabdc180efc37fd027e7f1bb2d14d174/scripts/deps-docker/mini-fa.sh#L30-L31). There will be subtle layout issues if this is replaced with a newer version, because they changed some line-heights or something in later versions, but shouldn't be a big issue
|
||||
* an old version of [font-awesome](https://github.com/FortAwesome/Font-Awesome) (OFL-1.1), size-reduced to [only the necessary icons](https://github.com/9001/copyparty/blob/hovudstraum/scripts/deps-docker/mini-fa.sh). I believe a newer version should also work.
|
||||
|
||||
## optional dependencies
|
||||
|
||||
explained in the [main readme](https://github.com/9001/copyparty/tree/hovudstraum#optional-dependencies), but a quick recap:
|
||||
|
||||
* recommended python libraries: `argon2-cffi paramiko pyftpdlib pyopenssl pillow rawpy pyzmq` [python-magic](https://pypi.org/project/python-magic/)
|
||||
* only recommended on Windows: `psutil` (not very useful on Linux)
|
||||
* NOT recommended: `impacket` because the feature it enables is a security nightmare
|
||||
* NOT recommended: `mutagen` because ffmpeg produces better results (albeit slower)
|
||||
* NOT recommended: `pyvips` because converting to jxl is extremely RAM-heavy
|
||||
* NOT recommended: `pillow-heif` due to [legal reasons](https://github.com/9001/copyparty/blob/hovudstraum/docs/bad-codecs.md)
|
||||
* recommended programs: `ffmpeg ffprobe cfssl cfssljson cfssl-certinfo`
|
||||
* FFmpeg powers audio transcoding, and thumbnails of formats not covered by pillow/pyvips
|
||||
|
||||
|
||||
# building
|
||||
|
||||
## dev env setup
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ FROM alpine:3.23
|
|||
WORKDIR /z
|
||||
ENV ver_hashwasm=4.12.0 \
|
||||
ver_marked=4.3.0 \
|
||||
ver_dompf=3.3.3 \
|
||||
ver_dompf=3.4.0 \
|
||||
ver_mde=2.18.0 \
|
||||
ver_codemirror=5.65.18 \
|
||||
ver_fontawesome=5.13.0 \
|
||||
|
|
|
|||
|
|
@ -31,5 +31,5 @@ a726fb46cce24f781fc8b55a3e6dea0a884ebc3b2b400ea74aa02333699f4955a5dc1e2ec5927ac7
|
|||
efc712162da7fb005c8869a7612d2f4983d2d073ec79e16a58e7bf1fcd01c88b1cc26656f0893c68edd2294be7c3990db2f6bd77e7e3f2613539d57994b6a033 pillow-12.1.1-cp313-cp313-win_amd64.whl
|
||||
b9b98714dfca6fa80b0b3f222965724d63be9c54d19435d1fe768e07016913d6db8d6e043fcb185b55a9bd6fe370a80cf961814fc096046a5f4640d99ed575ef pyinstaller-6.15.0-py3-none-win_amd64.whl
|
||||
cad0f7cf39de691813b1d4abc7d33f8bda99a87d9c5886039b814752e8690364150da26fb61b3e28d5698ff57a90e6dcd619ed2b64b04f72b5aadb75e201bdb0 pyinstaller_hooks_contrib-2025.8-py3-none-any.whl
|
||||
50dba4a63957220247be2985bd4ed6928679d9f6dc8cb7cee36394dda4e69cdee910fb39b01965d1358133855ace535eb1e08774fed7090feb8618dfc5fd2441 python-3.13.12-amd64.exe
|
||||
368ea2da3e3bfe765a37c62227e84774853aaabce6954475fa45c873e5547cb5346ca03a0f6a0789af369285bb3464881fed0275a19066913d9d396d5d9b9947 python-3.13.13-amd64.exe
|
||||
2a0420f7faaa33d2132b82895a8282688030e939db0225ad8abb95a47bdb87b45318f10985fc3cee271a9121441c1526caa363d7f2e4a4b18b1a674068766e87 setuptools-80.9.0-py3-none-any.whl
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ fns=(
|
|||
pillow-12.1.1-cp313-cp313-win_amd64.whl
|
||||
pyinstaller-6.15.0-py3-none-win_amd64.whl
|
||||
pyinstaller_hooks_contrib-2025.8-py3-none-any.whl
|
||||
python-3.13.12-amd64.exe
|
||||
python-3.13.13-amd64.exe
|
||||
setuptools-80.9.0-py3-none-any.whl
|
||||
)
|
||||
[ $w7 ] && fns+=(
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ from copyparty.broker_thr import BrokerThr
|
|||
from copyparty.ico import Ico
|
||||
from copyparty.u2idx import U2idx
|
||||
from copyparty.up2k import Up2k
|
||||
from copyparty.util import FHC, CachedDict, Garda, Unrecv
|
||||
from copyparty.util import FHC, CachedDict, Garda, Unrecv, expand_osenv_c
|
||||
|
||||
init_E(E)
|
||||
|
||||
|
|
@ -78,8 +78,10 @@ def get_ramdisk():
|
|||
return ret
|
||||
|
||||
for vol in ["/dev/shm", "/Volumes/cptd"]: # nosec (singleton test)
|
||||
if os.path.exists(vol):
|
||||
try:
|
||||
return subdir(vol)
|
||||
except:
|
||||
pass
|
||||
|
||||
if os.path.exists("/Volumes"):
|
||||
sck = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
|
|
@ -161,7 +163,7 @@ class Cfg(Namespace):
|
|||
ex = "ac_convt au_vol dl_list du_iwho mtab_age reg_cap s_thead s_tbody tail_tmax tail_who th_convt th_qv th_qvx ups_who ver_iwho zip_who"
|
||||
ka.update(**{k: 9 for k in ex.split()})
|
||||
|
||||
ex = "ctl_re db_act forget_ip idp_cookie idp_store k304 loris no304 nosubtle qr_pin qr_wait re_maxage rproxy rsp_jtr rsp_slp s_wr_slp snap_wri theme themes turbo u2ow zipmaxn zipmaxs"
|
||||
ex = "ctl_re db_act forget_ip gauto idp_cookie idp_store k304 loris no304 nosubtle qr_pin qr_wait re_maxage rproxy rsp_jtr rsp_slp s_wr_slp snap_wri theme themes turbo u2ow zipmaxn zipmaxs"
|
||||
ka.update(**{k: 0 for k in ex.split()})
|
||||
|
||||
ex = "ah_alg bname chdir chmod_f chpw_db db_xattr doctitle df epilogues exit favico fika ipa ipar html_head html_head_d html_head_s idp_login idp_logout lg_sba lg_sbf log_date log_fk md_sba md_sbf name og_desc og_site og_th og_title og_title_a og_title_v og_title_i opds_exts preadmes prologues readmes shr shr1 shr_site site smsg tcolor textfiles th_pregen txt_eol ufavico ufavico_h unlist up_site vc_url vname xff_src zipmaxt R RS SR"
|
||||
|
|
@ -193,6 +195,7 @@ class Cfg(Namespace):
|
|||
du_who="all",
|
||||
dk_salt="b" * 16,
|
||||
fk_salt="a" * 16,
|
||||
env_expand=2,
|
||||
fsnt="lin",
|
||||
grp_all="acct",
|
||||
idp_gsep=re.compile("[|:;+,]"),
|
||||
|
|
@ -215,6 +218,7 @@ class Cfg(Namespace):
|
|||
rw_edit="md",
|
||||
s_rd_sz=256 * 1024,
|
||||
s_wr_sz=256 * 1024,
|
||||
shenvexp=expand_osenv_c,
|
||||
shr_who="auth",
|
||||
sort="href",
|
||||
srch_hits=99999,
|
||||
|
|
|
|||
Loading…
Reference in a new issue