mirror of
https://github.com/9001/copyparty.git
synced 2026-01-12 07:44:08 -07:00
Compare commits
114 commits
v1.19.21
...
hovudstrau
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9d223d6ca7 | ||
|
|
caf831fc28 | ||
|
|
ae56f3bdae | ||
|
|
25a8b96fd1 | ||
|
|
7357d46f43 | ||
|
|
3aebfabd5c | ||
|
|
8914f0af70 | ||
|
|
a9ae6d5131 | ||
|
|
ecd4fcc1b3 | ||
|
|
738a419b2b | ||
|
|
3a16d3461d | ||
|
|
3a52096205 | ||
|
|
87a5c22a34 | ||
|
|
7d6e59f347 | ||
|
|
b918b592ca | ||
|
|
cbeb439ad2 | ||
|
|
74a6ce1c1e | ||
|
|
2f4a30b620 | ||
|
|
6c41bac6d2 | ||
|
|
8c9e1016de | ||
|
|
9030828494 | ||
|
|
13055c6451 | ||
|
|
038af50777 | ||
|
|
7ffb1fd0ca | ||
|
|
364f74a1bd | ||
|
|
feabbf3e6a | ||
|
|
5b2c84752d | ||
|
|
cce1210792 | ||
|
|
e55e5a4585 | ||
|
|
af3f777ec9 | ||
|
|
80a3749238 | ||
|
|
2f3591d036 | ||
|
|
65391a9d8c | ||
|
|
fd141c61b5 | ||
|
|
3ee91dffad | ||
|
|
ec7ea30951 | ||
|
|
4e9cf95e8d | ||
|
|
3bf80c8152 | ||
|
|
5b89a2e3b2 | ||
|
|
f81d80bcad | ||
|
|
f08cb25ccc | ||
|
|
7d7a1510fb | ||
|
|
63d8e5a033 | ||
|
|
ec51d3241c | ||
|
|
85639ad2cd | ||
|
|
05a4472075 | ||
|
|
82c496092f | ||
|
|
8551472bf0 | ||
|
|
d1ddcb19f5 | ||
|
|
39c3ccc2eb | ||
|
|
4714c2fa5a | ||
|
|
120fdfb257 | ||
|
|
4642d32366 | ||
|
|
2c26aecd87 | ||
|
|
d8c732469b | ||
|
|
4c73704ce7 | ||
|
|
e0845b2363 | ||
|
|
511dc01615 | ||
|
|
fa32e15958 | ||
|
|
2d1d295a4d | ||
|
|
c82a3cb226 | ||
|
|
485c60cf25 | ||
|
|
c0e167fd97 | ||
|
|
7bfd370b6c | ||
|
|
1f6e811674 | ||
|
|
0e6b167167 | ||
|
|
519bfe1f0b | ||
|
|
d006cecaef | ||
|
|
b6c2ec15db | ||
|
|
0b6d2d2424 | ||
|
|
8d46cf1823 | ||
|
|
c8f3b4ef05 | ||
|
|
9c64788d43 | ||
|
|
5e1d9a58d8 | ||
|
|
336842192c | ||
|
|
d12cf9aee1 | ||
|
|
db38cb2f79 | ||
|
|
d4a9787c6c | ||
|
|
b60eb3f01a | ||
|
|
3476d5e6d9 | ||
|
|
a3eec23cef | ||
|
|
2e7390a4c5 | ||
|
|
e0b04d9c16 | ||
|
|
9e64fe02f9 | ||
|
|
67ddc64171 | ||
|
|
56e15009c7 | ||
|
|
08474dbe14 | ||
|
|
5a1f0a330c | ||
|
|
3bc0bf19b0 | ||
|
|
efc6a09dd3 | ||
|
|
921954037b | ||
|
|
fecc3fd507 | ||
|
|
5e85e3d628 | ||
|
|
965a4a6949 | ||
|
|
7f82189da9 | ||
|
|
14bef85b87 | ||
|
|
594ec39481 | ||
|
|
ba017f7b53 | ||
|
|
3bbed1bc46 | ||
|
|
4b0064b209 | ||
|
|
e440578cae | ||
|
|
1a9d4c04d5 | ||
|
|
8e2fb05ab8 | ||
|
|
ca6c4deaac | ||
|
|
a1cbac0252 | ||
|
|
1b222fb576 | ||
|
|
ce2eeba226 | ||
|
|
ad45de9441 | ||
|
|
7d526eaba3 | ||
|
|
1b0eb45032 | ||
|
|
a86983928c | ||
|
|
c5c5f9b4b8 | ||
|
|
fa918228d5 | ||
|
|
29925dc22b |
94
README.md
94
README.md
|
|
@ -5,7 +5,7 @@
|
|||
turn almost any device into a file server with resumable uploads/downloads using [*any*](#browser-support) web browser
|
||||
|
||||
* server only needs Python (2 or 3), all dependencies optional
|
||||
* 🔌 protocols: [http](#the-browser) // [webdav](#webdav-server) // [ftp](#ftp-server) // [tftp](#tftp-server) // [smb/cifs](#smb-server)
|
||||
* 🔌 protocols: [http(s)](#the-browser) // [webdav](#webdav-server) // [sftp](#sftp-server) // [ftp(s)](#ftp-server) // [tftp](#tftp-server) // [smb/cifs](#smb-server)
|
||||
* 📱 [android app](#android-app) // [iPhone shortcuts](#ios-shortcuts)
|
||||
|
||||
👉 **[Get started](#quickstart)!** or visit the **[read-only demo server](https://a.ocv.me/pub/demo/)** 👀 running on a nuc in my basement
|
||||
|
|
@ -14,7 +14,7 @@ turn almost any device into a file server with resumable uploads/downloads using
|
|||
|
||||
🎬 **videos:** [upload](https://a.ocv.me/pub/demo/pics-vids/up2k.webm) // [cli-upload](https://a.ocv.me/pub/demo/pics-vids/u2cli.webm) // [race-the-beam](https://a.ocv.me/pub/g/nerd-stuff/cpp/2024-0418-race-the-beam.webm) // 👉 **[feature-showcase](https://a.ocv.me/pub/demo/showcase-hq.webm)** ([youtube](https://www.youtube.com/watch?v=15_-hgsX2V0))
|
||||
|
||||
made in Norway 🇳🇴
|
||||
built in Norway 🇳🇴 with contributions from [not-norway](https://github.com/9001/copyparty/graphs/contributors)
|
||||
|
||||
|
||||
## readme toc
|
||||
|
|
@ -69,6 +69,7 @@ made in Norway 🇳🇴
|
|||
* [ssdp](#ssdp) - windows-explorer announcer
|
||||
* [qr-code](#qr-code) - print a qr-code [(screenshot)](https://user-images.githubusercontent.com/241032/194728533-6f00849b-c6ac-43c6-9359-83e454d11e00.png) for quick access
|
||||
* [ftp server](#ftp-server) - an FTP server can be started using `--ftp 3921`
|
||||
* [sftp server](#sftp-server) - goes roughly 700 MiB/s (slower than webdav and ftp)
|
||||
* [webdav server](#webdav-server) - with read-write support
|
||||
* [connecting to webdav from windows](#connecting-to-webdav-from-windows) - using the GUI
|
||||
* [tftp server](#tftp-server) - a TFTP server (read/write) can be started using `--tftp 3969`
|
||||
|
|
@ -126,7 +127,7 @@ made in Norway 🇳🇴
|
|||
* [iOS shortcuts](#iOS-shortcuts) - there is no iPhone app, but
|
||||
* [performance](#performance) - defaults are usually fine - expect `8 GiB/s` download, `1 GiB/s` upload
|
||||
* [client-side](#client-side) - when uploading files
|
||||
* [security](#security) - there is a [discord server](https://discord.gg/25J8CdTT6G)
|
||||
* [security](#security) - there is a [discord server](https://discord.gg/25J8CdTT6G) with announcements
|
||||
* [gotchas](#gotchas) - behavior that might be unexpected
|
||||
* [cors](#cors) - cross-site request config
|
||||
* [filekeys](#filekeys) - prevent filename bruteforcing
|
||||
|
|
@ -138,7 +139,7 @@ made in Norway 🇳🇴
|
|||
* [firefox wsod](#firefox-wsod) - firefox 87 can crash during uploads
|
||||
* [HTTP API](#HTTP-API) - see [devnotes](./docs/devnotes.md#http-api)
|
||||
* [dependencies](#dependencies) - mandatory deps
|
||||
* [optional dependencies](#optional-dependencies) - install these to enable bonus features
|
||||
* [optional dependencies](#optional-dependencies) - enable bonus features
|
||||
* [dependency chickenbits](#dependency-chickenbits) - prevent loading an optional dependency
|
||||
* [dependency unvendoring](#dependency-unvendoring) - force use of system modules
|
||||
* [optional gpl stuff](#optional-gpl-stuff)
|
||||
|
|
@ -194,7 +195,7 @@ some recommended options:
|
|||
* `-e2ts` enables audio metadata indexing (needs either FFprobe or Mutagen)
|
||||
* `-v /mnt/music:/music:r:rw,foo -a foo:bar` shares `/mnt/music` as `/music`, `r`eadable by anyone, and read-write for user `foo`, password `bar`
|
||||
* replace `:r:rw,foo` with `:r,foo` to only make the folder readable by `foo` and nobody else
|
||||
* see [accounts and volumes](#accounts-and-volumes) (or `--help-accounts`) for the syntax and other permissions
|
||||
* see [accounts and volumes](#accounts-and-volumes) (or [`--help-accounts`](https://copyparty.eu/cli/#accounts-help-page)) for the syntax and other permissions
|
||||
|
||||
|
||||
### mirrors
|
||||
|
|
@ -236,12 +237,12 @@ you may also want these, especially on servers:
|
|||
|
||||
and remember to open the ports you want; here's a complete example including every feature copyparty has to offer:
|
||||
```
|
||||
firewall-cmd --permanent --add-port={80,443,3921,3923,3945,3990}/tcp # --zone=libvirt
|
||||
firewall-cmd --permanent --add-port={80,443,3921,3922,3923,3945,3990}/tcp # --zone=libvirt
|
||||
firewall-cmd --permanent --add-port=12000-12099/tcp # --zone=libvirt
|
||||
firewall-cmd --permanent --add-port={69,1900,3969,5353}/udp # --zone=libvirt
|
||||
firewall-cmd --reload
|
||||
```
|
||||
(69:tftp, 1900:ssdp, 3921:ftp, 3923:http/https, 3945:smb, 3969:tftp, 3990:ftps, 5353:mdns, 12000:passive-ftp)
|
||||
(69:tftp, 1900:ssdp, 3921:ftp, 3922:sftp, 3923:http/https, 3945:smb, 3969:tftp, 3990:ftps, 5353:mdns, 12000:passive-ftp)
|
||||
|
||||
|
||||
## features
|
||||
|
|
@ -261,7 +262,7 @@ also see [comparison to similar software](./docs/versus.md)
|
|||
* ☑ [upnp / zeroconf / mdns / ssdp](#zeroconf)
|
||||
* ☑ [event hooks](#event-hooks) / script runner
|
||||
* ☑ [reverse-proxy support](https://github.com/9001/copyparty#reverse-proxy)
|
||||
* ☑ cross-platform (Windows, Linux, Macos, Android, iOS, FreeBSD, arm32/arm64, ppc64le, s390x, risc-v/riscv64)
|
||||
* ☑ cross-platform (Windows, Linux, Macos, Android, iOS, FreeBSD, arm32/arm64, ppc64le, s390x, risc-v/riscv64, SGI IRIX)
|
||||
* upload
|
||||
* ☑ basic: plain multipart, ie6 support
|
||||
* ☑ [up2k](#uploading): js, resumable, multithreaded
|
||||
|
|
@ -451,6 +452,12 @@ upgrade notes
|
|||
* CopyParty?
|
||||
* nope! the name is either copyparty (all-lowercase) or Copyparty -- it's [one word](https://en.wiktionary.org/wiki/copyparty) after all :>
|
||||
|
||||
* what is a volflag?
|
||||
* per-volume configuration; many (not all) global-options can be set as volflags, and most (not all) volflags can be set as global-options; [complete list of volflags](https://copyparty.eu/cli/#flags-help-page)
|
||||
|
||||
* what is a volume?
|
||||
* a mapping from a URL (`/music/`) to a folder on your server's local filesystem (`C:\Users\ed\Music`) which can then be accessed through copyparty, depending on the permissions and options you set on it -- see [accounts and volumes](#accounts-and-volumes)
|
||||
|
||||
* can I change the 🌲 spinning pine-tree loading animation?
|
||||
* [yeah...](https://github.com/9001/copyparty/tree/hovudstraum/docs/rice#boring-loader-spinner) :-(
|
||||
|
||||
|
|
@ -461,6 +468,7 @@ upgrade notes
|
|||
* can I link someone to a password-protected volume/file by including the password in the URL?
|
||||
* yes, by adding `?pw=hunter2` to the end; replace `?` with `&` if there are parameters in the URL already, meaning it contains a `?` near the end
|
||||
* if you have enabled `--usernames` then do `?pw=username:password` instead
|
||||
* `?pw` can be disabled with `--pw-urlp=A` but this breaks support for many clients
|
||||
|
||||
* how do I stop `.hist` folders from appearing everywhere on my HDD?
|
||||
* by default, a `.hist` folder is created inside each volume for the filesystem index, thumbnails, audio transcodes, and markdown document history. Use the `--hist` global-option or the `hist` volflag to move it somewhere else; see [database location](#database-location)
|
||||
|
|
@ -498,7 +506,7 @@ per-folder, per-user permissions - if your setup is getting complex, consider m
|
|||
* much easier to manage, and you can modify the config at runtime with `systemctl reload copyparty` or more conveniently using the `[reload cfg]` button in the control-panel (if the user has `a`/admin in any volume)
|
||||
* changes to the `[global]` config section requires a restart to take effect
|
||||
|
||||
a quick summary can be seen using `--help-accounts`
|
||||
a quick summary can be seen using [`--help-accounts`](https://copyparty.eu/cli/#accounts-help-page)
|
||||
|
||||
configuring accounts/volumes with arguments:
|
||||
* `-a usr:pwd` adds account `usr` with password `pwd`
|
||||
|
|
@ -806,6 +814,7 @@ you can also zip a selection of files or folders by clicking them in the browser
|
|||
|
||||
cool trick: download a folder by appending url-params `?tar&opus` or `?tar&mp3` to transcode all audio files (except aac|m4a|mp3|ogg|opus|wma) to opus/mp3 before they're added to the archive
|
||||
* super useful if you're 5 minutes away from takeoff and realize you don't have any music on your phone but your server only has flac files and downloading those will burn through all your data + there wouldn't be enough time anyways
|
||||
* and url-param `&nodot` skips dotfiles/dotfolders; they are included by default if your account has permission to see them
|
||||
* and url-params `&j` / `&w` produce jpeg/webm thumbnails/spectrograms instead of the original audio/video/images (`&p` for audio waveforms)
|
||||
* can also be used to pregenerate thumbnails; combine with `--th-maxage=9999999` or `--th-clean=0`
|
||||
|
||||
|
|
@ -1263,7 +1272,7 @@ see [./srv/expand/](./srv/expand/) for usage and examples
|
|||
|
||||
* and `PREADME.md` / `preadme.md` is shown above directory listings unless `--no-readme` or `.prologue.html`
|
||||
|
||||
* `README.md` and `*logue.html` can contain placeholder values which are replaced server-side before embedding into directory listings; see `--help-exp`
|
||||
* `README.md` and `*logue.html` can contain placeholder values which are replaced server-side before embedding into directory listings; see [`--help-exp`](https://copyparty.eu/cli/#exp-help-page)
|
||||
|
||||
|
||||
## searching
|
||||
|
|
@ -1293,10 +1302,9 @@ using arguments or config files, or a mix of both:
|
|||
* or click the `[reload cfg]` button in the control-panel if the user has `a`/admin in any volume
|
||||
* changes to the `[global]` config section requires a restart to take effect
|
||||
|
||||
**NB:** as humongous as this readme is, there is also a lot of undocumented features. Run copyparty with `--help` to see all available global options; all of those can be used in the `[global]` section of config files, and everything listed in `--help-flags` can be used in volumes as volflags.
|
||||
**NB:** as humongous as this readme is, there is also a lot of undocumented features. Run copyparty with [`--help`](https://copyparty.eu/cli/) (or click that link) to see all available global options; all of those can be used in the `[global]` section of config files, and everything listed in [`--help-flags`](https://copyparty.eu/cli/#flags-help-page) can be used in volumes as volflags (per-volume configuration).
|
||||
* if running in docker/podman, try this: `docker run --rm -it copyparty/ac --help`
|
||||
* or see this: https://ocv.me/copyparty/helptext.html
|
||||
* or if you prefer plaintext, https://ocv.me/copyparty/helptext.txt
|
||||
* or if you prefer plaintext, https://copyparty.eu/helptext.txt
|
||||
|
||||
|
||||
## zeroconf
|
||||
|
|
@ -1393,6 +1401,26 @@ config file example, which restricts FTP to only use ports 3921 and 12000-12099
|
|||
```
|
||||
|
||||
|
||||
## sftp server
|
||||
|
||||
goes roughly 700 MiB/s (slower than webdav and ftp)
|
||||
|
||||
> this is **not** [ftps](#ftp-server) (which copyparty also supports); [ftps](#ftp-server) is ftp-tls (think http/https), while **sftp** is ssh-based and (preferably) uses ssh-keys for authentication
|
||||
|
||||
the sftp-server requires the optional dependency [paramiko](https://pypi.org/project/paramiko/);
|
||||
* if you are **not** using docker, then install paramiko somehow
|
||||
* if you **are** using docker, then use one of the following image variants: `ac` / `im` / `iv` / `dj`
|
||||
|
||||
enable sftpd with `--sftp 3922` to listen on port 3922;
|
||||
* use global-option `sftp-key` to associate an ssh-key with a user;
|
||||
* commandline: `--sftp-key 'david ssh-ed25519 AAAAC3NzaC...'`
|
||||
* config-file: `sftp-key: david ssh-ed25519 AAAAC3NzaC...`
|
||||
* `--sftp-pw` enables login with passwords (default is ssh-keys only)
|
||||
* `--sftp-anon foo` enables login with username `foo` and no password; gives the same access/permissions as the website does when not logged in
|
||||
|
||||
see the [sftp section in --help](https://copyparty.eu/cli/#g-sftp) for the other options
|
||||
|
||||
|
||||
## webdav server
|
||||
|
||||
with read-write support, supports winXP and later, macos, nautilus/gvfs ... a great way to [access copyparty straight from the file explorer in your OS](#mount-as-drive)
|
||||
|
|
@ -1467,7 +1495,7 @@ unsafe, slow, not recommended for wan, enable with `--smb` for read-only or `--
|
|||
|
||||
click the [connect](http://127.0.0.1:3923/?hc) button in the control-panel to see connection instructions for windows, linux, macos
|
||||
|
||||
dependencies: `python3 -m pip install --user -U impacket==0.11.0`
|
||||
dependencies: `python3 -m pip install --user -U impacket==0.13.0`
|
||||
* newer versions of impacket will hopefully work just fine but there is monkeypatching so maybe not
|
||||
|
||||
some **BIG WARNINGS** specific to SMB/CIFS, in decreasing importance:
|
||||
|
|
@ -1702,7 +1730,7 @@ set upload rules using volflags, some examples:
|
|||
* just to avoid additional complexity in up2k which is enough of a mess already
|
||||
* `:c,lifetime=300` delete uploaded files when they become 5 minutes old
|
||||
|
||||
you can also set transaction limits which apply per-IP and per-volume, but these assume `-j 1` (default) otherwise the limits will be off, for example `-j 4` would allow anywhere between 1x and 4x the limits you set depending on which processing node the client gets routed to
|
||||
you can also set transaction limits which apply per-IP and per-volume, but these assume `-j 1` (default) otherwise the limits will be messed up, for example `-j 4` would allow anywhere between 1x and 4x the limits you set depending on which processing node the client gets routed to
|
||||
|
||||
* `:c,maxn=250,3600` allows 250 files over 1 hour from each IP (tracked per-volume)
|
||||
* `:c,maxb=1g,300` allows 1 GiB total over 5 minutes from each IP (tracked per-volume)
|
||||
|
|
@ -1783,6 +1811,8 @@ notes:
|
|||
* `:c,magic` enables filetype detection for nameless uploads, same as `--magic`
|
||||
* needs https://pypi.org/project/python-magic/ `python3 -m pip install --user -U python-magic`
|
||||
* on windows grab this instead `python3 -m pip install --user -U python-magic-bin`
|
||||
* `cachectl` changes how webbrowser will cache responses (the `Cache-Control` response-header); default is `no-cache` which will prevent repeated downloading of the same file unless necessary (browser will ask copyparty if the file has changed)
|
||||
* adding `?cache` to a link will override this with "fully cache this for 69 seconds"; `?cache=321` is 321 seconds, and `?cache=i` is 7 days
|
||||
|
||||
|
||||
## database location
|
||||
|
|
@ -1889,7 +1919,7 @@ trigger a program on uploads, renames etc ([examples](./bin/hooks/))
|
|||
|
||||
you can set hooks before and/or after an event happens, and currently you can hook uploads, moves/renames, and deletes
|
||||
|
||||
there's a bunch of flags and stuff, see `--help-hooks`
|
||||
there's a bunch of flags and stuff, see [`--help-hooks`](https://copyparty.eu/cli/#hooks-help-page)
|
||||
|
||||
if you want to write your own hooks, see [devnotes](./docs/devnotes.md#event-hooks)
|
||||
|
||||
|
|
@ -2412,6 +2442,8 @@ it comes with a [systemd service](./contrib/systemd/copyparty@.service) as well
|
|||
|
||||
after installing, start either the system service or the user service and navigate to http://127.0.0.1:3923 for further instructions (unless you already edited the config files, in which case you are good to go, probably)
|
||||
|
||||
> to start the systemd service, either do `systemctl start --user copyparty` to start it as your own user, or `systemctl start copyparty@bob` to use unix-user `bob`
|
||||
|
||||
|
||||
## fedora package
|
||||
|
||||
|
|
@ -2629,7 +2661,9 @@ quick summary of more eccentric web-browsers trying to view a directory index:
|
|||
unexpected things that run copyparty:
|
||||
|
||||
* an old [allwinner](https://a.ocv.me/pub/g/nerd-stuff/cpp/servers/aallwinner.jpg) android tv-box (ziptie-strapped to an HDD) running a firmware which flips the CPU into Big-Endian mode early during boot
|
||||
* copyparty is [certified BE ready](https://a.ocv.me/pub/g/nerd-stuff/cpp/servers/be-ready.png)
|
||||
* copyparty is [certified BE ready](https://a.ocv.me/pub/g/nerd-stuff/cpp/servers/be-ready.png) -- thanks, [Øl Telecom](http://ol-tele.com/)!
|
||||
* an [SGI O2 (photo)](https://a.ocv.me/pub/g/nerd-stuff/cpp/servers/sgi-o2.jpg?cache) with a grand total of 64 MiB RAM running SGI IRIX; [screenshot](https://a.ocv.me/pub/g/nerd-stuff/cpp/servers/sgi-o2.png?cache)
|
||||
* thanks again to the wonderful people at [Øl Telecom](http://ol-tele.com/)
|
||||
* a [wristwatch](https://a.ocv.me/pub/g/nerd-stuff/cpp/servers/clockyparty.jpg)
|
||||
|
||||
|
||||
|
|
@ -2653,6 +2687,10 @@ interact with copyparty using non-browser clients
|
|||
* `chunk(){ curl -H pw:wark -T- http://127.0.0.1:3923/;}`
|
||||
`chunk <movie.mkv`
|
||||
|
||||
* curl: append to existing file with `?apnd`
|
||||
* `log(){ curl -H pw:wark -T- http://127.0.0.1:3923/logfile.txt?apnd;}`
|
||||
`echo hey | log`
|
||||
|
||||
* bash: when curl and wget is not available or too boring
|
||||
* `(printf 'PUT /junk?pw=wark HTTP/1.1\r\n\r\n'; cat movie.mkv) | nc 127.0.0.1 3923`
|
||||
* `(printf 'PUT / HTTP/1.1\r\n\r\n'; cat movie.mkv) >/dev/tcp/127.0.0.1/3923`
|
||||
|
|
@ -2775,12 +2813,12 @@ below are some tweaks roughly ordered by usefulness:
|
|||
* `--no-htp --hash-mt=0 --mtag-mt=1 --th-mt=1` minimizes the number of threads; can help in some eccentric environments (like the vscode debugger)
|
||||
* when running on AlpineLinux or other musl-based distro, try mimalloc for higher performance (and twice as much RAM usage); `apk add mimalloc2` and run copyparty with env-var `LD_PRELOAD=/usr/lib/libmimalloc-secure.so.2`
|
||||
* note that mimalloc requires special care when combined with prisonparty and/or bubbleparty/bubblewrap; you must give it access to `/proc` and `/sys` otherwise you'll encounter issues with FFmpeg (audio transcoding, thumbnails)
|
||||
* `-j0` enables multiprocessing (actual multithreading), can reduce latency to `20+80/numCores` percent and generally improve performance in cpu-intensive workloads, for example:
|
||||
* `-j0` (usually *not* recommended) enables multiprocessing (actual multithreading), can reduce latency to `20+80/numCores` percent and generally improve performance in cpu-intensive workloads, for example:
|
||||
* lots of connections (many users or heavy clients)
|
||||
* simultaneous downloads and uploads saturating a 20gbps connection
|
||||
* if `-e2d` is enabled, `-j2` gives 4x performance for directory listings; `-j4` gives 16x
|
||||
|
||||
...however it also increases the server/filesystem/HDD load during uploads, and adds an overhead to internal communication, so it is usually a better idea to don't
|
||||
...however it will probably *reduce* performance in most cases, since it also increases the server/filesystem/HDD load during uploads, and adds an overhead to internal communication, so keeping the default is generally best
|
||||
* using [pypy](https://www.pypy.org/) instead of [cpython](https://www.python.org/) *can* be 70% faster for some workloads, but slower for many others
|
||||
* and pypy can sometimes crash on startup with `-j0` (TODO make issue)
|
||||
|
||||
|
|
@ -2812,7 +2850,7 @@ when uploading files,
|
|||
|
||||
# security
|
||||
|
||||
there is a [discord server](https://discord.gg/25J8CdTT6G) with an `@everyone` for all important updates (at the lack of better ideas)
|
||||
there is a [discord server](https://discord.gg/25J8CdTT6G) with announcements ; an `@everyone` for all important updates (at the lack of better ideas)
|
||||
|
||||
some notes on hardening
|
||||
|
||||
|
|
@ -2820,7 +2858,7 @@ some notes on hardening
|
|||
* cors doesn't work right otherwise
|
||||
* if you allow anonymous uploads or otherwise don't trust the contents of a volume, you can prevent XSS with volflag `nohtml`
|
||||
* this returns html documents as plaintext, and also disables markdown rendering
|
||||
* when running behind a reverse-proxy, listen on a unix-socket for tighter access control (and more performance); see [reverse-proxy](#reverse-proxy) or `--help-bind`
|
||||
* when running behind a reverse-proxy, listen on a unix-socket for tighter access control (and more performance); see [reverse-proxy](#reverse-proxy) or [`--help-bind`](https://copyparty.eu/cli/#bind-help-page)
|
||||
|
||||
safety profiles:
|
||||
|
||||
|
|
@ -2908,7 +2946,7 @@ dirkeys are generated based on another salt (`--dk-salt`) + filesystem-path and
|
|||
|
||||
## password hashing
|
||||
|
||||
you can hash passwords before putting them into config files / providing them as arguments; see `--help-pwhash` for all the details
|
||||
you can hash passwords before putting them into config files / providing them as arguments; see [`--help-pwhash`](https://copyparty.eu/cli/#pwhash-help-page) for all the details
|
||||
|
||||
`--ah-alg argon2` enables it, and if you have any plaintext passwords then it'll print the hashed versions on startup so you can replace them
|
||||
|
||||
|
|
@ -2973,7 +3011,7 @@ mandatory deps:
|
|||
|
||||
## optional dependencies
|
||||
|
||||
install these to enable bonus features
|
||||
enable bonus features by installing these python-packages from pypi or so:
|
||||
|
||||
enable [hashed passwords](#password-hashing) in config: `argon2-cffi`
|
||||
|
||||
|
|
@ -2981,6 +3019,8 @@ enable [ftp-server](#ftp-server):
|
|||
* for just plaintext FTP, `pyftpdlib` (is built into the SFX)
|
||||
* with TLS encryption, `pyftpdlib pyopenssl`
|
||||
|
||||
enable [sftp-server](#sftp-server): `paramiko`
|
||||
|
||||
enable [music tags](#metadata-from-audio-files):
|
||||
* either `mutagen` (fast, pure-python, skips a few tags, makes copyparty GPL? idk)
|
||||
* or `ffprobe` (20x slower, more accurate, possibly dangerous depending on your distro and users)
|
||||
|
|
@ -2995,7 +3035,7 @@ enable [thumbnails](#thumbnails) of...
|
|||
|
||||
enable sending [zeromq messages](#zeromq) from event-hooks: `pyzmq`
|
||||
|
||||
enable [smb](#smb-server) support (**not** recommended): `impacket==0.12.0`
|
||||
enable [smb](#smb-server) support (**not** recommended): `impacket==0.13.0`
|
||||
|
||||
`pyvips` gives higher quality thumbnails than `Pillow` and is 320% faster, using 270% more ram
|
||||
* to install `pyvips` on Linux: `sudo apt install libvips42 && python3 -m pip install --user -U pyvips`
|
||||
|
|
@ -3021,12 +3061,15 @@ set any of the following environment variables to disable its associated optiona
|
|||
| `PRTY_NO_FFPROBE` | **audio transcoding** goes byebye, **thumbnailing** must be handled by Pillow/libvips, **metadata-scanning** must be handled by mutagen |
|
||||
| `PRTY_NO_MAGIC` | do not use [magic](https://pypi.org/project/python-magic/) for filetype detection |
|
||||
| `PRTY_NO_MUTAGEN` | do not use [mutagen](https://pypi.org/project/mutagen/) for reading metadata from media files; will fallback to ffprobe |
|
||||
| `PRTY_NO_PARAMIKO` | disable sftp server ([paramiko](https://www.paramiko.org/)-based) |
|
||||
| `PRTY_NO_PARTFTPY` | disable tftp server ([partftpy](https://github.com/9001/partftpy)-based) |
|
||||
| `PRTY_NO_PIL` | disable all [Pillow](https://pypi.org/project/pillow/)-based thumbnail support; will fallback to libvips or ffmpeg |
|
||||
| `PRTY_NO_PILF` | disable Pillow `ImageFont` text rendering, used for folder thumbnails |
|
||||
| `PRTY_NO_PIL_AVIF` | disable Pillow avif support (internal and/or [plugin](https://pypi.org/project/pillow-avif-plugin/)) |
|
||||
| `PRTY_NO_PIL_HEIF` | disable 3rd-party Pillow plugin for [HEIF support](https://pypi.org/project/pillow-heif/) |
|
||||
| `PRTY_NO_PIL_WEBP` | disable use of native webp support in Pillow |
|
||||
| `PRTY_NO_PSUTIL` | do not use [psutil](https://pypi.org/project/psutil/) for reaping stuck hooks and plugins on Windows |
|
||||
| `PRTY_NO_PYFTPD` | disable ftp(s) server ([pyftpdlib](https://pypi.org/project/pyftpdlib/)-based) |
|
||||
| `PRTY_NO_RAW` | disable all [rawpy](https://pypi.org/project/rawpy/)-based thumbnail support for RAW images |
|
||||
| `PRTY_NO_VIPS` | disable all [libvips](https://pypi.org/project/pyvips/)-based thumbnail support; will fallback to Pillow or ffmpeg |
|
||||
|
||||
|
|
@ -3129,6 +3172,9 @@ and then copypaste the following command into `a-Shell`:
|
|||
curl -L https://github.com/9001/copyparty/raw/refs/heads/hovudstraum/contrib/setup-ashell.sh | sh
|
||||
```
|
||||
|
||||
> if you want the latest copyparty beta, then do this instead:
|
||||
> `curl -L https://copyparty.eu/beta/setup-ashell.sh | sh`
|
||||
|
||||
what this does:
|
||||
* creates a basic [config file](#accounts-and-volumes) named `cpc` which you can edit with `vim cpc`
|
||||
* adds the command `cpp` to launch copyparty with that config file
|
||||
|
|
|
|||
|
|
@ -4,13 +4,19 @@ import os
|
|||
import sys
|
||||
import tempfile
|
||||
import subprocess as sp
|
||||
import keyfinder
|
||||
|
||||
try:
|
||||
import keyfinder
|
||||
|
||||
PKF = True
|
||||
except:
|
||||
PKF = False
|
||||
|
||||
from copyparty.util import fsenc
|
||||
|
||||
"""
|
||||
dep: github/mixxxdj/libkeyfinder
|
||||
dep: pypi/keyfinder
|
||||
dep: pypi/keyfinder -OR- EvanPurkhiser/keyfinder-cli
|
||||
dep: ffmpeg
|
||||
"""
|
||||
|
||||
|
|
@ -35,7 +41,17 @@ def det(tf):
|
|||
])
|
||||
# fmt: on
|
||||
|
||||
print(keyfinder.key(tf).camelot())
|
||||
if PKF:
|
||||
print(keyfinder.key(tf).camelot())
|
||||
else:
|
||||
# fmt: off
|
||||
sp.check_call([
|
||||
b"keyfinder-cli",
|
||||
b"-n",
|
||||
b"camelot",
|
||||
fsenc(tf)
|
||||
])
|
||||
# fmt: on
|
||||
|
||||
|
||||
def main():
|
||||
|
|
|
|||
|
|
@ -155,6 +155,11 @@ install_keyfinder() {
|
|||
return
|
||||
}
|
||||
|
||||
(cat /etc/alpine-release || echo a) 2>&1 | grep -E '3\.2[3-9]' && {
|
||||
echo "alpine too new; ffmpeg8 is keyfinder-py incompat; giving up"
|
||||
return
|
||||
}
|
||||
|
||||
cd "$td"
|
||||
github_tarball https://api.github.com/repos/mixxxdj/libkeyfinder/releases/latest
|
||||
ls -al
|
||||
|
|
@ -189,7 +194,7 @@ install_keyfinder() {
|
|||
exit 1
|
||||
}
|
||||
|
||||
x=${-//[^x]/}; set -x; cat /etc/alpine-release
|
||||
x=${-//[^x]/}; set -x; cat /etc/alpine-release || true
|
||||
# rm -rf /Users/ed/Library/Python/3.9/lib/python/site-packages/*keyfinder*
|
||||
CFLAGS="-I$h/pe/keyfinder/include -I/opt/local/include -I/usr/include/ffmpeg" \
|
||||
CXXFLAGS="-I$h/pe/keyfinder/include -I/opt/local/include -I/usr/include/ffmpeg" \
|
||||
|
|
|
|||
|
|
@ -6,8 +6,8 @@ __copyright__ = 2019
|
|||
__license__ = "MIT"
|
||||
__url__ = "https://github.com/9001/copyparty/"
|
||||
|
||||
S_VERSION = "2.1"
|
||||
S_BUILD_DT = "2025-09-06"
|
||||
S_VERSION = "2.2"
|
||||
S_BUILD_DT = "2025-12-16"
|
||||
|
||||
"""
|
||||
mount a copyparty server (local or remote) as a filesystem
|
||||
|
|
@ -284,8 +284,8 @@ class Gateway(object):
|
|||
if ar.td:
|
||||
self.ssl_context = ssl._create_unverified_context()
|
||||
elif ar.te:
|
||||
self.ssl_context = ssl.SSLContext(ssl.PROTOCOL_TLS)
|
||||
self.ssl_context.load_verify_locations(ar.te)
|
||||
self.ssl_context = ssl.create_default_context(cafile=ar.te)
|
||||
self.ssl_context.check_hostname = ar.teh
|
||||
|
||||
self.conns = {}
|
||||
|
||||
|
|
@ -1165,6 +1165,7 @@ NOTE: if server has --usernames enabled, then password is "username:password"
|
|||
|
||||
ap2 = ap.add_argument_group("https/TLS")
|
||||
ap2.add_argument("-te", metavar="PEMFILE", help="certificate to expect/verify")
|
||||
ap2.add_argument("-teh", action="store_true", help="require correct hostname in -te cert")
|
||||
ap2.add_argument("-td", action="store_true", help="disable certificate check")
|
||||
|
||||
ap2 = ap.add_argument_group("cache/perf")
|
||||
|
|
|
|||
36
bin/u2c.py
36
bin/u2c.py
|
|
@ -1,8 +1,8 @@
|
|||
#!/usr/bin/env python3
|
||||
from __future__ import print_function, unicode_literals
|
||||
|
||||
S_VERSION = "2.15"
|
||||
S_BUILD_DT = "2025-10-25"
|
||||
S_VERSION = "2.18"
|
||||
S_BUILD_DT = "2026-01-02"
|
||||
|
||||
"""
|
||||
u2c.py: upload to copyparty
|
||||
|
|
@ -165,8 +165,8 @@ class HCli(object):
|
|||
elif self.verify is True:
|
||||
self.ctx = None
|
||||
else:
|
||||
self.ctx = ssl.SSLContext(ssl.PROTOCOL_TLS)
|
||||
self.ctx.load_verify_locations(self.verify)
|
||||
self.ctx = ssl.create_default_context(cafile=self.verify)
|
||||
self.ctx.check_hostname = ar.teh
|
||||
|
||||
self.base_hdrs = {
|
||||
"Accept": "*/*",
|
||||
|
|
@ -196,6 +196,8 @@ class HCli(object):
|
|||
hdrs.update(self.base_hdrs)
|
||||
if self.ar.a:
|
||||
hdrs["PW"] = self.ar.a
|
||||
if self.ar.ba:
|
||||
hdrs["Authorization"] = self.ar.ba
|
||||
if ctype:
|
||||
hdrs["Content-Type"] = ctype
|
||||
if meth == "POST" and CLEN not in hdrs:
|
||||
|
|
@ -492,6 +494,12 @@ print = safe_print if VT100 else flushing_print
|
|||
|
||||
|
||||
def termsize():
|
||||
try:
|
||||
w, h = os.get_terminal_size()
|
||||
return w, h
|
||||
except:
|
||||
pass
|
||||
|
||||
env = os.environ
|
||||
|
||||
def ioctl_GWINSZ(fd):
|
||||
|
|
@ -851,7 +859,7 @@ def handshake(ar, file, search):
|
|||
return [], False
|
||||
elif sc == 409 or "<pre>upload rejected, file already exists" in txt:
|
||||
return [], False
|
||||
elif sc == 403:
|
||||
elif sc == 403 or sc == 401:
|
||||
print("\nERROR: login required, or wrong password:\n%s" % (txt,))
|
||||
raise BadAuth()
|
||||
|
||||
|
|
@ -1544,7 +1552,8 @@ NOTE: if server has --usernames enabled, then password is "username:password"
|
|||
ap.add_argument("url", type=unicode, help="server url, including destination folder")
|
||||
ap.add_argument("files", type=files_decoder, nargs="+", help="files and/or folders to process")
|
||||
ap.add_argument("-v", action="store_true", help="verbose")
|
||||
ap.add_argument("-a", metavar="PASSWD", help="password or $filepath")
|
||||
ap.add_argument("-a", metavar="PASSWD", default="", help="password (or $filepath) for copyparty (is sent in header 'PW')")
|
||||
ap.add_argument("--ba", metavar="PASSWD", default="", help="password (or $filepath) for basic-auth (usually not necessary)")
|
||||
ap.add_argument("-s", action="store_true", help="file-search (disables upload)")
|
||||
ap.add_argument("-x", type=unicode, metavar="REGEX", action="append", help="skip file if filesystem-abspath matches REGEX (option can be repeated), example: '.*/\\.hist/.*'")
|
||||
ap.add_argument("--ok", action="store_true", help="continue even if some local files are inaccessible")
|
||||
|
|
@ -1587,6 +1596,7 @@ NOTE: if server has --usernames enabled, then password is "username:password"
|
|||
|
||||
ap = app.add_argument_group("tls")
|
||||
ap.add_argument("-te", metavar="PATH", help="path to ca.pem or cert.pem to expect/verify")
|
||||
ap.add_argument("-teh", action="store_true", help="require correct hostname in -te cert")
|
||||
ap.add_argument("-td", action="store_true", help="disable certificate check")
|
||||
# fmt: on
|
||||
|
||||
|
|
@ -1682,11 +1692,15 @@ NOTE: if server has --usernames enabled, then password is "username:password"
|
|||
print("\n\n %s\n\n" % (t,))
|
||||
raise
|
||||
|
||||
if ar.a and ar.a.startswith("$"):
|
||||
fn = ar.a[1:]
|
||||
print("reading password from file [{0}]".format(fn))
|
||||
with open(fn, "rb") as f:
|
||||
ar.a = f.read().decode("utf-8").strip()
|
||||
for k in ("a", "ba"):
|
||||
zs = getattr(ar, k)
|
||||
if zs.startswith("$"):
|
||||
print("reading password from file [%s]" % (zs[1:],))
|
||||
with open(zs[1:], "rb") as f:
|
||||
setattr(ar, k, f.read().decode("utf-8").strip())
|
||||
|
||||
if ar.ba:
|
||||
ar.ba = "Basic " + base64.b64encode(ar.ba.encode("utf-8")).decode("utf-8")
|
||||
|
||||
for n in range(ar.rh):
|
||||
try:
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
#
|
||||
# to reverse-proxy a specific path/subpath/location below a domain
|
||||
# (rather than a complete subdomain), for example "/qw/er", you must
|
||||
# run copyparty with --rp-loc /qw/as and also change the following:
|
||||
# run copyparty with --rp-loc /qw/er and also change the following:
|
||||
# location / {
|
||||
# proxy_pass http://cpp_tcp;
|
||||
# to this:
|
||||
|
|
|
|||
|
|
@ -69,11 +69,8 @@ in
|
|||
options.services.copyparty = {
|
||||
enable = mkEnableOption "web-based file manager";
|
||||
|
||||
package = mkOption {
|
||||
type = types.package;
|
||||
default = pkgs.copyparty;
|
||||
defaultText = "pkgs.copyparty";
|
||||
description = ''
|
||||
package = mkPackageOption pkgs "copyparty" {
|
||||
extraDescription = ''
|
||||
Package of the application to run, exposed for overriding purposes.
|
||||
'';
|
||||
};
|
||||
|
|
|
|||
|
|
@ -3,9 +3,9 @@
|
|||
# NOTE: You generally shouldn't use this PKGBUILD on Arch, as it is mainly for testing purposes. Install copyparty using pacman instead.
|
||||
|
||||
pkgname=copyparty
|
||||
pkgver="1.19.20"
|
||||
pkgver="1.20.1"
|
||||
pkgrel=1
|
||||
pkgdesc="File server with accelerated resumable uploads, dedup, WebDAV, FTP, TFTP, zeroconf, media indexer, thumbnails++"
|
||||
pkgdesc="File server with accelerated resumable uploads, dedup, WebDAV, SFTP, FTP, TFTP, zeroconf, media indexer, thumbnails++"
|
||||
arch=("any")
|
||||
url="https://github.com/9001/${pkgname}"
|
||||
license=('MIT')
|
||||
|
|
@ -14,6 +14,7 @@ makedepends=("python-wheel" "python-setuptools" "python-build" "python-installer
|
|||
optdepends=("ffmpeg: thumbnails for videos, images (slower) and audio, music tags"
|
||||
"cfssl: generate TLS certificates on startup"
|
||||
"python-mutagen: music tags (alternative)"
|
||||
"python-paramiko: sftp server",
|
||||
"python-pillow: thumbnails for images"
|
||||
"python-pyvips: thumbnails for images (higher quality, faster, uses more ram)"
|
||||
"libkeyfinder: detection of musical keys"
|
||||
|
|
@ -23,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=("050ccc34554e59210aca7a67d87a186e69b3f4dbe013d5ee2f11a22c259a82a6")
|
||||
sha256sums=("4f513ca9e3d1c11a7bb4e1a8a925dda2449b9565e91f6ef7cbe10367fa4e2935")
|
||||
|
||||
build() {
|
||||
cd "${srcdir}/${pkgname}-${pkgver}/copyparty/web"
|
||||
|
|
|
|||
|
|
@ -2,9 +2,9 @@
|
|||
|
||||
|
||||
pkgname=copyparty
|
||||
pkgver=1.19.20
|
||||
pkgver=1.20.1
|
||||
pkgrel=1
|
||||
pkgdesc="File server with accelerated resumable uploads, dedup, WebDAV, FTP, TFTP, zeroconf, media indexer, thumbnails++"
|
||||
pkgdesc="File server with accelerated resumable uploads, dedup, WebDAV, SFTP, FTP, TFTP, zeroconf, media indexer, thumbnails++"
|
||||
arch=("any")
|
||||
url="https://github.com/9001/${pkgname}"
|
||||
license=('MIT')
|
||||
|
|
@ -13,6 +13,7 @@ makedepends=("python3-wheel" "python3-setuptools" "python3-build" "python3-insta
|
|||
optdepends=("ffmpeg: thumbnails for videos, images (slower) and audio, music tags"
|
||||
"golang-cfssl: generate TLS certificates on startup"
|
||||
"python3-mutagen: music tags (alternative)"
|
||||
"python3-paramiko: sftp server"
|
||||
"python3-pil: thumbnails for images"
|
||||
"python3-openssl: ftps functionality"
|
||||
"python3-zmq: send zeromq messages from event-hooks"
|
||||
|
|
@ -20,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=("050ccc34554e59210aca7a67d87a186e69b3f4dbe013d5ee2f11a22c259a82a6")
|
||||
sha256sums=("4f513ca9e3d1c11a7bb4e1a8a925dda2449b9565e91f6ef7cbe10367fa4e2935")
|
||||
|
||||
build() {
|
||||
cd "${srcdir}/${pkgname}-${pkgver}/copyparty/web"
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@
|
|||
pyzmq,
|
||||
ffmpeg,
|
||||
mutagen,
|
||||
paramiko,
|
||||
pyftpdlib,
|
||||
magic,
|
||||
partftpy,
|
||||
|
|
@ -44,6 +45,9 @@
|
|||
# send ZeroMQ messages from event-hooks
|
||||
withZeroMQ ? true,
|
||||
|
||||
# enable SFTP server
|
||||
withSFTP ? false,
|
||||
|
||||
# enable FTP server
|
||||
withFTP ? true,
|
||||
|
||||
|
|
@ -131,6 +135,7 @@ buildPythonApplication {
|
|||
fusepy
|
||||
]
|
||||
++ lib.optional withSMB impacket
|
||||
++ lib.optional withSFTP paramiko
|
||||
++ lib.optional withFTP pyftpdlib
|
||||
++ lib.optional withFTPS pyopenssl
|
||||
++ lib.optional withTFTP partftpy
|
||||
|
|
@ -152,7 +157,7 @@ buildPythonApplication {
|
|||
meta = {
|
||||
description = "Turn almost any device into a file server";
|
||||
longDescription = ''
|
||||
Portable file server with accelerated resumable uploads, dedup, WebDAV,
|
||||
Portable file server with accelerated resumable uploads, dedup, WebDAV, SFTP,
|
||||
FTP, TFTP, zeroconf, media indexer, thumbnails++ all in one file, no deps
|
||||
'';
|
||||
homepage = "https://github.com/9001/copyparty";
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"url": "https://github.com/9001/copyparty/releases/download/v1.19.20/copyparty-1.19.20.tar.gz",
|
||||
"version": "1.19.20",
|
||||
"hash": "sha256-BQzMNFVOWSEKynpn2HoYbmmz9NvgE9XuLxGiLCWagqY="
|
||||
"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="
|
||||
}
|
||||
|
|
@ -8,6 +8,7 @@ let
|
|||
withMediaProcessing = true;
|
||||
withBasicAudioMetadata = true;
|
||||
withZeroMQ = true;
|
||||
withSFTP = true;
|
||||
withFTP = true;
|
||||
withFTPS = true;
|
||||
withTFTP = true;
|
||||
|
|
|
|||
|
|
@ -5,15 +5,15 @@ License: MIT
|
|||
Group: Utilities
|
||||
URL: https://github.com/9001/copyparty
|
||||
Source0: copyparty-$pkgver.tar.gz
|
||||
Summary: File server with accelerated resumable uploads, dedup, WebDAV, FTP, TFTP, zeroconf, media indexer, thumbnails++
|
||||
Summary: File server with accelerated resumable uploads, dedup, WebDAV, SFTP, FTP, TFTP, zeroconf, media indexer, thumbnails++
|
||||
BuildArch: noarch
|
||||
BuildRequires: python3, python3-devel, pyproject-rpm-macros, python-setuptools, python-wheel, make
|
||||
Requires: python3, (python3-jinja2 or python-jinja2), lsof
|
||||
Recommends: ffmpeg, (golang-github-cloudflare-cfssl or cfssl), python-mutagen, python-pillow, python-pyvips
|
||||
Recommends: qm-vamp-plugins, python-argon2-cffi, (python-pyopenssl or pyopenssl), python-impacket
|
||||
Recommends: qm-vamp-plugins, python-argon2-cffi, (python-pyopenssl or pyopenssl), python-paramiko, python-impacket
|
||||
|
||||
%description
|
||||
Portable file server with accelerated resumable uploads, dedup, WebDAV, FTP, TFTP, zeroconf, media indexer, thumbnails++ all in one file, no deps
|
||||
Portable file server with accelerated resumable uploads, dedup, WebDAV, SFTP, FTP, TFTP, zeroconf, media indexer, thumbnails++ all in one file, no deps
|
||||
|
||||
See release at https://github.com/9001/copyparty/releases
|
||||
|
||||
|
|
|
|||
|
|
@ -24,8 +24,8 @@ Note that you can select the owner and group of this volume by changing the `uid
|
|||
To install and start copyparty with Podman and systemd as the root user, run the following:
|
||||
|
||||
```shell
|
||||
sudo mkdir -pv /etc/systemd/container/ /etc/copyparty/
|
||||
sudo cp -v copyparty.container /etc/systemd/containers/
|
||||
sudo mkdir -pv /etc/containers/systemd/ /etc/copyparty/
|
||||
sudo cp -v copyparty.container /etc/containers/systemd/
|
||||
sudo cp -v copyparty.conf /etc/copyparty/
|
||||
sudo systemctl daemon-reload
|
||||
sudo systemctl start copyparty
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ name="copyparty"
|
|||
rcvar="copyparty_enable"
|
||||
copyparty_user="copyparty"
|
||||
copyparty_args="-e2dsa -v /storage:/storage:r" # change as you see fit
|
||||
copyparty_command="/usr/local/bin/python3.9 /usr/local/copyparty/copyparty-sfx.py ${copyparty_args}"
|
||||
copyparty_command="/usr/local/bin/python3.11 /usr/local/copyparty/copyparty-sfx.py ${copyparty_args}"
|
||||
pidfile="/var/run/copyparty/${name}.pid"
|
||||
command="/usr/sbin/daemon"
|
||||
command_args="-P ${pidfile} -r -f ${copyparty_command}"
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
# https://apps.apple.com/us/app/a-shell/id1473805438
|
||||
#
|
||||
# step 2: copypaste the following command into a-Shell:
|
||||
# curl -L https://github.com/9001/copyparty/raw/refs/heads/hovudstraum/contrib/setup-ashell.sh
|
||||
# curl -L https://github.com/9001/copyparty/raw/refs/heads/hovudstraum/contrib/setup-ashell.sh | sh
|
||||
#
|
||||
# step 3: launch copyparty with this command: cpp
|
||||
#
|
||||
|
|
|
|||
|
|
@ -86,7 +86,6 @@ web/md2.js
|
|||
web/mde.css
|
||||
web/mde.html
|
||||
web/mde.js
|
||||
web/msg.css
|
||||
web/msg.html
|
||||
web/opds.xml
|
||||
web/rups.css
|
||||
|
|
@ -108,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
|
||||
|
|
@ -119,6 +119,7 @@ web/tl/spa.js
|
|||
web/tl/swe.js
|
||||
web/tl/tur.js
|
||||
web/tl/ukr.js
|
||||
web/tl/vie.js
|
||||
web/ui.css
|
||||
web/up2k.js
|
||||
web/util.js
|
||||
|
|
|
|||
|
|
@ -48,6 +48,7 @@ from .util import (
|
|||
HAVE_IPV6,
|
||||
IMPLICATIONS,
|
||||
JINJA_VER,
|
||||
MIKO_VER,
|
||||
MIMES,
|
||||
PARTFTPY_VER,
|
||||
PY_DESC,
|
||||
|
|
@ -1204,6 +1205,7 @@ def add_fs(ap):
|
|||
ap2 = ap.add_argument_group("filesystem options")
|
||||
rm_re_def = "15/0.1" if ANYWIN else "0/0"
|
||||
ap2.add_argument("--casechk", metavar="N", type=u, default="auto", help="detect and prevent CI (case-insensitive) behavior if the underlying filesystem is CI? [\033[32my\033[0m] = detect and prevent, [\033[32mn\033[0m] = ignore and allow, [\033[32mauto\033[0m] = \033[32my\033[0m if CI fs detected. NOTE: \033[32my\033[0m is very slow but necessary for correct WebDAV behavior on Windows/Macos (volflag=casechk)")
|
||||
ap2.add_argument("--fsnt", metavar="OS", type=u, default="auto", help="which characters to allow in file/folder names; [\033[32mwin\033[0m] = windows (not <>:|?*\"\\/), [\033[32mmac\033[0m] = macos (not :), [\033[32mlin\033[0m] = linux (anything goes) (volflag=fsnt)")
|
||||
ap2.add_argument("--rm-retry", metavar="T/R", type=u, default=rm_re_def, help="if a file cannot be deleted because it is busy, continue trying for \033[33mT\033[0m seconds, retry every \033[33mR\033[0m seconds; disable with 0/0 (volflag=rm_retry)")
|
||||
ap2.add_argument("--mv-retry", metavar="T/R", type=u, default=rm_re_def, help="if a file cannot be renamed because it is busy, continue trying for \033[33mT\033[0m seconds, retry every \033[33mR\033[0m seconds; disable with 0/0 (volflag=mv_retry)")
|
||||
ap2.add_argument("--iobuf", metavar="BYTES", type=int, default=256*1024, help="file I/O buffer-size; if your volumes are on a network drive, try increasing to \033[32m524288\033[0m or even \033[32m4194304\033[0m (and let me know if that improves your performance)")
|
||||
|
|
@ -1230,6 +1232,7 @@ def add_upload(ap):
|
|||
ap2.add_argument("--bup-ck", metavar="ALG", type=u, default="sha512", help="default checksum-hasher for bup/basic-uploader: no / md5 / sha1 / sha256 / sha512 / b2 / blake2 / b2s / blake2s (volflag=bup_ck)")
|
||||
ap2.add_argument("--unpost", metavar="SEC", type=int, default=3600*12, help="grace period where uploads can be deleted by the uploader, even without delete permissions; 0=disabled, default=12h")
|
||||
ap2.add_argument("--unp-who", metavar="NUM", type=int, default=1, help="clients can undo recent uploads by using the unpost tab (requires \033[33m-e2d\033[0m). [\033[32m0\033[0m] = never allowed (disable feature), [\033[32m1\033[0m] = allow if client has the same IP as the upload AND is using the same account, [\033[32m2\033[0m] = just check the IP, [\033[32m3\033[0m] = just check account-name (volflag=unp_who)")
|
||||
ap2.add_argument("--apnd-who", metavar="NUM", type=u, default="dw", help="who can append to existing files? [\033[32mno\033[0m]=nobody, [\033[32maw\033[0m]=admin+write, [\033[32mdw\033[0m]=delete+write, [\033[32mw\033[0m]=write (volflag=apnd_who)")
|
||||
ap2.add_argument("--u2abort", metavar="NUM", type=int, default=1, help="clients can abort incomplete uploads by using the unpost tab (requires \033[33m-e2d\033[0m). [\033[32m0\033[0m] = never allowed (disable feature), [\033[32m1\033[0m] = allow if client has the same IP as the upload AND is using the same account, [\033[32m2\033[0m] = just check the IP, [\033[32m3\033[0m] = just check account-name (volflag=u2abort)")
|
||||
ap2.add_argument("--blank-wt", metavar="SEC", type=int, default=300, help="file write grace period (any client can write to a blank file last-modified more recently than \033[33mSEC\033[0m seconds ago)")
|
||||
ap2.add_argument("--reg-cap", metavar="N", type=int, default=38400, help="max number of uploads to keep in memory when running without \033[33m-e2d\033[0m; roughly 1 MiB RAM per 600")
|
||||
|
|
@ -1275,9 +1278,15 @@ def add_network(ap):
|
|||
ap2.add_argument("--ll", action="store_true", help="include link-local IPv4/IPv6 in mDNS replies, even if the NIC has routable IPs (breaks some mDNS clients)")
|
||||
ap2.add_argument("--rproxy", metavar="DEPTH", type=int, default=9999999, help="which ip to associate clients with; [\033[32m0\033[0m]=tcp, [\033[32m1\033[0m]=origin (first x-fwd, unsafe), [\033[32m-1\033[0m]=closest-proxy, [\033[32m-2\033[0m]=second-hop, [\033[32m-3\033[0m]=third-hop")
|
||||
ap2.add_argument("--xff-hdr", metavar="NAME", type=u, default="x-forwarded-for", help="if reverse-proxied, which http header to read the client's real ip from")
|
||||
ap2.add_argument("--xf-host", metavar="NAME", type=u, default="x-forwarded-host", help="if reverse-proxied, which http header to read the correct Host value from; this header must contain the server's external domain name")
|
||||
ap2.add_argument("--xf-proto", metavar="NAME", type=u, default="x-forwarded-proto", help="if reverse-proxied, which http header to read the correct protocol value from; this header must contain either 'http' or 'https'")
|
||||
ap2.add_argument("--xf-proto-fb", metavar="T", type=u, default="", help="protocol to assume if the X-Forwarded-Proto header (\033[33m--xf-proto\033[0m) is not provided by the reverseproxy; either 'http' or 'https'")
|
||||
ap2.add_argument("--xff-src", metavar="CIDR", type=u, default="127.0.0.0/8, ::1/128", help="list of trusted reverse-proxy CIDRs (comma-separated); only accept the real-ip header (\033[33m--xff-hdr\033[0m) and IdP headers if the incoming connection is from an IP within either of these subnets. Specify [\033[32mlan\033[0m] to allow all LAN / private / non-internet IPs. Can be disabled with [\033[32many\033[0m] if you are behind cloudflare (or similar) and are using \033[32m--xff-hdr=cf-connecting-ip\033[0m (or similar)")
|
||||
ap2.add_argument("--ipa", metavar="CIDR", type=u, default="", help="only accept connections from IP-addresses inside \033[33mCIDR\033[0m (comma-separated); examples: [\033[32mlan\033[0m] or [\033[32m10.89.0.0/16, 192.168.33.0/24\033[0m]")
|
||||
ap2.add_argument("--ipa", metavar="CIDR", type=u, default="", help="only accept connections from IP-addresses inside \033[33mCIDR\033[0m (comma-separated); examples: [\033[32mlan\033[0m] or [\033[32m10.89.0.0/16, 192.168.33.0/24\033[0m]\n └─for performance and security, this only looks at the TCP/Network-level IP, and will NOT work behind a reverseproxy")
|
||||
ap2.add_argument("--ipar", metavar="CIDR", type=u, default="", help="only accept connections from IP-addresses inside \033[33mCIDR\033[0m (comma-separated).\n └─this is reverseproxy-compatible; reads client-IP from 'X-Forwarded-For' if possible, with TCP/Network IP as fallback")
|
||||
ap2.add_argument("--rp-loc", metavar="PATH", type=u, default="", help="if reverse-proxying on a location instead of a dedicated domain/subdomain, provide the base location here; example: [\033[32m/foo/bar\033[0m]")
|
||||
ap2.add_argument("--cachectl", metavar="TXT", default="no-cache", help="default-value of the 'Cache-Control' response-header (controls caching in webbrowsers). Default prevents repeated downloading of the same file unless necessary (browser will ask copyparty if the file has changed). Examples: [\033[32mmax-age=604869\033[0m] will cache for 7 days, [\033[32mno-store, max-age=0\033[0m] will always redownload. (volflag=cachectl)")
|
||||
ap2.add_argument("--http-vary", metavar="TXT", type=u, default="Origin, PW, Cookie", help="value of the 'Vary' response-header; a hint for caching proxies")
|
||||
ap2.add_argument("--http-no-tcp", action="store_true", help="do not listen on TCP/IP for http/https; only listen on unix-domain-sockets")
|
||||
if ANYWIN:
|
||||
ap2.add_argument("--reuseaddr", action="store_true", help="set reuseaddr on listening sockets on windows; allows rapid restart of copyparty at the expense of being able to accidentally start multiple instances")
|
||||
|
|
@ -1333,6 +1342,7 @@ def add_auth(ap):
|
|||
ap2.add_argument("--idp-h-grp", metavar="HN", type=u, default="", help="assume the request-header \033[33mHN\033[0m contains the groupname of the requesting user; can be referenced in config files for group-based access control")
|
||||
ap2.add_argument("--idp-h-key", metavar="HN", type=u, default="", help="optional but recommended safeguard; your reverse-proxy will insert a secret header named \033[33mHN\033[0m into all requests, and the other IdP headers will be ignored if this header is not present")
|
||||
ap2.add_argument("--idp-gsep", metavar="RE", type=u, default="|:;+,", help="if there are multiple groups in \033[33m--idp-h-grp\033[0m, they are separated by one of the characters in \033[33mRE\033[0m")
|
||||
ap2.add_argument("--idp-chsub", metavar="TXT", type=u, default="", help="characters to replace in usernames/groupnames; a list of pairs of characters separated by | so for example | _| will replace spaces with _ to make configuration easier, or |%%_|^_|@_| will replace %%/^/@ with _")
|
||||
ap2.add_argument("--idp-db", metavar="PATH", type=u, default=idp_db, help="where to store the known IdP users/groups (if you run multiple copyparty instances, make sure they use different DBs)")
|
||||
ap2.add_argument("--idp-store", metavar="N", type=int, default=1, help="how to use \033[33m--idp-db\033[0m; [\033[32m0\033[0m] = entirely disable, [\033[32m1\033[0m] = write-only (effectively disabled), [\033[32m2\033[0m] = remember users, [\033[32m3\033[0m] = remember users and groups.\nNOTE: Will remember and restore the IdP-volumes of all users for all eternity if set to 2 or 3, even when user is deleted from your IdP")
|
||||
ap2.add_argument("--idp-adm", metavar="U,U", type=u, default="", help="comma-separated list of users allowed to use /?idp (the cache management UI)")
|
||||
|
|
@ -1341,6 +1351,8 @@ def add_auth(ap):
|
|||
ap2.add_argument("--idp-login-t", metavar="T", type=u, default="Login with SSO", help="the label/text for the idp-login button")
|
||||
ap2.add_argument("--idp-logout", metavar="L", type=u, default="", help="replace all logout-buttons with a link to URL \033[33mL\033[0m")
|
||||
ap2.add_argument("--auth-ord", metavar="TXT", type=u, default="idp,ipu", help="controls auth precedence; examples: [\033[32mpw,idp,ipu\033[0m], [\033[32mipu,pw,idp\033[0m], see --help-auth-ord")
|
||||
ap2.add_argument("--pw-hdr", metavar="NAME", type=u, default="pw", help="lowercase name of password-header (NAME: foo); \033[1;31mWARNING:\033[0m Changing this will break support for many clients")
|
||||
ap2.add_argument("--pw-urlp", metavar="NAME", type=u, default="pw", help="lowercase name of password url-param (?NAME=foo); \033[1;31mWARNING:\033[0m Changing this will break support for many clients")
|
||||
ap2.add_argument("--no-bauth", action="store_true", help="disable basic-authentication support; do not accept passwords from the 'Authenticate' header at all. NOTE: This breaks support for the android app")
|
||||
ap2.add_argument("--bauth-last", action="store_true", help="keeps basic-authentication enabled, but only as a last-resort; if a cookie is also provided then the cookie wins")
|
||||
ap2.add_argument("--ses-db", metavar="PATH", type=u, default=ses_db, help="where to store the sessions database (if you run multiple copyparty instances, make sure they use different DBs)")
|
||||
|
|
@ -1412,13 +1424,30 @@ def add_zc_ssdp(ap):
|
|||
ap2.add_argument("--zsid", metavar="UUID", type=u, default=zsid, help="USN (device identifier) to announce")
|
||||
|
||||
|
||||
def add_sftp(ap):
|
||||
ap2 = ap.add_argument_group("SFTP options")
|
||||
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)
|
||||
ap2.add_argument("--sftp-pw", action="store_true", help="allow password-authentication with sftp (not just ssh-keys)")
|
||||
ap2.add_argument("--sftp-anon", metavar="TXT", type=u, default="", help="allow anonymous/unauthenticated connections with \033[33mTXT\033[0m as username")
|
||||
ap2.add_argument("--sftp-hostk", metavar="FP", type=u, default=E.cfg, help="path to folder with hostkeys, for example 'ssh_host_rsa_key'; missing keys will be generated")
|
||||
ap2.add_argument("--sftp-banner", metavar="T", type=u, default="", help="bannertext to send when someone connects; can be @filepath")
|
||||
ap2.add_argument("--sftp-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]")
|
||||
|
||||
|
||||
def add_ftp(ap):
|
||||
ap2 = ap.add_argument_group("FTP options (TCP only)")
|
||||
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. Examples: [\033[32mlan\033[0m] or [\033[32m10.89.0.0/16, 192.168.33.0/24\033[0m]")
|
||||
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")
|
||||
ap2.add_argument("--ftp-wt", metavar="SEC", type=int, default=7, help="grace period for resuming interrupted uploads (any client can write to any file last-modified more recently than \033[33mSEC\033[0m seconds ago)")
|
||||
ap2.add_argument("--ftp-nat", metavar="ADDR", type=u, default="", help="the NAT address to use for passive connections")
|
||||
|
|
@ -1432,25 +1461,30 @@ def add_webdav(ap):
|
|||
ap2.add_argument("--dav-mac", action="store_true", help="disable apple-garbage filter -- allow macos to create junk files (._* and .DS_Store, .Spotlight-*, .fseventsd, .Trashes, .AppleDouble, __MACOS)")
|
||||
ap2.add_argument("--dav-rt", action="store_true", help="show symlink-destination's lastmodified instead of the link itself; always enabled for recursive listings (volflag=davrt)")
|
||||
ap2.add_argument("--dav-auth", action="store_true", help="force auth for all folders (required by davfs2 when only some folders are world-readable) (volflag=davauth)")
|
||||
ap2.add_argument("--dav-ua1", metavar="PTN", type=u, default=r" kioworker/", help="regex of tricky user-agents which expect 401 from GET requests; disable with [\033[32mno\033[0m] or blank")
|
||||
ap2.add_argument("--dav-ua1", metavar="PTN", type=u, default=r" kioworker/", help="regex of user-agents which ARE webdav-clients, and expect 401 from GET requests; disable with [\033[32mno\033[0m] or blank")
|
||||
ap2.add_argument("--dav-port", metavar="P", type=int, default=0, help="additional port to listen on for misbehaving webdav clients which pretend they are graphical browsers; an alternative/supplement to dav-ua1")
|
||||
ap2.add_argument("--ua-nodav", metavar="PTN", type=u, default=r"^(Mozilla/|NetworkingExtension/|com\.apple\.WebKit)", help="regex of user-agents which are NOT webdav-clients")
|
||||
ap2.add_argument("--p-nodav", metavar="P,P", type=u, default="", help="server-ports (comma-sep.) which are NOT webdav-clients; an alternative/supplement to ua-nodav")
|
||||
|
||||
|
||||
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")
|
||||
ap2.add_argument("--tftp-no-fast", action="store_true", help="debug: disable optimizations")
|
||||
ap2.add_argument("--tftp-lsf", metavar="PTN", type=u, default="\\.?(dir|ls)(\\.txt)?", help="return a directory listing if a file with this name is requested and it does not exist; defaults matches .ls, dir, .dir.txt, ls.txt, ...")
|
||||
ap2.add_argument("--tftp-nols", action="store_true", help="if someone tries to download a directory, return an error instead of showing its directory listing")
|
||||
ap2.add_argument("--tftp-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. Examples: [\033[32mlan\033[0m] or [\033[32m10.89.0.0/16, 192.168.33.0/24\033[0m]")
|
||||
ap2.add_argument("--tftp-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("--tftp-pr", metavar="P-P", type=u, default="", help="the range of UDP ports to use for data transfer, for example \033[32m12000-13000")
|
||||
|
||||
|
||||
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")
|
||||
|
|
@ -1461,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")
|
||||
|
|
@ -1550,11 +1586,12 @@ def add_safety(ap):
|
|||
ap2.add_argument("--no-dot-ren", action="store_true", help="disallow renaming dotfiles; makes it impossible to turn something into a dotfile")
|
||||
ap2.add_argument("--no-logues", action="store_true", help="disable rendering .prologue/.epilogue.html into directory listings")
|
||||
ap2.add_argument("--no-readme", action="store_true", help="disable rendering readme/preadme.md into directory listings")
|
||||
ap2.add_argument("--vague-403", action="store_true", help="send 404 instead of 403 (security through ambiguity, very enterprise)")
|
||||
ap2.add_argument("--vague-403", action="store_true", help="send 404 instead of 403 (security through ambiguity, very enterprise). \033[1;31mWARNING:\033[0m Not compatible with WebDAV")
|
||||
ap2.add_argument("--force-js", action="store_true", help="don't send folder listings as HTML, force clients to use the embedded json instead -- slight protection against misbehaving search engines which ignore \033[33m--no-robots\033[0m")
|
||||
ap2.add_argument("--no-robots", action="store_true", help="adds http and html headers asking search engines to not index anything (volflag=norobots)")
|
||||
ap2.add_argument("--logout", metavar="H", type=float, default=8086.0, help="logout clients after \033[33mH\033[0m hours of inactivity; [\033[32m0.0028\033[0m]=10sec, [\033[32m0.1\033[0m]=6min, [\033[32m24\033[0m]=day, [\033[32m168\033[0m]=week, [\033[32m720\033[0m]=month, [\033[32m8760\033[0m]=year)")
|
||||
ap2.add_argument("--dont-ban", metavar="TXT", type=u, default="no", help="anyone at this accesslevel or above will not get banned: [\033[32mav\033[0m]=admin-in-volume, [\033[32maa\033[0m]=has-admin-anywhere, [\033[32mrw\033[0m]=read-write, [\033[32mauth\033[0m]=authenticated, [\033[32many\033[0m]=disable-all-bans, [\033[32mno\033[0m]=anyone-can-get-banned")
|
||||
ap2.add_argument("--banmsg", metavar="TXT", type=u, default="thank you for playing \u00a0 (see serverlog and readme)", help="the response to send to banned users; can be @ban.html to send the contents of ban.html")
|
||||
ap2.add_argument("--ban-pw", metavar="N,W,B", type=u, default="9,60,1440", help="more than \033[33mN\033[0m wrong passwords in \033[33mW\033[0m minutes = ban for \033[33mB\033[0m minutes; disable with [\033[32mno\033[0m]")
|
||||
ap2.add_argument("--ban-pwc", metavar="N,W,B", type=u, default="5,60,1440", help="more than \033[33mN\033[0m password-changes in \033[33mW\033[0m minutes = ban for \033[33mB\033[0m minutes; disable with [\033[32mno\033[0m]")
|
||||
ap2.add_argument("--ban-404", metavar="N,W,B", type=u, default="50,60,1440", help="hitting more than \033[33mN\033[0m 404's in \033[33mW\033[0m minutes = ban for \033[33mB\033[0m minutes; only affects users who cannot see directory listings because their access is either g/G/h")
|
||||
|
|
@ -1603,6 +1640,7 @@ def add_logging(ap):
|
|||
ap2.add_argument("--no-voldump", action="store_true", help="do not list volumes and permissions on startup")
|
||||
ap2.add_argument("--log-utc", action="store_true", help="do not use local timezone; assume the TZ env-var is UTC (tiny bit faster)")
|
||||
ap2.add_argument("--log-tdec", metavar="N", type=int, default=3, help="timestamp resolution / number of timestamp decimals")
|
||||
ap2.add_argument("--log-date", metavar="TXT", type=u, default="", help="date-format, for example [\033[32m%%Y-%%m-%%d\033[0m] (default is disabled; no date, just HH:MM:SS)")
|
||||
ap2.add_argument("--log-badpwd", metavar="N", type=int, default=2, help="log failed login attempt passwords: 0=terse, 1=plaintext, 2=hashed")
|
||||
ap2.add_argument("--log-badxml", action="store_true", help="log any invalid XML received from a client")
|
||||
ap2.add_argument("--log-conn", action="store_true", help="debug: print tcp-server msgs")
|
||||
|
|
@ -1643,6 +1681,7 @@ def add_thumbnail(ap):
|
|||
ap2.add_argument("--th-ram-max", metavar="GB", type=float, default=th_ram, help="max memory usage (GiB) permitted by thumbnailer; not very accurate")
|
||||
ap2.add_argument("--th-crop", metavar="TXT", type=u, default="y", help="crop thumbnails to 4:3 or keep dynamic height; client can override in UI unless force. [\033[32my\033[0m]=crop, [\033[32mn\033[0m]=nocrop, [\033[32mfy\033[0m]=force-y, [\033[32mfn\033[0m]=force-n (volflag=crop)")
|
||||
ap2.add_argument("--th-x3", metavar="TXT", type=u, default="n", help="show thumbs at 3x resolution; client can override in UI unless force. [\033[32my\033[0m]=yes, [\033[32mn\033[0m]=no, [\033[32mfy\033[0m]=force-yes, [\033[32mfn\033[0m]=force-no (volflag=th3x)")
|
||||
ap2.add_argument("--th-qv", metavar="N", type=int, default=40, help="thumbnail quality (10~90); higher is larger filesize and better quality (volflag=th_qv)")
|
||||
ap2.add_argument("--th-dec", metavar="LIBS", default="vips,pil,raw,ff", help="image decoders, in order of preference")
|
||||
ap2.add_argument("--th-no-jpg", action="store_true", help="disable jpg output")
|
||||
ap2.add_argument("--th-no-webp", action="store_true", help="disable webp output")
|
||||
|
|
@ -1695,7 +1734,9 @@ def add_rss(ap):
|
|||
ap2.add_argument("--rss", action="store_true", help="enable RSS output (experimental) (volflag=rss)")
|
||||
ap2.add_argument("--rss-nf", metavar="HITS", type=int, default=250, help="default number of files to return (url-param 'nf')")
|
||||
ap2.add_argument("--rss-fext", metavar="E,E", type=u, default="", help="default list of file extensions to include (url-param 'fext'); blank=all")
|
||||
ap2.add_argument("--rss-sort", metavar="ORD", type=u, default="m", help="default sort order (url-param 'sort'); [\033[32mm\033[0m]=last-modified [\033[32mu\033[0m]=upload-time [\033[32mn\033[0m]=filename [\033[32ms\033[0m]=filesize; Uppercase=oldest-first. Note that upload-time is 0 for non-uploaded files")
|
||||
ap2.add_argument("--rss-sort", metavar="ORD", type=u, default="m", help="default sort order (url-param 'sort'); [\033[32mm\033[0m]=last-modified [\033[32mu\033[0m]=upload-time [\033[32mn\033[0m]=filename [\033[32ms\033[0m]=filesize; Uppercase=oldest-first. Note that upload-time is 0 for non-uploaded files (volflag=rss_sort)")
|
||||
ap2.add_argument("--rss-fmt-t", metavar="TXT", type=u, default="{fname}", help="title format (url-param 'rss_fmt_t') (volflag=rss_fmt_t)")
|
||||
ap2.add_argument("--rss-fmt-d", metavar="TXT", type=u, default="{artist} - {title}", help="description format (url-param 'rss_fmt_d') (volflag=rss_fmt_d)")
|
||||
|
||||
|
||||
def add_db_general(ap, hcores):
|
||||
|
|
@ -1809,6 +1850,10 @@ def add_ui(ap, retry: int):
|
|||
ap2.add_argument("--ih", action="store_true", help="if a folder contains index.html, show that instead of the directory listing by default (can be changed in the client settings UI, or add ?v to URL for override)")
|
||||
ap2.add_argument("--textfiles", metavar="CSV", type=u, default="txt,nfo,diz,cue,readme", help="file extensions to present as plaintext")
|
||||
ap2.add_argument("--txt-max", metavar="KiB", type=int, default=64, help="max size of embedded textfiles on ?doc= (anything bigger will be lazy-loaded by JS)")
|
||||
ap2.add_argument("--prologues", metavar="T,T", type=u, default=".prologue.html", help="comma-sep. list of filenames to scan for and use as prologues (embed above/before directory listing) (volflag=prologues)")
|
||||
ap2.add_argument("--epilogues", metavar="T,T", type=u, default=".epilogue.html", help="comma-sep. list of filenames to scan for and use as epilogues (embed below/after directory listing) (volflag=epilogues)")
|
||||
ap2.add_argument("--preadmes", metavar="T,T", type=u, default="preadme.md,PREADME.md", help="comma-sep. list of filenames to scan for and use as preadmes (embed above/before directory listing) (volflag=preadmes)")
|
||||
ap2.add_argument("--readmes", metavar="T,T", type=u, default="readme.md,README.md", help="comma-sep. list of filenames to scan for and use as readmes (embed below/after directory listing) (volflag=readmes)")
|
||||
ap2.add_argument("--doctitle", metavar="TXT", type=u, default="copyparty @ --name", help="title / service-name to show in html documents")
|
||||
ap2.add_argument("--bname", metavar="TXT", type=u, default="--name", help="server name (displayed in filebrowser document title)")
|
||||
ap2.add_argument("--pb-url", metavar="URL", type=u, default=URL_PRJ, help="powered-by link; disable with \033[33m-nb\033[0m")
|
||||
|
|
@ -1830,7 +1875,7 @@ def add_ui(ap, retry: int):
|
|||
ap2.add_argument("--ui-noacci", action="store_true", help="hide account-info in the UI (volflag=ui_noacci)")
|
||||
ap2.add_argument("--ui-nosrvi", action="store_true", help="hide server-info in the UI (volflag=ui_nosrvi)")
|
||||
ap2.add_argument("--ui-nonav", action="store_true", help="hide navpane+breadcrumbs (volflag=ui_nonav)")
|
||||
ap2.add_argument("--ui-notree", action="store_true", help="hide navpane in the UI (volflag=ui_nonav)")
|
||||
ap2.add_argument("--ui-notree", action="store_true", help="hide navpane in the UI (volflag=ui_notree)")
|
||||
ap2.add_argument("--ui-nocpla", action="store_true", help="hide cpanel-link in the UI (volflag=ui_nocpla)")
|
||||
ap2.add_argument("--ui-nolbar", action="store_true", help="hide link-bar in the UI (volflag=ui_nolbar)")
|
||||
ap2.add_argument("--ui-noctxb", action="store_true", help="hide context-buttons in the UI (volflag=ui_noctxb)")
|
||||
|
|
@ -1908,6 +1953,7 @@ def run_argparse(
|
|||
add_thumbnail(ap)
|
||||
add_transcoding(ap)
|
||||
add_rss(ap)
|
||||
add_sftp(ap)
|
||||
add_ftp(ap)
|
||||
add_webdav(ap)
|
||||
add_tftp(ap)
|
||||
|
|
@ -1975,7 +2021,7 @@ def main(argv: Optional[list[str]] = None) -> None:
|
|||
|
||||
init_E(E)
|
||||
|
||||
f = '\033[36mcopyparty v{} "\033[35m{}\033[36m" ({})\n{}\033[0;36m\n sqlite {} | jinja {} | pyftpd {} | tftp {}\n\033[0m'
|
||||
f = '\033[36mcopyparty v{} "\033[35m{}\033[36m" ({})\n{}\033[0;36m\n sqlite {} | jinja {} | pyftpd {} | tftp {} | miko {}\n\033[0m'
|
||||
f = f.format(
|
||||
S_VERSION,
|
||||
CODENAME,
|
||||
|
|
@ -1985,6 +2031,7 @@ def main(argv: Optional[list[str]] = None) -> None:
|
|||
JINJA_VER,
|
||||
PYFTPD_VER,
|
||||
PARTFTPY_VER,
|
||||
MIKO_VER,
|
||||
)
|
||||
lprint(f)
|
||||
|
||||
|
|
@ -2109,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("-")]
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
# coding: utf-8
|
||||
|
||||
VERSION = (1, 19, 21)
|
||||
CODENAME = "usernames"
|
||||
BUILD_DT = (2025, 12, 2)
|
||||
VERSION = (1, 20, 1)
|
||||
CODENAME = "sftp is fine too"
|
||||
BUILD_DT = (2026, 1, 9)
|
||||
|
||||
S_VERSION = ".".join(map(str, VERSION))
|
||||
S_BUILD_DT = "{0:04d}-{1:02d}-{2:02d}".format(*BUILD_DT)
|
||||
|
|
|
|||
|
|
@ -93,6 +93,13 @@ LEELOO_DALLAS = "leeloo_dallas"
|
|||
## thanks for coming to my ted talk
|
||||
|
||||
|
||||
UP_MTE_MAP = { # db-order
|
||||
"w": "substr(w,1,16)",
|
||||
"up_ip": "ip",
|
||||
".up_at": "at",
|
||||
"up_by": "un",
|
||||
}
|
||||
|
||||
SEE_LOG = "see log for details"
|
||||
SEESLOG = " (see serverlog for details)"
|
||||
SSEELOG = " ({})".format(SEE_LOG)
|
||||
|
|
@ -160,6 +167,9 @@ class Lim(object):
|
|||
self.vbmax = 0 # volume bytes max
|
||||
self.vnmax = 0 # volume max num files
|
||||
|
||||
self.c_vb_v = 0 # cache: volume bytes used (value)
|
||||
self.c_vb_r = 0 # cache: volume bytes used (ref)
|
||||
|
||||
self.smin = 0 # filesize min
|
||||
self.smax = 0 # filesize max
|
||||
|
||||
|
|
@ -426,6 +436,7 @@ class VFS(object):
|
|||
self.js_htm = ""
|
||||
self.all_vols: dict[str, VFS] = {} # flattened recursive
|
||||
self.all_nodes: dict[str, VFS] = {} # also jumpvols/shares
|
||||
self.all_fvols: dict[str, VFS] = {} # volumes which are files
|
||||
|
||||
if realpath:
|
||||
rp = realpath + ("" if realpath.endswith(os.sep) else os.sep)
|
||||
|
|
@ -886,6 +897,7 @@ class VFS(object):
|
|||
flt: set[str],
|
||||
uname: str,
|
||||
dirs: bool,
|
||||
dots: int,
|
||||
scandir: bool,
|
||||
wrap: bool = True,
|
||||
) -> Generator[dict[str, Any], None, None]:
|
||||
|
|
@ -894,7 +906,7 @@ class VFS(object):
|
|||
# if single folder: the folder itself is the top-level item
|
||||
folder = "" if flt or not wrap else (vpath.split("/")[-1].lstrip(".") or "top")
|
||||
|
||||
g = self.walk(folder, vrem, [], uname, [[True, False]], 1, scandir, False)
|
||||
g = self.walk(folder, vrem, [], uname, [[True, False]], dots, scandir, False)
|
||||
for _, _, vpath, apath, files, rd, vd in g:
|
||||
if flt:
|
||||
files = [x for x in files if x[0] in flt]
|
||||
|
|
@ -1054,9 +1066,11 @@ class AuthSrv(object):
|
|||
self.is_lxc = args.c == ["/z/initcfg"]
|
||||
|
||||
self._vf0b = {
|
||||
"cachectl": self.args.cachectl,
|
||||
"tcolor": self.args.tcolor,
|
||||
"du_iwho": self.args.du_iwho,
|
||||
"shr_who": self.args.shr_who if self.args.shr else "no",
|
||||
"ls_q_m": ("", ""),
|
||||
}
|
||||
self._vf0 = self._vf0b.copy()
|
||||
self._vf0["d2d"] = True
|
||||
|
|
@ -1107,9 +1121,6 @@ class AuthSrv(object):
|
|||
def idp_checkin(
|
||||
self, broker: Optional["BrokerCli"], uname: str, gname: str
|
||||
) -> bool:
|
||||
if uname in self.acct:
|
||||
return False
|
||||
|
||||
if self.idp_usr_gh.get(uname) == gname:
|
||||
return False
|
||||
|
||||
|
|
@ -1457,7 +1468,10 @@ class AuthSrv(object):
|
|||
t = "group [%s] = " % (gn,)
|
||||
t += ", ".join("user [%s]" % (x,) for x in uns)
|
||||
self._l(ln, 5, t)
|
||||
grps[gn] = uns
|
||||
if gn in grps:
|
||||
grps[gn].extend(uns)
|
||||
else:
|
||||
grps[gn] = uns
|
||||
except:
|
||||
t = 'lines inside the [groups] section must be "groupname: user1, user2, user..."'
|
||||
raise Exception(t + SBADCFG)
|
||||
|
|
@ -1589,7 +1603,7 @@ class AuthSrv(object):
|
|||
uns = [x[0] for x in un_gns.items() if grp in x[1]]
|
||||
if grp == "${g}":
|
||||
unames.append(un)
|
||||
elif not uns and not self.args.idp_h_grp:
|
||||
elif not uns and not self.args.idp_h_grp and grp != self.args.grp_all:
|
||||
t = "group [%s] must be defined with --grp argument (or in a [groups] config section)"
|
||||
raise CfgEx(t % (grp,))
|
||||
|
||||
|
|
@ -1812,10 +1826,10 @@ class AuthSrv(object):
|
|||
derive_args(self.args)
|
||||
self.setup_auth_ord()
|
||||
|
||||
if self.args.ipu:
|
||||
if self.args.ipu and not self.args.have_idp_hdrs:
|
||||
# syntax (CIDR=UNAME) is verified in load_ipu
|
||||
zsl = [x.split("=", 1)[1] for x in self.args.ipu]
|
||||
zsl = [x for x in zsl if x not in acct]
|
||||
zsl = [x for x in zsl if x and x not in acct]
|
||||
if zsl:
|
||||
t = "ERROR: unknown users in ipu: %s" % (zsl,)
|
||||
self.log(t, 1)
|
||||
|
|
@ -1901,7 +1915,7 @@ class AuthSrv(object):
|
|||
vol.all_vps.sort(key=lambda x: len(x[0]), reverse=True)
|
||||
vol.root = vfs
|
||||
|
||||
zs = "neversymlink du_iwho"
|
||||
zs = "du_iwho ls_q_m neversymlink"
|
||||
k_ign = set(zs.split())
|
||||
for vol in vfs.all_vols.values():
|
||||
unknown_flags = set()
|
||||
|
|
@ -2384,7 +2398,7 @@ class AuthSrv(object):
|
|||
if vf not in vol.flags:
|
||||
vol.flags[vf] = getattr(self.args, ga)
|
||||
|
||||
zs = "forget_ip gid nrand tail_who th_spec_p u2abort u2ow uid unp_who ups_who zip_who"
|
||||
zs = "forget_ip gid nrand tail_who th_qv th_spec_p u2abort u2ow uid unp_who ups_who zip_who"
|
||||
for k in zs.split():
|
||||
if k in vol.flags:
|
||||
vol.flags[k] = int(vol.flags[k])
|
||||
|
|
@ -2524,6 +2538,18 @@ class AuthSrv(object):
|
|||
t = "WARNING: volume [/%s]: invalid value specified for ext-th: %s"
|
||||
self.log(t % (vol.vpath, etv), 3)
|
||||
|
||||
zsl1 = [x for x in vol.flags["preadmes"].split(",") if x]
|
||||
zsl2 = [x for x in vol.flags["readmes"].split(",") if x]
|
||||
zsl3 = list(set([x.lower() for x in zsl1]))
|
||||
zsl4 = list(set([x.lower() for x in zsl2]))
|
||||
vol.flags["emb_mds"] = [[0, zsl1, zsl3], [1, zsl2, zsl4]]
|
||||
|
||||
zsl1 = [x for x in vol.flags["prologues"].split(",") if x]
|
||||
zsl2 = [x for x in vol.flags["epilogues"].split(",") if x]
|
||||
zsl3 = list(set([x.lower() for x in zsl1]))
|
||||
zsl4 = list(set([x.lower() for x in zsl2]))
|
||||
vol.flags["emb_lgs"] = [[0, zsl1, zsl3], [1, zsl2, zsl4]]
|
||||
|
||||
zs = str(vol.flags.get("html_head") or "")
|
||||
if zs and zs[:1] in "%@":
|
||||
vol.flags["html_head_d"] = zs
|
||||
|
|
@ -2604,18 +2630,16 @@ class AuthSrv(object):
|
|||
vol.flags[k] = int(vol.flags[k])
|
||||
|
||||
if "e2d" not in vol.flags:
|
||||
if "lifetime" in vol.flags:
|
||||
t = 'removing lifetime config from volume "/{}" because e2d is disabled'
|
||||
self.log(t.format(vol.vpath), 1)
|
||||
del vol.flags["lifetime"]
|
||||
zs = "lifetime rss"
|
||||
drop = [x for x in zs.split() if x in vol.flags]
|
||||
|
||||
needs_e2d = [x for x in hooks if x in ("xau", "xiu")]
|
||||
drop = [x for x in needs_e2d if vol.flags.get(x)]
|
||||
if drop:
|
||||
t = 'removing [{}] from volume "/{}" because e2d is disabled'
|
||||
self.log(t.format(", ".join(drop), vol.vpath), 1)
|
||||
for x in drop:
|
||||
vol.flags.pop(x)
|
||||
zs = "xau xiu"
|
||||
drop += [x for x in zs.split() if vol.flags.get(x)]
|
||||
|
||||
for k in drop:
|
||||
t = 'cannot enable [%s] for volume "/%s" because this requires one of the following: e2d / e2ds / e2dsa (either as volflag or global-option)'
|
||||
self.log(t % (k, vol.vpath), 1)
|
||||
vol.flags.pop(k)
|
||||
|
||||
zi = vol.flags.get("lifetime") or 0
|
||||
zi2 = time.time() // (86400 * 365)
|
||||
|
|
@ -2626,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 = {}
|
||||
|
|
@ -2661,6 +2692,16 @@ class AuthSrv(object):
|
|||
self.log(t.format(vol.vpath, mtp), 1)
|
||||
errors = True
|
||||
|
||||
mte = vol.flags.get("mte") or {}
|
||||
up_m = [x for x in UP_MTE_MAP if x in mte]
|
||||
up_q = [UP_MTE_MAP[x] for x in up_m]
|
||||
zs = "select %s from up where rd=? and fn=?" % (", ".join(up_q),)
|
||||
vol.flags["ls_q_m"] = (zs if up_m else "", up_m)
|
||||
|
||||
vfs.all_fvols = {
|
||||
zs: vol for zs, vol in vfs.all_vols.items() if "is_file" in vol.flags
|
||||
}
|
||||
|
||||
for vol in vfs.all_nodes.values():
|
||||
if not vol.flags.get("is_file"):
|
||||
continue
|
||||
|
|
@ -2936,9 +2977,11 @@ class AuthSrv(object):
|
|||
pwds.extend([x.split(":", 1)[1] for x in pwds if ":" in x])
|
||||
if pwds:
|
||||
if self.ah.on:
|
||||
zs = r"(\[H\] pw:.*|[?&]pw=)([^&]+)"
|
||||
zs = r"(\[H\] %s:.*|[?&]%s=)([^&]+)"
|
||||
zs = zs % (self.args.pw_hdr, self.args.pw_urlp)
|
||||
else:
|
||||
zs = r"(\[H\] pw:.*|=)(" + "|".join(pwds) + r")([]&; ]|$)"
|
||||
zs = r"(\[H\] %s:.*|=)(" % (self.args.pw_hdr,)
|
||||
zs += "|".join(pwds) + r")([]&; ]|$)"
|
||||
|
||||
self.re_pwd = re.compile(zs)
|
||||
|
||||
|
|
@ -2994,6 +3037,7 @@ class AuthSrv(object):
|
|||
shn.realpath = s_vfs.canonical(s_rem)
|
||||
|
||||
o_vn, _ = shn._get_share_src("")
|
||||
shn.lim = o_vn.lim
|
||||
shn.flags = o_vn.flags.copy()
|
||||
shn.dbpath = o_vn.dbpath
|
||||
shn.histpath = o_vn.histpath
|
||||
|
|
@ -3296,7 +3340,7 @@ class AuthSrv(object):
|
|||
pwdb = {}
|
||||
else:
|
||||
jtxt = read_utf8(self.log, ap, True)
|
||||
pwdb = json.loads(jtxt)
|
||||
pwdb = json.loads(jtxt) if jtxt.strip() else {}
|
||||
|
||||
pwdb = [x for x in pwdb if x[0] != uname]
|
||||
pwdb.append((uname, self.defpw[uname], hpw))
|
||||
|
|
@ -3320,7 +3364,7 @@ class AuthSrv(object):
|
|||
return
|
||||
|
||||
jtxt = read_utf8(self.log, ap, True)
|
||||
pwdb = json.loads(jtxt)
|
||||
pwdb = json.loads(jtxt) if jtxt.strip() else {}
|
||||
|
||||
useen = set()
|
||||
urst = set()
|
||||
|
|
|
|||
|
|
@ -94,14 +94,18 @@ def vf_vmap() -> dict[str, str]:
|
|||
"th_x3": "th3x",
|
||||
}
|
||||
for k in (
|
||||
"apnd_who",
|
||||
"bup_ck",
|
||||
"cachectl",
|
||||
"casechk",
|
||||
"chmod_d",
|
||||
"chmod_f",
|
||||
"dbd",
|
||||
"du_who",
|
||||
"epilogues",
|
||||
"ufavico",
|
||||
"forget_ip",
|
||||
"fsnt",
|
||||
"hsortn",
|
||||
"html_head",
|
||||
"html_head_s",
|
||||
|
|
@ -122,10 +126,16 @@ def vf_vmap() -> dict[str, str]:
|
|||
"og_tpl",
|
||||
"og_ua",
|
||||
"opds_exts",
|
||||
"prologues",
|
||||
"preadmes",
|
||||
"put_ck",
|
||||
"put_name",
|
||||
"readmes",
|
||||
"mv_retry",
|
||||
"rm_retry",
|
||||
"rss_sort",
|
||||
"rss_fmt_t",
|
||||
"rss_fmt_d",
|
||||
"shr_who",
|
||||
"sort",
|
||||
"tail_fd",
|
||||
|
|
@ -133,6 +143,7 @@ def vf_vmap() -> dict[str, str]:
|
|||
"tail_tmax",
|
||||
"tail_who",
|
||||
"tcolor",
|
||||
"th_qv",
|
||||
"th_spec_p",
|
||||
"txt_eol",
|
||||
"unlist",
|
||||
|
|
@ -201,10 +212,12 @@ flagcats = {
|
|||
"noclone": "take dupe data from clients, even if available on HDD",
|
||||
"nodupe": "rejects existing files (instead of linking/cloning them)",
|
||||
"nodupem": "rejects existing files during moves as well",
|
||||
"casechk=auto": "actively prevent case-insensitive filesystem? y/n",
|
||||
"chmod_d=755": "unix-permission for new dirs/folders",
|
||||
"chmod_f=644": "unix-permission for new files",
|
||||
"uid=573": "change owner of new files/folders to unix-user 573",
|
||||
"gid=999": "change owner of new files/folders to unix-group 999",
|
||||
"fsnt=auto": "filesystem filename traits (lin/win/mac/auto)",
|
||||
"wram": "allow uploading into ramdisks",
|
||||
"sparse": "force use of sparse files, mainly for s3-backed storage",
|
||||
"nosparse": "deny use of sparse files, mainly for slow storage",
|
||||
|
|
@ -220,6 +233,7 @@ flagcats = {
|
|||
"pk": "forces server-side compression, optional arg: xz,9",
|
||||
},
|
||||
"upload rules": {
|
||||
"apnd_who=dw": "who can append? (aw/dw/w/no)",
|
||||
"maxn=250,600": "max 250 uploads over 15min",
|
||||
"maxb=1g,300": "max 1 GiB over 5min (suffixes: b, k, m, g, t)",
|
||||
"vmaxb=1g": "total volume size max 1 GiB (suffixes: b, k, m, g, t)",
|
||||
|
|
@ -266,7 +280,6 @@ flagcats = {
|
|||
"no_db_ip": "never store uploader-IP in the db; disables unpost",
|
||||
"fat32": "avoid excessive reindexing on android sdcardfs",
|
||||
"dbd=[acid|swal|wal|yolo]": "database speed-durability tradeoff",
|
||||
"casechk=auto": "actively prevent case-insensitive filesystem? y/n",
|
||||
"xlink": "cross-volume dupe detection / linking (dangerous)",
|
||||
"xdev": "do not descend into other filesystems",
|
||||
"xvol": "do not follow symlinks leaving the volume root",
|
||||
|
|
@ -289,6 +302,7 @@ flagcats = {
|
|||
"thsize": "thumbnail res; WxH",
|
||||
"crop": "center-cropping (y/n/fy/fn)",
|
||||
"th3x": "3x resolution (y/n/fy/fn)",
|
||||
"th_qv=40": "thumbnail quality (10~90)",
|
||||
"convt": "convert-to-image timeout in seconds",
|
||||
"aconvt": "convert-to-audio timeout in seconds",
|
||||
"th_spec_p=1": "make spectrograms? 0=never 1=fallback 2=always",
|
||||
|
|
@ -329,6 +343,10 @@ flagcats = {
|
|||
"norobots": "kindly asks search engines to leave",
|
||||
"unlistcr": "don't list read-access in controlpanel",
|
||||
"unlistcw": "don't list write-access in controlpanel",
|
||||
"prologues=.prologue.html": "files to embed above/before files",
|
||||
"epilogues=.epilogue.html": "files to embed below/after files",
|
||||
"readmes=readme.md,README.md": "files to embed as readmes",
|
||||
"preadmes=preadme.md,PREADME.md": "files to embed as preadmes",
|
||||
"no_sb_md": "disable js sandbox for markdown files",
|
||||
"no_sb_lg": "disable js sandbox for prologue/epilogue",
|
||||
"sb_md": "enable js sandbox for markdown files (default)",
|
||||
|
|
@ -381,6 +399,12 @@ flagcats = {
|
|||
"tail_tmax=30": "kill connection after 30 sec",
|
||||
"tail_who=2": "restrict ?tail access (1=admins,2=authed,3=everyone)",
|
||||
},
|
||||
"rss": {
|
||||
"rss": "allow '?rss' URL suffix (experimental)",
|
||||
"rss_sort=m": "default sort-order (m/u/n/s)",
|
||||
"rss_fmt_t={fname}": "default title-format",
|
||||
"rss_fmt_d={album},{.tn}": "default description-format",
|
||||
},
|
||||
"others": {
|
||||
"dots": "allow all users with read-access to\nenable the option to show dotfiles in listings",
|
||||
"fk=8": 'generates per-file accesskeys,\nwhich are then required at the "g" permission;\nkeys are invalidated if filesize or inode changes',
|
||||
|
|
@ -388,7 +412,6 @@ flagcats = {
|
|||
"dk=8": 'generates per-directory accesskeys,\nwhich are then required at the "g" permission;\nkeys are invalidated if filesize or inode changes',
|
||||
"dks": "per-directory accesskeys allow browsing into subdirs",
|
||||
"dky": 'allow seeing files (not folders) inside a specific folder\nwith "g" perm, and does not require a valid dirkey to do so',
|
||||
"rss": "allow '?rss' URL suffix (experimental)",
|
||||
"rmagic": "expensive analysis for mimetype accuracy",
|
||||
"shr_who=auth": "who can create shares? no/auth/a",
|
||||
"unp_who=2": "unpost only if same... 1=ip+name, 2=ip, 3=name",
|
||||
|
|
@ -399,6 +422,7 @@ flagcats = {
|
|||
"zipmaxt=no": "reply with 'no' if download-as-zip exceeds max",
|
||||
"zipmaxu": "zip-size-limit does not apply to authenticated users",
|
||||
"nopipe": "disable race-the-beam (download unfinished uploads)",
|
||||
"cachectl=no-cache": "controls caching in webbrowsers",
|
||||
"mv_retry": "ms-windows: timeout for renaming busy files",
|
||||
"rm_retry": "ms-windows: timeout for deleting busy files",
|
||||
"davauth": "ask webdav clients to login for all folders",
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
from __future__ import print_function, unicode_literals
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
import time
|
||||
|
|
@ -9,7 +10,7 @@ import time
|
|||
from .__init__ import ANYWIN, MACOS
|
||||
from .authsrv import AXS, VFS, AuthSrv
|
||||
from .bos import bos
|
||||
from .util import chkcmd, min_ex, undot
|
||||
from .util import chkcmd, json_hesc, min_ex, undot
|
||||
|
||||
if True: # pylint: disable=using-constant-test
|
||||
from typing import Optional, Union
|
||||
|
|
@ -127,6 +128,24 @@ class Fstab(object):
|
|||
|
||||
self.log("mtab has changed; reevaluating support for sparse files")
|
||||
|
||||
try:
|
||||
fuses = [mp for mp, fs in dtab.items() if fs == "fuseblk"]
|
||||
if not fuses or MACOS:
|
||||
raise Exception()
|
||||
try:
|
||||
so, _ = chkcmd(["lsblk", "-nrfo", "FSTYPE,MOUNTPOINT"]) # centos6
|
||||
except:
|
||||
so, _ = chkcmd(["lsblk", "-nrfo", "FSTYPE,MOUNTPOINTS"]) # future
|
||||
for ln in so.split("\n"):
|
||||
zsl = ln.split(" ", 1)
|
||||
if len(zsl) != 2:
|
||||
continue
|
||||
fs, mp = zsl
|
||||
if mp in fuses:
|
||||
dtab[mp] = fs
|
||||
except:
|
||||
pass
|
||||
|
||||
tab1 = list(dtab.items())
|
||||
tab1.sort(key=lambda x: (len(x[0]), x[0]))
|
||||
path1, fs1 = tab1[0]
|
||||
|
|
@ -194,24 +213,53 @@ class Fstab(object):
|
|||
return ret.realpath, ""
|
||||
|
||||
|
||||
_fstab: Optional[Fstab] = None
|
||||
winfs = set(("msdos", "vfat", "ntfs", "exfat"))
|
||||
# "msdos" = vfat on macos
|
||||
|
||||
|
||||
def ramdisk_chk(asrv: AuthSrv) -> None:
|
||||
# should have been in authsrv but that's a circular import
|
||||
global _fstab
|
||||
mods = []
|
||||
ramfs = ("tmpfs", "overlay")
|
||||
log = asrv.log_func or print
|
||||
fstab = Fstab(log, asrv.args, False)
|
||||
if not _fstab:
|
||||
_fstab = Fstab(log, asrv.args, False)
|
||||
for vn in asrv.vfs.all_nodes.values():
|
||||
if not vn.axs.uwrite or "wram" in vn.flags:
|
||||
continue
|
||||
ap = vn.realpath
|
||||
if not ap or os.path.isfile(ap):
|
||||
continue
|
||||
fs, mp = fstab.get(ap)
|
||||
fs, mp = _fstab.get(ap)
|
||||
mp = "/" + mp.strip("/")
|
||||
if fs == "tmpfs" or (mp == "/" and fs in ramfs):
|
||||
mods.append((vn.vpath, ap, fs, mp))
|
||||
vn.axs.uwrite.clear()
|
||||
vn.axs.umove.clear()
|
||||
for un, ztsp in list(vn.uaxs.items()):
|
||||
zsl = list(ztsp)
|
||||
zsl[1] = False
|
||||
zsl[2] = False
|
||||
vn.uaxs[un] = tuple(zsl) # type: ignore
|
||||
if mods:
|
||||
t = "WARNING: write-access was removed from the following volumes because they are not mapped to an actual HDD for storage! All uploaded data would live in RAM only, and all uploaded files would be LOST on next reboot. To allow uploading and ignore this hazard, enable the 'wram' option (global/volflag). List of affected volumes:"
|
||||
t2 = ["\n volume=[/%s], abspath=%r, type=%s, root=%r" % x for x in mods]
|
||||
log("vfs", t + "".join(t2) + "\n", 1)
|
||||
|
||||
assume = "mac" if MACOS else "lin"
|
||||
for vol in asrv.vfs.all_nodes.values():
|
||||
if not vol.realpath or vol.flags.get("is_file"):
|
||||
continue
|
||||
zs = vol.flags["fsnt"].strip()[:3].lower()
|
||||
if ANYWIN and not zs:
|
||||
zs = "win"
|
||||
if zs in ("lin", "win", "mac"):
|
||||
vol.flags["fsnt"] = zs
|
||||
continue
|
||||
fs = _fstab.get(vol.realpath)[0]
|
||||
fs = "win" if fs in winfs else assume
|
||||
htm = json.loads(vol.js_htm)
|
||||
vol.flags["fsnt"] = vol.js_ls["fsnt"] = htm["fsnt"] = fs
|
||||
vol.js_htm = json_hesc(json.dumps(htm))
|
||||
|
|
|
|||
|
|
@ -86,7 +86,7 @@ class FtpAuth(DummyAuthorizer):
|
|||
if args.usernames:
|
||||
alts = ["%s:%s" % (username, password)]
|
||||
else:
|
||||
alts = password, username
|
||||
alts = [password, username]
|
||||
|
||||
for zs in alts:
|
||||
zs = asrv.iacct.get(asrv.ah.hash(zs), "")
|
||||
|
|
@ -174,7 +174,7 @@ class FtpFs(AbstractedFS):
|
|||
t = "Unsupported characters in [{}]"
|
||||
raise FSE(t.format(vpath), 1)
|
||||
|
||||
fn = sanitize_fn(fn or "", "")
|
||||
fn = sanitize_fn(fn or "")
|
||||
vpath = vjoin(rd, fn)
|
||||
vfs, rem = self.hub.asrv.vfs.get(vpath, self.uname, r, w, m, d)
|
||||
if (
|
||||
|
|
@ -249,7 +249,33 @@ class FtpFs(AbstractedFS):
|
|||
need_unlink = False
|
||||
td = 0
|
||||
|
||||
if w and need_unlink:
|
||||
xbu = vfs.flags.get("xbu")
|
||||
if xbu:
|
||||
hr = runhook(
|
||||
self.log,
|
||||
None,
|
||||
self.hub.up2k,
|
||||
"xbu.ftp",
|
||||
xbu,
|
||||
ap,
|
||||
filename,
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
0,
|
||||
0,
|
||||
"1.3.8.7",
|
||||
time.time(),
|
||||
None,
|
||||
)
|
||||
t = hr.get("rejectmsg") or ""
|
||||
if t or hr.get("rc") != 0:
|
||||
if not t:
|
||||
t = "upload blocked by xbu server config: %r" % (filename,)
|
||||
self.log(t, 3)
|
||||
raise FSE(t)
|
||||
|
||||
if w and need_unlink: # type: ignore # !rm
|
||||
assert td # type: ignore # !rm
|
||||
if td >= -1 and td <= self.args.ftp_wt:
|
||||
# within permitted timeframe; allow overwrite or resume
|
||||
|
|
@ -615,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")
|
||||
|
||||
|
|
|
|||
|
|
@ -49,6 +49,9 @@ from .util import (
|
|||
HAVE_SQLITE3,
|
||||
HTTPCODE,
|
||||
UTC,
|
||||
VPTL_MAC,
|
||||
VPTL_OS,
|
||||
VPTL_WIN,
|
||||
Garda,
|
||||
MultipartParser,
|
||||
ODict,
|
||||
|
|
@ -146,32 +149,32 @@ _ = (argparse, threading)
|
|||
|
||||
USED4SEC = {"usedforsecurity": False} if sys.version_info > (3, 9) else {}
|
||||
|
||||
NO_CACHE = {"Cache-Control": "no-cache"}
|
||||
|
||||
ALL_COOKIES = "k304 no304 js idxh dots cppwd cppws".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"
|
||||
BADXFP = ', or change the copyparty global-option "xf-proto" to another header-name to read this value from. Alternatively, if your reverseproxy is not able to provide a header similar to "X-Forwarded-Proto", then you must tell copyparty which protocol to assume by setting global-option --xf-proto-fb to either http or https'
|
||||
BADXFFB = "<b>NOTE: serverlog has a message regarding your reverse-proxy config</b>"
|
||||
|
||||
H_CONN_KEEPALIVE = "Connection: Keep-Alive"
|
||||
H_CONN_CLOSE = "Connection: Close"
|
||||
|
||||
LOGUES = [[0, ".prologue.html"], [1, ".epilogue.html"]]
|
||||
|
||||
READMES = [[0, ["preadme.md", "PREADME.md"]], [1, ["readme.md", "README.md"]]]
|
||||
|
||||
RSS_SORT = {"m": "mt", "u": "at", "n": "fn", "s": "sz"}
|
||||
ACODE2_FMT = set(["opus", "owa", "caf", "mp3", "flac", "wav"])
|
||||
|
||||
A_FILE = os.stat_result(
|
||||
(0o644, -1, -1, 1, 1000, 1000, 8, 0x39230101, 0x39230101, 0x39230101)
|
||||
)
|
||||
|
||||
RE_CC = re.compile(r"[\x00-\x1f]") # search always faster
|
||||
RE_USAFE = re.compile(r'[\x00-\x1f<>"]') # search always faster
|
||||
RE_HSAFE = re.compile(r"[\x00-\x1f<>\"'&]") # search always much faster
|
||||
RE_HOST = re.compile(r"[^][0-9a-zA-Z.:_-]") # search faster <=17ch
|
||||
RE_MHOST = re.compile(r"^[][0-9a-zA-Z.:_-]+$") # match faster >=18ch
|
||||
RE_K = re.compile(r"[^0-9a-zA-Z_-]") # search faster <=17ch
|
||||
RE_HR = re.compile(r"[<>\"'&]")
|
||||
RE_MDV = re.compile(r"(.*)\.([0-9]+\.[0-9]{3})(\.[Mm][Dd])$")
|
||||
RE_RSS_KW = re.compile(r"(\{[^} ]+\})")
|
||||
|
||||
UPARAM_CC_OK = set("doc move tree".split())
|
||||
|
||||
|
|
@ -221,12 +224,11 @@ class HttpCli(object):
|
|||
self.log_func = conn.log_func # mypy404
|
||||
self.log_src = conn.log_src # mypy404
|
||||
self.gen_fk = self._gen_fk if self.args.log_fk else gen_filekey
|
||||
self.tls: bool = hasattr(self.s, "cipher")
|
||||
self.tls = self.is_https = hasattr(self.s, "cipher")
|
||||
self.is_vproxied = bool(self.args.R)
|
||||
|
||||
# placeholders; assigned by run()
|
||||
self.keepalive = False
|
||||
self.is_https = False
|
||||
self.in_hdr_recv = True
|
||||
self.headers: dict[str, str] = {}
|
||||
self.mode = " " # http verb
|
||||
|
|
@ -327,7 +329,7 @@ class HttpCli(object):
|
|||
def run(self) -> bool:
|
||||
"""returns true if connection can be reused"""
|
||||
self.out_headers = {
|
||||
"Vary": "Origin, PW, Cookie",
|
||||
"Vary": self.args.http_vary,
|
||||
"Cache-Control": "no-store, max-age=0",
|
||||
}
|
||||
|
||||
|
|
@ -390,9 +392,6 @@ class HttpCli(object):
|
|||
self.keepalive = "close" not in zs and (
|
||||
self.http_ver != "HTTP/1.0" or zs == "keep-alive"
|
||||
)
|
||||
self.is_https = (
|
||||
self.headers.get("x-forwarded-proto", "").lower() == "https" or self.tls
|
||||
)
|
||||
self.host = self.headers.get("host") or ""
|
||||
if not self.host:
|
||||
if self.s.family == socket.AF_UNIX:
|
||||
|
|
@ -417,7 +416,7 @@ class HttpCli(object):
|
|||
self.bad_xff = True
|
||||
if self.args.rproxy != 9999999:
|
||||
t = "global-option --rproxy %d could not be used (out-of-bounds) for the received header [%s]"
|
||||
self.log(t % (self.args.rproxy, zso), c=3)
|
||||
self.log(t % (self.args.rproxy, zso) + BADXFF2, c=3)
|
||||
else:
|
||||
zsl = [
|
||||
" rproxy: %d if this client's IP-address is [%s]"
|
||||
|
|
@ -426,7 +425,24 @@ class HttpCli(object):
|
|||
]
|
||||
t = 'could not determine the client\'s IP-address because the global-option --rproxy has not been configured, so the request-header [%s] specified by global-option --xff-hdr cannot be used safely! The raw header value was [%s]. Please see the "reverse-proxy" section in the readme. The best approach is to configure your reverse-proxy to give copyparty the exact IP-address to assume (perhaps in another header), but you may also try the following:'
|
||||
t = t % (self.args.xff_hdr, zso)
|
||||
self.log("%s\n\n%s\n" % (t, "\n".join(zsl)), 3)
|
||||
t = "%s\n\n%s\n" % (t, "\n".join(zsl))
|
||||
|
||||
zs = self.headers.get(self.args.xf_proto)
|
||||
t2 = "\nFurthermore, the following request-headers are also relevant, and you should check that the values below are sensible:\n\n request-header [%s] (configured with global-option --xf-proto) has the value [%s]; this should be the protocol that the webbrowser is using, so either 'http' or 'https'"
|
||||
t += t2 % (self.args.xf_proto, zs or "NOT-PROVIDED")
|
||||
if not zs:
|
||||
t += ". Because the header is not provided by the reverse-proxy, you must either fix the reverseproxy config"
|
||||
t += BADXFP
|
||||
zs = self.headers.get(self.args.xf_host)
|
||||
t2 = "\n\n request-header [%s] (configured with global-option --xf-host) has the value [%s]; this should be the website domain or external IP-address which the webbrowser is accessing"
|
||||
t += t2 % (self.args.xf_host, zs or "NOT-PROVIDED")
|
||||
if not zs:
|
||||
zs = self.headers.get("host")
|
||||
t2 = ". Because the header is not provided by the reverse-proxy, copyparty is using the standard [Host] header which has the value [%s]"
|
||||
t += t2 % (zs or "NOT-PROVIDED")
|
||||
if zs:
|
||||
t += ". If that is the address that visitors are supposed to use to access your server -- or, in other words, it is not some internal address you wish to keep secret -- then the current choice of using the [Host] header is fine (usually the case)"
|
||||
self.log(t + "\n\n\n", 3)
|
||||
|
||||
pip = self.conn.addr[0]
|
||||
xffs = self.conn.xff_nm
|
||||
|
|
@ -436,6 +452,7 @@ class HttpCli(object):
|
|||
t += ' Note: if you are behind cloudflare, then this default header is not a good choice; please first make sure your local reverse-proxy (if any) does not allow non-cloudflare IPs from providing cf-* headers, and then add this additional global setting: "--xff-hdr=cf-connecting-ip"'
|
||||
else:
|
||||
t += ' Note: depending on your reverse-proxy, and/or WAF, and/or other intermediates, you may want to read the true client IP from another header by also specifying "--xff-hdr=SomeOtherHeader"'
|
||||
t += BADXFF2
|
||||
|
||||
if "." in pip:
|
||||
zs = ".".join(pip.split(".")[:2]) + ".0.0/16"
|
||||
|
|
@ -448,7 +465,22 @@ class HttpCli(object):
|
|||
else:
|
||||
self.ip = cli_ip
|
||||
self.log_src = self.conn.set_rproxy(self.ip)
|
||||
self.host = self.headers.get("x-forwarded-host") or self.host
|
||||
self.host = self.headers.get(self.args.xf_host, self.host)
|
||||
try:
|
||||
self.is_https = len(self.headers[self.args.xf_proto]) == 5
|
||||
except:
|
||||
if self.args.xf_proto_fb:
|
||||
self.is_https = len(self.args.xf_proto_fb) == 5
|
||||
else:
|
||||
self.bad_xff = True
|
||||
self.host = "example.com"
|
||||
t = 'got proxied request without header "%s" (global-option "xf-proto"). This header must contain either "http" or "https". Either fix your reverse-proxy config to include this header%s%s'
|
||||
self.log(t % (self.args.xf_proto, BADXFP, BADXFF2), 3)
|
||||
|
||||
# the semantics of trusted_xff and bad_xff are different;
|
||||
# trusted_xff is whether the connection came from a trusted reverseproxy,
|
||||
# regardless of whether the client ip detection is correctly configured
|
||||
# (the primary safeguard for idp is --idp-h-key)
|
||||
trusted_xff = True
|
||||
|
||||
m = RE_HOST.search(self.host)
|
||||
|
|
@ -463,6 +495,11 @@ class HttpCli(object):
|
|||
if self.is_banned():
|
||||
return False
|
||||
|
||||
if self.conn.ipar_nm and not self.conn.ipar_nm.map(self.ip):
|
||||
self.log("client rejected (--ipar)", 3)
|
||||
self.terse_reply(b"", 500)
|
||||
return False
|
||||
|
||||
if self.conn.aclose:
|
||||
nka = self.conn.aclose
|
||||
ip = ipnorm(self.ip)
|
||||
|
|
@ -505,8 +542,7 @@ class HttpCli(object):
|
|||
self.loud_reply(t, status=400)
|
||||
return False
|
||||
|
||||
ptn_cc = RE_CC
|
||||
m = ptn_cc.search(self.req)
|
||||
m = RE_USAFE.search(self.req)
|
||||
if m:
|
||||
zs = self.req
|
||||
t = "malicious user; Cc in req0 %r => %r"
|
||||
|
|
@ -528,6 +564,7 @@ class HttpCli(object):
|
|||
vpath = undot(vpath)
|
||||
|
||||
re_k = RE_K
|
||||
ptn_cc = RE_CC
|
||||
k_safe = UPARAM_CC_OK
|
||||
for k in arglist.split("&"):
|
||||
if "=" in k:
|
||||
|
|
@ -610,20 +647,21 @@ class HttpCli(object):
|
|||
self.loud_reply("u wot m8", status=400)
|
||||
return False
|
||||
|
||||
if VPTL_OS:
|
||||
vpath = vpath.translate(VPTL_OS)
|
||||
|
||||
self.uparam = uparam
|
||||
self.cookies = cookies
|
||||
self.vpath = vpath
|
||||
self.vpaths = (
|
||||
self.vpath + "/" if self.trailing_slash and self.vpath else self.vpath
|
||||
)
|
||||
self.vpaths = vpath + "/" if self.trailing_slash and vpath else vpath
|
||||
|
||||
if "qr" in uparam:
|
||||
return self.tx_qr()
|
||||
|
||||
if relchk(self.vpath) and (self.vpath != "*" or self.mode != "OPTIONS"):
|
||||
if "\x00" in vpath or (ANYWIN and ("\n" in vpath or "\r" in vpath)):
|
||||
self.log("illegal relpath; req(%r) => %r" % (self.req, "/" + self.vpath))
|
||||
self.cbonk(self.conn.hsrv.gmal, self.req, "bad_vp", "invalid relpaths")
|
||||
return self.tx_404() and self.keepalive
|
||||
return self.tx_404() and False
|
||||
|
||||
zso = self.headers.get("authorization")
|
||||
bauth = ""
|
||||
|
|
@ -645,7 +683,12 @@ class HttpCli(object):
|
|||
except:
|
||||
pass
|
||||
|
||||
self.pw = uparam.get("pw") or self.headers.get("pw") or bauth or cookie_pw
|
||||
self.pw = (
|
||||
uparam.get(self.args.pw_urlp)
|
||||
or self.headers.get(self.args.pw_hdr)
|
||||
or bauth
|
||||
or cookie_pw
|
||||
)
|
||||
self.uname = (
|
||||
self.asrv.sesa.get(self.pw)
|
||||
or self.asrv.iacct.get(self.asrv.ah.hash(self.pw))
|
||||
|
|
@ -676,6 +719,9 @@ class HttpCli(object):
|
|||
if self.args.idp_h_grp
|
||||
else ""
|
||||
)
|
||||
if self.args.idp_chsub:
|
||||
idp_usr = idp_usr.translate(self.args.idp_chsub_tr)
|
||||
idp_grp = idp_grp.translate(self.args.idp_chsub_tr)
|
||||
|
||||
if not trusted_xff:
|
||||
pip = self.conn.addr[0]
|
||||
|
|
@ -767,6 +813,7 @@ class HttpCli(object):
|
|||
return self.tx_404() and False
|
||||
|
||||
try:
|
||||
assert avn # type: ignore # !rm
|
||||
(
|
||||
self.can_read,
|
||||
self.can_write,
|
||||
|
|
@ -955,13 +1002,13 @@ class HttpCli(object):
|
|||
return False
|
||||
|
||||
self.log("banned for {:.0f} sec".format(rt), 6)
|
||||
self.terse_reply(b"thank you for playing (see serverlog and readme)", 403)
|
||||
self.terse_reply(self.args.banmsg_b, 403)
|
||||
return True
|
||||
|
||||
def permit_caching(self) -> None:
|
||||
cache = self.uparam.get("cache")
|
||||
if cache is None:
|
||||
self.out_headers.update(NO_CACHE)
|
||||
self.out_headers["Cache-Control"] = self.vn.flags["cachectl"]
|
||||
return
|
||||
|
||||
n = 69 if not cache else 604869 if cache == "i" else int(cache)
|
||||
|
|
@ -1138,7 +1185,10 @@ class HttpCli(object):
|
|||
]
|
||||
|
||||
if body:
|
||||
lines.append("Content-Length: " + unicode(len(body)))
|
||||
lines.append(
|
||||
"Content-Type: text/html; charset=utf-8\r\nContent-Length: "
|
||||
+ unicode(len(body))
|
||||
)
|
||||
|
||||
lines.append("\r\n")
|
||||
self.s.sendall("\r\n".join(lines).encode("utf-8") + body)
|
||||
|
|
@ -1155,6 +1205,7 @@ class HttpCli(object):
|
|||
return ""
|
||||
|
||||
kv = {k: zs for k, zs in self.uparam.items() if k not in rm}
|
||||
# no reason to consider args.pw_urlp
|
||||
if "pw" in kv:
|
||||
pw = self.cookies.get("cppws") or self.cookies.get("cppwd")
|
||||
if kv["pw"] == pw:
|
||||
|
|
@ -1168,6 +1219,7 @@ class HttpCli(object):
|
|||
return "?" + "&".join(r)
|
||||
|
||||
def ourlq(self) -> str:
|
||||
# no reason to consider args.pw_urlp
|
||||
skip = ("pw", "h", "k")
|
||||
ret = []
|
||||
for k, v in self.ouparam.items():
|
||||
|
|
@ -1231,12 +1283,15 @@ class HttpCli(object):
|
|||
proto = "https" if self.is_https else "http"
|
||||
good_origins = self.args.acao + ["%s://%s" % (proto, host)]
|
||||
|
||||
if "pw" in ih or re.sub(r"(:[0-9]{1,5})?/?$", "", origin) in good_origins:
|
||||
if (
|
||||
self.args.pw_hdr in ih
|
||||
or re.sub(r"(:[0-9]{1,5})?/?$", "", origin) in good_origins
|
||||
):
|
||||
good_origin = True
|
||||
bad_hdrs = ("",)
|
||||
else:
|
||||
good_origin = False
|
||||
bad_hdrs = ("", "pw")
|
||||
bad_hdrs = ("", self.args.pw_hdr)
|
||||
|
||||
# '*' blocks auth through cookies / WWW-Authenticate;
|
||||
# exact-match for Origin is necessary to unlock those,
|
||||
|
|
@ -1469,7 +1524,7 @@ class HttpCli(object):
|
|||
uv.append(ext)
|
||||
uq += " and ( %s )" % (" or ".join(zsl),)
|
||||
|
||||
zs1 = self.uparam.get("sort", self.args.rss_sort)
|
||||
zs1 = self.uparam.get("sort") or self.args.rss_sort
|
||||
zs2 = zs1.lower()
|
||||
zs = RSS_SORT.get(zs2)
|
||||
if not zs:
|
||||
|
|
@ -1483,10 +1538,11 @@ class HttpCli(object):
|
|||
|
||||
hits = idx.run_query(self.uname, [self.vn], uq, uv, False, False, nmax)[0]
|
||||
|
||||
if "pw" in self.ouparam and "nopw" not in self.ouparam:
|
||||
zs = self.ouparam["pw"]
|
||||
q_pw = "?pw=%s" % (quotep(zs),)
|
||||
a_pw = "&pw=%s" % (quotep(zs),)
|
||||
pwk = self.args.pw_urlp
|
||||
if pwk in self.ouparam and "nopw" not in self.ouparam:
|
||||
zs = self.ouparam[pwk]
|
||||
q_pw = "?%s=%s" % (pwk, quotep(zs))
|
||||
a_pw = "&%s=%s" % (pwk, quotep(zs))
|
||||
for i in hits:
|
||||
i["rp"] += a_pw if "?" in i["rp"] else q_pw
|
||||
else:
|
||||
|
|
@ -1500,8 +1556,8 @@ class HttpCli(object):
|
|||
self.host,
|
||||
)
|
||||
feed = baseurl + self.req[1:]
|
||||
if "pw" in self.ouparam and self.ouparam.get("nopw") == "a":
|
||||
feed = re.sub(r"&pw=[^&]*", "", feed)
|
||||
if pwk in self.ouparam and self.ouparam.get("nopw") == "a":
|
||||
feed = re.sub(r"&%s=[^&]*" % (pwk,), "", feed)
|
||||
if self.is_vproxied:
|
||||
baseurl += self.args.RS
|
||||
efeed = html_escape(feed, True, True)
|
||||
|
|
@ -1542,18 +1598,31 @@ class HttpCli(object):
|
|||
ap = ""
|
||||
use_magic = "rmagic" in self.vn.flags
|
||||
|
||||
tpl_t = self.uparam.get("fmt_t") or self.vn.flags["rss_fmt_t"]
|
||||
tpl_d = self.uparam.get("fmt_d") or self.vn.flags["rss_fmt_d"]
|
||||
kw_t = [[x, x[1:-1]] for x in RE_RSS_KW.findall(tpl_t)]
|
||||
kw_d = [[x, x[1:-1]] for x in RE_RSS_KW.findall(tpl_d)]
|
||||
|
||||
for i in hits:
|
||||
if use_magic:
|
||||
ap = os.path.join(self.vn.realpath, i["rp"])
|
||||
|
||||
tags = i["tags"]
|
||||
iurl = html_escape("%s%s" % (baseurl, i["rp"]), True, True)
|
||||
title = unquotep(i["rp"].split("?")[0].split("/")[-1])
|
||||
title = html_escape(title, True, True)
|
||||
tag_t = str(i["tags"].get("title") or "")
|
||||
tag_a = str(i["tags"].get("artist") or "")
|
||||
desc = "%s - %s" % (tag_a, tag_t) if tag_t and tag_a else (tag_t or tag_a)
|
||||
desc = html_escape(desc, True, True) if desc else title
|
||||
mime = html_escape(guess_mime(title, ap))
|
||||
fname = tags["fname"] = unquotep(i["rp"].split("?")[0].split("/")[-1])
|
||||
title = tpl_t
|
||||
desc = tpl_d
|
||||
for zs1, zs2 in kw_t:
|
||||
title = title.replace(zs1, str(tags.get(zs2, "")))
|
||||
for zs1, zs2 in kw_d:
|
||||
desc = desc.replace(zs1, str(tags.get(zs2, "")))
|
||||
title = html_escape(title.strip(), True, True)
|
||||
if desc.strip(" -,"):
|
||||
desc = html_escape(desc.strip(), True, True)
|
||||
else:
|
||||
desc = title
|
||||
|
||||
mime = html_escape(guess_mime(fname, ap))
|
||||
lmod = formatdate(max(0, i["ts"]))
|
||||
zsa = (iurl, iurl, title, desc, lmod, iurl, mime, i["sz"])
|
||||
zs = (
|
||||
|
|
@ -1723,6 +1792,7 @@ class HttpCli(object):
|
|||
set(),
|
||||
self.uname,
|
||||
True,
|
||||
1,
|
||||
not self.args.no_scandir,
|
||||
wrap=False,
|
||||
)
|
||||
|
|
@ -1777,6 +1847,21 @@ class HttpCli(object):
|
|||
):
|
||||
bfree, btot, _ = get_df(vn.realpath, False)
|
||||
if btot:
|
||||
if "vmaxb" in vn.flags:
|
||||
assert vn.lim # type: ignore # !rm
|
||||
btot = vn.lim.vbmax
|
||||
if bfree == vn.lim.c_vb_r:
|
||||
bfree = min(bfree, max(0, vn.lim.vbmax - vn.lim.c_vb_v))
|
||||
else:
|
||||
try:
|
||||
zi, _ = self.conn.hsrv.broker.ask(
|
||||
"up2k.get_volsizes", [vn.realpath]
|
||||
).get()[0]
|
||||
vn.lim.c_vb_v = zi
|
||||
vn.lim.c_vb_r = bfree
|
||||
bfree = min(bfree, max(0, vn.lim.vbmax - zi))
|
||||
except:
|
||||
pass
|
||||
df = {
|
||||
"quota-available-bytes": str(bfree),
|
||||
"quota-used-bytes": str(btot - bfree),
|
||||
|
|
@ -2363,7 +2448,7 @@ class HttpCli(object):
|
|||
if rnd:
|
||||
fn = rand_name(fdir, fn, rnd)
|
||||
|
||||
fn = sanitize_fn(fn or "", "")
|
||||
fn = sanitize_fn(fn or "")
|
||||
|
||||
path = os.path.join(fdir, fn)
|
||||
|
||||
|
|
@ -2408,7 +2493,12 @@ class HttpCli(object):
|
|||
self.vpath = vjoin(self.vpath, fn)
|
||||
params["fdir"] = fdir
|
||||
|
||||
if is_put and not (self.args.no_dav or self.args.nw) and bos.path.exists(path):
|
||||
if (
|
||||
is_put
|
||||
and not (self.args.no_dav or self.args.nw)
|
||||
and "append" not in self.uparam
|
||||
and bos.path.exists(path)
|
||||
):
|
||||
# allow overwrite if...
|
||||
# * volflag 'daw' is set, or client is definitely webdav
|
||||
# * and account has delete-access
|
||||
|
|
@ -2456,7 +2546,24 @@ class HttpCli(object):
|
|||
else:
|
||||
raise Pebkac(500, "unknown hash alg")
|
||||
|
||||
f, fn = ren_open(fn, *open_a, **params)
|
||||
if "apnd" in self.uparam and not self.args.nw and bos.path.exists(path):
|
||||
zs = vfs.flags["apnd_who"]
|
||||
if (
|
||||
zs == "w"
|
||||
or (zs == "aw" and self.can_admin)
|
||||
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)
|
||||
self.log("upload will append to [%s]" % (zs,))
|
||||
f = open(zs, "ab")
|
||||
else:
|
||||
f, fn = ren_open(fn, *open_a, **params)
|
||||
|
||||
try:
|
||||
path = os.path.join(fdir, fn)
|
||||
post_sz, sha_hex, sha_b64 = copier(reader, f, hasher, 0, self.args.s_wr_slp)
|
||||
|
|
@ -2794,9 +2901,14 @@ class HttpCli(object):
|
|||
raise Pebkac(400, "your client is old; press CTRL-SHIFT-R and try again")
|
||||
|
||||
vfs, rem = self.asrv.vfs.get(self.vpath, self.uname, False, True)
|
||||
fsnt = vfs.flags["fsnt"]
|
||||
if fsnt != "lin":
|
||||
tl = VPTL_WIN if fsnt == "win" else VPTL_MAC
|
||||
rem = rem.translate(tl)
|
||||
name = name.translate(tl)
|
||||
dbv, vrem = vfs.get_dbv(rem)
|
||||
|
||||
name = sanitize_fn(name, "")
|
||||
name = sanitize_fn(name)
|
||||
if (
|
||||
not self.can_read
|
||||
and self.can_write
|
||||
|
|
@ -2814,7 +2926,7 @@ class HttpCli(object):
|
|||
body["addr"] = self.ip
|
||||
body["vcfg"] = dbv.flags
|
||||
|
||||
if not self.can_delete:
|
||||
if not self.can_delete and not body.get("replace") == "skip":
|
||||
body.pop("replace", None)
|
||||
|
||||
if rem:
|
||||
|
|
@ -3292,7 +3404,7 @@ class HttpCli(object):
|
|||
if "nosub" in vfs.flags:
|
||||
raise Pebkac(403, "mkdir is forbidden below this folder")
|
||||
|
||||
rem = sanitize_vpath(rem, "/")
|
||||
rem = sanitize_vpath(rem)
|
||||
fn = vfs.canonical(rem)
|
||||
|
||||
if not nullwrite:
|
||||
|
|
@ -3336,7 +3448,7 @@ class HttpCli(object):
|
|||
t = "you can only create .md files because you don't have the delete-permission"
|
||||
raise Pebkac(400, t)
|
||||
|
||||
sanitized = sanitize_fn(new_file, "")
|
||||
sanitized = sanitize_fn(new_file)
|
||||
fdir = vfs.canonical(rem)
|
||||
fn = os.path.join(fdir, sanitized)
|
||||
|
||||
|
|
@ -3488,7 +3600,7 @@ class HttpCli(object):
|
|||
# fallthrough
|
||||
|
||||
fdir = fdir_base
|
||||
fname = sanitize_fn(p_file or "", "")
|
||||
fname = sanitize_fn(p_file or "")
|
||||
abspath = os.path.join(fdir, fname)
|
||||
suffix = "-%.6f-%s" % (time.time(), dip)
|
||||
if p_file and not nullwrite:
|
||||
|
|
@ -4110,40 +4222,36 @@ class HttpCli(object):
|
|||
self, vn: VFS, abspath: str, lnames: Optional[dict[str, str]]
|
||||
) -> tuple[list[str], list[str]]:
|
||||
logues = ["", ""]
|
||||
if not self.args.no_logues:
|
||||
for n, fn in LOGUES:
|
||||
if lnames is not None and fn not in lnames:
|
||||
continue
|
||||
for n, fns1, fns2 in [] if self.args.no_logues else vn.flags["emb_lgs"]:
|
||||
for fn in fns1 if lnames is None else fns2:
|
||||
if lnames is not None:
|
||||
fn = lnames.get(fn)
|
||||
if not fn:
|
||||
continue
|
||||
fn = "%s/%s" % (abspath, fn)
|
||||
if bos.path.isfile(fn):
|
||||
logues[n] = read_utf8(self.log, fsenc(fn), False)
|
||||
if "exp" in vn.flags:
|
||||
logues[n] = self._expand(
|
||||
logues[n], vn.flags.get("exp_lg") or []
|
||||
)
|
||||
if not bos.path.isfile(fn):
|
||||
continue
|
||||
logues[n] = read_utf8(self.log, fsenc(fn), False)
|
||||
if "exp" in vn.flags:
|
||||
logues[n] = self._expand(logues[n], vn.flags.get("exp_lg") or [])
|
||||
break
|
||||
|
||||
readmes = ["", ""]
|
||||
for n, fns in [] if self.args.no_readme else READMES:
|
||||
for n, fns1, fns2 in [] if self.args.no_readme else vn.flags["emb_mds"]:
|
||||
if logues[n]:
|
||||
continue
|
||||
elif lnames is None:
|
||||
pass
|
||||
elif fns[0] in lnames:
|
||||
fns = [lnames[fns[0]]]
|
||||
else:
|
||||
fns = []
|
||||
|
||||
txt = ""
|
||||
for fn in fns:
|
||||
for fn in fns1 if lnames is None else fns2:
|
||||
if lnames is not None:
|
||||
fn = lnames.get(fn.lower())
|
||||
if not fn:
|
||||
continue
|
||||
fn = "%s/%s" % (abspath, fn)
|
||||
if bos.path.isfile(fn):
|
||||
txt = read_utf8(self.log, fsenc(fn), False)
|
||||
break
|
||||
|
||||
if txt and "exp" in vn.flags:
|
||||
txt = self._expand(txt, vn.flags.get("exp_md") or [])
|
||||
|
||||
readmes[n] = txt
|
||||
if not bos.path.isfile(fn):
|
||||
continue
|
||||
readmes[n] = read_utf8(self.log, fsenc(fn), False)
|
||||
if "exp" in vn.flags:
|
||||
readmes[n] = self._expand(readmes[n], vn.flags.get("exp_md") or [])
|
||||
break
|
||||
|
||||
return logues, readmes
|
||||
|
||||
|
|
@ -4923,6 +5031,9 @@ class HttpCli(object):
|
|||
packer = StreamZip
|
||||
ext = "zip"
|
||||
|
||||
dots = 0 if "nodot" in self.uparam else 1
|
||||
scandir = not self.args.no_scandir
|
||||
|
||||
fn = self.vpath.split("/")[-1] or self.host.split(":")[0]
|
||||
if items:
|
||||
fn = "sel-" + fn
|
||||
|
|
@ -4934,9 +5045,7 @@ class HttpCli(object):
|
|||
maxn = vn.flags.get("zipmaxn_v") or 0
|
||||
nf = 0
|
||||
nb = 0
|
||||
fgen = vn.zipgen(
|
||||
vpath, rem, set(items), self.uname, False, not self.args.no_scandir
|
||||
)
|
||||
fgen = vn.zipgen(vpath, rem, set(items), self.uname, False, dots, scandir)
|
||||
t = "total size exceeds a limit specified in server config"
|
||||
t = vn.flags.get("zipmaxt") or t
|
||||
if maxs and maxn:
|
||||
|
|
@ -4960,9 +5069,7 @@ class HttpCli(object):
|
|||
self.log(repr(cdis))
|
||||
self.send_headers(None, mime=mime, headers={"Content-Disposition": cdis})
|
||||
|
||||
fgen = vn.zipgen(
|
||||
vpath, rem, set(items), self.uname, False, not self.args.no_scandir
|
||||
)
|
||||
fgen = vn.zipgen(vpath, rem, set(items), self.uname, False, dots, scandir)
|
||||
# for f in fgen: print(repr({k: f[k] for k in ["vp", "ap"]}))
|
||||
cfmt = ""
|
||||
if self.thumbcli and not self.args.no_bacode:
|
||||
|
|
@ -5130,7 +5237,7 @@ class HttpCli(object):
|
|||
file_ts = int(max(ts_md, self.E.t0))
|
||||
file_lastmod, do_send, _ = self._chk_lastmod(file_ts)
|
||||
self.out_headers["Last-Modified"] = file_lastmod
|
||||
self.out_headers.update(NO_CACHE)
|
||||
self.out_headers["Cache-Control"] = "no-cache"
|
||||
status = 200 if do_send else 304
|
||||
|
||||
arg_base = "?"
|
||||
|
|
@ -5217,7 +5324,7 @@ class HttpCli(object):
|
|||
defpw = "dave:hunter2" if self.args.usernames else "hunter2"
|
||||
|
||||
vp = (self.uparam["hc"] or "").lstrip("/")
|
||||
pw = self.ouparam.get("pw") or defpw
|
||||
pw = self.ouparam.get(self.args.pw_urlp) or defpw
|
||||
if pw in self.asrv.sesa:
|
||||
pw = defpw
|
||||
|
||||
|
|
@ -5255,6 +5362,13 @@ class HttpCli(object):
|
|||
[("/" + x).rstrip("/") + "/" for x in y]
|
||||
for y in [self.rvol, self.wvol, self.avol]
|
||||
]
|
||||
for zs in self.asrv.vfs.all_fvols:
|
||||
if not zs:
|
||||
continue # webroot
|
||||
zs2 = ("/" + zs).rstrip("/") + "/"
|
||||
for zsl in (rvol, wvol, avol):
|
||||
if zs2 in zsl:
|
||||
zsl[zsl.index(zs2)] = zs2[:-1]
|
||||
|
||||
ups = []
|
||||
now = time.time()
|
||||
|
|
@ -5290,7 +5404,7 @@ class HttpCli(object):
|
|||
"dbwt": None,
|
||||
}
|
||||
|
||||
assert vstate.items and vs # type: ignore # !rm
|
||||
assert vstate is not None and vstate.items and vs # type: ignore # !rm
|
||||
|
||||
dls = dl_list = []
|
||||
if self.conn.hsrv.tdls:
|
||||
|
|
@ -5398,6 +5512,7 @@ class HttpCli(object):
|
|||
no304=self.no304(),
|
||||
k304vis=self.args.k304 > 0,
|
||||
no304vis=self.args.no304 > 0,
|
||||
msg=BADXFFB if hasattr(self, "bad_xff") else "",
|
||||
ver=S_VERSION if show_ver else "",
|
||||
chpw=self.args.chpw and self.uname != "*",
|
||||
ahttps="" if self.is_https else "https://" + self.host + self.req,
|
||||
|
|
@ -5408,7 +5523,7 @@ class HttpCli(object):
|
|||
def setck(self) -> bool:
|
||||
k, v = self.uparam["setck"].split("=", 1)
|
||||
t = 0 if v in ("", "x") else 86400 * 299
|
||||
ck = gencookie(k, v, self.args.R, self.args.cookie_lax, False, t)
|
||||
ck = gencookie(k, v, self.args.R, True, False, t)
|
||||
self.out_headerlist.append(("Set-Cookie", ck))
|
||||
if "cc" in self.ouparam:
|
||||
self.redirect("", "?h#cc")
|
||||
|
|
@ -5420,7 +5535,7 @@ class HttpCli(object):
|
|||
for k in ALL_COOKIES:
|
||||
if k not in self.cookies:
|
||||
continue
|
||||
cookie = gencookie(k, "x", self.args.R, self.args.cookie_lax, False)
|
||||
cookie = gencookie(k, "x", self.args.R, True, False)
|
||||
self.out_headerlist.append(("Set-Cookie", cookie))
|
||||
|
||||
self.redirect("", "?h#cc")
|
||||
|
|
@ -5450,17 +5565,19 @@ class HttpCli(object):
|
|||
# most webdav clients will not send credentials until they
|
||||
# get 401'd, so send a challenge if we're Absolutely Sure
|
||||
# that the client is not a graphical browser
|
||||
if (
|
||||
rc == 403
|
||||
and self.uname == "*"
|
||||
and "sec-fetch-site" not in self.headers
|
||||
and (
|
||||
not self.ua.startswith("Mozilla/")
|
||||
or (self.args.dav_ua1 and self.args.dav_ua1.search(self.ua))
|
||||
)
|
||||
):
|
||||
rc = 401
|
||||
self.out_headers["WWW-Authenticate"] = 'Basic realm="a"'
|
||||
if rc == 403 and self.uname == "*":
|
||||
sport = self.s.getsockname()[1]
|
||||
if self.args.dav_port == sport or (
|
||||
"sec-fetch-site" not in self.headers
|
||||
and self.cookies.get("js") != "y"
|
||||
and sport not in self.args.p_nodav
|
||||
and (
|
||||
not self.args.ua_nodav.search(self.ua)
|
||||
or (self.args.dav_ua1 and self.args.dav_ua1.search(self.ua))
|
||||
)
|
||||
):
|
||||
rc = 401
|
||||
self.out_headers["WWW-Authenticate"] = 'Basic realm="a"'
|
||||
|
||||
t = t.format(self.args.SR)
|
||||
qv = quotep(self.vpaths) + self.ourlq()
|
||||
|
|
@ -5717,17 +5834,18 @@ class HttpCli(object):
|
|||
and (self.uname in vol.axs.uread or self.uname in vol.axs.upget)
|
||||
}
|
||||
|
||||
bad_xff = hasattr(self, "bad_xff")
|
||||
if bad_xff:
|
||||
if hasattr(self, "bad_xff"):
|
||||
allvols = []
|
||||
t = "will not return list of recent uploads" + BADXFF
|
||||
self.log(t, 1)
|
||||
if self.avol:
|
||||
raise Pebkac(500, t)
|
||||
|
||||
x = self.conn.hsrv.broker.ask(
|
||||
"up2k.get_unfinished_by_user", self.uname, "" if bad_xff else self.ip
|
||||
)
|
||||
x = self.conn.hsrv.broker.ask("up2k.get_unfinished_by_user", self.uname, "")
|
||||
else:
|
||||
x = self.conn.hsrv.broker.ask(
|
||||
"up2k.get_unfinished_by_user", self.uname, self.ip
|
||||
)
|
||||
zdsa: dict[str, Any] = x.get()
|
||||
uret: list[dict[str, Any]] = []
|
||||
if "timeout" in zdsa:
|
||||
|
|
@ -5837,7 +5955,7 @@ class HttpCli(object):
|
|||
if shr_dbv:
|
||||
# translate vpaths from share-target to share-url
|
||||
# to satisfy access checks
|
||||
assert shr_vrem.split # type: ignore # !rm
|
||||
assert shr_vrem is not None and shr_vrem.split # type: ignore # !rm
|
||||
vp_shr, vp_vfs = vroots(self.vpath, vjoin(shr_dbv.vpath, shr_vrem))
|
||||
for v in ret:
|
||||
vp = v["vp"]
|
||||
|
|
@ -6206,6 +6324,7 @@ class HttpCli(object):
|
|||
|
||||
fn = quotep(fns[0]) if len(fns) == 1 else ""
|
||||
|
||||
# NOTE: several clients (frontend, party-up) expect url at response[15:]
|
||||
surl = "created share: %s://%s%s%s%s/%s" % (
|
||||
"https" if self.is_https else "http",
|
||||
self.host,
|
||||
|
|
@ -6327,10 +6446,10 @@ class HttpCli(object):
|
|||
eses = ["", ""]
|
||||
rvol = self.rvol
|
||||
wvol = self.wvol
|
||||
allvols = self.asrv.vfs.all_nodes
|
||||
if self.args.have_unlistc:
|
||||
allvols = self.asrv.vfs.all_vols
|
||||
rvol = [x for x in rvol if "unlistcr" not in allvols[x[1:-1]].flags]
|
||||
wvol = [x for x in wvol if "unlistcw" not in allvols[x[1:-1]].flags]
|
||||
rvol = [x for x in rvol if "unlistcr" not in allvols[x].flags]
|
||||
wvol = [x for x in wvol if "unlistcw" not in allvols[x].flags]
|
||||
vols = list(set(rvol + wvol))
|
||||
if self.vpath:
|
||||
zs = "%s/" % (self.vpath,)
|
||||
|
|
@ -6348,6 +6467,7 @@ class HttpCli(object):
|
|||
"tags": e_d,
|
||||
"dt": 0,
|
||||
"name": 0,
|
||||
"perms": allvols[x].get_perms("", self.uname),
|
||||
}
|
||||
for x in sorted(vols)
|
||||
]
|
||||
|
|
@ -6567,6 +6687,8 @@ class HttpCli(object):
|
|||
|
||||
if th_fmt == "p":
|
||||
raise Pebkac(404)
|
||||
elif th_fmt in ACODE2_FMT:
|
||||
raise Pebkac(415)
|
||||
|
||||
return self.tx_ico(rem)
|
||||
|
||||
|
|
@ -6669,6 +6791,21 @@ class HttpCli(object):
|
|||
):
|
||||
free, total, zs = get_df(abspath, False)
|
||||
if total:
|
||||
if "vmaxb" in vn.flags:
|
||||
assert vn.lim # type: ignore # !rm
|
||||
total = vn.lim.vbmax
|
||||
if free == vn.lim.c_vb_r:
|
||||
free = min(free, max(0, vn.lim.vbmax - vn.lim.c_vb_v))
|
||||
else:
|
||||
try:
|
||||
zi, _ = self.conn.hsrv.broker.ask(
|
||||
"up2k.get_volsizes", [vn.realpath]
|
||||
).get()[0]
|
||||
vn.lim.c_vb_v = zi
|
||||
vn.lim.c_vb_r = free
|
||||
free = min(free, max(0, vn.lim.vbmax - zi))
|
||||
except:
|
||||
pass
|
||||
h1 = humansize(free or 0)
|
||||
h2 = humansize(total)
|
||||
srv_info.append("{} free of {}".format(h1, h2))
|
||||
|
|
@ -6971,18 +7108,8 @@ class HttpCli(object):
|
|||
if self.can_admin:
|
||||
up_q = "select substr(w,1,16), ip, at, un from up where rd=? and fn=?"
|
||||
up_m = ["w", "up_ip", ".up_at", "up_by"]
|
||||
elif ".up_at" in mte:
|
||||
if "w" in mte:
|
||||
up_q = "select substr(w,1,16), at from up where rd=? and fn=?"
|
||||
up_m = ["w", ".up_at"]
|
||||
else:
|
||||
up_q = "select at from up where rd=? and fn=?"
|
||||
up_m = [".up_at"]
|
||||
elif "w" in mte:
|
||||
up_q = "select substr(w,1,16) from up where rd=? and fn=?"
|
||||
up_m = ["w"]
|
||||
else:
|
||||
up_q = ""
|
||||
up_q, up_m = vn.flags["ls_q_m"]
|
||||
|
||||
mt_q = "select mt.k, mt.v from up inner join mt on mt.w = substr(up.w,1,16) where up.rd = ? and up.fn = ? and +mt.k != 'x'"
|
||||
for fe in files:
|
||||
|
|
@ -7055,6 +7182,7 @@ class HttpCli(object):
|
|||
os.path.join(abspath, lnames["descript.ion"])
|
||||
):
|
||||
rem = []
|
||||
items = {x["name"].lower(): x for x in files + dirs}
|
||||
with open(os.path.join(abspath, lnames["descript.ion"]), "rb") as f:
|
||||
for bln in [x.strip() for x in f]:
|
||||
try:
|
||||
|
|
@ -7067,12 +7195,9 @@ class HttpCli(object):
|
|||
fn = fn[1:]
|
||||
else:
|
||||
fn, desc = ln.split(" ", 1)
|
||||
fe = next(
|
||||
(x for x in files if x["name"].lower() == fn.lower()), None
|
||||
)
|
||||
if fe:
|
||||
fe["tags"]["descript.ion"] = desc
|
||||
else:
|
||||
try:
|
||||
items[fn.lower()]["tags"]["descript.ion"] = desc
|
||||
except:
|
||||
t = "<li><code>%s</code> %s</li>"
|
||||
rem.append(t % (html_escape(fn), html_escape(desc)))
|
||||
except:
|
||||
|
|
@ -7265,6 +7390,7 @@ class HttpCli(object):
|
|||
ogh["og:site_name"] = zs
|
||||
|
||||
try:
|
||||
assert file is not None # type: ignore # !rm
|
||||
zs1, zs2 = file["tags"]["res"].split("x")
|
||||
file["tags"][".resw"] = zs1
|
||||
file["tags"][".resh"] = zs2
|
||||
|
|
@ -7304,6 +7430,7 @@ class HttpCli(object):
|
|||
}
|
||||
|
||||
try:
|
||||
assert file is not None # type: ignore # !rm
|
||||
for k, v in file["tags"].items():
|
||||
zs = "{{ %s }}" % (k,)
|
||||
title = title.replace(zs, str(v))
|
||||
|
|
@ -7320,6 +7447,7 @@ class HttpCli(object):
|
|||
|
||||
for tag, hname in tagmap.items():
|
||||
try:
|
||||
assert file is not None # type: ignore # !rm
|
||||
v = file["tags"][tag]
|
||||
if not v:
|
||||
continue
|
||||
|
|
|
|||
|
|
@ -62,6 +62,7 @@ class HttpConn(object):
|
|||
self.ipu_iu: Optional[dict[str, str]] = hsrv.ipu_iu
|
||||
self.ipu_nm: Optional[NetMap] = hsrv.ipu_nm
|
||||
self.ipa_nm: Optional[NetMap] = hsrv.ipa_nm
|
||||
self.ipar_nm: Optional[NetMap] = hsrv.ipar_nm
|
||||
self.xff_nm: Optional[NetMap] = hsrv.xff_nm
|
||||
self.xff_lan: NetMap = hsrv.xff_lan # type: ignore
|
||||
self.iphash: HMaccas = hsrv.broker.iphash
|
||||
|
|
|
|||
|
|
@ -201,6 +201,7 @@ class HttpSrv(object):
|
|||
self.ipr = None
|
||||
|
||||
self.ipa_nm = build_netmap(self.args.ipa)
|
||||
self.ipar_nm = build_netmap(self.args.ipar)
|
||||
self.xff_nm = build_netmap(self.args.xff_src)
|
||||
self.xff_lan = build_netmap("lan")
|
||||
|
||||
|
|
|
|||
|
|
@ -110,7 +110,7 @@ class MDNS(MCast):
|
|||
self.ttl = 300
|
||||
|
||||
if not self.args.zm_nwa_1 and DNS_VND:
|
||||
set_avahi_379()
|
||||
set_avahi_379() # type: ignore
|
||||
|
||||
zs = self.args.zm_fqdn or (self.args.name + ".local")
|
||||
zs = zs.replace("--name", self.args.name).rstrip(".") + "."
|
||||
|
|
|
|||
|
|
@ -385,7 +385,7 @@ def get_cover_from_epub(log: "NamedLogger", abspath: str) -> Optional[IO[bytes]]
|
|||
from .dxml import parse_xml
|
||||
|
||||
try:
|
||||
from urlparse import urljoin # Python2
|
||||
from urlparse import urljoin # type: ignore # Python2
|
||||
except ImportError:
|
||||
from urllib.parse import urljoin # Python3
|
||||
|
||||
|
|
@ -523,7 +523,7 @@ class MTag(object):
|
|||
],
|
||||
".tn": ["tracknumber", "trck", "trkn", "track"],
|
||||
"genre": ["genre", "tcon", "\u00a9gen"],
|
||||
"date": [
|
||||
"tdate": [
|
||||
"original-release-date",
|
||||
"release-date",
|
||||
"date",
|
||||
|
|
|
|||
870
copyparty/sftpd.py
Normal file
870
copyparty/sftpd.py
Normal file
|
|
@ -0,0 +1,870 @@
|
|||
# coding: utf-8
|
||||
from __future__ import print_function, unicode_literals
|
||||
|
||||
import errno
|
||||
import hashlib
|
||||
import logging
|
||||
import os
|
||||
import select
|
||||
import socket
|
||||
import time
|
||||
from threading import ExceptHookArgs
|
||||
|
||||
import paramiko
|
||||
import paramiko.common
|
||||
import paramiko.sftp_attr
|
||||
from paramiko.common import AUTH_FAILED, AUTH_SUCCESSFUL
|
||||
from paramiko.sftp import (
|
||||
SFTP_FAILURE,
|
||||
SFTP_NO_SUCH_FILE,
|
||||
SFTP_OK,
|
||||
SFTP_OP_UNSUPPORTED,
|
||||
SFTP_PERMISSION_DENIED,
|
||||
)
|
||||
|
||||
from .__init__ import ANYWIN, TYPE_CHECKING
|
||||
from .authsrv import LEELOO_DALLAS, VFS, AuthSrv
|
||||
from .bos import bos
|
||||
from .util import (
|
||||
FN_EMB,
|
||||
VF_CAREFUL,
|
||||
Daemon,
|
||||
ODict,
|
||||
Pebkac,
|
||||
ipnorm,
|
||||
min_ex,
|
||||
read_utf8,
|
||||
relchk,
|
||||
runhook,
|
||||
sanitize_fn,
|
||||
ub64enc,
|
||||
undot,
|
||||
vjoin,
|
||||
wunlink,
|
||||
)
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from .svchub import SvcHub
|
||||
|
||||
if True: # pylint: disable=using-constant-test
|
||||
from typing import Any, BinaryIO, Optional, Union
|
||||
|
||||
SATTR = paramiko.sftp_attr.SFTPAttributes
|
||||
|
||||
|
||||
class SSH_Srv(paramiko.ServerInterface):
|
||||
def __init__(self, hub: "SvcHub", addr: Any):
|
||||
self.hub = hub
|
||||
self.args = args = hub.args
|
||||
self.log_func = hub.log
|
||||
self.uname = "*"
|
||||
|
||||
self.addr = addr
|
||||
self.ip = addr[0]
|
||||
if self.ip.startswith("::ffff:"):
|
||||
self.ip = self.ip[7:]
|
||||
|
||||
zsl = []
|
||||
if args.sftp_anon:
|
||||
zsl.append("none")
|
||||
if args.sftp_key2u:
|
||||
zsl.append("publickey")
|
||||
if args.sftp_pw or args.sftp_anon:
|
||||
zsl.append("password")
|
||||
self._auths = ",".join(zsl)
|
||||
|
||||
def log(self, msg: str, c: Union[int, str] = 0) -> None:
|
||||
self.hub.log("sftp:%s" % (self.ip,), msg, c)
|
||||
|
||||
def get_allowed_auths(self, username: str) -> str:
|
||||
return self._auths
|
||||
|
||||
def get_banner(self) -> tuple[Optional[str], Optional[str]]:
|
||||
if self.args.sftpv:
|
||||
self.log("get_banner")
|
||||
t = self.args.sftp_banner
|
||||
if not t:
|
||||
return (None, None)
|
||||
if t.startswith("@"):
|
||||
t = read_utf8(self.log, t[1:], False)
|
||||
if t and not t.endswith("\n"):
|
||||
t += "\n"
|
||||
return (t, "en-US")
|
||||
|
||||
def check_channel_request(self, kind: str, chanid: int) -> int:
|
||||
if self.args.sftpv:
|
||||
self.log("channel-request: %r, %r" % (kind, chanid))
|
||||
if kind == "session":
|
||||
return paramiko.common.OPEN_SUCCEEDED
|
||||
return paramiko.common.OPEN_FAILED_ADMINISTRATIVELY_PROHIBITED
|
||||
|
||||
def check_auth_none(self, username: str) -> int:
|
||||
try:
|
||||
return self._check_auth_none(username)
|
||||
except:
|
||||
self.log("unhandled exception: %s" % (min_ex(),), 1)
|
||||
return AUTH_FAILED
|
||||
|
||||
def _check_auth_none(self, uname: str) -> int:
|
||||
args = self.args
|
||||
if uname != args.sftp_anon or not uname:
|
||||
return AUTH_FAILED
|
||||
|
||||
ipn = ipnorm(self.ip)
|
||||
bans = self.hub.bans
|
||||
if ipn in bans:
|
||||
rt = bans[ipn] - time.time()
|
||||
if rt < 0:
|
||||
self.log("client unbanned")
|
||||
del bans[ipn]
|
||||
else:
|
||||
self.log("client is banned")
|
||||
return AUTH_FAILED
|
||||
|
||||
self.uname = "*"
|
||||
self.log("auth-none OK: *")
|
||||
return AUTH_SUCCESSFUL
|
||||
|
||||
def check_auth_password(self, username: str, password: str) -> int:
|
||||
try:
|
||||
return self._check_auth_password(username, password)
|
||||
except:
|
||||
self.log("unhandled exception: %s" % (min_ex(),), 1)
|
||||
return AUTH_FAILED
|
||||
|
||||
def _check_auth_password(self, uname: str, pw: str) -> int:
|
||||
args = self.args
|
||||
if args.sftpv:
|
||||
logpw = pw
|
||||
if args.log_badpwd == 0:
|
||||
logpw = ""
|
||||
elif args.log_badpwd == 2:
|
||||
zb = hashlib.sha512(pw.encode("utf-8", "replace")).digest()
|
||||
logpw = "%" + ub64enc(zb[:12]).decode("ascii")
|
||||
self.log("auth-pw: %r, %r" % (uname, logpw))
|
||||
|
||||
ipn = ipnorm(self.ip)
|
||||
bans = self.hub.bans
|
||||
if ipn in bans:
|
||||
rt = bans[ipn] - time.time()
|
||||
if rt < 0:
|
||||
self.log("client unbanned")
|
||||
del bans[ipn]
|
||||
else:
|
||||
self.log("client is banned")
|
||||
return AUTH_FAILED
|
||||
|
||||
anon = args.sftp_anon
|
||||
if anon and uname == anon:
|
||||
self.uname = "*"
|
||||
self.log("auth-pw OK: *")
|
||||
return AUTH_SUCCESSFUL
|
||||
|
||||
if not args.sftp_pw:
|
||||
return AUTH_FAILED
|
||||
|
||||
if args.usernames:
|
||||
alts = ["%s:%s" % (uname, pw)]
|
||||
else:
|
||||
alts = [pw, uname]
|
||||
|
||||
attempt = "%s:%s" % (uname, pw)
|
||||
uname = ""
|
||||
asrv = self.hub.asrv
|
||||
for zs in alts:
|
||||
zs = asrv.iacct.get(asrv.ah.hash(zs), "")
|
||||
if zs:
|
||||
uname = zs
|
||||
break
|
||||
|
||||
if args.ipu and uname == "*":
|
||||
uname = args.ipu_iu[args.ipu_nm.map(self.ip)]
|
||||
if args.ipr and uname in args.ipr_u:
|
||||
if not args.ipr_u[uname].map(self.ip):
|
||||
logging.warning("username [%s] rejected by --ipr", uname)
|
||||
return AUTH_FAILED
|
||||
|
||||
if not uname or not (asrv.vfs.aread.get(uname) or asrv.vfs.awrite.get(uname)):
|
||||
g = self.hub.gpwd
|
||||
if g.lim:
|
||||
bonk, ip = g.bonk(self.ip, attempt)
|
||||
if bonk:
|
||||
logging.warning("client banned: invalid passwords")
|
||||
bans[self.ip] = bonk
|
||||
try:
|
||||
# only possible if multiprocessing disabled
|
||||
self.hub.broker.httpsrv.bans[ip] = bonk # type: ignore
|
||||
self.hub.broker.httpsrv.nban += 1 # type: ignore
|
||||
except:
|
||||
pass
|
||||
return AUTH_FAILED
|
||||
|
||||
self.uname = uname
|
||||
self.log("auth-pw OK: %s" % (uname,))
|
||||
return AUTH_SUCCESSFUL
|
||||
|
||||
def check_auth_publickey(self, username: str, key: paramiko.PKey) -> int:
|
||||
try:
|
||||
return self._check_auth_publickey(username, key)
|
||||
except:
|
||||
self.log("unhandled exception: %s" % (min_ex(),), 1)
|
||||
return AUTH_FAILED
|
||||
|
||||
def _check_auth_publickey(self, uname: str, key: paramiko.PKey) -> int:
|
||||
args = self.args
|
||||
if args.sftpv:
|
||||
zs = key.get_name() + "," + key.get_base64()[:32]
|
||||
self.log("auth-key: %r, %r" % (uname, zs))
|
||||
|
||||
ipn = ipnorm(self.ip)
|
||||
bans = self.hub.bans
|
||||
if ipn in bans:
|
||||
rt = bans[ipn] - time.time()
|
||||
if rt < 0:
|
||||
self.log("client unbanned")
|
||||
del bans[ipn]
|
||||
else:
|
||||
self.log("client is banned")
|
||||
return AUTH_FAILED
|
||||
|
||||
anon = args.sftp_anon
|
||||
if anon and uname == anon:
|
||||
self.uname = "*"
|
||||
self.log("auth-key OK: *")
|
||||
return AUTH_SUCCESSFUL
|
||||
|
||||
attempt = "%s %s" % (key.get_name(), key.get_base64())
|
||||
ok = args.sftp_key2u.get(attempt) == uname
|
||||
|
||||
if ok and args.ipr and uname in args.ipr_u:
|
||||
if not args.ipr_u[uname].map(self.ip):
|
||||
logging.warning("username [%s] rejected by --ipr", uname)
|
||||
return AUTH_FAILED
|
||||
|
||||
asrv = self.hub.asrv
|
||||
if not ok or not (asrv.vfs.aread.get(uname) or asrv.vfs.awrite.get(uname)):
|
||||
self.log("auth-key REJECTED: %s" % (uname,))
|
||||
return AUTH_FAILED
|
||||
|
||||
self.uname = uname
|
||||
self.log("auth-key OK: %s" % (uname,))
|
||||
return AUTH_SUCCESSFUL
|
||||
|
||||
|
||||
class SFTP_FH(paramiko.SFTPHandle):
|
||||
def __init__(self, flags: int = 0) -> None:
|
||||
self.filename = ""
|
||||
self.readfile: Optional[BinaryIO] = None
|
||||
self.writefile: Optional[BinaryIO] = None
|
||||
super(SFTP_FH, self).__init__(flags)
|
||||
|
||||
def stat(self):
|
||||
try:
|
||||
f = self.readfile or self.writefile
|
||||
return SATTR.from_stat(os.fstat(f.fileno()))
|
||||
except OSError as ex:
|
||||
return paramiko.SFTPServer.convert_errno(ex.errno)
|
||||
|
||||
def chattr(self, attr):
|
||||
# python doesn't have equivalents to fchown or fchmod, so we have to
|
||||
# use the stored filename
|
||||
if not self.writefile:
|
||||
return SFTP_PERMISSION_DENIED
|
||||
try:
|
||||
paramiko.SFTPServer.set_file_attr(self.filename, attr)
|
||||
return SFTP_OK
|
||||
except OSError as ex:
|
||||
return paramiko.SFTPServer.convert_errno(ex.errno)
|
||||
|
||||
|
||||
class SFTP_Srv(paramiko.SFTPServerInterface):
|
||||
def __init__(self, ssh: paramiko.ServerInterface, *a, **ka):
|
||||
super(SFTP_Srv, self).__init__(ssh, *a, **ka)
|
||||
self.ssh = ssh
|
||||
self.ip: str = ssh.ip # type: ignore
|
||||
self.hub: "SvcHub" = ssh.hub # type: ignore
|
||||
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)
|
||||
|
||||
def v2a(
|
||||
self,
|
||||
vpath: str,
|
||||
r: bool = False,
|
||||
w: bool = False,
|
||||
m: bool = False,
|
||||
d: bool = False,
|
||||
) -> tuple[str, VFS, str]:
|
||||
vpath = vpath.replace(os.sep, "/").strip("/")
|
||||
rd, fn = os.path.split(vpath)
|
||||
if relchk(rd):
|
||||
self.log("malicious vpath: %s", vpath)
|
||||
raise Exception("Unsupported characters in [%s]" % (vpath,))
|
||||
|
||||
fn = sanitize_fn(fn or "")
|
||||
vpath = vjoin(rd, fn)
|
||||
vn, rem = self.hub.asrv.vfs.get(vpath, self.uname, r, w, m, d)
|
||||
if (
|
||||
w
|
||||
and fn.lower() in FN_EMB
|
||||
and self.uname not in vn.axs.uread
|
||||
and "wo_up_readme" not in vn.flags
|
||||
):
|
||||
fn = "_wo_" + fn
|
||||
vpath = vjoin(rd, fn)
|
||||
vn, rem = self.hub.asrv.vfs.get(vpath, self.uname, r, w, m, d)
|
||||
|
||||
if not vn.realpath:
|
||||
# return "", vn, rem
|
||||
raise OSError(errno.ENOENT, "no filesystem mounted at [/%s]" % (vpath,))
|
||||
|
||||
if "xdev" in vn.flags or "xvol" in vn.flags:
|
||||
ap = vn.canonical(rem)
|
||||
avn = vn.chk_ap(ap)
|
||||
t = "Permission denied in [{}]"
|
||||
if not avn:
|
||||
raise OSError(errno.EPERM, "permission denied in [/%s]" % (vpath,))
|
||||
|
||||
cr, cw, cm, cd, _, _, _, _, _ = avn.uaxs[self.uname]
|
||||
if r and not cr or w and not cw or m and not cm or d and not cd:
|
||||
raise OSError(errno.EPERM, "permission denied in [/%s]" % (vpath,))
|
||||
|
||||
if "bcasechk" in vn.flags and not vn.casechk(rem, True):
|
||||
raise OSError(errno.ENOENT, "file does not exist case-sensitively")
|
||||
|
||||
return os.path.join(vn.realpath, rem), vn, rem
|
||||
|
||||
def list_folder(self, path: str) -> list[SATTR] | int:
|
||||
try:
|
||||
return self._list_folder(path)
|
||||
except Pebkac as ex:
|
||||
if ex.code == 404:
|
||||
self.log("folder 404: %s" % (path,))
|
||||
return SFTP_NO_SUCH_FILE
|
||||
return SFTP_PERMISSION_DENIED
|
||||
except:
|
||||
self.log("unhandled exception: %s" % (min_ex(),), 1)
|
||||
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 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))
|
||||
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(
|
||||
rem,
|
||||
self.uname,
|
||||
not self.args.no_scandir,
|
||||
[[True, False], [False, True]],
|
||||
throw=True,
|
||||
)
|
||||
ret = [SATTR.from_stat(x[1], filename=x[0]) for x in vfs_ls]
|
||||
for zs, vn2 in vfs_virt.items():
|
||||
if not vn2.realpath:
|
||||
continue
|
||||
st = bos.stat(vn2.realpath)
|
||||
ret.append(SATTR.from_stat(st, filename=zs))
|
||||
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:
|
||||
try:
|
||||
return self._stat(path)
|
||||
except:
|
||||
self.log("unhandled exception: %s" % (min_ex(),), 1)
|
||||
return SFTP_FAILURE
|
||||
|
||||
def lstat(self, path: str) -> SATTR | int:
|
||||
try:
|
||||
return self._stat(path)
|
||||
except:
|
||||
self.log("unhandled exception: %s" % (min_ex(),), 1)
|
||||
return SFTP_FAILURE
|
||||
|
||||
def _stat(self, vp: str) -> SATTR | int:
|
||||
vp = vp.strip("/")
|
||||
try:
|
||||
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 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:
|
||||
try:
|
||||
return self._open(path, flags, attr)
|
||||
except:
|
||||
self.log("unhandled exception: %s" % (min_ex(),), 1)
|
||||
return SFTP_FAILURE
|
||||
|
||||
def _open(self, vp: str, iflag: int, attr: SATTR) -> paramiko.SFTPHandle | int:
|
||||
if ANYWIN:
|
||||
iflag |= os.O_BINARY
|
||||
if iflag & os.O_WRONLY:
|
||||
rd = False
|
||||
wr = True
|
||||
if iflag & os.O_APPEND:
|
||||
smode = "ab"
|
||||
else:
|
||||
smode = "wb"
|
||||
elif iflag & os.O_RDWR:
|
||||
rd = wr = True
|
||||
if iflag & os.O_APPEND:
|
||||
smode = "a+b"
|
||||
else:
|
||||
smode = "r+b"
|
||||
else:
|
||||
rd = True
|
||||
wr = False
|
||||
smode = "rb"
|
||||
|
||||
try:
|
||||
vn, rem = self.asrv.vfs.get(vp, self.uname, rd, wr)
|
||||
ap = os.path.join(vn.realpath, rem)
|
||||
vf = vn.flags
|
||||
except Pebkac as 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)
|
||||
td = time.time() - st.st_mtime
|
||||
need_unlink = True
|
||||
except:
|
||||
need_unlink = False
|
||||
td = 0
|
||||
|
||||
xbu = vn.flags.get("xbu")
|
||||
if xbu:
|
||||
hr = runhook(
|
||||
self.log,
|
||||
None,
|
||||
self.hub.up2k,
|
||||
"xbu.sftp",
|
||||
xbu,
|
||||
ap,
|
||||
vp,
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
0,
|
||||
0,
|
||||
"7.3.8.7",
|
||||
time.time(),
|
||||
None,
|
||||
)
|
||||
t = hr.get("rejectmsg") or ""
|
||||
if t or hr.get("rc") != 0:
|
||||
if not t:
|
||||
t = "upload blocked by xbu server config: %r" % (vp,)
|
||||
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:
|
||||
# within permitted timeframe; allow overwrite or resume
|
||||
do_it = True
|
||||
elif self.args.no_del or self.args.ftp_no_ow:
|
||||
# file too old, or overwrite not allowed; reject
|
||||
do_it = False
|
||||
else:
|
||||
# allow overwrite if user has delete permission
|
||||
do_it = self.uname in vn.axs.udel
|
||||
|
||||
if not do_it:
|
||||
t = "file already exists and no permission to overwrite: %s"
|
||||
self.log(t % (vp,))
|
||||
return SFTP_PERMISSION_DENIED
|
||||
|
||||
# Don't unlink file for append mode
|
||||
elif "a" not in smode:
|
||||
wunlink(self.log, ap, VF_CAREFUL)
|
||||
|
||||
chmod = getattr(attr, "st_mode", None)
|
||||
if chmod is None:
|
||||
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]: %r"
|
||||
self.log(t % (vp, ap, iflag, chmod, ex), 3)
|
||||
return paramiko.SFTPServer.convert_errno(ex.errno)
|
||||
|
||||
if iflag & os.O_CREAT:
|
||||
paramiko.SFTPServer.set_file_attr(ap, attr)
|
||||
|
||||
try:
|
||||
f = os.fdopen(fd, smode)
|
||||
except OSError as ex:
|
||||
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)
|
||||
ret.filename = ap
|
||||
ret.readfile = f if rd else None
|
||||
ret.writefile = f if wr else None
|
||||
return ret
|
||||
|
||||
def remove(self, path: str) -> int:
|
||||
try:
|
||||
return self._remove(path)
|
||||
except:
|
||||
self.log("unhandled exception: %s" % (min_ex(),), 1)
|
||||
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:
|
||||
try:
|
||||
return self._rename(oldpath, newpath)
|
||||
except:
|
||||
self.log("unhandled exception: %s" % (min_ex(),), 1)
|
||||
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("/")
|
||||
dvp = dvp.strip("/")
|
||||
try:
|
||||
self.hub.up2k.handle_mv("", self.uname, self.ip, svp, dvp)
|
||||
return SFTP_OK
|
||||
except Pebkac as ex:
|
||||
t = "denied rename [%s] to [%s]: %s"
|
||||
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:
|
||||
try:
|
||||
return self._mkdir(path, attr)
|
||||
except:
|
||||
self.log("unhandled exception: %s" % (min_ex(),), 1)
|
||||
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)
|
||||
bos.makedirs(ap, vf=vn.flags) # filezilla expects this
|
||||
if attr is not None:
|
||||
paramiko.SFTPServer.set_file_attr(ap, attr)
|
||||
return SFTP_OK
|
||||
except Pebkac as ex:
|
||||
t = "denied mkdir [%s]: %s"
|
||||
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:
|
||||
try:
|
||||
return self._rmdir(path)
|
||||
except:
|
||||
self.log("unhandled exception: %s" % (min_ex(),), 1)
|
||||
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)
|
||||
bos.rmdir(ap)
|
||||
return SFTP_OK
|
||||
except Pebkac as ex:
|
||||
t = "denied rmdir [%s]: %s"
|
||||
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:
|
||||
try:
|
||||
return self._chattr(path, attr)
|
||||
except:
|
||||
self.log("unhandled exception: %s" % (min_ex(),), 1)
|
||||
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)
|
||||
paramiko.SFTPServer.set_file_attr(ap, attr)
|
||||
return SFTP_OK
|
||||
except Pebkac as ex:
|
||||
t = "denied chattr [%s]: %s"
|
||||
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:
|
||||
return SFTP_OP_UNSUPPORTED
|
||||
|
||||
def readlink(self, path: str) -> str | int:
|
||||
return path
|
||||
|
||||
def canonicalize(self, path: str) -> str:
|
||||
return "/%s" % (undot(path),)
|
||||
|
||||
|
||||
class Sftpd(object):
|
||||
def __init__(self, hub: "SvcHub") -> None:
|
||||
self.hub = hub
|
||||
self.args = args = hub.args
|
||||
self.log_func = hub.log
|
||||
self.srv: list[socket.socket] = []
|
||||
self.bound: list[str] = []
|
||||
self.sessions = {}
|
||||
|
||||
ips = args.sftp_i
|
||||
if "::" in ips:
|
||||
ips.append("0.0.0.0")
|
||||
|
||||
ips = [x for x in ips if not x.startswith(("unix:", "fd:"))]
|
||||
|
||||
if args.sftp4:
|
||||
ips = [x for x in ips if ":" not in x]
|
||||
|
||||
if not ips:
|
||||
self.log("cannot start sftp-server; no compatible IPs in -i", 1)
|
||||
return
|
||||
|
||||
self.hostkeys = []
|
||||
hostkeytypes = (
|
||||
("ed25519", "Ed25519Key", {}), # best
|
||||
("ecdsa", "ECDSAKey", {"bits": 384}),
|
||||
("rsa", "RSAKey", {"bits": 4096}),
|
||||
("dsa", "DSSKey", {}), # worst
|
||||
)
|
||||
for fname, aname, opts in hostkeytypes:
|
||||
fpath = "%s/ssh_host_%s_key" % (args.sftp_hostk, fname.lower())
|
||||
try:
|
||||
pkey = getattr(paramiko, aname).from_private_key_file(fpath)
|
||||
except Exception as ex:
|
||||
try:
|
||||
genfun = getattr(paramiko, aname).generate
|
||||
except Exception as ex2:
|
||||
if args.sftpv or fname not in ("dsa", "ed25519"):
|
||||
# dsa dropped in 4.0
|
||||
# ed25519 not supported yet
|
||||
self.log("cannot generate %s hostkey: %r" % (aname, ex2), 3)
|
||||
continue
|
||||
self.log("generating hostkey [%s] due to %r" % (fpath, ex))
|
||||
pkey = genfun(**opts)
|
||||
pkey.write_private_key_file(fpath)
|
||||
pkey = getattr(paramiko, aname).from_private_key_file(fpath)
|
||||
self.hostkeys.append(pkey)
|
||||
if args.sftpv:
|
||||
self.log("loaded hostkey %r" % (pkey,))
|
||||
|
||||
ips = list(ODict.fromkeys(ips)) # dedup
|
||||
|
||||
for ip in ips:
|
||||
self._bind(ip)
|
||||
|
||||
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)
|
||||
|
||||
def _bind(self, ip: str) -> None:
|
||||
port = self.args.sftp
|
||||
try:
|
||||
ipv = socket.AF_INET6 if ":" in ip else socket.AF_INET
|
||||
srv = socket.socket(ipv, socket.SOCK_STREAM)
|
||||
if not ANYWIN or self.args.reuseaddr:
|
||||
srv.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
||||
srv.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
|
||||
srv.settimeout(0) # == srv.setblocking(False)
|
||||
try:
|
||||
srv.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, False)
|
||||
except:
|
||||
pass # will create another ipv4 socket instead
|
||||
if getattr(self.args, "freebind", False):
|
||||
srv.setsockopt(socket.SOL_IP, socket.IP_FREEBIND, 1)
|
||||
srv.bind((ip, port))
|
||||
srv.listen(10)
|
||||
self.srv.append(srv)
|
||||
self.bound.append(ip)
|
||||
except Exception as ex:
|
||||
if ip == "0.0.0.0" and "::" in self.bound:
|
||||
return # dualstack
|
||||
self.log("could not listen on (%s,%s): %r" % (ip, port, ex), 3)
|
||||
|
||||
def _accept(self, srv: socket.socket) -> None:
|
||||
cli, addr = srv.accept()
|
||||
# cli.settimeout(0) # == srv.setblocking(False)
|
||||
self.log("%r is connecting" % (addr,))
|
||||
zs = "sftp-%s" % (addr[0],)
|
||||
# Daemon(self._accept2, zs, (cli, addr))
|
||||
self._accept2(cli, addr)
|
||||
|
||||
def _accept2(self, cli, addr) -> None:
|
||||
tra = paramiko.Transport(cli)
|
||||
for hkey in self.hostkeys:
|
||||
tra.add_server_key(hkey)
|
||||
tra.set_subsystem_handler("sftp", paramiko.SFTPServer, SFTP_Srv)
|
||||
psrv = SSH_Srv(self.hub, addr)
|
||||
try:
|
||||
tra.start_server(server=psrv)
|
||||
except Exception as ex:
|
||||
self.log("%r could not establish connection: %r" % (addr, ex), 3)
|
||||
cli.close()
|
||||
return
|
||||
|
||||
chan = tra.accept()
|
||||
if chan is None:
|
||||
self.log("%r did not open an sftp channel" % (addr,), 3)
|
||||
cli.close()
|
||||
return
|
||||
|
||||
self.sessions[addr] = (chan, tra, psrv)
|
||||
# tra.join()
|
||||
# self.log("%r disconnected" % (addr,))
|
||||
|
||||
def run(self):
|
||||
lgr = logging.getLogger("paramiko.transport")
|
||||
lgr.setLevel(logging.DEBUG if self.args.sftpvv else logging.INFO)
|
||||
|
||||
if self.args.no_poll:
|
||||
fun = self._run_select
|
||||
else:
|
||||
fun = self._run_poll
|
||||
Daemon(fun, "sftpd")
|
||||
|
||||
def _run_select(self):
|
||||
while not self.hub.stopping:
|
||||
rx, _, _ = select.select(self.srv, [], [], 180)
|
||||
for sck in rx:
|
||||
self._accept(sck)
|
||||
|
||||
def _run_poll(self):
|
||||
fd2sck = {}
|
||||
poll = select.poll()
|
||||
for sck in self.srv:
|
||||
fd = sck.fileno()
|
||||
fd2sck[fd] = sck
|
||||
poll.register(fd, select.POLLIN)
|
||||
while not self.hub.stopping:
|
||||
pr = poll.poll(180 * 1000)
|
||||
rx = [fd2sck[x[0]] for x in pr if x[1] & select.POLLIN]
|
||||
for sck in rx:
|
||||
self._accept(sck)
|
||||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -79,6 +79,7 @@ from .util import (
|
|||
start_stackmon,
|
||||
termsize,
|
||||
ub64enc,
|
||||
umktrans,
|
||||
)
|
||||
|
||||
if HAVE_SQLITE3:
|
||||
|
|
@ -219,6 +220,10 @@ class SvcHub(object):
|
|||
self.log("root", t.format(args.j), c=3)
|
||||
args.no_fpool = True
|
||||
|
||||
args.p_nodav = [int(x.strip()) for x in args.p_nodav.split(",") if x]
|
||||
if args.dav_port and args.dav_port not in args.p:
|
||||
args.p.append(args.dav_port)
|
||||
|
||||
for name, arg in (
|
||||
("iobuf", "iobuf"),
|
||||
("s-rd-sz", "s_rd_sz"),
|
||||
|
|
@ -408,6 +413,11 @@ class SvcHub(object):
|
|||
if not args.http_only:
|
||||
zms += "D"
|
||||
|
||||
if args.sftp:
|
||||
from .sftpd import Sftpd
|
||||
|
||||
self.sftpd: Optional[Sftpd] = None
|
||||
|
||||
if args.ftp or args.ftps:
|
||||
from .ftpd import Ftpd
|
||||
|
||||
|
|
@ -419,7 +429,7 @@ class SvcHub(object):
|
|||
|
||||
self.tftpd: Optional[Tftpd] = None
|
||||
|
||||
if args.ftp or args.ftps or args.tftp:
|
||||
if args.sftp or args.ftp or args.ftps or args.tftp:
|
||||
Daemon(self.start_ftpd, "start_tftpd")
|
||||
|
||||
if args.smb:
|
||||
|
|
@ -746,12 +756,28 @@ class SvcHub(object):
|
|||
def start_ftpd(self) -> None:
|
||||
time.sleep(30)
|
||||
|
||||
if hasattr(self, "sftpd") and not self.sftpd:
|
||||
self.restart_sftpd()
|
||||
|
||||
if hasattr(self, "ftpd") and not self.ftpd:
|
||||
self.restart_ftpd()
|
||||
|
||||
if hasattr(self, "tftpd") and not self.tftpd:
|
||||
self.restart_tftpd()
|
||||
|
||||
def restart_sftpd(self) -> None:
|
||||
if not hasattr(self, "sftpd"):
|
||||
return
|
||||
|
||||
from .sftpd import Sftpd
|
||||
|
||||
if self.sftpd:
|
||||
return # todo
|
||||
|
||||
self.sftpd = Sftpd(self)
|
||||
self.sftpd.run()
|
||||
self.log("root", "started SFTPd")
|
||||
|
||||
def restart_ftpd(self) -> None:
|
||||
if not hasattr(self, "ftpd"):
|
||||
return
|
||||
|
|
@ -888,9 +914,9 @@ class SvcHub(object):
|
|||
return
|
||||
|
||||
ar = self.args
|
||||
for _ in range(10 if ar.ftp or ar.ftps else 0):
|
||||
for _ in range(10 if ar.sftp or ar.ftp or ar.ftps else 0):
|
||||
time.sleep(0.03)
|
||||
if self.ftpd:
|
||||
if self.ftpd if ar.ftp or ar.ftps else ar.sftp:
|
||||
break
|
||||
|
||||
if self.tcpsrv.qr:
|
||||
|
|
@ -1087,7 +1113,7 @@ class SvcHub(object):
|
|||
vsa = [x.lower() for x in vsa if x]
|
||||
setattr(al, k + "_set", set(vsa))
|
||||
|
||||
zs = "dav_ua1 sus_urls nonsus_urls ua_nodoc ua_nozip"
|
||||
zs = "dav_ua1 sus_urls nonsus_urls ua_nodav ua_nodoc ua_nozip"
|
||||
for k in zs.split(" "):
|
||||
vs = getattr(al, k)
|
||||
if not vs or vs == "no":
|
||||
|
|
@ -1102,6 +1128,12 @@ class SvcHub(object):
|
|||
else:
|
||||
setattr(al, k, re.compile("^" + vs + "$"))
|
||||
|
||||
if al.banmsg.startswith("@"):
|
||||
with open(al.banmsg[1:], "rb") as f:
|
||||
al.banmsg_b = f.read()
|
||||
else:
|
||||
al.banmsg_b = al.banmsg.encode("utf-8") + b"\n"
|
||||
|
||||
if not al.sus_urls:
|
||||
al.ban_url = "no"
|
||||
elif al.ban_url == "no":
|
||||
|
|
@ -1125,8 +1157,25 @@ class SvcHub(object):
|
|||
except:
|
||||
raise Exception("invalid --idp-hm-usr [%s]" % (zs0,))
|
||||
|
||||
al.ftp_ipa_nm = build_netmap(al.ftp_ipa or al.ipa, True)
|
||||
al.tftp_ipa_nm = build_netmap(al.tftp_ipa or al.ipa, True)
|
||||
zs1 = ""
|
||||
zs2 = ""
|
||||
zs = al.idp_chsub
|
||||
while zs:
|
||||
if zs[:1] != "|":
|
||||
raise Exception("invalid --idp-chsub; expected another | but got " + zs)
|
||||
zs1 += zs[1:2]
|
||||
zs2 += zs[2:3]
|
||||
zs = zs[3:]
|
||||
al.idp_chsub_tr = umktrans(zs1, zs2)
|
||||
|
||||
al.sftp_ipa_nm = build_netmap(al.sftp_ipa or al.ipa or al.ipar, True)
|
||||
al.ftp_ipa_nm = build_netmap(al.ftp_ipa or al.ipa or al.ipar, True)
|
||||
al.tftp_ipa_nm = build_netmap(al.tftp_ipa or al.ipa or al.ipar, True)
|
||||
|
||||
al.sftp_key2u = {
|
||||
"%s %s" % (x[1], x[2]): x[0]
|
||||
for x in [x.split(" ") for x in al.sftp_key or []]
|
||||
}
|
||||
|
||||
mte = ODict.fromkeys(DEF_MTE.split(","), True)
|
||||
al.mte = odfusion(mte, al.mte)
|
||||
|
|
@ -1552,6 +1601,9 @@ class SvcHub(object):
|
|||
with self.log_mutex:
|
||||
dt = datetime.now(self.tz)
|
||||
if dt.day != self.cday or dt.month != self.cmon:
|
||||
if self.args.log_date:
|
||||
zs = dt.strftime(self.args.log_date)
|
||||
self.log_efmt = "%s %s" % (zs, self.log_efmt.split(" ")[-1])
|
||||
zs = "{}\n" if self.no_ansi else "\033[36m{}\033[0m\n"
|
||||
zs = zs.format(dt.strftime("%Y-%m-%d"))
|
||||
print(zs, end="")
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import time
|
|||
from .authsrv import AuthSrv
|
||||
from .bos import bos
|
||||
from .sutil import StreamArc, errdesc
|
||||
from .util import min_ex, sanitize_fn, spack, sunpack, yieldfile, zlib
|
||||
from .util import VPTL_WIN, min_ex, sanitize_to, spack, sunpack, yieldfile, zlib
|
||||
|
||||
if True: # pylint: disable=using-constant-test
|
||||
from typing import Any, Generator, Optional
|
||||
|
|
@ -104,7 +104,7 @@ def gen_hdr(
|
|||
ret += spack(b"<LL", vsz, vsz)
|
||||
|
||||
# windows support (the "?" replace below too)
|
||||
fn = sanitize_fn(fn, "/")
|
||||
fn = sanitize_to(fn, VPTL_WIN)
|
||||
bfn = fn.encode("utf-8" if utf8 else "cp437", "replace").replace(b"?", b"_")
|
||||
|
||||
# add ntfs (0x24) and/or unix (0x10) extrafields for utc, add z64 if requested
|
||||
|
|
|
|||
|
|
@ -422,6 +422,7 @@ class TcpSrv(object):
|
|||
self.hub.broker.say("httpsrv.set_netdevs", self.netdevs)
|
||||
self.hub.start_zeroconf()
|
||||
gencert(self.log, self.args, self.netdevs)
|
||||
self.hub.restart_sftpd()
|
||||
self.hub.restart_ftpd()
|
||||
self.hub.restart_tftpd()
|
||||
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ import time
|
|||
|
||||
from queue import Queue
|
||||
|
||||
from .__init__ import ANYWIN, PY2, TYPE_CHECKING
|
||||
from .__init__ import ANYWIN, PY2, TYPE_CHECKING, unicode
|
||||
from .authsrv import VFS
|
||||
from .bos import bos
|
||||
from .mtag import HAVE_FFMPEG, HAVE_FFPROBE, au_unpk, ffprobe
|
||||
|
|
@ -56,6 +56,56 @@ EXTS_SPEC_SAFE = set("aif aiff flac mp3 opus wav".split())
|
|||
|
||||
PTN_TS = re.compile("^-?[0-9a-f]{8,10}$")
|
||||
|
||||
# for n in {1..100}; do rm -rf /home/ed/Pictures/wp/.hist/th/ ; python3 -m copyparty -qv /home/ed/Pictures/wp/::r --th-no-webp --th-qv $n --th-dec pil >/dev/null 2>&1 & p=$!; printf '\033[A\033[J%3d ' $n; while true; do sleep 0.1; curl -s 127.1:3923 >/dev/null && break; done; curl -s '127.1:3923/?tar=j' >/dev/null ; cat /home/ed/Pictures/wp/.hist/th/1n/bs/1nBsjDetfie1iDq3y2D4YzF5/*.* | wc -c; kill $p; wait >/dev/null 2>&1; done
|
||||
# filesize-equivalent, not quality (ff looks much shittier)
|
||||
FF_JPG_Q = {
|
||||
0: b"30", # 0
|
||||
1: b"30", # 5
|
||||
2: b"30", # 10
|
||||
3: b"30", # 15
|
||||
4: b"28", # 20
|
||||
5: b"21", # 25
|
||||
6: b"17", # 30
|
||||
7: b"15", # 35
|
||||
8: b"13", # 40
|
||||
9: b"12", # 45
|
||||
10: b"11", # 50
|
||||
11: b"10", # 55
|
||||
12: b"9", # 60
|
||||
13: b"8", # 65
|
||||
14: b"7", # 70
|
||||
15: b"6", # 75
|
||||
16: b"5", # 80
|
||||
17: b"4", # 85
|
||||
18: b"3", # 90
|
||||
19: b"2", # 95
|
||||
20: b"2", # 100
|
||||
}
|
||||
# FF_JPG_Q = {xn: ("%d" % (xn,)).encode("ascii") for xn in range(2, 33)}
|
||||
VIPS_JPG_Q = {
|
||||
0: 4, # 0
|
||||
1: 7, # 5
|
||||
2: 12, # 10
|
||||
3: 17, # 15
|
||||
4: 22, # 20
|
||||
5: 27, # 25
|
||||
6: 32, # 30
|
||||
7: 37, # 35
|
||||
8: 42, # 40
|
||||
9: 47, # 45
|
||||
10: 52, # 50
|
||||
11: 56, # 55
|
||||
12: 61, # 60
|
||||
13: 66, # 65
|
||||
14: 71, # 70
|
||||
15: 75, # 75
|
||||
16: 80, # 80
|
||||
17: 85, # 85
|
||||
18: 89, # 90 (vips explodes past this point)
|
||||
19: 91, # 95
|
||||
20: 97, # 100
|
||||
}
|
||||
|
||||
|
||||
try:
|
||||
if os.environ.get("PRTY_NO_PIL"):
|
||||
|
|
@ -308,6 +358,7 @@ class ThumbSrv(object):
|
|||
if not bos.path.exists(inf_path):
|
||||
with open(inf_path, "wb") as f:
|
||||
f.write(afsenc(os.path.dirname(abspath)))
|
||||
self.writevolcfg(histpath)
|
||||
|
||||
self.busy[tpath] = [cond]
|
||||
do_conv = True
|
||||
|
|
@ -351,6 +402,47 @@ class ThumbSrv(object):
|
|||
"ffa": self.fmt_ffa,
|
||||
}
|
||||
|
||||
def volcfgi(self, vn: VFS) -> str:
|
||||
ret = []
|
||||
zs = "th_dec th_no_webp th_no_jpg"
|
||||
for zs in zs.split(" "):
|
||||
ret.append("%s(%s)\n" % (zs, getattr(self.args, zs)))
|
||||
zs = "th_qv thsize th_spec_p convt"
|
||||
for zs in zs.split(" "):
|
||||
ret.append("%s(%s)\n" % (zs, vn.flags.get(zs)))
|
||||
return "".join(ret)
|
||||
|
||||
def volcfga(self, vn: VFS) -> str:
|
||||
ret = []
|
||||
zs = "q_opus q_mp3"
|
||||
for zs in zs.split(" "):
|
||||
ret.append("%s(%s)\n" % (zs, getattr(self.args, zs)))
|
||||
zs = "aconvt"
|
||||
for zs in zs.split(" "):
|
||||
ret.append("%s(%s)\n" % (zs, vn.flags.get(zs)))
|
||||
return "".join(ret)
|
||||
|
||||
def writevolcfg(self, histpath: str) -> None:
|
||||
try:
|
||||
bos.stat(os.path.join(histpath, "th", "cfg.txt"))
|
||||
bos.stat(os.path.join(histpath, "ac", "cfg.txt"))
|
||||
return
|
||||
except:
|
||||
pass
|
||||
cfgi = cfga = ""
|
||||
for vn in self.asrv.vfs.all_vols.values():
|
||||
if vn.histpath == histpath:
|
||||
cfgi = self.volcfgi(vn)
|
||||
cfga = self.volcfga(vn)
|
||||
break
|
||||
t = "writing thumbnailer-config %d,%d to %s"
|
||||
self.log(t % (len(cfgi), len(cfga), histpath))
|
||||
chmod = bos.MKD_700 if self.args.free_umask else bos.MKD_755
|
||||
for cfg, cat in ((cfgi, "th"), (cfga, "ac")):
|
||||
bos.makedirs(os.path.join(histpath, cat), vf=chmod)
|
||||
with open(os.path.join(histpath, cat, "cfg.txt"), "wb") as f:
|
||||
f.write(cfg.encode("utf-8"))
|
||||
|
||||
def wait4ram(self, need: float, ttpath: str) -> None:
|
||||
ram = self.args.th_ram_max
|
||||
if need > ram * 0.99:
|
||||
|
|
@ -529,7 +621,7 @@ class ThumbSrv(object):
|
|||
im.thumbnail(self.getres(vn, fmt))
|
||||
|
||||
fmts = ["RGB", "L"]
|
||||
args = {"quality": 40}
|
||||
args = {"quality": vn.flags["th_qv"]}
|
||||
|
||||
if tpath.endswith(".webp"):
|
||||
# quality 80 = pillow-default
|
||||
|
|
@ -573,7 +665,12 @@ class ThumbSrv(object):
|
|||
raise
|
||||
|
||||
assert img # type: ignore # !rm
|
||||
img.write_to_file(tpath, Q=40)
|
||||
args = {}
|
||||
qv = vn.flags["th_qv"]
|
||||
if tpath.endswith("jpg"):
|
||||
qv = VIPS_JPG_Q[qv // 5]
|
||||
args["optimize_coding"] = True
|
||||
img.write_to_file(tpath, Q=qv, strip=True, **args)
|
||||
|
||||
def conv_raw(self, abspath: str, tpath: str, fmt: str, vn: VFS) -> None:
|
||||
self.wait4ram(0.2, tpath)
|
||||
|
|
@ -607,7 +704,12 @@ class ThumbSrv(object):
|
|||
raise
|
||||
|
||||
assert img # type: ignore # !rm
|
||||
img.write_to_file(tpath, Q=40)
|
||||
args = {}
|
||||
qv = vn.flags["th_qv"]
|
||||
if tpath.endswith("jpg"):
|
||||
qv = VIPS_JPG_Q[qv // 5]
|
||||
args["optimize_coding"] = True
|
||||
img.write_to_file(tpath, Q=qv, strip=True, **args)
|
||||
elif HAVE_PIL:
|
||||
if thumb.format == rawpy.ThumbFormat.BITMAP:
|
||||
im = Image.fromarray(thumb.data, "RGB")
|
||||
|
|
@ -671,12 +773,12 @@ class ThumbSrv(object):
|
|||
if tpath.endswith(".jpg"):
|
||||
cmd += [
|
||||
b"-q:v",
|
||||
b"6", # default=??
|
||||
FF_JPG_Q[vn.flags["th_qv"] // 5], # default=??
|
||||
]
|
||||
else:
|
||||
cmd += [
|
||||
b"-q:v",
|
||||
b"50", # default=75
|
||||
unicode(vn.flags["th_qv"]).encode("ascii"), # default=75
|
||||
b"-compression_level:v",
|
||||
b"6", # default=4, 0=fast, 6=max
|
||||
]
|
||||
|
|
@ -722,7 +824,7 @@ class ThumbSrv(object):
|
|||
if len(lines) > 50:
|
||||
lines = lines[:25] + ["[...]"] + lines[-25:]
|
||||
|
||||
txt = "\n".join(["ff: " + str(x) for x in lines])
|
||||
txt = "\n".join(["ff: " + unicode(x) for x in lines])
|
||||
if len(txt) > 5000:
|
||||
txt = txt[:2500] + "...\nff: [...]\nff: ..." + txt[-2500:]
|
||||
|
||||
|
|
@ -880,12 +982,12 @@ class ThumbSrv(object):
|
|||
if tpath.endswith(".jpg"):
|
||||
cmd += [
|
||||
b"-q:v",
|
||||
b"6", # default=??
|
||||
FF_JPG_Q[vn.flags["th_qv"] // 5], # default=??
|
||||
]
|
||||
else:
|
||||
cmd += [
|
||||
b"-q:v",
|
||||
b"50", # default=75
|
||||
unicode(vn.flags["th_qv"]).encode("ascii"), # default=75
|
||||
b"-compression_level:v",
|
||||
b"6", # default=4, 0=fast, 6=max
|
||||
]
|
||||
|
|
@ -1143,7 +1245,7 @@ class ThumbSrv(object):
|
|||
ret = []
|
||||
for k, vs in raw_tags.items():
|
||||
for v in vs:
|
||||
if len(str(v)) >= 1024:
|
||||
if len(unicode(v)) >= 1024:
|
||||
bv = k.encode("utf-8", "replace")
|
||||
ret += [b"-metadata", bv + b"="]
|
||||
break
|
||||
|
|
@ -1181,6 +1283,28 @@ class ThumbSrv(object):
|
|||
time.sleep(interval)
|
||||
|
||||
def clean(self, histpath: str) -> int:
|
||||
cfgi = cfga = ""
|
||||
for vn in self.asrv.vfs.all_vols.values():
|
||||
if vn.histpath == histpath:
|
||||
cfgi = self.volcfgi(vn)
|
||||
cfga = self.volcfga(vn)
|
||||
break
|
||||
for cfg, cat in ((cfgi, "th"), (cfga, "ac")):
|
||||
if not cfg:
|
||||
continue
|
||||
try:
|
||||
with open(os.path.join(histpath, cat, "cfg.txt"), "rb") as f:
|
||||
oldcfg = f.read().decode("utf-8")
|
||||
except:
|
||||
oldcfg = ""
|
||||
if cfg == oldcfg:
|
||||
continue
|
||||
zs = os.path.join(histpath, cat)
|
||||
if not os.path.exists(zs):
|
||||
continue
|
||||
self.log("thumbnailer-config changed; deleting %s" % (zs,), 3)
|
||||
shutil.rmtree(zs)
|
||||
|
||||
ret = 0
|
||||
for cat in ["th", "ac"]:
|
||||
top = os.path.join(histpath, cat)
|
||||
|
|
@ -1239,7 +1363,7 @@ class ThumbSrv(object):
|
|||
if len(b64) != 24 or len(ts) != 8 or ext not in exts:
|
||||
raise Exception()
|
||||
except:
|
||||
if f != "dir.txt":
|
||||
if f != "dir.txt" and f != "cfg.txt":
|
||||
self.log("foreign file in thumbs dir: [{}]".format(fp), 1)
|
||||
|
||||
continue
|
||||
|
|
|
|||
|
|
@ -1148,7 +1148,7 @@ class Up2k(object):
|
|||
ft = "\033[0;32m{}{:.0}"
|
||||
ff = "\033[0;35m{}{:.0}"
|
||||
fv = "\033[0;36m{}:\033[90m{}"
|
||||
zs = "bcasechk du_iwho ext_th_d html_head html_head_d html_head_s put_name2 mv_re_r mv_re_t rm_re_r rm_re_t srch_re_dots srch_re_nodot zipmax zipmaxn_v zipmaxs_v"
|
||||
zs = "bcasechk du_iwho emb_lgs emb_mds ext_th_d html_head html_head_d html_head_s ls_q_m put_name2 mv_re_r mv_re_t rm_re_r rm_re_t srch_re_dots srch_re_nodot zipmax zipmaxn_v zipmaxs_v"
|
||||
fx = set(zs.split())
|
||||
fd = vf_bmap()
|
||||
fd.update(vf_cmap())
|
||||
|
|
@ -3323,7 +3323,7 @@ class Up2k(object):
|
|||
job["ptop"] = vfs.realpath
|
||||
job["vtop"] = vfs.vpath
|
||||
job["prel"] = rem
|
||||
job["name"] = sanitize_fn(job["name"], "")
|
||||
job["name"] = sanitize_fn(job["name"])
|
||||
ud2 = (vfs.vpath, job["prel"], job["name"])
|
||||
if ud1 != ud2:
|
||||
# print(json.dumps(job, sort_keys=True, indent=4))
|
||||
|
|
@ -3468,7 +3468,15 @@ class Up2k(object):
|
|||
fp = djoin(fdir, fname)
|
||||
|
||||
ow = job.get("replace") and bos.path.exists(fp)
|
||||
if ow and "mt" in str(job["replace"]).lower():
|
||||
if ow:
|
||||
replace_arg = str(job["replace"]).lower()
|
||||
|
||||
if ow and "skip" in replace_arg: # type: ignore
|
||||
self.log("skipping upload, filename already exists: %r" % fp)
|
||||
err = "upload rejected, a file with that name already exists"
|
||||
raise Pebkac(409, err)
|
||||
|
||||
if ow and "mt" in replace_arg: # type: ignore
|
||||
mts = bos.stat(fp).st_mtime
|
||||
mtc = job["lmod"]
|
||||
if mtc < mts:
|
||||
|
|
@ -4174,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):
|
||||
|
|
@ -5186,7 +5195,7 @@ class Up2k(object):
|
|||
job["ptop"] = vfs.realpath
|
||||
job["vtop"] = vfs.vpath
|
||||
job["prel"] = rem
|
||||
job["name"] = sanitize_fn(job["name"], "")
|
||||
job["name"] = sanitize_fn(job["name"])
|
||||
ud2 = (vfs.vpath, job["prel"], job["name"])
|
||||
if ud1 != ud2:
|
||||
self.log("xbu reloc2:%d..." % (depth,), 6)
|
||||
|
|
|
|||
|
|
@ -294,6 +294,23 @@ RE_MEMTOTAL = re.compile("^MemTotal:.* kB")
|
|||
RE_MEMAVAIL = re.compile("^MemAvailable:.* kB")
|
||||
|
||||
|
||||
if PY2:
|
||||
|
||||
def umktrans(s1, s2):
|
||||
return {ord(c1): ord(c2) for c1, c2 in zip(s1, s2)}
|
||||
|
||||
else:
|
||||
umktrans = str.maketrans
|
||||
|
||||
FNTL_WIN = umktrans('<>:|?*"\\/', "<>:|?*"\/")
|
||||
VPTL_WIN = umktrans('<>:|?*"\\', "<>:|?*"\")
|
||||
APTL_WIN = umktrans('<>:|?*"/', "<>:|?*"/")
|
||||
FNTL_MAC = VPTL_MAC = APTL_MAC = umktrans(":", ":")
|
||||
FNTL_OS = FNTL_WIN if ANYWIN else FNTL_MAC if MACOS else None
|
||||
VPTL_OS = VPTL_WIN if ANYWIN else VPTL_MAC if MACOS else None
|
||||
APTL_OS = APTL_WIN if ANYWIN else APTL_MAC if MACOS else None
|
||||
|
||||
|
||||
BOS_SEP = ("%s" % (os.sep,)).encode("ascii")
|
||||
|
||||
|
||||
|
|
@ -335,6 +352,7 @@ HTTPCODE = {
|
|||
411: "Length Required",
|
||||
412: "Precondition Failed",
|
||||
413: "Payload Too Large",
|
||||
415: "Unsupported Media Type",
|
||||
416: "Requested Range Not Satisfiable",
|
||||
422: "Unprocessable Entity",
|
||||
423: "Locked",
|
||||
|
|
@ -360,6 +378,7 @@ IMPLICATIONS = [
|
|||
["tftpvv", "tftpv"],
|
||||
["nodupem", "nodupe"],
|
||||
["no_dupe_m", "no_dupe"],
|
||||
["sftpvv", "sftpv"],
|
||||
["smbw", "smb"],
|
||||
["smb1", "smb"],
|
||||
["smbvvv", "smbvv"],
|
||||
|
|
@ -470,9 +489,9 @@ MAGIC_MAP = {"jpeg": "jpg"}
|
|||
|
||||
DEF_EXP = "self.ip self.ua self.uname self.host cfg.name cfg.logout vf.scan vf.thsize hdr.cf-ipcountry srv.itime srv.htime"
|
||||
|
||||
DEF_MTE = ".files,circle,album,.tn,artist,title,.bpm,key,.dur,.q,.vq,.aq,vc,ac,fmt,res,.fps,ahash,vhash"
|
||||
DEF_MTE = ".files,circle,album,.tn,artist,title,tdate,.bpm,key,.dur,.q,.vq,.aq,vc,ac,fmt,res,.fps,ahash,vhash"
|
||||
|
||||
DEF_MTH = ".vq,.aq,vc,ac,fmt,res,.fps"
|
||||
DEF_MTH = "tdate,.vq,.aq,vc,ac,fmt,res,.fps"
|
||||
|
||||
|
||||
REKOBO_KEY = {
|
||||
|
|
@ -637,22 +656,41 @@ except:
|
|||
JINJA_VER = "(None)"
|
||||
|
||||
try:
|
||||
if os.environ.get("PRTY_NO_PYFTPD"):
|
||||
raise Exception()
|
||||
|
||||
from pyftpdlib.__init__ import __ver__ as PYFTPD_VER
|
||||
except:
|
||||
PYFTPD_VER = "(None)"
|
||||
|
||||
try:
|
||||
if os.environ.get("PRTY_NO_PARTFTPY"):
|
||||
raise Exception()
|
||||
|
||||
from partftpy.__init__ import __version__ as PARTFTPY_VER
|
||||
except:
|
||||
PARTFTPY_VER = "(None)"
|
||||
|
||||
try:
|
||||
if os.environ.get("PRTY_NO_PARAMIKO"):
|
||||
raise Exception()
|
||||
|
||||
from paramiko import __version__ as MIKO_VER
|
||||
except:
|
||||
MIKO_VER = "(None)"
|
||||
|
||||
|
||||
PY_DESC = py_desc()
|
||||
|
||||
VERSIONS = (
|
||||
"copyparty v{} ({})\n{}\n sqlite {} | jinja {} | pyftpd {} | tftp {}".format(
|
||||
S_VERSION, S_BUILD_DT, PY_DESC, SQLITE_VER, JINJA_VER, PYFTPD_VER, PARTFTPY_VER
|
||||
)
|
||||
VERSIONS = "copyparty v{} ({})\n{}\n sqlite {} | jinja {} | pyftpd {} | tftp {} | miko {}".format(
|
||||
S_VERSION,
|
||||
S_BUILD_DT,
|
||||
PY_DESC,
|
||||
SQLITE_VER,
|
||||
JINJA_VER,
|
||||
PYFTPD_VER,
|
||||
PARTFTPY_VER,
|
||||
MIKO_VER,
|
||||
)
|
||||
|
||||
|
||||
|
|
@ -684,7 +722,7 @@ except Exception as ex:
|
|||
ub64dec = base64.urlsafe_b64decode # type: ignore
|
||||
b64enc = base64.b64encode # type: ignore
|
||||
b64dec = base64.b64decode # type: ignore
|
||||
if not PY36:
|
||||
if PY36:
|
||||
print("using fallback base64 codec due to %r" % (ex,))
|
||||
|
||||
|
||||
|
|
@ -2228,38 +2266,30 @@ def undot(path: str) -> str:
|
|||
return "/".join(ret)
|
||||
|
||||
|
||||
def sanitize_fn(fn: str, ok: str) -> str:
|
||||
if "/" not in ok:
|
||||
fn = fn.replace("\\", "/").split("/")[-1]
|
||||
def sanitize_fn(fn: str) -> str:
|
||||
fn = fn.replace("\\", "/").split("/")[-1]
|
||||
if APTL_OS:
|
||||
fn = sanitize_to(fn, APTL_OS)
|
||||
return fn.strip()
|
||||
|
||||
|
||||
def sanitize_to(fn: str, tl: dict[int, int]) -> str:
|
||||
fn = fn.translate(tl)
|
||||
if ANYWIN:
|
||||
remap = [
|
||||
["<", "<"],
|
||||
[">", ">"],
|
||||
[":", ":"],
|
||||
['"', """],
|
||||
["/", "/"],
|
||||
["\\", "\"],
|
||||
["|", "|"],
|
||||
["?", "?"],
|
||||
["*", "*"],
|
||||
]
|
||||
for a, b in [x for x in remap if x[0] not in ok]:
|
||||
fn = fn.replace(a, b)
|
||||
|
||||
bad = ["con", "prn", "aux", "nul"]
|
||||
for n in range(1, 10):
|
||||
bad += ("com%s lpt%s" % (n, n)).split(" ")
|
||||
|
||||
if fn.lower().split(".")[0] in bad:
|
||||
fn = "_" + fn
|
||||
|
||||
return fn.strip()
|
||||
return fn
|
||||
|
||||
|
||||
def sanitize_vpath(vp: str, ok: str) -> str:
|
||||
def sanitize_vpath(vp: str) -> str:
|
||||
if not APTL_OS:
|
||||
return vp
|
||||
parts = vp.replace(os.sep, "/").split("/")
|
||||
ret = [sanitize_fn(x, ok) for x in parts]
|
||||
ret = [sanitize_to(x, APTL_OS) for x in parts]
|
||||
return "/".join(ret)
|
||||
|
||||
|
||||
|
|
@ -2564,12 +2594,12 @@ def pathmod(
|
|||
|
||||
def _w8dec2(txt: bytes) -> str:
|
||||
"""decodes filesystem-bytes to wtf8"""
|
||||
return surrogateescape.decodefilename(txt)
|
||||
return surrogateescape.decodefilename(txt) # type: ignore
|
||||
|
||||
|
||||
def _w8enc2(txt: str) -> bytes:
|
||||
"""encodes wtf8 to filesystem-bytes"""
|
||||
return surrogateescape.encodefilename(txt)
|
||||
return surrogateescape.encodefilename(txt) # type: ignore
|
||||
|
||||
|
||||
def _w8dec3(txt: bytes) -> str:
|
||||
|
|
@ -3627,7 +3657,11 @@ def retchk(
|
|||
|
||||
t = "error {} from [{}]".format(t, c)
|
||||
if serr:
|
||||
t += "\n" + serr
|
||||
if len(serr) > 8192:
|
||||
zs = "%s\n[ ...TRUNCATED... ]\n%s\n[ NOTE: full msg was %d chars ]"
|
||||
serr = zs % (serr[:4096], serr[-4096:].rstrip(), len(serr))
|
||||
serr = serr.replace("\n", "\nstderr: ")
|
||||
t += "\nstderr: " + serr
|
||||
|
||||
if logger:
|
||||
logger(t, color)
|
||||
|
|
@ -4175,7 +4209,12 @@ def wrap(txt: str, maxlen: int, maxlen2: int) -> list[str]:
|
|||
|
||||
|
||||
def termsize() -> tuple[int, int]:
|
||||
# from hashwalk
|
||||
try:
|
||||
w, h = os.get_terminal_size()
|
||||
return w, h
|
||||
except:
|
||||
pass
|
||||
|
||||
env = os.environ
|
||||
|
||||
def ioctl_GWINSZ(fd: int) -> Optional[tuple[int, int]]:
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
"use strict";
|
||||
|
||||
/*!
|
||||
* baguetteBox.js
|
||||
* @author feimosi
|
||||
|
|
@ -5,6 +7,8 @@
|
|||
* @url https://github.com/feimosi/baguetteBox.js
|
||||
*/
|
||||
|
||||
var J_BBX = 1;
|
||||
|
||||
window.baguetteBox = (function () {
|
||||
'use strict';
|
||||
|
||||
|
|
@ -1014,7 +1018,9 @@ window.baguetteBox = (function () {
|
|||
if (index >= imagesElements.length)
|
||||
return bounceAnimation(options.readDirRtl ? 'left' : 'right');
|
||||
|
||||
var orot;
|
||||
try {
|
||||
orot = vidimg().getAttribute('rot');
|
||||
vid().pause();
|
||||
}
|
||||
catch (ex) { }
|
||||
|
|
@ -1032,6 +1038,9 @@ window.baguetteBox = (function () {
|
|||
else if (toast.tag == 'bb-ded')
|
||||
toast.hide();
|
||||
|
||||
if (orot && im.getAttribute('rot') === null)
|
||||
rotn(orot / 90, 1);
|
||||
|
||||
if (options.animation == 'none')
|
||||
unvid(vid());
|
||||
else
|
||||
|
|
@ -1048,7 +1057,7 @@ window.baguetteBox = (function () {
|
|||
}
|
||||
|
||||
var prev_cw = 0, prev_ch = 0, unrot_timer = null;
|
||||
function rotn(n) {
|
||||
function rotn(n, asap) {
|
||||
var el = vidimg(),
|
||||
orot = parseInt(el.getAttribute('rot') || 0),
|
||||
frot = orot + (n || 0) * 90;
|
||||
|
|
@ -1063,6 +1072,8 @@ window.baguetteBox = (function () {
|
|||
if (!n && prev_cw === cw && prev_ch === ch)
|
||||
return; // reflow noop
|
||||
|
||||
clmod(el, 'asap', asap);
|
||||
|
||||
prev_cw = cw;
|
||||
prev_ch = ch;
|
||||
var rot = frot,
|
||||
|
|
@ -1072,8 +1083,13 @@ window.baguetteBox = (function () {
|
|||
dl = el.closest('div').querySelector('figcaption a'),
|
||||
vw = cw,
|
||||
vh = ch - dl.offsetHeight + magic,
|
||||
pmag = Math.min(1, Math.min(vw / ih, vh / iw)),
|
||||
wmag = Math.min(1, Math.min(vw / iw, vh / ih));
|
||||
pmag = Math.min(vw / ih, vh / iw),
|
||||
wmag = Math.min(vw / iw, vh / ih);
|
||||
|
||||
if (!options.bbzoom) {
|
||||
pmag = Math.min(1, pmag);
|
||||
wmag = Math.min(1, wmag);
|
||||
}
|
||||
|
||||
while (rot < 0) rot += 360;
|
||||
while (rot >= 360) rot -= 360;
|
||||
|
|
@ -1117,7 +1133,7 @@ window.baguetteBox = (function () {
|
|||
return;
|
||||
|
||||
clmod(el, 'nt', 1);
|
||||
el.removeAttribute('rot');
|
||||
el.setAttribute('rot', 0);
|
||||
el.removeAttribute("style");
|
||||
rot = el.offsetHeight;
|
||||
clmod(el, 'nt');
|
||||
|
|
@ -1347,3 +1363,5 @@ window.baguetteBox = (function () {
|
|||
destroy: destroyPlugin
|
||||
};
|
||||
})();
|
||||
|
||||
J_BBX = 2;
|
||||
|
|
|
|||
|
|
@ -2166,6 +2166,10 @@ html.noscroll .sbar::-webkit-scrollbar {
|
|||
vertical-align: middle;
|
||||
transition: transform .23s, left .23s, top .23s, width .23s, height .23s;
|
||||
}
|
||||
.full-image img.asap,
|
||||
.full-image video.asap {
|
||||
transition: none;
|
||||
}
|
||||
#bbox-overlay.fill .full-image img,
|
||||
#bbox-overlay.fill .full-image video {
|
||||
width: 100%;
|
||||
|
|
@ -4121,3 +4125,47 @@ html.e #doc {
|
|||
html.e #detree {
|
||||
padding: 0px;
|
||||
}
|
||||
|
||||
#rcm {
|
||||
position: absolute;
|
||||
display: none;
|
||||
background: #fff;
|
||||
background: var(--bg);
|
||||
border: 1px solid var(--bg-u3);
|
||||
outline: none;
|
||||
border-radius: .3rem;
|
||||
box-shadow: 0 0 .3rem var(--bg-d3);
|
||||
z-index: 3;
|
||||
}
|
||||
|
||||
#rcm > * {
|
||||
display: block;
|
||||
}
|
||||
|
||||
#rcm > a {
|
||||
padding: .2rem .3rem;
|
||||
}
|
||||
|
||||
#rcm > .hide {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#rcm > a:hover {
|
||||
background: var(--bg-d2);
|
||||
}
|
||||
|
||||
#rcm > .sep {
|
||||
margin: 0 .2rem;
|
||||
border-bottom: 1px solid var(--a-gray);
|
||||
}
|
||||
|
||||
#tempname {
|
||||
color: var(--fg);
|
||||
background: var(--txt-bg);
|
||||
border: none;
|
||||
box-shadow: 0 0 2px var(--txt-sh);
|
||||
border-bottom: 1px solid #999;
|
||||
border-color: var(--a);
|
||||
border-radius: .2em;
|
||||
padding: .2em .3em;
|
||||
}
|
||||
|
|
@ -53,7 +53,7 @@
|
|||
📝<input type="text" name="name" class="i" placeholder="weekend-plans">
|
||||
<input type="submit" value="new file">
|
||||
</form>
|
||||
<span id="new_mdi"></p>
|
||||
<span id="new_mdi"></span>
|
||||
</div>
|
||||
|
||||
<div id="op_msg" class="opview opbox {% if not ls0 %}act{% endif %}">
|
||||
|
|
@ -129,6 +129,8 @@
|
|||
|
||||
<div id="widget"></div>
|
||||
|
||||
<div id="rcm" tabindex="0"></div>
|
||||
|
||||
<script>
|
||||
var SR = "{{ r }}",
|
||||
CGV1 = {{ cgv1 }},
|
||||
|
|
@ -154,7 +156,13 @@
|
|||
{%- if js %}
|
||||
<script src="{{ js }}_={{ ts }}"></script>
|
||||
{%- endif %}
|
||||
<script>
|
||||
Date.now();function jsldp(a,b){2!=window[a]&&alert("FATAL ERROR: cannot load "+b+".js due to unreliable network or broken reverse-proxy; try CTRL-SHIFT-R")}
|
||||
jsldp("J_UTL","util");
|
||||
jsldp("J_BBX","baguettebox");
|
||||
jsldp("J_BRW","browser");
|
||||
jsldp("J_U2K","up2k");
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
"use strict";
|
||||
|
||||
var J_BRW = 1;
|
||||
|
||||
var XHR = XMLHttpRequest,
|
||||
img_re = /\.(a?png|avif|bmp|gif|heif|jpe?g|jfif|svg|webp|webm|mkv|mp4|m4v|mov)(\?|$)/i;
|
||||
|
||||
|
|
@ -158,7 +160,7 @@ if (1)
|
|||
"ul_par": "parallel uploads:",
|
||||
"ut_rand": "randomize filenames",
|
||||
"ut_u2ts": "copy the last-modified timestamp$Nfrom your filesystem to the server\">📅",
|
||||
"ut_ow": "overwrite existing files on the server?$N🛡️: never (will generate a new filename instead)$N🕒: overwrite if server-file is older than yours$N♻️: always overwrite if the files are different",
|
||||
"ut_ow": "overwrite existing files on the server?$N🛡️: never (will generate a new filename instead)$N🕒: overwrite if server-file is older than yours$N♻️: always overwrite if the files are different$N⏭️: unconditionally skip all existing files",
|
||||
"ut_mt": "continue hashing other files while uploading$N$Nmaybe disable if your CPU or HDD is a bottleneck",
|
||||
"ut_ask": 'ask for confirmation before upload starts">💭',
|
||||
"ut_pot": "improve upload speed on slow devices$Nby making the UI less complex",
|
||||
|
|
@ -223,6 +225,7 @@ if (1)
|
|||
"cl_reset": "reset",
|
||||
"cl_hpick": "tap on column headers to hide in the table below",
|
||||
"cl_hcancel": "column hiding aborted",
|
||||
"cl_rcm": "right-click menu",
|
||||
|
||||
"ct_grid": '田 the grid',
|
||||
"ct_ttips": '◔ ◡ ◔">ℹ️ tooltips',
|
||||
|
|
@ -265,6 +268,8 @@ if (1)
|
|||
"cdt_lim": "max number of files to show in a folder",
|
||||
"cdt_ask": "when scrolling to the bottom,$Ninstead of loading more files,$Nask what to do",
|
||||
"cdt_hsort": "how many sorting rules (<code>,sorthref</code>) to include in media-URLs. Setting this to 0 will also ignore sorting-rules included in media links when clicking them",
|
||||
"cdt_ren": "enable custom right-click menu, you can still access the regular menu by pressing the shift key and right-clicking",
|
||||
"cdt_rdb": "show the regular right-click menu when the custom one is already open and right-clicking again",
|
||||
|
||||
"tt_entree": "show navpane (directory tree sidebar)$NHotkey: B",
|
||||
"tt_detree": "show breadcrumbs$NHotkey: B",
|
||||
|
|
@ -335,6 +340,7 @@ if (1)
|
|||
"mm_eunk": "Unknown Errol",
|
||||
"mm_e404": "Could not play audio; error 404: File not found.",
|
||||
"mm_e403": "Could not play audio; error 403: Access denied.\n\nTry pressing F5 to reload, maybe you got logged out",
|
||||
"mm_e415": "Could not play audio; error 415: File transcoding failed; check server logs.",
|
||||
"mm_e500": "Could not play audio; error 500: Check server logs.",
|
||||
"mm_e5xx": "Could not play audio; server error ",
|
||||
"mm_nof": "not finding any more audio files nearby",
|
||||
|
|
@ -422,9 +428,10 @@ if (1)
|
|||
"fcc_warn": 'copied {0} items to clipboard\n\nbut: only <b>this</b> browser-tab can paste them\n(since the selection is so absolutely massive)',
|
||||
|
||||
"fp_apply": "use these names",
|
||||
"fp_skip": "skip conflicts", // TLNote: "skip existing names" (filenames taken in target folder)
|
||||
"fp_ecut": "first cut or copy some files / folders to paste / move\n\nnote: you can cut / paste across different browser tabs",
|
||||
"fp_ename": "{0} items cannot be moved here because the names are already taken. Give them new names below to continue, or blank the name to skip them:",
|
||||
"fcp_ename": "{0} items cannot be copied here because the names are already taken. Give them new names below to continue, or blank the name to skip them:",
|
||||
"fp_ename": "{0} items cannot be moved here because the names are already taken. Give them new names below to continue, or blank the name (\"skip conflicts\") to skip them:",
|
||||
"fcp_ename": "{0} items cannot be copied here because the names are already taken. Give them new names below to continue, or blank the name (\"skip conflicts\") to skip them:",
|
||||
"fp_emore": "there are still some filename collisions left to fix",
|
||||
"fp_ok": "move OK",
|
||||
"fcp_ok": "copy OK",
|
||||
|
|
@ -640,6 +647,24 @@ if (1)
|
|||
"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 textfile viewer",
|
||||
"rc_md": "open in markdown viewer",
|
||||
"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_rnm": "rename",
|
||||
"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?",
|
||||
};
|
||||
|
||||
|
|
@ -654,6 +679,7 @@ var LANGN = [
|
|||
["fra", "français"],
|
||||
["grc", "Ελληνικά"],
|
||||
["ita", "Italiano"],
|
||||
["jpn", "日本語"],
|
||||
["kor", "한국어"],
|
||||
["nld", "Nederlands"],
|
||||
["nno", "Nynorsk"],
|
||||
|
|
@ -664,6 +690,7 @@ var LANGN = [
|
|||
["swe", "Svenska"],
|
||||
["tur", "Türkçe"],
|
||||
["ukr", "Українська"],
|
||||
["vie", "Tiếng Việt"],
|
||||
];
|
||||
|
||||
if (window.langmod)
|
||||
|
|
@ -676,7 +703,7 @@ for (var a = 0; a < LANGN.length; a++)
|
|||
|
||||
function langtest() {
|
||||
var n = LANGS.length - 1;
|
||||
for (var a = 1; a < LANGS.length; a++)
|
||||
for (var a = 1; a < LANGS.length; a++)
|
||||
import_js(SR + '/.cpr/tl/' + LANGS[a] + '.js', function () { if (!--n) langtest2(); });
|
||||
}
|
||||
function langtest2() {
|
||||
|
|
@ -965,6 +992,7 @@ ebi('op_cfg').innerHTML = (
|
|||
' </div>\n' +
|
||||
'</div>\n' +
|
||||
'<div><h3>' + L.cl_keytype + '</h3><div><select id="key_notation"></select></div></div>\n' +
|
||||
(!MOBILE ? '<div><h3>' + L.cl_rcm + '</h3><div><a id="ren" class="tgl btn" href="#" tt="' + L.cdt_ren + '">enable</a><a id="rdb" class="tgl btn" href="#" tt="' + L.cdt_rdb + '">double</a></div></div>' : '') +
|
||||
'<div><h3>' + L.cl_hiddenc + ' ' + (MOBILE ? '<a href="#" id="hcolsh">' + L.cl_hidec + '</a> / ' : '') + '<a href="#" id="hcolsr">' + L.cl_reset + '</a></h3><div id="hcols"></div></div>'
|
||||
);
|
||||
|
||||
|
|
@ -994,6 +1022,34 @@ QS('#op_mkdir input[type="submit"]').value = L.ab_mkdir;
|
|||
QS('#op_new_md input[type="submit"]').value = L.ab_mkdoc;
|
||||
QS('#op_msg input[type="submit"]').value = L.ab_msg;
|
||||
|
||||
// right-click menu
|
||||
ebi('rcm').innerHTML = (
|
||||
'<a href="#" id="ropn">' + L.rc_opn + '</a>' +
|
||||
'<a href="#" id="rply">' + L.rc_ply + '</a>' +
|
||||
'<a href="#" id="rpla">' + L.rc_pla + '</a>' +
|
||||
'<a href="#" id="rtxt">' + L.rc_txt + '</a>' +
|
||||
'<a href="#" id="rmd">' + L.rc_md + '</a>' +
|
||||
'<div id="rs1" class="sep"></div>' +
|
||||
'<a href="#" id="rcpl">' + L.rc_cpl + '</a>' +
|
||||
'<a href="#" id="rdl">' + L.rc_dl + '</a>' +
|
||||
(have_zip ?
|
||||
'<a href="#" id="rzip">' + L.rc_zip + '</a>'
|
||||
: '') +
|
||||
'<div id="rs2" class="sep"></div>' +
|
||||
(have_del ? '<a href="#" id="rdel">' + L.rc_del + '</a>' : '') +
|
||||
(have_mv ? '<a href="#" id="rcut">' + L.rc_cut + '</a>' : '') +
|
||||
'<a href="#" id="rcpy">' + L.rc_cpy + '</a>' +
|
||||
(has(perms, "write") ?
|
||||
'<a href="#" id="rpst">' + L.rc_pst + '</a>' +
|
||||
(have_mv ? '<a href="#" id="rrnm">' + L.rc_rnm + '</a>' : '') +
|
||||
'<div id="rs3" class="sep"></div>' +
|
||||
'<a href="#" id="rnfo">' + L.rc_nfo + '</a>' +
|
||||
'<a href="#" id="rnfi">' + L.rc_nfi + '</a>'
|
||||
: '') +
|
||||
'<div id="rs4" class="sep"></div>' +
|
||||
'<a href="#" id="rsal">' + L.rc_sal + '</a>' +
|
||||
'<a href="#" id="rsin">' + L.rc_sin + '</a>'
|
||||
);
|
||||
|
||||
(function () {
|
||||
var ops = QSA('#ops>a');
|
||||
|
|
@ -1139,6 +1195,20 @@ var ACtx = !IPHONE && (window.AudioContext || window.webkitAudioContext),
|
|||
dk, mp;
|
||||
|
||||
|
||||
var x = '';
|
||||
if (!fullui) {
|
||||
if (window.ui_nombar || /[?&]nombar\b/.exec(sloc0)) x += '#ops,';
|
||||
if (window.ui_noacci || /[?&]noacci\b/.exec(sloc0)) x += '#acc_info,';
|
||||
if (window.ui_nosrvi || /[?&]nosrvi\b/.exec(sloc0)) x += '#srv_info,#srv_info2,';
|
||||
if (window.ui_nocpla || /[?&]nocpla\b/.exec(sloc0)) x += '#goh,';
|
||||
if (window.ui_nolbar || /[?&]nolbar\b/.exec(sloc0)) x += '#wfp,';
|
||||
if (window.ui_noctxb || /[?&]noctxb\b/.exec(sloc0)) x += '#wtoggle,';
|
||||
if (window.ui_norepl || /[?&]norepl\b/.exec(sloc0)) x += '#repl,';
|
||||
}
|
||||
if (x)
|
||||
document.head.appendChild(mknod('style', '', x.slice(0, -1) + '{display:none!important}'));
|
||||
|
||||
|
||||
if (location.pathname.indexOf('//') === 0)
|
||||
hist_replace(location.pathname.replace(/^\/+/, '/'));
|
||||
|
||||
|
|
@ -1242,6 +1312,7 @@ var mpl = (function () {
|
|||
"os_ctl": bcfg_get('au_os_ctl', have_mctl) && have_mctl,
|
||||
'traversals': 0,
|
||||
'm3ut': '#EXTM3U\n',
|
||||
'np': [{'file': 'nothing'}, ['file']],
|
||||
};
|
||||
bcfg_bind(r, 'one', 'au_one', false, function (v) {
|
||||
if (mp.au)
|
||||
|
|
@ -1438,7 +1509,7 @@ var mpl = (function () {
|
|||
if (!r.os_ctl || !mp.au)
|
||||
return;
|
||||
|
||||
var np = get_np()[0],
|
||||
var np = mpl.np[0],
|
||||
fns = np.file.split(' - '),
|
||||
artist = (np.circle && np.circle != np.artist ? np.circle + ' // ' : '') + (np.artist || (fns.length > 1 ? fns[0] : '')),
|
||||
title = np.title || fns.pop(),
|
||||
|
|
@ -1784,12 +1855,6 @@ function ft2dict(tr, skip) {
|
|||
}
|
||||
|
||||
|
||||
function get_np() {
|
||||
var tr = QS('#files tr.play');
|
||||
return ft2dict(tr, { 'up_ip': 1 });
|
||||
};
|
||||
|
||||
|
||||
// toggle player widget
|
||||
var widget = (function () {
|
||||
var r = {},
|
||||
|
|
@ -1847,9 +1912,8 @@ var widget = (function () {
|
|||
ck = irc ? '06' : '',
|
||||
cv = irc ? '07' : '',
|
||||
m = ck + 'np: ',
|
||||
npr = get_np(),
|
||||
npk = npr[1],
|
||||
np = npr[0];
|
||||
npk = mpl.np[1],
|
||||
np = mpl.np[0];
|
||||
|
||||
for (var a = 0; a < npk.length; a++)
|
||||
m += (npk[a] == 'file' ? '' : npk[a]).replace(/^\./, '') + '(' + cv + np[npk[a]] + ck + ') // ';
|
||||
|
|
@ -2548,6 +2612,9 @@ var mpui = (function () {
|
|||
if (mpl.prescan_evp == evp)
|
||||
throw "evp match";
|
||||
|
||||
if (treectl.trunc)
|
||||
return treectl.showmore(99999, repreload);
|
||||
|
||||
if (mpl.traversals++ > 4) {
|
||||
mpl.prescan_evp = null;
|
||||
toast.inf(10, L.mm_nof);
|
||||
|
|
@ -3024,6 +3091,9 @@ function play(tid, is_ev, seek) {
|
|||
}
|
||||
|
||||
if (tn >= mp.order.length) {
|
||||
if (treectl.trunc)
|
||||
return treectl.showmore(99999, next_song);
|
||||
|
||||
if (mpl.pb_mode == 'loop' || ebi('unsearch')) {
|
||||
tn = 0;
|
||||
}
|
||||
|
|
@ -3097,9 +3167,12 @@ function play(tid, is_ev, seek) {
|
|||
for (var a = 0, aa = trs.length; a < aa; a++)
|
||||
clmod(trs[a], 'play');
|
||||
|
||||
var oid = 'a' + tid;
|
||||
clmod(ebi(oid), 'act', 1);
|
||||
clmod(ebi(oid).closest('tr'), 'play', 1);
|
||||
var oid = 'a' + tid,
|
||||
t_a = ebi(oid),
|
||||
t_tr = t_a.closest('tr');
|
||||
|
||||
clmod(t_a, 'act', 1);
|
||||
clmod(t_tr, 'play', 1);
|
||||
clmod(ebi('wtoggle'), 'np', mpl.clip);
|
||||
clmod(ebi('wtoggle'), 'm3u', mpl.m3uen);
|
||||
if (thegrid)
|
||||
|
|
@ -3121,12 +3194,12 @@ function play(tid, is_ev, seek) {
|
|||
}
|
||||
|
||||
if (!seek && !ebi('unsearch')) {
|
||||
var o = ebi(oid);
|
||||
o.setAttribute('id', 'thx_js');
|
||||
t_a.setAttribute('id', 'thx_js');
|
||||
if (mpl.aplay)
|
||||
sethash(oid + getsort());
|
||||
o.setAttribute('id', oid);
|
||||
t_a.setAttribute('id', oid);
|
||||
}
|
||||
mpl.np = ft2dict(t_tr, { 'up_ip': 1 });
|
||||
|
||||
pbar.unwave();
|
||||
if (mpl.waves)
|
||||
|
|
@ -3141,7 +3214,7 @@ function play(tid, is_ev, seek) {
|
|||
catch (ex) {
|
||||
toast.err(0, esc(L.mm_playerr + basenames(ex)));
|
||||
}
|
||||
clmod(ebi(oid), 'act');
|
||||
clmod(t_a, 'act');
|
||||
mpl.t_eplay = setTimeout(next_song, 5000);
|
||||
}
|
||||
|
||||
|
|
@ -3210,6 +3283,7 @@ function evau_error(e) {
|
|||
var em = '' + eplaya.error.message,
|
||||
mfile = '\n\nFile: «' + uricom_dec(eplaya.src.split('/').pop()) + '»',
|
||||
e500 = L.mm_e500,
|
||||
e415 = L.mm_e415,
|
||||
e404 = L.mm_e404,
|
||||
e403 = L.mm_e403;
|
||||
|
||||
|
|
@ -3222,6 +3296,9 @@ function evau_error(e) {
|
|||
if (em.startsWith('404: '))
|
||||
err = e404;
|
||||
|
||||
if (em.startsWith('415: '))
|
||||
err = e415;
|
||||
|
||||
if (em.startsWith('500: '))
|
||||
err = e500;
|
||||
|
||||
|
|
@ -3238,6 +3315,7 @@ function evau_error(e) {
|
|||
|
||||
err = this.status == 403 ? e403 :
|
||||
this.status == 404 ? e404 :
|
||||
this.status == 415 ? e415 :
|
||||
this.status == 500 ? e500 :
|
||||
L.mm_e5xx + this.status;
|
||||
|
||||
|
|
@ -4561,6 +4639,7 @@ var fileman = (function () {
|
|||
var html = [
|
||||
'<div>',
|
||||
'<button id="rn_cancel" tt="' + L.frt_abrt + '</button>',
|
||||
'<button id="rn_skip">⏭ ' + L.fp_skip + '</button>',
|
||||
'<button id="rn_apply">✅ ' + L.fp_apply + '</button>',
|
||||
' src: ' + esc(r.clip[0].replace(/[^/]+$/, '')),
|
||||
'</div>',
|
||||
|
|
@ -4664,11 +4743,21 @@ var fileman = (function () {
|
|||
rn_cancel(e);
|
||||
okgo();
|
||||
}
|
||||
function rn_skip(e) {
|
||||
var o = QSA('#rn_f tr.ng');
|
||||
for (var a = o.length - 1; a >= 0; a--) {
|
||||
var oo = o[a].querySelector('input');
|
||||
oo.value = '';
|
||||
oo.oninput.call(oo);
|
||||
}
|
||||
rn_apply();
|
||||
}
|
||||
function rn_cancel(e) {
|
||||
ev(e);
|
||||
rui.parentNode.removeChild(rui);
|
||||
}
|
||||
ebi('rn_cancel').onclick = rn_cancel;
|
||||
ebi('rn_skip').onclick = rn_skip;
|
||||
ebi('rn_apply').onclick = rn_apply;
|
||||
|
||||
var first_bad = 0;
|
||||
|
|
@ -5400,7 +5489,8 @@ var thegrid = (function () {
|
|||
|
||||
function gclick(e, dbl) {
|
||||
var oth = ebi(this.getAttribute('ref')),
|
||||
href = noq_href(this),
|
||||
qhref = this.getAttribute('href'),
|
||||
href = qhref.split('?')[0],
|
||||
fid = oth.getAttribute('id'),
|
||||
aplay = ebi('a' + fid),
|
||||
atext = ebi('t' + fid),
|
||||
|
|
@ -5429,13 +5519,13 @@ var thegrid = (function () {
|
|||
aplay.click();
|
||||
|
||||
else if (is_dir && !have_sel)
|
||||
treectl.reqls(href, true);
|
||||
treectl.reqls(qhref, true);
|
||||
|
||||
else if (is_txt && !has(['md', 'htm', 'html'], is_txt))
|
||||
atext.click();
|
||||
|
||||
else if (!is_img && have_sel)
|
||||
window.open(href, '_blank');
|
||||
window.open(qhref, '_blank');
|
||||
|
||||
else {
|
||||
if (!dbl)
|
||||
|
|
@ -5956,6 +6046,9 @@ var ahotkeys = function (e) {
|
|||
if (ebi('hkhelp'))
|
||||
return qsr('#hkhelp');
|
||||
|
||||
if (ebi('rcm').style.display)
|
||||
return rcm.hide();
|
||||
|
||||
if (toast.visible)
|
||||
return toast.hide();
|
||||
|
||||
|
|
@ -6720,10 +6813,11 @@ var treectl = (function () {
|
|||
aligngriditems();
|
||||
};
|
||||
|
||||
r.detree = function (e) {
|
||||
r.detree = function (e, nw) {
|
||||
ev(e);
|
||||
entreed = false;
|
||||
swrite('entreed', 'na');
|
||||
if (!nw)
|
||||
swrite('entreed', 'na');
|
||||
|
||||
r.hide();
|
||||
if (!nonav)
|
||||
|
|
@ -6922,6 +7016,8 @@ var treectl = (function () {
|
|||
};
|
||||
|
||||
r.prunetree = function (res) {
|
||||
if (r.dots)
|
||||
return;
|
||||
var ptn = new RegExp(res.unlist);
|
||||
var els = QSA('#treeul li>a+a');
|
||||
for (var a = els.length - 1; a >= 0; a--)
|
||||
|
|
@ -7321,7 +7417,7 @@ var treectl = (function () {
|
|||
memo_dk(top, m[1]);
|
||||
|
||||
r.lsc = res;
|
||||
if (res.unlist) {
|
||||
if (res.unlist && !r.dots) {
|
||||
var ptn = new RegExp(res.unlist);
|
||||
for (var a = nodes.length - 1; a >= 0; a--)
|
||||
if (ptn.exec(uricom_dec(nodes[a].href.split('?')[0])))
|
||||
|
|
@ -7520,7 +7616,7 @@ var treectl = (function () {
|
|||
catch (ex) { }
|
||||
};
|
||||
|
||||
r.showmore = function (n) {
|
||||
r.showmore = function (n, cb) {
|
||||
window.removeEventListener('scroll', r.tscroll);
|
||||
console.log('nvis {0} -> {1}'.format(r.nvis, n));
|
||||
r.nvis = n;
|
||||
|
|
@ -7530,6 +7626,8 @@ var treectl = (function () {
|
|||
setTimeout(function () {
|
||||
r.gentab(get_evpath(), r.lsc);
|
||||
ebi('wrap').style.opacity = CLOSEST ? 'unset' : 1;
|
||||
if (cb)
|
||||
cb();
|
||||
}, 1);
|
||||
};
|
||||
|
||||
|
|
@ -7620,7 +7718,7 @@ var treectl = (function () {
|
|||
|
||||
if (notree) {
|
||||
cs = 'na';
|
||||
r.hide();
|
||||
r.detree(null, 1);
|
||||
}
|
||||
|
||||
if (cs == 'tree' || (cs != 'na' && vw >= 60))
|
||||
|
|
@ -8226,7 +8324,12 @@ var settheme = (function () {
|
|||
freshen();
|
||||
};
|
||||
|
||||
freshen();
|
||||
var m = /[?&]theme=([0-9]+)/.exec(sloc0);
|
||||
if (m)
|
||||
r.go(parseInt(m[1]));
|
||||
else
|
||||
freshen();
|
||||
|
||||
return r;
|
||||
})();
|
||||
|
||||
|
|
@ -8595,13 +8698,17 @@ var msel = (function () {
|
|||
tb = QS('#op_new_md input[name="name"]');
|
||||
|
||||
form.onsubmit = function (e) {
|
||||
if (!has(perms, "delete") && !/\.md$/.test(tb.value)) {
|
||||
ev(e);
|
||||
toast.err(10, L.nmd_i2);
|
||||
return false;
|
||||
}
|
||||
if (tb.value) {
|
||||
if (toast.tag == L.mk_noname)
|
||||
toast.hide();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
ev(e);
|
||||
toast.err(10, L.mk_noname, L.mk_noname);
|
||||
return false;
|
||||
|
|
@ -9368,6 +9475,200 @@ ebi('files').onclick = ebi('docul').onclick = function (e) {
|
|||
};
|
||||
|
||||
|
||||
|
||||
var rcm = (function () {
|
||||
if (MOBILE)
|
||||
return {enabled: false}
|
||||
|
||||
var r = {
|
||||
enabled: true,
|
||||
double: false
|
||||
};
|
||||
bcfg_bind(r, 'enabled', 'ren', true);
|
||||
bcfg_bind(r, 'double', 'rdb', false);
|
||||
|
||||
var menu = ebi('rcm');
|
||||
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',
|
||||
'<td>-new-</td>' +
|
||||
'<td colspan="' + (QSA("#files thead th").length - 1) + '"><input id="tempname" class="i" type="text" placeholder="' + (is_dir ? 'Folder' : "File") + ' Name"></td>'
|
||||
);
|
||||
QS("#files tbody").appendChild(row);
|
||||
|
||||
function sendit(name) {
|
||||
name = ('' + name).trim();
|
||||
if (!name)
|
||||
return;
|
||||
var data = new FormData();
|
||||
data.set("act", is_dir ? "mkdir" : "new_md");
|
||||
data.set("name", name);
|
||||
|
||||
var req = new XHR();
|
||||
req.open("POST", get_evpath());
|
||||
req.onload = req.onerror = function() {
|
||||
if (req.status == 405 || req.status == 500)
|
||||
return toast.err(3, "a " + (is_dir ? "folder" : "file") + " with that name already exists.");
|
||||
if (req.status < 200 || req.status > 399)
|
||||
return toast.err(3, "couldn't create " + (is_dir ? "folder" : "file") + ": <br><code>" + esc(req.responseText) + '</code>');
|
||||
treectl.goto();
|
||||
};
|
||||
req.send(data);
|
||||
}
|
||||
|
||||
var input = ebi("tempname");
|
||||
input.onblur = function() {
|
||||
sendit(input.value);
|
||||
// Chrome blurs elements when calling remove for some reason
|
||||
input.onblur = null;
|
||||
row.remove();
|
||||
};
|
||||
input.onkeydown = function(e) {
|
||||
if (e.key == "Enter")
|
||||
sendit(input.value);
|
||||
if (e.key == "Enter" || e.key == "Escape") {
|
||||
input.onblur = null;
|
||||
row.remove();
|
||||
ev(e);
|
||||
}
|
||||
};
|
||||
input.focus();
|
||||
}
|
||||
|
||||
var opts = QSA('#rcm a');
|
||||
for (var i = 0; i < opts.length; i++) {
|
||||
opts[i].onclick = function(e) {
|
||||
ev(e);
|
||||
switch(e.target.id.slice(1)) {
|
||||
case 'opn':
|
||||
var a = mknod('a');
|
||||
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(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;
|
||||
case 'cut': fileman.cut(); break;
|
||||
case 'cpy': fileman.cpy(); break;
|
||||
case 'pst':
|
||||
fileman.paste();
|
||||
fileman.clip = [];
|
||||
break;
|
||||
case 'rnm': fileman.rename(); break;
|
||||
case 'nfo': mktemp(true); break;
|
||||
case 'nfi': mktemp(); break;
|
||||
case 'sal':
|
||||
msel.evsel(null, true);
|
||||
selFile.no_dsel = true;
|
||||
break;
|
||||
case 'sin': msel.evsel(null, 't'); break;
|
||||
}
|
||||
r.hide(true);
|
||||
};
|
||||
}
|
||||
|
||||
function show(x, y, target, isGrid) {
|
||||
selFile = jcp(nsFile);
|
||||
if (target) {
|
||||
var file = target.closest("#files tbody tr");
|
||||
if (isGrid && target.matches && target.matches('#ggrid > a')) {
|
||||
var ref = ebi(target.getAttribute('ref'));
|
||||
file = ref && ref.closest('#files tbody tr');
|
||||
}
|
||||
if (file) {
|
||||
selFile.no_dsel = clgot(file, "sel");
|
||||
clmod(file, "sel", true);
|
||||
selFile.elem = file;
|
||||
|
||||
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";
|
||||
else {
|
||||
var lead = file.firstChild.firstChild;
|
||||
selFile.id = lead.id.split('-')[1];
|
||||
selFile.type = lead.innerHTML[0] == '(' ? 'gf' : lead.id.split('-')[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
console.log(selFile);
|
||||
msel.selui();
|
||||
|
||||
var has_sel = msel.getsel().length;
|
||||
var has_clip = fileman.clip.length;
|
||||
|
||||
clmod(ebi('ropn'), 'hide', !selFile.path);
|
||||
clmod(ebi('rply'), 'hide', selFile.type != 'gf' && selFile.type != 'af');
|
||||
clmod(ebi('rpla'), 'hide', selFile.type != 'gf');
|
||||
clmod(ebi('rtxt'), 'hide', !selFile.id);
|
||||
clmod(ebi('rs1'), 'hide', !selFile.path);
|
||||
clmod(ebi('rmd'), 'hide', !selFile.id || selFile.relpath.slice(-3) != ".md");
|
||||
clmod(ebi('rcpl'), 'hide', !selFile.path);
|
||||
clmod(ebi('rdl'), 'hide', !has_sel);
|
||||
clmod(ebi('rzip'), 'hide', !has_sel);
|
||||
clmod(ebi('rs2'), 'hide', !has_sel);
|
||||
clmod(ebi('rcut'), 'hide', !has_sel);
|
||||
clmod(ebi('rdel'), 'hide', !has_sel);
|
||||
clmod(ebi('rcpy'), 'hide', !has_sel);
|
||||
clmod(ebi('rpst'), 'hide', !has_clip);
|
||||
clmod(ebi('rrnm'), 'hide', !has_sel);
|
||||
clmod(ebi('rs3'), 'hide', !has_sel);
|
||||
clmod(ebi('rs4'), 'hide', !has_sel && !has(perms, "write"));
|
||||
|
||||
menu.style.left = x + 5 + 'px';
|
||||
menu.style.top = y + 5 + 'px';
|
||||
menu.style.display = 'block';
|
||||
menu.focus();
|
||||
}
|
||||
|
||||
r.hide = function(force) {
|
||||
if (!menu.style.display || (!force && menu.contains(document.activeElement)))
|
||||
return;
|
||||
if (selFile.elem && !selFile.no_dsel) {
|
||||
clmod(selFile.elem, "sel", false);
|
||||
msel.selui();
|
||||
}
|
||||
selFile = jcp(nsFile);
|
||||
menu.style.display = '';
|
||||
}
|
||||
|
||||
ebi('wrap').oncontextmenu = function(e) {
|
||||
r.hide(true);
|
||||
if (!r.enabled || e.shiftKey || (r.double && menu.style.display)) {
|
||||
return true;
|
||||
}
|
||||
if (selFile.elem && !selFile.no_dsel) {
|
||||
clmod(selFile.elem, "sel", false);
|
||||
msel.selui();
|
||||
}
|
||||
ev(e);
|
||||
var gfile = thegrid.en && e.target && e.target.closest('#ggrid > a');
|
||||
show(xscroll() + e.clientX, yscroll() + e.clientY, gfile || e.target, gfile);
|
||||
return false;
|
||||
};
|
||||
menu.onblur = function() {setTimeout(r.hide)};
|
||||
|
||||
return r;
|
||||
})();
|
||||
|
||||
|
||||
function reload_mp() {
|
||||
if (mp && mp.au) {
|
||||
mpo.au = mp.au;
|
||||
|
|
@ -9438,13 +9739,4 @@ function reload_browser() {
|
|||
}
|
||||
treectl.hydrate();
|
||||
|
||||
if (!fullui && (window.ui_nombar || /[?&]nombar\b/.exec(sloc0))) ebi('ops').style.display = 'none';
|
||||
if (!fullui && (window.ui_noacci || /[?&]noacci\b/.exec(sloc0))) ebi('acc_info').style.display = 'none';
|
||||
if (!fullui && (window.ui_nosrvi || /[?&]nosrvi\b/.exec(sloc0))) ebi('srv_info').style.display = 'none';
|
||||
if (!fullui && (window.ui_nocpla || /[?&]nocpla\b/.exec(sloc0))) ebi('goh').style.display = 'none';
|
||||
if (!fullui && (window.ui_nolbar || /[?&]nolbar\b/.exec(sloc0))) ebi('wfp').style.display = 'none';
|
||||
if (!fullui && (window.ui_noctxb || /[?&]noctxb\b/.exec(sloc0))) ebi('wtoggle').style.display = 'none';
|
||||
if (!fullui && (window.ui_norepl || /[?&]norepl\b/.exec(sloc0))) ebi('repl').style.display = 'none';
|
||||
|
||||
var m = /[?&]theme=([0-9]+)/.exec(sloc0);
|
||||
if (m) settheme.go(parseInt(m[1]));
|
||||
J_BRW = 2;
|
||||
|
|
|
|||
|
|
@ -50,6 +50,10 @@ document.documentElement.className = (STG && STG.cpp_thm) || "{{ this.args.theme
|
|||
{%- if js %}
|
||||
<script src="{{ js }}_={{ ts }}"></script>
|
||||
{%- endif %}
|
||||
<script>
|
||||
Date.now();function jsldp(a,b){2!=window[a]&&alert("FATAL ERROR: cannot load "+b+".js due to unreliable network or broken reverse-proxy; try CTRL-SHIFT-R")}
|
||||
jsldp("J_UTL","util");
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
|
|
|||
|
|
@ -161,8 +161,17 @@ try { l.light = drk? 0:1; } catch (ex) { }
|
|||
{%- if edit %}
|
||||
<script src="{{ r }}/.cpr/md2.js?_={{ ts }}"></script>
|
||||
{%- endif %}
|
||||
<script>
|
||||
Date.now();function jsldp(a,b){2!=window[a]&&alert("FATAL ERROR: cannot load "+b+".js due to unreliable network or broken reverse-proxy; try CTRL-SHIFT-R")}
|
||||
jsldp("J_UTL","util");
|
||||
jsldp("J_MD","md");
|
||||
{%- if edit %}
|
||||
jsldp("J_MD2","md2");
|
||||
{%- endif %}
|
||||
</script>
|
||||
{%- if js %}
|
||||
<script src="{{ js }}_={{ ts }}"></script>
|
||||
{%- endif %}
|
||||
</body></html>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
"use strict";
|
||||
|
||||
var J_MD = 1;
|
||||
var dom_toc = ebi('toc'),
|
||||
dom_wrap = ebi('mw'),
|
||||
dom_hbar = ebi('mh'),
|
||||
|
|
@ -524,3 +525,5 @@ if (sread('hidenav') == 1)
|
|||
|
||||
if (window.tt && tt.init)
|
||||
tt.init();
|
||||
|
||||
J_MD = 2;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
"use strict";
|
||||
|
||||
|
||||
var J_MD2 = 1;
|
||||
var sloc0 = '' + location,
|
||||
dbg_kbd = /[?&]dbgkbd\b/.exec(sloc0);
|
||||
|
||||
|
|
@ -1230,3 +1231,5 @@ action_stack = (function () {
|
|||
_ref: ref
|
||||
}
|
||||
})();
|
||||
|
||||
J_MD2 = 2;
|
||||
|
|
|
|||
|
|
@ -57,5 +57,10 @@ try { l.light = drk? 0:1; } catch (ex) { }
|
|||
{%- if js %}
|
||||
<script src="{{ js }}_={{ ts }}"></script>
|
||||
{%- endif %}
|
||||
<script>
|
||||
Date.now();function jsldp(a,b){2!=window[a]&&alert("FATAL ERROR: cannot load "+b+".js due to unreliable network or broken reverse-proxy; try CTRL-SHIFT-R")}
|
||||
jsldp("J_UTL","util");
|
||||
jsldp("J_MDE","mde");
|
||||
</script>
|
||||
</body></html>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
"use strict";
|
||||
|
||||
var J_MDE = 1;
|
||||
var dom_wrap = ebi('mw');
|
||||
var dom_nav = ebi('mn');
|
||||
var dom_doc = ebi('m');
|
||||
|
|
@ -199,3 +200,5 @@ function save_chk() {
|
|||
|
||||
toast.ok(2, 'save OK' + (this.ntry ? '\nattempt ' + this.ntry : ''));
|
||||
}
|
||||
|
||||
J_MDE = 2;
|
||||
|
|
|
|||
|
|
@ -1,36 +0,0 @@
|
|||
:root {
|
||||
--font-main: sans-serif;
|
||||
--font-serif: serif;
|
||||
--font-mono: 'scp';
|
||||
}
|
||||
html,body,tr,th,td,#files,a {
|
||||
color: inherit;
|
||||
background: none;
|
||||
font-weight: inherit;
|
||||
font-size: inherit;
|
||||
padding: 0;
|
||||
border: none;
|
||||
}
|
||||
html {
|
||||
color: #ccc;
|
||||
background: #333;
|
||||
font-family: sans-serif;
|
||||
font-family: var(--font-main), sans-serif;
|
||||
text-shadow: 1px 1px 0px #000;
|
||||
touch-action: manipulation;
|
||||
}
|
||||
html, body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
#box {
|
||||
padding: .5em 1em;
|
||||
background: #2c2c2c;
|
||||
}
|
||||
pre {
|
||||
font-family: monospace, monospace;
|
||||
font-family: var(--font-mono), monospace, monospace;
|
||||
}
|
||||
a {
|
||||
color: #fc5;
|
||||
}
|
||||
|
|
@ -7,7 +7,12 @@
|
|||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=0.8">
|
||||
<meta name="theme-color" content="#{{ tcolor }}">
|
||||
<link rel="stylesheet" media="screen" href="{{ r }}/.cpr/msg.css?_={{ ts }}">
|
||||
<style>:root{--font-main:sans-serif;--font-mono:monospace}
|
||||
html,body,a{margin:0;padding:0;border:none;color:#ccc;background:none;font-family:sans-serif;font-family:var(--font-main),sans-serif}
|
||||
pre{font-family:monospace,monospace;font-family:var(--font-mono),monospace}
|
||||
html{touch-action:manipulation;background:#333}
|
||||
#box{padding:.5em 1em;background:#2c2c2c}
|
||||
a{color:#fc5}</style>
|
||||
{{ html_head }}
|
||||
</head>
|
||||
|
||||
|
|
@ -43,7 +48,7 @@
|
|||
<script>
|
||||
setTimeout(function() {
|
||||
location.replace("{{ redir }}");
|
||||
}, 600);
|
||||
}, 800);
|
||||
</script>
|
||||
{%- endif %}
|
||||
{%- if js %}
|
||||
|
|
|
|||
|
|
@ -39,6 +39,11 @@ document.documentElement.className = (STG && STG.cpp_thm) || "{{ this.args.theme
|
|||
{%- if js %}
|
||||
<script src="{{ js }}_={{ ts }}"></script>
|
||||
{%- endif %}
|
||||
<script>
|
||||
Date.now();function jsldp(a,b){2!=window[a]&&alert("FATAL ERROR: cannot load "+b+".js due to unreliable network or broken reverse-proxy; try CTRL-SHIFT-R")}
|
||||
jsldp("J_UTL","util");
|
||||
jsldp("J_RUP","rups");
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +1,7 @@
|
|||
"use strict";
|
||||
|
||||
var J_RUP = 1;
|
||||
|
||||
function render() {
|
||||
var html = ['<table id="tab"><thead><tr><th>size</th><th>who</th><th>ip</th><th>when</th><th>age</th><th>dir</th><th>file</th></tr></thead><tbody>'];
|
||||
var ups = V.ups, now = V.now;
|
||||
|
|
@ -67,3 +71,5 @@ ebi('filter').onkeydown = function (e) {
|
|||
if (('' + e.key).endsWith('Enter'))
|
||||
ask();
|
||||
};
|
||||
|
||||
J_RUP = 2;
|
||||
|
|
|
|||
|
|
@ -79,6 +79,11 @@ document.documentElement.className = (STG && STG.cpp_thm) || "{{ this.args.theme
|
|||
{%- if js %}
|
||||
<script src="{{ js }}_={{ ts }}"></script>
|
||||
{%- endif %}
|
||||
<script>
|
||||
Date.now();function jsldp(a,b){2!=window[a]&&alert("FATAL ERROR: cannot load "+b+".js due to unreliable network or broken reverse-proxy; try CTRL-SHIFT-R")}
|
||||
jsldp("J_UTL","util");
|
||||
jsldp("J_SHR","shares");
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +1,7 @@
|
|||
"use strict";
|
||||
|
||||
var J_SHR = 1;
|
||||
|
||||
var t = QSA('a[k]');
|
||||
for (var a = 0; a < t.length; a++)
|
||||
t[a].onclick = rm;
|
||||
|
|
@ -81,3 +85,5 @@ function showqr(href) {
|
|||
for (var a = 0; a < aa; a++)
|
||||
btns[a].onclick = bump;
|
||||
})();
|
||||
|
||||
J_SHR = 2;
|
||||
|
|
|
|||
|
|
@ -117,6 +117,7 @@
|
|||
{%- if ahttps %}
|
||||
<a id="w" href="{{ ahttps }}">switch to https</a>
|
||||
{%- endif %}
|
||||
<div id="lm"></div>
|
||||
</form>
|
||||
</div>
|
||||
{%- else %}
|
||||
|
|
@ -149,6 +150,7 @@
|
|||
{%- if ahttps %}
|
||||
<a id="w" href="{{ ahttps }}">switch to https</a>
|
||||
{%- endif %}
|
||||
<div id="lm"></div>
|
||||
</form>
|
||||
{%- endif %}
|
||||
</div>
|
||||
|
|
@ -222,6 +224,11 @@ document.documentElement.className = (STG && STG.cpp_thm) || "{{ this.args.theme
|
|||
{%- if js %}
|
||||
<script src="{{ js }}_={{ ts }}"></script>
|
||||
{%- endif %}
|
||||
<script>
|
||||
Date.now();function jsldp(a,b){2!=window[a]&&alert("FATAL ERROR: cannot load "+b+".js due to unreliable network or broken reverse-proxy; try CTRL-SHIFT-R")}
|
||||
jsldp("J_UTL","util");
|
||||
jsldp("J_SPL","splash");
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +1,7 @@
|
|||
"use strict";
|
||||
|
||||
var J_SPL = 1;
|
||||
|
||||
Ls.eng = {
|
||||
"splash": {
|
||||
"d2": "shows the state of all active threads",
|
||||
|
|
@ -8,6 +12,8 @@ Ls.eng = {
|
|||
"ta1": "fill in your new password first",
|
||||
"ta2": "repeat to confirm new password:",
|
||||
"ta3": "found a typo; please try again",
|
||||
"nop": "ERROR: Password cannot be blank",
|
||||
"nou": "ERROR: Username and/or password cannot be blank",
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -15,6 +21,10 @@ if (window.langmod)
|
|||
langmod();
|
||||
|
||||
var d = (Ls[lang] || Ls.eng).splash;
|
||||
if (Ls.eng && d !== Ls.eng.splash)
|
||||
for (var k in Ls.eng.splash)
|
||||
if (d[k] === undefined)
|
||||
d[k] = Ls.eng.splash[k];
|
||||
|
||||
d.wb = d.w;
|
||||
|
||||
|
|
@ -95,8 +105,25 @@ if (/\&re=/.test('' + location))
|
|||
ebi('x').onclick = function (e) {
|
||||
ev(e);
|
||||
if (!pwi.value)
|
||||
return redo(d.ta1);
|
||||
return ebi('lm').innerHTML = d.ta1;
|
||||
|
||||
modal.prompt(d.ta2, "y", mok, null, stars);
|
||||
};
|
||||
})();
|
||||
|
||||
if (ebi('lf'))
|
||||
ebi('lf').onsubmit = function() {
|
||||
var un = ebi('lu');
|
||||
if (ebi('lp').value && (!un || un.value))
|
||||
return true;
|
||||
ebi('lm').innerHTML = un ? d.nou : d.nop;
|
||||
return false;
|
||||
};
|
||||
|
||||
if (ebi('lp'))
|
||||
ebi('lp').oninput = function() {
|
||||
ebi('lm').innerHTML = this.value.length <= 64 ?
|
||||
'' : 'ERROR: Password too long (max=64)';
|
||||
};
|
||||
|
||||
J_SPL = 2;
|
||||
|
|
|
|||
|
|
@ -362,6 +362,11 @@ document.documentElement.className = (STG && STG.cpp_thm) || "{{ args.theme }}";
|
|||
{%- if js %}
|
||||
<script src="{{ js }}_={{ ts }}"></script>
|
||||
{%- endif %}
|
||||
<script>
|
||||
Date.now();function jsldp(a,b){2!=window[a]&&alert("FATAL ERROR: cannot load "+b+".js due to unreliable network or broken reverse-proxy; try CTRL-SHIFT-R")}
|
||||
jsldp("J_UTL","util");
|
||||
jsldp("J_SVC","svcs");
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +1,7 @@
|
|||
"use strict";
|
||||
|
||||
var J_SVC = 1;
|
||||
|
||||
var oa = QSA('pre');
|
||||
for (var a = 0; a < oa.length; a++) {
|
||||
var html = oa[a].innerHTML,
|
||||
|
|
@ -104,3 +108,5 @@ ebi('qr').onclick = function () {
|
|||
var txt = esc(url) + '<img class="b64" width="100" height="100" src="' + addq(url, 'qr') + '" />';
|
||||
modal.alert(txt);
|
||||
};
|
||||
|
||||
J_SVC = 2;
|
||||
|
|
|
|||
|
|
@ -155,7 +155,7 @@ Ls.chi = {
|
|||
"ul_par": "并行上传:",
|
||||
"ut_rand": "随机化文件名",
|
||||
"ut_u2ts": "将最后修改的时间戳$N从你的文件系统复制到服务器\">📅",
|
||||
"ut_ow": "覆盖服务器上的现有文件?$N🛡️: 从不(会生成一个新文件名)$N🕒: 服务器文件较旧则覆盖$N♻️: 总是覆盖,如果文件内容不同", //m
|
||||
"ut_ow": "覆盖服务器上的现有文件?$N🛡️: 从不(会生成一个新文件名)$N🕒: 服务器文件较旧则覆盖$N♻️: 如果文件内容不同则总是覆盖$N⏭️: 无条件跳过所有已存在的文件", //m
|
||||
"ut_mt": "在上传时继续哈希其他文件$N$N如果你的 CPU 或硬盘是瓶颈,可能需要禁用",
|
||||
"ut_ask": '上传开始前询问确认">💭',
|
||||
"ut_pot": "通过简化 UI 来$N提高慢设备上的上传速度",
|
||||
|
|
@ -220,6 +220,7 @@ Ls.chi = {
|
|||
"cl_reset": "重置",
|
||||
"cl_hpick": "点击列标题以在下表中隐藏",
|
||||
"cl_hcancel": "列隐藏已取消",
|
||||
"cl_rcm": "右键菜单", //m
|
||||
|
||||
"ct_grid": '网格视图',
|
||||
"ct_ttips": '◔ ◡ ◔">ℹ️ 工具提示',
|
||||
|
|
@ -262,6 +263,7 @@ Ls.chi = {
|
|||
"cdt_lim": "文件夹中显示的最大文件数",
|
||||
"cdt_ask": "滚动到底部时,$N不会加载更多文件,$N而是询问你该怎么做",
|
||||
"cdt_hsort": "包含在媒体 URL 中的排序规则 (<code>,sorthref</code>) 数量。将其设置为 0 时,点击媒体链接时也会忽略排序规则。", //m
|
||||
"cdt_ren": "启用自定义右键菜单,按住 shift 键并右键单击仍可访问常规菜单", //m
|
||||
|
||||
"tt_entree": "显示导航面板(目录树侧边栏)$N快捷键: B",
|
||||
"tt_detree": "显示面包屑导航$N快捷键: B",
|
||||
|
|
@ -332,6 +334,7 @@ Ls.chi = {
|
|||
"mm_eunk": "未知错误",
|
||||
"mm_e404": "无法播放音频;错误 404:文件未找到。",
|
||||
"mm_e403": "无法播放音频;错误 403:访问被拒绝。\n\n尝试按 F5 重新加载,也许你已被注销",
|
||||
"mm_e415": "无法播放音频;错误 415:文件转码失败;检查服务器日志。", //m
|
||||
"mm_e500": "无法播放音频;错误 500:检查服务器日志。", //m
|
||||
"mm_e5xx": "无法播放音频;服务器错误",
|
||||
"mm_nof": "附近找不到更多音频文件",
|
||||
|
|
@ -419,9 +422,10 @@ Ls.chi = {
|
|||
"fcc_warn": '已将 {0} 项复制到剪贴板\n\n但:只有 <b>这个</b> 浏览器标签页可以粘贴它们\n(因为选择非常庞大)', //m
|
||||
|
||||
"fp_apply": "确认并立即粘贴", //m
|
||||
"fp_skip": "跳过冲突", //m
|
||||
"fp_ecut": "首先剪切或复制一些文件/文件夹以粘贴/移动\n\n注意:你可以在不同的浏览器标签页之间剪切/粘贴", //m
|
||||
"fp_ename": "{0} 项不能移动到这里,因为名称已被占用。请在下方输入新名称以继续,或将名称留空以跳过这些项:", //m
|
||||
"fcp_ename": "{0} 项不能复制到这里,因为名称已被占用。请在下方输入新名称以继续,或将名称留空以跳过这些项:", //m
|
||||
"fp_ename": "{0} 项不能移动到这里,因为名称已被占用。请在下方输入新名称以继续,或将名称留空(“跳过冲突”)以跳过这些项:", //m
|
||||
"fcp_ename": "{0} 项不能复制到这里,因为名称已被占用。请在下方输入新名称以继续,或将名称留空(“跳过冲突”)以跳过这些项:", //m
|
||||
"fp_emore": "还有一些文件名冲突需要解决", //m
|
||||
"fp_ok": "移动成功",
|
||||
"fcp_ok": "复制成功", //m
|
||||
|
|
@ -440,7 +444,7 @@ Ls.chi = {
|
|||
"fcp_both_b": '<a href="#" id="modal-ok">复制</a><a href="#" id="modal-ng">上传</a>', //m
|
||||
|
||||
"mk_noname": "在左侧文本框中输入名称,然后再执行此操作 :p",
|
||||
"nmd_i1": "还可以添加需要的文件扩展名,例如 <code>.txt</code>", //m
|
||||
"nmd_i1": "还可以添加需要的文件扩展名,例如 <code>.md</code>", //m
|
||||
"nmd_i2": "由于没有删除权限,你只能创建 <code>.md</code> 文件", //m
|
||||
|
||||
"tv_load": "加载文本文件:\n\n{0}\n\n{1}% ({2} 的 {3} MiB 已加载)",
|
||||
|
|
@ -637,6 +641,23 @@ Ls.chi = {
|
|||
"ur_um": "完成;\n{0} 个上传成功,\n{1} 个上传失败,抱歉",
|
||||
"ur_sm": "完成;\n{0} 个文件在服务器上找到,\n{1} 个文件未在服务器上找到",
|
||||
|
||||
"rc_opn": "打开", //m
|
||||
"rc_ply": "播放", //m
|
||||
"rc_pla": "作为音频播放", //m
|
||||
"rc_txt": "在文件查看器中打开", //m
|
||||
"rc_md": "在文本编辑器中打开", //m
|
||||
"rc_dl": "下载", //m
|
||||
"rc_zip": "下载为压缩包", //m
|
||||
"rc_cpl": "复制链接", //m
|
||||
"rc_del": "删除", //m
|
||||
"rc_cut": "剪切", //m
|
||||
"rc_cpy": "复制", //m
|
||||
"rc_pst": "粘贴", //m
|
||||
"rc_nfo": "新建文件夹", //m
|
||||
"rc_nfi": "新建文件", //m
|
||||
"rc_sal": "全选", //m
|
||||
"rc_sin": "反选", //m
|
||||
|
||||
"lang_set": "刷新以使更改生效?",
|
||||
|
||||
"splash": {
|
||||
|
|
@ -678,6 +699,8 @@ Ls.chi = {
|
|||
"ta1": "请先输入新密码",
|
||||
"ta2": "重复以确认新密码:",
|
||||
"ta3": "发现拼写错误;请重试",
|
||||
"nop": "错误:密码不能为空", //m
|
||||
"nou": "错误:用户名和/或密码不能为空", //m
|
||||
"aa1": "正在接收的文件:", //m
|
||||
"ab1": "关闭 k304",
|
||||
"ac1": "开启 k304",
|
||||
|
|
|
|||
|
|
@ -159,7 +159,7 @@ Ls.cze = {
|
|||
"ul_par": "paralelní nahrávání:",
|
||||
"ut_rand": "náhodné názvy souborů",
|
||||
"ut_u2ts": "kopírovat časovou značku poslední změny$Nz vašeho souborového systému na server\">📅",
|
||||
"ut_ow": "přepsat existující soubory na serveru?$N🛡️: nikdy (místo toho vytvoří nový název souboru)$N🕒: přepsat pokud je soubor na serveru starší než váš$N♻️: vždy přepsat pokud se soubory liší",
|
||||
"ut_ow": "přepsat existující soubory na serveru?$N🛡️: nikdy (místo toho vytvoří nový název souboru)$N🕒: přepsat pokud je soubor na serveru starší než váš$N♻️: vždy přepsat pokud se soubory liší$N⏭️: bezpodmínečně přeskočit všechny existující soubory", //m
|
||||
"ut_mt": "pokračovat v hashování ostatních souborů během nahrávání$N$Nmožná zakázat pokud je vaše CPU nebo HDD bottleneckem",
|
||||
"ut_ask": 'požádat o potvrzení před zahájením nahrávání">💭',
|
||||
"ut_pot": "zlepšit rychlost nahrávání na pomalých zařízeních$Nzjednodušením UI",
|
||||
|
|
@ -224,6 +224,7 @@ Ls.cze = {
|
|||
"cl_reset": "resetovat",
|
||||
"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
|
||||
|
||||
"ct_grid": '田 mřížka',
|
||||
"ct_ttips": '◔ ◡ ◔">ℹ️ nápovědy',
|
||||
|
|
@ -266,6 +267,7 @@ Ls.cze = {
|
|||
"cdt_lim": "maximální počet souborů k zobrazení ve složce",
|
||||
"cdt_ask": "při posunování na konec,$Nmísto načítání více souborů,$N se zeptat co dělat",
|
||||
"cdt_hsort": "kolik pravidel řazení (<code>,sorthref</code>) zahrnout do media-URL. Nastavení na 0 bude také ignorovat pravidla řazení zahrnutá v media odkazech při kliknutí na ně",
|
||||
"cdt_ren": "povolit vlastní kontextovou nabídku, běžnou nabídku lze otevřít podržením klávesy shift a kliknutím pravým tlačítkem", //m
|
||||
|
||||
"tt_entree": "zobrazit navigační panel (postranní strom adresářů)$NKlávesová zkratka: B",
|
||||
"tt_detree": "zobrazit drobečkovou navigaci$NKlávesová zkratka: B",
|
||||
|
|
@ -336,6 +338,7 @@ Ls.cze = {
|
|||
"mm_eunk": "Neznámá chyba",
|
||||
"mm_e404": "Nelze přehrát audio; chyba 404: Soubor nenalezen.",
|
||||
"mm_e403": "Nelze přehrát audio; chyba 403: Přístup odepřen.\n\nZkuste stisknout F5 pro obnovení, možná jste se odhlásili",
|
||||
"mm_e415": "Nelze přehrát audio; chyba 415: Převod souboru selhal; zkontrolujte logy serveru.", //m
|
||||
"mm_e500": "Nelze přehrát audio; chyba 500: Zkontrolujte logy serveru.",
|
||||
"mm_e5xx": "Nelze přehrát audio; chyba serveru ",
|
||||
"mm_nof": "žádné další audio soubory v okolí nenalezeny",
|
||||
|
|
@ -423,9 +426,10 @@ Ls.cze = {
|
|||
"fcc_warn": 'zkopírováno {0} položek do schránky\n\nale: pouze <b>tato</b> karta prohlížeče je může vložit\n(protože výběr je tak absolutně masivní)',
|
||||
|
||||
"fp_apply": "použít tyto názvy",
|
||||
"fp_skip": "přeskočit konflikty", //m
|
||||
"fp_ecut": "nejprve vyjměte nebo zkopírujte nějaké soubory / složky k vložení / přesunutí\n\npoznámka: můžete vyjmout / vložit přes různé karty prohlížeče",
|
||||
"fp_ename": "{0} položek sem nelze přesunout protože názvy jsou již obsazené. Dejte jim nové názvy níže pro pokračování, nebo název nechte prázdný pro přeskočení:",
|
||||
"fcp_ename": "{0} položek sem nelze zkopírovat protože názvy jsou již obsazené. Dejte jim nové názvy níže pro pokračování, nebo název nechte prázdný pro přeskočení:",
|
||||
"fp_ename": "{0} položek sem nelze přesunout protože názvy jsou již obsazené. Dejte jim nové názvy níže pro pokračování, nebo název nechte prázdný (\"přeskočit konflikty\") pro přeskočení:", //m
|
||||
"fcp_ename": "{0} položek sem nelze zkopírovat protože názvy jsou již obsazené. Dejte jim nové názvy níže pro pokračování, nebo název nechte prázdný (\"přeskočit konflikty\") pro přeskočení:", //m
|
||||
"fp_emore": "stále jsou některé kolize názvů souborů k opravě",
|
||||
"fp_ok": "přesun OK",
|
||||
"fcp_ok": "kopírování OK",
|
||||
|
|
@ -444,7 +448,7 @@ Ls.cze = {
|
|||
"fcp_both_b": '<a href="#" id="modal-ok">Kopírovat</a><a href="#" id="modal-ng">Nahrát</a>',
|
||||
|
||||
"mk_noname": "napište název do textového pole vlevo předtím než to uděláte :p",
|
||||
"nmd_i1": "můžeš také přidat příponu souboru, například <code>.txt</code>", //m
|
||||
"nmd_i1": "můžeš také přidat příponu souboru, například <code>.md</code>", //m
|
||||
"nmd_i2": "můžeš vytvářet pouze <code>.md</code> soubory, protože nemáš oprávnění mazat", //m
|
||||
|
||||
"tv_load": "Načítání textového dokumentu:\n\n{0}\n\n{1}% ({2} z {3} MiB načteno)",
|
||||
|
|
@ -641,6 +645,23 @@ Ls.cze = {
|
|||
"ur_um": "Dokončeno;\n{0} nahrání OK,\n{1} nahrání selhalo, omlouváme se",
|
||||
"ur_sm": "Dokončeno;\n{0} souborů nalezeno na serveru,\n{1} souborů NENALEZENO na serveru",
|
||||
|
||||
"rc_opn": "otevřít", //m
|
||||
"rc_ply": "přehrát", //m
|
||||
"rc_pla": "přehrát jako zvuk", //m
|
||||
"rc_txt": "otevřít v prohlížeči souborů", //m
|
||||
"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
|
||||
"rc_pst": "vložit", //m
|
||||
"rc_nfo": "nová složka", //m
|
||||
"rc_nfi": "nový soubor", //m
|
||||
"rc_sal": "vybrat vše", //m
|
||||
"rc_sin": "invertovat výběr", //m
|
||||
|
||||
"lang_set": "obnovit stránku, aby se změna projevila?",
|
||||
|
||||
"splash": {
|
||||
|
|
@ -682,11 +703,14 @@ Ls.cze = {
|
|||
"ta1": "nejprve vyplňte své nové heslo",
|
||||
"ta2": "zopakujte pro potvrzení nového hesla:",
|
||||
"ta3": "nalezen překlep; zkuste to prosím znovu",
|
||||
"nop": "CHYBA: Heslo nesmí být prázdné", //m
|
||||
"nou": "CHYBA: Uživatelské jméno a/nebo heslo nesmí být prázdné", //m
|
||||
"aa1": "příchozí soubory:",
|
||||
"ab1": "deaktivovat no304",
|
||||
"ac1": "povolit no304",
|
||||
"ad1": "povolení no304 deaktivuje veškeré mezipaměti; zkuste to, pokud k304 nestačilo. To ovšem zapříčíní obrovské množství síťového provozu!",
|
||||
"ae1": "aktivní stahování:",
|
||||
"af1": "zobrazit nedávné nahrávání",
|
||||
"ag1": "zobrazit známé uživatele IdP", //m
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -60,7 +60,7 @@ Ls.deu = {
|
|||
["0..9", "zu 0%..90% springen"],
|
||||
["P", "Wiedergabe / Pause"],
|
||||
["S", "aktuell abgespielten Song auswählen"],
|
||||
["Y", "Sing herunterladen"],
|
||||
["Y", "Song herunterladen"],
|
||||
], [
|
||||
"image-viewer",
|
||||
["J/L, ←/→", "vorheriges / nächstes Bild"],
|
||||
|
|
@ -84,8 +84,8 @@ Ls.deu = {
|
|||
["M", "Textdatei schliessen"],
|
||||
["E", "Textdatei bearbeiten"],
|
||||
["S", "Textdatei auswählen (für Ausschneiden / Kopieren / Umbenennen)"],
|
||||
["Y", "Textdatei herunterladen"], //m
|
||||
["⇧ J", "json verschönern"], //m
|
||||
["Y", "Textdatei herunterladen"],
|
||||
["⇧ J", "json verschönern"],
|
||||
]
|
||||
],
|
||||
|
||||
|
|
@ -111,15 +111,15 @@ Ls.deu = {
|
|||
"gou": 'zum übergeordneter Ordner springen">hoch',
|
||||
"gon": 'zum nächsten Ordner springen">nächst.',
|
||||
"logout": "Abmelden ",
|
||||
"login": "Anmelden", //m
|
||||
"login": "Anmelden",
|
||||
"access": " Zugriff",
|
||||
"ot_close": "Submenu schliessen",
|
||||
"ot_search": "Dateien nach Attributen, Pfad/Name, Musiktags oder beliebiger Kombination suchen$N$N<code>foo bar</code> = muss «foo» und «bar» enthalten,$N<code>foo -bar</code> = muss «foo» aber nicht «bar» enthalten,$N<code>^yana .opus$</code> = beginnt mit «yana» und ist «opus»-Datei$N<code>"try unite"</code> = genau «try unite» enthalten$N$NDatumsformat ist iso-8601, z.B.$N<code>2009-12-31</code> oder <code>2020-09-12 23:30:00</code>",
|
||||
"ot_unpost": "unpost: lösche deine letzten Uploads oder breche unvollständige ab",
|
||||
"ot_bup": "bup: Basic Uploader, unterstützt sogar Neuheiten wie Netscape 4.0",
|
||||
"ot_mkdir": "mkdir: Neuen Ordner erstellen",
|
||||
"ot_md": "new-file: Neues Textdokument erstellen", //m
|
||||
"ot_msg": "msg: Eine Nachricht an das Server-Log schicken",
|
||||
"ot_mkdir": "mkdir: neuen Ordner erstellen",
|
||||
"ot_md": "new-file: neue Textdatei erstellen",
|
||||
"ot_msg": "msg: eine Nachricht an das Server-Log schicken",
|
||||
"ot_mp": "Media Player-Optionen",
|
||||
"ot_cfg": "Konfigurationsoptionen",
|
||||
"ot_u2i": 'up2k: Dateien hochladen (wenn du Schreibrechte hast) oder in den Suchmodus wechseln, um zu prüfen, ob sie bereits auf dem Server existieren$N$NUploads sind fortsetzbar, multithreaded und behalten Dateizeitstempel, verbrauchen aber mehr CPU als [🎈] (der einfache Uploader)<br /><br />während Uploads wird dieses Symbol zu einem Fortschrittsanzeiger!',
|
||||
|
|
@ -127,7 +127,7 @@ Ls.deu = {
|
|||
"ot_noie": 'Bitte benutze Chrome / Firefox / Edge',
|
||||
|
||||
"ab_mkdir": "Ordner erstellen",
|
||||
"ab_mkdoc": "Textdatei erstellen", //m
|
||||
"ab_mkdoc": "Textdatei erstellen",
|
||||
"ab_msg": "Nachricht an Server Log senden",
|
||||
|
||||
"ay_path": "zu Ordnern springen",
|
||||
|
|
@ -155,7 +155,7 @@ Ls.deu = {
|
|||
"ul_par": "Parallele Uploads:",
|
||||
"ut_rand": "Zufällige Dateinamen",
|
||||
"ut_u2ts": "Zuletzt geändert-Zeitstempel von$Ndeinem Dateisystem auf den Server übertragen\">📅",
|
||||
"ut_ow": "Existierende Dateien auf dem Server überschreiben?$N🛡️: Nie (generiert einen neuen Dateinamen)$N🕒: Überschreiben, wenn Server-Datei älter ist als meine$N♻️: Überschreiben, wenn der Dateiinhalt anders ist",
|
||||
"ut_ow": "Existierende Dateien auf dem Server überschreiben?$N🛡️: Nie (generiert einen neuen Dateinamen)$N🕒: Überschreiben, wenn Server-Datei älter ist als meine$N♻️: Überschreiben, wenn der Dateiinhalt anders ist$N⏭️: Vorhandene Dateien immer überspringen",
|
||||
"ut_mt": "Andere Dateien während des Uploads hashen$N$Nsolltest du deaktivieren, falls deine CPU oder Festplatte zum Flaschenhals werden könnte",
|
||||
"ut_ask": 'Vor dem Upload nach Bestätigung fragen">💭',
|
||||
"ut_pot": "Verbessert Upload-Geschwindigkeit$Nindem das UI weniger komplex gemacht wird",
|
||||
|
|
@ -206,7 +206,7 @@ Ls.deu = {
|
|||
"u_nav_b": '<a href="#" id="modal-ok">Dateien</a><a href="#" id="modal-ng">1 Ordner</a>',
|
||||
|
||||
"cl_opts": "Schalter",
|
||||
"cl_hfsz": "Dateigröße", //m
|
||||
"cl_hfsz": "Dateigröße",
|
||||
"cl_themes": "Themes",
|
||||
"cl_langs": "Sprache",
|
||||
"cl_ziptype": "Ordner Download",
|
||||
|
|
@ -220,18 +220,19 @@ Ls.deu = {
|
|||
"cl_reset": "zurücksetzen",
|
||||
"cl_hpick": "zum Verstecken, tippe auf Spaltenüberschriften in der Tabelle unten",
|
||||
"cl_hcancel": "Spaltenbearbeitung abgebrochen",
|
||||
"cl_rcm": "Rechtsklick-Menü",
|
||||
|
||||
"ct_grid": '田 Das Raster™',
|
||||
"ct_ttips": '◔ ◡ ◔">ℹ️ Tooltips',
|
||||
"ct_thumb": 'In Raster-Ansicht, zwischen Icons und Vorschau wechseln$NHotkey: T">🖼️ Vorschaubilder',
|
||||
"ct_csel": 'Benutze STRG und UMSCHALT für Dateiauswahl in Raster-Ansicht">sel',
|
||||
"ct_dl": 'Herunterladen erzwingen (nicht inline anzeigen), wenn eine datei angeklickt wird">dl', //m
|
||||
"ct_dl": 'Beim Klick auf Dateien sie immer herunterladen (nicht einbetten)">dl',
|
||||
"ct_ihop": 'Wenn die Bildanzeige geschlossen ist, scrolle runter zu den zuletzt angesehenen Dateien">g⮯',
|
||||
"ct_dots": 'Verstecke Dateien anzeigen (wenn erlaubt durch Server)">dotfiles',
|
||||
"ct_dots": 'Verstecke Dateien anzeigen (wenn durch den Server erlaubt)">dotfiles',
|
||||
"ct_qdel": 'Nur einmal fragen, wenn mehrere Dateien gelöscht werden">qdel',
|
||||
"ct_dir1st": 'Ordner vor Dateien sortieren">📁 zuerst',
|
||||
"ct_nsort": 'Natürliche Sortierung (für Dateinamen mit führenden Ziffern)">nsort',
|
||||
"ct_utc": 'Verwenden Sie UTC für alle Zeitangaben">UTC', //m
|
||||
"ct_utc": 'Für alle Zeitangaben UTC verwenden">UTC',
|
||||
"ct_readme": 'README.md in Dateiliste anzeigen">📜 readme',
|
||||
"ct_idxh": 'index.html anstelle von Dateiliste anzeigen">htm',
|
||||
"ct_sbars": 'Scrollbars zeigen">⟊',
|
||||
|
|
@ -262,6 +263,7 @@ Ls.deu = {
|
|||
"cdt_lim": "max. Anz. Dateien, die in einem Ordner gezeigt werden sollen",
|
||||
"cdt_ask": "beim Runterscrollen nach $NAktion fragen statt mehr,$NDateien zu laden",
|
||||
"cdt_hsort": "Menge an Sortierregeln (<code>,sorthref</code>) in Media-URLs enthalten sein sollen. Ein Wert von 0 sorgt dafür, dass Sortierregeln in Media-URLs ignoriert werden",
|
||||
"cdt_ren": "spezielles Rechtsklick-Menü aktivieren, das Browser-Menü ist weiterhin mit Shift + Rechtsklick erreichbar",
|
||||
|
||||
"tt_entree": "Navpane anzeigen (Ordnerbaum Sidebar)$NHotkey: B",
|
||||
"tt_detree": "Breadcrumbs anzeigen$NHotkey: B",
|
||||
|
|
@ -307,8 +309,8 @@ Ls.deu = {
|
|||
"mt_c2owa": "opus-weba, für iOS 17.5 und neuer\">owa",
|
||||
"mt_c2caf": "opus-caf, für iOS 11 bis 17\">caf",
|
||||
"mt_c2mp3": "benutze dieses Format für ältere Geräte\">mp3",
|
||||
"mt_c2flac": "beste Klangqualität, aber große Downloads\">flac", //m
|
||||
"mt_c2wav": "unkomprimierte Wiedergabe (noch größer)\">wav", //m
|
||||
"mt_c2flac": "beste Klangqualität, aber riesige Downloads\">flac",
|
||||
"mt_c2wav": "unkomprimierte Wiedergabe (noch größer)\">wav",
|
||||
"mt_c2ok": "Gute Wahl, Chef!",
|
||||
"mt_c2nd": "Das ist nicht das empfohlene Ausgabeformat für dein Gerät, aber passt schon",
|
||||
"mt_c2ng": "Dein Gerät scheint dieses Ausgabeformat nicht zu unterstützen, aber lass trotzdem mal probieren",
|
||||
|
|
@ -332,6 +334,7 @@ Ls.deu = {
|
|||
"mm_eunk": "Unbekannter Fehler",
|
||||
"mm_e404": "Konnte Datei nicht abspielen; Fehler 404: Datei nicht gefunden.",
|
||||
"mm_e403": "Konnte Datei nicht abspielen; Fehler 403: Zugriff verweigert.\n\nDrücke F5 zum Neuladen, vielleicht wurdest du abgemeldet",
|
||||
"mm_e415": "Konnte Datei nicht abspielen; Fehler 415: Umwandlung der Datei fehlgeschlagen; Serverlogs prüfen.", //m
|
||||
"mm_e500": "Konnte Datei nicht abspielen; Fehler 500: Prüfe die Serverlogs.",
|
||||
"mm_e5xx": "Konnte Datei nicht abspielen; Server Fehler ",
|
||||
"mm_nof": "finde keine weiteren Audiodateien in der Nähe",
|
||||
|
|
@ -419,15 +422,16 @@ Ls.deu = {
|
|||
"fcc_warn": '{0} Elemente in die Zwischenablage kopiert\n\nAber: nur <b>dieses</b> Browsertab kann sie einfügen\n(da deine Auswahl so abartig riesig war)',
|
||||
|
||||
"fp_apply": "Diese Namen verwenden",
|
||||
"fp_skip": "Konflikte überspringen",
|
||||
"fp_ecut": "Kopiere erst ein paar Dateien / Ordner, um sie einzufügen\n\nTipp: Ausschneiden und Kopieren funktioniert über Browsertabs hinweg",
|
||||
"fp_ename": "{0} Elemente konnten nicht verschoben werden, weil bereits andere Dateien mit diesen Namen existieren. Gib ihnen unten neue Namen um fortzufahren, oder lass das Feld leer zum Überspringen:",
|
||||
"fcp_ename": "{0} Elemente konnten nicht kopiert werden, weil bereits andere Dateien mit diesen Namen existieren. Gib ihnen unten neue Namen um fortzufahren, oder lass das Feld leer zum Überspringen:",
|
||||
"fp_ename": '{0} Elemente konnten nicht verschoben werden, weil bereits andere Dateien mit diesen Namen existieren. Gib ihnen unten neue Namen um fortzufahren, oder lass das Feld leer um sie zu überspringen ("Konflikte überspringen" macht das automatisch):',
|
||||
"fcp_ename": '{0} Elemente konnten nicht kopiert werden, weil bereits andere Dateien mit diesen Namen existieren. Gib ihnen unten neue Namen um fortzufahren, oder lass das Feld leer um sie zu überspringen ("Konflikte überspringen" macht das automatisch):',
|
||||
"fp_emore": "Es gibt noch ein paar Dateinamen, die geändert werden müssen",
|
||||
"fp_ok": "Verschieben OK",
|
||||
"fcp_ok": "Kopieren OK",
|
||||
"fp_busy": "Verschiebe {0} Elemente...\n\n{1}",
|
||||
"fcp_busy": "Kopiere {0} Elemente...\n\n{1}",
|
||||
"fp_abrt": "Abbrechen...", //m
|
||||
"fp_abrt": "wird abgebrochen...",
|
||||
"fp_err": "Verschieben fehlgeschlagen:\n",
|
||||
"fcp_err": "Kopieren fehlgeschlagen:\n",
|
||||
"fp_confirm": "Diese {0} Elemente hierher verschieben?",
|
||||
|
|
@ -440,8 +444,8 @@ Ls.deu = {
|
|||
"fcp_both_b": '<a href="#" id="modal-ok">Kopieren</a><a href="#" id="modal-ng">Hochladen</a>',
|
||||
|
||||
"mk_noname": "Tipp' mal vorher lieber einen Namen in das Textfeld links, bevor du das machst :p",
|
||||
"nmd_i1": "Fügen Sie auch die gewünschte Dateiendung hinzu, z. B. <code>.txt</code>", //m
|
||||
"nmd_i2": "Sie können nur <code>.md</code>-Dateien erstellen, da Ihnen die Löschberechtigung fehlt", //m
|
||||
"nmd_i1": "Füge auch die Dateiendung hinzu, z.B. <code>.md</code>",
|
||||
"nmd_i2": "Du kannst nur <code>.md</code>-Dateien erstellen, da dir Lösch-Rechte fehlen",
|
||||
|
||||
"tv_load": "Textdatei wird geladen:\n\n{0}\n\n{1}% ({2} von {3} MiB geladen)",
|
||||
"tv_xe1": "Konnte Textdatei nicht laden:\n\nFehler ",
|
||||
|
|
@ -452,7 +456,7 @@ Ls.deu = {
|
|||
"tvt_prev": "Vorheriges Dokument zeigen$NHotkey: i\">⬆ vorh.",
|
||||
"tvt_next": "Nächstes Dokument zeigen$NHotkey: K\">⬇ nächst.",
|
||||
"tvt_sel": "Wählt diese Datei aus ( zum Ausschneiden / Kopieren / Löschen / ... )$NHotkey: S\">ausw.",
|
||||
"tvt_j": "json verschönern$NHotkey: shift-J\">j", //m
|
||||
"tvt_j": "json verschönern$NHotkey: shift-J\">j",
|
||||
"tvt_edit": "Datei im Texteditor zum Bearbeiten öffnen$NHotkey: E\">✏️ bearb.",
|
||||
"tvt_tail": "Datei auf Veränderungen überwachen; Neue Zeilen werden in Echtzeit angezeigt\">📡 folgen",
|
||||
"tvt_wrap": "Zeilenumbruch\">↵",
|
||||
|
|
@ -522,14 +526,14 @@ Ls.deu = {
|
|||
"fu_xe2": "404: Datei nicht gefunden??",
|
||||
|
||||
"fz_tar": "Unkomprimierte GNU TAR-Datei (Linux / Mac)",
|
||||
"fz_pax": "Unkomprimierte pax-format TAR-Datei (etwas langsamer)", //m
|
||||
"fz_pax": "Unkomprimierte pax-Format TAR-Datei (langsamer)",
|
||||
"fz_targz": "GNU-TAR mit gzip Level 3 Kompression$N$Nüblicherweise recht langsam,$Nbenutze stattdessen ein unkomprimiertes TAR",
|
||||
"fz_tarxz": "GNU-TAR mit xz level 1 Kompression$N$Nüblicherweise recht langsam,$Nbenutze stattdessen ein unkomprimiertes TAR",
|
||||
"fz_zip8": "ZIP mit UTF8-Dateinamen (könnte kaputt gehen auf Windows 7 oder älter)",
|
||||
"fz_zipd": "ZIP mit traditionellen CP437-Dateinamen, für richtig alte Software",
|
||||
"fz_zipc": "CP437 mit CRC32 früh berechnet,$Nfür MS-DOS PKZIP v2.04g (Oktober 1993)$N(braucht länger zum Verarbeiten, bevor der Download starten kann)",
|
||||
"fz_zipc": "CP437 mit früh berechnetem CRC32,$Nfür MS-DOS PKZIP v2.04g (Oktober 1993)$N(braucht länger zum Verarbeiten, bevor der Download starten kann)",
|
||||
|
||||
"un_m1": "Unten kannst du deine neusten Uploads löschen (oder Unvollständige abbrechen)",
|
||||
"un_m1": "Unten kannst du deine neusten Uploads löschen (oder unvollständige abbrechen)",
|
||||
"un_upd": "Neu laden",
|
||||
"un_m4": "Oder die unten sichtbaren Dateien teilen:",
|
||||
"un_ulist": "Anzeigen",
|
||||
|
|
@ -637,6 +641,23 @@ Ls.deu = {
|
|||
"ur_um": "Fertig;\n{0} Uploads OK,\n{1} Uploads fehlgeschlagen, sorry",
|
||||
"ur_sm": "Fertig;\n{0} Uploads gefunden auf dem Server,\n{1} Dateien NICHT gefunden auf dem Server",
|
||||
|
||||
"rc_opn": "öffnen",
|
||||
"rc_ply": "abspielen",
|
||||
"rc_pla": "als Audio abspielen",
|
||||
"rc_txt": "als Text öffnen",
|
||||
"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",
|
||||
"rc_pst": "einfügen",
|
||||
"rc_nfo": "neuer Ordner",
|
||||
"rc_nfi": "neue Datei",
|
||||
"rc_sal": "alles auswählen",
|
||||
"rc_sin": "auswahl umkehren",
|
||||
|
||||
"lang_set": "Neuladen um Änderungen anzuwenden?",
|
||||
|
||||
"splash": {
|
||||
|
|
@ -655,11 +676,11 @@ Ls.deu = {
|
|||
"j1": "k304 trennt die Clientverbindung bei jedem HTTP 304, was Bugs mit problematischen Proxies vorbeugen kann (z.B. nicht ladenden Seiten), macht Dinge aber generell langsamer",
|
||||
"k1": "Client-Einstellungen zurücksetzen",
|
||||
"l1": "Melde dich an für mehr:",
|
||||
"ls3": "Anmelden", //m
|
||||
"lu4": "Benutzername", //m
|
||||
"lp4": "Passwort", //m
|
||||
"lo3": "“{0}” überall abmelden", //m
|
||||
"lo2": "Dies beendet die Sitzung in allen Browsern", //m
|
||||
"ls3": "Anmelden",
|
||||
"lu4": "Benutzername",
|
||||
"lp4": "Passwort",
|
||||
"lo3": "“{0}” überall abmelden",
|
||||
"lo2": "Das beendet die Sitzung in allen Browsern",
|
||||
"m1": "Willkommen zurück,",
|
||||
"n1": "404 Nicht gefunden ┐( ´ -`)┌",
|
||||
"o1": 'or maybe you don\'t have access -- try a password or <a href="' + SR + '/?h">go home</a>',
|
||||
|
|
@ -678,11 +699,14 @@ Ls.deu = {
|
|||
"ta1": "Trage zuerst dein Passwort ein",
|
||||
"ta2": "Wiederhole dein Passwort zur Bestätigung:",
|
||||
"ta3": "Da stimmt etwas nicht; probier's nochmal",
|
||||
"nop": "FEHLER: Passwort darf nicht leer sein",
|
||||
"nou": "FEHLER: Benutzername und/oder Passwort dürfen nicht leer sein",
|
||||
"aa1": "Eingehende Dateien:",
|
||||
"ab1": "no304 deaktivieren",
|
||||
"ac1": "no304 aktivieren",
|
||||
"ad1": "Das Aktivieren von no304 deaktiviert jegliche Form von Caching; probier dies, wenn k304 nicht genug war. Dies verschwendet eine grosse Menge Netzwerk-Traffic!",
|
||||
"ae1": "Aktive Downloads:",
|
||||
"af1": "Zeige neue Uploads",
|
||||
"ag1": "Bekannte IdP-Benutzer anzeigen", //m
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -155,7 +155,7 @@ Ls.epo = {
|
|||
"ul_par": "paralelaj alŝutoj:",
|
||||
"ut_rand": "hazardigi dosiernomojn",
|
||||
"ut_u2ts": "kopii la tempon de lasta modifo$Nel via dosiersistemo al la servilo\">📅",
|
||||
"ut_ow": "ĉu anstataŭigi dosierojn ĉe la servilo?$N🛡️: neniam (dosiero estos alŝutita kun nova dosiernomo)$N🕒: anstataŭigi, se servila dosiero estas pli malnova ol via$N♻️: ĉiam anstataŭigi, se dosieroj estas malsamaj",
|
||||
"ut_ow": "ĉu anstataŭigi dosierojn ĉe la servilo?$N🛡️: neniam (dosiero estos alŝutita kun nova dosiernomo)$N🕒: anstataŭigi, se servila dosiero estas pli malnova ol via$N♻️: ĉiam anstataŭigi, se dosieroj estas malsamaj$N⏭️: senkondiĉe preterlasi ĉiujn ekzistantajn dosierojn", //m
|
||||
"ut_mt": "daŭri kalkuladon de kontrolsumoj por aliaj dosieroj dum alŝutado$N$Nmalŝaltinda, se via procesoro aŭ disko ne estas sufiĉe rapidaj",
|
||||
"ut_ask": 'peti konfirmon antaŭ komenco de alŝutado">💭',
|
||||
"ut_pot": "plirapidigi alŝutadon por malrapidaj komputiloj$Nper malkomplikado de fasado",
|
||||
|
|
@ -220,6 +220,7 @@ Ls.epo = {
|
|||
"cl_reset": "restarigi",
|
||||
"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", //m
|
||||
|
||||
"ct_grid": '田 krado',
|
||||
"ct_ttips": '◔ ◡ ◔">ℹ️ ŝpruchelpiloj',
|
||||
|
|
@ -262,6 +263,7 @@ Ls.epo = {
|
|||
"cdt_lim": "maks. nombro de dosieroj por montri en dosierujo",
|
||||
"cdt_ask": "je malsupro de paĝo, peti por ago$Nanstataŭ ŝarĝi pli da dosieroj",
|
||||
"cdt_hsort": "kiom da ordigo-reguloj (<code>,sorthref</code>) inkludi en adreso de la paĝo. Se agordita kiel 0, reguloj, inkluditaj en la adreso, estos ignoritaj",
|
||||
"cdt_ren": "ebligi propran dekstra-klakan menuon, la normala menuo restas alirebla per shift + dekstra klako", //m
|
||||
|
||||
"tt_entree": "montri arbovidan navig-panelon$NFulmoklavo: B",
|
||||
"tt_detree": "montri paĝnivelan navig-panelon$NFulmoklavo: B",
|
||||
|
|
@ -332,6 +334,7 @@ Ls.epo = {
|
|||
"mm_eunk": "Nekonata eraro",
|
||||
"mm_e404": "Ne povas ludi aŭdiaĵon; eraro 404: Dosiero ne trovita.",
|
||||
"mm_e403": "Ne povas ludi aŭdiaĵon; eraro 403: Atingo malpermesita.\n\nKlopodu reŝargi paĝon per klavo F5, eble via seanco senvalidiĝis",
|
||||
"mm_e415": "Ne povas ludi aŭdiaĵon; eraro 415: Transkodigo de dosiero malsukcesis; rigardu la protokolojn de servilo.", //m
|
||||
"mm_e500": "Ne povas ludi aŭdiaĵon; eraro 500: Rigardu la protokolojn de servilo.",
|
||||
"mm_e5xx": "Ne povas ludi aŭdiaĵon; servila eraro ",
|
||||
"mm_nof": "neniuj aŭdio-dosieroj trovitaj proksime",
|
||||
|
|
@ -419,9 +422,10 @@ Ls.epo = {
|
|||
"fcc_warn": '{0} aĵoj kopiitaj al tondujo\n\nnur <b>ĉi tiu</b> langeto de retumilo povas alglui ilin\n(pro la grando de elektaĵo)',
|
||||
|
||||
"fp_apply": "uzi ĉi tiujn nomojn",
|
||||
"fp_skip": "preterpasi konfliktojn", //m
|
||||
"fp_ecut": "unue eltondi aŭ kopii dosier(uj)ojn, do alglui ĝin poste\n\nnoto: tondujo ankaŭ funkcias inter aliaj langetoj de retumilo",
|
||||
"fp_ename": "{0} aĵoj ne povas esti movitaj, ĉar iliaj nomoj estas jam uzataj. Alinomi ilin sube aŭ lasi la nomokampojn malplenaj por preterpasi:",
|
||||
"fcp_ename": "{0} aĵoj ne povas esti kopiitaj, ĉar iliaj nomoj estas jam uzataj. Alinomi ilin sube aŭ lasi la nomokampojn malplenaj por preterpasi:",
|
||||
"fp_ename": "{0} aĵoj ne povas esti movitaj, ĉar iliaj nomoj estas jam uzataj. Alinomi ilin sube aŭ lasi la nomokampojn malplenaj (\"preterpasi konfliktojn\") por preterpasi:", //m
|
||||
"fcp_ename": "{0} aĵoj ne povas esti kopiitaj, ĉar iliaj nomoj estas jam uzataj. Alinomi ilin sube aŭ lasi la nomokampojn malplenaj (\"preterpasi konfliktojn\") por preterpasi:", //m
|
||||
"fp_emore": "ankoraŭ restas koincidoj de dosiernomoj, kiuj bezonas solvon",
|
||||
"fp_ok": "movado sukcesis",
|
||||
"fcp_ok": "kopiado sukcesis",
|
||||
|
|
@ -440,7 +444,7 @@ Ls.epo = {
|
|||
"fcp_both_b": '<a href="#" id="modal-ok">Kopii</a><a href="#" id="modal-ng">Alŝuti</a>',
|
||||
|
||||
"mk_noname": "tajpu nomon en tekstokampo maldekstre antaŭ vi faras ĉi tion :p",
|
||||
"nmd_i1": "vi povas aldoni la deziratan sufikson, ekzemple <code>.txt</code>", //m
|
||||
"nmd_i1": "vi povas aldoni la deziratan sufikson, ekzemple <code>.md</code>", //m
|
||||
"nmd_i2": "vi povas krei nur <code>.md</code>-dosierojn ĉar vi ne havas forigan permeson", //m
|
||||
|
||||
"tv_load": "Ŝargado de teksto-dokumento:\n\n{0}\n\n{1}% ({2} da {3} MiB ŝargita)",
|
||||
|
|
@ -637,6 +641,23 @@ Ls.epo = {
|
|||
"ur_um": "Finita;\n{0} alŝutoj sukcesis,\n{1} alŝutoj malsukcesis, pardonon",
|
||||
"ur_sm": "Finita;\n{0} dosieroj trovitaj ĉe la servilo,\n{1} dosieroj NE trovitaj ĉe la servilo",
|
||||
|
||||
"rc_opn": "malfermi", //m
|
||||
"rc_ply": "Ludi", //m
|
||||
"rc_pla": "Ludi kiel sonon", //m
|
||||
"rc_txt": "malfermi en dosiera vidilo", //m
|
||||
"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
|
||||
"rc_pst": "alglui", //m
|
||||
"rc_nfo": "nova dosierujo", //m
|
||||
"rc_nfi": "nova dosiero", //m
|
||||
"rc_sal": "elekti ĉion", //m
|
||||
"rc_sin": "inversigi elekton", //m
|
||||
|
||||
"lang_set": "ĉu reŝargi paĝon por efektivigi lingvo-ŝanĝon?",
|
||||
|
||||
"splash": {
|
||||
|
|
@ -678,6 +699,8 @@ Ls.epo = {
|
|||
"ta1": "entajpu novan pasvorton unue",
|
||||
"ta2": "retajpu por konfirmi:",
|
||||
"ta3": "tajpo-eraro; bonvolu provu denove",
|
||||
"nop": "ERARO: Pasvorto ne povas esti malplena", //m
|
||||
"nou": "ERARO: Uzantnomo kaj/aŭ pasvorto ne povas esti malplena", //m
|
||||
"aa1": "aktivaj alŝutoj:",
|
||||
"ab1": "malŝalti no304-on",
|
||||
"ac1": "ŝalti no304-on",
|
||||
|
|
|
|||
|
|
@ -155,7 +155,7 @@ Ls.fin = {
|
|||
"ul_par": "rinnakkaislatausten lkm:",
|
||||
"ut_rand": "satunnaisgeneroidut tiedostonimet",
|
||||
"ut_u2ts": "kopioi viimeksi muokattu aikaleima$Ntiedostojärjestelmästäsi palvelimelle\">📅",
|
||||
"ut_ow": "korvaa olemassa olevat tiedostot palvelimella?$N🛡️: ei koskaan (luo sen sijaan uuden tiedostonimen)$N🕒: korvaa jos palvelintiedosto on vanhempi kuin omasi$N♻️: korvaa aina jos tiedostot ovat erilaisia",
|
||||
"ut_ow": "korvaa olemassa olevat tiedostot palvelimella?$N🛡️: ei koskaan (luo sen sijaan uuden tiedostonimen)$N🕒: korvaa jos palvelintiedosto on vanhempi kuin omasi$N♻️: korvaa aina jos tiedostot ovat erilaisia$N⏭️: ohita kaikki olemassa olevat tiedostot ehdottomasti", //m
|
||||
"ut_mt": "jatka muiden tiedostojen tiivisteiden laskemista latauksen aikana$N$Nkannattanee poistaa käytöstä, mikäli prosessori tai kovalevy on vanhempaa mallia",
|
||||
"ut_ask": 'kysy vahvistusta ennen latauksen aloittamista">💭',
|
||||
"ut_pot": "paranna latausnopeutta hitailla laitteilla$Nvähentämällä käyttöliittymän monimutkaisuutta",
|
||||
|
|
@ -220,6 +220,7 @@ Ls.fin = {
|
|||
"cl_reset": "palauta",
|
||||
"cl_hpick": "napauta sarakeotsikoita piilottaaksesi alla olevassa taulukossa",
|
||||
"cl_hcancel": "sarakkeiden piilotus peruttu",
|
||||
"cl_rcm": "hiiren oikean painikkeen valikko", //m
|
||||
|
||||
"ct_grid": '田 kuvanäkymä',
|
||||
"ct_ttips": '◔ ◡ ◔">ℹ️ vihjelaatikot',
|
||||
|
|
@ -262,6 +263,7 @@ Ls.fin = {
|
|||
"cdt_lim": "tiedostojen enimmäismäärä näytettäväksi hakemistossa",
|
||||
"cdt_ask": "sivun lopussa, sen sijaan että lataa $Nautomaattisesti lisää tiedostoja, kysy mitä tehdä",
|
||||
"cdt_hsort": "kuinka monta lajittelusääntöä (<code>,sorthref</code>) sisällyttää media-URL:eihin. Tämän asettaminen nollaan jättää myös huomioimatta media-linkeissä sisällytetyt lajittelusäännöt kun napsautat niitä",
|
||||
"cdt_ren": "ota käyttöön mukautettu valikko, tavallinen valikko on käytettävissä painamalla shift ja napsauttamalla oikealla", //m
|
||||
|
||||
"tt_entree": "näytä navigointipaneeli$NPikanäppäin: B",
|
||||
"tt_detree": "näytä linkkipolku$NPikanäppäin: B",
|
||||
|
|
@ -332,6 +334,7 @@ Ls.fin = {
|
|||
"mm_eunk": "Tuntematon virhe",
|
||||
"mm_e404": "Kappaletta ei voitu toistaa; virhe 404: Tiedostoa ei löydy.",
|
||||
"mm_e403": "Kappaletta ei voitu toistaa; virhe 403: Pääsy kielletty.\n\nKokeile painaa F5 päivittääksesi, ehkä kirjauduit ulos",
|
||||
"mm_e415": "Kappaletta ei voitu toistaa; virhe 415: Tiedoston muunnos epäonnistui; tarkista palvelinlokit.", //m
|
||||
"mm_e500": "Kappaletta ei voitu toistaa; virhe 500: Tarkista palvelinlokit.",
|
||||
"mm_e5xx": "Kappaletta ei voitu toistaa; palvelinvirhe ",
|
||||
"mm_nof": "ei löydy enempää äänitiedostoja lähistöltä",
|
||||
|
|
@ -419,9 +422,10 @@ Ls.fin = {
|
|||
"fcc_warn": 'kopioitiin {0} kohdetta leikepöydälle\n\nmutta: vain <b>tämä</b> selain-välilehti voi liittää ne\n(koska valinta on niin valtavan suuri)',
|
||||
|
||||
"fp_apply": "käytä näitä nimiä",
|
||||
"fp_skip": "ohita ristiriidat", //m
|
||||
"fp_ecut": "leikkaa tai kopioi ensin joitakin tiedostoja / hakemistoja liitettäväksi / siirrettäväksi\n\nhuom: voit leikata / liittää eri selain-välilehtien välillä",
|
||||
"fp_ename": "{0} kohdetta ei voida siirtää tänne koska nimet ovat jo käytössä. Anna niille uudet nimet alla jatkaaksesi, tai tyhjennä nimi ohittaaksesi ne:",
|
||||
"fcp_ename": "{0} kohdetta ei voida kopioida tänne koska nimet ovat jo käytössä. Anna niille uudet nimet alla jatkaaksesi, tai tyhjennä nimi ohittaaksesi ne:",
|
||||
"fp_ename": "{0} kohdetta ei voida siirtää tänne koska nimet ovat jo käytössä. Anna niille uudet nimet alla jatkaaksesi, tai tyhjennä nimi (\"ohita ristiriidat\") ohittaaksesi ne:", //m
|
||||
"fcp_ename": "{0} kohdetta ei voida kopioida tänne koska nimet ovat jo käytössä. Anna niille uudet nimet alla jatkaaksesi, tai tyhjennä nimi (\"ohita ristiriidat\") ohittaaksesi ne:", //m
|
||||
"fp_emore": "tiedostonimien törmäyksiä on vielä korjaamatta",
|
||||
"fp_ok": "siirto OK",
|
||||
"fcp_ok": "kopiointi OK",
|
||||
|
|
@ -440,7 +444,7 @@ Ls.fin = {
|
|||
"fcp_both_b": '<a href="#" id="modal-ok">Kopioi</a><a href="#" id="modal-ng">Lähetä</a>',
|
||||
|
||||
"mk_noname": "kirjoita nimi vasemmalla olevaan tekstikenttään ennen kuin teet tuon :p",
|
||||
"nmd_i1": "voit myös lisätä haluamasi tiedostopäätteen, esimerkiksi <code>.txt</code>", //m
|
||||
"nmd_i1": "voit myös lisätä haluamasi tiedostopäätteen, esimerkiksi <code>.md</code>", //m
|
||||
"nmd_i2": "voit luoda vain <code>.md</code>-tiedostoja, koska sinulla ei ole poistolupaa", //m
|
||||
|
||||
"tv_load": "Ladataan tekstidokumenttia:\n\n{0}\n\n{1}% ({2} / {3} Mt ladattu)",
|
||||
|
|
@ -637,6 +641,23 @@ Ls.fin = {
|
|||
"ur_um": "Valmis;\n{0} latausta OK,\n{1} latausta epäonnistui, pahoittelen",
|
||||
"ur_sm": "Valmis;\n{0} tiedostoa löytyi palvelimelta,\n{1} tiedostoa EI löytynyt palvelimelta",
|
||||
|
||||
"rc_opn": "avaa", //m
|
||||
"rc_ply": "toista", //m
|
||||
"rc_pla": "toista äänenä", //m
|
||||
"rc_txt": "avaa tiedostoselaimessa", //m
|
||||
"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
|
||||
"rc_pst": "Liitä", //m
|
||||
"rc_nfo": "uusi kansio", //m
|
||||
"rc_nfi": "uusi tiedosto", //m
|
||||
"rc_sal": "valitse kaikki", //m
|
||||
"rc_sin": "käännä valinta", //m
|
||||
|
||||
"lang_set": "ladataanko sivu uudestaan kielen vaihtamiseksi?",
|
||||
|
||||
"splash": {
|
||||
|
|
@ -678,6 +699,8 @@ Ls.fin = {
|
|||
"ta1": "täytä ensin uusi salasana",
|
||||
"ta2": "toista vahvistaaksesi uuden salasanan:",
|
||||
"ta3": "löytyi kirjoitusvirhe; yritä uudelleen",
|
||||
"nop": "VIRHE: Salasana ei voi olla tyhjä", //m
|
||||
"nou": "VIRHE: Käyttäjänimi ja/tai salasana ei voi olla tyhjä", //m
|
||||
"aa1": "saapuvat:",
|
||||
"ab1": "poista no304 käytöstä",
|
||||
"ac1": "ota no304 käyttöön",
|
||||
|
|
|
|||
|
|
@ -155,7 +155,7 @@ Ls.fra = {
|
|||
"ul_par": "téléversements parallèles:",
|
||||
"ut_rand": "attribution de noms de fichiers aléatoires",
|
||||
"ut_u2ts": "copier l'horodatage de dernière modification$Nde votre système de fichiers vers le serveur\">📅",
|
||||
"ut_ow": "écraser les fichiers existants sur le serveur?$N🛡️: jamais (générera un nouveau nom de fichier à la place)$N🕒: écraser si le fichier sur le serveur est plus ancien que le vôtre$N♻️: toujours écraser si les fichiers sont différents",
|
||||
"ut_ow": "écraser les fichiers existants sur le serveur?$N🛡️: jamais (générera un nouveau nom de fichier à la place)$N🕒: écraser si le fichier sur le serveur est plus ancien que le vôtre$N♻️: toujours écraser si les fichiers sont différents$N⏭️: ignorer systématiquement tous les fichiers existants", //m
|
||||
"ut_mt": "continuer à calculer la somme de contrôle d'autres fichiers pendant le téléversement$N$Npeut-être désactiver si votre CPU ou HDD est la cause de perte de performances",
|
||||
"ut_ask": 'demander confirmation avant le début du téléversement">💭',
|
||||
"ut_pot": "améliorer la vitesse de téléversement sur les appareils lents$Nen simplifiant l'interface utilisateur",
|
||||
|
|
@ -220,6 +220,7 @@ Ls.fra = {
|
|||
"cl_reset": "réinitialiser",
|
||||
"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
|
||||
|
||||
"ct_grid": '田 grille',
|
||||
"ct_ttips": '◔ ◡ ◔">ℹ️ infobulles',
|
||||
|
|
@ -262,6 +263,7 @@ Ls.fra = {
|
|||
"cdt_lim": "nombre maximum de fichiers à afficher dans un dossier",
|
||||
"cdt_ask": "lorsque vous faites défiler vers le bas,$Nau lieu de charger plus de fichiers,$Ndemander quoi faire",
|
||||
"cdt_hsort": "combien de règles de tri (<code>,sorthref</code>) à inclure dans les media-URLs. Définir cette valeur à 0 ignorera également les règles de tri incluses dans les liens média lorsque vous cliquez dessus.",
|
||||
"cdt_ren": "activer le menu contextuel personnalisé, le menu normal reste accessible avec shift + clic droit", //m
|
||||
|
||||
"tt_entree": "afficher le panneau de navigation (arborescence des dossiers)$NHotkey: B",
|
||||
"tt_detree": "afficher le fil d’Ariane$NHotkey: B",
|
||||
|
|
@ -332,6 +334,7 @@ Ls.fra = {
|
|||
"mm_eunk": "Erreur inconnue",
|
||||
"mm_e404": "Impossible de lire l'audio ; erreur 404 : fichier introuvable.",
|
||||
"mm_e403": "Impossible de lire l'audio ; erreur 403 : accès refusé.\n\nEssayez d'appuyer sur F5 pour recharger, peut-être que vous avez été déconnecté",
|
||||
"mm_e415": "Impossible de lire l'audio ; erreur 415 : échec de la conversion du fichier ; vérifiez les journaux du serveur.", //m
|
||||
"mm_e500": "Impossible de lire l'audio ; erreur 500 : vérifiez les journaux du serveur.",
|
||||
"mm_e5xx": "Impossible de lire l'audio ; erreur serveur ",
|
||||
"mm_nof": "Pas d'autres fichiers audio trouvés par ici",
|
||||
|
|
@ -419,9 +422,10 @@ Ls.fra = {
|
|||
"fcc_warn": 'copié {0} éléments dans le presse-papiers\n\nmais : seul <b>cet</b> onglet peut les coller\n(puisque la sélection est si absolument massive)',
|
||||
|
||||
"fp_apply": "utiliser ces noms",
|
||||
"fp_skip": "ignorer les conflits", //m
|
||||
"fp_ecut": "en premier, coupez ou copiez quelques fichiers / dossiers à coller / déplacer\n\nnote: vous pouvez couper / coller a travers different onglets",
|
||||
"fp_ename": "{0} éléments ne peuvent pas être déplacés ici parceque leurs noms sont déjà pris. Donnez leurs un nouveau nom ci-dessous pour continuer, ou laissez les vides pour les sauter:",
|
||||
"fcp_ename": "{0} éléments ne peuvent pas être copiés ici parce que les noms sont déjà pris. Donnez-leur un nouveau nom ci-dessous pour continuer, ou laissez-les vides pour les sauter :",
|
||||
"fp_ename": "{0} éléments ne peuvent pas être déplacés ici parce que leurs noms sont déjà pris. Donnez-leur un nouveau nom ci-dessous pour continuer, ou laissez le nom vide (\"ignorer les conflits\") pour les sauter :", //m
|
||||
"fcp_ename": "{0} éléments ne peuvent pas être copiés ici parce que les noms sont déjà pris. Donnez-leur un nouveau nom ci-dessous pour continuer, ou laissez le nom vide (\"ignorer les conflits\") pour les sauter :", //m
|
||||
"fp_emore": "il reste encore des collisions de noms de fichiers à corriger",
|
||||
"fp_ok": "déplacement OK",
|
||||
"fcp_ok": "copie OK",
|
||||
|
|
@ -440,7 +444,7 @@ Ls.fra = {
|
|||
"fcp_both_b": '<a href="#" id="modal-ok">Copier</a><a href="#" id="modal-ng">Téléverser</a>',
|
||||
|
||||
"mk_noname": "entrez un nom dans le champ de texte à gauche avant de faire ça :p",
|
||||
"nmd_i1": "ajoutez aussi l’extension souhaitée, par exemple <code>.txt</code>", //m
|
||||
"nmd_i1": "ajoutez aussi l’extension souhaitée, par exemple <code>.md</code>", //m
|
||||
"nmd_i2": "vous ne pouvez créer que des fichiers <code>.md</code> car vous n’avez pas la permission d’effacer", //m
|
||||
|
||||
"tv_load": "Chargement du document texte:\n\n{0}\n\n{1}% ({2} de {3} MiB chargés)",
|
||||
|
|
@ -637,6 +641,23 @@ Ls.fra = {
|
|||
"ur_um": "Terminé;\n{0} téléversements OK,\n{1} téléversements échoués, désolé",
|
||||
"ur_sm": "Terminé;\n{0} fichiers trouvés sur le serveur,\n{1} fichiers NON trouvés sur le serveur",
|
||||
|
||||
"rc_opn": "ouvrir", //m
|
||||
"rc_ply": "Lire", //m
|
||||
"rc_pla": "Lire comme audio", //m
|
||||
"rc_txt": "ouvrir dans le visionneur de fichiers", //m
|
||||
"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
|
||||
"rc_pst": "coller", //m
|
||||
"rc_nfo": "nouveau dossier", //m
|
||||
"rc_nfi": "nouveau fichier", //m
|
||||
"rc_sal": "tout sélectionner", //m
|
||||
"rc_sin": "inverser la sélection", //m
|
||||
|
||||
"lang_set": "rafraîchir pour que les changements prennent effet ?",
|
||||
|
||||
"splash": {
|
||||
|
|
@ -678,11 +699,14 @@ Ls.fra = {
|
|||
"ta1": "entrez d'abord votre nouveau mot de passe",
|
||||
"ta2": "répétez pour confirmer le nouveau mot de passe :",
|
||||
"ta3": "une faute de frappe a été détectée ; veuillez réessayer.",
|
||||
"nop": "ERREUR : Le mot de passe ne peut pas être vide", //m
|
||||
"nou": "ERREUR : Le nom d’utilisateur et/ou le mot de passe ne peut pas être vide", //m
|
||||
"aa1": "fichiers entrants :",
|
||||
"ab1": "désactiver no304",
|
||||
"ac1": "activer no304",
|
||||
"ad1": "l'activation de no304 désactivera toute mise en cache ; essayez ceci si k304 n'était pas suffisant. Cela va générer un trafic réseau considérable !",
|
||||
"ae1": "téléchargements actifs :",
|
||||
"af1": "afficher les derniers téléchargements",
|
||||
"ag1": "afficher les utilisateurs IdP connus", //m
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -155,7 +155,7 @@ Ls.grc = {
|
|||
"ul_par": "παράλληλες μεταφορτώσεις:",
|
||||
"ut_rand": "τυχαιοποίηση ονομάτων αρχείων",
|
||||
"ut_u2ts": "αντιγραφή της τελευταίας τροποποιημένης χρονοσφραγίδας αλλαγής$Nαπό το σύστημά σου στον server\">📅",
|
||||
"ut_ow": "αντικατάσταση σε ήδη υπάρχοντα αρχεία του server?$N🛡️: ποτέ (θα δημιουργηθεί νέο όνομα)$N🕒: αν το αρχείο του server είναι παλαιότερο$N♻️: πάντα να αντικαθίστανται αν διαφέρουν",
|
||||
"ut_ow": "αντικατάσταση σε ήδη υπάρχοντα αρχεία του server?$N🛡️: ποτέ (θα δημιουργηθεί νέο όνομα)$N🕒: αν το αρχείο του server είναι παλαιότερο$N♻️: πάντα να αντικαθίστανται αν διαφέρουν$N⏭️: παράλειψη όλων των υπαρχόντων αρχείων χωρίς όρους", //m
|
||||
"ut_mt": "συνέχιση υπολογισμού hash για άλλα αρχεία κατά τη μεταφόρτωση$N$Nαπενεργοποίησέ το αν η CPU ή ο δίσκος σου ζορίζονται",
|
||||
"ut_ask": 'επιβεβαίωση πριν ξεκινήσει η μεταφόρτωση">💭',
|
||||
"ut_pot": "βελτίωση ταχύτητας μεταφόρτωσης σε αργές συσκευές$Nμε απλοποίηση του UI",
|
||||
|
|
@ -220,6 +220,7 @@ Ls.grc = {
|
|||
"cl_reset": "επανεκκίνηση",
|
||||
"cl_hpick": "πάτησε στις κεφαλίδες στηλών για να τις κρύψεις στον πίνακα παρακάτω",
|
||||
"cl_hcancel": "η απόκρυψη στηλών ακυρώθηκε",
|
||||
"cl_rcm": "μενού δεξιού κλικ", //m
|
||||
|
||||
"ct_grid": '田 το πλέγμα',
|
||||
"ct_ttips": '◔ ◡ ◔">ℹ️ συμβουλές εργαλείων',
|
||||
|
|
@ -262,6 +263,7 @@ Ls.grc = {
|
|||
"cdt_lim": "μέγιστος αριθμός αρχείων προς εμφάνιση σε ένα φάκελο",
|
||||
"cdt_ask": "όταν φτάνεις στο τέλος,$Nαντί να φορτώσει περισσότερα αρχεία,$Nρωτά τι να κάνει",
|
||||
"cdt_hsort": "πόσους κανόνες ταξινόμησης (<code>,sorthref</code>) να συμπεριλάβει σε URLs πολυμέσων. Αν το βάλεις 0 αγνοεί και κανόνες ταξινόμησης στους συνδέσμους πολυμέσων",
|
||||
"cdt_ren": "ενεργοποίηση προσαρμοσμένου μενού δεξιού κλικ, το κανονικό μενού είναι προσβάσιμο με shift + δεξί κλικ", //m
|
||||
|
||||
"tt_entree": "εμφάνιση navpane (δέντρο διαδρομών)$NΠλήκτρο συντόμευσης: B",
|
||||
"tt_detree": "εμφάνιση breadcrumbs (καρτέλες διαδρομών)$NΠλήκτρο συντόμευσης: B",
|
||||
|
|
@ -332,6 +334,7 @@ Ls.grc = {
|
|||
"mm_eunk": "Άγνωστο σφάλμα",
|
||||
"mm_e404": "Αδύνατη η αναπαραγωγή ήχου; σφάλμα 404: Το αρχείο δεν βρέθηκε.",
|
||||
"mm_e403": "Αδύνατη η αναπαραγωγή ήχου; σφάλμα 403: Άρνηση πρόσβασης.\n\nΔοκίμασε F5 για επαναφόρτωση, ίσως να έχεις αποσυνδεθεί",
|
||||
"mm_e415": "Αδύνατη η αναπαραγωγή ήχου; σφάλμα 415: Απέτυχε η μετατροπή αρχείου; έλεγξε τα logs του διακομιστή.", //m
|
||||
"mm_e500": "Αδύνατη η αναπαραγωγή ήχου; σφάλμα 500: Έλεγξε τα logs του διακομιστή.",
|
||||
"mm_e5xx": "Αδύνατη η αναπαραγωγή ήχου; σφάλμα διακομιστή",
|
||||
"mm_nof": "δεν βρέθηκαν άλλα αρχεία ήχου τριγύρω",
|
||||
|
|
@ -419,9 +422,10 @@ Ls.grc = {
|
|||
"fcc_warn": "αντιγράφηκαν {0} αντικείμενα στο πρόχειρο\n\nαλλά: μόνο <b>αυτή</b> η καρτέλα browser μπορεί να τα επικολλήσει\n(λόγω πολύ μεγάλης επιλογής)",
|
||||
|
||||
"fp_apply": "χρησιμοποίησε αυτά τα ονόματα",
|
||||
"fp_skip": "αγνόησε συγκρούσεις", //m
|
||||
"fp_ecut": "πρώτα κάνε αποκοπή ή αντιγραφή κάποιων αρχείων / φακέλων για επικόλληση / μετακίνηση\n\nσημείωση: μπορείς να αποκόπτεις / επικολλάς ανάμεσα σε διαφορετικές καρτέλες browser",
|
||||
"fp_ename": "τα {0} αντικείμενα δεν μπορούν να μετακινηθούν εδώ γιατί τα ονόματα υπάρχουν ήδη. Δώσε νέα ονόματα παρακάτω για να συνεχίσεις, ή άφησε κενό για να τα αγνοήσεις:",
|
||||
"fcp_ename": "τα {0} αντικείμενα δεν μπορούν να αντιγραφούν εδώ γιατί τα ονόματα υπάρχουν ήδη. Δώσε νέα ονόματα παρακάτω για να συνεχίσεις, ή άφησε κενό για να τα αγνοήσεις:",
|
||||
"fp_ename": "τα {0} αντικείμενα δεν μπορούν να μετακινηθούν εδώ γιατί τα ονόματα υπάρχουν ήδη. Δώσε νέα ονόματα παρακάτω για να συνεχίσεις, ή άφησε κενό (\"αγνόησε συγκρούσεις\") για να τα αγνοήσεις:", //m
|
||||
"fcp_ename": "τα {0} αντικείμενα δεν μπορούν να αντιγραφούν εδώ γιατί τα ονόματα υπάρχουν ήδη. Δώσε νέα ονόματα παρακάτω για να συνεχίσεις, ή άφησε κενό (\"αγνόησε συγκρούσεις\") για να τα αγνοήσεις:", //m
|
||||
"fp_emore": "υπάρχουν ακόμα συγκρούσεις ονομάτων που πρέπει να διορθωθούν",
|
||||
"fp_ok": "μετακίνηση OK",
|
||||
"fcp_ok": "αντιγραφή OK",
|
||||
|
|
@ -440,7 +444,7 @@ Ls.grc = {
|
|||
"fcp_both_b": '<a href="#" id="modal-ok">Αντιγραφή</a><a href="#" id="modal-ng">Μεταφόρτωση</a>',
|
||||
|
||||
"mk_noname": "γράψε ένα όνομα στο πεδίο κειμένου αριστερά πριν το κάνεις :p",
|
||||
"nmd_i1": "μπορείτε επίσης να προσθέσετε την κατάληξη που θέλετε, όπως <code>.txt</code>", //m
|
||||
"nmd_i1": "μπορείτε επίσης να προσθέσετε την κατάληξη που θέλετε, όπως <code>.md</code>", //m
|
||||
"nmd_i2": "μπορείτε να δημιουργήσετε μόνο αρχεία <code>.md</code> επειδή δεν έχετε δικαίωμα διαγραφής", //m
|
||||
|
||||
"tv_load": "Φόρτωση αρχείου κειμένου:\n\n{0}\n\n{1}% ({2} από {3} MiB φορτωμένα)",
|
||||
|
|
@ -637,6 +641,23 @@ Ls.grc = {
|
|||
"ur_um": "Ολοκληρώθηκε;\n{0} μεταφορτώσεις είναι OK,\n{1} μεταφορτώσεις απέτυχαν, συγγνώμη",
|
||||
"ur_sm": "Ολοκληρώθηκε;\n{0} αρχεία βρέθηκαν στο διακομιστή,\n{1} αρχεία ΔΕΝ βρέθηκαν στο διακομιστή",
|
||||
|
||||
"rc_opn": "άνοιγμα", //m
|
||||
"rc_ply": "αναπαραγωγή", //m
|
||||
"rc_pla": "αναπαραγωγή ως ήχος", //m
|
||||
"rc_txt": "άνοιγμα στον προβολέα αρχείων", //m
|
||||
"rc_md": "άνοιγμα στον επεξεργαστή κειμένου", //m
|
||||
"rc_dl": "λήψη", //m
|
||||
"rc_zip": "λήψη ως αρχείο", //m
|
||||
"rc_cpl": "ἀντίγραφε σύνδεσμον", //m
|
||||
"rc_del": "διαγραφή", //m
|
||||
"rc_cut": "αποκοπή", //m
|
||||
"rc_cpy": "αντιγραφή", //m
|
||||
"rc_pst": "επικόλληση", //m
|
||||
"rc_nfo": "νέος φάκελος", //m
|
||||
"rc_nfi": "νέο αρχείο", //m
|
||||
"rc_sal": "επιλογή όλων", //m
|
||||
"rc_sin": "αντιστροφή επιλογής", //m
|
||||
|
||||
"lang_set": "ανανέωση σελίδας για εφαρμογή της αλλαγής;",
|
||||
|
||||
"splash": {
|
||||
|
|
@ -678,11 +699,14 @@ Ls.grc = {
|
|||
"ta1": "συμπλήρωσε πρώτα το νέο σου κωδικό",
|
||||
"ta2": "επανέλαβε για να επιβεβαιώσεις το νέο κωδικό:",
|
||||
"ta3": "βρέθηκε τυπογραφικό λάθος· δοκίμασε ξανά",
|
||||
"nop": "ΣΦΑΛΜΑ: Ο κωδικός πρόσβασης δεν μπορεί να είναι κενός", //m
|
||||
"nou": "ΣΦΑΛΜΑ: Το όνομα χρήστη και/ή ο κωδικός πρόσβασης δεν μπορεί να είναι κενό", //m
|
||||
"aa1": "εισερχόμενα αρχεία:",
|
||||
"ab1": "απενεργοποίηση no304",
|
||||
"ac1": "ενεργοποίηση no304",
|
||||
"ad1": "η ενεργοποίηση του no304 θα απενεργοποιήσει όλη την προσωρινή αποθήκευση· δοκίμασέ το αν το k304 δεν ήταν αρκετό. Προσοχή, θα σπαταλήσει τεράστιο όγκο δικτυακής κίνησης!",
|
||||
"ae1": "ενεργές μεταφορτώσεις:",
|
||||
"af1": "προβολή πρόσφατων μεταφορτώσεων",
|
||||
"ag1": "εμφάνιση γνωστών χρηστών IdP", //m
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -155,7 +155,7 @@ Ls.ita = {
|
|||
"ul_par": "caricamenti paralleli:",
|
||||
"ut_rand": "randomizza nomi file",
|
||||
"ut_u2ts": "copia il timestamp di ultima modifica$Ndal tuo filesystem al server\">📅",
|
||||
"ut_ow": "sovrascrivere file esistenti sul server?$N🛡️: mai (genererà un nuovo nome file)$N🕒: sovrascrivi se il file del server è più vecchio del tuo$N♻️: sovrascrivi sempre se i file sono diversi",
|
||||
"ut_ow": "sovrascrivere file esistenti sul server?$N🛡️: mai (genererà un nuovo nome file)$N🕒: sovrascrivi se il file del server è più vecchio del tuo$N♻️: sovrascrivi sempre se i file sono diversi$N⏭️: ignora sempre tutti i file esistenti", //m
|
||||
"ut_mt": "continua l'hashing di altri file durante il caricamento$N$NProva a disabilitare se la tua CPU o HDD è un collo di bottiglia",
|
||||
"ut_ask": 'chiedi conferma prima che inizi il caricamento">💭',
|
||||
"ut_pot": "migliora la velocità di caricamento su dispositivi lenti$Nrendendo l'interfaccia meno complessa",
|
||||
|
|
@ -220,6 +220,7 @@ Ls.ita = {
|
|||
"cl_reset": "reset",
|
||||
"cl_hpick": "tocca le intestazioni delle colonne per nascondere nella tabella sottostante",
|
||||
"cl_hcancel": "nascondere colonne annullato",
|
||||
"cl_rcm": "menu contestuale", //m
|
||||
|
||||
"ct_grid": '田 griglia',
|
||||
"ct_ttips": '◔ ◡ ◔">ℹ️ tooltip',
|
||||
|
|
@ -262,6 +263,7 @@ Ls.ita = {
|
|||
"cdt_lim": "numero massimo di file da mostrare in una cartella",
|
||||
"cdt_ask": "quando scorri verso il fondo,$Ninvece di caricare più file,$Nchiedi cosa fare",
|
||||
"cdt_hsort": "quante regole di ordinamento (<code>,sorthref</code>) includere negli URL multimediali. Impostandolo a 0 ignorerà anche le regole di ordinamento incluse nei link multimediali quando li clicchi",
|
||||
"cdt_ren": "abilita il menu contestuale personalizzato, il menu normale è accessibile con shift + clic destro", //m
|
||||
|
||||
"tt_entree": "mostra pannello nav (barra laterale albero directory)$NTasto rapido: B",
|
||||
"tt_detree": "mostra breadcrumb$NTasto rapido: B",
|
||||
|
|
@ -332,6 +334,7 @@ Ls.ita = {
|
|||
"mm_eunk": "Errore Sconosciuto",
|
||||
"mm_e404": "Non è stato possibile riprodurre audio; errore 404: File non trovato.",
|
||||
"mm_e403": "Non è stato possibile riprodurre audio; errore 403: Accesso negato.\n\nProva a premere F5 per ricaricare, forse sei stato disconnesso",
|
||||
"mm_e415": "Non è stato possibile riprodurre audio; errore 415: Conversione del file non riuscita; controlla i log del server.", //m
|
||||
"mm_e500": "Non è stato possibile riprodurre audio; errore 500: Controlla i log del server.",
|
||||
"mm_e5xx": "Non è stato possibile riprodurre audio; errore server ",
|
||||
"mm_nof": "non trovo altri file audio nelle vicinanze",
|
||||
|
|
@ -419,9 +422,10 @@ Ls.ita = {
|
|||
"fcc_warn": 'copiati {0} elementi negli appunti\n\nma: solo <b>questa</b> scheda-browser può incollarli\n(dato che la selezione è così assolutamente massiva)',
|
||||
|
||||
"fp_apply": "usa questi nomi",
|
||||
"fp_skip": "salta conflitti", //m
|
||||
"fp_ecut": "prima taglia o copia alcuni file / cartelle da incollare / spostare\n\nnota: puoi tagliare / incollare attraverso diverse schede del browser",
|
||||
"fp_ename": "{0} elementi non possono essere spostati qui perché i nomi sono già presi. Dai loro nuovi nomi qui sotto per continuare, o lascia vuoto il nome per saltarli:",
|
||||
"fcp_ename": "{0} elementi non possono essere copiati qui perché i nomi sono già presi. Dai loro nuovi nomi qui sotto per continuare, o lascia vuoto il nome per saltarli:",
|
||||
"fp_ename": "{0} elementi non possono essere spostati qui perché i nomi sono già presi. Dai loro nuovi nomi qui sotto per continuare, o lascia vuoto il nome (\"salta conflitti\") per saltarli:", //m
|
||||
"fcp_ename": "{0} elementi non possono essere copiati qui perché i nomi sono già presi. Dai loro nuovi nomi qui sotto per continuare, o lascia vuoto il nome (\"salta conflitti\") per saltarli:", //m
|
||||
"fp_emore": "ci sono ancora alcune collisioni di nomi file rimaste da risolvere",
|
||||
"fp_ok": "spostamento OK",
|
||||
"fcp_ok": "copia OK",
|
||||
|
|
@ -440,7 +444,7 @@ Ls.ita = {
|
|||
"fcp_both_b": '<a href="#" id="modal-ok">Copia</a><a href="#" id="modal-ng">Carica</a>',
|
||||
|
||||
"mk_noname": "scrivi un nome nel campo di testo a sinistra prima di farlo :p",
|
||||
"nmd_i1": "puoi anche aggiungere l’estensione che vuoi, per esempio <code>.txt</code>", //m
|
||||
"nmd_i1": "puoi anche aggiungere l’estensione che vuoi, per esempio <code>.md</code>", //m
|
||||
"nmd_i2": "puoi creare solo file <code>.md</code> perché non hai il permesso di eliminare", //m
|
||||
|
||||
"tv_load": "Caricando documento di testo:\n\n{0}\n\n{1}% ({2} di {3} MiB caricati)",
|
||||
|
|
@ -637,6 +641,23 @@ Ls.ita = {
|
|||
"ur_um": "Finito;\n{0} caricamenti OK,\n{1} caricamenti falliti, scusa",
|
||||
"ur_sm": "Finito;\n{0} file trovati sul server,\n{1} file NON trovati sul server",
|
||||
|
||||
"rc_opn": "apri", //m
|
||||
"rc_ply": "riproduci", //m
|
||||
"rc_pla": "riproduci come audio", //m
|
||||
"rc_txt": "apri nel visualizzatore di file", //m
|
||||
"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
|
||||
"rc_pst": "incolla", //m
|
||||
"rc_nfo": "nuova cartella", //m
|
||||
"rc_nfi": "nuovo file", //m
|
||||
"rc_sal": "seleziona tutto", //m
|
||||
"rc_sin": "inverti selezione", //m
|
||||
|
||||
"lang_set": "aggiornare per rendere effettivo il cambiamento?",
|
||||
|
||||
"splash": {
|
||||
|
|
@ -678,6 +699,8 @@ Ls.ita = {
|
|||
"ta1": "devi prima inserire una nuova password",
|
||||
"ta2": "ripeti per confermare la nuova password:",
|
||||
"ta3": "errore di digitazione; riprova",
|
||||
"nop": "ERRORE: La password non può essere vuota", //m
|
||||
"nou": "ERRORE: Il nome utente e/o la password non possono essere vuoti", //m
|
||||
"aa1": "in arrivo:",
|
||||
"ab1": "disattiva no304",
|
||||
"ac1": "attiva no304",
|
||||
|
|
|
|||
712
copyparty/web/tl/jpn.js
Normal file
712
copyparty/web/tl/jpn.js
Normal file
|
|
@ -0,0 +1,712 @@
|
|||
|
||||
//末尾が //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 を使用します<br /><br />アップロード中、このアイコンは進行状況インジケーターになります。',
|
||||
"ot_u2w": 'up2k: 再開サポート付きのファイルのアップロード(ブラウザを閉じて、後で同じファイルをドロップします)$N$Nマルチスレッドで、ファイルのタイムスタンプが保持されますが、[🎈](基本的なアップローダー)よりも多くの CPU を使用します。<br /><br />アップロード中は、このアイコンが進行状況インジケーターになります。',
|
||||
"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プレイリストに追加 (後で <code>📻コピー</code> をクリック)",
|
||||
"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": "ファイル / フォルダを<br>ここにドロップしてください(またはクリックしてください)",
|
||||
"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": "ファイルがキューに追加されました</b><br>しかし、別のブラウザタブにはビジー状態のup2kがあり、<br>それが終わるまで待機します",
|
||||
"ul_btnlk": "サーバー構成によりこのスイッチはこの状態にロックされています",
|
||||
|
||||
"udt_up": "アップロード",
|
||||
"udt_srch": "検索",
|
||||
"udt_drop": "ここにドロップ",
|
||||
|
||||
"u_nav_m": '<h6>はい、何かもってますか?</h6><code>Enter</code> = ファイル(1つ以上)\n<code>ESC</code> = 1つのフォルダ(サブフォルダを含む)',
|
||||
"u_nav_b": '<a href="#" id="modal-ok">ファイル</a><a href="#" id="modal-ng">フォルダ</a>',
|
||||
|
||||
"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これはハッシュチェックを単純な<em>"これはサーバー上で同じファイルサイズですか?"</em>に置き換えます。そのため、ファイルの内容が異なる場合はアップロードされません。$N$Nアップロードが完了したらこれをオフにし、同じファイルを再度 "アップロード" してクライアントに検証させる必要があります。\">turbo",
|
||||
|
||||
"cut_datechk": "turboボタンが有効になっていなければ効果はありません$N$Nyolo要素をわずかに減らしサーバー上のファイルのタイムスタンプが一致するかどうかを確認します。$N$N<em>理論的</em>にはほとんどの未完了 / 破損したアップロードを把握できるはずですが、その後ターボを無効にして検証パスを実行する代わりにはなりません。\">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✅ <b>信頼できない</b>接続で有効にする、$N❌ 低速接続では<b>無効</b>にする\">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": "再生するには<code>Enter/OK</code>を押します\n編集するには<code>ESC/キャンセル</code>を押してください",
|
||||
"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_e415": "オーディオを再生できませんでした。エラー415: ファイルの変換に失敗しました。サーバーログを確認してください。", //m
|
||||
"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": '<div id="blazy">表示中 <code>{0}</code> / <code>{1}</code> ファイル; <a href="#" id="bd_more">{2}件を表示</a> または <a href="#" id="bd_all">すべて表示</a></div>',
|
||||
"fbd_all": '<div id="blazy">表示中 <code>{0}</code> / <code>{1}</code> ファイル; <a href="#" id="bd_all">すべて表示</a></div>',
|
||||
"f_anota": "{1}件のアイテムのうち {0}件が選択されました;\nフォルダ全体を選択するには、まず一番下までスクロールします",
|
||||
|
||||
"f_dls": '現在のフォルダ内のファイルリンクは\nダウンロードリンクに変更されました',
|
||||
|
||||
"f_partial": "現在アップロード中のファイルを安全にダウンロードするには、同じファイル名で<code>.PARTIAL</code>拡張子がないファイルをクリックしてください。これを行うにはキャンセルまたはEscキーを押してください。\n\nOK / Enter を押すとこの警告は無視され、代わりに<code>.PARTIAL</code>スクラッチファイルのダウンロードが続行されますが、ほとんどの場合データが破損することになります。",
|
||||
|
||||
"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": "<code>Enter/OK</code>を押してクリップボードに保存します\n<code>ESC/キャンセル</code>を押して閉じます",
|
||||
|
||||
"frt_dec": "壊れたファイル名を修正するかもしれません\">url-decode",
|
||||
"frt_rst": "変更したファイル名を元の名前に戻す\">↺ リセット",
|
||||
"frt_abrt": "中止してこのウィンドウを閉じる\">❌ キャンセル",
|
||||
"frb_apply": "名前の変更を適用",
|
||||
"fr_adv": "バッチ / メタデータ / パターン名変更\">詳細設定",
|
||||
"fr_case": "大文字と小文字を区別する正規表現\">case",
|
||||
"fr_win": "Windowsで安全な名前; <code><>:"\\|?*</code>を日本語の全角文字に置き換える\">win",
|
||||
"fr_slash": "<code>/</code>を新しいフォルダが作成されない文字に置き換える\">no /",
|
||||
"fr_re": "元のファイル名に適用する正規表現検索パターン; キャプチャグループは、<code>(1)</code> や <code>(2)</code> のように、以下のフォーマットフィールドで参照することができるので、",
|
||||
"fr_fmt": "foobar2000 を参考にしています:$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}件は、<code>win</code> および/または <code>no /</code> により変更されました。\n\nこれらを変更後の新しい名前で続行しますか?",
|
||||
|
||||
"fd_ok": "削除成功",
|
||||
"fd_err": "削除失敗:\n",
|
||||
"fd_none": "何も削除されませんでした; サーバー構成 (xbd) によってブロックされた可能性があります。",
|
||||
"fd_busy": "{0}件のアイテムを削除中...\n\n{1}",
|
||||
"fd_warn1": "これら {0}件のアイテムを削除しますか?",
|
||||
"fd_warn2": "<b>最後の警告!</b> 取り消し不可。削除しますか?",
|
||||
|
||||
"fc_ok": "{0}件のアイテムを切り取り",
|
||||
"fc_warn": '{0}件のアイテムを切り取りました\n\nしかし: <b>この</b>ブラウザタブでのみ貼り付けることができます\n(選択範囲が非常に大きいため)',
|
||||
|
||||
"fcc_ok": "{0}件のアイテムをクリップボードにコピー",
|
||||
"fcc_warn": '{0}件のアイテムをクリップボードにコピーしました\n\nしかし: <b>この</b>ブラウザタブでのみ貼り付けることができます\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": '<h6>貼り付けるものを選択</h6><code>Enter</code> = «{1}»から{0}件のファイルを移動\n<code>ESC</code> = デバイスから{2}件のファイルをアップロード',
|
||||
"fcp_both_m": '<h6>貼り付けるものを選択</h6><code>Enter</code> = «{1}»から{0}件のファイルをコピー\n<code>ESC</code> = デバイスから{2}件のファイルをアップロード',
|
||||
"fp_both_b": '<a href="#" id="modal-ok">移動</a><a href="#" id="modal-ng">アップロード</a>',
|
||||
"fcp_both_b": '<a href="#" id="modal-ok">コピー</a><a href="#" id="modal-ng">アップロード</a>',
|
||||
|
||||
"mk_noname": "それをする前に左側のテキストフィールドに名前を入力してください :p",
|
||||
"nmd_i1": "必要なファイル拡張子も追加します。例: <code>.md</code>",
|
||||
"nmd_i2": "削除権限がないため、<code>.md</code> ファイルのみを作成できます",
|
||||
|
||||
"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": "[📜<em>readme</em>] 無効 [⚙️] -- ドキュメントを非表示",
|
||||
|
||||
"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": '<b>警告:</b> サーバーが混雑しているため未完了のアップロードを表示できません; しばらくしてから「更新」リンクをクリックしてください',
|
||||
"un_noc": '<b>警告:</b> 完全にアップロードされたファイルの未投稿化はサーバー設定で有効化 / 許可されていません',
|
||||
"un_max": "最初の2000件のファイルを表示中 (フィルターを使用)",
|
||||
"un_avail": "{0}件の最近のアップロードを削除できます<br />未完了のものは{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": '現在のブラウザは驚くほど古いです -- <a href="#" onclick="goto(\'bup\')">代わりにbup</a>を使った方がいいかもしれません',
|
||||
"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": '<a href="#">potato UI</a>に切り替える (アップロード速度が向上する可能性があります)',
|
||||
"u_depot": '<a href="#">fancy UI</a>に切り替える(アップロード速度が低下する可能性があります)',
|
||||
"u_gotpot": 'アップロード速度を向上させるためにポテトUIに切り替えました。\n\n同意しない場合は遠慮なく元に戻してください!',
|
||||
"u_pott": "<p>ファイル: <b>{0}</b> 完了済み, <b>{1}</b> 失敗, <b>{2}</b> ビジー, <b>{3}</b> キュー内</p>",
|
||||
"u_ever": "これは基本的なアップローダーです; up2kは少なくとも以下が必要です<br>chrome 21 // firefox 13 // edge 12 // opera 12 // safari 5.1",
|
||||
"u_su2k": 'これは基本的なアップローダーです; <a href="#" id="u2yea">up2k</a>の方が優れています',
|
||||
"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次のファイルをスキップするには<code>OK/Enter</code>を押してください、\n除外しない場合は<code>キャンセル/ESC</code>を押してそれらもアップロードしてください:\n\n',
|
||||
"u_just1": '\nファイルを1つだけ選択した方がうまくいくかもしれません',
|
||||
"u_ff_many": "<b>Linux / MacOS / Android,</b>を使用している場合この量のファイルにより<a href=\"https://bugzilla.mozilla.org/show_bug.cgi?id=1790500\" target=\"_blank\">Firefoxがクラッシュする<em>可能性</em></a>があります\nその場合はもう一度お試しください(または Chrome を使用してください)。",
|
||||
"u_up_life": "このアップロードは {0}後に\nサーバーから削除されます。",
|
||||
"u_asku": 'これらの{0}件のファイルを<code>{1}</code>にアップロードします',
|
||||
"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": "他のウィンドウ/タブに切り替えるときに<br />パフォーマンスの低下を防ぐにはこのテキストをクリックしてください",
|
||||
"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": ' <a href="{0}">httpsに切り替える(推奨)</a>もしくは',
|
||||
"u_emtleak3": ' ',
|
||||
"u_emtleakc": '次を試してください:\n<ul><li><code>F5</code>を押してページを更新してください</li><li>次に <code>mt</code>を無効にします <code>⚙️ settings</code></li>ボタンをクリックし<li>もう一度アップロードしてみてください</li></ul>アップロードは少し遅くなりますが、仕方ないでしょう。\nご迷惑をおかけして申し訳ありません!\n\n追記: chrome v107ではこの問題が<a href="https://bugs.chromium.org/p/chromium/issues/detail?id=1354816" target="_blank">修正されました</a>',
|
||||
"u_emtleakf": '次を試してください:\n<ul><li><code>F5</code>を押してページを更新してください</li><li>アップロードUIで<code>🥔</code>(ポテト)を有効にして<li>もう一度アップロードを試してください</li></ul>\n追記: firefoxは<a href="https://bugzilla.mozilla.org/show_bug.cgi?id=1790500" target="_blank">バグ修正が行われることを期待しています</a>',
|
||||
"u_s404": "サーバー上にファイルが見つかりません",
|
||||
"u_expl": "説明",
|
||||
"u_maxconn": "ほとんどのブラウザではこの数が6に制限されていますが、Firefoxでは<code>about:config</code>の<code>connections-per-server</code>でこの数を増やすことができます",
|
||||
"u_tu": '<p class="warn">警告: ターボが有効になっている場合、<span> クライアントは不完全なアップロードを検出して再開できない可能性があります; ターボボタンのツールチップを見る</span></p>',
|
||||
"u_ts": '<p class="warn">警告: ターボが有効になっている場合、<span> 検索結果が間違っている可能性があります; ターボボタンのツールチップを見る</span></p>',
|
||||
"u_turbo_c": "サーバー設定でターボが無効になっています",
|
||||
"u_turbo_g": "このボリューム内でディレクトリ一覧表示の権限がないため\nターボを無効化しています",
|
||||
"u_life_cfg": '<input id="lifem" p="60" />分後に自動削除(もしくは<input id="lifeh" p="3600" />時間)',
|
||||
"u_life_est": 'アップロードは削除されます <span id="lifew" tt="local time">---</span>',
|
||||
"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": "やあ、見知らぬ人 <small>(あなたはログインしていません)</small>",
|
||||
"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ごとにクライアントが切断されバグのあるプロキシがスタックするのを防ぐことができます(突然ページが読み込まれなくなるなど)<em>ただし</em>全体的に速度が低下します",
|
||||
"k1": "クライアント設定をリセット",
|
||||
"l1": "詳しくはログインしてください:",
|
||||
"ls3": "ログイン",
|
||||
"lu4": "ユーザー名",
|
||||
"lp4": "パスワード",
|
||||
"lo3": "「{0}」をどこからでもログアウトする",
|
||||
"lo2": "これによりすべてのブラウザでセッションが終了します",
|
||||
"m1": "おかえりなさい、", // トピックノート: "おかえりなさい、ユーザー名"
|
||||
"n1": "404 not found ┐( ´ -`)┌",
|
||||
"o1": 'あるいはアクセスできないかもしれません -- パスワードを試すか <a href="' + SR + '/?h">ホームに戻ってください</a>',
|
||||
"p1": "403 forbiddena ~┻━┻",
|
||||
"q1": 'パスワードを使うか <a href="' + SR + '/?h">ホームに戻ってください</a>',
|
||||
"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ユーザーを管理できるページへのリンクです
|
||||
}
|
||||
};
|
||||
|
|
@ -155,7 +155,7 @@ Ls.kor = {
|
|||
"ul_par": "동시 업로드:",
|
||||
"ut_rand": "파일명 무작위로 만들기",
|
||||
"ut_u2ts": "사용자 파일 시스템의 마지막 수정 타임스탬프를$N서버에 복사\">📅",
|
||||
"ut_ow": "서버에 있는 기존 파일을 덮어쓸까요?$N🛡️: 안 함 (대신 새 파일 이름 생성)$N🕒: 서버 파일이 더 오래된 경우 덮어쓰기$N♻️: 파일이 다르면 항상 덮어쓰기",
|
||||
"ut_ow": "서버에 있는 기존 파일을 덮어쓸까요?$N🛡️: 안 함 (대신 새 파일 이름 생성)$N🕒: 서버 파일이 더 오래된 경우 덮어쓰기$N♻️: 파일이 다르면 항상 덮어쓰기$N⏭️: 기존 파일을 모두 무조건 건너뜀", //m
|
||||
"ut_mt": "업로드 중 다른 파일 해싱 계속하기$N$NCPU 또는 HDD가 병목 현상을 일으키는 경우 비활성화하세요",
|
||||
"ut_ask": '업로드 시작 전 확인 요청">💭',
|
||||
"ut_pot": "느린 기기에서 UI를 단순화하여$N업로드 속도 향상",
|
||||
|
|
@ -220,6 +220,7 @@ Ls.kor = {
|
|||
"cl_reset": "초기화",
|
||||
"cl_hpick": "아래 테이블에서 숨기고 싶은 열의 헤더를 탭하세요",
|
||||
"cl_hcancel": "열 숨기기가 중단되었습니다",
|
||||
"cl_rcm": "우클릭 메뉴", //m
|
||||
|
||||
"ct_grid": "田 그리드",
|
||||
"ct_ttips": '◔ ◡ ◔">ℹ️ 도움말',
|
||||
|
|
@ -262,6 +263,7 @@ Ls.kor = {
|
|||
"cdt_lim": "폴더에 표시할 최대 파일 수",
|
||||
"cdt_ask": "맨 아래로 스크롤할 때$N더 많은 파일을 불러오는 대신$N무엇을 할지 묻기",
|
||||
"cdt_hsort": "미디어 URL에 포함할 정렬 규칙 (<code>,sorthref</code>)의 수. 0으로 설정하면 미디어 링크를 클릭할 때 포함된 정렬 규칙도 무시됩니다.",
|
||||
"cdt_ren": "사용자 지정 우클릭 메뉴를 활성화합니다. shift 키를 누른 채 우클릭하면 기본 메뉴를 사용할 수 있습니다", //m
|
||||
|
||||
"tt_entree": "탐색 창 (디렉터리 트리 사이드바) 표시$N단축키: B",
|
||||
"tt_detree": "이동 경로 표시$N단축키: B",
|
||||
|
|
@ -332,6 +334,7 @@ Ls.kor = {
|
|||
"mm_eunk": "알 수 없는 오류",
|
||||
"mm_e404": "오디오를 재생할 수 없습니다; 오류 404: 파일을 찾을 수 없습니다.",
|
||||
"mm_e403": "오디오를 재생할 수 없습니다; 오류 403: 접근이 거부되었습니다.\n\nF5를 눌러 새로고침 해보세요, 로그아웃되었을 수 있습니다",
|
||||
"mm_e415": "오디오를 재생할 수 없습니다; 오류 415: 파일 변환에 실패했습니다; 서버 로그를 확인하세요.", //m
|
||||
"mm_e500": "오디오를 재생할 수 없습니다; 오류 500: 서버 로그를 확인하세요.",
|
||||
"mm_e5xx": "오디오를 재생할 수 없습니다; 서버 오류 ",
|
||||
"mm_nof": "주변에서 더 이상 오디오 파일을 찾을 수 없습니다",
|
||||
|
|
@ -419,9 +422,10 @@ Ls.kor = {
|
|||
"fcc_warn": "{0}개 항목을 클립보드에 복사했습니다\n\n하지만: 선택 항목이 너무 커서 <b>이</b> 브라우저 탭에서만 붙여넣을 수 있습니다",
|
||||
|
||||
"fp_apply": "이 이름 사용",
|
||||
"fp_skip": "충돌 건너뛰기", //m
|
||||
"fp_ecut": "붙여넣거나 이동하려면 먼저 파일/폴더를 잘라내거나 복사하세요\n\n참고: 다른 브라우저 탭 간에 잘라내기/붙여넣기를 할 수 있습니다",
|
||||
"fp_ename": "이름이 이미 사용 중이므로 {0}개 항목을 여기로 이동할 수 없습니다. 계속하려면 아래에 새 이름을 지정하거나, 이름을 비워두면 건너뜁니다:",
|
||||
"fcp_ename": "이름이 이미 사용 중이므로 {0}개 항목을 여기로 복사할 수 없습니다. 계속하려면 아래에 새 이름을 지정하거나, 이름을 비워두면 건너뜁니다:",
|
||||
"fp_ename": "이름이 이미 사용 중이므로 {0}개 항목을 여기로 이동할 수 없습니다. 계속하려면 아래에 새 이름을 지정하거나, 이름을 비워두면 (\"충돌 건너뛰기\") 건너뜁니다:", //m
|
||||
"fcp_ename": "이름이 이미 사용 중이므로 {0}개 항목을 여기로 복사할 수 없습니다. 계속하려면 아래에 새 이름을 지정하거나, 이름을 비워두면 (\"충돌 건너뛰기\") 건너뜁니다:", //m
|
||||
"fp_emore": "아직 해결해야 할 파일 이름 충돌이 남아 있습니다",
|
||||
"fp_ok": "이동 완료",
|
||||
"fcp_ok": "복사 완료",
|
||||
|
|
@ -440,7 +444,7 @@ Ls.kor = {
|
|||
"fcp_both_b": '<a href="#" id="modal-ok">복사</a><a href="#" id="modal-ng">업로드</a>',
|
||||
|
||||
"mk_noname": "왼쪽 텍스트 필드에 이름을 먼저 입력해주세요 :p",
|
||||
"nmd_i1": "원하는 파일 확장자를 추가할 수 있습니다. 예: <code>.txt</code>", //m
|
||||
"nmd_i1": "원하는 파일 확장자를 추가할 수 있습니다. 예: <code>.md</code>", //m
|
||||
"nmd_i2": "삭제 권한이 없어서 <code>.md</code> 파일만 만들 수 있습니다", //m
|
||||
|
||||
"tv_load": "텍스트 문서 불러오는 중:\n\n{0}\n\n{1}% ({3} MiB 중 {2} MiB 로드됨)",
|
||||
|
|
@ -637,6 +641,23 @@ Ls.kor = {
|
|||
"ur_um": "완료;\n{0}개 업로드 성공,\n{1}개 업로드 실패, 죄송",
|
||||
"ur_sm": "완료;\n서버에서 {0}개 파일 찾음,\n서버에서 {1}개 파일 찾지 못함",
|
||||
|
||||
"rc_opn": "열기", //m
|
||||
"rc_ply": "재생", //m
|
||||
"rc_pla": "오디오로 재생", //m
|
||||
"rc_txt": "파일 뷰어에서 열기", //m
|
||||
"rc_md": "텍스트 편집기에서 열기", //m
|
||||
"rc_dl": "다운로드", //m
|
||||
"rc_zip": "압축 파일로 다운로드", //m
|
||||
"rc_cpl": "링크 복사", //m
|
||||
"rc_del": "삭제", //m
|
||||
"rc_cut": "잘라내기", //m
|
||||
"rc_cpy": "복사", //m
|
||||
"rc_pst": "붙여넣기", //m
|
||||
"rc_nfo": "새 폴더", //m
|
||||
"rc_nfi": "새 파일", //m
|
||||
"rc_sal": "모두 선택", //m
|
||||
"rc_sin": "선택 반전", //m
|
||||
|
||||
"lang_set": '변경 사항을 적용하기 위해 새로고침하시겠습니까?',
|
||||
|
||||
"splash": {
|
||||
|
|
@ -678,6 +699,8 @@ Ls.kor = {
|
|||
"ta1": "새 비밀번호를 먼저 입력하세요",
|
||||
"ta2": "새 비밀번호 확인을 위해 다시 입력하세요:",
|
||||
"ta3": "오타가 있습니다. 다시 시도해주세요",
|
||||
"nop": "오류: 비밀번호를 비워 둘 수 없습니다", //m
|
||||
"nou": "오류: 사용자 이름 및/또는 비밀번호를 비워 둘 수 없습니다", //m
|
||||
"aa1": "수신 중인 파일:",
|
||||
"ab1": "no304 비활성화",
|
||||
"ac1": "no304 활성화",
|
||||
|
|
|
|||
|
|
@ -155,7 +155,7 @@ Ls.nld = {
|
|||
"ul_par": "Parallel uploads:",
|
||||
"ut_rand": "Willekeurige bestandsnaam",
|
||||
"ut_u2ts": "Kopieer de laatste-gewijzigde tijdstamp$Nvan je bestandsysteem naar de server\">📅",
|
||||
"ut_ow": "Overschrijf bestaande bestanden op de server?$N🛡️: nooit (zal in plaats daarvan een nieuwe bestandsnaam genereren)$N🕒: overschrijven als de server-bestand ouder is dan het geüploade bestand$N♻️: altijd overschrijven als de bestanden verschillend zijn",
|
||||
"ut_ow": "Overschrijf bestaande bestanden op de server?$N🛡️: nooit (zal in plaats daarvan een nieuwe bestandsnaam genereren)$N🕒: overschrijven als de server-bestand ouder is dan het geüploade bestand$N♻️: altijd overschrijven als de bestanden verschillend zijn$N⏭️: alle bestaande bestanden onvoorwaardelijk overslaan", //m
|
||||
"ut_mt": "Ga door met hashen van andere bestanden tijdens het uploaden$N$Moet je misschien uitschakelen als je CPU of HDD het niet aan kan",
|
||||
"ut_ask": 'Vraag voor bevestiging voordat het uploaden start">💭',
|
||||
"ut_pot": "Verbeter de uploadsnelheid voor langzame apparaten$Ndoor de interface minder complex te maken",
|
||||
|
|
@ -220,6 +220,7 @@ Ls.nld = {
|
|||
"cl_reset": "Reset",
|
||||
"cl_hpick": "Tik op de kolomkoppen om ze in de onderstaande tabel te verbergen",
|
||||
"cl_hcancel": "Kolumn verbergen geannuleerd",
|
||||
"cl_rcm": "Rechtermuisknopmenu", //m
|
||||
|
||||
"ct_grid": '田 grid',
|
||||
"ct_ttips": '◔ ◡ ◔">ℹ️ tooltips',
|
||||
|
|
@ -262,6 +263,7 @@ Ls.nld = {
|
|||
"cdt_lim": "Max aantal bestanden laten zien in een map",
|
||||
"cdt_ask": "Als helemaal naar beneden gescrolld bent,$Nin plaats van meer inladen,$Nvraag wat het moet doen",
|
||||
"cdt_hsort": "Hoeveel sorteerregels (<code>,sorthref</code>) moeten er in media-URL's worden opgenomen? Als je dit op 0 instelt, worden de sorteerregels in medialinks ook genegeerd wanneer erop geklikt word.",
|
||||
"cdt_ren": "Aangepast rechtermuisknopmenu inschakelen, het normale menu blijft beschikbaar met shift + rechtermuisknop", //m
|
||||
|
||||
"tt_entree": "Laat navpane zien (directoryboom zijbalk)$NHotkey: B",
|
||||
"tt_detree": "Laat breadcrumbs zien$NHotkey: B",
|
||||
|
|
@ -332,6 +334,7 @@ Ls.nld = {
|
|||
"mm_eunk": "Onbekende fout",
|
||||
"mm_e404": "Kan audio niet afspelen; fout 404: Bestand niet gevonden..",
|
||||
"mm_e403": "Kan audio niet afspelen; fout 403: Toegang geweigerd.\n\nProbeer op F5 te drukken om opnieuw te laden, misschien ben je uitgelogd",
|
||||
"mm_e415": "Kan geen audio afspelen; fout 415: Bestandsconversie mislukt; controleer serverlogs.", //m
|
||||
"mm_e500": "Kan geen audio afspelen; fout 500: Controleer serverlogs.",
|
||||
"mm_e5xx": "Kan geen audio afspelen; serverfout ",
|
||||
"mm_nof": "Geen audiobestanden meer vinden in de buurt",
|
||||
|
|
@ -419,9 +422,10 @@ Ls.nld = {
|
|||
"fcc_warn": '{0} items naar klembord gekopieerd\n\maar: alleen <b>deze</b> browser-tabblad kan weer plakken\n(omdat de selectie zo enorm is)',
|
||||
|
||||
"fp_apply": "Gebruik deze namen",
|
||||
"fp_skip": "Conflicten overslaan", //m
|
||||
"fp_ecut": "Knip of kopieer eerst enkele bestanden/mappen om te verplaatsen/plakken\n\nnotitie: je kunt knippen/plakken in verschillende browsertabbladen",
|
||||
"fp_ename": "{0} items kunnen hier niet worden verplaatst omdat de namen al in gebruik zijn. Geef ze hieronder een nieuwe naam om verder te gaan, of verwijder de naam om ze over te slaan:",
|
||||
"fcp_ename": "{0} items kunnen hier niet worden gekopieerd omdat de namen al in gebruik zijn. Geef ze hieronder een nieuwe naam om verder te gaan, of verwijder de naam om ze over te slaan:",
|
||||
"fp_ename": "{0} items kunnen hier niet worden verplaatst omdat de namen al in gebruik zijn. Geef ze hieronder een nieuwe naam om verder te gaan, of verwijder de naam (\"conflicten overslaan\") om ze over te slaan:", //m
|
||||
"fcp_ename": "{0} items kunnen hier niet worden gekopieerd omdat de namen al in gebruik zijn. Geef ze hieronder een nieuwe naam om verder te gaan, of verwijder de naam (\"conflicten overslaan\") om ze over te slaan:", //m
|
||||
"fp_emore": "Er zijn nog enkele bestandsnaambotsingen die moeten worden opgelost",
|
||||
"fp_ok": "Verplaatsen OK",
|
||||
"fcp_ok": "Kopiëren OK",
|
||||
|
|
@ -440,7 +444,7 @@ Ls.nld = {
|
|||
"fcp_both_b": '<a href="#" id="modal-ok">Kopieer</a><a href="#" id="modal-ng">Upload</a>',
|
||||
|
||||
"mk_noname": "Voer een naam in het tekstveld aan de linkerkant voordat je verder gaat :p",
|
||||
"nmd_i1": "Voeg ook de gewenste extensie toe, bijvoorbeeld <code>.txt</code>", //m
|
||||
"nmd_i1": "Voeg ook de gewenste extensie toe, bijvoorbeeld <code>.md</code>", //m
|
||||
"nmd_i2": "Je kunt alleen <code>.md</code>-bestanden maken omdat je geen verwijderrechten hebt", //m
|
||||
|
||||
"tv_load": "Tekstdocument laden:\n\n{0}\n\n{1}% ({2} van de {3} MiB geladen)",
|
||||
|
|
@ -637,6 +641,23 @@ Ls.nld = {
|
|||
"ur_um": "Voltooid;\n{0} upload(s) OK,\n{1} upload(s) mislukt, sorry",
|
||||
"ur_sm": "Voltooid;\n{0} bestand(en) gevonden op de server,\n{1} bestand(en) NIET gevonden op de server",
|
||||
|
||||
"rc_opn": "Openen", //m
|
||||
"rc_ply": "Afspelen", //m
|
||||
"rc_pla": "Afspelen als audio", //m
|
||||
"rc_txt": "Openen in bestandsviewer", //m
|
||||
"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
|
||||
"rc_pst": "Plakken", //m
|
||||
"rc_nfo": "Nieuwe map", //m
|
||||
"rc_nfi": "Nieuw bestand", //m
|
||||
"rc_sal": "Alles selecteren", //m
|
||||
"rc_sin": "Selectie omkeren", //m
|
||||
|
||||
"lang_set": "Vernieuw de pagina om de wijziging door te voeren?",
|
||||
|
||||
"splash": {
|
||||
|
|
@ -678,6 +699,8 @@ Ls.nld = {
|
|||
"ta1": "Je moet eerst een nieuw wachtwoord invoeren",
|
||||
"ta2": "Herhaal om nieuw wachtwoord te bevestigen:",
|
||||
"ta3": "Typefout gevonden; probeer het opnieuw",
|
||||
"nop": "FOUT: Wachtwoord mag niet leeg zijn", //m
|
||||
"nou": "FOUT: Gebruikersnaam en/of wachtwoord mag niet leeg zijn", //m
|
||||
"aa1": "Inkomend:",
|
||||
"ab1": "Schakel nr. 304 uit",
|
||||
"ac1": "Schakel nr. 304 in",
|
||||
|
|
|
|||
|
|
@ -152,7 +152,7 @@ Ls.nno = {
|
|||
"ul_par": "samtidige handl.:",
|
||||
"ut_rand": "finn opp nye tilfeldige filnamn",
|
||||
"ut_u2ts": "gje fila på serveren same$Ntidsstempel som lokalt hos deg\">📅",
|
||||
"ut_ow": "overskrive eksisterande filer på serveren?$N🛡️: aldri (finn på eit nytt filnamn i staden for)$N🕒: overskriv viss fila åt serveren er eldre$N♻️: alltid, gitt at innholdet er annleis",
|
||||
"ut_ow": "overskrive eksisterande filer på serveren?$N🛡️: aldri (finn på eit nytt filnamn i staden for)$N🕒: overskriv viss fila åt serveren er eldre$N♻️: alltid, gitt at innhaldet er annleis$N⏭️: hopp over alle eksisterande filer",
|
||||
"ut_mt": "fortsett å synfare køa mens opplasting føregår$N$Nskru denne av dersom du har ein$Ntreig prosessor eller harddisk",
|
||||
"ut_ask": 'bekreft filutvalg før opplasting startar">💭',
|
||||
"ut_pot": "forbetre ytinga på treige einheiter ved å$Nforenkle brukergrensesnittet",
|
||||
|
|
@ -217,6 +217,7 @@ Ls.nno = {
|
|||
"cl_reset": "nullstill",
|
||||
"cl_hpick": "klikk på overskrifta åt kolonnene du ønskjer å skjule i tabellen nedanfor",
|
||||
"cl_hcancel": "kolonne-skjuling avbrote",
|
||||
"cl_rcm": "høgreklikkmeny",
|
||||
|
||||
"ct_grid": '田 ikon',
|
||||
"ct_ttips": 'vis hjelpetekst ved å holde musa over ting">ℹ️ tips',
|
||||
|
|
@ -259,6 +260,7 @@ Ls.nno = {
|
|||
"cdt_lim": "maks mengd filer å vise per mappe",
|
||||
"cdt_ask": "vis knappar for å laste fleire filer nederst på sida i staden for å gradvis laste meir av mappea når man scroller ned",
|
||||
"cdt_hsort": "antall sorteringsreglar (<code>,sorthref</code>) som skal inkluderast når media-URL'ar genererast. Dersom denne er 0 så vil sorteringsreglar i URL'ar korkje bli generert eller lest",
|
||||
"cdt_ren": "slå på tilpassa høgreklikkmeny (den vanlege menyen er tilgjengeleg med shift + høgreklikk)",
|
||||
|
||||
"tt_entree": "bytt åt mappehierarki$NSnarvei: B",
|
||||
"tt_detree": "bytt åt tradisjonell stivising$NSnarvei: B",
|
||||
|
|
@ -329,6 +331,7 @@ Ls.nno = {
|
|||
"mm_eunk": "Ukjent feil",
|
||||
"mm_e404": "Avspeling feilet: Fil ikkje funnet.",
|
||||
"mm_e403": "Avspeling feilet: Høve nekta.\n\nKanskje du blei logget ut?\nPrøv å trykk F5 for å laste sida på nytt.",
|
||||
"mm_e415": "Avspeling feilet: Kunne ikkje konvertere fila, sjekk serverloggen.",
|
||||
"mm_e500": "Avspeling feilet: Rusk i maskineriet, sjekk serverloggen.",
|
||||
"mm_e5xx": "Avspeling feilet: ",
|
||||
"mm_nof": "finn ikkje flere songer i nærheita",
|
||||
|
|
@ -416,9 +419,10 @@ Ls.nno = {
|
|||
"fcc_warn": 'kopierte {0} filer åt utklippstavla\n\nmen: kun <b>denne</b> nettlesarfana har muligheit åt å lime dei inn ein annan plass, sidan antallet filer er heilt på hi sida',
|
||||
|
||||
"fp_apply": "bekreft og lim inn no",
|
||||
"fp_skip": "berre ledige",
|
||||
"fp_ecut": "du må klippe ut eller kopiere nokre filer / mapper først\n\nmerk: du kan gjerne jobbe på kryss av nettlesarfaner; klippe ut i éi fane, lime inn i ei anna",
|
||||
"fp_ename": "{0} filer kan ikkje flyttast åt målmappa fordi det allereie finnast filer med same namn. Gi dei nye namn nedanfor, eller gje dei eit blankt namn for å hoppe over dei:",
|
||||
"fcp_ename": "{0} filer kan ikkje kopierast åt målmappa fordi det allereie finnast filer med same namn. Gi dei nye namn nedanfor, eller gje dei eit blankt namn for å hoppe over dei:",
|
||||
"fp_ename": "{0} filer kan ikkje flyttast åt målmappa fordi det allereie finnast filer med same namn. Gi dei nye namn nedanfor, eller gje dei eit blankt namn (\"berre ledige\") for å hoppe over dei:",
|
||||
"fcp_ename": "{0} filer kan ikkje kopierast åt målmappa fordi det allereie finnast filer med same namn. Gi dei nye namn nedanfor, eller gje dei eit blankt namn (\"berre ledige\") for å hoppe over dei:",
|
||||
"fp_emore": "det er fortsatt fleire namn som må endrast",
|
||||
"fp_ok": "flytting OK",
|
||||
"fcp_ok": "kopiering OK",
|
||||
|
|
@ -437,7 +441,7 @@ Ls.nno = {
|
|||
"fcp_both_b": '<a href="#" id="modal-ok">Kopiér</a><a href="#" id="modal-ng">Last opp</a>',
|
||||
|
||||
"mk_noname": "skriv inn eit namn i tekstboksa åt venstre først :p",
|
||||
"nmd_i1": "leggja også til filendinga du vil, til dømes <code>.txt</code>", //m
|
||||
"nmd_i1": "leggja også til filendinga du vil, til dømes <code>.md</code>", //m
|
||||
"nmd_i2": "du kan berre laga <code>.md</code>-filer fordi du ikkje har delete-tilgang", //m
|
||||
|
||||
"tv_load": "Lastar inn tekstfil:\n\n{0}\n\n{1}% ({2} av {3} MiB lasta ned)",
|
||||
|
|
@ -634,6 +638,23 @@ Ls.nno = {
|
|||
"ur_um": "Ferdig;\n{0} opplastingar gjekk bra,\n{1} opplastingar gjekk feil",
|
||||
"ur_sm": "Ferdig;\n{0} filer blei funne,\n{1} filer finnast IKKJE på serveren",
|
||||
|
||||
"rc_opn": "opne",
|
||||
"rc_ply": "spel av",
|
||||
"rc_pla": "spel av som lyd",
|
||||
"rc_txt": "opne i filvisar",
|
||||
"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",
|
||||
"rc_pst": "Lim inn",
|
||||
"rc_nfo": "ny mappe",
|
||||
"rc_nfi": "ny fil",
|
||||
"rc_sal": "vel alle",
|
||||
"rc_sin": "inverter val",
|
||||
|
||||
"lang_set": "passar det å laste sida på nytt?",
|
||||
|
||||
"splash": {
|
||||
|
|
@ -675,6 +696,8 @@ Ls.nno = {
|
|||
"ta1": "du må skrive eit nytt passord først",
|
||||
"ta2": "gjenta for å stadfeste nytt passord:",
|
||||
"ta3": "fant ein skrivefeil; vennligst prøv igjen",
|
||||
"nop": "FEIL: Passord kan ikkje vere tomt",
|
||||
"nou": "FEIL: Brukarnamn og passord må fyllast ut",
|
||||
"aa1": "innkommande:",
|
||||
"ab1": "skru av no304",
|
||||
"ac1": "skru på no304",
|
||||
|
|
|
|||
|
|
@ -152,7 +152,7 @@ Ls.nor = {
|
|||
"ul_par": "samtidige handl.:",
|
||||
"ut_rand": "finn opp nye tilfeldige filnavn",
|
||||
"ut_u2ts": "gi filen på serveren samme$Ntidsstempel som lokalt hos deg\">📅",
|
||||
"ut_ow": "overskrive eksisterende filer på serveren?$N🛡️: aldri (finner på et nytt filnavn istedenfor)$N🕒: overskriv hvis serverens fil er eldre$N♻️: alltid, gitt at innholdet er forskjellig",
|
||||
"ut_ow": "overskrive eksisterende filer på serveren?$N🛡️: aldri (finner på et nytt filnavn istedenfor)$N🕒: overskriv hvis serverens fil er eldre$N♻️: alltid, gitt at innholdet er forskjellig$N⏭️: hopp over alle eksisterende filer",
|
||||
"ut_mt": "fortsett å befare køen mens opplastning foregår$N$Nskru denne av dersom du har en$Ntreg prosessor eller harddisk",
|
||||
"ut_ask": 'bekreft filutvalg før opplastning starter">💭',
|
||||
"ut_pot": "forbedre ytelsen på trege enheter ved å$Nforenkle brukergrensesnittet",
|
||||
|
|
@ -217,6 +217,7 @@ Ls.nor = {
|
|||
"cl_reset": "nullstill",
|
||||
"cl_hpick": "klikk på overskriften til kolonnene du ønsker å skjule i tabellen nedenfor",
|
||||
"cl_hcancel": "kolonne-skjuling avbrutt",
|
||||
"cl_rcm": "høyreklikkmeny",
|
||||
|
||||
"ct_grid": '田 ikoner',
|
||||
"ct_ttips": 'vis hjelpetekst ved å holde musen over ting">ℹ️ tips',
|
||||
|
|
@ -259,6 +260,7 @@ Ls.nor = {
|
|||
"cdt_lim": "maks antall filer å vise per mappe",
|
||||
"cdt_ask": "vis knapper for å laste flere filer nederst på siden istedenfor å gradvis laste mer av mappen når man scroller ned",
|
||||
"cdt_hsort": "antall sorterings-regler (<code>,sorthref</code>) som skal inkluderes når media-URL'er genereres. Hvis denne er 0 så vil sorterings-regler i URL'er hverken bli generert eller lest",
|
||||
"cdt_ren": "bruk egendefinert høyreklikkmeny (den vanlige menyen er tilgjengelig med shift + høyreklikk)",
|
||||
|
||||
"tt_entree": "bytt til mappehierarki$NSnarvei: B",
|
||||
"tt_detree": "bytt til tradisjonell sti-visning$NSnarvei: B",
|
||||
|
|
@ -329,6 +331,7 @@ Ls.nor = {
|
|||
"mm_eunk": "Ukjent feil",
|
||||
"mm_e404": "Avspilling feilet: Fil ikke funnet.",
|
||||
"mm_e403": "Avspilling feilet: Tilgang nektet.\n\nKanskje du ble logget ut?\nPrøv å trykk F5 for å laste siden på nytt.",
|
||||
"mm_e415": "Avspilling feilet: Kunne ikke konvertere filen, sjekk serverloggen.",
|
||||
"mm_e500": "Avspilling feilet: Rusk i maskineriet, sjekk serverloggen.",
|
||||
"mm_e5xx": "Avspilling feilet: ",
|
||||
"mm_nof": "finner ikke flere sanger i nærheten",
|
||||
|
|
@ -416,9 +419,10 @@ Ls.nor = {
|
|||
"fcc_warn": 'kopierte {0} filer til utklippstavlen\n\nmen: kun <b>denne</b> nettleserfanen har mulighet til å lime dem inn et annet sted, siden antallet filer er helt hinsides',
|
||||
|
||||
"fp_apply": "bekreft og lim inn nå",
|
||||
"fp_skip": "kun ledige",
|
||||
"fp_ecut": "du må klippe ut eller kopiere noen filer / mapper først\n\nmerk: du kan gjerne jobbe på kryss av nettleserfaner; klippe ut i én fane, lime inn i en annen",
|
||||
"fp_ename": "{0} filer kan ikke flyttes til målmappen fordi det allerede finnes filer med samme navn. Gi dem nye navn nedenfor, eller gi dem et blankt navn for å hoppe over dem:",
|
||||
"fcp_ename": "{0} filer kan ikke kopieres til målmappen fordi det allerede finnes filer med samme navn. Gi dem nye navn nedenfor, eller gi dem et blankt navn for å hoppe over dem:",
|
||||
"fp_ename": "{0} filer kan ikke flyttes til målmappen fordi det allerede finnes filer med samme navn. Gi dem nye navn nedenfor, eller gi dem et blankt navn (\"kun ledige\") for å hoppe over dem:",
|
||||
"fcp_ename": "{0} filer kan ikke kopieres til målmappen fordi det allerede finnes filer med samme navn. Gi dem nye navn nedenfor, eller gi dem et blankt navn (\"kun ledige\") for å hoppe over dem:",
|
||||
"fp_emore": "det er fortsatt flere navn som må endres",
|
||||
"fp_ok": "flytting OK",
|
||||
"fcp_ok": "kopiering OK",
|
||||
|
|
@ -437,7 +441,7 @@ Ls.nor = {
|
|||
"fcp_both_b": '<a href="#" id="modal-ok">Kopiér</a><a href="#" id="modal-ng">Last opp</a>',
|
||||
|
||||
"mk_noname": "skriv inn et navn i tekstboksen til venstre først :p",
|
||||
"nmd_i1": "legg også til ønsket filtype, for eksempel <code>.txt</code>", //m
|
||||
"nmd_i1": "legg også til ønsket filtype, for eksempel <code>.md</code>", //m
|
||||
"nmd_i2": "du kan bare lage <code>.md</code>-filer fordi du ikke har delete-tilgang", //m
|
||||
|
||||
"tv_load": "Laster inn tekstfil:\n\n{0}\n\n{1}% ({2} av {3} MiB lastet ned)",
|
||||
|
|
@ -634,6 +638,23 @@ Ls.nor = {
|
|||
"ur_um": "Ferdig;\n{0} opplastninger gikk bra,\n{1} opplastninger gikk feil",
|
||||
"ur_sm": "Ferdig;\n{0} filer ble funnet,\n{1} filer finnes IKKE på serveren",
|
||||
|
||||
"rc_opn": "åpne",
|
||||
"rc_ply": "spill av",
|
||||
"rc_pla": "spill av som lyd",
|
||||
"rc_txt": "åpne i filviser",
|
||||
"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",
|
||||
"rc_pst": "Lim inn",
|
||||
"rc_nfo": "ny mappe",
|
||||
"rc_nfi": "ny fil",
|
||||
"rc_sal": "velg alle",
|
||||
"rc_sin": "inverter utvalg",
|
||||
|
||||
"lang_set": "passer det å laste siden på nytt?",
|
||||
|
||||
"splash": {
|
||||
|
|
@ -675,6 +696,8 @@ Ls.nor = {
|
|||
"ta1": "du må skrive et nytt passord først",
|
||||
"ta2": "gjenta for å bekrefte nytt passord:",
|
||||
"ta3": "fant en skrivefeil; vennligst prøv igjen",
|
||||
"nop": "FEIL: Passord kan ikke være blankt",
|
||||
"nou": "FEIL: Både brukernavn og passord må angis",
|
||||
"aa1": "innkommende:",
|
||||
"ab1": "skru av no304",
|
||||
"ac1": "skru på no304",
|
||||
|
|
|
|||
|
|
@ -158,7 +158,7 @@ Ls.pol = {
|
|||
"ul_par": "przesyłane równolegle:",
|
||||
"ut_rand": "losuj nazwy plików",
|
||||
"ut_u2ts": "kopiuj znacznik ostatniej modyfikacji$Nz twojego systemu plików na serwer\">📅",
|
||||
"ut_ow": "nadpisywać istniejące pliki na serwerzę?$N🛡️: nigdy (wygeneruje nową nazwę)$N🕒: nadpisz jeśli pliki na serwerze są starsze niż przesyłane$N♻️: zawsze nadpisuj jeśli zawartość plików się różni",
|
||||
"ut_ow": "nadpisywać istniejące pliki na serwerzę?$N🛡️: nigdy (wygeneruje nową nazwę)$N🕒: nadpisz jeśli pliki na serwerze są starsze niż przesyłane$N♻️: zawsze nadpisuj jeśli zawartość plików się różni$N⏭️: bezwarunkowo pomiń wszystkie istniejące pliki", //m
|
||||
"ut_mt": "hashuj inne pliki podczas przesyłania$N$Nmożna wyłączyć w przypadku wystąpienia wąskiego gardła na CPU lub HDD",
|
||||
"ut_ask": 'pytaj o potwierdzenie rozpoczęcia przesyłania">💭',
|
||||
"ut_pot": "przyspiesz przesyłanie na słabszych urządzeniach,$Nupraszczając interfejs",
|
||||
|
|
@ -223,6 +223,7 @@ Ls.pol = {
|
|||
"cl_reset": "zresetuj",
|
||||
"cl_hpick": "kliknij nagłówki kolumn, aby ukryć je w tabeli niżej",
|
||||
"cl_hcancel": "ukrywanie kolumn przerwane",
|
||||
"cl_rcm": "menu kontekstowe", //m
|
||||
|
||||
"ct_grid": '田 siatka',
|
||||
"ct_ttips": '◔ ◡ ◔">ℹ️ podpowiedzi',
|
||||
|
|
@ -265,6 +266,7 @@ Ls.pol = {
|
|||
"cdt_lim": "maksymalna liczba plików do pokazania na raz w folderze",
|
||||
"cdt_ask": "przy przewijaniu w dół,$Nzapytaj co robić,$Nzamiast wczytywać kolejne pliki",
|
||||
"cdt_hsort": "ile zasad sortowania (<code>,sorthref</code>) zawierać w generowanych linkach multimediów. Wartość 0 sprawi, że zasady sortowania zawarte w linkach multimediów przy otwarciu również będą ignorowane",
|
||||
"cdt_ren": "włącz niestandardowe menu kontekstowe, standardowe menu jest dostępne po wciśnięciu shift i kliknięciu prawym przyciskiem", //m
|
||||
|
||||
"tt_entree": "pokaż panel nawigacyjny (panel boczny z drzewem folderów)$NSkrót: B",
|
||||
"tt_detree": "pokaż ślad nawigacyjny$NSkrót: B",
|
||||
|
|
@ -335,6 +337,7 @@ Ls.pol = {
|
|||
"mm_eunk": "Nieznany błąd",
|
||||
"mm_e404": "Nie można odtworzyć; błąd 404: Nie znaleziono pliku.",
|
||||
"mm_e403": "Nie można odtworzyć; błąd 403: Odmowa dostępu.\n\nSpróbuj przeładować stronę (F5), może cię wylogowało",
|
||||
"mm_e415": "Nie można odtworzyć; błąd 415: Konwersja pliku nie powiodła się; sprawdź logi serwera.", //m
|
||||
"mm_e500": "Nie można odtworzyć; błąd 500: Sprawdź logi serwera.",
|
||||
"mm_e5xx": "Nie można odtworzyć; błąd serwera",
|
||||
"mm_nof": "nie znaleziono więcej plików audio",
|
||||
|
|
@ -422,9 +425,10 @@ Ls.pol = {
|
|||
"fcc_warn": 'skopiowano {0} elementów,\n\nlecz można je wkleić tylko w <b>tej</b> karcie\n(ze względu na ogromną ilość wybranych elementów)',
|
||||
|
||||
"fp_apply": "zastosuj te nazwy",
|
||||
"fp_skip": "pomiń konflikty", //m
|
||||
"fp_ecut": "najpierw wytnij lub skopiuj pliki / foldery, aby je wkleić / przenieść\n\nuwaga: można wycinać / wklejać pomiędzy różnymi kartami przeglądarki",
|
||||
"fp_ename": "Nie udało się przenieść {0} elementów, gdyż ich nazwy już istnieją w tym folderze. Nadaj im nowe nazwy poniżej, bądź zostaw pole nazwy puste, aby je pominąć:",
|
||||
"fcp_ename": "Nie udało się przekopiować {0} elementów, gdyż ich nazwy już istnieją w tym folderze. Nadaj im nowe nazwy poniżej, bądź zostaw pole nazwy puste, aby je pominąć:",
|
||||
"fp_ename": "Nie udało się przenieść {0} elementów, gdyż ich nazwy już istnieją w tym folderze. Nadaj im nowe nazwy poniżej, bądź zostaw pole nazwy puste (\"pomiń konflikty\"), aby je pominąć:", //m
|
||||
"fcp_ename": "Nie udało się przekopiować {0} elementów, gdyż ich nazwy już istnieją w tym folderze. Nadaj im nowe nazwy poniżej, bądź zostaw pole nazwy puste (\"pomiń konflikty\"), aby je pominąć:", //m
|
||||
"fp_emore": "pozostało jeszcze kilka kolizji nazw plików do poprawy",
|
||||
"fp_ok": "przeniesiono",
|
||||
"fcp_ok": "przekopiowano",
|
||||
|
|
@ -443,7 +447,7 @@ Ls.pol = {
|
|||
"fcp_both_b": '<a href="#" id="modal-ok">Kopiuj</a><a href="#" id="modal-ng">Prześlij</a>',
|
||||
|
||||
"mk_noname": "wpisz nazwę do pola po lewej zanim to zrobisz :p",
|
||||
"nmd_i1": "możesz też dodać wybrane rozszerzenie, np. <code>.txt</code>", //m
|
||||
"nmd_i1": "możesz też dodać wybrane rozszerzenie, np. <code>.md</code>", //m
|
||||
"nmd_i2": "możesz tworzyć tylko pliki <code>.md</code>, ponieważ nie masz uprawnień do usuwania", //m
|
||||
|
||||
"tv_load": "Wczytywanie pliku tekstowego:\n\n{0}\n\n{1}% (wczytano {2} z {3} MiB)",
|
||||
|
|
@ -640,6 +644,23 @@ Ls.pol = {
|
|||
"ur_um": "Zakończono;\n{0} przesłań OK,\n{1} przesłań nie powiodło się",
|
||||
"ur_sm": "Zakończono;\nznaleziono {0} pliki(ów),\nnie znaleziono {1} pliki(ów) na serwerze",
|
||||
|
||||
"rc_opn": "otwórz", //m
|
||||
"rc_ply": "odtwórz", //m
|
||||
"rc_pla": "odtwórz jako dźwięk", //m
|
||||
"rc_txt": "otwórz w przeglądarce plików", //m
|
||||
"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
|
||||
"rc_pst": "wklej", //m
|
||||
"rc_nfo": "nowy folder", //m
|
||||
"rc_nfi": "nowy plik", //m
|
||||
"rc_sal": "zaznacz wszystko", //m
|
||||
"rc_sin": "odwróć zaznaczenie", //m
|
||||
|
||||
"lang_set": "odśwież stronę (F5), aby zastosować zmianę.",
|
||||
|
||||
"splash": {
|
||||
|
|
@ -681,6 +702,8 @@ Ls.pol = {
|
|||
"ta1": "najpierw wprowadź nowe hasło",
|
||||
"ta2": "powtórz hasło dla potwierdzenia:",
|
||||
"ta3": "znaleziono literówkę, spróbuj ponownie",
|
||||
"nop": "BŁĄD: Hasło nie może być puste", //m
|
||||
"nou": "BŁĄD: Nazwa użytkownika i/lub hasło nie może być puste", //m
|
||||
"aa1": "pliki przychodzące:",
|
||||
"ab1": "wyłącz no304",
|
||||
"ac1": "włącz no304",
|
||||
|
|
|
|||
|
|
@ -155,7 +155,7 @@ Ls.por = {
|
|||
"ul_par": "uploads paralelos:",
|
||||
"ut_rand": "randomizar nomes de arquivos",
|
||||
"ut_u2ts": "copiar o carimbo de data/hora de última modificação$Ndo seu sistema de arquivos para o servidor\">📅",
|
||||
"ut_ow": "substituir arquivos existentes no servidor?$N🛡️: nunca (irá gerar um novo nome de arquivo em vez disso)$N🕒: substituir se o arquivo no servidor for mais antigo que o seu$N♻️: sempre substituir se os arquivos forem diferentes",
|
||||
"ut_ow": "substituir arquivos existentes no servidor?$N🛡️: nunca (irá gerar um novo nome de arquivo em vez disso)$N🕒: substituir se o arquivo no servidor for mais antigo que o seu$N♻️: sempre substituir se os arquivos forem diferentes$N⏭️: ignorar incondicionalmente todos os arquivos existentes", //m
|
||||
"ut_mt": "continuar a fazer o hash de outros arquivos enquanto faz upload$N$Ntalvez desativar se sua CPU ou HDD for um gargalo",
|
||||
"ut_ask": 'pedir confirmação antes do upload começar">💭',
|
||||
"ut_pot": "melhorar a velocidade de upload em dispositivos lentos$Ntornando a UI menos complexa",
|
||||
|
|
@ -220,6 +220,7 @@ Ls.por = {
|
|||
"cl_reset": "resetar",
|
||||
"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", //m
|
||||
|
||||
"ct_grid": '田 a grade',
|
||||
"ct_ttips": '◔ ◡ ◔">ℹ️ dicas de ferramentas',
|
||||
|
|
@ -262,6 +263,7 @@ Ls.por = {
|
|||
"cdt_lim": "número máximo de arquivos para mostrar em uma pasta",
|
||||
"cdt_ask": "ao rolar para o final,$nem vez de carregar mais arquivos,$nperguntar o que fazer",
|
||||
"cdt_hsort": "quantas regras de ordenação (<code>,sorthref</code>) incluir em URLs de mídia. Definir isso para 0 também ignorará as regras de ordenação incluídas em links de mídia quando você clicar neles",
|
||||
"cdt_ren": "ativar menu de clique direito personalizado, o menu normal permanece acessível com shift + clique direito", //m
|
||||
|
||||
"tt_entree": "mostrar painel de navegação (árvore de diretórios)$NHotkey: B",
|
||||
"tt_detree": "mostrar breadcrumbs$NHotkey: B",
|
||||
|
|
@ -332,6 +334,7 @@ Ls.por = {
|
|||
"mm_eunk": "Erro Desconhecido",
|
||||
"mm_e404": "Não foi possível reproduzir áudio; erro 404: Arquivo não encontrado.",
|
||||
"mm_e403": "Não foi possível reproduzir áudio; erro 403: Acesso negado.\n\nTente pressionar F5 para recarregar, talvez você tenha saído da conta",
|
||||
"mm_e415": "Não foi possível reproduzir áudio; erro 415: Falha na conversão do ficheiro; verifique os logs do servidor.", //m
|
||||
"mm_e500": "Não foi possível reproduzir áudio; erro 500: Verifique os logs do servidor.",
|
||||
"mm_e5xx": "Não foi possível reproduzir áudio; erro do servidor ",
|
||||
"mm_nof": "não encontrando mais arquivos de áudio por perto",
|
||||
|
|
@ -419,9 +422,10 @@ Ls.por = {
|
|||
"fcc_warn": 'copiado {0} itens para a área de transferência\n\nmas: apenas <b>esta</b> aba do navegador pode colá-los\n(já que a seleção é tão absolutamente massiva)',
|
||||
|
||||
"fp_apply": "usar estes nomes",
|
||||
"fp_skip": "pular conflitos", //m
|
||||
"fp_ecut": "primeiro recorte ou copie alguns arquivos / pastas para colar / mover\n\nnota: você pode recortar / colar entre abas diferentes do navegador",
|
||||
"fp_ename": "{0} itens não podem ser movidos para cá porque os nomes já estão em uso. Dê a eles novos nomes abaixo para continuar, ou deixe o nome em branco para pular:",
|
||||
"fcp_ename": "{0} itens não podem ser copiados para cá porque os nomes já estão em uso. Dê a eles novos nomes abaixo para continuar, ou deixe o nome em branco para pular:",
|
||||
"fp_ename": "{0} itens não podem ser movidos para cá porque os nomes já estão em uso. Dê a eles novos nomes abaixo para continuar, ou deixe o nome em branco (\"pular conflitos\") para pular:", //m
|
||||
"fcp_ename": "{0} itens não podem ser copiados para cá porque os nomes já estão em uso. Dê a eles novos nomes abaixo para continuar, ou deixe o nome em branco (\"pular conflitos\") para pular:", //m
|
||||
"fp_emore": "ainda há algumas colisões de nome de arquivo para consertar",
|
||||
"fp_ok": "movimento OK",
|
||||
"fcp_ok": "cópia OK",
|
||||
|
|
@ -440,7 +444,7 @@ Ls.por = {
|
|||
"fcp_both_b": '<a href="#" id="modal-ok">Copiar</a><a href="#" id="modal-ng">Enviar</a>',
|
||||
|
||||
"mk_noname": "digite um nome no campo de texto à esquerda antes de fazer isso :p",
|
||||
"nmd_i1": "também pode adicionar a extensão desejada, por exemplo <code>.txt</code>", //m
|
||||
"nmd_i1": "também pode adicionar a extensão desejada, por exemplo <code>.md</code>", //m
|
||||
"nmd_i2": "só pode criar ficheiros <code>.md</code> porque não tem permissão para apagar", //m
|
||||
|
||||
"tv_load": "Carregando documento de texto:\n\n{0}\n\n{1}% ({2} de {3} MiB carregados)",
|
||||
|
|
@ -637,6 +641,23 @@ Ls.por = {
|
|||
"ur_um": "Concluído;\n{0} uploads OK,\n{1} uploads falharam, desculpe",
|
||||
"ur_sm": "Concluído;\n{0} arquivos encontrados no servidor,\n{1} arquivos NÃO encontrados no servidor",
|
||||
|
||||
"rc_opn": "abrir", //m
|
||||
"rc_ply": "reproduzir", //m
|
||||
"rc_pla": "reproduzir como áudio", //m
|
||||
"rc_txt": "abrir no visualizador de arquivos", //m
|
||||
"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
|
||||
"rc_pst": "colar", //m
|
||||
"rc_nfo": "nova pasta", //m
|
||||
"rc_nfi": "novo arquivo", //m
|
||||
"rc_sal": "selecionar tudo", //m
|
||||
"rc_sin": "inverter seleção", //m
|
||||
|
||||
"lang_set": "atualizar para a mudança ter efeito?",
|
||||
|
||||
"splash": {
|
||||
|
|
@ -678,6 +699,8 @@ Ls.por = {
|
|||
"ta1": "primeiro digite sua nova senha",
|
||||
"ta2": "repita para confirmar a nova senha:",
|
||||
"ta3": "há um erro; por favor, tente novamente",
|
||||
"nop": "ERRO: A senha não pode estar em branco", //m
|
||||
"nou": "ERRO: O nome de usuário e/ou a senha não podem estar em branco", //m
|
||||
"aa1": "arquivos de entrada:",
|
||||
"ab1": "desativar no304",
|
||||
"ac1": "ativar no304",
|
||||
|
|
|
|||
|
|
@ -155,7 +155,7 @@ Ls.rus = {
|
|||
"ul_par": "параллельные загрузки:",
|
||||
"ut_rand": "случайные имена файлов",
|
||||
"ut_u2ts": "копировать время последнего изменения$Nиз вашей файловой системы на сервер\">📅",
|
||||
"ut_ow": "перезаписывать существующие файлы на сервере?$N🛡️: нет (для повторяющихся файлов будут создаваться новые имена)$N🕒: перезаписать файлы с датой изменения старее, чем у загружаемых$N♻️: всегда перезаписывать (если файлы различаются по содержанию)",
|
||||
"ut_ow": "перезаписывать существующие файлы на сервере?$N🛡️: нет (для повторяющихся файлов будут создаваться новые имена)$N🕒: перезаписать файлы с датой изменения старее, чем у загружаемых$N♻️: всегда перезаписывать (если файлы различаются по содержанию)$N⏭️: безусловно пропускать все существующие файлы", //m
|
||||
"ut_mt": "продолжать хешировать другие файлы во время загрузки$N$Nесть смысл отключить при медленном диске или процессоре",
|
||||
"ut_ask": 'требовать подтверждения перед началом загрузки">💭',
|
||||
"ut_pot": "улучшить скорость загрузки на слабых устройства$Nс помощью упрощения интерфейса",
|
||||
|
|
@ -220,6 +220,7 @@ Ls.rus = {
|
|||
"cl_reset": "сбросить",
|
||||
"cl_hpick": "нажмите на заголовки столбцов, чтобы скрыть их в таблице ниже",
|
||||
"cl_hcancel": "скрытие столбца отменено",
|
||||
"cl_rcm": "контекстное меню", //m
|
||||
|
||||
"ct_grid": '田 сетка',
|
||||
"ct_ttips": '◔ ◡ ◔">ℹ️ подсказки',
|
||||
|
|
@ -262,6 +263,7 @@ Ls.rus = {
|
|||
"cdt_lim": "максимальное количество файлов для показа в папке",
|
||||
"cdt_ask": "внизу страницы спрашивать о действии вместо автоматической загрузки следующих файлов",
|
||||
"cdt_hsort": "сколько правил сортировки (<code>,sorthref</code>) включать в адрес страницы. Если значение равно 0, по нажатии на ссылки будут игнорироваться правила, включённые в них",
|
||||
"cdt_ren": "включить настраиваемое контекстное меню, обычное меню доступно при нажатии shift и правой кнопки мыши", //m
|
||||
|
||||
"tt_entree": "показать панель навигации$NГорячая клавиша: B",
|
||||
"tt_detree": "скрыть панель навигации$NГорячая клавиша: B",
|
||||
|
|
@ -332,6 +334,7 @@ Ls.rus = {
|
|||
"mm_eunk": "Неопознанная ошибка",
|
||||
"mm_e404": "Не удалось воспроизвести аудио; ошибка 404: Файл не найден.",
|
||||
"mm_e403": "Не удалось воспроизвести аудио; ошибка 403: Доступ запрещён.\n\nПопробуйте перезагрузить страницу, возможно, ваша сессия истекла",
|
||||
"mm_e415": "Не удалось воспроизвести аудио; ошибка 415: Сбой преобразования файла; проверьте логи сервера.", //m
|
||||
"mm_e500": "Не удалось воспроизвести аудио; ошибка 500: Проверьте логи сервера.",
|
||||
"mm_e5xx": "Не удалось воспроизвести аудио; ошибка сервера ",
|
||||
"mm_nof": "больше аудио-файлов не найдено",
|
||||
|
|
@ -419,9 +422,10 @@ Ls.rus = {
|
|||
"fcc_warn": 'скопировано {0} файлов в буфер\n\nно только <b>эта</b> вкладка браузера может их вставить\n(поскольку выделение оказалось настолько огромным)',
|
||||
|
||||
"fp_apply": "использовать эти имена",
|
||||
"fp_skip": "пропустить конфликты", //m
|
||||
"fp_ecut": "сначала вырезать или скопировать только некоторые файлы / папки\n\nучтите: вы можете вырезать / вставлять файлы между вкладками",
|
||||
"fp_ename": "{0} файлов невозможно перенести сюда, потому что их имена уже заняты. Введите имена ниже, чтобы продолжить, или оставьте поля пустыми, чтобы пропустить:",
|
||||
"fcp_ename": "{0} файлов невозможно скопировать сюда, потому что их имена уже заняты. Введите имена ниже, чтобы продолжить, или оставьте поля пустыми, чтобы пропустить:",
|
||||
"fp_ename": "{0} файлов невозможно перенести сюда, потому что их имена уже заняты. Введите имена ниже, чтобы продолжить, или оставьте поля пустыми (\"пропустить конфликты\"), чтобы пропустить:", //m
|
||||
"fcp_ename": "{0} файлов невозможно скопировать сюда, потому что их имена уже заняты. Введите имена ниже, чтобы продолжить, или оставьте поля пустыми (\"пропустить конфликты\"), чтобы пропустить:", //m
|
||||
"fp_emore": "есть ещё коллизии имён, которые требуется исправить",
|
||||
"fp_ok": "успешно перенесено",
|
||||
"fcp_ok": "успешно скопировано",
|
||||
|
|
@ -440,7 +444,7 @@ Ls.rus = {
|
|||
"fcp_both_b": '<a href="#" id="modal-ok">Скопировать</a><a href="#" id="modal-ng">Загрузить</a>',
|
||||
|
||||
"mk_noname": "введите имя в текстовое поле слева перед тем, как это делать :p",
|
||||
"nmd_i1": "вы также можете указать нужное расширение, например <code>.txt</code>", //m
|
||||
"nmd_i1": "вы также можете указать нужное расширение, например <code>.md</code>", //m
|
||||
"nmd_i2": "вы можете создавать только файлы <code>.md</code>, так как у вас нет разрешения на удаление", //m
|
||||
|
||||
"tv_load": "Загружаю текстовый документ:\n\n{0}\n\n{1}% ({2} из {3} МиБ загружено)",
|
||||
|
|
@ -637,6 +641,23 @@ Ls.rus = {
|
|||
"ur_um": "Завершено;\n{0} успешно,\n{1} ошибок, извините",
|
||||
"ur_sm": "Завершено;\n{0} файлов найдено на сервере,\n{1} файлов НЕ найдено на сервере",
|
||||
|
||||
"rc_opn": "открыть", //m
|
||||
"rc_ply": "воспроизвести", //m
|
||||
"rc_pla": "воспроизвести как аудио", //m
|
||||
"rc_txt": "открыть в просмотрщике файлов", //m
|
||||
"rc_md": "открыть в текстовом редакторе", //m
|
||||
"rc_dl": "скачать", //m
|
||||
"rc_zip": "скачать как архив", //m
|
||||
"rc_cpl": "копировать ссылку", //m
|
||||
"rc_del": "удалить", //m
|
||||
"rc_cut": "вырезать", //m
|
||||
"rc_cpy": "копировать", //m
|
||||
"rc_pst": "вставить", //m
|
||||
"rc_nfo": "новая папка", //m
|
||||
"rc_nfi": "новый файл", //m
|
||||
"rc_sal": "выбрать всё", //m
|
||||
"rc_sin": "инвертировать выделение", //m
|
||||
|
||||
"lang_set": "перезагрузить страницу, чтобы применить изменения?",
|
||||
|
||||
"splash": {
|
||||
|
|
@ -678,6 +699,8 @@ Ls.rus = {
|
|||
"ta1": "сначала введите свой новый пароль",
|
||||
"ta2": "повторите новый пароль:",
|
||||
"ta3": "опечатка; попробуйте снова",
|
||||
"nop": "ОШИБКА: Пароль не может быть пустым", //m
|
||||
"nou": "ОШИБКА: Имя пользователя и/или пароль не могут быть пустыми", //m
|
||||
"aa1": "входящие файлы:",
|
||||
"ab1": "отключить no304",
|
||||
"ac1": "включить no304",
|
||||
|
|
|
|||
|
|
@ -155,7 +155,7 @@ Ls.spa = {
|
|||
"ul_par": "subidas paralelas:",
|
||||
"ut_rand": "aleatorizar nombres de archivo",
|
||||
"ut_u2ts": 'copiar la fecha de última modificación$Nde tu sistema de archivos al servidor">📅',
|
||||
"ut_ow": "sobrescribir archivos existentes en el servidor?$N🛡️: nunca (generará un nuevo nombre de archivo en su lugar)$N🕒: sobrescribir si el archivo del servidor es más antiguo que el tuyo$N♻️: siempre sobrescribir si los archivos son diferentes",
|
||||
"ut_ow": "sobrescribir archivos existentes en el servidor?$N🛡️: nunca (generará un nuevo nombre de archivo en su lugar)$N🕒: sobrescribir si el archivo del servidor es más antiguo que el tuyo$N♻️: siempre sobrescribir si los archivos son diferentes$N⏭️: omitir incondicionalmente todos los archivos existentes", //m
|
||||
"ut_mt": "continuar generando hashes de otros archivos mientras se sube$N$Nquizás desactivar si tu CPU o HDD es un cuello de botella",
|
||||
"ut_ask": 'pedir confirmación antes de iniciar la subida">💭',
|
||||
"ut_pot": "mejorar la velocidad de subida en dispositivos lentos$Nsimplificando la interfaz de usuario",
|
||||
|
|
@ -219,6 +219,7 @@ Ls.spa = {
|
|||
"cl_reset": "restablecer",
|
||||
"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
|
||||
|
||||
"ct_grid": '田 cuadrícula',
|
||||
"ct_ttips": '◔ ◡ ◔">ℹ️ tooltips',
|
||||
|
|
@ -261,6 +262,7 @@ Ls.spa = {
|
|||
"cdt_lim": "número máximo de archivos a mostrar en una carpeta",
|
||||
"cdt_ask": "al llegar al final,$Nen lugar de cargar más archivos,$Npreguntar qué hacer",
|
||||
"cdt_hsort": "cuántas reglas de ordenación (<code>,sorthref</code>) incluir en las URLs de medios. Ponerlo a 0 también ignorará las reglas de ordenación incluidas en los enlaces de medios al hacer clic en ellos",
|
||||
"cdt_ren": "habilitar menú contextual personalizado, el menú normal sigue siendo accesible con shift + clic derecho", //m
|
||||
|
||||
"tt_entree": "mostrar panel de navegación (barra lateral con árbol de directorios)$NAtajo: B",
|
||||
"tt_detree": "mostrar breadcrumbs$NAtajo: B",
|
||||
|
|
@ -331,6 +333,7 @@ Ls.spa = {
|
|||
"mm_eunk": "Error desconocido",
|
||||
"mm_e404": "No se pudo reproducir el audio; error 404: Archivo no encontrado.",
|
||||
"mm_e403": "No se pudo reproducir el audio; error 403: Acceso denegado.\n\nIntenta pulsar F5 para recargar, quizás se cerró tu sesión",
|
||||
"mm_e415": "No se pudo reproducir el audio; error 415: Falló la conversión del archivo; revisa los registros del servidor.", //m
|
||||
"mm_e500": "No se pudo reproducir el audio; error 500: Revisa los registros del servidor.",
|
||||
"mm_e5xx": "No se pudo reproducir el audio; error del servidor ",
|
||||
"mm_nof": "no se encuentran más archivos de audio cerca",
|
||||
|
|
@ -418,9 +421,10 @@ Ls.spa = {
|
|||
"fcc_warn": "copiados {0} elementos al portapapeles\n\npero: solo <b>esta</b> pestaña del navegador puede pegarlos\n(dado que la selección es absolutamente masiva)",
|
||||
|
||||
"fp_apply": "usar estos nombres",
|
||||
"fp_skip": "omitir conflictos", //m
|
||||
"fp_ecut": "primero corta o copia algunos archivos / carpetas para pegar / mover\n\nnota: puedes cortar / pegar entre diferentes pestañas del navegador",
|
||||
"fp_ename": "{0} elementos no se pueden mover aquí porque los nombres ya existen. Dales nuevos nombres abajo para continuar, o deja el nombre en blanco para omitirlos:",
|
||||
"fcp_ename": "{0} elementos no se pueden copiar aquí porque los nombres ya existen. Dales nuevos nombres abajo para continuar, o deja el nombre en blanco para omitirlos:",
|
||||
"fp_ename": "{0} elementos no se pueden mover aquí porque los nombres ya existen. Dales nuevos nombres abajo para continuar, o deja el nombre en blanco (\"omitir conflictos\") para omitirlos:", //m
|
||||
"fcp_ename": "{0} elementos no se pueden copiar aquí porque los nombres ya existen. Dales nuevos nombres abajo para continuar, o deja el nombre en blanco (\"omitir conflictos\") para omitirlos:", //m
|
||||
"fp_emore": "todavía quedan algunas colisiones de nombres por resolver",
|
||||
"fp_ok": "movimiento correcto",
|
||||
"fcp_ok": "copia correcta",
|
||||
|
|
@ -439,7 +443,7 @@ Ls.spa = {
|
|||
"fcp_both_b": "<a href=\"#\" id=\"modal-ok\">Copiar</a><a href=\"#\" id=\"modal-ng\">Subir</a>",
|
||||
|
||||
"mk_noname": "escribe un nombre en el campo de texto de la izquierda antes de hacer eso :p",
|
||||
"nmd_i1": "también puedes añadir la extensión que quieras, por ejemplo <code>.txt</code>", //m
|
||||
"nmd_i1": "también puedes añadir la extensión que quieras, por ejemplo <code>.md</code>", //m
|
||||
"nmd_i2": "solo puedes crear archivos <code>.md</code> porque no tienes permiso para borrar", //m
|
||||
|
||||
"tv_load": "Cargando documento de texto:\n\n{0}\n\n{1}% ({2} de {3} MiB cargados)",
|
||||
|
|
@ -636,6 +640,23 @@ Ls.spa = {
|
|||
"ur_um": "Finalizado;\n{0} subidas OK,\n{1} subidas fallidas, lo siento",
|
||||
"ur_sm": "Finalizado;\n{0} archivos encontrados en el servidor,\n{1} archivos NO encontrados en el servidor",
|
||||
|
||||
"rc_opn": "abrir", //m
|
||||
"rc_ply": "reproducir", //m
|
||||
"rc_pla": "reproducir como audio", //m
|
||||
"rc_txt": "abrir en el visor de archivos", //m
|
||||
"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
|
||||
"rc_pst": "pegar", //m
|
||||
"rc_nfo": "nueva carpeta", //m
|
||||
"rc_nfi": "nuevo archivo", //m
|
||||
"rc_sal": "seleccionar todo", //m
|
||||
"rc_sin": "invertir selección", //m
|
||||
|
||||
"lang_set": "¿refrescar para que el cambio surta efecto?",
|
||||
|
||||
"splash": {
|
||||
|
|
@ -677,6 +698,8 @@ Ls.spa = {
|
|||
"ta1": "primero escribe tu nueva contraseña",
|
||||
"ta2": "repite para confirmar la nueva contraseña:",
|
||||
"ta3": "hay un error; por favor, inténtalo de nuevo",
|
||||
"nop": "ERROR: La contraseña no puede estar vacía", //m
|
||||
"nou": "ERROR: El nombre de usuario y/o la contraseña no pueden estar vacíos", //m
|
||||
"aa1": "archivos entrantes:",
|
||||
"ab1": "desactivar no304",
|
||||
"ac1": "activar no304",
|
||||
|
|
|
|||
|
|
@ -155,7 +155,7 @@ Ls.swe = {
|
|||
"ul_par": "samtidiga uppladdningar:",
|
||||
"ut_rand": "slumpa filnamn",
|
||||
"ut_u2ts": "bevara tidsstämpeln för senaste ändring$Nfrån ditt filsystem till servern\">📅",
|
||||
"ut_ow": "skriv över existerande filer på servern?$N🛡️: aldrig (skapar ett nytt filnamn istället)$N🕒: skriv över om serverns fil är äldre än din$N♻️: skriv alltid över om filerna skiljer sig",
|
||||
"ut_ow": "skriv över existerande filer på servern?$N🛡️: aldrig (skapar ett nytt filnamn istället)$N🕒: skriv över om serverns fil är äldre än din$N♻️: skriv alltid över om filerna skiljer sig$N⏭️: hoppa ovillkorligen över alla befintliga filer", //m
|
||||
"ut_mt": "fortsätt hasha filer under uppladdningens gång$N$Nstäng av om din CPU eller disk är en flaskhals",
|
||||
"ut_ask": 'bekräfta innan uppladdningar påbörjas">💭',
|
||||
"ut_pot": "förbättra uppladdningshastigheten på långsamma enheter$Ngenom att förenkla användargränssnittet",
|
||||
|
|
@ -220,6 +220,7 @@ Ls.swe = {
|
|||
"cl_reset": "återställ",
|
||||
"cl_hpick": "tryck på en kolumntitel för att dölja den i filvyn",
|
||||
"cl_hcancel": "kolumndöljning avbruten",
|
||||
"cl_rcm": "högerklicksmeny", //m
|
||||
|
||||
"ct_grid": '田 rutnätet',
|
||||
"ct_ttips": '◔ ◡ ◔">ℹ️ tips',
|
||||
|
|
@ -262,6 +263,7 @@ Ls.swe = {
|
|||
"cdt_lim": "högsta antal filer att visa in en mapp",
|
||||
"cdt_ask": "när du når botten av vyn,$Nbe om en åtgärd istället för att ladda fler filer",
|
||||
"cdt_hsort": "hur många sorteringsregler (<code>,sorthref</code>) att inkludera i media-URL:er. Sätts detta till 0 kommer regler i klickade medialänkar även att ignoreras",
|
||||
"cdt_ren": "aktivera anpassad högerklicksmeny, den vanliga menyn är tillgänglig med shift + högerklick", //m
|
||||
|
||||
"tt_entree": "visa trädvy$NSnabbtangent: B",
|
||||
"tt_detree": "visa brödsmulor$NSnabbtangent: B",
|
||||
|
|
@ -332,6 +334,7 @@ Ls.swe = {
|
|||
"mm_eunk": "Okänt Fel",
|
||||
"mm_e404": "Kunde inte spela upp ljudfil; fel 404: Filen hittades inte.",
|
||||
"mm_e403": "Kunde inte spela upp ljudfil; fel 403: Åtkomst nekad.\n\nProva att ladda om sidan med F5, du kanske blev utloggad",
|
||||
"mm_e415": "Kunde inte spela upp ljudfil; fel 415: Filkonvertering misslyckades; kolla serverloggen.", //m
|
||||
"mm_e500": "Kunde inte spela upp ljudfil; fel 500: Kolla serverloggen.",
|
||||
"mm_e5xx": "Kunde inte spela upp ljudfil; serverfel ",
|
||||
"mm_nof": "hittade inga fler låtar i närheten",
|
||||
|
|
@ -419,9 +422,10 @@ Ls.swe = {
|
|||
"fcc_warn": 'kopierade {0} objekt till urklippet, men:\n\nendast <b>denna</b> webbläsarflik kan klistra in dem\n(eftersom urvalet är så enormt stort)',
|
||||
|
||||
"fp_apply": "använd dessa namn",
|
||||
"fp_skip": "skippa upptagna", //m
|
||||
"fp_ecut": "klipp eller kopiera filer / mappar först för att klistra / flytta dem\n\nobs.: du kan klippa och klistra mellan webbläsarflikar",
|
||||
"fp_ename": "{0} objekt kan ej flyttas hit eftersom filnamnen redan är tagna. Ge dem nya namn nedan för att fortsätta, eller lämna fältet tomt för att skippa:",
|
||||
"fcp_ename": "{0} objekt kan ej kopieras hit eftersom filnamnen redan är tagna. Ge dem nya namn nedan för att fortsätta, eller lämna fältet tomt för att skippa:",
|
||||
"fp_ename": "{0} objekt kan ej flyttas hit eftersom filnamnen redan är tagna. Ge dem nya namn nedan för att fortsätta, eller lämna fältet tomt (\"skippa upptagna\") för att skippa:", //m
|
||||
"fcp_ename": "{0} objekt kan ej kopieras hit eftersom filnamnen redan är tagna. Ge dem nya namn nedan för att fortsätta, eller lämna fältet tomt (\"skippa upptagna\") för att skippa:", //m
|
||||
"fp_emore": "det finns fortfarande filnamnskrockar att fixa",
|
||||
"fp_ok": "flytt lyckades",
|
||||
"fcp_ok": "kopiering lyckades",
|
||||
|
|
@ -440,7 +444,7 @@ Ls.swe = {
|
|||
"fcp_both_b": '<a href="#" id="modal-ok">Kopiera</a><a href="#" id="modal-ng">Ladda upp</a>',
|
||||
|
||||
"mk_noname": "skriv ett namn i fältet till vänster först :p",
|
||||
"nmd_i1": "lägg också till filändelsen du vill ha, till exempel <code>.txt</code>", //m
|
||||
"nmd_i1": "lägg också till filändelsen du vill ha, till exempel <code>.md</code>", //m
|
||||
"nmd_i2": "du kan bara skapa <code>.md</code>-filer eftersom du inte har borttagningsbehörighet", //m
|
||||
|
||||
"tv_load": "Laddar textfil:\n\n{0}\n\n{1}% ({2} av {3} MiB laddat)",
|
||||
|
|
@ -637,6 +641,23 @@ Ls.swe = {
|
|||
"ur_um": "Klar;\n{0} uppladdningar gick okej,\n{1} uppladdningar misslyckades, ledsen",
|
||||
"ur_sm": "Klar;\n{0} filer hittades på servern,\n{1} filer hittades INTE på servern",
|
||||
|
||||
"rc_opn": "öppna", //m
|
||||
"rc_ply": "spela upp", //m
|
||||
"rc_pla": "spela upp som ljud", //m
|
||||
"rc_txt": "öppna i filvisare", //m
|
||||
"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
|
||||
"rc_pst": "klistra in", //m
|
||||
"rc_nfo": "ny mapp", //m
|
||||
"rc_nfi": "ny fil", //m
|
||||
"rc_sal": "markera alla", //m
|
||||
"rc_sin": "invertera markering", //m
|
||||
|
||||
"lang_set": "uppdatera för att ändringen ska ta effekt?",
|
||||
|
||||
"splash": {
|
||||
|
|
@ -678,6 +699,8 @@ Ls.swe = {
|
|||
"ta1": "fyll i ditt nya lösenord",
|
||||
"ta2": "upprepa det nya lösenordet:",
|
||||
"ta3": "det blev fel; vänligen försök igen",
|
||||
"nop": "FEL: Lösenordet får inte vara tomt", //m
|
||||
"nou": "FEL: Användarnamn och/eller lösenord får inte vara tomt", //m
|
||||
"aa1": "inkommande filer:",
|
||||
"ab1": "avaktivera no304",
|
||||
"ac1": "aktivera no304",
|
||||
|
|
|
|||
|
|
@ -155,7 +155,7 @@ Ls.tur = {
|
|||
"ul_par": "paralel yüklemeler:",
|
||||
"ut_rand": "dosya adlarını rastgeleleştir",
|
||||
"ut_u2ts": "kendi dosyalarınızdan sunucuya$Nzaman damgasını kopyala\">📅",
|
||||
"ut_ow": "sunucudaki mevcut dosyaları üzerine yazmak mı?$N🛡️: asla (yerine yeni bir dosya adı oluşturur)$N🕒: sunucu dosyası sizinkinden daha eskiyse üzerine yaz$N♻️: dosyalar farklıysa her zaman üzerine yaz",
|
||||
"ut_ow": "sunucudaki mevcut dosyaları üzerine yazmak mı?$N🛡️: asla (yerine yeni bir dosya adı oluşturur)$N🕒: sunucu dosyası sizinkinden daha eskiyse üzerine yaz$N♻️: dosyalar farklıysa her zaman üzerine yaz$N⏭️: mevcut tüm dosyaları koşulsuz atla", //m
|
||||
"ut_mt": "yükleme yaparken diğer dosyaların hash'lenmesini durdur$N$kötü bir CPU veya HDD'ye sahipseniz kullanabilirsiniz.",
|
||||
"ut_ask": 'yüklemeye başlamadan önce doğrulama mesajı göster">💭',
|
||||
"ut_pot": "arayüzü daha az karmaşık hale getirerek$Nyükleme hızını yavaş cihazlarda artır",
|
||||
|
|
@ -220,6 +220,7 @@ Ls.tur = {
|
|||
"cl_reset": "sıfırla",
|
||||
"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
|
||||
|
||||
"ct_grid": '田 ızgara',
|
||||
"ct_ttips": '◔ ◡ ◔">ℹ️ ipuçları',
|
||||
|
|
@ -262,6 +263,7 @@ Ls.tur = {
|
|||
"cdt_lim": "bir klasörde gösterilecek maksimum dosya sayısı",
|
||||
"cdt_ask": "aşağı kaydırırken,$Ndaha fazla dosya yüklemek yerine,$Nne yapılacağını sor",
|
||||
"cdt_hsort": "medya-URL'lerinde dahil edilecek sıralama kurallarının sayısı (<code>,sorthref</code>). Bunu 0 olarak ayarlamak, tıklanırken medya bağlantılarına dahil edilen sıralama kurallarını da yok sayacaktır",
|
||||
"cdt_ren": "özel sağ tık menüsünü etkinleştir, normal menü shift + sağ tık ile erişilebilir", //m
|
||||
|
||||
"tt_entree": "navigasyon panosunu göster (yan dizin panosu)$NHotkey: B",
|
||||
"tt_detree": "içerik haritasını göster$Kısayol: B",
|
||||
|
|
@ -332,6 +334,7 @@ Ls.tur = {
|
|||
"mm_eunk": "Bilinmeyen Hata",
|
||||
"mm_e404": "Ses oynatılamadı; hata 404: Dosya bulunamadı.",
|
||||
"mm_e403": "Ses oynatılamadı; hata 403: Erişim reddedildi.\n\nYeniden yüklemek için F5 tuşuna basın, oturumunuz kapanmış olabilir.",
|
||||
"mm_e415": "Ses oynatılamadı; hata 415: Dosya dönüştürme başarısız oldu; sunucu günlüklerini kontrol edin.", //m
|
||||
"mm_e500": "Ses oynatılamadı; hata 500: Sunucu günlüklerini kontrol edin.",
|
||||
"mm_e5xx": "Ses oynatılamadı; sunucu hatası ",
|
||||
"mm_nof": "yakınlarda başka ses dosyası bulunamadı",
|
||||
|
|
@ -419,9 +422,10 @@ Ls.tur = {
|
|||
"fcc_warn": '{0} öge panoya kopyalandı\n\namma velakin: sadece <b>bu</b> tarayıcı penceresine yapıştırılabilirler\n(çünkü seçiminin boyutu hayvan gibi)',
|
||||
|
||||
"fp_apply": "bu adları kullan",
|
||||
"fp_skip": "çakışmaları atla", //m
|
||||
"fp_ecut": "önce bazı dosyaları / klasörleri kes veya kopyala, sonra yapıştır / taşı\n\nnot: farklı tarayıcı sekmeleri arasında kes / yapıştır yapabilirsiniz",
|
||||
"fp_ename": "{0} öğe buraya taşınamaz çünkü adlar zaten alınmış. Devam etmek için aşağıda yeni adlar verin veya atlamak için adları boş bırakın:",
|
||||
"fcp_ename": "{0} öğe buraya kopyalanamaz çünkü adlar zaten alınmış. Devam etmek için aşağıda yeni adlar verin veya atlamak için adları boş bırakın:",
|
||||
"fp_ename": "{0} öğe buraya taşınamaz çünkü adlar zaten alınmış. Devam etmek için aşağıda yeni adlar verin veya atlamak için adları boş bırakın (\"çakışmaları atla\"):", //m
|
||||
"fcp_ename": "{0} öğe buraya kopyalanamaz çünkü adlar zaten alınmış. Devam etmek için aşağıda yeni adlar verin veya atlamak için adları boş bırakın (\"çakışmaları atla\"):", //m
|
||||
"fp_emore": "hâlâ düzeltilmesi gereken bazı dosya adı çakışmaları var",
|
||||
"fp_ok": "taşıma tamam",
|
||||
"fcp_ok": "kopyalama tamam",
|
||||
|
|
@ -440,7 +444,7 @@ Ls.tur = {
|
|||
"fcp_both_b": '<a href="#" id="modal-ok">Kopyala</a><a href="#" id="modal-ng">Yükle</a>',
|
||||
|
||||
"mk_noname": "bunu yapmadan önce soldaki boşluğa bir şeyler yazsana :p",
|
||||
"nmd_i1": "ayrıca istediğin dosya uzantısını ekleyebilirsin, örneğin <code>.txt</code>", //m
|
||||
"nmd_i1": "ayrıca istediğin dosya uzantısını ekleyebilirsin, örneğin <code>.md</code>", //m
|
||||
"nmd_i2": "silme iznin olmadığı için yalnızca <code>.md</code> dosyaları oluşturabilirsin", //m
|
||||
|
||||
"tv_load": "Metin belgesi yükleniyor:\n\n{0}\n\n{1}% ({2} of {3} MiB yüklendi)",
|
||||
|
|
@ -637,6 +641,23 @@ Ls.tur = {
|
|||
"ur_um": "Tamamlandı;\n{0} yükleme başarılı,\n{1} yükleme başarısız oldu, üzgünüm",
|
||||
"ur_sm": "Tamamlandı;\n{0} dosya sunucuda bulundu,\n{1} dosya sunucuda bulunamadı",
|
||||
|
||||
"rc_opn": "aç", //m
|
||||
"rc_ply": "oynat", //m
|
||||
"rc_pla": "ses olarak oynat", //m
|
||||
"rc_txt": "dosya görüntüleyicide aç", //m
|
||||
"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
|
||||
"rc_pst": "yapıştır", //m
|
||||
"rc_nfo": "yeni klasör", //m
|
||||
"rc_nfi": "yeni dosya", //m
|
||||
"rc_sal": "tümünü seç", //m
|
||||
"rc_sin": "seçimi tersine çevir", //m
|
||||
|
||||
"lang_set": "Değişikliklerin etki göstermesi için sayfa yenilensin mi?",
|
||||
|
||||
"splash": {
|
||||
|
|
@ -673,11 +694,14 @@ Ls.tur = {
|
|||
"ta1": "ilk önce yeni şifreyi doldur",
|
||||
"ta2": "yeni şifreyi onaylamak için tekrar girin:",
|
||||
"ta3": "bir yazım hatası bulundu; lütfen tekrar deneyin",
|
||||
"nop": "HATA: Parola boş olamaz", //m
|
||||
"nou": "HATA: Kullanıcı adı ve/veya parola boş olamaz", //m
|
||||
"aa1": "gelen dosyalar:",
|
||||
"ab1": "no304'ü devre dışı bırak",
|
||||
"ac1": "no304'ü etkinleştir",
|
||||
"ad1": "no304'ü etkinleştirmek, tüm önbelleği devre dışı bırakır; bunu k304 yeterli olmadıysa deneyin. Bu, büyük miktarda ağ trafiği israf edecektir!",
|
||||
"ae1": "aktif indirmeler:",
|
||||
"af1": "son yüklemeleri göster",
|
||||
"ag1": "bilinen IdP kullanıcılarını göster", //m
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -155,7 +155,7 @@ Ls.ukr = {
|
|||
"ul_par": "паралельні завантаження:",
|
||||
"ut_rand": "випадкові імена файлів",
|
||||
"ut_u2ts": "копіювати часову мітку останньої зміни$Nз вашої файлової системи на сервер\">📅",
|
||||
"ut_ow": "перезаписати існуючі файли на сервері?$N🛡️: ніколи (замість цього створить нове ім'я файлу)$N🕒: перезаписати, якщо файл на сервері старіший за ваш$N♻️: завжди перезаписувати, якщо файли відрізняються",
|
||||
"ut_ow": "перезаписати існуючі файли на сервері?$N🛡️: ніколи (замість цього створить нове ім'я файлу)$N🕒: перезаписати, якщо файл на сервері старіший за ваш$N♻️: завжди перезаписувати, якщо файли відрізняються$N⏭️: безумовно пропускати всі наявні файли", //m
|
||||
"ut_mt": "продовжувати хешування інших файлів під час завантаження$N$Nможливо, вимкніть, якщо ваш CPU або HDD є вузьким місцем",
|
||||
"ut_ask": 'запитати підтвердження перед початком завантаження">💭',
|
||||
"ut_pot": "покращити швидкість завантаження на повільних пристроях$Nроблячи інтерфейс менш складним",
|
||||
|
|
@ -220,6 +220,7 @@ Ls.ukr = {
|
|||
"cl_reset": "скинути",
|
||||
"cl_hpick": "натисніть на заголовки стовпців, щоб приховати їх у таблиці нижче",
|
||||
"cl_hcancel": "приховання стовпців скасовано",
|
||||
"cl_rcm": "контекстне меню", //m
|
||||
|
||||
"ct_grid": '田 сітка',
|
||||
"ct_ttips": '◔ ◡ ◔">ℹ️ підказки',
|
||||
|
|
@ -262,6 +263,7 @@ Ls.ukr = {
|
|||
"cdt_lim": "максимальна кількість файлів для показу в папці",
|
||||
"cdt_ask": "при прокрутці до низу,$Nзамість завантаження більше файлів,$Nзапитати, що робити",
|
||||
"cdt_hsort": "скільки правил сортування (<code>,sorthref</code>) включати в медіа-URL. Встановлення цього в 0 також буде ігнорувати правила сортування, включені в медіа посилання при їх натисканні",
|
||||
"cdt_ren": "увімкнути користувацьке контекстне меню, звичайне меню доступне при натисканні shift і правої кнопки миші", //m
|
||||
|
||||
"tt_entree": "показати панель навігації (бічна панель дерева каталогів)$NГаряча клавіша: B",
|
||||
"tt_detree": "показати хлібні крихти$NГаряча клавіша: B",
|
||||
|
|
@ -332,6 +334,7 @@ Ls.ukr = {
|
|||
"mm_eunk": "Невідома помилка",
|
||||
"mm_e404": "Не вдалося відтворити аудіо; помилка 404: Файл не знайдено.",
|
||||
"mm_e403": "Не вдалося відтворити аудіо; помилка 403: Доступ заборонено.\n\nСпробуйте натиснути F5 для перезавантаження, можливо, ви вийшли з системи",
|
||||
"mm_e415": "Не вдалося відтворити аудіо; помилка 415: Не вдалося перетворити файл; перевірте логи сервера.", //m
|
||||
"mm_e500": "Не вдалося відтворити аудіо; помилка 500: Перевірте логи сервера.",
|
||||
"mm_e5xx": "Не вдалося відтворити аудіо; помилка сервера ",
|
||||
"mm_nof": "не знаходжу більше аудіо файлів поблизу",
|
||||
|
|
@ -419,9 +422,10 @@ Ls.ukr = {
|
|||
"fcc_warn": 'скопійовано {0} елементів до буфера\n\nале: тільки <b>ця</b> вкладка браузера може їх вставити\n(оскільки вибір настільки величезний)',
|
||||
|
||||
"fp_apply": "використовувати ці імена",
|
||||
"fp_skip": "пропустити конфлікти", //m
|
||||
"fp_ecut": "спочатку вирізати або скопіювати деякі файли / папки для вставки / переміщення\n\nзауваження: ви можете вирізати / вставляти через різні вкладки браузера",
|
||||
"fp_ename": "{0} елементів не можуть бути переміщені сюди, тому що імена вже зайняті. Дайте їм нові імена нижче для продовження, або залиште ім'я порожнім, щоб пропустити їх:",
|
||||
"fcp_ename": "{0} елементів не можуть бути скопійовані сюди, тому що імена вже зайняті. Дайте їм нові імена нижче для продовження, або залиште ім'я порожнім, щоб пропустити їх:",
|
||||
"fp_ename": "{0} елементів не можуть бути переміщені сюди, тому що імена вже зайняті. Дайте їм нові імена нижче для продовження, або залиште ім'я порожнім (\"пропустити конфлікти\"), щоб пропустити їх:", //m
|
||||
"fcp_ename": "{0} елементів не можуть бути скопійовані сюди, тому що імена вже зайняті. Дайте їм нові імена нижче для продовження, або залиште ім'я порожнім (\"пропустити конфлікти\"), щоб пропустити їх:", //m
|
||||
"fp_emore": "є ще деякі конфлікти імен файлів, які потрібно виправити",
|
||||
"fp_ok": "переміщення OK",
|
||||
"fcp_ok": "копіювання OK",
|
||||
|
|
@ -440,7 +444,7 @@ Ls.ukr = {
|
|||
"fcp_both_b": '<a href="#" id="modal-ok">Скопіювати</a><a href="#" id="modal-ng">Завантажити</a>',
|
||||
|
||||
"mk_noname": "введіть ім'я в текстове поле зліва перед тим, як робити це :p",
|
||||
"nmd_i1": "ви також можете додати потрібне розширення, наприклад <code>.txt</code>", //m
|
||||
"nmd_i1": "ви також можете додати потрібне розширення, наприклад <code>.md</code>", //m
|
||||
"nmd_i2": "ви можете створювати тільки файли <code>.md</code>, оскільки не маєте дозволу на видалення", //m
|
||||
|
||||
"tv_load": "Завантаження текстового документа:\n\n{0}\n\n{1}% ({2} з {3} MiB завантажено)",
|
||||
|
|
@ -637,6 +641,23 @@ Ls.ukr = {
|
|||
"ur_um": "Завершено;\n{0} завантажень OK,\n{1} завантажень невдалих, вибачте",
|
||||
"ur_sm": "Завершено;\n{0} файлів знайдено на сервері,\n{1} файлів НЕ знайдено на сервері",
|
||||
|
||||
"rc_opn": "відкрити", //m
|
||||
"rc_ply": "відтворити", //m
|
||||
"rc_pla": "відтворити як аудіо", //m
|
||||
"rc_txt": "відкрити у переглядачі файлів", //m
|
||||
"rc_md": "відкрити в текстовому редакторі", //m
|
||||
"rc_dl": "завантажити", //m
|
||||
"rc_zip": "завантажити як архів", //m
|
||||
"rc_cpl": "копіювати посилання", //m
|
||||
"rc_del": "видалити", //m
|
||||
"rc_cut": "вирізати", //m
|
||||
"rc_cpy": "копіювати", //m
|
||||
"rc_pst": "вставити", //m
|
||||
"rc_nfo": "нова папка", //m
|
||||
"rc_nfi": "новий файл", //m
|
||||
"rc_sal": "вибрати все", //m
|
||||
"rc_sin": "інвертувати вибір", //m
|
||||
|
||||
"lang_set": "оновити сторінку, щоб зміни набули чинності?",
|
||||
|
||||
"splash": {
|
||||
|
|
@ -678,6 +699,8 @@ Ls.ukr = {
|
|||
"ta1": "спочатку заповніть ваш новий пароль",
|
||||
"ta2": "повторіть для підтвердження нового пароля:",
|
||||
"ta3": "описка; спробуйте знову",
|
||||
"nop": "ПОМИЛКА: Пароль не може бути порожнім", //m
|
||||
"nou": "ПОМИЛКА: Ім’я користувача та/або пароль не можуть бути порожніми", //m
|
||||
"aa1": "вхідні файли:",
|
||||
"ab1": "вимкнути no304",
|
||||
"ac1": "увімкнути no304",
|
||||
|
|
|
|||
740
copyparty/web/tl/vie.js
Normal file
740
copyparty/web/tl/vie.js
Normal file
|
|
@ -0,0 +1,740 @@
|
|||
Ls.vie = {
|
||||
"tt": "Tiếng Việt",
|
||||
|
||||
"cols": {
|
||||
"c": "nút hành động",
|
||||
"dur": "thời lượng",
|
||||
"q": "chất lượng / bitrate",
|
||||
"Ac": "codec âm thanh",
|
||||
"Vc": "codec video",
|
||||
"Fmt": "định dạng / container",
|
||||
"Ahash": "checksum âm thanh",
|
||||
"Vhash": "checksum video",
|
||||
"Res": "độ phân giải",
|
||||
"T": "loại tệp",
|
||||
"aq": "chất lượng âm thanh / bitrate",
|
||||
"vq": "chất lượng video / bitrate",
|
||||
"pixfmt": "subsampling / pixel structure",
|
||||
"resw": "độ phân giải ngang",
|
||||
"resh": "độ phân giải dọc",
|
||||
"chs": "kênh âm thanh",
|
||||
"hz": "tốc độ lấy mẫu",
|
||||
},
|
||||
|
||||
"hks": [
|
||||
[
|
||||
"misc",
|
||||
["ESC", "đóng nhiều mục"],
|
||||
|
||||
"file-manager",
|
||||
["G", "chuyển đổi chế độ xem danh sách / lưới"],
|
||||
["T", "chuyển đổi ảnh thu nhỏ / biểu tượng"],
|
||||
["⇧ A/D", "kích thước ảnh thu nhỏ"],
|
||||
["ctrl-K", "xoá mục đã chọn"],
|
||||
["ctrl-X", "cắt mục đã chọn vào bảng nhớ tạm"],
|
||||
["ctrl-C", "sao chép mục đã chọn vào bảng nhớ tạm"],
|
||||
["ctrl-V", "dán (di chuyển/sao chép) tại đây"],
|
||||
["Y", "tải xuống mục đã chọn"],
|
||||
["F2", "đổi tên mục đã chọn"],
|
||||
|
||||
"file-list-sel",
|
||||
["space", "chuyển đổi chọn tệp"],
|
||||
["↑/↓", "di chuyển con trỏ chọn"],
|
||||
["ctrl ↑/↓", "di chuyển con trỏ và khung nhìn"],
|
||||
["⇧ ↑/↓", "chọn tệp trước / sau"],
|
||||
["ctrl-A", "chọn tất cả tệp / thư mục"],
|
||||
], [
|
||||
"navigation",
|
||||
["B", "chuyển đổi đường dẫn / thanh điều hướng"],
|
||||
["I/K", "thư mục trước / sau"],
|
||||
["M", "thư mục cha (hoặc thu gọn hiện tại)"],
|
||||
["V", "chuyển đổi thư mục / tệp văn bản trong thanh điều hướng"],
|
||||
["A/D", "kích thước thanh điều hướng"],
|
||||
], [
|
||||
"audio-player",
|
||||
["J/L", "bài trước / sau"],
|
||||
["U/O", "lùi / tiến 10 giây"],
|
||||
["0..9", "nhảy đến 0%..90%"],
|
||||
["P", "phát/tạm dừng (cũng khởi động)"],
|
||||
["S", "chọn bài đang phát"],
|
||||
["Y", "tải xuống bài hát"],
|
||||
], [
|
||||
"image-viewer",
|
||||
["J/L, ←/→", "ảnh trước / sau"],
|
||||
["Home/End", "ảnh đầu / cuối"],
|
||||
["F", "toàn màn hình"],
|
||||
["R", "xoay theo chiều kim đồng hồ"],
|
||||
["⇧ R", "xoay ngược chiều kim đồng hồ"],
|
||||
["S", "chọn ảnh"],
|
||||
["Y", "tải xuống ảnh"],
|
||||
], [
|
||||
"video-player",
|
||||
["U/O", "lùi / tiến 10 giây"],
|
||||
["P/K/Space", "phát/tạm dừng"],
|
||||
["C", "tiếp tục phát bài tiếp theo"],
|
||||
["V", "vòng lặp"],
|
||||
["M", "tắt tiếng"],
|
||||
["[ and ]", "đặt khoảng lặp"],
|
||||
], [
|
||||
"textfile-viewer",
|
||||
["I/K", "tệp trước / sau"],
|
||||
["M", "đóng tệp văn bản"],
|
||||
["E", "chỉnh sửa tệp văn bản"],
|
||||
["S", "chọn tệp (để cắt/sao chép/đổi tên)"],
|
||||
]
|
||||
],
|
||||
|
||||
"m_ok": "OK",
|
||||
"m_ng": "Hủy",
|
||||
|
||||
"enable": "Bật",
|
||||
"danger": "NGUY HIỂM",
|
||||
"clipped": "đã sao chép vào bảng nhớ tạm",
|
||||
|
||||
"ht_s1": "giây",
|
||||
"ht_s2": "giây",
|
||||
"ht_m1": "phút",
|
||||
"ht_m2": "phút",
|
||||
"ht_h1": "giờ",
|
||||
"ht_h2": "giờ",
|
||||
"ht_d1": "ngày",
|
||||
"ht_d2": "ngày",
|
||||
"ht_and": " và ",
|
||||
|
||||
"goh": "bảng điều khiển",
|
||||
"gop": 'thư mục trước">trước',
|
||||
"gou": 'thư mục cha">lên',
|
||||
"gon": 'thư mục sau">tiếp',
|
||||
"logout": "Đăng xuất ",
|
||||
"login": "Đăng nhập",
|
||||
"access": "quyền truy cập",
|
||||
"ot_close": "đóng menu con",
|
||||
|
||||
"ot_search": "tìm kiếm các tệp theo thuộc tính, đường dẫn / tên, tag nhạc hoặc bất kỳ sự kết hợp nào của chúng$N$N<code>foo bar</code> = phải chứa cả «foo» và «bar»,$N<code>foo -bar</code> = phải chứa «foo» nhưng không chứa «bar»,$N<code>^yana .opus$</code> = bắt đầu bằng «yana» và là tệp «opus»$N<code>"try unite"</code> = chứa chính xác «try unite»$N$Nđịnh dạng ngày là iso-8601, như$N<code>2009-12-31</code> hoặc <code>2020-09-12 23:30:00</code>",
|
||||
|
||||
"ot_unpost": "unpost: xoá các tệp đã tải lên gần đây hoặc huỷ những tệp đang tải dở",
|
||||
"ot_bup": "bup: trình tải lên cơ bản, hỗ trợ cả Netscape 4.0",
|
||||
"ot_mkdir": "mkdir: tạo thư mục mới",
|
||||
"ot_md": "new-file: tạo tệp văn bản mới",
|
||||
"ot_msg": "msg: gửi tin nhắn đến nhật ký máy chủ",
|
||||
"ot_mp": "tuỳ chọn trình phát phương tiện",
|
||||
"ot_cfg": "tuỳ chọn cấu hình",
|
||||
"ot_u2i": 'up2k: tải tệp lên (nếu bạn có quyền ghi) hoặc chuyển sang chế độ tìm kiếm để xem chúng có tồn tại ở đâu đó trên máy chủ không$N$Ntải lên có thế tiếp tục nếu bị gián đoạn, chạy đa luồng và giữ nguyên dấu thời gian tệp, nhưng tiêu tốn nhiều CPU hơn [🎈] (trình tải lên cơ bản)<br /><br />trong quá trình tải, biểu tượng này sẽ trở thành chỉ thị tiến trình!',
|
||||
"ot_u2w": 'up2k: tải tệp lên với hỗ trợ tiếp tục (đóng trình duyệt và thả lại tệp đó lên sau)$N$Nchạy đa luồng và giữ nguyên dấu thời gian tệp, nhưng tiêu tốn nhiều CPU hơn [🎈] (trình tải lên cơ bản)<br /><br />trong quá trình tải, biểu tượng này sẽ trở thành chỉ thị tiến trình!',
|
||||
"ot_noie": 'Vui lòng sử dụng Chrome / Firefox / Edge',
|
||||
|
||||
"ab_mkdir": "tạo thư mục",
|
||||
"ab_mkdoc": "tạo tệp văn bản",
|
||||
"ab_msg": "gửi tin nhắn đến nhật ký máy chủ",
|
||||
|
||||
"ay_path": "bỏ qua đến thư mục",
|
||||
"ay_files": "bỏ qua đến tệp",
|
||||
|
||||
"wt_ren": "đổi tên các mục đã chọn$NPhím tắt: F2",
|
||||
"wt_del": "xóa các mục đã chọn$NPhím tắt: ctrl-K",
|
||||
"wt_cut": "cắt các mục đã chọn <small>(sau đó dán ở nơi khác)</small>$NPhím tắt: ctrl-X",
|
||||
"wt_cpy": "sao chép các mục đã chọn vào bảng nhớ tạm$N(để dán ở nơi khác)$NPhím tắt: ctrl-C",
|
||||
"wt_pst": "dán một lựa chọn đã cắt / sao chép trước đó$NPhím tắt: ctrl-V",
|
||||
"wt_selall": "chọn tất cả các tệp$NPhím tắt: ctrl-A (khi tệp được chọn)",
|
||||
"wt_selinv": "đảo ngược lựa chọn",
|
||||
"wt_zip1": "tải thư mục này dưới định dạng nén",
|
||||
"wt_selzip": "tải lựa chọn dưới định dạng nén",
|
||||
"wt_seldl": "tải lựa chọn dưới dạng các tệp riêng biệt$NPhím tắt: Y",
|
||||
"wt_npirc": "sao chép thông tin bản nhạc theo định dạng irc",
|
||||
"wt_nptxt": "sao chép thông tin bản nhạc dưới dạng văn bản thuần túy",
|
||||
"wt_m3ua": "thêm vào danh sách phát m3u (bấm <code>📻copy</code> sau)",
|
||||
"wt_m3uc": "sao chép danh sách phát m3u vào bảng nhớ tạm",
|
||||
"wt_grid": "chuyển đổi chế độ xem danh sách / lưới $NPhím tắt: G",
|
||||
"wt_prev": "bài trước$NPhím tắt: J",
|
||||
"wt_play": "phát / tạm dừng$NPhím tắt: P",
|
||||
"wt_next": "bài sau$NPhím tắt: L",
|
||||
|
||||
"ul_par": "tải lên song song:",
|
||||
"ut_rand": "ngẫu nhiên hoá tên tệp",
|
||||
"ut_u2ts": "sao chép dấu thời gian chỉnh sửa cuối$Ntừ hệ thống tệp của bạn lên máy chủ\">📅",
|
||||
"ut_ow": "ghi đè các tệp đã có trên máy chủ?$N🛡️: không bao giờ (sẽ tạo tên tệp mới)$N🕒: ghi đè nếu tệp trên máy chủ cũ hơn$N♻️: luôn ghi đè nếu hai tệp khác nhau$N⏭️: bỏ qua vô điều kiện tất cả các tệp hiện có", //m
|
||||
"ut_mt": "tiếp tục hash các tệp khác trong khi tải lên$N$NCó thể tắt nếu CPU hoặc HDD của bạn bị nghẽn",
|
||||
"ut_ask": 'yêu cầu xác nhận trước khi bắt đầu tải lên">💭',
|
||||
"ut_pot": "cải thiện tốc độ tải lên trên các thiết bị chậm$Nbằng cách đơn giản hoá giao diện người dùng",
|
||||
"ut_srch": "không tải lên, chỉ kiểm tra xem tệp$Nđã tồn tại trên máy chủ hay chưa (sẽ quét toàn bộ thư mục bạn có quyền đọc)",
|
||||
"ut_par": "tạm dừng tải lên bằng cách đặt thành 0$N$NTăng lên nếu kết nối chậm hoặc độ trễ cao$N$NGiữ ở mức 1 khi dùng LAN hoặc nếu ổ cứng máy chủ bị nghẽn",
|
||||
"ul_btn": "thả tệp / thư mục<br>ở đây (hoặc nhấn vào tôi)",
|
||||
"ul_btnu": "T Ả I L Ê N",
|
||||
"ul_btns": "T Ì M K I Ế M",
|
||||
|
||||
"ul_hash": "hash",
|
||||
"ul_send": "gửi",
|
||||
"ul_done": "hoàn tất",
|
||||
"ul_idle1": "chưa có mục nào trong hàng chờ tải lên",
|
||||
"ut_etah": "tốc độ <em>hash</em> trung bình và thời gian dự kiến để hoàn tất",
|
||||
"ut_etau": "tốc độ <em>tải lên</em> trung bình và thời gian dự kiến để hoàn tất",
|
||||
"ut_etat": "tốc độ <em>tổng</em> trung bình và thời gian dự kiến để hoàn tất",
|
||||
|
||||
"uct_ok": "hoàn tất thành công",
|
||||
"uct_ng": "không hợp lệ: lỗi / bị từ chối / không tìm thấy",
|
||||
"uct_done": "đã xử lý: gồm cả thành công và không hợp lệ",
|
||||
"uct_bz": "đang hash hoặc tải lên",
|
||||
"uct_q": "nhàn rỗi, đang chờ",
|
||||
|
||||
"utl_name": "tên tệp",
|
||||
"utl_ulist": "danh sách",
|
||||
"utl_ucopy": "sao chép",
|
||||
"utl_links": "đường dẫn",
|
||||
"utl_stat": "trạng thái",
|
||||
"utl_prog": "tiến trình",
|
||||
|
||||
// keep short:
|
||||
|
||||
// phần up2k
|
||||
"utl_404": "404",
|
||||
"utl_err": "LỖI",
|
||||
"utl_oserr": "Lỗi hệ thống",
|
||||
"utl_found": "tìm thấy",
|
||||
"utl_defer": "hoãn",
|
||||
"utl_yolo": "YOLO",
|
||||
"utl_done": "hoàn tất",
|
||||
|
||||
"ul_flagblk": "tệp đã được thêm vào hàng chờ</b><br>tuy vậy đang có một tiến trình up2k đang chạy ở một tab khác<br>vui lòng đợi cho đến khi tiến trình đó hoàn tất hoặc bị hủy",
|
||||
"ul_btnlk": "cài đặt của máy chủ đã khóa tùy chọn ở trạng thái này",
|
||||
|
||||
"udt_up": "Tải lên",
|
||||
"udt_srch": "Tìm kiếm",
|
||||
"udt_drop": "thả vào đây",
|
||||
|
||||
"u_nav_m": '<h6>chọn phương thức tải lên</h6><code>Enter</code> = Tệp (một hoặc nhiều)\n<code>ESC</code> = Một thư mục (kèm thư mục con)',
|
||||
"u_nav_b": '<a href="#" id="modal-ok">Tệp</a><a href="#" id="modal-ng">Một thư mục</a>',
|
||||
|
||||
// settings / config:
|
||||
"cl_opts": "tuỳ chọn",
|
||||
"cl_hfsz": "kích thước tệp",
|
||||
"cl_themes": "giao diện",
|
||||
"cl_langs": "ngôn ngữ",
|
||||
"cl_ziptype": "định dạng nén",
|
||||
"cl_uopts": "tuỳ chọn up2k",
|
||||
"cl_favico": "favicon",
|
||||
"cl_bigdir": "thư mục lớn",
|
||||
"cl_hsort": "#sắp xếp",
|
||||
"cl_keytype": "ghi chú bàn phím",
|
||||
"cl_hiddenc": "cột đã ẩn",
|
||||
"cl_hidec": "ẩn",
|
||||
"cl_reset": "đặt lại",
|
||||
"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
|
||||
|
||||
// settings / tuỳ chọn
|
||||
"ct_grid": '田 chế độ lưới',
|
||||
"ct_ttips": '༼ ◕_◕ ༽">ℹ️ tooltips',
|
||||
"ct_thumb": 'ở chế độ lưới, chuyển biểu tượng hoặc hình thu nhỏ$NPhím tắt: T">🖼️ ảnh thu nhỏ',
|
||||
"ct_csel": 'dùng CTRL và SHIFT để chọn tệp trong chế độ lưới">sel',
|
||||
"ct_dl": 'cưỡng chế tải xuống (không hiện thị trong dòng) khi nhấp vào tệp">dl',
|
||||
"ct_ihop": 'khi đóng trình xem ảnh, cuộn xuống tệp đã xem gần nhất">g⮯',
|
||||
"ct_dots": 'hiển thị tệp ẩn (nếu máy chủ cho phép)">dotfiles',
|
||||
"ct_qdel": 'khi xóa tệp, chỉ hỏi xác nhận một lần">qdel',
|
||||
"ct_dir1st": 'sắp xếp thư mục trước tệp">📁 first',
|
||||
"ct_nsort": 'sắp xếp tự nhiên (cho tên tệp có số ở đầu)">nsort',
|
||||
"ct_utc": 'hiển thị mọi thời gian theo UTC">UTC',
|
||||
"ct_readme": 'hiển thị README.md trong danh sách thư mục">📜 readme',
|
||||
"ct_idxh": 'hiển thị index.html thay cho danh sách thư mục">htm',
|
||||
"ct_sbars": 'hiển thị thanh cuộn">⟊',
|
||||
|
||||
// tuỳ chọn up2k
|
||||
"cut_umod": "nếu tệp đã tồn tại trên máy chủ, cập nhật dấu thời gian chỉnh sửa cuối của máy chủ cho khớp với tệp cục bộ của bạn (yêu cầu quyền ghi và xóa)\">re📅",
|
||||
|
||||
"cut_turbo": "nút YOLO, bạn gần như KHÔNG nên bật tuỳ chọn này:$N$Ndùng khi bạn đang tải lên một lượng tệp rất lớn và phải khởi động lại vì lý do nào đó, và muốn tiếp tục tải càng sớm sàng tốt$N$Ntuỳ chọn này thay thế kiểm tra hash bằng kiểm tra <em>"kích thước tệp ở trên máy chủ có giống nhau không"</em> nên nếu nội dung tệp khác nhau thì sẽ KHÔNG được tải lên$N$Nbạn nên tắt tuỳ chọn này sau khi tải lên xong, và "tải lên" lại các tệp đó để xác minh\">turbo",
|
||||
|
||||
"cut_datechk": "không có tác dụng trừ khi nút turbo được bật$N$Ngiảm mức độ yolo một chút; kiểm tra xem dấu thời gian tệp trên máy chủ có khớp với của bạn không$N$Nnên <em>về lý thuyết</em> có thể phát hiện phần lớn tệp chưa xong hoặc bị lỗi, nhưng không thể thay thế cho việc chạy xác minh sau khi tắt turbo\">date-chk",
|
||||
|
||||
"cut_u2sz": "kích thước (tính theo MiB) của mỗi khối tải lên; dùng kích thước khối lớn khi truyền ở khoảng cách lục địa. Hãy thử giá trị nhỏ hơn với kết nối không ổn định",
|
||||
|
||||
"cut_flag": "đảm bảo chỉ một tab được tải lên tại một thời điểm $N -- tab khác cũng cần bật tùy chọn này $N -- tác dụng trong các tab cùng tên miền",
|
||||
|
||||
"cut_az": "tải tệp theo thứ tự bảng chữ cái thay vì tệp nhỏ trước$N$Ntải lên theo thứ tự bảng chữ cái giúp dễ quan sát nếu có vấn đề trên máy chủ, nhưng làm tốc độ tải chậm hơn trên mạng cáp quang hoặc LAN",
|
||||
|
||||
"cut_nag": "thông báo của hệ điều hành khi tải lên hoàn tất$N(chỉ khi trình duyệt hoặc tab không hoạt động)",
|
||||
"cut_sfx": "âm báo khi tải lên hoàn tất$N(chỉ khi trình duyệt hoặc tab không hoạt động)",
|
||||
|
||||
"cut_mt": "dùng đa luồng để tăng tốc hash tệp$N$dùng web workers và cần $nhiều RAM hơn (tối đa thêm 512 MiB)$N$làm cho https nhanh hơn 30%, http nhanh hơn 4.5 lần\">mt",
|
||||
|
||||
"cut_wasm": "dùng wasm thay vì bộ hash tích hợp của trình duyệt; nhanh hơn trên trình duyệt chromium nhưng làm tăng tải CPU, và nhiều bản chrome cũ có lỗi khiến trình duyệt dùng hết RAM và treo nếu bật tuỳ chọn này\">wasm",
|
||||
|
||||
// favicon
|
||||
"cft_text": "chuỗi favicon (để trống và làm mới trang để tắt)",
|
||||
"cft_fg": "màu chữ",
|
||||
"cft_bg": "màu nền",
|
||||
|
||||
// big dirs
|
||||
"cdt_lim": "số tệp tối đa hiển thị trong thư mục",
|
||||
"cdt_ask": "khi cuộn xuống cuối,$Nthay vì tải thêm tệp,$Nhỏi người dùng muốn làm gì",
|
||||
"cdt_hsort": "số lượng luật sắp xếp(<code>,sorthref</code>) được đưa vào URL media. Đặt bằng 0 cũng sẽ bỏ qua các quy tắc sắp xếp trong liên kết media khi nhấp vào chúng",
|
||||
"cdt_ren": "bật menu chuột phải tùy chỉnh, menu mặc định vẫn truy cập được bằng shift + chuột phải", //m
|
||||
|
||||
"tt_entree": "hiển thị thanh điều hướng (cây thư mục)$NPhím tắt: B",
|
||||
"tt_detree": "hiển thị đường dẫn$NPhím tắt: B",
|
||||
"tt_visdir": "cuộn đến thư mục đã chọn",
|
||||
"tt_ftree": "chuyển đổi cây thư mục / tệp văn bản$NPhím tắt: V",
|
||||
"tt_pdock": "hiển thị thư mục cha trong thanh ghim trên cùng",
|
||||
"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 )",
|
||||
|
||||
"ml_pmode": "ở cuối thư mục...",
|
||||
"ml_btns": "lệnh",
|
||||
"ml_tcode": "mã hoá lại",
|
||||
// chắc là phần nhạc
|
||||
"ml_tcode2": "mã hoá lại thành",
|
||||
"ml_tint": "tô màu",
|
||||
"ml_eq": "bộ cân bằng âm thanh",
|
||||
"ml_drc": "bộ nén dải động",
|
||||
|
||||
// nhạc
|
||||
|
||||
"mt_loop": "lặp lại một bài\">🔁",
|
||||
"mt_one": "dừng sau một bài\">1️⃣",
|
||||
"mt_shuf": "trộn các bài trong thư mụcr\">🔀",
|
||||
"mt_aplay": "tự động phát nếu có ID bài trong link bạn nhấp để truy cập máy chủ$N$Ntắt tuỳ chọn sẽ ngăn URL của trang cập nhật theo ID bài khi phát nhạc, tránh tự động phát nếu cài đặt mất nhưng URL còn\">a▶",
|
||||
"mt_preload": "bắt đầu tải bài hát tiếp theo khi gần hết bài để phát liền mạch\">preload",
|
||||
"mt_prescan": "chuyển đến thư mục tiếp theo trước khi bài cuối cùng $Nkết thúc, giúp giữ trình duyệt hoạt động $N và không dừng phát nhạc\">nav",
|
||||
"mt_fullpre": "cố gắng tải trước toàn bộ bài;$N✅ bật với kết nối <b>không ổn định</b>,$N❌ tắt <b>với kết nối chậm</b>\">full",
|
||||
"mt_fau": "trên điện thoại, ngăn nhạc dừng nếu bài tiếp theo tải chậm (có thể gây lỗi hiển thị tag nhạc)\">☕️",
|
||||
"mt_waves": "thanh tiến trình bài hát dạng sóng:$Nhiển thị biên độ âm thanh trong thanh tiến trình\">~s",
|
||||
"mt_npclip": "hiển thị nút để sao chép bài đang phát\">/np",
|
||||
"mt_m3u_c": "hiển thị nút để sao chép $Nnhững bài đã chọn dưới dạng danh sách phát m3u8\">📻",
|
||||
"mt_octl": "tích hợp hệ điều hành (phím tắt media / OSD)\">os-ctl",
|
||||
"mt_oseek": "cho phép tìm kiếm qua tích hợp hệ điều hành$N$Nlưu ý: trên một số thiết bị (iphone), $Nthao tác này sẽ thay thế nút bài hát tiếp theo\">seek",
|
||||
"mt_oscv": "hiển thị bài album trên OSD\">art",
|
||||
"mt_follow": "giữ bài đang phát trong tầm nhìn\">🎯",
|
||||
"mt_compact": "giao diện điều khiển thu gọn\">⟎",
|
||||
"mt_uncache": "xoá bộ nhớ đệm (thử nếu trình duyệt lưu trữ đệm $Nmột bản nhạc bị lỗi và không thể phát)\">uncache",
|
||||
"mt_mloop": "lặp trong thư mục đang mở\">🔁 loop",
|
||||
"mt_mnext": "tải thư mục tiếp theo và tiếp tục\">📂 next",
|
||||
"mt_mstop": "dừng phát\">⏸ stop",
|
||||
"mt_cflac": "chuyển flac / wav sang {0}\">flac",
|
||||
"mt_caac": "chuyển aac / m4a sang {0}\">aac",
|
||||
"mt_coth": "chuyển mọi loại khác (trừ mp3) thành {0}\">oth",
|
||||
"mt_c2opus": "lựa chọn tốt nhất cho máy tính, laptop, android\">opus",
|
||||
"mt_c2owa": "opus-weba, cho iOS 17.5 trở lên\">owa",
|
||||
"mt_c2caf": "opus-caf, cho iOS 11 đến 17\">caf",
|
||||
"mt_c2mp3": "dùng trên thiết bị cũ\">mp3",
|
||||
"mt_c2flac": "chất lượng âm thanh tốt nhất, nhưng tệp tải xuống lớn\">flac",
|
||||
"mt_c2wav": "phát không nén (tệp còn lớn hơn nữa)\">wav",
|
||||
"mt_c2ok": "lựa chọn hợp lý",
|
||||
"mt_c2nd": "không phải định dạng khuyến nghị cho thiết bị, nhưng hãy thử nếu bạn muốn",
|
||||
"mt_c2ng": "thiết bị dường như không hỗ trợ định dạng này, nhưng hãy thử nếu bạn muốn",
|
||||
"mt_xowa": "có một vài lỗi trên iOS ngăn phát nền với định dạng này; vui lòng dùng caf hoặc mp3",
|
||||
"mt_tint": "mức nền (0-100) trên thanh tiến trình",
|
||||
|
||||
"mt_eq": "bật bộ cân bằng âm thanh và bộ tăng ích;$N$Nboost <code>0 </code> = âm lượng chuẩn 100% (không chỉnh)$N$Nwidth <code>1 </code> = stereo chuẩn (không chỉnh)$Nwidth <code>0.5</code> = 50% pha trái-phải$Nwidth <code>0 </code> = mono$N$Nboost <code>-0.8</code> & width <code>10</code> = loại bỏ lời hát :^)$N$Nbật EQ giúp cho album được phát liền mạch không ngắt quãng, nên giữ các giá trị bằng 0 (trừ width = 1) nếu bạn không muốn thay đổi âm thanh gốc",
|
||||
|
||||
"mt_drc": "bật bộ nén dải động (làm phẳng âm lượng / brickwaller); cũng bật EQ để cân bằng, nên đặt tất cả EQ trừ 'width' = 0 nếu không muốn$N$Ngiảm âm thanh trên THRESHOLD dB; với mỗi RATIO dB vượt THRESHOLD thì có 1 dB đầu ra, ví dụ tresh -24 và ratio 12 => âm lượng không vượt -22 dB, có thể tăng EQ boost lên 0.8 hoặc 1.8 với ATK 0 và RLS lớn 90 (chỉ Firefox; RLS max 1 trên browser khác)$N$NXem Wikipedia để hiểu chi tiết hơn",
|
||||
|
||||
"mb_play": "phát",
|
||||
"mm_hashplay": "phát bản nhạc này?",
|
||||
"mm_m3u": "bấm <code>Enter/OK</code> để phát\nbấm <code>ESC/Cancel</code> để chỉnh sửa",
|
||||
"mp_breq": "cần firefox 82+ hoặc chrome 73+ hoặc iOS 15+",
|
||||
"mm_bload": "đang tải...",
|
||||
"mm_bconv": "đang chuyển đổi sang {0}, vui lòng chờ...",
|
||||
"mm_opusen": "trình duyệt không hỗ trợ tệp aac / m4a;\nchuyển sang định dạng opus hiện đã được bật",
|
||||
"mm_playerr": "phát lỗi: ",
|
||||
"mm_eabrt": "Việc phát nhạc đã bị huỷ",
|
||||
"mm_enet": "Kết nối Internet không ổn định",
|
||||
"mm_edec": "Tệp này dường như đã bị hỏng??",
|
||||
"mm_esupp": "Trình duyệt của bạn không nhận dạng được định dạng tệp này.",
|
||||
"mm_eunk": "Lỗi không xác định",
|
||||
"mm_e404": "Không thể phát âm thanh; lỗi 404: Không tìm thấy tệp.",
|
||||
"mm_e403": "Không thể phát âm thanh; lỗi 403: Từ chối truy cập.\n\nThử nhấn F5 để tải lại, có thể bạn đã đăng xuất",
|
||||
"mm_e415": "Không thể phát âm thanh; lỗi 415: Chuyển đổi tệp thất bại; kiểm tra nhật ký máy chủ.", //m
|
||||
"mm_e500": "Không thể phát âm thanh; lỗi 500: Kiểm tra nhật ký máy chủ.",
|
||||
"mm_e5xx": "Không thể phát âm thanh; lỗi máy chủ ",
|
||||
"mm_nof": "không tìm thấy thêm tệp âm thanh nào gần đó",
|
||||
"mm_prescan": "Đang tìm bài nhạc tiếp theo để phát...",
|
||||
"mm_scank": "Đã tìm thấy bài nhạc tiếp theo:",
|
||||
"mm_uncache": "đã xoá bộ nhớ đệm; tất cả bài nhạc sẽ được tải lại khi phát tiếp",
|
||||
"mm_hnf": "bài nhạc này không còn tồn tại nữa",
|
||||
|
||||
"im_hnf": "hình ảnh này không còn tồn tại nữa",
|
||||
|
||||
"f_empty": 'thư mục này trống',
|
||||
"f_chide": 'ẩn cột «{0}»\n\bạn có thế hiện lại nó trong tuỳ chọn cấu hình',
|
||||
"f_bigtxt": "tệp này nặng {0} MiB -- xác nhận xem dưới dạng văn bản?",
|
||||
"f_bigtxt2": "chỉ xem phần cuối của tệp? điều này cũng sẽ bật theo dõi, hiển thị các dòng văn bản mới được thêm vào theo thời gian thực",
|
||||
"fbd_more": '<div id="blazy">hiện <code>{0}</code> của <code>{1}</code> tệp; <a href="#" id="bd_more">hiện {2}</a> hoặc <a href="#" id="bd_all">hiện tất cả</a></div>',
|
||||
"fbd_all": '<div id="blazy">đang hiện <code>{0}</code> của <code>{1}</code> tệp; <a href="#" id="bd_all">hiện tất cả</a></div>',
|
||||
"f_anota": "chỉ {0} trong {1} tệp được chọn;\nđể chọn toàn bộ thư mục, trước tiên hãy kéo xuống cuối",
|
||||
|
||||
"f_dls": 'những đường dẫn đến tệp trong thư mục này\nđã được chuyển thành đường dẫn tải trực tiếp',
|
||||
|
||||
"f_partial": "Để tải an toàn một tệp đang được tải lên, hãy bấm vào tệp có cùng tên nhưng *không* có phần mở rộng <code>.PARTIAL</code>. Hãy nhấn CANCEL hoặc Escape để thực hiện.\n\nNếu nhấn OK / Enter, cảnh báo sẽ bị bỏ qua và bạn sẽ tải tệp tạm <code>.PARTIAL</code> thay vào đó, gần như chắc chắn dẫn đến dữ liệu bị hỏng.",
|
||||
|
||||
"ft_paste": "dán {0} mục$NPhím tắt: ctrl-V",
|
||||
"fr_eperm": "không thể đổi tên:\nbạn không có quyền “move” trong thư mục này",
|
||||
"fd_eperm": "không thể xóa:\nbạn không có quyền “delete” trong thư mục này",
|
||||
"fc_eperm": "không thể cắt:\nbạn không có quyền “move” trong thư mục này",
|
||||
"fp_eperm": "không thể dán:\nbạn không có quyền “write” trong thư mục này",
|
||||
"fr_emore": "hãy chọn ít nhất một mục để đổi tên",
|
||||
"fd_emore": "hãy chọn ít nhất một mục để xóa",
|
||||
"fc_emore": "hãy chọn ít nhất một mục để cắt",
|
||||
"fcp_emore": "hãy chọn ít nhất một mục để sao chép vào bảng nhớ tạm",
|
||||
|
||||
"fs_sc": "chia sẻ thư mục hiện tại",
|
||||
"fs_ss": "chia sẻ các tệp đã chọn",
|
||||
"fs_just1d": "bạn không thể chọn nhiều hơn một thư mục,\nhoặc trộn tệp và thư mục trong cùng một lựa chọn",
|
||||
"fs_abrt": "❌ hủy",
|
||||
"fs_rand": "🎲 tên ngẫu nhiên",
|
||||
"fs_go": "✅ tạo liên kết chia sẻ",
|
||||
"fs_name": "tên",
|
||||
"fs_src": "nguồn",
|
||||
"fs_pwd": "mật khẩu",
|
||||
"fs_exp": "hết hạn",
|
||||
"fs_tmin": "phút",
|
||||
"fs_thrs": "giờ",
|
||||
"fs_tdays": "ngày",
|
||||
"fs_never": "vĩnh viễn",
|
||||
"fs_pname": "tên liên kết tùy chọn; sẽ dùng tên ngẫu nhiên nếu để trống",
|
||||
"fs_tsrc": "tệp hoặc thư mục cần chia sẻ",
|
||||
"fs_ppwd": "mật khẩu tùy chọn",
|
||||
"fs_w8": "đang tạo liên kết chia sẻ...",
|
||||
"fs_ok": "nhấn <code>Enter/OK</code> để chép vào bảng nhớ tạm\nnhấn <code>ESC/Cancel</code> để đóng",
|
||||
|
||||
"frt_dec": "có thể sửa một số trường hợp tên tệp bị lỗi\">url-decode",
|
||||
"frt_rst": "khôi phục tên gốc\">↺ reset",
|
||||
"frt_abrt": "hủy và đóng cửa sổ này\">❌ cancel",
|
||||
"frb_apply": "ÁP DỤNG ĐỔI TÊN",
|
||||
"fr_adv": "đổi tên theo lô / metadata / pattern\">advanced",
|
||||
"fr_case": "regex phân biệt hoa thường\">case",
|
||||
"fr_win": "tên tương thích Windows; thay <code><>:"\\|?*</code> bằng ký tự fullwidth tiếng Nhật\">win",
|
||||
"fr_slash": "thay <code>/</code> bằng ký tự khác để tránh tạo thư mục mới\">no /",
|
||||
"fr_re": "regex áp dụng lên tên gốc; các nhóm bắt có thể được tham chiếu trong trường định dạng bên dưới như <code>(1)</code>, <code>(2)</code> ...",
|
||||
"fr_fmt": "lấy cảm hứng từ foobar2000:$N<code>(title)</code> được thay bằng tên bài hát,$N<code>[(artist) - ](title)</code> bỏ qua phần trong ngoặc nếu artist trống,$N<code>$lpad((tn),2,0)</code> thêm số 0 để tracknumber đủ 2 chữ số",
|
||||
"fr_pdel": "xóa",
|
||||
"fr_pnew": "lưu dưới tên mới",
|
||||
"fr_pname": "nhập tên cho preset mới",
|
||||
"fr_aborted": "đã hủy",
|
||||
"fr_lold": "tên cũ",
|
||||
"fr_lnew": "tên mới",
|
||||
"fr_tags": "tag của các tệp đã chọn (chỉ xem, không chỉnh sửa):",
|
||||
"fr_busy": "đang đổi tên {0} mục...\n\n{1}",
|
||||
"fr_efail": "đổi tên thất bại:\n",
|
||||
"fr_nchg": "{0} tên mới đã bị chỉnh sửa do <code>win</code> và/hoặc <code>no /</code>\n\nTiếp tục với các tên đã chỉnh sửa?",
|
||||
|
||||
"fd_ok": "hoàn tất xoá",
|
||||
"fd_err": "xoá gặp lỗi:\n",
|
||||
"fd_none": "không xóa được mục nào; có thể bị chặn bởi cấu hình máy chủ (xbd)?",
|
||||
"fd_busy": "đang xóa {0} mục...\n\n{1}",
|
||||
"fd_warn1": "XÓA {0} mục này?",
|
||||
"fd_warn2": "<b>Cảnh báo cuối!</b> Không thể hoàn tác. Xóa?",
|
||||
|
||||
"fc_ok": "đã cắt {0} mục",
|
||||
"fc_warn": "đã cắt {0} mục\n\nnhưng: chỉ <b>tab trình duyệt này</b> có thể dán\n(vì lựa chọn quá lớn)",
|
||||
|
||||
"fcc_ok": "đã sao chép {0} mục vào bảng nhớ tạm",
|
||||
"fcc_warn": "đã sao chép {0} mục vào bảng nhớ tạm\n\nnhưng: chỉ <b>tab trình duyệt này</b> có thể dán\n(vì lựa chọn quá lớn)",
|
||||
|
||||
"fp_apply": "dùng các tên này",
|
||||
"fp_skip": "bỏ qua xung đột", //m
|
||||
"fp_ecut": "hãy cắt hoặc sao chép một số tệp / thư mục trước khi dán / di chuyển\n\nlưu ý: bạn có thể cắt / dán giữa các tab trình duyệt khác nhau",
|
||||
"fp_ename": "{0} mục không thể được di chuyển vào đây vì tên đã tồn tại. Hãy đặt tên mới bên dưới để tiếp tục, hoặc để trống (\"bỏ qua xung đột\") để bỏ qua:", //m
|
||||
"fcp_ename": "{0} mục không thể được sao chép vào đây vì tên đã tồn tại. Hãy đặt tên mới bên dưới để tiếp tục, hoặc để trống (\"bỏ qua xung đột\") để bỏ qua:", //m
|
||||
"fp_emore": "vẫn còn xung đột tên tệp cần xử lý",
|
||||
"fp_ok": "di chuyển OK",
|
||||
"fcp_ok": "sao chép OK",
|
||||
"fp_busy": "đang di chuyển {0} mục...\n\n{1}",
|
||||
"fcp_busy": "đang sao chép {0} mục...\n\n{1}",
|
||||
"fp_abrt": "đang hủy...",
|
||||
"fp_err": "di chuyển thất bại:\n",
|
||||
"fcp_err": "sao chép thất bại:\n",
|
||||
"fp_confirm": "di chuyển {0} mục này vào đây?",
|
||||
"fcp_confirm": "sao chép {0} mục này vào đây?",
|
||||
"fp_etab": "không đọc được bảng nhớ tạm từ tab trình duyệt khác",
|
||||
"fp_name": "đang tải lên tệp từ thiết bị. Hãy đặt tên cho tệp:",
|
||||
"fp_both_m": "<h6>chọn thao tác để dán</h6><code>Enter</code> = Di chuyển {0} tệp từ «{1}»\n<code>ESC</code> = Tải lên {2} tệp từ thiết bị",
|
||||
"fcp_both_m": "<h6>chọn thao tác để dán</h6><code>Enter</code> = Sao chép {0} tệp từ «{1}»\n<code>ESC</code> = Tải lên {2} tệp từ thiết bị",
|
||||
"fp_both_b": "<a href=\"#\" id=\"modal-ok\">Di chuyển</a><a href=\"#\" id=\"modal-ng\">Tải lên</a>",
|
||||
"fcp_both_b": "<a href=\"#\" id=\"modal-ok\">Sao chép</a><a href=\"#\" id=\"modal-ng\">Tải lên</a>",
|
||||
|
||||
"mk_noname": "hãy nhập tên vào ô bên trái trước khi thực hiện :p",
|
||||
"nmd_i1": "hãy thêm cả phần mở rộng tệp bạn muốn, ví dụ <code>.md</code>",
|
||||
"nmd_i2": "bạn chỉ có thể tạo tệp <code>.md</code> vì bạn không có quyền xóa",
|
||||
|
||||
"tv_load": "Đang tải tài liệu văn bản:\n\n{0}\n\n{1}% ({2} / {3} MiB)",
|
||||
"tv_xe1": "không thể tải tệp văn bản:\n\nlỗi ",
|
||||
"tv_xe2": "404, không tìm thấy tệp",
|
||||
"tv_lst": "danh sách các tệp văn bản trong",
|
||||
"tvt_close": "quay lại chế độ xem thư mục$NPhím tắt: M (hoặc Esc)\">❌ close",
|
||||
"tvt_dl": "tải xuống tệp này$NPhím tắt: Y\">💾 download",
|
||||
"tvt_prev": "hiển thị tài liệu trước đó$NPhím tắt: I\">⬆ prev",
|
||||
"tvt_next": "hiển thị tài liệu kế tiếp$NPhím tắt: K\">⬇ next",
|
||||
"tvt_sel": "chọn tệp (để cắt / sao chép / xóa / ...)$NPhím tắt: S\">sel",
|
||||
"tvt_j": "chuẩn hóa json$NPhím tắt: shift-J\">j",
|
||||
"tvt_edit": "mở tệp trong trình soạn thảo văn bản$NPhím tắt: E\">✏️ edit",
|
||||
"tvt_tail": "theo dõi thay đổi của tệp; hiển thị dòng mới theo thời gian thực\">📡 follow",
|
||||
"tvt_wrap": "ngắt dòng\">↵",
|
||||
"tvt_atail": "khóa cuộn ở cuối trang\">⚓",
|
||||
"tvt_ctail": "giải mã màu terminal (ansi escape codes)\">🌈",
|
||||
"tvt_ntail": "giới hạn scrollback (số byte văn bản được giữ trong bộ nhớ)",
|
||||
|
||||
"m3u_add1": "đã thêm 1 bài vào danh sách phát m3u",
|
||||
"m3u_addn": "đã thêm {0} bài vào danh sách phát m3u",
|
||||
"m3u_clip": "danh sách phát m3u đã được chép vào bảng nhớ tạm\n\nbạn nên tạo một tệp văn bản mới tên bất kỳ.m3u rồi dán nội dung danh sách phát vào đó để có thể phát được",
|
||||
|
||||
"gt_vau": "không hiện video, chỉ phát âm thanh\">🎧",
|
||||
"gt_msel": "bật chọn nhiều; ctrl-click để ghi đè$N$N<em>khi bật: nhấn đúp để mở tệp / thư mục</em>$N$NPhím tắt: S\">multiselect",
|
||||
"gt_crop": "cắt chính giữa ảnh\">crop",
|
||||
|
||||
"gt_3x": "ảnh độ nét cao\">3x",
|
||||
"gt_zoom": "zoom",
|
||||
"gt_chop": "chop",
|
||||
"gt_sort": "sắp xếp theo",
|
||||
"gt_name": "tên",
|
||||
"gt_sz": "dung lượng",
|
||||
"gt_ts": "ngày",
|
||||
"gt_ext": "loại",
|
||||
"gt_c1": "rút ngắn tên tệp hơn (hiện ít hơn)",
|
||||
"gt_c2": "rút ngắn tên tệp ít hơn (hiện nhiều hơn)",
|
||||
|
||||
"sm_w8": "đang tìm...",
|
||||
"sm_prev": "kết quả bên dưới là từ lần tìm trước:\n ",
|
||||
"sl_close": "đóng kết quả tìm kiếm",
|
||||
"sl_hits": "hiển thị {0} kết quả",
|
||||
"sl_moar": "tải thêm",
|
||||
|
||||
"s_sz": "dung lượng",
|
||||
"s_dt": "ngày",
|
||||
"s_rd": "đường dẫn",
|
||||
"s_fn": "tên",
|
||||
"s_ta": "tag",
|
||||
"s_ua": "up@",
|
||||
"s_ad": "nâng cao",
|
||||
"s_s1": "tối thiểu MiB",
|
||||
"s_s2": "tối đa MiB",
|
||||
"s_d1": "ngày tối thiểu (iso8601)",
|
||||
"s_d2": "ngày tối đa (iso8601)",
|
||||
"s_u1": "tải lên sau",
|
||||
"s_u2": "và/hoặc trước",
|
||||
"s_r1": "đường dẫn chứa (cách nhau bằng dấu cách)",
|
||||
"s_f1": "tên chứa (thêm -nope để phủ định)",
|
||||
"s_t1": "tag chứa (^=bắt đầu, kết thúc=$)",
|
||||
"s_a1": "thuộc tính metadata cụ thể",
|
||||
|
||||
"md_eshow": "không thể tải",
|
||||
"md_off": "[📜<em>readme</em>] đã tắt trong [⚙️] -- tài liệu bị ẩn",
|
||||
|
||||
"badreply": "Không thể phân tích phản hồi từ máy chủ",
|
||||
|
||||
"xhr403": "403: Access denied\n\nhãy thử nhấn F5, có thể bạn đã bị đăng xuất",
|
||||
"xhr0": "không rõ (có thể mất kết nối với máy chủ hoặc máy chủ đang offline)",
|
||||
"cf_ok": "rất tiếc, lớp bảo vệ DD" + wah + "oS đã kích hoạt\n\nmọi thứ sẽ tiếp tục sau khoảng 30 giây\n\nnếu không có gì xảy ra, hãy nhấn F5 để tải lại trang",
|
||||
"tl_xe1": "không thể liệt kê thư mục con:\n\nlỗi ",
|
||||
"tl_xe2": "404: Không tìm thấy thư mục",
|
||||
"fl_xe1": "không thể liệt kê tệp trong thư mục:\n\nlỗi ",
|
||||
"fl_xe2": "404: Không tìm thấy thư mục",
|
||||
"fd_xe1": "không thể tạo thư mục con:\n\nlỗi ",
|
||||
"fd_xe2": "404: Không tìm thấy thư mục cha",
|
||||
"fsm_xe1": "không thể gửi tin nhắn:\n\nlỗi ",
|
||||
"fsm_xe2": "404: Không tìm thấy thư mục cha",
|
||||
"fu_xe1": "không tải được danh sách unpost từ máy chủ:\n\nlỗi ",
|
||||
"fu_xe2": "404: Không tìm thấy tệp??",
|
||||
|
||||
"fz_tar": "file gnu-tar không nén (linux / mac)",
|
||||
"fz_pax": "file tar định dạng pax không nén (chậm hơn)",
|
||||
"fz_targz": "gnu-tar với nén gzip mức 3$N$Nthường rất chậm nên$Nhãy dùng tar không nén",
|
||||
"fz_tarxz": "gnu-tar với nén xz mức 1$N$Nthường rất chậm nên$Nhãy dùng tar không nén",
|
||||
"fz_zip8": "zip với tên tệp utf8 (có thể lỗi trên windows 7 và cũ hơn)",
|
||||
"fz_zipd": "zip với tên tệp cp437 truyền thống, dành cho phần mềm rất cũ",
|
||||
"fz_zipc": "cp437 với crc32 tính sớm,$Ndành cho MS-DOS PKZIP v2.04g (tháng 10/1993)$N(mất thời gian xử lý lâu hơn trước khi tải xuống bắt đầu)",
|
||||
|
||||
"un_m1": "bạn có thể xóa các lần upload gần đây (hoặc hủy những mục chưa hoàn tất) bên dưới",
|
||||
"un_upd": "làm mới",
|
||||
"un_m4": "hoặc chia sẻ các tệp hiển thị bên dưới:",
|
||||
"un_ulist": "hiện",
|
||||
"un_ucopy": "chép",
|
||||
"un_flt": "bộ lọc tùy chọn: đường dẫn phải chứa",
|
||||
"un_fclr": "xóa bộ lọc",
|
||||
"un_derr": "xóa unpost thất bại:\n",
|
||||
"un_f5": "có gì đó lỗi rồi, hãy thử tải lại trang hoặc nhấn F5",
|
||||
"un_uf5": "xin lỗi nhưng bạn cần tải lại trang (ví dụ nhấn F5 hoặc CTRL-R) trước khi có thể hủy upload này",
|
||||
"un_nou": "<b>cảnh báo:</b> máy chủ đang bận nên không thể hiển thị các upload chưa hoàn tất; hãy bấm “làm mới” sau một lúc",
|
||||
"un_noc": "<b>cảnh báo:</b> unpost cho các tệp đã upload xong không được bật hoặc không được phép trong cấu hình máy chủ",
|
||||
"un_max": "đang hiển thị 2000 tệp đầu tiên (hãy dùng bộ lọc)",
|
||||
"un_avail": "có thể xóa {0} tải lên gần đây<br />{1} mục chưa hoàn tất có thể hủy",
|
||||
"un_m2": "sắp xếp theo thời gian tải lên; mới nhất ở trên:",
|
||||
"un_no1": "không có tải lên nào đủ mới",
|
||||
"un_no2": "không có tải lên nào đủ mới khớp với bộ lọc đó",
|
||||
"un_next": "xóa {0} tệp kế bên dưới",
|
||||
"un_abrt": "hủy",
|
||||
"un_del": "xóa",
|
||||
"un_m3": "đang tải danh sách tải lên gần đây...",
|
||||
"un_busy": "đang xóa {0} tệp...",
|
||||
"un_clip": "{0} liên kết đã chép vào bảng nhớ tạm",
|
||||
|
||||
"u_https1": "bạn nên",
|
||||
"u_https2": "chuyển sang https",
|
||||
"u_https3": "để có hiệu suất tốt hơn",
|
||||
|
||||
"u_ancient": "trình duyệt của bạn quá cũ; bạn có thể <a href=\"#\" onclick=\"goto('bup')\">dùng bup</a> thay thế",
|
||||
"u_nowork": "cần Firefox 53+, Chrome 57+ hoặc iOS 11+",
|
||||
"tail_2old": "cần Firefox 105+, Chrome 71+ hoặc iOS 14.5+",
|
||||
"u_nodrop": "trình duyệt của bạn quá cũ để dùng kéo thả khi tải lên",
|
||||
"u_notdir": "đây không phải thư mục\n\ntrình duyệt của bạn quá cũ,\nvui lòng thử dùng dragdrop",
|
||||
|
||||
"u_uri": "để kéo thả ảnh từ cửa sổ trình duyệt khác,\nvui lòng thả lên nút tải lên lớn",
|
||||
|
||||
"u_enpot": "chuyển sang <a href=\"#\">giao diện đơn giản</a> (có thể tăng tốc độ tải lên)",
|
||||
"u_depot": "chuyển sang <a href=\"#\">giao diện đầy đủ</a> (có thể giảm tốc độ tải lên)",
|
||||
"u_gotpot": "đang chuyển sang giao diện đơn giản để cải thiện tốc độ tải lên\n\nbạn có thể đổi lại bất kỳ lúc nào",
|
||||
|
||||
"u_pott": "<p>tệp: <b>{0}</b> hoàn tất, <b>{1}</b> lỗi, <b>{2}</b> đang chạy, <b>{3}</b> chờ</p>",
|
||||
|
||||
"u_ever": "đây là trình tải lên cơ bản; up2k yêu cầu<br>Chrome 21 // Firefox 13 // Edge 12 // Opera 12 // Safari 5.1 trở lên",
|
||||
"u_su2k": "đây là trình tải lên cơ bản; <a href=\"#\" id=\"u2yea\">up2k</a> tốt hơn",
|
||||
|
||||
"u_uput": "tối ưu tốc độ (bỏ qua checksum)",
|
||||
"u_ewrite": "bạn không có quyền ghi vào thư mục này",
|
||||
"u_eread": "bạn không có quyền đọc thư mục này",
|
||||
"u_enoi": "tìm kiếm tệp chưa được bật trong cấu hình máy chủ",
|
||||
"u_enoow": "ghi đè không khả dụng; cần quyền Xóa",
|
||||
|
||||
"u_badf": "Đã bỏ qua {0} tệp (trong tổng {1}) có thể do quyền hệ thống tệp:\n\n",
|
||||
"u_blankf": "{0} tệp (trong tổng {1}) trống; vẫn tải lên?\n\n",
|
||||
"u_applef": "{0} tệp (trong tổng {1}) có thể không cần thiết;\nNhấn <code>OK/Enter</code> để BỎ QUA,\nNhấn <code>Cancel/ESC</code> để giữ lại và tải lên:\n\n",
|
||||
|
||||
"u_just1": "\nCó thể sẽ hoạt động tốt hơn nếu bạn chỉ chọn một tệp",
|
||||
"u_ff_many": "nếu bạn dùng <b>Linux / macOS / Android</b> thì số lượng tệp này <a href=\"https://bugzilla.mozilla.org/show_bug.cgi?id=1790500\" target=\"_blank\"><em>có thể</em> làm Firefox crash</a>\nnếu xảy ra, vui lòng thử lại hoặc dùng Chrome",
|
||||
|
||||
"u_up_life": "Tệp tải lên sẽ bị xóa khỏi máy chủ\n{0} sau khi hoàn tất",
|
||||
"u_asku": "tải {0} tệp này lên <code>{1}</code>",
|
||||
"u_unpt": "bạn có thể hoàn tác hoặc xóa lần tải lên này bằng biểu tượng ở góc trên bên trái 🧯",
|
||||
|
||||
"u_bigtab": "sắp hiển thị {0} tệp\n\nviệc này có thể làm trình duyệt treo, bạn có chắc không?",
|
||||
"u_scan": "Đang quét tệp...",
|
||||
"u_dirstuck": "bộ lặp thư mục bị treo khi truy cập {0} mục sau; sẽ bỏ qua:",
|
||||
"u_etadone": "Hoàn tất ({0}, {1} tệp)",
|
||||
"u_etaprep": "(đang chuẩn bị tải lên)",
|
||||
"u_hashdone": "hash hoàn tất",
|
||||
"u_hashing": "hash",
|
||||
"u_hs": "đang bắt tay...",
|
||||
"u_started": "tệp đang được tải lên; xem biểu tượng [🚀]",
|
||||
"u_dupdefer": "bị trùng; sẽ xử lý sau khi hoàn tất các tệp khác",
|
||||
|
||||
"u_actx": "nhấn vào dòng này để tránh giảm hiệu suất khi chuyển cửa sổ hoặc tab",
|
||||
"u_fixed": "OK; đã sửa 👍",
|
||||
|
||||
"u_cuerr": "không tải được phần {0}/{1};\ncó thể không nghiêm trọng, tiếp tục\n\ntệp: {2}",
|
||||
"u_cuerr2": "máy chủ từ chối phần tải {0}/{1};\nsẽ thử lại sau\n\ntệp: {2}\n\nlỗi ",
|
||||
"u_ehstmp": "sẽ thử lại; xem góc dưới bên phải",
|
||||
"u_ehsfin": "máy chủ từ chối yêu cầu hoàn tất tải lên; đang thử lại...",
|
||||
"u_ehssrch": "máy chủ từ chối yêu cầu tìm kiếm; đang thử lại...",
|
||||
"u_ehsinit": "máy chủ từ chối yêu cầu bắt đầu tải lên; đang thử lại...",
|
||||
"u_eneths": "lỗi mạng khi thực hiện bắt tay; đang thử lại...",
|
||||
"u_enethd": "lỗi mạng khi kiểm tra tồn tại mục tiêu; đang thử lại...",
|
||||
"u_cbusy": "chờ máy chủ cho phép tiếp tục sau sự cố mạng...",
|
||||
"u_ehsdf": "máy chủ hết dung lượng!\n\nsẽ tiếp tục thử trong trường hợp có ai đó giải phóng dung lượng",
|
||||
|
||||
"u_emtleak1": "có dấu hiệu cho thấy trình duyệt có thể bị rò rỉ bộ nhớ;\nvui lòng",
|
||||
"u_emtleak2": " <a href=\"{0}\">chuyển sang https (khuyến nghị)</a> hoặc ",
|
||||
"u_emtleak3": " ",
|
||||
"u_emtleakc": "thử cách sau:\n<ul><li>nhấn <code>F5</code> để tải lại trang</li><li>sau đó tắt nút <code>mt</code> trong <code>⚙️ cài đặt</code></li><li>và thử tải lại</li></ul>Tốc độ có thể chậm hơn một chút\nXin lỗi vì sự bất tiện\n\nPS: chrome v107 <a href=\"https://bugs.chromium.org/p/chromium/issues/detail?id=1354816\" target=\"_blank\">đã có bản sửa</a>",
|
||||
"u_emtleakf": "thử cách sau:\n<ul><li>nhấn <code>F5</code> để tải lại trang</li><li>sau đó bật <code>🥔</code> (potato) trong giao diện tải lên<li>và thử lại</li></ul>\nPS: firefox <a href=\"https://bugzilla.mozilla.org/show_bug.cgi?id=1790500\" target=\"_blank\">hy vọng sẽ có bản sửa</a>",
|
||||
|
||||
"u_s404": "không tìm thấy trên máy chủ",
|
||||
"u_expl": "giải thích",
|
||||
"u_maxconn": "đa số trình duyệt giới hạn giá trị này ở mức 6, nhưng Firefox cho phép tăng bằng <code>connections-per-server</code> trong <code>about:config</code>",
|
||||
|
||||
"u_tu": "<p class=\"warn\">CẢNH BÁO: turbo đang bật <span> client có thể không nhận diện và tiếp tục tải lại tệp chưa hoàn tất; xem tooltip nút turbo</span></p>",
|
||||
"u_ts": "<p class=\"warn\">CẢNH BÁO: turbo đang bật <span> kết quả tìm kiếm có thể không chính xác; xem tooltip nút turbo</span></p>",
|
||||
|
||||
"u_turbo_c": "turbo bị tắt trong cấu hình máy chủ",
|
||||
"u_turbo_g": "đang tắt turbo vì bạn không có quyền liệt kê thư mục trong phân vùng này",
|
||||
|
||||
"u_life_cfg": "tự xóa sau <input id=\"lifem\" p=\"60\" /> phút (hoặc <input id=\"lifeh\" p=\"3600\" /> giờ)",
|
||||
"u_life_est": "tệp sẽ bị xóa <span id=\"lifew\" tt=\"local time\">---</span>",
|
||||
"u_life_max": "thư mục này áp dụng\nthời gian tồn tại tối đa {0}",
|
||||
|
||||
"u_unp_ok": "cho phép unpost trong {0}",
|
||||
"u_unp_ng": "không cho phép unpost",
|
||||
|
||||
"ue_ro": "bạn chỉ có quyền đọc thư mục này\n\n",
|
||||
"ue_nl": "bạn chưa đăng nhập",
|
||||
"ue_la": "bạn đang đăng nhập với tài khoản \"{0}\"",
|
||||
"ue_sr": "bạn đang ở chế độ tìm kiếm tệp\n\nhãy chuyển sang chế độ tải lên bằng cách nhấn biểu tượng kính lúp 🔎 (bên cạnh nút SEARCH), rồi thử lại\n\nxin lỗi",
|
||||
"ue_ta": "hãy thử tải lên lại; giờ có thể được",
|
||||
"ue_ab": "tệp này đang được tải lên ở thư mục khác và cần hoàn tất trước khi có thể tải lên nơi khác.\n\nBạn có thể hủy và quên tiến trình ban đầu bằng biểu tượng ở góc trên bên trái 🧯",
|
||||
|
||||
"ur_1uo": "OK: tệp đã tải lên thành công",
|
||||
"ur_auo": "OK: toàn bộ {0} tệp đã tải lên thành công",
|
||||
"ur_1so": "OK: đã tìm thấy tệp trên máy chủ",
|
||||
"ur_aso": "OK: đã tìm thấy toàn bộ {0} tệp trên máy chủ",
|
||||
|
||||
"ur_1un": "Tải lên thất bại",
|
||||
"ur_aun": "{0} lần tải lên đều thất bại",
|
||||
|
||||
"ur_1sn": "KHÔNG tìm thấy tệp trên máy chủ",
|
||||
"ur_asn": "{0} tệp KHÔNG tìm thấy trên máy chủ",
|
||||
|
||||
"ur_um": "Hoàn tất\n{0} tải lên thành công,\n{1} tải lên thất bại",
|
||||
"ur_sm": "Hoàn tất\n{0} tệp tìm thấy trên máy chủ,\n{1} tệp KHÔNG tìm thấy",
|
||||
|
||||
"rc_opn": "mở", //m
|
||||
"rc_ply": "phát", //m
|
||||
"rc_pla": "phát dưới dạng âm thanh", //m
|
||||
"rc_txt": "mở trong trình xem tệp", //m
|
||||
"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
|
||||
"rc_pst": "dán", //m
|
||||
"rc_nfo": "thư mục mới", //m
|
||||
"rc_nfi": "tệp mới", //m
|
||||
"rc_sal": "chọn tất cả", //m
|
||||
"rc_sin": "đảo ngược lựa chọn", //m
|
||||
|
||||
"lang_set": "tải lại trang để áp dụng thay đổi ngôn ngữ",
|
||||
|
||||
"splash": {
|
||||
"a1": "tải lại",
|
||||
"b1": "xin chào khách <small>(bạn chưa đăng nhập)</small>",
|
||||
"c1": "đăng xuất",
|
||||
"d1": "ghi lại ngăn xếp",
|
||||
"d2": "hiển thị trạng thái của tất cả các luồng đang hoạt động",
|
||||
"e1": "tải lại cấu hình",
|
||||
"e2": "tải lại các tệp cấu hình (tài khoản/ổ đĩa/cờ ổ đĩa),$Nvà quét lại tất cả các ổ đĩa e2ds$N$Nchú ý: bất kỳ thay đổi nào đối với cài đặt toàn cục$Ncần khởi động lại hoàn toàn để có hiệu lực",
|
||||
"f1": "bạn có thể duyệt:",
|
||||
"g1": "bạn có thể tải lên:",
|
||||
"cc1": "thứ khác:",
|
||||
"h1": "vô hiệu hoá k304",
|
||||
"i1": "bật k304",
|
||||
"j1": "bật k304 sẽ ngắt kết nối client của bạn trên mỗi HTTP 304, tùy chọn này có thể ngăn một số proxy bị lỗi kẹt (đột ngột không tải được trang), <em>nhưng</em> nó cũng sẽ làm mọi thứ chậm hơn",
|
||||
"k1": "đặt lại cài đặt client",
|
||||
"l1": "đăng nhập để có thêm:",
|
||||
"m1": "chào mừng trở lại,",
|
||||
"n1": "404 không tìm thấy ┐( ´ -`)┌",
|
||||
"o1": 'hoặc có thể bạn không có quyền truy cập -- thử mật khẩu hoặc <a href="' + SR + '/?h">về trang chủ</a>',
|
||||
"p1": "403 bị cấm ~┻━┻",
|
||||
"q1": 'sử dụng mật khẩu hoặc <a href="' + SR + '/?h">về trang chủ</a>',
|
||||
"r1": "về trang chủ",
|
||||
".s1": "quét lại",
|
||||
"t1": "hành động",
|
||||
"u2": "thời gian kể từ lần ghi máy chủ cuối cùng$N( tải lên / đổi tên / ... )$N$N17d = 17 ngày$N1h23 = 1 giờ 23 phút$N4m56 = 4 phút 56 giây",
|
||||
"v1": "kết nối",
|
||||
"v2": "sử dụng máy chủ này như một ổ cứng cục bộ",
|
||||
"w1": "chuyển sang https",
|
||||
"x1": "đổi mật khẩu",
|
||||
"y1": "chỉnh sửa chia sẻ",
|
||||
"z1": "mở khóa chia sẻ này:",
|
||||
"ta1": "điền mật khẩu mới:",
|
||||
"ta2": "nhập lại mật khẩu mới:",
|
||||
"ta3": "mật khẩu không khớp, xin hãy thử lại",
|
||||
"nop": "LỖI: Mật khẩu không được để trống", //m
|
||||
"nou": "LỖI: Tên người dùng và/hoặc mật khẩu không được để trống", //m
|
||||
"aa1": "tệp đến",
|
||||
"ab1": "vô hiệu hóa no304",
|
||||
"ac1": "bật no304",
|
||||
"ad1": "bật no304 sẽ vô hiệu hóa tất cả bộ nhớ đệm; hãy thử tùy chọn này nếu k304 không đủ. Tùy chọn này sẽ làm lãng phí một lượng lớn lưu lượng mạng!",
|
||||
"ae1": "tải xuống đang hoạt động:",
|
||||
"af1": "hiển thị các tệp đã tải lên gần đây",
|
||||
"ag1": "hiển thị người dùng IdP đã biết", //m
|
||||
}
|
||||
};
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
"use strict";
|
||||
|
||||
var J_U2K = 1;
|
||||
|
||||
(function () {
|
||||
var x = sread('nosubtle');
|
||||
|
|
@ -900,19 +901,19 @@ function up2k_init(subtle) {
|
|||
bcfg_bind(uc, 'upnag', 'upnag', false, set_upnag);
|
||||
bcfg_bind(uc, 'upsfx', 'upsfx', false, set_upsfx);
|
||||
|
||||
uc.ow = parseInt(sread('u2ow', ['0', '1', '2']) || u2ow);
|
||||
uc.owt = ['🛡️', '🕒', '♻️'];
|
||||
uc.ow = parseInt(sread('u2ow', ['0', '1', '2', '3']) || u2ow);
|
||||
uc.owt = ['🛡️', '🕒', '♻️', '⏭️'];
|
||||
function set_ow() {
|
||||
QS('label[for="u2ow"]').innerHTML = uc.owt[uc.ow];
|
||||
ebi('u2ow').checked = true; //cosmetic
|
||||
}
|
||||
ebi('u2ow').onclick = function (e) {
|
||||
ev(e);
|
||||
if (++uc.ow > 2)
|
||||
if (++uc.ow > 3)
|
||||
uc.ow = 0;
|
||||
swrite('u2ow', uc.ow);
|
||||
set_ow();
|
||||
if (uc.ow && !has(perms, 'delete'))
|
||||
if (uc.ow && uc.ow !== 3 && !has(perms, 'delete'))
|
||||
toast.warn(10, L.u_enoow, 'noow');
|
||||
else if (toast.tag == 'noow')
|
||||
toast.hide();
|
||||
|
|
@ -2727,9 +2728,10 @@ function up2k_init(subtle) {
|
|||
var err_pend = rsp.indexOf('partial upload exists at a different') + 1,
|
||||
err_srcb = rsp.indexOf('source file busy; please try again') + 1,
|
||||
err_plug = rsp.indexOf('upload blocked by x') + 1,
|
||||
err_dupe = rsp.indexOf('upload rejected, file already exists') + 1;
|
||||
err_dupe = rsp.indexOf('upload rejected, file already exists') + 1,
|
||||
err_exists = rsp.indexOf('upload rejected, a file with that name already exists') + 1;
|
||||
|
||||
if (err_pend || err_srcb || err_plug || err_dupe) {
|
||||
if (err_pend || err_srcb || err_plug || err_dupe || err_exists) {
|
||||
err = rsp;
|
||||
ofs = err.indexOf('\n/');
|
||||
if (ofs !== -1) {
|
||||
|
|
@ -2793,6 +2795,8 @@ function up2k_init(subtle) {
|
|||
req.replace = 'mt';
|
||||
if (uc.ow == 2)
|
||||
req.replace = true;
|
||||
if (uc.ow == 3)
|
||||
req.replace = 'skip';
|
||||
}
|
||||
|
||||
xhr.open('POST', t.purl, true);
|
||||
|
|
@ -3461,3 +3465,5 @@ if (ls0)
|
|||
}
|
||||
catch (ex) { }
|
||||
})();
|
||||
|
||||
J_U2K = 2;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
"use strict";
|
||||
|
||||
var J_UTL = 1;
|
||||
|
||||
if (!window.console || !console.log)
|
||||
window.console = {
|
||||
"log": function (msg) { }
|
||||
|
|
@ -594,6 +596,19 @@ function yscroll() {
|
|||
}
|
||||
return 0;
|
||||
}
|
||||
function xscroll() {
|
||||
if (document.documentElement.scrollLeft) {
|
||||
return (window.xscroll = function () {
|
||||
return document.documentElement.scrollLeft;
|
||||
})();
|
||||
}
|
||||
if (window.pageXOffset) {
|
||||
return (window.xscroll = function () {
|
||||
return window.pageXOffset;
|
||||
})();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
function showsort(tab) {
|
||||
|
|
@ -1848,7 +1863,13 @@ var modal = (function () {
|
|||
document.removeEventListener('selectionchange', onselch);
|
||||
document.removeEventListener('focus', onfocus);
|
||||
document.removeEventListener('keydown', onkey);
|
||||
o.parentNode.removeChild(o);
|
||||
try {
|
||||
o.parentNode.removeChild(o);
|
||||
}
|
||||
catch (ex) {
|
||||
if (r.busy)
|
||||
alert('WARNING: you have a browser-extension which is causing problems');
|
||||
}
|
||||
r.busy = false;
|
||||
setTimeout(next, 50);
|
||||
};
|
||||
|
|
@ -2330,3 +2351,5 @@ function xhrchk(xhr, prefix, e404, lvl, tag) {
|
|||
|
||||
return fun(0, prefix + xhr.status + ": " + errtxt, tag);
|
||||
}
|
||||
|
||||
J_UTL = 2;
|
||||
|
|
|
|||
|
|
@ -1,3 +1,205 @@
|
|||
▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
|
||||
# 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
|
||||
|
||||
## 🧪 new features
|
||||
|
||||
* #1080 new translation: Vietnamese (thx @thatfrozenfrog and @khoidauminh!) b60eb3f0 d4a9787c
|
||||
* #1110 add option `xf-proto-fb` to support reverseproxies which do not provide an `x-forwarded-proto` header 9c64788d
|
||||
* and improve the rproxy config guidance message in the serverlog c8f3b4ef
|
||||
* and show a warning in the controlpanel if misconfiguration is detected c8f3b4ef
|
||||
* #1109 add option `--ipar`, reverseproxy-aware alternative to `--ipa` 33684219
|
||||
* its purpose is rejecting connections from unexpected/unwanted IPs/subnets
|
||||
* option `idp-chsub` can be used to replace spaces in IdP usernames/groupnames 5e1d9a58
|
||||
* #1029 indicate password max-length in ui 8d46cf18
|
||||
* thx to @grantbacon for the initial take!
|
||||
|
||||
## 🩹 bugfixes
|
||||
|
||||
* #1111 apple gave us coal for xmas this year 0b6d2d24
|
||||
* workaround for a new bug in safari (iOS and Macos) where it would randomly show a login-popup
|
||||
* #1113 the `@acct` group was unavailable in groupless IdP setups b6c2ec15
|
||||
|
||||
## 🌠 fun facts
|
||||
|
||||
* speaking of current events, @stackxp made copyparty [bad-apple-certified](https://copyparty.eu/bad-apple.mp4) a little while back :grin:
|
||||
* the plugin is [available here](https://github.com/stackxp/copyparty-badapple)
|
||||
|
||||
|
||||
|
||||
▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
|
||||
# 2025-1214-2304 `v1.19.22` merikuri
|
||||
|
||||
## 🧪 new features
|
||||
|
||||
* #1068 #1089 add [options](https://copyparty.eu/cli#g-prologues) to customize which textfiles (readme/prologue/epilogue) to embed above/below directory listings 14bef85b
|
||||
* `prologues`, `epilogues`, `readmes`, `preadmes` (global-options and/or volflags) accept a comma-separated list of filenames to look for
|
||||
* #1092 add option [th-qv](https://copyparty.eu/cli#g-th-qv) to change the thumbnail quality a1cbac02
|
||||
* also found and enabled a size-optimization for libvips, so:
|
||||
* #1092 automatically delete and rebuild thumbnails if thumbnailer-config is changed ca6c4dea
|
||||
* #1049 add option [log-date](https://copyparty.eu/cli#g-log-date) to display dates in logs 965a4a69
|
||||
* #1047 rss-feed: title/description of each entry is now a [template-string](https://copyparty.eu/cli#g-rss-fmt-t) which can reference arbitrary metadata properties (thx @djjeane!) 5e85e3d6
|
||||
* extend the ramdisk safeguard to also prevent moving files into ephemeral storage fa918228
|
||||
* would previously prevent creating new files, but this was another potential source for confusion (thx coworker!)
|
||||
* now possible to [customize](https://copyparty.eu/cli#g-banmsg) the `thank you for playing` ban-message ce2eeba2
|
||||
* #964 option to change the default value of the [`Cache-Control`](https://github.com/9001/copyparty/#other-flags) response-header 3bc0bf19
|
||||
* [don't let this be you](https://a.ocv.me/pub/g/2025/12/f7mc7gbkvkdd1.jpeg) :^)
|
||||
|
||||
## 🩹 bugfixes
|
||||
|
||||
* #1010 correctly replace illegal characters in filenames according to underlying filesystem ba017f7b
|
||||
* for example, uploading a folder named [COMPLE:X](https://www.youtube.com/watch?v=U9QKCUufpDc&list=OLAK5uy_lMgzSTDg0XcZcqYSVAlfZ4O3rlGckolW4) into an exFAT flashdrive on linux is now possible
|
||||
* and, to make that possible, filesystem-detection now sees the true filesystem behind FUSE (for example ntfs-3g) 3bbed1bc
|
||||
* audio-playback would skip into the next folder rather than play the rest of the current one if the folder was sufficiently massive 8e2fb05a
|
||||
* #1094 fix `ipu` with idp users 594ec394
|
||||
* [commandline uploader](https://github.com/9001/copyparty/tree/hovudstraum/bin#u2cpy): fix termsize detection on windows 7d526eab
|
||||
* #1104 the rss feature now complains loudly if e2d is not enabled (because that was always necessary but not obvious) 92195403
|
||||
* ui/ux:
|
||||
* #1102 the option to cosmetically hide server info did not apply for all themes e440578c
|
||||
* the metadata-property `date` (default-disabled) was renamed to `tdate` to avoid colliding with the last-modified timestamp if enabled fecc3fd5
|
||||
* docs:
|
||||
* #1070 how to use the bundled [archlinux](https://github.com/9001/copyparty/#arch-package) systemd scripts 7f82189d
|
||||
* [podman-systemd](https://github.com/9001/copyparty/tree/hovudstraum/contrib/podman-systemd): fix paths in guide (thx @emiliatheworst!) a8698392
|
||||
* [synology](https://github.com/9001/copyparty/blob/hovudstraum/docs/synology-dsm.md): better way to hide `@eaDir` 1b0eb450
|
||||
|
||||
## 🔧 other changes
|
||||
|
||||
* add a loud warning in logs if `X-Forwarded-Proto` is not added by the reverseproxy ad45de94 1b222fb5
|
||||
* almost did the same for `X-Forwarded-Host` too before realizing that's generally not a thing
|
||||
* #1038 creating a blank `chpw.json` before starting copyparty is now supported and no longer crashes on startup efc6a09d
|
||||
* #1105 better feedback in the login ui (thx @stackxp!) 08474dbe
|
||||
* [mtag/audio-key.py](https://github.com/9001/copyparty/blob/hovudstraum/bin/mtag/audio-key.py): replaced the [melodic key detector](https://github.com/9001/copyparty/tree/hovudstraum/scripts/docker#detecting-bpm-and-musical-key) since ffmpeg-8 / alpine-3.23 broke it 67ddc641
|
||||
* updated deps:
|
||||
* webdeps: dompurify-3.3.1 e0b04d9c
|
||||
* copyparty.exe: python-3.13.11 9e64fe02
|
||||
|
||||
## 🌠 fun facts
|
||||
|
||||
* 39c3 has a LOT of awesome [self-organized sessions](https://events.ccc.de/congress/2025/hub/en/event/list/so)
|
||||
* didn't have anything copyparty-related this time but CCC HYPE!
|
||||
|
||||
|
||||
|
||||
▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
|
||||
# 2025-1202-2047 `v1.19.21` tadaimback
|
||||
|
||||
## 🧪 new features
|
||||
|
||||
* [hooks](https://github.com/9001/copyparty/tree/hovudstraum/bin/hooks#readme) now behave more usefully/predictably; 889bd324
|
||||
* hooks returning `0` will run the next hook (if any), and let the initiating action proceed if no other hooks object
|
||||
* hooks returning `100` will stop processing successive hooks, but return success, letting the initiating action proceed
|
||||
* hooks returning anything else will stop processing successive hooks (like the documentation always said) and also fail the initiating action (if hook is checked)
|
||||
* zmq hooks can now respond with json, doing relocations and all that stuff
|
||||
* new mtag plugin, [geotag.py](https://github.com/9001/copyparty/blob/hovudstraum/bin/mtag/geotag.py): read image geotags with exiftool ([demo](https://a.ocv.me/pub/blog/j8/11/)) 1c15c0d5 ac085b81
|
||||
* #972 markdown-links are rewritten to open in the markdown-viewer 278a0d85
|
||||
* #794 add json beautifier / minifier
|
||||
* ...in the textfile-editor fd8c5bfc
|
||||
* ...in the textfile-viewer 89cab5b5
|
||||
* #1058 ui-option and server-config to force download instead of showing files inline a9174e5d
|
||||
* option `stats-u` to grant access to prometheus-metrics based on username, not just permissions b427d780
|
||||
|
||||
## 🩹 bugfixes
|
||||
|
||||
* #1003 u2c.py (commandline uploader) did not install correctly on archlinux and/or pypi 9385daea
|
||||
* #1035 uploader could fail to initialize if: 98701b78
|
||||
* the `mt` button (webworkers) was enabled in the settings tab
|
||||
* **and** the network was severely strained during intial page load
|
||||
* possible deadlock on shutdown if thumbnailer queue was hella busy fb9f0441
|
||||
* #971 windows: fix deadlock on startup if trying to use a nonexistant driveletter as a volume 945b2276
|
||||
* #1022 js-panic if audio playback is set to stay-in-folder a28503e8
|
||||
* links to ongoing file transfers in the controlpanel could 404 (thx @Habetdin!) 77f74ddb f4d67ff0
|
||||
* video scrubbing on iOS dba7c5d4
|
||||
* #1054 audio volume slider could skip one percent (thx @shermanhlc!) ca6d3a5c
|
||||
* detect invalid config:
|
||||
* #959 panic if `ipu` user doesn't exist 79e10786
|
||||
* panic if share config overlaps with a volume cedfc444
|
||||
* #943
|
||||
|
||||
## 🔧 other changes
|
||||
|
||||
* the "new-markdown" feature was repurposed into "new-file", accepting any file extension 7d62335c
|
||||
* #1023 the option to grant delete-access when creating a share was removed due to never having been implemented in the backend 04ac7fbd
|
||||
* #1012 rephrased the controlpanel login-text when logged in to avoid confusion 7a291403
|
||||
* add hints that the serverlog is a good place to look in some situations c424a55d
|
||||
* all thumbnail types and combinations can now be pregenerated a359b89e
|
||||
* #1030 add debug if cfssl is misbehaving ec00dc18
|
||||
* #871 `grid` volflag is applied during navigation if user has not set a preference a9378a8e
|
||||
* cosmetic:
|
||||
* show column number in markdown editor b9aacba1
|
||||
* reduced grid margins in theme2 e469bc94
|
||||
* reduced redirect delay after logging in f7e7b03f
|
||||
* controlpanel greeting in some fail-early responses acde21d4
|
||||
* update hooks to ignore the new upload-queue-empty message 3f4b79ff
|
||||
* docs:
|
||||
* #1032 fix typo in example docker idp config (thx @tuetenk0pp!) 867237d0
|
||||
* warn that using/changing `-j` is usually a bad idea cad15fbf
|
||||
* add hotlink anchors to https://copyparty.eu/cli/ 7f9c139e
|
||||
* nixos:
|
||||
* #868 option to install from git-head (thx @shelvacu!) c7345308
|
||||
* #962 support idp volumes (thx @nicomem!) 904c984b
|
||||
* #963 use configured chmod-d when creating volumes (thx @nicomem!) 3242145e
|
||||
* copyparty.exe: update to python 3.13.10, pillow 12.0 cdffde78
|
||||
|
||||
## 🌠 fun facts
|
||||
|
||||
* copyparty has been observed running [on a wristwatch](https://a.ocv.me/pub/g/nerd-stuff/cpp/servers/clockyparty.jpg) and on an [android tv-box](https://a.ocv.me/pub/g/nerd-stuff/cpp/servers/aallwinner.jpg) running in big-endian mode, so copyparty is [BE-certified](https://a.ocv.me/pub/g/nerd-stuff/cpp/servers/be-ready.png)
|
||||
* also... **it's december!** [you know what that means](https://a.ocv.me/pub/demo/music/.bonus/#af-55d4554d) :^)
|
||||
|
||||
|
||||
|
||||
▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
|
||||
# 2025-1102-0109 `v1.19.20` november
|
||||
|
||||
|
|
|
|||
8
docs/copyparty.d/more-users/david.conf
Normal file
8
docs/copyparty.d/more-users/david.conf
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
[accounts]
|
||||
david: letmein
|
||||
|
||||
[groups]
|
||||
friends: david
|
||||
|
||||
# the "friends" group was already created in ../some.conf,
|
||||
# so we are just adding david into it
|
||||
8
docs/copyparty.d/more-users/james.conf
Normal file
8
docs/copyparty.d/more-users/james.conf
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
[accounts]
|
||||
james: metooplease
|
||||
|
||||
[groups]
|
||||
friends: james
|
||||
|
||||
# the "friends" group was already created in ../some.conf,
|
||||
# so we are just adding james into it
|
||||
|
|
@ -6,7 +6,7 @@
|
|||
# first declare the accounts just once:
|
||||
[accounts]
|
||||
usr1: passw0rd
|
||||
usr2: letmein
|
||||
usr2: touhou
|
||||
|
||||
[global]
|
||||
i: 127.0.0.1 # listen on 127.0.0.1 only,
|
||||
|
|
@ -31,3 +31,21 @@
|
|||
#
|
||||
# because another.conf sets the read/write permissions before it
|
||||
# includes sibling.conf which adds the move permission
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
|
||||
# we can also create a group here;
|
||||
[groups]
|
||||
friends: usr1, usr2
|
||||
|
||||
# and a volume which the group "friends" can read/write;
|
||||
[/friends]
|
||||
/srv/pub/friends
|
||||
accs:
|
||||
rw: @friends
|
||||
|
||||
# then we include all the config-files in the folder "more-users",
|
||||
# which can define more users, and maybe even add them to the "friends" group
|
||||
# (spoiler: the users "usr1", "usr2", "david", and "james" will have access)
|
||||
|
||||
% more-users/
|
||||
|
|
|
|||
|
|
@ -155,7 +155,11 @@ there is a static salt for all passwords;
|
|||
* method `uPOST` = url-encoded post
|
||||
* `FILE` = conventional HTTP file upload entry (rfc1867 et al, filename in `Content-Disposition`)
|
||||
|
||||
authenticate using header `Cookie: cppwd=foo` or url param `&pw=foo`
|
||||
clients can authenticate in the following ways; the first of these which is not blank will be used:
|
||||
* url-param `&pw=foo` -- can be disabled with `--pw-urlp=A` (or renamed, if provided value is lowercase)
|
||||
* then, header `PW: foo` -- can be disabled with `--pw-hdr=A` (or renamed, if provided value is lowercase)
|
||||
* then, basic-auth -- can be disabled with `--no-bauth`
|
||||
* then, depending on protocol, header `Cookie: cppwd=foo` on plaintext http, or header `Cookie: cppws=foo` on https
|
||||
|
||||
## read
|
||||
|
||||
|
|
@ -226,11 +230,13 @@ authenticate using header `Cookie: cppwd=foo` or url param `&pw=foo`
|
|||
| PUT | `?ck=md5` | (binary data) | return md5 instead of sha512 |
|
||||
| PUT | `?gz` | (binary data) | compress with gzip and write into file at URL |
|
||||
| PUT | `?xz` | (binary data) | compress with xz and write into file at URL |
|
||||
| PUT | `?apnd` | (binary data) | append to existing file |
|
||||
| mPOST | | `f=FILE` | upload `FILE` into the folder at URL |
|
||||
| mPOST | `?j` | `f=FILE` | ...and reply with json |
|
||||
| mPOST | `?ck` | `f=FILE` | ...and disable checksum gen (faster) |
|
||||
| mPOST | `?ck=md5` | `f=FILE` | ...and return md5 instead of sha512 |
|
||||
| mPOST | `?replace` | `f=FILE` | ...and overwrite existing files |
|
||||
| mPOST | `?apnd` | `f=FILE` | ...and append to existing files |
|
||||
| mPOST | `?media` | `f=FILE` | ...and return medialink (not hotlink) |
|
||||
| mPOST | | `act=mkdir`, `name=foo` | create directory `foo` at URL |
|
||||
| POST | `?delete` | | delete URL recursively |
|
||||
|
|
@ -367,6 +373,7 @@ pip install jinja2 strip_hints # MANDATORY
|
|||
pip install argon2-cffi # password hashing
|
||||
pip install pyzmq # send 0mq from hooks
|
||||
pip install mutagen # audio metadata
|
||||
pip install paramiko # sftp server
|
||||
pip install pyftpdlib # ftp server
|
||||
pip install partftpy # tftp server
|
||||
pip install impacket # smb server -- disable Windows Defender if you REALLY need this on windows
|
||||
|
|
@ -377,7 +384,7 @@ pip install black==21.12b0 click==8.0.2 bandit pylint flake8 isort mypy # vscod
|
|||
```
|
||||
|
||||
* on archlinux you can do this:
|
||||
* `sudo pacman -Sy --needed python-{pip,isort,jinja,argon2-cffi,pyzmq,mutagen,pyftpdlib,pillow}`
|
||||
* `sudo pacman -Sy --needed python-{pip,isort,jinja,argon2-cffi,pyzmq,mutagen,paramiko,pyftpdlib,pillow}`
|
||||
* then, as user: `python3 -m pip install --user --break-system-packages -U strip_hints black==21.12b0 click==8.0.2`
|
||||
* for building docker images: `sudo pacman -Sy --needed qemu-user-static{,-binfmt} podman{,-docker} jq`
|
||||
|
||||
|
|
|
|||
|
|
@ -12,6 +12,8 @@
|
|||
|
||||
# p: 3939 # listen on another port
|
||||
# ipa: 10.89. # only allow connections from 10.89.*
|
||||
# ipa: 172.16.4.0/23 # ...or only 172.16.4.* and 172.16.5.*
|
||||
# ipa: lan # ...or allow LAN only; reject internet IPs
|
||||
# df: 16 # stop accepting uploads if less than 16 GB free disk space
|
||||
# ver # show copyparty version in the controlpanel
|
||||
# grid # show thumbnails/grid-view by default
|
||||
|
|
|
|||
|
|
@ -339,3 +339,6 @@ mk && t0="$(date)" && for n in {1..40}; do date -s "$(date '+ 1 day')"; systemd-
|
|||
|
||||
# number of megabytes downloaded since some date
|
||||
awk </var/log/wjaycore.out '/^..36m2025-05-20/{o=1} !o{next} !/ plain 20[06](,| \[[^,]+\],) +[0-9.]+.\[33m[KM] .* n[0-9]+$/{next} {v=$0;sub(/.* plain 20[06](,| \[[^,]+\],) +/,"",v);sub(/ .*/,"",v);u=v;sub(/.\[.*/,"",v);sub(/.*m/,"",u);$0=u} /[KMG]/{v*=1024} /[MG]/{v*=1024} /G/{v*=1024} {t+=v} END{printf "%d\n",t/(1024*1024)}'
|
||||
|
||||
# find format/% mixups
|
||||
%[sdfr].*format| % [^,]+\)|\{!?r?\}.*%[sdfr]|%[sdfr].*\{!?r?\}|runhook
|
||||
|
|
|
|||
|
|
@ -40,6 +40,7 @@ open the Package Center and install `Text Editor` (by Synology Inc.) to create a
|
|||
rss, daw, ver # some other nice-to-have features
|
||||
#dedup # you may want this, or maybe not
|
||||
hist: /cfg/hist # don't pollute the shared-folder
|
||||
unlist: ^@eaDir # hide the synology "@eaDir" folders
|
||||
name: synology # shows in the browser, can be anything
|
||||
|
||||
[accounts]
|
||||
|
|
@ -49,10 +50,6 @@ open the Package Center and install `Text Editor` (by Synology Inc.) to create a
|
|||
/w # the "/w" docker-volume (the shared-folder)
|
||||
accs:
|
||||
A: ed # give Admin to username ed
|
||||
|
||||
# hide the synology system files by creating a hidden volume
|
||||
[/@eaDir]
|
||||
/w/@eaDir
|
||||
```
|
||||
|
||||
if you ever change the copyparty config file, then [restart the container](https://ocv.me/copyparty/doc/pics/dsm71-02.png) to make the changes take effect
|
||||
|
|
|
|||
|
|
@ -147,6 +147,8 @@ symbol legend,
|
|||
| runs on Macos | █ | | █ | █ | █ | █ | █ | █ | █ | █ | █ | █ | |
|
||||
| runs on FreeBSD | █ | | █ | • | █ | █ | █ | • | █ | █ | | █ | |
|
||||
| runs on Risc-V | █ | | | █ | █ | █ | | • | | █ | | | |
|
||||
| runs on SGI IRIX | █ | | | • | | | | | | | | | |
|
||||
| runs on aarch64-BE | █ | | | • | | • | | | | • | | • | |
|
||||
| portable binary | █ | █ | █ | | | █ | █ | | | █ | | █ | █ |
|
||||
| zero setup, just go | █ | █ | █ | | | ╱ | █ | | | █ | | ╱ | █ |
|
||||
| android app | ╱ | | | █ | █ | | | | | | | | |
|
||||
|
|
@ -158,6 +160,8 @@ symbol legend,
|
|||
* runs on iOS / iPads using [a-Shell](https://holzschu.github.io/a-Shell_iOS/) (pretty good) or [iSH](https://ish.app/) (very slow) but cannot run in the background and is not able to share all of your phone storage (just a separate dedicated folder)
|
||||
* [android app](https://f-droid.org/en/packages/me.ocv.partyup/) is for uploading only
|
||||
* no iOS app but has [shortcuts](https://github.com/9001/copyparty#ios-shortcuts) for easy uploading
|
||||
* validated on aarch64-BE by [Øl Telecom](http://ol-tele.com/) during eth0:2025; [photo1](https://a.ocv.me/pub/g/nerd-stuff/cpp/servers/aallwinner.jpg?cache) and [diploma](https://a.ocv.me/pub/g/nerd-stuff/cpp/servers/be-ready.png?cache)
|
||||
* validated on [SGI IRIX](https://en.wikipedia.org/wiki/IRIX) ([an O2](https://en.wikipedia.org/wiki/SGI_O2)) by [Øl Telecom](http://ol-tele.com/) during 39c3; [photo1](https://a.ocv.me/pub/g/nerd-stuff/cpp/servers/sgi-o2.jpg?cache) and [screenshot](https://a.ocv.me/pub/g/nerd-stuff/cpp/servers/sgi-o2.png?cache)
|
||||
* `b`/hfs2 runs on linux through wine
|
||||
* `f`/rclone must be started with the command `rclone serve webdav .` or similar
|
||||
* `h`/chibisafe has undocumented windows support
|
||||
|
|
@ -242,7 +246,7 @@ symbol legend,
|
|||
| serve ftp (tcp) | █ | | | | | █ | | | | | | █ | █ |
|
||||
| serve ftps (tls) | █ | | | | | █ | | | | | | █ | |
|
||||
| serve tftp (udp) | █ | | | | | | | | | | | | |
|
||||
| serve sftp (ssh) | | | | | | █ | | | | | | █ | █ |
|
||||
| serve sftp (ssh) | █ | | | | | █ | | | | | | █ | █ |
|
||||
| serve smb/cifs | ╱ | | | | | █ | | | | | | | |
|
||||
| serve dlna | | | | | | █ | | | | | | | |
|
||||
| listen on unix-socket | █ | | | █ | █ | | █ | █ | █ | █ | █ | █ | |
|
||||
|
|
@ -640,8 +644,7 @@ symbol legend,
|
|||
* ⚠️ impractical directory URLs
|
||||
* ⚠️ AGPL licensed
|
||||
* 🔵 uploading small files is fast; `340` files per sec (copyparty does `670`/sec)
|
||||
* 🔵 ftp, ftps, webdav
|
||||
* ✅ sftp server
|
||||
* 🔵 sftp, ftp, ftps, webdav
|
||||
* ✅ settings gui
|
||||
* ✅ acme (automatic tls certs)
|
||||
* 💾 relies on caddy/certbot/acme.sh
|
||||
|
|
@ -667,7 +670,6 @@ symbol legend,
|
|||
* ⚠️ not self-contained (pulls from jsdelivr)
|
||||
* ⚠️ has an audio player, but supports less filetypes
|
||||
* ⚠️ limited support for configuring real-ip detection
|
||||
* ✅ sftp server
|
||||
* ✅ settings gui
|
||||
* ✅ good-looking gui
|
||||
* ✅ an IDE, msoffice viewer, rich host integration, much more
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
name = "copyparty"
|
||||
description = """
|
||||
Portable file server with accelerated resumable uploads, \
|
||||
deduplication, WebDAV, FTP, zeroconf, media indexer, \
|
||||
deduplication, WebDAV, SFTP, FTP, zeroconf, media indexer, \
|
||||
video thumbnails, audio transcoding, and write-only folders"""
|
||||
readme = "README.md"
|
||||
authors = [{ name = "ed", email = "copyparty@ocv.me" }]
|
||||
|
|
@ -47,6 +47,7 @@ classifiers = [
|
|||
[project.optional-dependencies]
|
||||
all = [
|
||||
"argon2-cffi",
|
||||
"paramiko",
|
||||
"partftpy>=0.4.0",
|
||||
"Pillow",
|
||||
"pyftpdlib",
|
||||
|
|
@ -56,6 +57,7 @@ all = [
|
|||
thumbnails = ["Pillow"]
|
||||
thumbnails2 = ["pyvips"]
|
||||
audiotags = ["mutagen"]
|
||||
sftp = ["paramiko"]
|
||||
ftpd = ["pyftpdlib"]
|
||||
ftps = ["pyftpdlib", "pyopenssl"]
|
||||
tftpd = ["partftpy>=0.4.0"]
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ WORKDIR /z
|
|||
ENV ver_asmcrypto=c72492f4a66e17a0e5dd8ad7874de354f3ccdaa5 \
|
||||
ver_hashwasm=4.12.0 \
|
||||
ver_marked=4.3.0 \
|
||||
ver_dompf=3.2.7 \
|
||||
ver_dompf=3.3.1 \
|
||||
ver_mde=2.18.0 \
|
||||
ver_codemirror=5.65.18 \
|
||||
ver_fontawesome=5.13.0 \
|
||||
|
|
|
|||
|
|
@ -9,7 +9,8 @@ ENV XDG_CONFIG_HOME=/cfg
|
|||
|
||||
RUN apk --no-cache add !pyc \
|
||||
tzdata wget mimalloc2 mimalloc2-insecure \
|
||||
py3-jinja2 py3-argon2-cffi py3-pyzmq py3-openssl py3-pillow \
|
||||
py3-jinja2 py3-argon2-cffi py3-pyzmq \
|
||||
py3-openssl py3-paramiko py3-pillow \
|
||||
ffmpeg
|
||||
|
||||
COPY i/dist/copyparty-sfx.py innvikler.sh ./
|
||||
|
|
|
|||
|
|
@ -12,13 +12,14 @@ COPY i/bin/mtag/audio-bpm.py /mtag/
|
|||
COPY i/bin/mtag/audio-key.py /mtag/
|
||||
RUN apk add -U !pyc \
|
||||
tzdata wget mimalloc2 mimalloc2-insecure \
|
||||
py3-jinja2 py3-argon2-cffi py3-pyzmq py3-openssl py3-pillow \
|
||||
py3-jinja2 py3-argon2-cffi py3-pyzmq \
|
||||
py3-openssl py3-paramiko py3-pillow \
|
||||
py3-pip py3-cffi \
|
||||
ffmpeg \
|
||||
py3-magic \
|
||||
vips-jxl vips-heif vips-poppler vips-magick \
|
||||
py3-numpy fftw libsndfile \
|
||||
vamp-sdk vamp-sdk-libs \
|
||||
vamp-sdk vamp-sdk-libs keyfinder-cli \
|
||||
libraw py3-numpy cython \
|
||||
&& apk add -t .bd \
|
||||
bash wget gcc g++ make cmake patchelf \
|
||||
|
|
|
|||
|
|
@ -9,7 +9,8 @@ ENV XDG_CONFIG_HOME=/cfg
|
|||
|
||||
RUN apk --no-cache add !pyc \
|
||||
tzdata wget mimalloc2 mimalloc2-insecure \
|
||||
py3-jinja2 py3-argon2-cffi py3-openssl py3-pillow py3-mutagen
|
||||
py3-jinja2 py3-argon2-cffi \
|
||||
py3-openssl py3-paramiko py3-pillow py3-mutagen
|
||||
|
||||
COPY i/dist/copyparty-sfx.py innvikler.sh ./
|
||||
ADD base ./base
|
||||
|
|
|
|||
|
|
@ -9,7 +9,8 @@ ENV XDG_CONFIG_HOME=/cfg
|
|||
|
||||
RUN apk add -U !pyc \
|
||||
tzdata wget mimalloc2 mimalloc2-insecure \
|
||||
py3-jinja2 py3-argon2-cffi py3-pyzmq py3-openssl py3-pillow \
|
||||
py3-jinja2 py3-argon2-cffi py3-pyzmq \
|
||||
py3-openssl py3-paramiko py3-pillow \
|
||||
py3-pip py3-cffi \
|
||||
ffmpeg \
|
||||
py3-magic \
|
||||
|
|
|
|||
|
|
@ -48,8 +48,12 @@ help() { exec cat <<'EOF'
|
|||
#
|
||||
# `no-tfp` saves ~10k by removing the tftp server, disabling --tftp
|
||||
#
|
||||
# `no-sfp` saves ~?k by removing the sftp server, disabling --sftp
|
||||
#
|
||||
# `no-zm` saves ~7k by removing the zeroconf mDNS server
|
||||
#
|
||||
# `no-z` saves ~7k by removing all zeroconf (mDNS, SSDP)
|
||||
#
|
||||
# `no-smb` saves ~3.5k by removing the smb / cifs server
|
||||
#
|
||||
# _____________________________________________________________________
|
||||
|
|
@ -133,10 +137,12 @@ while [ ! -z "$1" ]; do
|
|||
xz) use_xz=1 ; ;;
|
||||
gz) use_gz=1 ; ;;
|
||||
gzz) shift;use_gzz=$1;use_gz=1; ;;
|
||||
no-sfp) no_sfp=1 ; ;;
|
||||
no-ftp) no_ftp=1 ; ;;
|
||||
no-tfp) no_tfp=1 ; ;;
|
||||
no-smb) no_smb=1 ; ;;
|
||||
no-zm) no_zm=1 ; ;;
|
||||
no-z) no_zm=1;no_z=1; ;;
|
||||
no-pf) no_pf=1 ; ;;
|
||||
no-fnt) no_fnt=1 ; ;;
|
||||
no-hl) no_hl=1 ; ;;
|
||||
|
|
@ -451,6 +457,14 @@ unhelp() {
|
|||
{sub(/help=.*/,"help=argparse.SUPPRESS)")}1' copyparty/__main__.py
|
||||
}
|
||||
|
||||
unhelpg() {
|
||||
iawk '/^def/{m=0}
|
||||
/^def add_'$1'/{m=1}
|
||||
m>1{sub(/, help=".*"\)$/, ", help=argparse.SUPPRESS)")}
|
||||
m==1&&/, help="/{m++;sub(/, help=".*"\)$/, ", help=\"not available in this build\")")}
|
||||
1' copyparty/__main__.py
|
||||
}
|
||||
|
||||
[ $no_ftp ] && {
|
||||
unhelp ftp
|
||||
rm -rf copyparty/ftpd.py ftp
|
||||
|
|
@ -461,6 +475,11 @@ unhelp() {
|
|||
rm -rf copyparty/tftpd.py partftpy
|
||||
}
|
||||
|
||||
[ $no_sfp ] && {
|
||||
unhelp sftp
|
||||
rm -rf copyparty/sftpd.py
|
||||
}
|
||||
|
||||
[ $no_smb ] && {
|
||||
unhelp smb
|
||||
rm -f copyparty/smbd.py
|
||||
|
|
@ -468,8 +487,14 @@ unhelp() {
|
|||
}
|
||||
|
||||
[ $no_zm ] &&
|
||||
iawk '$1=="],"{s=0}/"mDNS debugging"/{s=1;sub(/".*/,"\"not available in this build\",\"\"");print};!s' copyparty/__main__.py &&
|
||||
unhelpg zc_mdns &&
|
||||
rm -rf copyparty/mdns.py copyparty/stolen/dnslib
|
||||
|
||||
[ $no_z ] &&
|
||||
unhelpg '(zeroconf|zc_ssdp)' &&
|
||||
rm -rf copyparty/ssdp.py copyparty/multicast.py
|
||||
|
||||
[ $no_pf ] &&
|
||||
rm -rf copyparty/web/a/partyfuse.py copyparty/web/deps/fuse.py
|
||||
|
||||
|
|
|
|||
|
|
@ -28,8 +28,8 @@ 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
|
||||
7d937df1345407398f215c0943514f9dadf2a951b2687e20c06116b2cb3e1d641289da705fcc0b3548f77d19f42f52306c27aa1fc00ed56d19bf47e839c495a5 python-3.13.10-amd64.exe
|
||||
1735728ae50e003badc5266638e41a73358f2151405e7888b6dc45697c074a60e6e58c8507b49a3f42d8f4fe4005fbc225cd766ab6582cbf85aa79bab699c08f python-3.13.11-amd64.exe
|
||||
2a0420f7faaa33d2132b82895a8282688030e939db0225ad8abb95a47bdb87b45318f10985fc3cee271a9121441c1526caa363d7f2e4a4b18b1a674068766e87 setuptools-80.9.0-py3-none-any.whl
|
||||
|
|
|
|||
|
|
@ -39,10 +39,10 @@ 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.10-amd64.exe
|
||||
python-3.13.11-amd64.exe
|
||||
setuptools-80.9.0-py3-none-any.whl
|
||||
)
|
||||
[ $w7 ] && fns+=(
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@ copyparty/res,
|
|||
copyparty/res/__init__.py,
|
||||
copyparty/res/COPYING.txt,
|
||||
copyparty/res/insecure.pem,
|
||||
copyparty/sftpd.py,
|
||||
copyparty/smbd.py,
|
||||
copyparty/ssdp.py,
|
||||
copyparty/star.py,
|
||||
|
|
@ -99,7 +100,6 @@ copyparty/web/md2.js,
|
|||
copyparty/web/mde.css,
|
||||
copyparty/web/mde.html,
|
||||
copyparty/web/mde.js,
|
||||
copyparty/web/msg.css,
|
||||
copyparty/web/msg.html,
|
||||
copyparty/web/opds.xml,
|
||||
copyparty/web/rups.css,
|
||||
|
|
@ -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,
|
||||
|
|
@ -133,6 +134,7 @@ copyparty/web/tl/spa.js,
|
|||
copyparty/web/tl/swe.js,
|
||||
copyparty/web/tl/tur.js,
|
||||
copyparty/web/tl/ukr.js,
|
||||
copyparty/web/tl/vie.js,
|
||||
copyparty/web/ui.css,
|
||||
copyparty/web/up2k.js,
|
||||
copyparty/web/util.js,
|
||||
|
|
|
|||
|
|
@ -184,7 +184,7 @@ Ls.hmn = {
|
|||
"ul_par": "parallel uploads:",
|
||||
"ut_rand": "randomize filenames",
|
||||
"ut_u2ts": "copy the last-modified timestamp$Nfrom your filesystem to the server\">📅",
|
||||
"ut_ow": "overwrite existing files on the server?$N🛡️: never (will generate a new filename instead)$N🕒: overwrite if server-file is older than yours$N♻️: always overwrite if the files are different",
|
||||
"ut_ow": "overwrite existing files on the server?$N🛡️: never (will generate a new filename instead)$N🕒: overwrite if server-file is older than yours$N♻️: always overwrite if the files are different$N⏭️: unconditionally skip all existing files",
|
||||
"ut_mt": "continue hashing other files while uploading$N$Nmaybe disable if your CPU or HDD is a bottleneck",
|
||||
"ut_ask": 'ask for confirmation before upload starts">💭',
|
||||
"ut_pot": "improve upload speed on slow devices$Nby making the UI less complex",
|
||||
|
|
@ -249,6 +249,7 @@ Ls.hmn = {
|
|||
"cl_reset": "reset",
|
||||
"cl_hpick": "tap on column headers to hide in the table below",
|
||||
"cl_hcancel": "column hiding aborted",
|
||||
"cl_rcm": "right-click menu",
|
||||
|
||||
"ct_grid": '田 the grid',
|
||||
"ct_ttips": '◔ ◡ ◔">ℹ️ tooltips',
|
||||
|
|
@ -291,6 +292,7 @@ Ls.hmn = {
|
|||
"cdt_lim": "max number of files to show in a folder",
|
||||
"cdt_ask": "when scrolling to the bottom,$Ninstead of loading more files,$Nask what to do",
|
||||
"cdt_hsort": "how many sorting rules (<code>,sorthref</code>) to include in media-URLs. Setting this to 0 will also ignore sorting-rules included in media links when clicking them",
|
||||
"cdt_ren": "enable custom right-click menu, you can still access the regular menu by pressing the shift key and right-clicking",
|
||||
|
||||
"tt_entree": "show navpane (directory tree sidebar)$NHotkey: B",
|
||||
"tt_detree": "show breadcrumbs$NHotkey: B",
|
||||
|
|
@ -361,6 +363,7 @@ Ls.hmn = {
|
|||
"mm_eunk": "Unknown Errol",
|
||||
"mm_e404": "Could not play audio; error 404: File not found.",
|
||||
"mm_e403": "Could not play audio; error 403: Access denied.\n\nTry pressing F5 to reload, maybe you got logged out",
|
||||
"mm_e415": "Could not play audio; error 415: File transcoding failed; check server logs.",
|
||||
"mm_e500": "Could not play audio; error 500: Check server logs.",
|
||||
"mm_e5xx": "Could not play audio; server error ",
|
||||
"mm_nof": "not finding any more audio files nearby",
|
||||
|
|
@ -448,9 +451,10 @@ Ls.hmn = {
|
|||
"fcc_warn": 'copied {0} items to clipboard\n\nbut: only <b>this</b> browser-tab can paste them\n(since the selection is so absolutely massive)',
|
||||
|
||||
"fp_apply": "use these names",
|
||||
"fp_skip": "skip conflicts", // TLNote: "skip existing names" (filenames taken in target folder)
|
||||
"fp_ecut": "first cut or copy some files / folders to paste / move\n\nnote: you can cut / paste across different browser tabs",
|
||||
"fp_ename": "{0} items cannot be moved here because the names are already taken. Give them new names below to continue, or blank the name to skip them:",
|
||||
"fcp_ename": "{0} items cannot be copied here because the names are already taken. Give them new names below to continue, or blank the name to skip them:",
|
||||
"fp_ename": "{0} items cannot be moved here because the names are already taken. Give them new names below to continue, or blank the name (\"skip conflicts\") to skip them:",
|
||||
"fcp_ename": "{0} items cannot be copied here because the names are already taken. Give them new names below to continue, or blank the name (\"skip conflicts\") to skip them:",
|
||||
"fp_emore": "there are still some filename collisions left to fix",
|
||||
"fp_ok": "move OK",
|
||||
"fcp_ok": "copy OK",
|
||||
|
|
@ -666,6 +670,23 @@ 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 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?",
|
||||
|
||||
"splash": {
|
||||
|
|
@ -684,6 +705,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), <em>but</em> 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 <a href="' + SR + '/?h">go home</a>',
|
||||
|
|
@ -702,11 +728,14 @@ Ls.hmn = {
|
|||
"ta1": "fill in your new password first",
|
||||
"ta2": "repeat to confirm new password:",
|
||||
"ta3": "found a typo; please try again",
|
||||
"nop": "ERROR: Password cannot be blank",
|
||||
"nou": "ERROR: Username and/or password cannot be blank",
|
||||
"aa1": "incoming files:",
|
||||
"ab1": "disable no304",
|
||||
"ac1": "enable no304",
|
||||
"ad1": "enabling no304 will disable all caching; try this if k304 wasn't enough. This will waste a huge amount of network traffic!",
|
||||
"ae1": "active downloads:",
|
||||
"af1": "show recent uploads",
|
||||
"ag1": "view idp cache", // TLNote: is a link to a page where IdP users can be managed
|
||||
}
|
||||
};
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue