diff --git a/contrib/package/arch/PKGBUILD b/contrib/package/arch/PKGBUILD index 6da55748..fa8237d1 100644 --- a/contrib/package/arch/PKGBUILD +++ b/contrib/package/arch/PKGBUILD @@ -3,7 +3,7 @@ # NOTE: You generally shouldn't use this PKGBUILD on Arch, as it is mainly for testing purposes. Install copyparty using pacman instead. pkgname=copyparty -pkgver="1.20.0" +pkgver="1.20.1" pkgrel=1 pkgdesc="File server with accelerated resumable uploads, dedup, WebDAV, SFTP, FTP, TFTP, zeroconf, media indexer, thumbnails++" arch=("any") @@ -24,7 +24,7 @@ optdepends=("ffmpeg: thumbnails for videos, images (slower) and audio, music tag ) source=("https://github.com/9001/${pkgname}/releases/download/v${pkgver}/${pkgname}-${pkgver}.tar.gz") backup=("etc/${pkgname}/copyparty.conf" ) -sha256sums=("a1fd3aab51f30435f07f1d128cf773262ee69a1554c9c32ea1e36d06438eb291") +sha256sums=("4f513ca9e3d1c11a7bb4e1a8a925dda2449b9565e91f6ef7cbe10367fa4e2935") build() { cd "${srcdir}/${pkgname}-${pkgver}/copyparty/web" diff --git a/contrib/package/makedeb-mpr/PKGBUILD b/contrib/package/makedeb-mpr/PKGBUILD index 2be27fb5..0941869c 100644 --- a/contrib/package/makedeb-mpr/PKGBUILD +++ b/contrib/package/makedeb-mpr/PKGBUILD @@ -2,7 +2,7 @@ pkgname=copyparty -pkgver=1.20.0 +pkgver=1.20.1 pkgrel=1 pkgdesc="File server with accelerated resumable uploads, dedup, WebDAV, SFTP, FTP, TFTP, zeroconf, media indexer, thumbnails++" arch=("any") @@ -21,7 +21,7 @@ optdepends=("ffmpeg: thumbnails for videos, images (slower) and audio, music tag ) source=("https://github.com/9001/${pkgname}/releases/download/v${pkgver}/${pkgname}-${pkgver}.tar.gz") backup=("/etc/${pkgname}.d/init" ) -sha256sums=("a1fd3aab51f30435f07f1d128cf773262ee69a1554c9c32ea1e36d06438eb291") +sha256sums=("4f513ca9e3d1c11a7bb4e1a8a925dda2449b9565e91f6ef7cbe10367fa4e2935") build() { cd "${srcdir}/${pkgname}-${pkgver}/copyparty/web" diff --git a/contrib/package/nix/copyparty/pin.json b/contrib/package/nix/copyparty/pin.json index 53efdd47..e15e49ed 100644 --- a/contrib/package/nix/copyparty/pin.json +++ b/contrib/package/nix/copyparty/pin.json @@ -1,5 +1,5 @@ { - "url": "https://github.com/9001/copyparty/releases/download/v1.20.0/copyparty-1.20.0.tar.gz", - "version": "1.20.0", - "hash": "sha256-of06q1HzBDXwfx0SjPdzJi7mmhVUycMuoeNtBkOOspE=" + "url": "https://github.com/9001/copyparty/releases/download/v1.20.1/copyparty-1.20.1.tar.gz", + "version": "1.20.1", + "hash": "sha256-T1E8qePRwRp7tOGoqSXdokSblWXpH273y+EDZ/pOKTU=" } \ No newline at end of file diff --git a/copyparty/__init__.py b/copyparty/__init__.py index ed48dc50..afb05d18 100644 --- a/copyparty/__init__.py +++ b/copyparty/__init__.py @@ -107,6 +107,7 @@ web/tl/fin.js web/tl/fra.js web/tl/grc.js web/tl/ita.js +web/tl/jpn.js web/tl/kor.js web/tl/nld.js web/tl/nno.js diff --git a/copyparty/__main__.py b/copyparty/__main__.py index ede8472b..b2186d36 100644 --- a/copyparty/__main__.py +++ b/copyparty/__main__.py @@ -1429,6 +1429,7 @@ def add_sftp(ap): ap2.add_argument("--sftp", metavar="PORT", type=int, default=0, help="enable SFTP server on \033[33mPORT\033[0m, for example \033[32m3922") ap2.add_argument("--sftpv", action="store_true", help="verbose") ap2.add_argument("--sftpvv", action="store_true", help="verboser") + ap2.add_argument("--sftp-i", metavar="IP", type=u, default="-i", help="IPs to listen on (comma-separated list). Set this to override \033[33m-i\033[0m for this protocol") ap2.add_argument("--sftp4", action="store_true", help="only listen on IPv4") ap2.add_argument("--sftp-key", metavar="U K", type=u, action="append", help="\033[34mREPEATABLE:\033[0m add ssh-key \033[33mK\033[0m for user \033[33mU\033[0m (username, space, key-type, space, base64); if user has multiple keys, then repeat this option for each key\n └─commandline example: --sftp-key 'david ssh-ed25519 AAAAC3NzaC...'\n └─config-file example: sftp-key: david ssh-ed25519 AAAAC3NzaC...") ap2.add_argument("--sftp-key2u", action="append", help=argparse.SUPPRESS) @@ -1444,6 +1445,7 @@ def add_ftp(ap): ap2.add_argument("--ftp", metavar="PORT", type=int, default=0, help="enable FTP server on \033[33mPORT\033[0m, for example \033[32m3921") ap2.add_argument("--ftps", metavar="PORT", type=int, default=0, help="enable FTPS server on \033[33mPORT\033[0m, for example \033[32m3990") ap2.add_argument("--ftpv", action="store_true", help="verbose") + ap2.add_argument("--ftp-i", metavar="IP", type=u, default="-i", help="IPs to listen on (comma-separated list). Set this to override \033[33m-i\033[0m for this protocol") ap2.add_argument("--ftp4", action="store_true", help="only listen on IPv4") ap2.add_argument("--ftp-ipa", metavar="CIDR", type=u, default="", help="only accept connections from IP-addresses inside \033[33mCIDR\033[0m (comma-separated); specify [\033[32many\033[0m] to disable inheriting \033[33m--ipa\033[0m / \033[33m--ipar\033[0m. Examples: [\033[32mlan\033[0m] or [\033[32m10.89.0.0/16, 192.168.33.0/24\033[0m]") ap2.add_argument("--ftp-no-ow", action="store_true", help="if target file exists, reject upload instead of overwrite") @@ -1468,6 +1470,7 @@ def add_webdav(ap): def add_tftp(ap): ap2 = ap.add_argument_group("TFTP options (UDP only)") ap2.add_argument("--tftp", metavar="PORT", type=int, default=0, help="enable TFTP server on \033[33mPORT\033[0m, for example \033[32m69 \033[0mor \033[32m3969") + ap2.add_argument("--tftp-i", metavar="IP", type=u, default="-i", help="IPs to listen on (comma-separated list). Set this to override \033[33m-i\033[0m for this protocol") ap2.add_argument("--tftp4", action="store_true", help="only listen on IPv4") ap2.add_argument("--tftpv", action="store_true", help="verbose") ap2.add_argument("--tftpvv", action="store_true", help="verboser") @@ -1481,6 +1484,7 @@ def add_tftp(ap): def add_smb(ap): ap2 = ap.add_argument_group("SMB/CIFS options") ap2.add_argument("--smb", action="store_true", help="enable smb (read-only) -- this requires running copyparty as root on linux and macos unless \033[33m--smb-port\033[0m is set above 1024 and your OS does port-forwarding from 445 to that.\n\033[1;31mWARNING:\033[0m this protocol is DANGEROUS and buggy! Never expose to the internet!") + ap2.add_argument("--smb-i", metavar="IP", type=u, default="-i", help="IPs to listen on (comma-separated list). Set this to override \033[33m-i\033[0m for this protocol") ap2.add_argument("--smbw", action="store_true", help="enable write support (please dont)") ap2.add_argument("--smb1", action="store_true", help="disable SMBv2, only enable SMBv1 (CIFS)") ap2.add_argument("--smb-port", metavar="PORT", type=int, default=445, help="port to listen on -- if you change this value, you must NAT from TCP:445 to this port using iptables or similar") @@ -1491,11 +1495,13 @@ def add_smb(ap): ap2.add_argument("--smbvv", action="store_true", help="verboser") ap2.add_argument("--smbvvv", action="store_true", help="verbosest") + def add_opds(ap): ap2 = ap.add_argument_group("OPDS options") ap2.add_argument("--opds", action="store_true", help="enable opds -- allows e-book readers to browse and download files (volflag=opds)") ap2.add_argument("--opds-exts", metavar="T,T", type=u, default="epub,cbz,pdf", help="file formats to list in OPDS feeds; leave empty to show everything (volflag=opds_exts)") + def add_handlers(ap): ap2 = ap.add_argument_group("handlers (see --help-handlers)") ap2.add_argument("--on404", metavar="PY", type=u, action="append", help="\033[34mREPEATABLE:\033[0m handle 404s by executing \033[33mPY\033[0m file") @@ -2150,10 +2156,17 @@ def main(argv: Optional[list[str]] = None) -> None: if getattr(al, k1): setattr(al, k2, False) - if not HAVE_IPV6 and al.i == "::": - al.i = "0.0.0.0" + protos = "ftp tftp sftp smb".split() + opts = ["%s_i" % (zs,) for zs in protos] + for opt in opts: + if getattr(al, opt) == "-i": + setattr(al, opt, al.i) + for opt in ["i"] + opts: + zs = getattr(al, opt) + if not HAVE_IPV6 and zs == "::": + zs = "0.0.0.0" + setattr(al, opt, [x.strip() for x in zs.split(",")]) - al.i = [x.strip() for x in al.i.split(",")] try: if "-" in al.p: lo, hi = [int(x) for x in al.p.split("-")] diff --git a/copyparty/__version__.py b/copyparty/__version__.py index c773bd66..ec5f5dd7 100644 --- a/copyparty/__version__.py +++ b/copyparty/__version__.py @@ -1,8 +1,8 @@ # coding: utf-8 -VERSION = (1, 20, 0) +VERSION = (1, 20, 1) CODENAME = "sftp is fine too" -BUILD_DT = (2026, 1, 2) +BUILD_DT = (2026, 1, 9) S_VERSION = ".".join(map(str, VERSION)) S_BUILD_DT = "{0:04d}-{1:02d}-{2:02d}".format(*BUILD_DT) diff --git a/copyparty/authsrv.py b/copyparty/authsrv.py index ce782bc3..da944eb8 100644 --- a/copyparty/authsrv.py +++ b/copyparty/authsrv.py @@ -2650,6 +2650,13 @@ class AuthSrv(object): self.log(t, 1) raise Exception(t) + if ( + "dedup" in vol.flags + and "reflink" not in vol.flags + and vol.flags["apnd_who"] != "no" + ): + vol.flags["apnd_who"] = "ndd" + # verify tags mentioned by -mt[mp] are used by -mte local_mtp = {} local_only_mtp = {} diff --git a/copyparty/ftpd.py b/copyparty/ftpd.py index be091c18..c1bc543b 100644 --- a/copyparty/ftpd.py +++ b/copyparty/ftpd.py @@ -641,7 +641,7 @@ class Ftpd(object): lgr = logging.getLogger("pyftpdlib") lgr.setLevel(logging.DEBUG if self.args.ftpv else logging.INFO) - ips = self.args.i + ips = self.args.ftp_i if "::" in ips: ips.append("0.0.0.0") diff --git a/copyparty/httpcli.py b/copyparty/httpcli.py index e58867c4..a9fec2ec 100644 --- a/copyparty/httpcli.py +++ b/copyparty/httpcli.py @@ -2553,6 +2553,8 @@ class HttpCli(object): or (zs == "dw" and self.can_delete) ): pass + elif zs == "ndd": + raise Pebkac(400, "append is denied here due to non-reflink dedup") else: raise Pebkac(400, "you do not have permission to append") zs = os.path.join(params["fdir"], fn) diff --git a/copyparty/sftpd.py b/copyparty/sftpd.py index 2221caf1..deb92042 100644 --- a/copyparty/sftpd.py +++ b/copyparty/sftpd.py @@ -286,10 +286,26 @@ class SFTP_Srv(paramiko.SFTPServerInterface): self.uname: str = ssh.uname # type: ignore self.args = self.hub.args self.asrv: "AuthSrv" = self.hub.asrv + self.v = self.args.sftpv + self.vv = self.args.sftpvv if self.uname == LEELOO_DALLAS: raise Exception("send her back") + self.vols = [ + vp + for vp, vn in self.asrv.vfs.all_vols.items() + if self.uname in vn.axs.uread + or self.uname in vn.axs.uwrite + or self.uname in vn.axs.uget + ] + self.vis = set() + for zs in self.vols: + self.vis.add(zs) + while zs: + zs = zs.rsplit("/", 1)[0] if "/" in zs else "" + self.vis.add(zs) + def log(self, msg: str, c: Union[int, str] = 0) -> None: self.hub.log("sftp:%s" % (self.ip,), msg, c) @@ -353,29 +369,35 @@ class SFTP_Srv(paramiko.SFTPServerInterface): return SFTP_FAILURE def _list_folder(self, path: str) -> list[SATTR] | int: + if self.v: + self.log("ls(%s):" % (path,)) + path = path.strip("/") try: ap, vn, rem = self.v2a(path, r=True) except Pebkac: try: self.v2a(path, w=True) + self.log("ls(%s): [] (write-only)" % (path,)) return [] # display write-only folders as empty except: pass - if self.asrv.vfs.realpath or path.strip("/"): + if path not in self.vis: + self.log("ls(%s): EPERM" % (path,)) return SFTP_PERMISSION_DENIED # list of accessible volumes ret = [] zi = int(time.time()) vst = os.stat_result((16877, -1, -1, 1, 1000, 1000, 8, zi, zi, zi)) - for vn in self.asrv.vfs.all_vols.values(): - if "/" in vn.vpath or not vn.vpath: - continue # only include toplevel-mounted vols - try: - self.hub.asrv.vfs.get(vn.vpath, self.uname, True, False) - ret.append(SATTR.from_stat(vst, filename=vn.vpath)) - except: - pass + prefix = path + "/" + for vn in self.asrv.vfs.all_nodes.values(): + if path and not vn.vpath.startswith(prefix): + continue # vn is parent + vname = vn.vpath[len(prefix) :] + if "/" in vname or not vname: + continue # only include vols at current level + ret.append(SATTR.from_stat(vst, filename=vn.vpath)) ret.sort(key=lambda x: x.filename) + self.log("ls(%s): vfs-vols; |%d|" % (path, len(ret))) return ret _, vfs_ls, vfs_virt = vn.ls( @@ -394,6 +416,7 @@ class SFTP_Srv(paramiko.SFTPServerInterface): if self.uname not in vn.axs.udot: ret = [x for x in ret if not x.filename.split("/")[-1].startswith(".")] ret.sort(key=lambda x: x.filename) + self.log("ls(%s): |%d|" % (path, len(ret))) return ret def stat(self, path: str) -> SATTR | int: @@ -411,17 +434,25 @@ class SFTP_Srv(paramiko.SFTPServerInterface): return SFTP_FAILURE def _stat(self, vp: str) -> SATTR | int: + vp = vp.strip("/") try: - ap = self.v2a(vp, r=True)[0] + ap, vn, _ = self.v2a(vp) + if ( + self.uname not in vn.axs.uread + and self.uname not in vn.axs.uwrite + and self.uname not in vn.axs.uget + ): + self.log("stat(%s): EPERM" % (vp,)) + return SFTP_PERMISSION_DENIED st = bos.stat(ap) + self.log("stat(%s): %s" % (vp, st)) except: - if vp.strip("/") or self.asrv.vfs.realpath: - try: - self.v2a(vp, w=True)[0] - except: - return SFTP_PERMISSION_DENIED + if vp not in self.vis: + self.log("stat(%s): ENOENT" % (vp,)) + return SFTP_NO_SUCH_FILE zi = int(time.time()) st = os.stat_result((16877, -1, -1, 1, 1000, 1000, 8, zi, zi, zi)) + self.log("stat(%s): vfs-vols") return SATTR.from_stat(st) def open(self, path: str, flags: int, attr: SATTR) -> paramiko.SFTPHandle | int: @@ -457,10 +488,12 @@ class SFTP_Srv(paramiko.SFTPServerInterface): ap = os.path.join(vn.realpath, rem) vf = vn.flags except Pebkac as ex: - t = "denied open file [%s], iflag=%s, attr=%s, read=%s, write=%s: %s" - self.log(t % (vp, iflag, attr, rd, wr, ex)) + t = "denied open file [%s], iflag=%s, read=%s, write=%s: %s" + self.log(t % (vp, iflag, rd, wr, ex)) return SFTP_PERMISSION_DENIED + self.log("open(%s, %x, %s)" % (vp, iflag, smode)) + if wr: try: st = bos.stat(ap) @@ -496,6 +529,8 @@ class SFTP_Srv(paramiko.SFTPServerInterface): self.log(t, 3) return SFTP_PERMISSION_DENIED + self.log("writing to [%s] => [%s]" % (vp, ap)) + if wr and need_unlink: # type: ignore # !rm assert td # type: ignore # !rm if td >= -1 and td <= self.args.ftp_wt: @@ -519,13 +554,16 @@ class SFTP_Srv(paramiko.SFTPServerInterface): chmod = getattr(attr, "st_mode", None) if chmod is None: - chmod = vf.get("chmod_f", 644) + chmod = vf.get("chmod_f", 0o644) + self.log("open(%s, %x): client did not chmod" % (vp, iflag)) + else: + self.log("open(%s, %x): client set chmod 0%o" % (vp, iflag, chmod)) try: fd = os.open(ap, iflag, chmod) except OSError as ex: - t = "failed to os.open [%s] -> [%s] with iflag [%s] and chmod [%s]" - self.log(t % (vp, ap, iflag, chmod), 3) + t = "failed to os.open [%s] -> [%s] with iflag [%s] and chmod [%s]: %r" + self.log(t % (vp, ap, iflag, chmod, ex), 3) return paramiko.SFTPServer.convert_errno(ex.errno) if iflag & os.O_CREAT: @@ -534,8 +572,8 @@ class SFTP_Srv(paramiko.SFTPServerInterface): try: f = os.fdopen(fd, smode) except OSError as ex: - t = "failed to os.fdpen [%s] -> [%s] with smode [%s]" - self.log(t % (vp, ap, smode), 3) + t = "failed to os.fdpen [%s] -> [%s] with smode [%s]: %r" + self.log(t % (vp, ap, smode, ex), 3) return paramiko.SFTPServer.convert_errno(ex.errno) ret = SFTP_FH(iflag) @@ -552,17 +590,37 @@ class SFTP_Srv(paramiko.SFTPServerInterface): return SFTP_FAILURE def _remove(self, vp: str) -> int: + self.log("rm(%s)" % (vp,)) if self.args.no_del: self.log("The delete feature is disabled in server config") return SFTP_PERMISSION_DENIED try: self.hub.up2k.handle_rm(self.uname, self.ip, [vp], [], False, False) + self.log("rm(%s): ok" % (vp,)) return SFTP_OK except Pebkac as ex: t = "denied delete [%s]: %s" self.log(t % (vp, ex)) + if str(ex).startswith("file not found"): + return SFTP_NO_SUCH_FILE + try: + # write-only client trying to rm before upload? + ap, vn, _ = self.v2a(vp) + if ( + self.uname not in vn.axs.uread + and self.uname not in vn.axs.uwrite + and self.uname not in vn.axs.uget + ): + self.log("rm(%s): EPERM" % (vp,)) + return SFTP_PERMISSION_DENIED + if not bos.path.exists(ap): + self.log(" `- file didn't exist; returning ENOENT") + return SFTP_NO_SUCH_FILE + except: + pass return SFTP_PERMISSION_DENIED except OSError as ex: + self.log("failed: rm(%s): %r" % (vp, ex)) return paramiko.SFTPServer.convert_errno(ex.errno) def rename(self, oldpath: str, newpath: str) -> int: @@ -573,6 +631,7 @@ class SFTP_Srv(paramiko.SFTPServerInterface): return SFTP_FAILURE def _rename(self, svp: str, dvp: str) -> int: + self.log("mv(%s, %s)" % (svp, dvp)) if self.args.no_mv: self.log("The rename/move feature is disabled in server config") svp = svp.strip("/") @@ -585,6 +644,7 @@ class SFTP_Srv(paramiko.SFTPServerInterface): self.log(t % (svp, dvp, ex)) return SFTP_PERMISSION_DENIED except OSError as ex: + self.log("mv(%s, %s): %r" % (svp, dvp, ex)) return paramiko.SFTPServer.convert_errno(ex.errno) def mkdir(self, path: str, attr: SATTR) -> int: @@ -595,6 +655,7 @@ class SFTP_Srv(paramiko.SFTPServerInterface): return SFTP_FAILURE def _mkdir(self, vp: str, attr: SATTR) -> int: + self.log("mkdir(%s)" % (vp,)) try: vn, rem = self.asrv.vfs.get(vp, self.uname, False, True) ap = os.path.join(vn.realpath, rem) @@ -607,6 +668,7 @@ class SFTP_Srv(paramiko.SFTPServerInterface): self.log(t % (vp, ex)) return SFTP_PERMISSION_DENIED except OSError as ex: + self.log("mkdir(%s): %r" % (vp, ex)) return paramiko.SFTPServer.convert_errno(ex.errno) def rmdir(self, path: str) -> int: @@ -617,6 +679,7 @@ class SFTP_Srv(paramiko.SFTPServerInterface): return SFTP_FAILURE def _rmdir(self, vp: str) -> int: + self.log("rmdir(%s)" % (vp,)) try: vn, rem = self.asrv.vfs.get(vp, self.uname, False, False, will_del=True) ap = os.path.join(vn.realpath, rem) @@ -627,6 +690,7 @@ class SFTP_Srv(paramiko.SFTPServerInterface): self.log(t % (vp, ex)) return SFTP_PERMISSION_DENIED except OSError as ex: + self.log("rmdir(%s): %r" % (vp, ex)) return paramiko.SFTPServer.convert_errno(ex.errno) def chattr(self, path: str, attr: SATTR) -> int: @@ -637,6 +701,7 @@ class SFTP_Srv(paramiko.SFTPServerInterface): return SFTP_FAILURE def _chattr(self, vp: str, attr: SATTR) -> int: + self.log("chattr(%s, %s)" % (vp, attr)) try: vn, rem = self.asrv.vfs.get(vp, self.uname, False, True, will_del=True) ap = os.path.join(vn.realpath, rem) @@ -647,6 +712,7 @@ class SFTP_Srv(paramiko.SFTPServerInterface): self.log(t % (vp, ex)) return SFTP_PERMISSION_DENIED except OSError as ex: + self.log("chattr(%s): %r" % (vp, ex)) return paramiko.SFTPServer.convert_errno(ex.errno) def symlink(self, target_path: str, path: str) -> int: @@ -668,7 +734,7 @@ class Sftpd(object): self.bound: list[str] = [] self.sessions = {} - ips = args.i + ips = args.sftp_i if "::" in ips: ips.append("0.0.0.0") @@ -714,7 +780,7 @@ class Sftpd(object): for ip in ips: self._bind(ip) - self.log("listening on %s port %s" % (self.srv, args.sftp)) + self.log("listening @ %s port %s" % (self.bound, args.sftp)) def log(self, msg: str, c: Union[int, str] = 0) -> None: self.hub.log("sftp", msg, c) diff --git a/copyparty/smbd.py b/copyparty/smbd.py index 0d48a733..4d3d1996 100644 --- a/copyparty/smbd.py +++ b/copyparty/smbd.py @@ -89,7 +89,7 @@ class SMB(object): smbserver.isInFileJail = self._is_in_file_jail self._disarm() - ip = next((x for x in self.args.i if ":" not in x), None) + ip = next((x for x in self.args.smb_i if ":" not in x), None) if not ip: self.log("smb", "IPv6 not supported for SMB; listening on 0.0.0.0", 3) ip = "0.0.0.0" diff --git a/copyparty/tftpd.py b/copyparty/tftpd.py index e1f75c8b..9eb76643 100644 --- a/copyparty/tftpd.py +++ b/copyparty/tftpd.py @@ -175,7 +175,7 @@ class Tftpd(object): p1, p2 = [int(x) for x in self.args.tftp_pr.split("-")] ports = list(range(p1, p2 + 1)) - ips = self.args.i + ips = self.args.tftp_i if "::" in ips: ips.append("0.0.0.0") diff --git a/copyparty/up2k.py b/copyparty/up2k.py index 079001ee..06696cf4 100644 --- a/copyparty/up2k.py +++ b/copyparty/up2k.py @@ -4182,6 +4182,7 @@ class Up2k(object): st = bos.lstat(atop) is_dir = stat.S_ISDIR(st.st_mode) except: + # NOTE: "file not found" *sftpd raise Pebkac(400, "file not found on disk (already deleted?)") if "bcasechk" in vn.flags and not vn.casechk(rem, False): diff --git a/copyparty/util.py b/copyparty/util.py index ff5c8c10..75fc0ada 100644 --- a/copyparty/util.py +++ b/copyparty/util.py @@ -377,6 +377,7 @@ IMPLICATIONS = [ ["tftpvv", "tftpv"], ["nodupem", "nodupe"], ["no_dupe_m", "no_dupe"], + ["sftpvv", "sftpv"], ["smbw", "smb"], ["smb1", "smb"], ["smbvvv", "smbvv"], diff --git a/copyparty/web/browser.js b/copyparty/web/browser.js index 263927c4..3e9b65cf 100644 --- a/copyparty/web/browser.js +++ b/copyparty/web/browser.js @@ -678,6 +678,7 @@ var LANGN = [ ["fra", "français"], ["grc", "Ελληνικά"], ["ita", "Italiano"], + ["jpn", "日本語"], ["kor", "한국어"], ["nld", "Nederlands"], ["nno", "Nynorsk"], @@ -9481,14 +9482,16 @@ var rcm = (function () { bcfg_bind(r, 'double', 'rdb', false); var menu = ebi('rcm'); - var selFile = { + var nsFile = { elem: null, type: null, path: null, + url: null, id: null, relpath: null, no_dsel: false }; + var selFile = jcp(nsFile); function mktemp(is_dir) { var row = mknod('tr', 'temp', @@ -9543,15 +9546,15 @@ var rcm = (function () { switch(e.target.id.slice(1)) { case 'opn': var a = mknod('a'); - a.href = selFile.path; - a.target = selFile.type == 'dir' ? '' : '_blank'; + a.href = selFile.url; + a.target = selFile.type == "dir" ? '' : '_blank'; a.click(); break; case 'ply': selFile.type == 'gf' ? thegrid.imshow(selFile.relpath) : play('f-' + selFile.id); break; case 'pla': play('f-' + selFile.id); break; case 'txt': location = '?doc=' + selFile.relpath; break; case 'md': location = selFile.path + (has(selFile.path, '?') ? '&v' : '?v'); break; - case 'cpl': cliptxt(location.protocol + '//' + location.host + selFile.path, function() {toast.ok(2, L.clipped)}); break; + case 'cpl': cliptxt(selFile.url, function() {toast.ok(2, L.clipped)}); break; case 'dl': ebi('seldl').click(); break; case 'zip': ebi('selzip').click(); break; case 'del': fileman.delete(); break; @@ -9575,8 +9578,7 @@ var rcm = (function () { } function show(x, y, target, isGrid) { - selFile.elem = selFile.type = selFile.path = selFile.id = selFile.relpath = null; - selFile.no_dsel = false; + selFile = jcp(nsFile); if (target) { var file = target.closest("#files tbody tr"); if (isGrid && target.matches && target.matches('#ggrid > a')) { @@ -9588,7 +9590,8 @@ var rcm = (function () { clmod(file, "sel", true); selFile.elem = file; - selFile.path = basenames(file.children[1].firstChild.href).replace(/(&|\?)v/, ''); + selFile.url = file.children[1].firstChild.href; + selFile.path = basenames(selFile.url).replace(/(&|\?)v/, ''); selFile.relpath = selFile.path.split('/').slice(-1)[0].split("?")[0]; if (noq_href(file.children[1].firstChild).endsWith("/")) selFile.type = "dir"; @@ -9636,8 +9639,7 @@ var rcm = (function () { clmod(selFile.elem, "sel", false); msel.selui(); } - selFile.elem = selFile.type = selFile.path = selFile.id = selFile.relpath = null; - selFile.no_dsel = false; + selFile = jcp(nsFile); menu.style.display = ''; } diff --git a/copyparty/web/tl/chi.js b/copyparty/web/tl/chi.js index fdaca92e..7f02c973 100644 --- a/copyparty/web/tl/chi.js +++ b/copyparty/web/tl/chi.js @@ -647,6 +647,7 @@ Ls.chi = { "rc_md": "在文本编辑器中打开", //m "rc_dl": "下载", //m "rc_zip": "下载为压缩包", //m + "rc_cpl": "复制链接", //m "rc_del": "删除", //m "rc_cut": "剪切", //m "rc_cpy": "复制", //m diff --git a/copyparty/web/tl/cze.js b/copyparty/web/tl/cze.js index 66fcd850..658ff736 100644 --- a/copyparty/web/tl/cze.js +++ b/copyparty/web/tl/cze.js @@ -651,6 +651,7 @@ Ls.cze = { "rc_md": "otevřít v textovém editoru", //m "rc_dl": "stáhnout", //m "rc_zip": "stáhnout jako archiv", //m + "rc_cpl": "kopírovat odkaz", //m "rc_del": "smazat", //m "rc_cut": "vyjmout", //m "rc_cpy": "kopírovat", //m diff --git a/copyparty/web/tl/deu.js b/copyparty/web/tl/deu.js index d2db7787..4a423568 100644 --- a/copyparty/web/tl/deu.js +++ b/copyparty/web/tl/deu.js @@ -647,6 +647,7 @@ Ls.deu = { "rc_md": "im Texteditor öffnen", "rc_dl": "herunterladen", "rc_zip": "als Archiv herunterladen", + "rc_cpl": "Link kopieren", //m "rc_del": "löschen", "rc_cut": "ausschneiden", "rc_cpy": "kopieren", diff --git a/copyparty/web/tl/epo.js b/copyparty/web/tl/epo.js index 5e02196b..d4a0a44c 100644 --- a/copyparty/web/tl/epo.js +++ b/copyparty/web/tl/epo.js @@ -647,6 +647,7 @@ Ls.epo = { "rc_md": "malfermi en tekstredaktilo", //m "rc_dl": "elŝuti", //m "rc_zip": "elŝuti kiel arkivon", //m + "rc_cpl": "kopii ligilon", //m "rc_del": "forigi", //m "rc_cut": "eltondi", //m "rc_cpy": "kopii", //m diff --git a/copyparty/web/tl/fin.js b/copyparty/web/tl/fin.js index d227726c..18eb567b 100644 --- a/copyparty/web/tl/fin.js +++ b/copyparty/web/tl/fin.js @@ -647,6 +647,7 @@ Ls.fin = { "rc_md": "avaa tekstieditorissa", //m "rc_dl": "Lataa", //m "rc_zip": "Lataa arkistona", //m + "rc_cpl": "kopioi linkki", //m "rc_del": "poista", //m "rc_cut": "Leikkaa", //m "rc_cpy": "kopioi", //m diff --git a/copyparty/web/tl/fra.js b/copyparty/web/tl/fra.js index b1ae2814..d33d1420 100644 --- a/copyparty/web/tl/fra.js +++ b/copyparty/web/tl/fra.js @@ -647,6 +647,7 @@ Ls.fra = { "rc_md": "ouvrir dans l’éditeur de texte", //m "rc_dl": "télécharger", //m "rc_zip": "télécharger comme archive", //m + "rc_cpl": "copier le lien", //m "rc_del": "supprimer", //m "rc_cut": "couper", //m "rc_cpy": "copier", //m diff --git a/copyparty/web/tl/grc.js b/copyparty/web/tl/grc.js index 48552fee..9c9ae70c 100644 --- a/copyparty/web/tl/grc.js +++ b/copyparty/web/tl/grc.js @@ -647,6 +647,7 @@ Ls.grc = { "rc_md": "άνοιγμα στον επεξεργαστή κειμένου", //m "rc_dl": "λήψη", //m "rc_zip": "λήψη ως αρχείο", //m + "rc_cpl": "ἀντίγραφε σύνδεσμον", //m "rc_del": "διαγραφή", //m "rc_cut": "αποκοπή", //m "rc_cpy": "αντιγραφή", //m diff --git a/copyparty/web/tl/ita.js b/copyparty/web/tl/ita.js index 6ee5b0ea..597b22dd 100644 --- a/copyparty/web/tl/ita.js +++ b/copyparty/web/tl/ita.js @@ -647,6 +647,7 @@ Ls.ita = { "rc_md": "apri nell’editor di testo", //m "rc_dl": "scarica", //m "rc_zip": "scarica come archivio", //m + "rc_cpl": "copia link", //m "rc_del": "elimina", //m "rc_cut": "taglia", //m "rc_cpy": "copia", //m diff --git a/copyparty/web/tl/jpn.js b/copyparty/web/tl/jpn.js new file mode 100644 index 00000000..c5d00370 --- /dev/null +++ b/copyparty/web/tl/jpn.js @@ -0,0 +1,711 @@ + +//末尾が //m の行は未検証の機械翻訳です + +Ls.jpn = { + "tt": "日本語", + + "cols": { + "c": "アクションボタン", + "dur": "間隔", + "q": "品質 / ビットレート", + "Ac": "オーディオコーデック", + "Vc": "ビデオコーデック", + "Fmt": "フォーマット / コンテナ", + "Ahash": "オーディオチェックサム", + "Vhash": "ビデオチェックサム", + "Res": "解像度", + "T": "ファイル形式", + "aq": "オーディオ 品質 / ビットレート", + "vq": "ビデオ 品質 / ビットレート", + "pixfmt": "サブサンプリング / ピクセル構造", + "resw": "水平解像度", + "resh": "垂直解像度", + "chs": "オーディオチャンネル", + "hz": "サンプリングレート", + }, + + "hks": [ + [ + "misc", + ["ESC", "閉じる"], + + "file-manager", + ["G", "リスト / グリッド表示を切り替える"], + ["T", "サムネイル / アイコンを切り替える"], + ["⇧ A/D", "サムネイルサイズ"], + ["ctrl-K", "選択した項目を削除"], + ["ctrl-X", "選択範囲をクリップボードに切り取る"], + ["ctrl-C", "選択範囲をクリップボードにコピー"], + ["ctrl-V", "ここに貼り付け(移動/コピー)"], + ["Y", "選択した項目をダウンロード"], + ["F2", "選択した項目の名前を変更"], + + "file-list-sel", + ["space", "ファイル選択の切り替え"], + ["↑/↓", "選択カーソルを移動"], + ["ctrl ↑/↓", "カーソルとビューポートを移動"], + ["⇧ ↑/↓", "前/次のファイルを選択"], + ["ctrl-A", "すべてのファイル / フォルダを選択"], + ], [ + "navigation", + ["B", "パンくずリスト / ナビペインを切り替える"], + ["I/K", "前/次のフォルダ"], + ["M", "親フォルダ(または現在のフォルダを展開解除)"], + ["V", "ナビペインのフォルダ / テキストファイルを切り替える"], + ["A/D", "ナビペインサイズ"], + ], [ + "audio-player", + ["J/L", "前/次の曲"], + ["U/O", "10秒前/後スキップ"], + ["0..9", "0%~90%へジャンプ"], + ["P", "再生/一時停止(開始も)"], + ["S", "再生中の曲を選択"], + ["Y", "曲をダウンロード"], + ], [ + "image-viewer", + ["J/L, ←/→", "前/次の画像"], + ["Home/End", "最初/最後の画像"], + ["F", "フルスクリーン"], + ["R", "時計回りに回転"], + ["⇧ R", "反時計回りに回転"], + ["S", "画像を選択"], + ["Y", "画像をダウンロード"], + ], [ + "video-player", + ["U/O", "10秒前/後へスキップ"], + ["P/K/Space", "再生/一時停止"], + ["C", "自動再生"], + ["V", "ループ"], + ["M", "ミュート"], + ["[ and ]", "ループ間隔を設定"], + ], [ + "textfile-viewer", + ["I/K", "前/次のファイル"], + ["M", "テキストファイルを閉じる"], + ["E", "テキストファイルの編集"], + ["S", "ファイルを選択(切り取り/コピー/名前変更)"], + ["Y", "テキストファイルをダウンロード"], + ["⇧ J", "JSONを整形する"], + ] + ], + + "m_ok": "OK", + "m_ng": "キャンセル", + + "enable": "有効", + "danger": "危険", + "clipped": "クリップボードにコピーされました", + + "ht_s1": "秒", + "ht_s2": "秒", + "ht_m1": "分", + "ht_m2": "分", + "ht_h1": "時間", + "ht_h2": "時間", + "ht_d1": "日", + "ht_d2": "日", + "ht_and": " と ", + + "goh": "コントロールパネル", + "gop": '前のフォルダ">prev', + "gou": '親フォルダ">up', + "gon": '次のフォルダ">next', + "logout": "ログアウト ", + "login": "ログイン", + "access": " アクセス", + "ot_close": "サブメニューを閉じる", + "ot_search": "属性、パス/名前、音楽タグ、またはそれらの組み合わせでファイルを検索$N$N<code>foo bar</code> = «foo» と «bar» の両方を含める。$N<code>foo -bar</code> = «foo» を含み、«bar» を含まない必要があります。$N<code>^yana .opus$</code> = «yana» で始まり、«opus» ファイルである$N<code>"try unite"</code> = «try unite» だけを含む$N$N日付形式は iso-8601。たとえば、$N<code>2009-12-31</code> や <code>2020-09-12 23:30:00</code>", + "ot_unpost": "unpost: 最近アップロードした投稿を削除するか、未完成の投稿を中止", + "ot_bup": "bup: 基本的なアップローダー。Netscape 4.0 もサポートしています。", + "ot_mkdir": "mkdir: 新しいディレクトリを作成", + "ot_md": "new-file: 新しいテキストファイルを作成", + "ot_msg": "msg: サーバーログにメッセージを送信", + "ot_mp": "メディアプレーヤー設定", + "ot_cfg": "設定オプション", + "ot_u2i": 'up2k: ファイルをアップロードする (書き込みアクセス権がある場合)、または検索モードに切り替えてファイルがサーバーのどこかに存在するかどうかを確認$N$Nアップロードは再開可能で、マルチスレッドであり、ファイルのタイムスタンプが保持されますが、[🎈] (基本的なアップローダー) よりも多くの CPU を使用します

アップロード中、このアイコンは進行状況インジケーターになります。', + "ot_u2w": 'up2k: 再開サポート付きのファイルのアップロード(ブラウザを閉じて、後で同じファイルをドロップします)$N$Nマルチスレッドで、ファイルのタイムスタンプが保持されますが、[🎈](基本的なアップローダー)よりも多くの CPU を使用します。

アップロード中は、このアイコンが進行状況インジケーターになります。', + "ot_noie": 'Chrome / Firefox / Edgeを利用してください', + + "ab_mkdir": "ディレクトリを作成", + "ab_mkdoc": "新しいテキストファイル", + "ab_msg": "メッセージをサーバーログに送信", + + "ay_path": "フォルダへジャンプ", + "ay_files": "ファイルへジャンプ", + + "wt_ren": "選択した項目の名前を変更する$Nホットキー: F2", + "wt_del": "選択した項目を削除$Nホットキー: ctrl-K", + "wt_cut": "選択した項目を切り取る <small>(その後別の場所に貼り付ける)</small>$Nホットキー: ctrl-X", + "wt_cpy": "選択した項目をクリップボード$Nにコピー(別の場所に貼り付ける)$Nホットキー: ctrl-C", + "wt_pst": "以前に切り取った/コピーした選択範囲を貼り付ける$Nホットキー: ctrl-V", + "wt_selall": "すべてのファイルを選択$Nホットキー: ctrl-A(ファイルにフォーカスがあるとき)", + "wt_selinv": "選択を反転", + "wt_zip1": "このフォルダを圧縮してダウンロード", + "wt_selzip": "選択内容を圧縮してダウンロード", + "wt_seldl": "選択した項目を個別のファイルとしてダウンロード$Nホットキー: Y", + "wt_npirc": "IRC形式のトラック情報をコピーする", + "wt_nptxt": "プレーンテキストのトラック情報をコピー", + "wt_m3ua": "m3uプレイリストに追加 (後で 📻コピー をクリック)", + "wt_m3uc": "m3uプレイリストをクリップボードにコピー", + "wt_grid": "グリッド / リスト表示を切り替える$Nホットキー: G", + "wt_prev": "前のトラック$Nホットキー: J", + "wt_play": "再生 / 一時停止$Nホットキー: P", + "wt_next": "次のトラック$Nホットキー: L", + + "ul_par": "並列アップロード:", + "ut_rand": "ファイル名をランダム化する", + "ut_u2ts": "最終更新日時のタイムスタンプ$Nファイルシステムからサーバーへコピーする\">📅", + "ut_ow": "サーバー上の既存のファイルを上書きする?$N🛡️: しない(代わりに新しいファイル名を生成する)$N🕒: サーバーのファイルが古い場合は上書きする$N♻️: ファイルが異なる場合は常に上書きする$N⏭️: 既存のファイルをすべて無条件にスキップする", + "ut_mt": "アップロード中に他のファイルのハッシュを継続する$N$NCPUやHDDがボトルネックになっている場合は無効にしてください", + "ut_ask": 'aアップロードを開始する前に確認を求める">💭', + "ut_pot": "UIをシンプルにすることで$N低速デバイスでのアップロード速度を向上させる", + "ut_srch": "実際にはアップロードせず、代わりにファイルが既にアップロードされているかどうかを確認 $N すでにサーバー上に存在(読み取り可能なすべてのフォルダをスキャン)", + "ut_par": "0に設定するとアップロードを一時停止$N$N接続が遅い / 遅延が大きい場合は増やす$N$NLANやサーバーのHDDがボトルネックになっている場合は1にする", + "ul_btn": "ファイル / フォルダを
ここにドロップしてください(またはクリックしてください)", + "ul_btnu": "ア ッ プ ロ ー ド", + "ul_btns": "検 索", + + "ul_hash": "ハッシュ", + "ul_send": "送信", + "ul_done": "完了", + "ul_idle1": "キューにはまだアップロードは登録されていない", + "ut_etah": "平均 <em>ハッシュ化</em> 速度と完了予想時間", + "ut_etau": "平均 <em>アップロード</em> 速度と完了予想時間", + "ut_etat": "平均 <em>合計</em> 速度と完了予想時間", + + "uct_ok": "正常に完了", + "uct_ng": "NG: 失敗 / 拒否 / 見つからない", + "uct_done": "OKとNGの組み合わせ", + "uct_bz": "ハッシュ化またはアップロード", + "uct_q": "アイドル状態、保留中", + + "utl_name": "ファイル名", + "utl_ulist": "リスト", + "utl_ucopy": "コピー", + "utl_links": "リンク", + "utl_stat": "状態", + "utl_prog": "進捗", + + // keep short: + "utl_404": "404", + "utl_err": "エラー", + "utl_oserr": "OS-エラー", + "utl_found": "発見", + "utl_defer": "延期", + "utl_yolo": "YOLO", + "utl_done": "完了", + + "ul_flagblk": "ファイルがキューに追加されました
しかし、別のブラウザタブにはビジー状態のup2kがあり、
それが終わるまで待機します", + "ul_btnlk": "サーバー構成によりこのスイッチはこの状態にロックされています", + + "udt_up": "アップロード", + "udt_srch": "検索", + "udt_drop": "ここにドロップ", + + "u_nav_m": '
はい、何かもってますか?
Enter = ファイル(1つ以上)\nESC = 1つのフォルダ(サブフォルダを含む)', + "u_nav_b": 'ファイルフォルダ', + + "cl_opts": "スイッチ", + "cl_hfsz": "ファイルサイズ", + "cl_themes": "テーマ", + "cl_langs": "言語", + "cl_ziptype": "フォルダダウロード", + "cl_uopts": "up2kスイッチ", + "cl_favico": "ファビコン", + "cl_bigdir": "ディレクトリの最大数", + "cl_hsort": "#ソート", + "cl_keytype": "キーの表記タイプ", + "cl_hiddenc": "非表示の列", + "cl_hidec": "非表示", + "cl_reset": "リセット", + "cl_hpick": "下の表で非表示にするには列ヘッダーをタップします", + "cl_hcancel": "列の非表示を解除", + "cl_rcm": "右クリックメニュー", + + "ct_grid": '田 グリッド', + "ct_ttips": '◔ ◡ ◔">ℹ️ ツールチップ', + "ct_thumb": 'グリッドビューではアイコンまたはサムネイルを切り替える$Nホットキー: T">🖼️ サムネイル', + "ct_csel": 'グリッドビューでファイルを選択するにはCtrlとShiftを使用する。">選択', + "ct_dl": 'ファイルをクリックしたときに強制的にダウンロードする(インラインで表示しない)">dl', + "ct_ihop": '画像ビューアを閉じたら最後に表示したファイルまでスクロールする。">g⮯', + "ct_dots": '隠しファイルを表示する(サーバーが許可している場合)">隠しファイル', + "ct_qdel": 'ファイルを削除するときは確認を一度だけ求める">qdel', + "ct_dir1st": 'ファイルの前にフォルダを並べ替える">📁 優先', + "ct_nsort": '自然ソート(先頭に数字があるファイル名の場合)">nsort', + "ct_utc": 'すべての日時をUTCで表示">UTC', + "ct_readme": 'フォルダ一覧にREADME.mdを表示">📜 readme', + "ct_idxh": 'フォルダ一覧の代わりにindex.htmlを表示">htm', + "ct_sbars": 'スクロールバーを表示">⟊', + + "cut_umod": "サーバー上にファイルが既に存在する場合はサーバーの最終更新タイムスタンプをローカルファイルと一致するように更新します(書き込み+削除権限が必要です)\">re📅", + + "cut_turbo": "yoloボタンを使用する場合できればこれを有効にしないでください:$N$N大量のファイルをアップロードしていて何らかの理由で再起動する必要があり、できるだけ早くアップロードを続行したい場合にこれを使用します。$N$Nこれはハッシュチェックを単純な"これはサーバー上で同じファイルサイズですか?"に置き換えます。そのため、ファイルの内容が異なる場合はアップロードされません。$N$Nアップロードが完了したらこれをオフにし、同じファイルを再度 "アップロード" してクライアントに検証させる必要があります。\">turbo", + + "cut_datechk": "turboボタンが有効になっていなければ効果はありません$N$Nyolo要素をわずかに減らしサーバー上のファイルのタイムスタンプが一致するかどうかを確認します。$N$N理論的にはほとんどの未完了 / 破損したアップロードを把握できるはずですが、その後ターボを無効にして検証パスを実行する代わりにはなりません。\">date-chk", + + "cut_u2sz": "各アップロードチャンクのサイズ(MiB); 大西洋を横断する場合は大きな値の方が飛行効率が良いです。接続の信頼性が低い場合は、小さな値を試してください。", + + "cut_flag": "一度にアップロードするタブは1つだけにしてください $N -- 他のタブでもこれを有効にする必要があります $N -- 同じドメインのタブにのみ影響します", + + "cut_az": "ファイルの最小サイズ順ではなくアルファベット順にファイルをアップロードします。$N$アルファベット順にするとサーバー上で何か問題が発生した場合に簡単に確認できるようになりますが、光ファイバー / LANでのアップロードが若干遅くなります。", + + "cut_nag": "アップロード完了時のOS通知$N(ブラウザまたはタブがアクティブでない場合のみ)", + "cut_sfx": "アップロードが完了すると音声アラートが鳴ります$N(ブラウザまたはタブがアクティブでない場合のみ)", + + "cut_mt": "マルチスレッドを使用してファイルハッシュを高速化する$N$NこれはWebワーカーを使用し$N追加のRAM(最大512 MiB)が必要$N$Nこれによりhttpsは30%高速化、httpは4.5倍高速化r\">mt", + + "cut_wasm": "ブラウザの組込みのハッシュ関数の代わりにwasmを使用します。Chromeベースのブラウザでは速度が向上しますがCPU負荷が増加します。また、Chromeの古いバージョンの多くにはこれを有効にするとブラウザがすべてのRAMを消費してクラッシュするというバグがあります。\">wasm", + + "cft_text": "ファビコンテキスト(無効にするには空白にして更新してください)", + "cft_fg": "テキストカラー", + "cft_bg": "背景色", + + "cdt_lim": "フォルダに表示するファイルの最大数", + "cdt_ask": "一番下までスクロールしたときに$N更にファイルを読み込む代わりに$N何をするか尋ねる", + "cdt_hsort": "メディアURLに含めるソートルール (<code>,sorthref</code>) の数。0に設定するとメディアリンクをクリックした際にそのリンクに含まれるソートルールも無視されます。", + "cdt_ren": "カスタム右クリックメニューを有効にしてもShiftキーを押しながら右クリックすることで通常のメニューにアクセスできます。", + + "tt_entree": "ナビペインを表示(ディレクトリツリーサイドバー)$Nホットキー: B", + "tt_detree": "パンくずリストを表示$Nホットキー: B", + "tt_visdir": "選択したフォルダまでスクロール", + "tt_ftree": "フォルダツリー / テキストファイルの切り替え$Nホットキー: V", + "tt_pdock": "上部のドッキングされたペインに親フォルダを表示", + "tt_dynt": "ツリーが拡大するにつれて自動的に増加", + "tt_wrap": "単語の折り返し", + "tt_hover": "ホバーすると溢れた線を表示する$N( マウスを押さない限りスクロールが中断されます $N  カーソルは左余白です )", + + "ml_pmode": "フォルダの末尾...", + "ml_btns": "コマンド", + "ml_tcode": "変換", + "ml_tcode2": "この形式に変換", + "ml_tint": "色合い", + "ml_eq": "オーディオイコライザー", + "ml_drc": "ダイナミックレンジコンプレッサー", + + "mt_loop": "1曲をループ/リピート再生\">🔁", + "mt_one": "1曲で止める\">1️⃣", + "mt_shuf": "各フォルダ内の曲をシャッフルする\">🔀", + "mt_aplay": "サーバーにアクセスするためにクリックしたリンクに曲IDがある場合は自動再生されます$N$Nこれを無効にすると、音楽を再生するときにページのURLが曲IDで更新されなくなります。これにより設定が失われてもURLが残っている場合の自動再生が防止されます。\">a▶", + "mt_preload": "ギャップレス再生のために曲の終わり近くに次の曲の読み込みを開始する\">preload", + "mt_prescan": "最後の曲が終了する前に次のフォルダへ移動し$Nウェブブラウザが$N再生を停止しないようにする\">nav", + "mt_fullpre": "曲全体を事前ロードしてみる;$N✅ 信頼できない接続で有効にする、$N❌ 低速接続では無効にする\">full", + "mt_fau": "携帯電話では、次の曲が十分に早く読み込まれない場合に音楽が停止しないようにする(タグの表示が不安定になる可能性があります)\">☕️", + "mt_waves": "波形シークバー:$Nスクラバーにオーディオ振幅を表示する\">~s", + "mt_npclip": "現在再生中の曲をクリップボードに保存するためのボタンを表示する\">/np", + "mt_m3u_c": "選択した曲をm3u8プレイリストエントリとして$Nクリップボードに保存するためのボタンを表示する\">📻", + "mt_octl": "OS統合(メディアホットキー / OSD)\">os-ctl", + "mt_oseek": "OS統合によるシークを許可する$N$N注: 一部のデバイス(iPhone)では$N次の曲ボタンの代わりになります\">シーク", + "mt_oscv": "OSDでアルバムカバーを表示する\">art", + "mt_follow": "再生中の曲をスクロールして表示したままにする\">🎯", + "mt_compact": "コントローラーを小さく\">⟎", + "mt_uncache": "キャッシュクリア  (ブラウザが破損した曲のコピーをキャッシュしているために$N再生できない場合はこれを試してください)\">uncache", + "mt_mloop": "開いているフォルダをループ\">🔁 ループ", + "mt_mnext": "次のフォルダを読み込んで続行\">📂 次", + "mt_mstop": "再生を停止\">⏸ 停止", + "mt_cflac": "flac / wavを{0}に変換\">flac", + "mt_caac": "aac / m4aを{0}に変換\">aac", + "mt_coth": "その他すべて(mp3以外)を{0}に変換\">その他", + "mt_c2opus": "デスクトップ、ノート、Androidに最適\">opus", + "mt_c2owa": "opus-weba(iOS 17.5以降)\">owa", + "mt_c2caf": "opus-caf(iOS 11から17まで)\">caf", + "mt_c2mp3": "非常に古いデバイスでこれを使用\">mp3", + "mt_c2flac": "最高の音質、ダウンロードサイズが大きい\">flac", + "mt_c2wav": "非圧縮再生(さらに大きい)\">wav", + "mt_c2ok": "素晴らしい、良い選択です", + "mt_c2nd": "これは現在のデバイスに推奨される出力形式ではありませんが、問題ありません", + "mt_c2ng": "現在のデバイスはこの出力形式をサポートしていないようですが、とにかく試してみましょう", + "mt_xowa": "iOSにはこのフォーマットを使用したバックグラウンド再生を妨げるバグがあります; 代わりにcafまたはmp3を使用してください", + "mt_tint": "シークバーの背景レベル(0~100)を調整し$Nバッファリングの邪魔にならにようにする", + "mt_eq": "イコライザーとゲイン制御を有効にします;$N$Nブースト <code>0</code> = 標準音量100%(変更なし)$N$N幅 <code>1  </code> = 標準ステレオ(変更なし)$N幅 <code>0.5</code> = 左右のクロスフィード50%$N幅 <code>0  </code> = モノラル$N$Nブースト <code>-0.8</code> & 幅 <code>10</code> = ボーカル除去 :^)$N$Nイコライザーを有効にするとギャップレスアルバムは完全にギャップレスになります。そのため、それを気に場合すべての値をゼロ(幅 = 1を除く)にしてイコライザーをオンにしたままにしてください。", + "mt_drc": "ダイナミックレンジコンプレッサー(ボリュームフラットナー / ブリックウォーラー)を有効にします; EQでスパゲッティのバランスをとることもできます。そのため、EQのフィールドを「幅」以外すべて0に設定してください。$N$Nしきい値を超えるオーディオの音量を下げる; しきい値を超えるRATIO dBごとに1dBの出力されますしきい値-24と比率12のデフォルト値は、-22dBを超えることはなく、イコライザーのブーストを0.8に、またはATK 0とRLS 90などの非常に大きな値にした場合1.8まで安全に上げられます (Firefoxでのみ機能し、他のブラウザーではRLSは最大 1 です)。$N$N(Wikipedia を参照してください。もっとわかりやすく説明されています)", + + "mb_play": "再生", + "mm_hashplay": "このオーディオファイルを再生しますか?", + "mm_m3u": "再生するにはEnter/OKを押します\n編集するにはESC/キャンセルを押してください", + "mp_breq": "Firefox 82以降、Chrome 73以降、またはiOS 15以降が必要です", + "mm_bload": "読み込み中...", + "mm_bconv": "{0}に変換中。お待​​ちください。...", + "mm_opusen": "現在のブラウザはaac / m4aファイルを再生できません;\nOpusへのトランスコードが有効になりました", + "mm_playerr": "再生に失敗: ", + "mm_eabrt": "再生の試行はキャンセルされました", + "mm_enet": "インターネット接続が不安定です", + "mm_edec": "このファイルは破損している??", + "mm_esupp": "現在のブラウザはこのオーディオ形式に対応していません", + "mm_eunk": "不明なエラー", + "mm_e404": "オーディオを再生できませんでした。エラー404: ファイルが見つかりません。", + "mm_e403": "オーディオを再生できませんでした。エラー403: アクセス拒否。\n\nF5キーを押してリロードしてみてください。ログアウトしている可能性があります。", + "mm_e500": "オーディオを再生できませんでした。エラー500: サーバーログを確認してください。", + "mm_e5xx": "オーディオを再生できませんでした。サーバーエラー ", + "mm_nof": "近くにオーディオファイルが見つかりません", + "mm_prescan": "次に再生する曲を探しています...", + "mm_scank": "次の曲を見つけました:", + "mm_uncache": "キャッシュがクリアされました; 次回の再生時にすべての曲が再ダウンロードされます", + "mm_hnf": "その曲はもう存在しません", + + "im_hnf": "その画像はもう存在しません", + + "f_empty": 'このフォルダは空です', + "f_chide": 'これにより列 «{0}» が非表示になります\n\n設定タブで列の非表示を解除できます', + "f_bigtxt": "このファイルは {0} MiB の大きさです -- 本当にテキストとして表示しますか?", + "f_bigtxt2": "代わりにファイルの末尾だけを表示しませんか?これにより追跡も有効になり、新しく追加されたテキスト行がリアルタイムで表示されます。", + "fbd_more": '
表示中 {0} / {1} ファイル; {2}件を表示 または すべて表示
', + "fbd_all": '
表示中 {0} / {1} ファイル; すべて表示
', + "f_anota": "{1}件のアイテムのうち {0}件が選択されました;\nフォルダ全体を選択するには、まず一番下までスクロールします", + + "f_dls": '現在のフォルダ内のファイルリンクは\nダウンロードリンクに変更されました', + + "f_partial": "現在アップロード中のファイルを安全にダウンロードするには、同じファイル名で.PARTIAL拡張子がないファイルをクリックしてください。これを行うにはキャンセルまたはEscキーを押してください。\n\nOK / Enter を押すとこの警告は無視され、代わりに.PARTIALスクラッチファイルのダウンロードが続行されますが、ほとんどの場合データが破損することになります。", + + "ft_paste": "{0}件のアイテムを貼り付け$Nホットキー: ctrl-V", + "fr_eperm": '名前を変更できません:\nこのフォルダではの"移動"の権限がありません', + "fd_eperm": '削除できません:\nこのフォルダではの"削除"の権限がありません', + "fc_eperm": '切り取りできません:\nこのフォルダではの"移動"の権限がありません', + "fp_eperm": '貼付けできません:\nこのフォルダではの"書込"の権限がありません', + "fr_emore": "名前を変更する項目を1つ以上選択してください", + "fd_emore": "削除する項目を1つ以上選択してください", + "fc_emore": "切り取る項目を1つ以上選択してください", + "fcp_emore": "クリップボードにコピーする項目を1つ以上選択してください", + + "fs_sc": "現在表示中のフォルダを共有する", + "fs_ss": "選択したファイルを共有する", + "fs_just1d": "複数のフォルダを選択することはできません\nまた、1回の選択でファイルとフォルダを混在させることはできません。", + "fs_abrt": "❌ 中止", + "fs_rand": "🎲 ランダム名", + "fs_go": "✅ 共有を作成", + "fs_name": "名前", + "fs_src": "ソース", + "fs_pwd": "パスワード", + "fs_exp": "有効期限", + "fs_tmin": "分", + "fs_thrs": "時間", + "fs_tdays": "日", + "fs_never": "無期限", + "fs_pname": "任意のリンク名; 空白の場合はランダムになります", + "fs_tsrc": "共有するファイルまたはフォルダ", + "fs_ppwd": "任意のパスワード", + "fs_w8": "共有の作成...", + "fs_ok": "Enter/OKを押してクリップボードに保存します\nESC/キャンセルを押して閉じます", + + "frt_dec": "壊れたファイル名を修正するかもしれません\">url-decode", + "frt_rst": "変更したファイル名を元の名前に戻す\">↺ リセット", + "frt_abrt": "中止してこのウィンドウを閉じる\">❌ キャンセル", + "frb_apply": "名前の変更を適用", + "fr_adv": "バッチ / メタデータ / パターン名変更\">詳細設定", + "fr_case": "大文字と小文字を区別する正規表現\">case", + "fr_win": "Windowsで安全な名前; <>:"\\|?*を日本語の全角文字に置き換える\">win", + "fr_slash": "/を新しいフォルダが作成されない文字に置き換える\">no /", + "fr_re": "元のファイル名に適用する正規表現検索パターン; キャプチャグループは、<code>(1)</code> や <code>(2)</code> のように、以下のフォーマットフィールドで参照することができるので、", + "fr_fmt": "foob​​ar2000 を参考にしています:$N<code>(title)</code> は曲名に置き換えられます。$N<code>[(artist) - ](title)</code> はアーティストが空白の場合は[この]部分をスキップします。$N<code>$lpad((tn),2,0)</code> はトラック番号を2桁にパディングします。", + "fr_pdel": "削除", + "fr_pnew": "名前をつけて保存", + "fr_pname": "新しいプリセットの名前を入力します", + "fr_aborted": "中止されました", + "fr_lold": "古い名前", + "fr_lnew": "新しい名前", + "fr_tags": "選択したファイルのタグ(読み取り専用、参照のみ):", + "fr_busy": "{0}件のアイテムの名前を変更...\n\n{1}", + "fr_efail": "名前の変更に失敗:\n", + "fr_nchg": "新しい名前のうち {0}件は、win および/または no / により変更されました。\n\nこれらを変更後の新しい名前で続行しますか?", + + "fd_ok": "削除成功", + "fd_err": "削除失敗:\n", + "fd_none": "何も削除されませんでした; サーバー構成 (xbd) によってブロックされた可能性があります。", + "fd_busy": "{0}件のアイテムを削除中...\n\n{1}", + "fd_warn1": "これら {0}件のアイテムを削除しますか?", + "fd_warn2": "最後の警告! 取り消し不可。削除しますか?", + + "fc_ok": "{0}件のアイテムを切り取り", + "fc_warn": '{0}件のアイテムを切り取りました\n\nしかし: このブラウザタブでのみ貼り付けることができます\n(選択範囲が非常に大きいため)', + + "fcc_ok": "{0}件のアイテムをクリップボードにコピー", + "fcc_warn": '{0}件のアイテムをクリップボードにコピーしました\n\nしかし: このブラウザタブでのみ貼り付けることができます\n(選択範囲が非常に大きいため)', + + "fp_apply": "これらの名前を使用する", + "fp_skip": "競合をスキップ", // トピックノート: "既存の名前をスキップ" (対象フォルダ内のファイル名) + "fp_ecut": "最初にファイル / フォルダを切り取りまたはコピーし、貼り付け / 移動します\n\n注: 異なるブラウザタブ間で切り取り / 貼り付けが可能です", + "fp_ename": "名前がすでに使用されているため、{0}件のアイテムはここに移動することはできません。続行するには、以下に新しい名前を入力するか名前を空白(\"競合をスキップ\")にしてスキップしてください:", + "fcp_ename": "名前がすでに使用されているため、{0}件のアイテムはここにコピーすることはできません。続行するには、以下に新しい名前を入力するか名前を空白(\"競合をスキップ\")にしてスキップしてください:", + "fp_emore": "まだ修正すべきファイル名の競合がいくつか残っています", + "fp_ok": "移動完了", + "fcp_ok": "コピー完了", + "fp_busy": "{0}件のアイテムを移動中...\n\n{1}", + "fcp_busy": "{0}件のアイテムをコピー中...\n\n{1}", + "fp_abrt": "中止しています...", + "fp_err": "移動に失敗:\n", + "fcp_err": "コピーに失敗:\n", + "fp_confirm": "これらの{0}件のアイテムをここに移動しますか?", + "fcp_confirm": "これらの{0}件のアイテムをここにコピーしますか?", + "fp_etab": '他のブラウザタブからクリップボードを読み取ることができませんでした', + "fp_name": "デバイスからファイルをアップロードします。ファイル名を入力してください:", + "fp_both_m": '
貼り付けるものを選択
Enter = «{1}»から{0}件のファイルを移動\nESC = デバイスから{2}件のファイルをアップロード', + "fcp_both_m": '
貼り付けるものを選択
Enter = «{1}»から{0}件のファイルをコピー\nESC = デバイスから{2}件のファイルをアップロード', + "fp_both_b": '移動アップロード', + "fcp_both_b": 'コピーアップロード', + + "mk_noname": "それをする前に左側のテキストフィールドに名前を入力してください :p", + "nmd_i1": "必要なファイル拡張子も追加します。例: .md", + "nmd_i2": "削除権限がないため、.md ファイルのみを作成できます", + + "tv_load": "テキストドキュメントの読み込み中:\n\n{0}\n\n{1}%({2} / {3} MiB ロード済み)", + "tv_xe1": "テキストファイルを読み込めませんでした:\n\nエラー ", + "tv_xe2": "404、ファイルが見つかりません", + "tv_lst": "テキストファイルの一覧", + "tvt_close": "フォルダビューに戻る$Nホットキー: M(または Esc)\">❌ 閉じる", + "tvt_dl": "このファイルをダウンロード$Nホットキー: Y\">💾 ダウンロード", + "tvt_prev": "前のドキュメントを表示$Nホットキー: i\">⬆ 前へ", + "tvt_next": "次のドキュメントを表示$Nホットキー: K\">⬇ 次へ", + "tvt_sel": "ファイルの選択  (切り取り/コピー/削除/...)$Nホットキー: S\">選択", + "tvt_j": "jsonの整形$Nホットキー: shift-J\">j", + "tvt_edit": "テキストエディタでファイルを開く$Nホットキー: E\">✏️ 編集", + "tvt_tail": "ファイルの変更を監視する; 新しい行をリアルタイムで表示する\">📡 監視", + "tvt_wrap": "ワードラップ\">↵", + "tvt_atail": "ページ最下部までスクロールをロック\">⚓", + "tvt_ctail": "端末カラーをデコード(ANSIエスケープコード)\">🌈", + "tvt_ntail": "スクロールバック制限(読み込むテキストの最大バイト数)", + + "m3u_add1": "曲がm3uプレイリストに追加されました", + "m3u_addn": "{0}曲をm3uプレイリストに追加しました", + "m3u_clip": "m3uプレイリストがクリップボードにコピーされました\n\nsomething.m3uという新しいテキストファイルを作成し、そのドキュメントにプレイリストを貼り付けます; これで再生可能になります", + + "gt_vau": "動画は表示せず音声のみを再生する\">🎧", + "gt_msel": "ファイル選択を有効にする; ファイルをCtrlキーを押しながらクリックすると上書き保存します$N$N<em>有効時: ファイル / フォルダをダブルクリックすると開きます</em>$N$Nホットキー: S\">複数選択", + "gt_crop": "中央切り抜きのサムネイル\">切抜き", + "gt_3x": "高解像度サムネイル\">3x", + "gt_zoom": "拡大", + "gt_chop": "切断", + "gt_sort": "並べ替え", + "gt_name": "名前", + "gt_sz": "サイズ", + "gt_ts": "日付", + "gt_ext": "種類", + "gt_c1": "ファイル名をさらに短縮する(表示を減らす)", + "gt_c2": "ファイル名を短縮する(詳細を表示)", + + "sm_w8": "検索中...", + "sm_prev": "以下の検索結果は以前のクエリからのものです:\n ", + "sl_close": "検索結果を閉じる", + "sl_hits": "{0}件の結果を表示中", + "sl_moar": "さらに読み込む", + + "s_sz": "サイズ", + "s_dt": "日付", + "s_rd": "パス", + "s_fn": "名前", + "s_ta": "タグ", + "s_ua": "Up@", + "s_ad": "詳細.", + "s_s1": "最小 MiB", + "s_s2": "最大 MiB", + "s_d1": "古い. iso8601", + "s_d2": "新しい. iso8601", + "s_u1": "アップロード後", + "s_u2": "および/または前", + "s_r1": "パスに含まれる  (スペース区切り)", + "s_f1": "名前に含まれる  (-nopeで否定)", + "s_t1": "タグに含まれる  (^=開始、終了=$)", + "s_a1": "特定のメタデータプロパティ", + + "md_eshow": "レンダリングできません ", + "md_off": "[📜readme] 無効 [⚙️] -- ドキュメントを非表示", + + "badreply": "サーバーからの応答を解析できませんでした", + + "xhr403": "403: アクセスが拒否されました\n\nF5キーを押してみてください。ログアウトしている可能性があります", + "xhr0": "不明(サーバーへの接続が失われたか、サーバーがオフラインになっている可能性があります)", + "cf_ok": "申し訳ありません -- DD" + wah + "oS 保護が作動し\n\n約30秒で再開されます\n\n何も起こらない場合はF5キーを押してページを再読み込みしてください", + "tl_xe1": "サブフォルダを一覧表示できませんでした:\n\nエラー ", + "tl_xe2": "404: フォルダーが見つかりません", + "fl_xe1": "フォルダ内のファイルを一覧表示できませんでした:\n\nエラー ", + "fl_xe2": "404: フォルダーが見つかりません", + "fd_xe1": "サブフォルダを作成できませんでした:\n\nエラー ", + "fd_xe2": "404: 親フォルダが見つかりません", + "fsm_xe1": "メッセージを送信できませんでした:\n\nエラー ", + "fsm_xe2": "404: 親フォルダが見つかりません", + "fu_xe1": "サーバーから投稿取り消しリストを読み込めませんでした:\n\nエラー ", + "fu_xe2": "404: ファイルが見つかりません??", + + "fz_tar": "非圧縮gnu-tarファイル(Linux / Mac)", + "fz_pax": "非圧縮pax形式のtar(遅い)", + "fz_targz": "gzip圧縮レベル3のgnu-tar$N$Nこれは通常非常に遅いので$N代わりに非圧縮tarを使用してください。", + "fz_tarxz": "xz圧縮レベル1のgnu-tar$N$Nこれは通常非常に遅いので$N代わりに非圧縮のtarを使用してください。", + "fz_zip8": "UTF8ファイル名のzip形式(Windows7以前では動作が不安定になる可能性があります)", + "fz_zipd": "非常に古いソフトウェア用の従来のcp437ファイル名のzip", + "fz_zipc": "cp437はcrc32を早期に計算します$NMS-DOS PKZIP v2.04g用(1993年10月)$N(ダウンロードを開始するまでの処理に時間がかかります)", + + "un_m1": "最近アップロードした動画を削除したり未完成の動画を中止したりできます。", + "un_upd": "更新", + "un_m4": "または以下のファイルを共有する:", + "un_ulist": "表示", + "un_ucopy": "コピー", + "un_flt": "任意のフィルター:  URLには以下を含める必要があります", + "un_fclr": "フィルターをクリア", + "un_derr": '未投稿の削除に失敗しました:\n', + "un_f5": '何かが壊れています。リロードするかF5キーを押してみてください。', + "un_uf5": "申し訳ありませんが、このアップロードを中止するにはページを更新する必要があります(例:F5キーまたはCtrl+Rキーを押す)", + "un_nou": '警告: サーバーが混雑しているため未完了のアップロードを表示できません; しばらくしてから「更新」リンクをクリックしてください', + "un_noc": '警告: 完全にアップロードされたファイルの未投稿化はサーバー設定で有効化 / 許可されていません', + "un_max": "最初の2000件のファイルを表示中 (フィルターを使用)", + "un_avail": "{0}件の最近のアップロードを削除できます
未完了のものは{1}件中止できます", + "un_m2": "アップロード日時順に並べ替え; 新しい順:", + "un_no1": "冗談だよ!アップロードされたものはどれも最新じゃない", + "un_no2": "冗談だよ!そのフィルターに一致するアップロードに最新のものはありません", + "un_next": "次の{0}件のファイルを削除します", + "un_abrt": "中止", + "un_del": "削除", + "un_m3": "最近のアップロードを読み込み中...", + "un_busy": "{0}件のファイルを削除中...", + "un_clip": "{0}件のリンクをクリップボードにコピーしました", + + "u_https1": "あなたはするべき", + "u_https2": "httpsに切り替える", + "u_https3": "パフォーマンス向上のため", + "u_ancient": '現在のブラウザは驚くほど古いです -- 代わりにbupを使った方がいいかもしれません', + "u_nowork": "Firefox 53以降、Chrome 57以降、またはiOS 11以降が必要です", + "tail_2old": "Firefox 105以降、Chrome 71以降、またはiOS 14.5以降が必要です", + "u_nodrop": '現在のブラウザは古すぎてドラッグ&ドロップによるアップロードに対応していません', + "u_notdir": "それはフォルダではありません!\n\nブラウザが古すぎです、\n代わりにドラッグドロップを試してください", + "u_uri": "他のブラウザウィンドウから画像をドラッグアンドドロップするには、\n大きなアップロードボタンにドロップしてください", + "u_enpot": 'potato UIに切り替える (アップロード速度が向上する可能性があります)', + "u_depot": 'fancy UIに切り替える(アップロード速度が低下する可能性があります)', + "u_gotpot": 'アップロード速度を向上させるためにポテトUIに切り替えました。\n\n同意しない場合は遠慮なく元に戻してください!', + "u_pott": "

ファイル:   {0} 完了済み,   {1} 失敗,   {2} ビジー,   {3} キュー内

", + "u_ever": "これは基本的なアップローダーです; up2kは少なくとも以下が必要です
chrome 21 // firefox 13 // edge 12 // opera 12 // safari 5.1", + "u_su2k": 'これは基本的なアップローダーです; up2kの方が優れています', + "u_uput": '速度を最適化(チェックサムをスキップ)', + "u_ewrite": 'このフォルダへの書き込み権限がありません', + "u_eread": 'このフォルダーへの読み取りアクセス権がありません', + "u_enoi": 'サーバー設定でファイル検索が有効になっていません', + "u_enoow": "ここでは上書きは機能しません; 削除権限が必要です", + "u_badf": 'これらの {0}件のファイル(合計 {1}件中)はファイルシステムの権限が原因でスキップされた可能性があります:\n\n', + "u_blankf": 'これらの {0}件のファイル(合計 {1}件中)は空白です; それでもアップロードしますか?\n\n', + "u_applef": 'これらの {0}件のファイル(合計 {1}件中)はおそらく不要です;\n次のファイルをスキップするにはOK/Enterを押してください、\n除外しない場合はキャンセル/ESCを押してそれらもアップロードしてください:\n\n', + "u_just1": '\nファイルを1つだけ選択した方がうまくいくかもしれません', + "u_ff_many": "Linux / MacOS / Android,を使用している場合この量のファイルによりFirefoxがクラッシュする可能性があります\nその場合はもう一度お試しください(または Chrome を使用してください)。", + "u_up_life": "このアップロードは {0}後に\nサーバーから削除されます。", + "u_asku": 'これらの{0}件のファイルを{1}にアップロードします', + "u_unpt": "左上のボタン🧯を使ってこのアップロードを取り消し / 削除できます", + "u_bigtab": '{0}件のファイルを表示しようとしています\n\nブラウザがクラッシュする可能性がありますが、よろしいですか?', + "u_scan": 'ファイルをスキャン中...', + "u_dirstuck": 'ディレクトリ走査が次の {0}件のアイテムへのアクセス中に停止しました; スキップします:', + "u_etadone": '完了 ({0}, {1}件のファイル)', + "u_etaprep": '(アップロード準備中)', + "u_hashdone": 'ハッシュ化完了', + "u_hashing": 'ハッシュ', + "u_hs": 'ハンドシェイク中...', + "u_started": "ファイルをアップロード中です; 参照 [🚀]", + "u_dupdefer": "複製; 他のすべてのファイルの処理完了後に処理されます", + "u_actx": "他のウィンドウ/タブに切り替えるときに
パフォーマンスの低下を防ぐにはこのテキストをクリックしてください", + "u_fixed": "OK!  修正しました 👍", + "u_cuerr": "{0} / {1} のチャンクのアップロードに失敗しました;\nおそらく無害、継続中\n\nファイル: {2}", + "u_cuerr2": "サーバーがアップロードを拒否しました({0} / {1}のチャンク);\n後で再試行します\n\nファイル: {2}\n\nエラー ", + "u_ehstmp": "再試行します; 右下を参照", + "u_ehsfin": "サーバーがアップロードの完了リクエストを拒否しました; 再試行中...", + "u_ehssrch": "サーバーは検索実行リクエストを拒否しました; 再試行中...", + "u_ehsinit": "サーバーはアップロード開始リクエストを拒否しました; 再試行中...", + "u_eneths": "アップロードハンドシェイク中にネットワークエラーが発生しました; 再試行中...", + "u_enethd": "ターゲットの存在をテスト中にネットワークエラーが発生しました; 再試行中...", + "u_cbusy": "ネットワークの不具合後サーバーが再び信頼するのを待っています...", + "u_ehsdf": "サーバーのディスク容量が不足しました!\n\n誰かが十分な空き領域を確保して続行できるようになるまで\n再試行を続けます", + "u_emtleak1": "お使いのウェブブラウザでメモリリークが発生している可能性があります;\nお願いします", + "u_emtleak2": ' httpsに切り替える(推奨)もしくは', + "u_emtleak3": ' ', + "u_emtleakc": '次を試してください:\nアップロードは少し遅くなりますが、仕方ないでしょう。\nご迷惑をおかけして申し訳ありません!\n\n追記: chrome v107ではこの問題が修正されました', + "u_emtleakf": '次を試してください:\n\n追記: firefoxはバグ修正が行われることを期待しています', + "u_s404": "サーバー上にファイルが見つかりません", + "u_expl": "説明", + "u_maxconn": "ほとんどのブラウザではこの数が6に制限されていますが、Firefoxではabout:configconnections-per-serverでこの数を増やすことができます", + "u_tu": '

警告: ターボが有効になっている場合、 クライアントは不完全なアップロードを検出して再開できない可能性があります; ターボボタンのツールチップを見る

', + "u_ts": '

警告: ターボが有効になっている場合、 検索結果が間違っている可能性があります; ターボボタンのツールチップを見る

', + "u_turbo_c": "サーバー設定でターボが無効になっています", + "u_turbo_g": "このボリューム内でディレクトリ一覧表示の権限がないため\nターボを無効化しています", + "u_life_cfg": '分後に自動削除(もしくは時間)', + "u_life_est": 'アップロードは削除されます ---', + "u_life_max": 'このフォルダーの最大保存期間が\n{0}に設定されています。', + "u_unp_ok": '{0}に対して未投稿が許可されています', + "u_unp_ng": '未投稿は許可されません', + "ue_ro": 'このフォルダのアクセス権限は読み取り専用です\n\n', + "ue_nl": '現在ログインしていません', + "ue_la": '現在"{0}"としてログインしています', + "ue_sr": '現在ファイル検索モードです\n\n虫眼鏡 🔎(大きな検索ボタンの横)をクリックしてアップロードモードに切り替え、もう一度アップロードしてみてください。\n\n申し訳ありません', + "ue_ta": 'もう一度アップロードしてみてください。今度は動作するはずです。', + "ue_ab": "このファイルはすでに別のフォルダにアップロード中です。それが完了するまでこのファイルを他の場所にアップロードすることはできません。\n\n左上の🧯を使用して最初のアップロードを中止して忘れることができます。", + "ur_1uo": "OK: ファイルのアップロードに成功しました", + "ur_auo": "OK: {0}件すべてのファイルが正常にアップロードされました", + "ur_1so": "OK: ファイルがサーバー上で見つかりました", + "ur_aso": "OK: {0}件すべてのファイルがサーバー上で見つかりました", + "ur_1un": "アップロードに失敗しました。申し訳ありません。", + "ur_aun": "{0}件すべてのファイルのアップロードに失敗しました。申し訳ありません。", + "ur_1sn": "サーバー上にファイルが見つかりません", + "ur_asn": "サーバー上に {0}件のファイルが見つかりませんでした", + "ur_um": "完了;\n{0}件のアップロードはOKです,\n{1}件のアップロードに失敗しました。申し訳ありません。", + "ur_sm": "完了;\n{0}件のファイルがサーバー上に見つかりました,\n{1}件のファイルがサーバー上に見つかりませんでした", + + "rc_opn": "開く", + "rc_ply": "再生", + "rc_pla": "オーディオとして再生", + "rc_txt": "ファイルビューアで開く", + "rc_md": "markdownエディターで開く", + "rc_dl": "ダウンロード", + "rc_zip": "圧縮してダウンロード", + "rc_cpl": "リンクをコピー", + "rc_del": "削除", + "rc_cut": "切り取り", + "rc_cpy": "コピー", + "rc_pst": "貼り付け", + "rc_nfo": "新しいフォルダ", + "rc_nfi": "新しいファイル", + "rc_sal": "すべて選択", + "rc_sin": "選択を反転", + + "lang_set": "変更を反映させるために更新しますか?", + + "splash": { + "a1": "更新", + "b1": "やあ、見知らぬ人   (あなたはログインしていません)", + "c1": "ログアウト", + "d1": "dump stack", // トピックノート: "d2" はこのボタンのツールチップです + "d2": "すべてのアクティブなスレッドの状態を表示します", + "e1": "cfgの再読み込み", + "e2": "設定ファイル(アカウント/ボリューム/ボリュームフラグ)を再読み込みして、$Nすべてのe2dsボリュームを再スキャンします$N$N注: グローバル設定の変更を$N有効にするには完全な再起動が必要です", + "f1": "閲覧可能:", + "g1": "アップロード加納:", + "cc1": "その他:", + "h1": "k304を無効化", // トピックノート: "j1" はk304が何であるかを説明します + "i1": "k304を有効化", + "j1": "k304 を有効にすると、HTTP 304ごとにクライアントが切断されバグのあるプロキシがスタックするのを防ぐことができます(突然ページが読み込まれなくなるなど)ただし全体的に速度が低下します", + "k1": "クライアント設定をリセット", + "l1": "詳しくはログインしてください:", + "ls3": "ログイン", + "lu4": "ユーザー名", + "lp4": "パスワード", + "lo3": "「{0}」をどこからでもログアウトする", + "lo2": "これによりすべてのブラウザでセッションが終了します", + "m1": "おかえりなさい、", // トピックノート: "おかえりなさい、ユーザー名" + "n1": "404 not found  ┐( ´ -`)┌", + "o1": 'あるいはアクセスできないかもしれません -- パスワードを試すか ホームに戻ってください', + "p1": "403 forbiddena  ~┻━┻", + "q1": 'パスワードを使うか ホームに戻ってください', + "r1": "ホームに戻る", + ".s1": "再スキャン", + "t1": "アクション", // トピックノート: これは"再スキャン"ボタンの上のヘッダーです + "u2": "最後のサーバー書き込みからの時間$N(アップロード / 名前変更 / ...)$N$N17d = 17日$N1h23 = 1時間23分$N4m56 = 4分56秒", + "v1": "接続", + "v2": "このサーバーをローカルHDDとして使用する", + "w1": "httpsへ切り替え", + "x1": "パスワードの変更", + "y1": "共有の編集", // トピックノート: ユーザーが共有することを決定したフォルダのリストを表示します + "z1": "この共有のロックを解除する:", // トピックノート: 隠し共有を表示するためのパスワードプロンプト + "ta1": "最初に新しいパスワードを入力してください", + "ta2": "確認のため新しいパスワードを再入力してください:", + "ta3": "タイプミスを発見しました; もう一度試してください", + "nop": "エラー: パスワードは空白にできません", + "nou": "エラー: ユーザー名とパスワードは空白にできません", + "aa1": "受信ファイル:", + "ab1": "no304を無効化", + "ac1": "no304を有効化", + "ad1": "no304を有効にするとすべてのキャッシュが無効になります; k304 では不十分な場合はこちらをお試しください。これにより膨大な量のネットワークトラフィックが無駄になります!", + "ae1": "アクティブなダウンロード:", + "af1": "最近のアップロードを表示", + "ag1": "idpキャッシュを表示", // トピックノート: IdPユーザーを管理できるページへのリンクです + } +}; diff --git a/copyparty/web/tl/kor.js b/copyparty/web/tl/kor.js index cf23b915..cd9729ab 100644 --- a/copyparty/web/tl/kor.js +++ b/copyparty/web/tl/kor.js @@ -647,6 +647,7 @@ Ls.kor = { "rc_md": "텍스트 편집기에서 열기", //m "rc_dl": "다운로드", //m "rc_zip": "압축 파일로 다운로드", //m + "rc_cpl": "링크 복사", //m "rc_del": "삭제", //m "rc_cut": "잘라내기", //m "rc_cpy": "복사", //m diff --git a/copyparty/web/tl/nld.js b/copyparty/web/tl/nld.js index 69381214..bcb24b54 100644 --- a/copyparty/web/tl/nld.js +++ b/copyparty/web/tl/nld.js @@ -647,6 +647,7 @@ Ls.nld = { "rc_md": "Openen in teksteditor", //m "rc_dl": "Downloaden", //m "rc_zip": "Downloaden als archief", //m + "rc_cpl": "Link kopiëren", //m "rc_del": "Verwijderen", //m "rc_cut": "Knippen", //m "rc_cpy": "Kopiëren", //m diff --git a/copyparty/web/tl/nno.js b/copyparty/web/tl/nno.js index 7d99ddfa..997bc6cb 100644 --- a/copyparty/web/tl/nno.js +++ b/copyparty/web/tl/nno.js @@ -644,6 +644,7 @@ Ls.nno = { "rc_md": "opne i tekstredigerar", "rc_dl": "Last ned", "rc_zip": "Last ned som arkiv", + "rc_cpl": "kopier lenke", "rc_del": "slett", "rc_cut": "klipp ut", "rc_cpy": "kopier", diff --git a/copyparty/web/tl/nor.js b/copyparty/web/tl/nor.js index 8e357726..1bbdccae 100644 --- a/copyparty/web/tl/nor.js +++ b/copyparty/web/tl/nor.js @@ -644,6 +644,7 @@ Ls.nor = { "rc_md": "åpne i teksteditor", "rc_dl": "Last ned", "rc_zip": "Last ned som arkiv", + "rc_cpl": "kopier lenke", "rc_del": "slett", "rc_cut": "klipp ut", "rc_cpy": "kopier", diff --git a/copyparty/web/tl/pol.js b/copyparty/web/tl/pol.js index 397990f9..09cc5a22 100644 --- a/copyparty/web/tl/pol.js +++ b/copyparty/web/tl/pol.js @@ -650,6 +650,7 @@ Ls.pol = { "rc_md": "otwórz w edytorze tekstu", //m "rc_dl": "pobierz", //m "rc_zip": "pobierz jako archiwum", //m + "rc_cpl": "kopiuj link", //m "rc_del": "usuń", //m "rc_cut": "wytnij", //m "rc_cpy": "kopiuj", //m diff --git a/copyparty/web/tl/por.js b/copyparty/web/tl/por.js index 0ba3ee6c..2e40a504 100644 --- a/copyparty/web/tl/por.js +++ b/copyparty/web/tl/por.js @@ -647,6 +647,7 @@ Ls.por = { "rc_md": "abrir no editor de texto", //m "rc_dl": "baixar", //m "rc_zip": "baixar como arquivo", //m + "rc_cpl": "copiar link", //m "rc_del": "excluir", //m "rc_cut": "recortar", //m "rc_cpy": "copiar", //m diff --git a/copyparty/web/tl/rus.js b/copyparty/web/tl/rus.js index 5a7bcc65..b44a27e8 100644 --- a/copyparty/web/tl/rus.js +++ b/copyparty/web/tl/rus.js @@ -647,6 +647,7 @@ Ls.rus = { "rc_md": "открыть в текстовом редакторе", //m "rc_dl": "скачать", //m "rc_zip": "скачать как архив", //m + "rc_cpl": "копировать ссылку", //m "rc_del": "удалить", //m "rc_cut": "вырезать", //m "rc_cpy": "копировать", //m diff --git a/copyparty/web/tl/spa.js b/copyparty/web/tl/spa.js index d6303cc9..2ed5db8f 100644 --- a/copyparty/web/tl/spa.js +++ b/copyparty/web/tl/spa.js @@ -646,6 +646,7 @@ Ls.spa = { "rc_md": "abrir en el editor de texto", //m "rc_dl": "descargar", //m "rc_zip": "descargar como archivo", //m + "rc_cpl": "copiar enlace", //m "rc_del": "eliminar", //m "rc_cut": "cortar", //m "rc_cpy": "copiar", //m diff --git a/copyparty/web/tl/swe.js b/copyparty/web/tl/swe.js index 599fd5d7..e7f4d7c8 100644 --- a/copyparty/web/tl/swe.js +++ b/copyparty/web/tl/swe.js @@ -647,6 +647,7 @@ Ls.swe = { "rc_md": "öppna i textredigerare", //m "rc_dl": "Ladda ner", //m "rc_zip": "Ladda ner som arkiv", //m + "rc_cpl": "kopiera länk", //m "rc_del": "radera", //m "rc_cut": "klipp ut", //m "rc_cpy": "kopiera", //m diff --git a/copyparty/web/tl/tur.js b/copyparty/web/tl/tur.js index fe0cf435..3a31816d 100644 --- a/copyparty/web/tl/tur.js +++ b/copyparty/web/tl/tur.js @@ -647,6 +647,7 @@ Ls.tur = { "rc_md": "metin düzenleyicide aç", //m "rc_dl": "i̇ndir", //m "rc_zip": "arşiv olarak indir", //m + "rc_cpl": "bağlantıyı kopyala", //m "rc_del": "sil", //m "rc_cut": "kes", //m "rc_cpy": "kopyala", //m diff --git a/copyparty/web/tl/ukr.js b/copyparty/web/tl/ukr.js index 1b1b7bd2..187d703f 100644 --- a/copyparty/web/tl/ukr.js +++ b/copyparty/web/tl/ukr.js @@ -647,6 +647,7 @@ Ls.ukr = { "rc_md": "відкрити в текстовому редакторі", //m "rc_dl": "завантажити", //m "rc_zip": "завантажити як архів", //m + "rc_cpl": "копіювати посилання", //m "rc_del": "видалити", //m "rc_cut": "вирізати", //m "rc_cpy": "копіювати", //m diff --git a/copyparty/web/tl/vie.js b/copyparty/web/tl/vie.js index 948f14bb..b2becc29 100644 --- a/copyparty/web/tl/vie.js +++ b/copyparty/web/tl/vie.js @@ -691,6 +691,7 @@ Ls.vie = { "rc_md": "mở trong trình soạn thảo văn bản", //m "rc_dl": "tải xuống", //m "rc_zip": "tải xuống dưới dạng gói nén", //m + "rc_cpl": "sao chép liên kết", //m "rc_del": "xóa", //m "rc_cut": "cắt", //m "rc_cpy": "sao chép", //m diff --git a/docs/changelog.md b/docs/changelog.md index 8a9beb6c..ac067cf0 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,3 +1,57 @@ +▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ +# 2026-0102-0007 `v1.20.0` sftp is fine too + +## 🧪 new features + +* sftp server 4714c2fa ec7ea309 + * included in [docker-images](https://hub.docker.com/u/copyparty) `im`, `iv`, `ac`, `dj` + * not using docker? install the optional dependency [paramiko](https://pypi.org/project/paramiko/) +* #1135 right-click menu (thx @stackxp and @Scotsguy!) 82c49609 05a44720 +* PUT can now append to existing files 63d8e5a0 + * new option [apnd-who](https://copyparty.eu/cli/#g-apnd-who) to configure who is allowed to do that +* #1128 added option to skip uploading a file if the filename is already taken on the server (thx @Scotsguy!) fa32e159 +* #1127 descript.ion now also works for folders 2c26aecd +* in file listings, `up_by` and `up_ip` (uploader info) can now be displayed for non-admin users by adding them to the [mte](https://copyparty.eu/cli/#g-mte) option 7bfd370b +* #1120 display the disk-space quota instead of the underlying HDD size (thx @rabid-dev!) 511dc016 e0845b23 +* option to skip dotfiles/dotfolders when using download-as-zip 7d7a1510 +* #1124 button to skip files with a filename collision when copying/moving files 85639ad2 +* #1151 u2c ([commandline uploader](https://github.com/9001/copyparty/tree/hovudstraum/bin#u2cpy)): option to use basic-auth instead of the `PW` header (thx @Le0Developer!) 120fdfb2 +* the name of the `pw` url-param and http-header can be changed f81d80bc + * mainly to force basic-auth, but perhaps also for other purposes + * changing these will break support for many clients, so you probably want to keep the default + +## 🩹 bugfixes + +* image-viewer: + * images now scale properly when rotated while the zoom feature is enabled c0e167fd + * the current image rotation will now be applied to the next image as well 485c60cf c82a3cb2 +* groups announced by an IdP will now also apply for native (copyparty-config) users f08cb25c +* windows: download-as-zip would flatten everything to a single folder 2d1d295a +* #1157 [dirkeys](https://github.com/9001/copyparty/#dirkeys) did not work in grid-view d1ddcb19 +* #1123 the [ui-notree](https://copyparty.eu/cli/#g-ui-notree) option to simplify the UI would simplify a bit too much 4c73704c +* don't add the trailing slash to a volume in the controlpanel when the volume is a file 80a37492 + * the link couldn't be clicked on Windows CE 4.20 using Internet Explorer 4.01 + +## 🔧 other changes + +* #1158 updated german translation (thx @Scotsguy!) 3bf80c81 +* the `dotfiles` button now also toggles showing [unlisted](https://copyparty.eu/cli/#g-unlist) files e55e5a45 +* `/?h&ls` (the api to list volumes) now includes the user's permissions for each volume 1f6e8116 +* #1142 new option [dav-port](https://copyparty.eu/cli/#g-dav-port) to open a dedicated port for webdav clients 4642d323 + * workaround for certain clients which pretend to be webbrowsers +* #1147 workaround for a buggy browser-extension 8551472b +* detect (and panic) when a webbrowser has failed to load one of the javascript files af3f777e + * replaces the confusing errormessage resulting from half of the code missing + +## 🌠 fun facts + +* it [turns out](https://github.com/9001/copyparty/#server-hall-of-fame) that copyparty runs just fine on [SGI IRIX](https://en.wikipedia.org/wiki/IRIX)! 39c3ccc2 + * there's a [photo of the server](https://a.ocv.me/pub/g/nerd-stuff/cpp/servers/sgi-o2.jpg?cache) and a [screenshot](https://a.ocv.me/pub/g/nerd-stuff/cpp/servers/sgi-o2.png?cache) as proof 😁 + * the [feature comparison](https://github.com/9001/copyparty/blob/hovudstraum/docs/versus.md#general) has been updated accordingly +* this release was mostly coded at 39c3 (see photo above) and the release was [made at revspace](https://a.ocv.me/pub/g/2026/01/PXL_20260102_235328552.jpg) + + + ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ # 2025-1217-0014 `v1.19.23` bad apple x2 diff --git a/scripts/pyinstaller/deps.sha512 b/scripts/pyinstaller/deps.sha512 index 3b49e889..b1eb51b3 100644 --- a/scripts/pyinstaller/deps.sha512 +++ b/scripts/pyinstaller/deps.sha512 @@ -28,7 +28,7 @@ f4b4e330995ebe96c0bd06e16e5b26062ece9473f06d369775aa68eab261dedcf32dfdd159acaa22 00731cfdd9d5c12efef04a7161c90c1e5ed1dc4677aa88a1d4054aff836f3430df4da5262ed4289c21637358a9e10e5df16f76743cbf5a29bb3a44b146c19cf3 MarkupSafe-3.0.2-cp313-cp313-win_amd64.whl 8a6e2b13a2ec4ef914a5d62aad3db6464d45e525a82e07f6051ed10474eae959069e165dba011aefb8207cdfd55391d73d6f06362c7eb247b08763106709526e mutagen-1.47.0-py3-none-any.whl a726fb46cce24f781fc8b55a3e6dea0a884ebc3b2b400ea74aa02333699f4955a5dc1e2ec5927ac72f35a624401f3f3b442882ba1cc4cadaf9c88558b5b8bdae packaging-25.0-py3-none-any.whl -fa5d24c51e39760fc5121e56e9948384e03f62b66907ba313a6a803dd601832df62fb5066f3019620664d7cc6b0482e13000cd2d3d1553b709a56a347919565e pillow-12.0.0-cp313-cp313-win_amd64.whl +c26171ef5f108553209f937e6839311dc6b232033564aa3aca1c623fea7342069d3e859189de9c5ed41ac62d618725cad4ce61094a69aa1311beaef375d43022 pillow-12.1.0-cp313-cp313-win_amd64.whl b9b98714dfca6fa80b0b3f222965724d63be9c54d19435d1fe768e07016913d6db8d6e043fcb185b55a9bd6fe370a80cf961814fc096046a5f4640d99ed575ef pyinstaller-6.15.0-py3-none-win_amd64.whl cad0f7cf39de691813b1d4abc7d33f8bda99a87d9c5886039b814752e8690364150da26fb61b3e28d5698ff57a90e6dcd619ed2b64b04f72b5aadb75e201bdb0 pyinstaller_hooks_contrib-2025.8-py3-none-any.whl 1735728ae50e003badc5266638e41a73358f2151405e7888b6dc45697c074a60e6e58c8507b49a3f42d8f4fe4005fbc225cd766ab6582cbf85aa79bab699c08f python-3.13.11-amd64.exe diff --git a/scripts/pyinstaller/notes.txt b/scripts/pyinstaller/notes.txt index e6ae3cd9..e4ee20d3 100644 --- a/scripts/pyinstaller/notes.txt +++ b/scripts/pyinstaller/notes.txt @@ -39,7 +39,7 @@ fns=( MarkupSafe-3.0.2-cp313-cp313-win_amd64.whl mutagen-1.47.0-py3-none-any.whl packaging-25.0-py3-none-any.whl - pillow-12.0.0-cp313-cp313-win_amd64.whl + pillow-12.1.0-cp313-cp313-win_amd64.whl pyinstaller-6.15.0-py3-none-win_amd64.whl pyinstaller_hooks_contrib-2025.8-py3-none-any.whl python-3.13.11-amd64.exe diff --git a/scripts/sfx.ls b/scripts/sfx.ls index 28158d94..0b8f14f2 100644 --- a/scripts/sfx.ls +++ b/scripts/sfx.ls @@ -122,6 +122,7 @@ copyparty/web/tl/fin.js, copyparty/web/tl/fra.js, copyparty/web/tl/grc.js, copyparty/web/tl/ita.js, +copyparty/web/tl/jpn.js, copyparty/web/tl/kor.js, copyparty/web/tl/nld.js, copyparty/web/tl/nno.js, diff --git a/scripts/tl.js b/scripts/tl.js index 4b51e975..5d732ff8 100644 --- a/scripts/tl.js +++ b/scripts/tl.js @@ -669,21 +669,22 @@ Ls.hmn = { "ur_um": "Finished;\n{0} uploads OK,\n{1} uploads failed, sorry", "ur_sm": "Finished;\n{0} files found on server,\n{1} files NOT found on server", - "rc_opn": "Open", - "rc_ply": "Play", - "rc_pla": "Play as audio", - "rc_txt": "Open in file viewer", - "rc_md": "Open in markdown editor", - "rc_dl": "Download", - "rc_zip": "Download as archive", - "rc_del": "Delete", - "rc_cut": "Cut", - "rc_cpy": "Copy", - "rc_pst": "Paste", - "rc_nfo": "New folder", - "rc_nfi": "New file", - "rc_sal": "Select all", - "rc_sin": "Invert selection", + "rc_opn": "open", + "rc_ply": "play", + "rc_pla": "play as audio", + "rc_txt": "open in textfile viewer", + "rc_md": "open in text editor", + "rc_dl": "download", + "rc_zip": "download as archive", + "rc_cpl": "copy link", + "rc_del": "delete", + "rc_cut": "cut", + "rc_cpy": "copy", + "rc_pst": "paste", + "rc_nfo": "new folder", + "rc_nfi": "new file", + "rc_sal": "select all", + "rc_sin": "invert selection", "lang_set": "refresh to make the change take effect?", @@ -703,6 +704,11 @@ Ls.hmn = { "j1": "enabling k304 will disconnect your client on every HTTP 304, which can prevent some buggy proxies from getting stuck (suddenly not loading pages), but it will also make things slower in general", "k1": "reset client settings", "l1": "login for more:", + "ls3": "login", + "lu4": "username", + "lp4": "password", + "lo3": "logout “{0}” everywhere", + "lo2": "ends the session on all browsers", "m1": "welcome back,", // TLNote: "welcome back, USERNAME" "n1": "404 not found  ┐( ´ -`)┌", "o1": 'or maybe you don\'t have access -- try a password or go home', diff --git a/scripts/tl.py b/scripts/tl.py index 39893445..407ce495 100755 --- a/scripts/tl.py +++ b/scripts/tl.py @@ -87,6 +87,11 @@ Ls.{lang3} = {{ "j1": "enabling k304 will disconnect your client on every HTTP 304, which can prevent some buggy proxies from getting stuck (suddenly not loading pages), but it will also make things slower in general", "k1": "reset client settings", "l1": "login for more:", + "ls3": "login", + "lu4": "username", + "lp4": "password", + "lo3": "logout “{{0}}” everywhere", + "lo2": "ends the session on all browsers", "m1": "welcome back,", // TLNote: "welcome back, USERNAME" "n1": "404 not found  ┐( ´ -`)┌", "o1": 'or maybe you don\\'t have access -- try a password or go home',