diff --git a/.gitignore b/.gitignore index b7b533b4..b70c56b7 100644 --- a/.gitignore +++ b/.gitignore @@ -43,3 +43,6 @@ scripts/docker/*.err # nix build output link result + +# IDEA config +.idea/ diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index edc53c69..a067404f 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -6,13 +6,13 @@ but please: -# do not use AI / LMM when writing code +# do not use AI / LLM when writing code copyparty is 100% organic, free-range, human-written software! > โš  you are now entering a no-copilot zone -the *only* place where LMM/AI *may* be accepted is for [localization](https://github.com/9001/copyparty/tree/hovudstraum/docs/rice#translations) if you are fluent and have confirmed that the translation is accurate. +the *only* place where LLM/AI *may* be accepted is for [localization](https://github.com/9001/copyparty/tree/hovudstraum/docs/rice#translations) if you are fluent and have confirmed that the translation is accurate. sorry for the harsh tone, but this is important to me ๐Ÿ™ diff --git a/README.md b/README.md index 8f470a50..e93e4282 100644 --- a/README.md +++ b/README.md @@ -90,7 +90,9 @@ made in Norway ๐Ÿ‡ณ๐Ÿ‡ด * [upload events](#upload-events) - the older, more powerful approach ([examples](./bin/mtag/)) * [handlers](#handlers) - redefine behavior with plugins ([examples](./bin/handlers/)) * [ip auth](#ip-auth) - autologin based on IP range (CIDR) + * [restrict to ip](#restrict-to-ip) - limit a user to certain IP ranges (CIDR) * [identity providers](#identity-providers) - replace copyparty passwords with oauth and such + * [generic header auth](#generic-header-auth) - other ways to auth by header * [user-changeable passwords](#user-changeable-passwords) - if permitted, users can change their own passwords * [using the cloud as storage](#using-the-cloud-as-storage) - connecting to an aws s3 bucket and similar * [hiding from google](#hiding-from-google) - tell search engines you don't wanna be indexed @@ -110,6 +112,7 @@ made in Norway ๐Ÿ‡ณ๐Ÿ‡ด * [packages](#packages) - the party might be closer than you think * [arch package](#arch-package) - `pacman -S copyparty` (in [arch linux extra](https://archlinux.org/packages/extra/any/copyparty/)) * [fedora package](#fedora-package) - does not exist yet + * [homebrew formulae](#homebrew-formulae) - `brew install copyparty ffmpeg` * [nix package](#nix-package) - `nix profile install github:9001/copyparty` * [nixos module](#nixos-module) * [browser support](#browser-support) - TLDR: yes @@ -139,6 +142,7 @@ made in Norway ๐Ÿ‡ณ๐Ÿ‡ด * [copyparty.exe](#copypartyexe) - download [copyparty.exe](https://github.com/9001/copyparty/releases/latest/download/copyparty.exe) (win8+) or [copyparty32.exe](https://github.com/9001/copyparty/releases/latest/download/copyparty32.exe) (win7+) * [zipapp](#zipapp) - another emergency alternative, [copyparty.pyz](https://github.com/9001/copyparty/releases/latest/download/copyparty.pyz) * [install on android](#install-on-android) +* [install on iOS](#install-on-iOS) * [reporting bugs](#reporting-bugs) - ideas for context to include, and where to submit them * [devnotes](#devnotes) - for build instructions etc, see [./docs/devnotes.md](./docs/devnotes.md) @@ -153,6 +157,7 @@ just run **[copyparty-sfx.py](https://github.com/9001/copyparty/releases/latest/ * or if you cannot install python, you can use [copyparty.exe](#copypartyexe) instead * or install [on arch](#arch-package) โ•ฑ [on NixOS](#nixos-module) โ•ฑ [through nix](#nix-package) * or if you are on android, [install copyparty in termux](#install-on-android) +* or maybe an iPhone or iPad? [install in a-Shell on iOS](#install-on-iOS) * or maybe you have a [synology nas / dsm](./docs/synology-dsm.md) * or if you have [uv](https://docs.astral.sh/uv/) installed, run `uv tool run copyparty` * or if your computer is messed up and nothing else works, [try the pyz](#zipapp) @@ -239,7 +244,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, FreeBSD, arm32/arm64, ppc64le, s390x, risc-v/riscv64) + * โ˜‘ cross-platform (Windows, Linux, Macos, Android, iOS, FreeBSD, arm32/arm64, ppc64le, s390x, risc-v/riscv64) * upload * โ˜‘ basic: plain multipart, ie6 support * โ˜‘ [up2k](#uploading): js, resumable, multithreaded @@ -262,10 +267,11 @@ also see [comparison to similar software](./docs/versus.md) * โ˜‘ play video files as audio (converted on server) * โ˜‘ create and play [m3u8 playlists](#playlists) * โ˜‘ image gallery with webm player - * โ˜‘ [textfile browser](#textfile-viewer) with syntax hilighting + * โ˜‘ [textfile browser](#textfile-viewer) with syntax highlighting * โ˜‘ realtime streaming of growing files (logfiles and such) * โ˜‘ [thumbnails](#thumbnails) * โ˜‘ ...of images using Pillow, pyvips, or FFmpeg + * โ˜‘ ...of RAW images using rawpy * โ˜‘ ...of videos using FFmpeg * โ˜‘ ...of audio (spectrograms) using FFmpeg * โ˜‘ cache eviction (max-age; maybe max-size eventually) @@ -512,6 +518,8 @@ examples: * replacing the `g` permission with `wg` would let anonymous users upload files, but not see the required filekey to access it * replacing the `g` permission with `wG` would let anonymous users upload files, receiving a working direct link in return +if you want to grant access to all users who are logged in, the group `acct` will always contain all known users, so for example `-v /mnt/music:music:r,@acct` + anyone trying to bruteforce a password gets banned according to `--ban-pw`; default is 24h ban for 9 failed attempts in 1 hour and if you want to use config files instead of commandline args (good!) then here's the same examples as a configfile; save it as `foobar.conf` and use it like this: `python copyparty-sfx.py -c foobar.conf` @@ -537,6 +545,7 @@ and if you want to use config files instead of commandline args (good!) then her accs: r: u1, u2 # only these accounts can read, r: @g1 # (exactly the same, just with a group instead) + r: @acct # (alternatively, ALL users who are logged in) rw: u3 # and only u3 can read-write [/inc] @@ -563,6 +572,8 @@ for example `-v /mnt::r -v /var/empty:web/certs:r` mounts the server folder `/mn the example config file right above this section may explain this better; the first volume `/` is mapped to `/srv` which means http://127.0.0.1:3923/music would try to read `/srv/music` on the server filesystem, but since there's another volume at `/music` mapped to `/mnt/music` then it'll go to `/mnt/music` instead +> โ„น๏ธ this also works for single files, because files can also be volumes + ## dotfiles @@ -825,7 +836,7 @@ the up2k UI is the epitome of polished intuitive experiences: * `[๐Ÿ”Ž]` switch between upload and [file-search](#file-search) mode * ignore `[๐Ÿ”Ž]` if you add files by dragging them into the browser -and then theres the tabs below it, +and then there's the tabs below it, * `[ok]` is the files which completed successfully * `[ng]` is the ones that failed / got rejected (already exists, ...) * `[done]` shows a combined list of `[ok]` and `[ng]`, chronological order @@ -1057,7 +1068,7 @@ plays almost every audio format there is (if the server has FFmpeg installed fo the following audio formats are usually always playable, even without FFmpeg: `aac|flac|m4a|mp3|ogg|opus|wav` -some hilights: +some highlights: * OS integration; control playback from your phone's lockscreen ([windows](https://user-images.githubusercontent.com/241032/233213022-298a98ba-721a-4cf1-a3d4-f62634bc53d5.png) // [iOS](https://user-images.githubusercontent.com/241032/142711926-0700be6c-3e31-47b3-9928-53722221f722.png) // [android](https://user-images.githubusercontent.com/241032/233212311-a7368590-08c7-4f9f-a1af-48ccf3f36fad.png)) * shows the audio waveform in the seekbar * not perfectly gapless but can get really close (see settings + eq below); good enough to enjoy gapless albums as intended @@ -1288,6 +1299,12 @@ print a qr-code [(screenshot)](https://user-images.githubusercontent.com/241032/ * `--qrl lootbox/?pw=hunter2` appends to the url, linking to the `lootbox` folder with password `hunter2` * `--qrz 1` forces 1x zoom instead of autoscaling to fit the terminal size * 1x may render incorrectly on some terminals/fonts, but 2x should always work +* `--qr-pin 1` makes the qr-code stick to the bottom of the console (never scrolls away) +* `--qr-file qr.txt:1:2` writes a small qr-code to `qr.txt` +* `--qr-file qr.txt:2:2` writes a big qr-code to `qr.txt` +* `--qr-file qr.svg:1:2` writes a vector-graphics qr-code to `qr.svg` +* `--qr-file qr.png:8:4:333333:ffcc55` writes an 8x-magnified yellow-on-gray `qr.png` +* `--qr-file qr.png:8:4::ffffff` writes an 8x-magnified white-on-transparent `qr.png` it uses the server hostname if [mdns](#mdns) is enabled, otherwise it'll use your external ip (default route) unless `--qri` specifies a specific ip-prefix or domain @@ -1311,6 +1328,16 @@ some recommended FTP / FTPS clients; `wark` = example password: * https://rclone.org/ does FTPS with `tls=false explicit_tls=true` * `lftp -u k,wark -p 3921 127.0.0.1 -e ls` * `lftp -u k,wark -p 3990 127.0.0.1 -e 'set ssl:verify-certificate no; ls'` +* `curl ftp://127.0.0.1:3921/` (plaintext ftp) +* `curl --ssl-reqd ftp://127.0.0.1:3990/` (encrypted ftps) + +config file example, which restricts FTP to only use ports 3921 and 12000-12099 so all of those ports must be opened in your firewall: + +```yaml +[global] + ftp: 3921 + ftp-pr: 12000-12099 +``` ## webdav server @@ -1406,6 +1433,7 @@ and some minor issues, * win10 onwards does not allow connecting anonymously / without accounts * python3 only * slow (the builtin webdav support in windows is 5x faster, and rclone-webdav is 30x faster) + * those numbers are specifically for copyparty's smb-server (because it sucks); other smb-servers should be similar to webdav known client bugs: * on win7 only, `--smb1` is much faster than smb2 (default) because it keeps rescanning folders on smb2 @@ -1892,6 +1920,20 @@ repeat the option to map additional subnets **be careful with this one!** if you have a reverseproxy, then you definitely want to make sure you have [real-ip](#real-ip) configured correctly, and it's probably a good idea to nullmap the reverseproxy's IP just in case; so if your reverseproxy is sending requests from `172.24.27.9` then that would be `--ipu=172.24.27.9/32=` +### restrict to ip + +limit a user to certain IP ranges (CIDR) , using the global-option `--ipr` + +for example, if the user `spartacus` should get rejected if they're not connecting from an IP that starts with `192.168.123` or `172.16`, then you can either specify `--ipr=192.168.123.0/24,172.16.0.0/16=spartacus` as a commandline option, or put this in a config file: + +```yaml +[global] + ipr: 192.168.123.0/24,172.16.0.0/16=spartacus +``` + +repeat the option to map additional users + + ## identity providers replace copyparty passwords with oauth and such @@ -1900,6 +1942,10 @@ you can disable the built-in password-based login system, and instead replace it * the regular config-defined users will be used as a fallback for requests which don't include a valid (trusted) IdP username header + * `--auth-ord` configured auth precedence, for example to allow overriding the IdP with a copyparty password + +* the login/logout links/buttons can be replaced with links to your IdP with `--idp-login` and `--idp-logout` , for example `--idp-login /idp/login/?redir={dst}` will expand `{dst}` to the page the user was on when clicking Login + * if your IdP-server is slow, consider `--idp-cookie` and let requests with the cookie `cppws` bypass the IdP; experimental sessions-based feature added for a party some popular identity providers are [Authelia](https://www.authelia.com/) (config-file based) and [authentik](https://goauthentik.io/) (GUI-based, more complex) @@ -1911,6 +1957,20 @@ a more complete example of the copyparty configuration options [look like this]( but if you just want to let users change their own passwords, then you probably want [user-changeable passwords](#user-changeable-passwords) instead +### generic header auth + +other ways to auth by header + +if you have a middleware which adds a header with a user identifier, for example tailscale's `Tailscale-User-Login: alice.m@forest.net` then you can automatically auth as `alice` by defining that mapping with `--idp-hm-usr '^Tailscale-User-Login^alice.m@forest.net^alice'` or the following config file: + +```yaml +[global] + idp-hm-usr: ^Tailscale-User-Login^alice.m@forest.net^alice +``` + +repeat the whole `idp-hm-usr` option to add more mappings + + ## user-changeable passwords if permitted, users can change their own passwords in the control-panel @@ -2126,7 +2186,7 @@ when connecting the reverse-proxy to `127.0.0.1` instead (the basic and/or old-f in summary, `haproxy > caddy > traefik > nginx > apache > lighttpd`, and use uds when possible (traefik does not support it yet) -* if these results are bullshit because my config exampels are bad, please submit corrections! +* if these results are bullshit because my config examples are bad, please submit corrections! ## permanent cloudflare tunnel @@ -2262,6 +2322,7 @@ buggy feature? rip it out by setting any of the following environment variables | `PRTY_NO_SQLITE` | disable all database-related functionality (file indexing, metadata indexing, most file deduplication logic) | | `PRTY_NO_TLS` | disable native HTTPS support; if you still want to accept HTTPS connections then TLS must now be terminated by a reverse-proxy | | `PRTY_NO_TPOKE` | disable systemd-tmpfilesd avoider | +| `PRTY_UNSAFE_STATE` | allow storing secrets into emergency-fallback locations | example: `PRTY_NO_IFADDR=1 python3 copyparty-sfx.py` @@ -2297,6 +2358,15 @@ after installing, start either the system service or the user service and naviga does not exist yet; there are rumours that it is being packaged! keep an eye on this space... +## homebrew formulae + +`brew install copyparty ffmpeg` -- https://formulae.brew.sh/formula/copyparty + +should work on all macs (both intel and apple silicon) and all relevant macos versions + +the homebrew package is maintained by the homebrew team (thanks!) + + ## nix package `nix profile install github:9001/copyparty` @@ -2310,7 +2380,7 @@ some recommended dependencies are enabled by default; [override the package](htt ## nixos module -for this setup, you will need a [flake-enabled](https://nixos.wiki/wiki/Flakes) installation of NixOS. +for [flake-enabled](https://nixos.wiki/wiki/Flakes) installations of NixOS: ```nix { @@ -2337,6 +2407,33 @@ for this setup, you will need a [flake-enabled](https://nixos.wiki/wiki/Flakes) } ``` +if you don't use a flake in your configuration, you can use other dependency management tools like [npins](https://github.com/andir/npins), [niv](https://github.com/nmattia/niv), or even plain [`fetchTarball`](https://nix.dev/manual/nix/stable/language/builtins#builtins-fetchTarball), like so: + +```nix +{ pkgs, ... }: + +let + # npins example, adjust for your setup. copyparty should be a path to the downloaded repo + # for niv, just replace the npins folder import with the sources.nix file + copyparty = (import ./npins).copyparty; + + # or with fetchTarball: + copyparty = fetchTarball "https://github.com/9001/copyparty/archive/hovudstraum.tar.gz"; +in + +{ + # load the copyparty NixOS module + imports = [ "${copyparty}/contrib/nixos/modules/copyparty.nix" ]; + + # add the copyparty overlay to expose the package to the module + nixpkgs.overlays = [ (import "${copyparty}/contrib/package/nix/overlay.nix") ]; + # (optional) install the package globally + environment.systemPackages = [ pkgs.copyparty ]; + # configure the copyparty module + services.copyparty.enable = true; +} +``` + copyparty on NixOS is configured via `services.copyparty` options, for example: ```nix services.copyparty = { @@ -2523,11 +2620,20 @@ sync folders to/from copyparty NOTE: full bidirectional sync, like what [nextcloud](https://docs.nextcloud.com/server/latest/user_manual/sv/files/desktop_mobile_sync.html) and [syncthing](https://syncthing.net/) does, will never be supported! Only single-direction sync (server-to-client, or client-to-server) is possible with copyparty +* if you want bidirectional sync, then copyparty and syncthing *should* be entirely safe to combine; they should be able to collaborate on the same folders without causing any trouble for eachother. Many people do this, and there have been no issues so far. But, if you *do* encounter any problems, please [file a copyparty bug](https://github.com/9001/copyparty/issues/new/choose) and I'll try to help -- just keep in mind I've never used syncthing before :-) + the commandline uploader [u2c.py](https://github.com/9001/copyparty/tree/hovudstraum/bin#u2cpy) with `--dr` is the best way to sync a folder to copyparty; verifies checksums and does files in parallel, and deletes unexpected files on the server after upload has finished which makes file-renames really cheap (it'll rename serverside and skip uploading) +if you want to sync with `u2c.py` then: +* the `e2dsa` option (either globally or volflag) must be enabled on the server for the volumes you're syncing into +* ...but DON'T enable global-options `no-hash` or `no-idx` (or volflags `nohash` / `noidx`), or at least make sure they are configured so they do not affect anything you are syncing into +* ...and u2c needs the delete-permission, so either `rwd` at minimum, or just `A` which is the same as `rwmd.a` + * quick reminder that `a` and `A` are different permissions, and `.` is very useful for sync + alternatively there is [rclone](./docs/rclone.md) which allows for bidirectional sync and is *way* more flexible (stream files straight from sftp/s3/gcs to copyparty, ...), although there is no integrity check and it won't work with files over 100 MiB if copyparty is behind cloudflare * starting from rclone v1.63, rclone is faster than u2c.py on low-latency connections + * but this is only true for the initial upload; u2c will be faster for periodic syncing ## mount as drive @@ -2569,6 +2675,8 @@ there is no iPhone app, but the following shortcuts are almost as good: * can download links and rehost the target file on copyparty (see first comment inside the shortcut) * pics become lowres if you share from gallery to shortcut, so better to launch the shortcut and pick stuff from there +if you want to run the copyparty server on your iPhone or iPad, see [install on iOS](#install-on-iOS) + # performance @@ -2792,9 +2900,10 @@ enable [music tags](#metadata-from-audio-files): enable [thumbnails](#thumbnails) of... * **images:** `Pillow` and/or `pyvips` and/or `ffmpeg` (requires py2.7 or py3.5+) * **videos/audio:** `ffmpeg` and `ffprobe` somewhere in `$PATH` -* **HEIF pictures:** `pyvips` or `ffmpeg` or `pyheif-pillow-opener` (requires Linux or a C compiler) +* **HEIF pictures:** `pyvips` or `ffmpeg` or `pillow-heif` * **AVIF pictures:** `pyvips` or `ffmpeg` or `pillow-avif-plugin` or pillow v11.3+ * **JPEG XL pictures:** `pyvips` or `ffmpeg` +* **RAW images:** `rawpy`, plus one of `pyvips` or `Pillow` (for some formats) enable sending [zeromq messages](#zeromq) from event-hooks: `pyzmq` @@ -2825,9 +2934,10 @@ set any of the following environment variables to disable its associated optiona | `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/pyheif-pillow-opener/) | +| `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_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 | example: `PRTY_NO_PIL=1 python3 copyparty-sfx.py` @@ -2900,6 +3010,27 @@ if you want thumbnails (photos+videos) and you're okay with spending another 132 * or if you want to use `vips` for photo-thumbs instead, `pkg install libvips && python -m pip install --user -U wheel && python -m pip install --user -U pyvips && (cd /data/data/com.termux/files/usr/lib/; ln -s libgobject-2.0.so{,.0}; ln -s libvips.so{,.42})` +# install on iOS + +first install one of the following: +* [a-Shell mini](https://apps.apple.com/us/app/a-shell-mini/id1543537943) gives you the essential features +* [a-Shell](https://apps.apple.com/us/app/a-shell/id1473805438) also enables audio transcoding and better thubmnails + +and then copypaste the following command into `a-Shell`: + +```sh +curl https://github.com/9001/copyparty/raw/refs/heads/hovudstraum/contrib/setup-ashell.sh | sh +``` + +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 + +known issues: +* cannot run in the background; it needs to be on-screen to accept connections / uploads / downloads +* the best way to exit copyparty is to swipe away the app + + # reporting bugs ideas for context to include, and where to submit them diff --git a/bin/README.md b/bin/README.md index fd94172f..1fef13f4 100644 --- a/bin/README.md +++ b/bin/README.md @@ -1,7 +1,7 @@ # [`u2c.py`](u2c.py) * command-line up2k client [(webm)](https://ocv.me/stuff/u2cli.webm) * file uploads, file-search, autoresume of aborted/broken uploads -* sync local folder to server +* [sync local folder to server](https://github.com/9001/copyparty/#folder-sync) * generally faster than browsers * if something breaks just restart it diff --git a/bin/bubbleparty.sh b/bin/bubbleparty.sh index 2e6aab3e..564546ee 100755 --- a/bin/bubbleparty.sh +++ b/bin/bubbleparty.sh @@ -1,4 +1,4 @@ -#!/bin/sh +#!/bin/bash # usage: ./bubbleparty.sh ./copyparty-sfx.py .... bwrap \ --unshare-all \ @@ -9,7 +9,7 @@ bwrap \ --dev-bind /dev /dev \ --dir /tmp \ --dir /var \ - --bind $(pwd) $(pwd) \ + --bind "$(pwd)" "$(pwd)" \ --share-net \ --die-with-parent \ --file 11 /etc/passwd \ diff --git a/bin/dbtool.py b/bin/dbtool.py index ff92fca3..12e3d535 100755 --- a/bin/dbtool.py +++ b/bin/dbtool.py @@ -8,7 +8,7 @@ import sqlite3 import argparse DB_VER1 = 3 -DB_VER2 = 5 +DB_VER2 = 6 BY_PATH = None NC = None @@ -39,7 +39,7 @@ def ls(db): print(f"{nfiles} files") print(f"{ntags} tags\n") - print("number of occurences for each tag,") + print("number of occurrences for each tag,") print(" 'x' = file has no tags") print(" 't:mtp' = the mtp flag (file not mtp processed yet)") print() diff --git a/bin/handlers/redirect.py b/bin/handlers/redirect.py index 07b91d0c..fde29454 100644 --- a/bin/handlers/redirect.py +++ b/bin/handlers/redirect.py @@ -46,7 +46,7 @@ def main(cli, vn, rem): # uncomment one of these: send_http_302_temporary_redirect(cli, new_path) - #send_http_301_permanent_redirect(cli, new_path) - #send_errorpage_with_redirect_link(cli, new_path) + # send_http_301_permanent_redirect(cli, new_path) + # send_errorpage_with_redirect_link(cli, new_path) return "true" diff --git a/bin/hooks/notify.py b/bin/hooks/notify.py index 6e5dda86..bc12269d 100755 --- a/bin/hooks/notify.py +++ b/bin/hooks/notify.py @@ -9,7 +9,7 @@ from plyer import notification _ = r""" show os notification on upload; works on windows, linux, macos, android -depdencies: +dependencies: windows: python3 -m pip install --user -U plyer linux: python3 -m pip install --user -U plyer macos: python3 -m pip install --user -U plyer pyobjus diff --git a/bin/hooks/wget.py b/bin/hooks/wget.py index ad0c71c0..1f5a824b 100755 --- a/bin/hooks/wget.py +++ b/bin/hooks/wget.py @@ -66,7 +66,7 @@ def main(): try: sp.check_call(cmd) except: - t = "-- FAILED TO DONWLOAD " + name + t = "-- FAILED TO DOWNLOAD " + name print(f"{t}\n", end="") open(t, "wb").close() diff --git a/bin/mtag/guestbook-read.py b/bin/mtag/guestbook-read.py index 704addbe..e9119903 100755 --- a/bin/mtag/guestbook-read.py +++ b/bin/mtag/guestbook-read.py @@ -7,7 +7,7 @@ example copyparty config to use this: --urlform save,get -vsrv/hello:hello:w:c,e2ts,mtp=guestbook=t10,ad,p,bin/mtag/guestbook-read.py:mte=+guestbook explained: - for realpath srv/hello (served at /hello), write-only for eveyrone, + for realpath srv/hello (served at /hello), write-only for everyone, enable file analysis on upload (e2ts), use mtp plugin "bin/mtag/guestbook-read.py" to provide metadata tag "guestbook", do this on all uploads regardless of extension, diff --git a/bin/mtag/guestbook.py b/bin/mtag/guestbook.py index 437289b6..84f8fa53 100644 --- a/bin/mtag/guestbook.py +++ b/bin/mtag/guestbook.py @@ -11,7 +11,7 @@ example copyparty config to use this: --urlform save,get -vsrv/hello:hello:w:c,e2ts,mtp=xgb=ebin,t10,ad,p,bin/mtag/guestbook.py:mte=+xgb explained: - for realpath srv/hello (served at /hello),write-only for eveyrone, + for realpath srv/hello (served at /hello),write-only for everyone, enable file analysis on upload (e2ts), use mtp plugin "bin/mtag/guestbook.py" to provide metadata tag "xgb", do this on all uploads with the file extension "bin", diff --git a/bin/mtag/wget.py b/bin/mtag/wget.py index 26a1fa45..d706052b 100644 --- a/bin/mtag/wget.py +++ b/bin/mtag/wget.py @@ -84,7 +84,7 @@ def main(): # on success, delete the .bin file which contains the URL os.unlink(fp) except: - open("-- FAILED TO DONWLOAD " + name, "wb").close() + open("-- FAILED TO DOWNLOAD " + name, "wb").close() os.unlink(tfn) print(url) diff --git a/bin/partyfuse.py b/bin/partyfuse.py index da7ce54e..0d88caca 100755 --- a/bin/partyfuse.py +++ b/bin/partyfuse.py @@ -6,8 +6,8 @@ __copyright__ = 2019 __license__ = "MIT" __url__ = "https://github.com/9001/copyparty/" -S_VERSION = "2.0" -S_BUILD_DT = "2024-10-01" +S_VERSION = "2.1" +S_BUILD_DT = "2025-09-06" """ mount a copyparty server (local or remote) as a filesystem @@ -99,7 +99,7 @@ except: elif MACOS: libfuse = "install https://osxfuse.github.io/" else: - libfuse = "apt install libfuse3-3\n modprobe fuse" + libfuse = "apt install libfuse2\n modprobe fuse" m = """\033[33m could not import fuse; these may help: @@ -359,7 +359,7 @@ class Gateway(object): def sendreq(self, meth, path, headers, **kwargs): tid = get_tid() if self.password: - headers["Cookie"] = "=".join(["cppwd", self.password]) + headers["PW"] = self.password try: c = self.getconn(tid) @@ -902,9 +902,7 @@ class CPPF(Operations): return ret def _readdir(self, path, fh=None): - path = path.strip("/") - dbg("readdir %r [%s]", path, fh) - + dbg("dircache miss") ret = self.gw.listdir(path) if not self.n_dircache: return ret @@ -914,11 +912,17 @@ class CPPF(Operations): self.dircache.append(cn) self.clean_dircache() - # import pprint; pprint.pprint(ret) return ret def readdir(self, path, fh=None): - return [".", ".."] + list(self._readdir(path, fh)) + dbg("readdir %r [%s]", path, fh) + path = path.strip("/") + cn = self.get_cached_dir(path) + if cn: + ret = cn.data + else: + ret = self._readdir(path, fh) + return [".", ".."] + list(ret) def read(self, path, length, offset, fh=None): req_max = 1024 * 1024 * 8 @@ -993,7 +997,6 @@ class CPPF(Operations): if cn: dents = cn.data else: - dbg("cache miss") dents = self._readdir(dirpath) try: @@ -1141,10 +1144,15 @@ def main(): if WINDOWS: examples.append("http://192.168.1.69:3923/music/ M:") + epi = "example:" + ex_pre + ex_pre.join(examples) + epi += """\n +NOTE: if server has --usernames enabled, then password is "username:password" +""" + ap = argparse.ArgumentParser( formatter_class=TheArgparseFormatter, description="mount a copyparty server as a local filesystem -- " + ver, - epilog="example:" + ex_pre + ex_pre.join(examples), + epilog=epi, ) # fmt: off ap.add_argument("base_url", type=str, help="remote copyparty URL to mount") diff --git a/bin/prisonparty.sh b/bin/prisonparty.sh index d495c833..403f1588 100755 --- a/bin/prisonparty.sh +++ b/bin/prisonparty.sh @@ -141,7 +141,7 @@ chmod 777 "$jail/tmp" # create a dev -(cd $jail; mkdir -p dev; cd dev +(cd "$jail"; mkdir -p dev; cd dev [ -e null ] || mknod -m 666 null c 1 3 [ -e zero ] || mknod -m 666 zero c 1 5 [ -e random ] || mknod -m 444 random c 1 8 diff --git a/bin/u2c.py b/bin/u2c.py index e92c1e4e..aec03918 100755 --- a/bin/u2c.py +++ b/bin/u2c.py @@ -1,8 +1,8 @@ #!/usr/bin/env python3 from __future__ import print_function, unicode_literals -S_VERSION = "2.11" -S_BUILD_DT = "2025-05-18" +S_VERSION = "2.13" +S_BUILD_DT = "2025-09-05" """ u2c.py: upload to copyparty @@ -10,7 +10,7 @@ u2c.py: upload to copyparty https://github.com/9001/copyparty/blob/hovudstraum/bin/u2c.py - dependencies: no -- supports python 2.6, 2.7, and 3.3 through 3.12 +- supports python 2.6, 2.7, and 3.3 through 3.14 - if something breaks just try again and it'll autoresume """ @@ -590,9 +590,10 @@ def undns(url): def _scd(err, top): """non-recursive listing of directory contents, along with stat() info""" + top_ = os.path.join(top, b"") with os.scandir(top) as dh: for fh in dh: - abspath = os.path.join(top, fh.name) + abspath = top_ + fh.name try: yield [abspath, fh.stat()] except Exception as ex: @@ -601,8 +602,9 @@ def _scd(err, top): def _lsd(err, top): """non-recursive listing of directory contents, along with stat() info""" + top_ = os.path.join(top, b"") for name in os.listdir(top): - abspath = os.path.join(top, name) + abspath = top_ + name try: yield [abspath, os.stat(abspath)] except Exception as ex: @@ -677,7 +679,7 @@ def walkdirs(err, tops, excl): yield stop, ap[len(stop) :].lstrip(sep), inf else: d, n = top.rsplit(sep, 1) - yield d, n, os.stat(top) + yield d or b"/", n, os.stat(top) # mostly from copyparty/util.py @@ -1527,10 +1529,10 @@ def main(): # fmt: off ap = app = argparse.ArgumentParser(formatter_class=APF, description="copyparty up2k uploader / filesearch tool " + ver, epilog=""" -NOTE: -source file/folder selection uses rsync syntax, meaning that: +NOTE: source file/folder selection uses rsync syntax, meaning that: "foo" uploads the entire folder to URL/foo/ "foo/" uploads the CONTENTS of the folder into URL/ +NOTE: if server has --usernames enabled, then password is "username:password" """) ap.add_argument("url", type=unicode, help="server url, including destination folder") diff --git a/contrib/nginx/copyparty.conf b/contrib/nginx/copyparty.conf index 121e52ab..f0d382cc 100644 --- a/contrib/nginx/copyparty.conf +++ b/contrib/nginx/copyparty.conf @@ -31,7 +31,7 @@ # generate the list of permitted IP ranges like so: # (curl -s https://www.cloudflare.com/ips-v{4,6} | sed 's/^/allow /; s/$/;/'; echo; echo "deny all;") > /etc/nginx/cloudflare-only.conf # -# and then enable it below by uncomenting the cloudflare-only.conf line +# and then enable it below by uncommenting the cloudflare-only.conf line # # ====================================================================== diff --git a/contrib/nixos/modules/copyparty.nix b/contrib/nixos/modules/copyparty.nix index 994b2575..1f8242df 100644 --- a/contrib/nixos/modules/copyparty.nix +++ b/contrib/nixos/modules/copyparty.nix @@ -50,6 +50,7 @@ let configStr = '' ${mkSection "global" cfg.settings} + ${cfg.globalExtraConfig} ${mkSection "accounts" (accountsWithPlaceholders cfg.accounts)} ${concatStringsSep "\n" (mapAttrsToList mkVolume cfg.volumes)} ''; @@ -131,6 +132,12 @@ in ''; }; + globalExtraConfig = mkOption { + type = types.str; + default = ""; + description = "Appended to the end of the [global] section verbatim. This is useful for flags which are used in a repeating manner (e.g. ipu: 255.255.255.1=user) which can't be repeated in the settings = {} attribute set."; + }; + accounts = mkOption { type = types.attrsOf ( types.submodule ( @@ -373,3 +380,4 @@ in } ); } + diff --git a/contrib/package/arch/PKGBUILD b/contrib/package/arch/PKGBUILD index e5896fd7..a83a6698 100644 --- a/contrib/package/arch/PKGBUILD +++ b/contrib/package/arch/PKGBUILD @@ -3,7 +3,7 @@ # NOTE: You generally shouldn't use this PKGBUILD on Arch, as it is mainly for testing purposes. Install copyparty using pacman instead. pkgname=copyparty -pkgver="1.19.0" +pkgver="1.19.8" pkgrel=1 pkgdesc="File server with accelerated resumable uploads, dedup, WebDAV, FTP, TFTP, zeroconf, media indexer, thumbnails++" arch=("any") @@ -23,7 +23,7 @@ optdepends=("ffmpeg: thumbnails for videos, images (slower) and audio, music tag ) source=("https://github.com/9001/${pkgname}/releases/download/v${pkgver}/${pkgname}-${pkgver}.tar.gz") backup=("etc/${pkgname}/copyparty.conf" ) -sha256sums=("179b027d51e4fe7ebdab2b18c07475d52c57e2ce69256292b157a8efacd82118") +sha256sums=("3143ba5216c8d4cf1fbc58fa08c6ecef955de04b5e34b3910ab0b71cffec88ef") build() { cd "${srcdir}/${pkgname}-${pkgver}/copyparty/web" diff --git a/contrib/package/makedeb-mpr/PKGBUILD b/contrib/package/makedeb-mpr/PKGBUILD index 645623c0..d1512976 100644 --- a/contrib/package/makedeb-mpr/PKGBUILD +++ b/contrib/package/makedeb-mpr/PKGBUILD @@ -2,7 +2,7 @@ pkgname=copyparty -pkgver=1.19.0 +pkgver=1.19.8 pkgrel=1 pkgdesc="File server with accelerated resumable uploads, dedup, WebDAV, FTP, TFTP, zeroconf, media indexer, thumbnails++" arch=("any") @@ -20,7 +20,7 @@ optdepends=("ffmpeg: thumbnails for videos, images (slower) and audio, music tag ) source=("https://github.com/9001/${pkgname}/releases/download/v${pkgver}/${pkgname}-${pkgver}.tar.gz") backup=("/etc/${pkgname}.d/init" ) -sha256sums=("179b027d51e4fe7ebdab2b18c07475d52c57e2ce69256292b157a8efacd82118") +sha256sums=("3143ba5216c8d4cf1fbc58fa08c6ecef955de04b5e34b3910ab0b71cffec88ef") build() { cd "${srcdir}/${pkgname}-${pkgver}/copyparty/web" diff --git a/contrib/package/nix/copyparty/default.nix b/contrib/package/nix/copyparty/default.nix index 28d733ab..22ae5bcd 100644 --- a/contrib/package/nix/copyparty/default.nix +++ b/contrib/package/nix/copyparty/default.nix @@ -1,10 +1,10 @@ { lib, - stdenv, - makeWrapper, + buildPythonApplication, fetchurl, util-linux, python, + setuptools, jinja2, impacket, pyopenssl, @@ -15,6 +15,10 @@ pyzmq, ffmpeg, mutagen, + pyftpdlib, + magic, + partftpy, + fusepy, # for partyfuse # use argon2id-hashed passwords in config files (sha2 is always available) withHashedPasswords ? true, @@ -40,12 +44,21 @@ # send ZeroMQ messages from event-hooks withZeroMQ ? true, + # enable FTP server + withFTP ? true, + # enable FTPS support in the FTP server withFTPS ? false, + # enable TFTP server + withTFTP ? false, + # samba/cifs server; dangerous and buggy, enable if you really need it withSMB ? false, + # enables filetype detection for nameless uploads + withMagic ? false, + # extra packages to add to the PATH extraPackages ? [ ], @@ -58,14 +71,23 @@ let pinData = lib.importJSON ./pin.json; - pyEnv = python.withPackages ( - ps: - with ps; + runtimeDeps = ([ util-linux ] ++ extraPackages ++ lib.optional withMediaProcessing ffmpeg); +in +buildPythonApplication { + pname = "copyparty"; + inherit (pinData) version; + src = fetchurl { + inherit (pinData) url hash; + }; + dependencies = [ jinja2 + fusepy ] ++ lib.optional withSMB impacket + ++ lib.optional withFTP pyftpdlib ++ lib.optional withFTPS pyopenssl + ++ lib.optional withTFTP partftpy ++ lib.optional withCertgen cfssl ++ lib.optional withThumbnails pillow ++ lib.optional withFastThumbnails pyvips @@ -73,25 +95,14 @@ let ++ lib.optional withBasicAudioMetadata mutagen ++ lib.optional withHashedPasswords argon2-cffi ++ lib.optional withZeroMQ pyzmq - ++ (extraPythonPackages ps) - ); + ++ lib.optional withMagic magic + ++ (extraPythonPackages python.pkgs); + makeWrapperArgs = [ "--prefix PATH : ${lib.makeBinPath runtimeDeps}" ]; - runtimeDeps = ([ util-linux ] ++ extraPackages ++ lib.optional withMediaProcessing ffmpeg); -in -stdenv.mkDerivation { - pname = "copyparty"; - inherit (pinData) version; - src = fetchurl { - inherit (pinData) url hash; - }; - nativeBuildInputs = [ makeWrapper ]; - dontUnpack = true; - installPhase = '' - install -Dm755 $src $out/share/copyparty-sfx.py - makeWrapper ${pyEnv.interpreter} $out/bin/copyparty \ - --prefix PATH : ${lib.makeBinPath runtimeDeps} \ - --add-flag $out/share/copyparty-sfx.py - ''; + pyproject = true; + build-system = [ + setuptools + ]; meta = { description = "Turn almost any device into a file server"; longDescription = '' @@ -101,8 +112,7 @@ stdenv.mkDerivation { homepage = "https://github.com/9001/copyparty"; changelog = "https://github.com/9001/copyparty/releases/tag/v${pinData.version}"; license = lib.licenses.mit; - inherit (python.meta) platforms; mainProgram = "copyparty"; - sourceProvenance = [ lib.sourceTypes.binaryBytecode ]; + sourceProvenance = [ lib.sourceTypes.fromSource ]; }; } diff --git a/contrib/package/nix/copyparty/pin.json b/contrib/package/nix/copyparty/pin.json index c0d8c68a..5ec6e679 100644 --- a/contrib/package/nix/copyparty/pin.json +++ b/contrib/package/nix/copyparty/pin.json @@ -1,5 +1,5 @@ { - "url": "https://github.com/9001/copyparty/releases/download/v1.19.0/copyparty-sfx.py", - "version": "1.19.0", - "hash": "sha256-9A+zPtkVtUuGHB/JJV3fhVtJderLUGxHqvuJQz0/1+Q=" + "url": "https://github.com/9001/copyparty/releases/download/v1.19.8/copyparty-1.19.8.tar.gz", + "version": "1.19.8", + "hash": "sha256-MUO6UhbI1M8fvFj6CMbs75Vd4EteNLORCrC3HP/siO8=" } \ No newline at end of file diff --git a/contrib/package/nix/copyparty/update.py b/contrib/package/nix/copyparty/update.py index 363773c0..90542d2e 100755 --- a/contrib/package/nix/copyparty/update.py +++ b/contrib/package/nix/copyparty/update.py @@ -11,14 +11,14 @@ import base64 import json import hashlib import sys -import re +import tarfile from pathlib import Path OUTPUT_FILE = Path("pin.json") -TARGET_ASSET = "copyparty-sfx.py" +TARGET_ASSET = lambda version: f"copyparty-{version}.tar.gz" HASH_TYPE = "sha256" LATEST_RELEASE_URL = "https://api.github.com/repos/9001/copyparty/releases/latest" -DOWNLOAD_URL = lambda version: f"https://github.com/9001/copyparty/releases/download/v{version}/{TARGET_ASSET}" +DOWNLOAD_URL = lambda version: f"https://github.com/9001/copyparty/releases/download/v{version}/{TARGET_ASSET(version)}" def get_formatted_hash(binary): @@ -29,11 +29,13 @@ def get_formatted_hash(binary): return f"{HASH_TYPE}-{encoded_hash}" -def version_from_sfx(binary): - result = re.search(b'^VER = "(.*)"$', binary, re.MULTILINE) - if result: - return result.groups(1)[0].decode("ascii") +def version_from_tar_gz(path): + with tarfile.open(path) as tarball: + release_name = tarball.getmembers()[0].name + prefix = "copyparty-" + if release_name.startswith(prefix): + return release_name.replace(prefix, "") raise ValueError("version not found in provided file") @@ -42,7 +44,7 @@ def remote_release_pin(): response = requests.get(LATEST_RELEASE_URL).json() version = response["tag_name"].lstrip("v") - asset_info = [a for a in response["assets"] if a["name"] == TARGET_ASSET][0] + asset_info = [a for a in response["assets"] if a["name"] == TARGET_ASSET(version)][0] download_url = asset_info["browser_download_url"] asset = requests.get(download_url) formatted_hash = get_formatted_hash(asset.content) @@ -52,10 +54,9 @@ def remote_release_pin(): def local_release_pin(path): - asset = path.read_bytes() - version = version_from_sfx(asset) + version = version_from_tar_gz(path) download_url = DOWNLOAD_URL(version) - formatted_hash = get_formatted_hash(asset) + formatted_hash = get_formatted_hash(path.read_bytes()) result = {"url": download_url, "version": version, "hash": formatted_hash} return result diff --git a/contrib/package/nix/overlay.nix b/contrib/package/nix/overlay.nix new file mode 100644 index 00000000..ca8d5c6e --- /dev/null +++ b/contrib/package/nix/overlay.nix @@ -0,0 +1,11 @@ +final: prev: { + copyparty = final.python3.pkgs.callPackage ./copyparty { + ffmpeg = final.ffmpeg-full; + }; + + python3 = prev.python3.override { + packageOverrides = pyFinal: pyPrev: { + partftpy = pyFinal.callPackage ./partftpy { }; + }; + }; +} diff --git a/contrib/package/nix/partftpy/default.nix b/contrib/package/nix/partftpy/default.nix new file mode 100644 index 00000000..8293b44a --- /dev/null +++ b/contrib/package/nix/partftpy/default.nix @@ -0,0 +1,30 @@ +{ + lib, + buildPythonPackage, + fetchurl, + setuptools, +}: +let + pinData = lib.importJSON ./pin.json; +in + +buildPythonPackage rec { + pname = "partftpy"; + inherit (pinData) version; + pyproject = true; + + src = fetchurl { + inherit (pinData) url hash; + }; + + build-system = [ setuptools ]; + + pythonImportsCheck = [ "partftpy.TftpServer" ]; + + meta = { + description = "Pure Python TFTP library (copyparty edition)"; + homepage = "https://github.com/9001/partftpy"; + changelog = "https://github.com/9001/partftpy/releases/tag/${version}"; + license = lib.licenses.mit; + }; +} diff --git a/contrib/package/nix/partftpy/pin.json b/contrib/package/nix/partftpy/pin.json new file mode 100644 index 00000000..99882f05 --- /dev/null +++ b/contrib/package/nix/partftpy/pin.json @@ -0,0 +1,5 @@ +{ + "url": "https://github.com/9001/partftpy/releases/download/v0.4.0/partftpy-0.4.0.tar.gz", + "version": "0.4.0", + "hash": "sha256-5Q2zyuJ892PGZmb+YXg0ZPW/DK8RDL1uE0j5HPd4We0=" +} \ No newline at end of file diff --git a/contrib/package/nix/partftpy/update.py b/contrib/package/nix/partftpy/update.py new file mode 100755 index 00000000..01f046b5 --- /dev/null +++ b/contrib/package/nix/partftpy/update.py @@ -0,0 +1,50 @@ +#!/usr/bin/env python3 + +# Update the Nix package pin +# +# Usage: ./update.sh + +import base64 +import json +import hashlib +import sys +from pathlib import Path + +OUTPUT_FILE = Path("pin.json") +TARGET_ASSET = lambda version: f"partftpy-{version}.tar.gz" +HASH_TYPE = "sha256" +LATEST_RELEASE_URL = "https://api.github.com/repos/9001/partftpy/releases/latest" + + +def get_formatted_hash(binary): + hasher = hashlib.new("sha256") + hasher.update(binary) + asset_hash = hasher.digest() + encoded_hash = base64.b64encode(asset_hash).decode("ascii") + return f"{HASH_TYPE}-{encoded_hash}" + + +def remote_release_pin(): + import requests + + response = requests.get(LATEST_RELEASE_URL).json() + version = response["tag_name"].lstrip("v") + asset_info = [a for a in response["assets"] if a["name"] == TARGET_ASSET(version)][0] + download_url = asset_info["browser_download_url"] + asset = requests.get(download_url) + formatted_hash = get_formatted_hash(asset.content) + + result = {"url": download_url, "version": version, "hash": formatted_hash} + return result + + +def main(): + result = remote_release_pin() + + print(result) + json_result = json.dumps(result, indent=4) + OUTPUT_FILE.write_text(json_result) + + +if __name__ == "__main__": + main() diff --git a/contrib/package/nix/partyfuse/default.nix b/contrib/package/nix/partyfuse/default.nix deleted file mode 100644 index 59faa914..00000000 --- a/contrib/package/nix/partyfuse/default.nix +++ /dev/null @@ -1,26 +0,0 @@ -{ - stdenvNoCC, - copyparty, - python3, - makeBinaryWrapper, -}: -let - python = python3.withPackages (p: [ p.fusepy ]); -in -stdenvNoCC.mkDerivation { - pname = "partyfuse"; - inherit (copyparty) version meta; - src = ../../../..; - - nativeBuildInputs = [ makeBinaryWrapper ]; - - installPhase = '' - runHook preInstall - - install -Dm444 bin/partyfuse.py -t $out/share/copyparty - makeWrapper ${python.interpreter} $out/bin/partyfuse \ - --add-flag $out/share/copyparty/partyfuse.py - - runHook postInstall - ''; -} diff --git a/contrib/package/nix/u2c/default.nix b/contrib/package/nix/u2c/default.nix deleted file mode 100644 index dc1e4c56..00000000 --- a/contrib/package/nix/u2c/default.nix +++ /dev/null @@ -1,24 +0,0 @@ -{ - stdenvNoCC, - copyparty, - python312, - makeBinaryWrapper, -}: -stdenvNoCC.mkDerivation { - pname = "u2c"; - inherit (copyparty) version meta; - src = ../../../..; - - nativeBuildInputs = [ makeBinaryWrapper ]; - - installPhase = '' - runHook preInstall - - install -Dm444 bin/u2c.py -t $out/share/copyparty - mkdir $out/bin - makeWrapper ${python312.interpreter} $out/bin/u2c \ - --add-flag $out/share/copyparty/u2c.py - - runHook postInstall - ''; -} diff --git a/contrib/package/rpm/copyparty.spec b/contrib/package/rpm/copyparty.spec new file mode 100644 index 00000000..6d3ebb3f --- /dev/null +++ b/contrib/package/rpm/copyparty.spec @@ -0,0 +1,62 @@ +Name: copyparty +Version: $pkgver +Release: $pkgrel +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++ +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 + +%description +Portable file server with accelerated resumable uploads, dedup, WebDAV, FTP, TFTP, zeroconf, media indexer, thumbnails++ all in one file, no deps + +See release at https://github.com/9001/copyparty/releases + +%global debug_package %{nil} + +%generate_buildrequires +%pyproject_buildrequires + +%prep +%setup -q + +%build +cd "copyparty/web" +make +cd - +%pyproject_wheel + +%install +mkdir -p %{buildroot}%{_bindir} +mkdir -p %{buildroot}%{_libdir}/systemd/{system,user} +mkdir -p %{buildroot}/etc/%{name} +mkdir -p %{buildroot}/var/lib/%{name}-jail +mkdir -p %{buildroot}%{_datadir}/licenses/%{name} + +%pyproject_install +%pyproject_save_files copyparty + +install -m 0755 bin/prisonparty.sh %{buildroot}%{_bindir}/prisonpary.sh +install -m 0644 contrib/systemd/%{name}.conf %{buildroot}/etc/%{name}/%{name}.conf +install -m 0644 contrib/systemd/%{name}@.service %{buildroot}%{_libdir}/systemd/system/%{name}@.service +install -m 0644 contrib/systemd/%{name}-user.service %{buildroot}%{_libdir}/systemd/user/%{name}.service +install -m 0644 contrib/systemd/prisonparty@.service %{buildroot}%{_libdir}/systemd/system/prisonparty@.service +install -m 0644 contrib/systemd/index.md %{buildroot}/var/lib/%{name}-jail/README.md +install -m 0644 LICENSE %{buildroot}%{_datadir}/licenses/%{name}/LICENSE + +%files -n copyparty -f %{pyproject_files} +%license LICENSE +%{_bindir}/copyparty +%{_bindir}/partyfuse +%{_bindir}/u2c +%{_bindir}/prisonpary.sh +/etc/%{name}/%{name}.conf +%{_libdir}/systemd/system/%{name}@.service +%{_libdir}/systemd/user/%{name}.service +%{_libdir}/systemd/system/prisonparty@.service +/var/lib/%{name}-jail/README.md diff --git a/contrib/plugins/quickmove.js b/contrib/plugins/quickmove.js index 34b3a0d3..3a0381df 100644 --- a/contrib/plugins/quickmove.js +++ b/contrib/plugins/quickmove.js @@ -101,10 +101,10 @@ function our_hotkey_handler(e) { // bail if either ALT, CTRL, or SHIFT is pressed - if (e.altKey || e.shiftKey || e.isComposing || ctrl(e)) + if (anymod(e)) return main_hotkey_handler(e); // let copyparty handle this keystroke - var key_name = (e.code || e.key) + '', + var keycode = (e.key || e.code) + '', ae = document.activeElement, aet = ae && ae != document.body ? ae.nodeName.toLowerCase() : ''; @@ -114,7 +114,7 @@ if (aet && !/^(a|button|tr|td|div|pre)$/.test(aet)) return main_hotkey_handler(e); // let copyparty handle this keystroke - if (key_name == 'KeyW') { + if (keycode == 'w' || keycode == 'KeyW') { // okay, this one's for us... do the thing action_to_perform(); return ev(e); diff --git a/contrib/setup-ashell.sh b/contrib/setup-ashell.sh new file mode 100644 index 00000000..006da444 --- /dev/null +++ b/contrib/setup-ashell.sh @@ -0,0 +1,71 @@ +#!/bin/bash +# +# this script will install copyparty onto an iOS device (iPhone/iPad) +# +# step 1: install a-Shell: +# https://apps.apple.com/us/app/a-shell/id1473805438 +# +# step 2: copypaste the following command into a-Shell: +# curl https://github.com/9001/copyparty/raw/refs/heads/hovudstraum/contrib/setup-ashell.sh +# +# step 3: launch copyparty with this command: cpp +# +# if you ever want to upgrade copyparty, just repeat step 2 + + + +cd "$HOME/Documents" +curl -Locopyparty https://github.com/9001/copyparty/releases/latest/download/copyparty-sfx.py + + + +# create the config file? (cannot use heredoc because body too large) +[ -e cpc ] || { +echo '[global]' >cpc +echo ' p: 80, 443, 3923 # enable http and https on these ports' >>cpc +echo ' e2dsa # enable file indexing and filesystem scanning' >>cpc +echo ' e2ts # and enable multimedia indexing' >>cpc +echo ' ver # show copyparty version in the controlpanel' >>cpc +echo ' qrz: 2 # enable qr-code and make it big' >>cpc +echo ' qrp: 1 # reduce qr-code padding' >>cpc +echo ' qr-fg: -1 # optimize for basic/simple terminals' >>cpc +echo ' qr-wait: 0.3 # less chance of getting scrolled away' >>cpc +echo '' >>cpc +echo ' # enable these by uncommenting them:' >>cpc +echo ' # ftp: 21 # enable ftp server on port 21' >>cpc +echo ' # tftp: 69 # enable tftp server on port 69' >>cpc +echo '' >>cpc +echo '[/]' >>cpc +echo ' ~/Documents' >>cpc +echo ' accs:' >>cpc +echo ' A: *' >>cpc +} + + + +# create the launcher? +[ -e cpp ] || { +echo '#!/bin/sh' >cpp +echo '' >>cpp +echo '# change the font so the qr-code draws correctly:' >>cpp +echo 'config -n "Menlo" # name' >>cpp +echo 'config -s 8 # size' >>cpp +echo '' >>cpp +echo '# launch copyparty' >>cpp +echo 'exec copyparty -c cpc "$@"' >>cpp +} + + + +chmod 755 copyparty cpp +echo +echo ================================= +echo +echo 'okay, all done!' +echo +echo 'you can edit your config' +echo 'with this command: vim cpc' +echo +echo 'you can run copyparty' +echo 'with this command: cpp' +echo diff --git a/copyparty/__init__.py b/copyparty/__init__.py index d1630ba0..f9e7da5b 100644 --- a/copyparty/__init__.py +++ b/copyparty/__init__.py @@ -111,7 +111,9 @@ class EnvParams(object): def __init__(self) -> None: self.t0 = time.time() self.mod = "" + self.mod_ = "" self.cfg = "" + self.scfg = True E = EnvParams() diff --git a/copyparty/__main__.py b/copyparty/__main__.py index e87fe487..074f865f 100644 --- a/copyparty/__main__.py +++ b/copyparty/__main__.py @@ -36,6 +36,7 @@ from .__init__ import ( ) from .__version__ import CODENAME, S_BUILD_DT, S_VERSION from .authsrv import expand_config_file, split_cfg_ln, upgrade_cfg_fmt +from .bos import bos from .cfg import flagcats, onedash from .svchub import SvcHub from .util import ( @@ -186,7 +187,7 @@ def init_E(EE: EnvParams) -> None: E = EE # pylint: disable=redefined-outer-name - def get_unixdir() -> str: + def get_unixdir() -> tuple[str, bool]: paths: list[tuple[Callable[..., Any], str]] = [ (os.environ.get, "XDG_CONFIG_HOME"), (os.path.expanduser, "~/.config"), @@ -196,41 +197,57 @@ def init_E(EE: EnvParams) -> None: (unicode, "/tmp"), ] errs = [] - for chk in [os.listdir, os.mkdir]: - for npath, (pf, pa) in enumerate(paths): - p = "" + for npath, (pf, pa) in enumerate(paths): + priv = npath < 2 # private/trusted location + ram = npath > 1 # "nonvolatile"; not semantically same as `not priv` + p = "" + try: + p = pf(pa) + if not p or p.startswith("~"): + continue + + p = os.path.normpath(p) + mkdir = not os.path.isdir(p) + if mkdir: + os.mkdir(p, 0o700) + + p = os.path.join(p, "copyparty") + if not priv and os.path.isdir(p): + uid = os.geteuid() + if os.stat(p).st_uid != uid: + p += ".%s" % (uid,) + if os.path.isdir(p) and os.stat(p).st_uid != uid: + raise Exception("filesystem has broken unix permissions") try: - p = pf(pa) - # print(chk.__name__, p, pa) - if not p or p.startswith("~"): - continue + os.listdir(p) + except: + os.mkdir(p, 0o700) - p = os.path.normpath(p) - chk(p) # type: ignore - p = os.path.join(p, "copyparty") - if not os.path.isdir(p): - os.mkdir(p) + if ram: + t = "Using %s/copyparty [%s] for config; filekeys/dirkeys will change on every restart. Consider setting XDG_CONFIG_HOME or giving the unix-user a ~/.config/" + errs.append(t % (pa, p)) + elif mkdir: + t = "Using %s/copyparty [%s] for config%s (Warning: %s did not exist and was created just now)" + errs.append(t % (pa, p, " instead" if npath else "", pa)) + elif errs: + errs.append("Using %s/copyparty [%s] instead" % (pa, p)) - if npath > 1: - t = "Using [%s] for config; filekeys/dirkeys will change on every restart. Consider setting XDG_CONFIG_HOME or giving the unix-user a ~/.config/" - errs.append(t % (p,)) - elif errs: - errs.append("Using [%s] instead" % (p,)) + if errs: + warn(". ".join(errs)) - if errs: - warn(". ".join(errs)) + return p, priv + except Exception as ex: + if p: + t = "Unable to store config in %s [%s] due to %r" + errs.append(t % (pa, p, ex)) - return p # type: ignore - except Exception as ex: - if p and npath < 2: - t = "Unable to store config in [%s] due to %r" - errs.append(t % (p, ex)) - - raise Exception("could not find a writable path for config") + t = "could not find a writable path for runtime state:\n> %s" + raise Exception(t % ("\n> ".join(errs))) E.mod = os.path.dirname(os.path.realpath(__file__)) if E.mod.endswith("__init__"): E.mod = os.path.dirname(E.mod) + E.mod_ = os.path.join(E.mod, "") try: p = os.environ.get("XDG_CONFIG_HOME") @@ -241,7 +258,7 @@ def init_E(EE: EnvParams) -> None: p = os.path.abspath(os.path.realpath(p)) p = os.path.join(p, "copyparty") if not os.path.isdir(p): - os.mkdir(p) + os.mkdir(p, 0o700) os.listdir(p) except: p = "" @@ -254,11 +271,11 @@ def init_E(EE: EnvParams) -> None: elif sys.platform == "darwin": E.cfg = os.path.expanduser("~/Library/Preferences/copyparty") else: - E.cfg = get_unixdir() + E.cfg, E.scfg = get_unixdir() E.cfg = E.cfg.replace("\\", "/") try: - os.makedirs(E.cfg) + bos.makedirs(E.cfg, bos.MKD_700) except: if not os.path.isdir(E.cfg): raise @@ -436,6 +453,29 @@ def args_from_cfg(cfg_path: str) -> list[str]: return ret +def expand_cfg(argv) -> list[str]: + if CFG_DEF: + supp = args_from_cfg(CFG_DEF[0]) + argv = argv[:1] + supp + argv[1:] + + n = 0 + while n < len(argv): + v1 = argv[n] + v1v = v1[2:].lstrip("=") + try: + v2 = argv[n + 1] + except: + v2 = "" + + n += 1 + if v1 == "-c" and v2 and os.path.isfile(v2): + n += 1 + argv = argv[:n] + args_from_cfg(v2) + argv[n:] + elif v1.startswith("-c") and v1v and os.path.isfile(v1v): + argv = argv[:n] + args_from_cfg(v1v) + argv[n:] + return argv + + def sighandler(sig: Optional[int] = None, frame: Optional[FrameType] = None) -> None: msg = [""] * 5 for th in threading.enumerate(): @@ -609,8 +649,77 @@ def get_sects(): if no accounts or volumes are configured, current folder will be read/write for everyone + the group @acct will always have every user with an account + (the name of that group can be changed with --grp-all) + consider the config file for more flexible account/volume management, including dynamic reload at runtime (and being more readable w) + + see \033[32m--help-auth\033[0m for ways to provide the password in requests; + see \033[32m--help-idp\033[0m for replacing it with SSO and auth-middlewares + """ + ), + ], + [ + "auth", + "how to login from a client", + dedent( + """ + different ways to provide the password so you become authenticated: + + login with the ui: + go to \033[36mhttp://127.0.0.1:3923/?h\033[0m and login there + + send the password in the '\033[36mPW\033[0m' http-header: + \033[36mPW: \033[35mhunter2\033[0m + or if you have \033[33m--accounts\033[0m enabled, + \033[36mPW: \033[35med:hunter2\033[0m + + send the password in the URL itself: + \033[36mhttp://127.0.0.1:3923/\033[35m?pw=hunter2\033[0m + or if you have \033[33m--accounts\033[0m enabled, + \033[36mhttp://127.0.0.1:3923/\033[35m?pw=ed:hunter2\033[0m + + use basic-authentication: + \033[36mhttp://\033[35med:hunter2\033[36m@127.0.0.1:3923/\033[0m + which should be the same as this header: + \033[36mAuthorization: Basic \033[35mZWQ6aHVudGVyMg==\033[0m + """ + ), + ], + [ + "auth-ord", + "authentication precedence", + dedent( + """ + \033[33m--auth-ord\033[0m is a comma-separated list of auth options + (one or more of the [\033[35moptions\033[0m] below); first one wins + + [\033[35mpw\033[0m] is conventional login, for example the "\033[36mPW\033[0m" header, + or the \033[36m?pw=\033[0m[...] URL-suffix, or a valid session cookie + (see \033[33m--help-auth\033[0m) + + [\033[35midp\033[0m] is a username provided in the http-request-header + defined by \033[33m--idp-h-usr\033[0m and/or \033[33m--idp-hm-usr\033[0m, which is + provided by an authentication middleware such as + authentik, authelia, tailscale, ... (see \033[33m--help-idp\033[0m) + + [\033[35midp-h\033[0m] is specifically an \033[33m--idp-h-usr\033[0m header, + [\033[35midp-hm\033[0m] is specifically an \033[33m--idp-hm-usr\033[0m header; + [\033[35midp\033[0m] is the same as [\033[35midp-hm,idp-h\033[0m] + + [\033[35mipu\033[0m] is a mapping from an IP-address to a username, + auto-authing that client-IP to that account + (see the description of \033[36m--ipu\033[0m in \033[33m--help\033[0m) + + NOTE: even if an option (\033[35mpw\033[0m/\033[35mipu\033[0m/...) is not in the list, + it may still be enabled and can still take effect if + none of the other alternatives identify the user + + NOTE: if [\033[35mipu\033[0m] is in the list, it must be FIRST or LAST + + NOTE: if [\033[35mpw\033[0m] is not in the list, the logout-button + will be hidden when any idp feature is enabled """ ), ], @@ -719,7 +828,7 @@ def get_sects(): \033[36mc0\033[35m show all process output (default) \033[36mc1\033[35m show only stderr \033[36mc2\033[35m show only stdout - \033[36mc3\033[35m mute all process otput + \033[36mc3\033[35m mute all process output \033[0m examples: @@ -762,6 +871,41 @@ def get_sects(): the upload speed can easily drop to 10% for small files)""" ), ], + [ + "idp", + "replacing the login system with fancy middleware", + dedent( + """ + if you already have a centralized service which handles + user-authentication for other services already, you can + integrate copyparty with that for automatic login + + if the middleware is providing the username in an http-header + named '\033[35mtheUsername\033[0m' then do this: \033[36m--idp-h-usr theUsername\033[0m + + if the middleware is providing a list of groups in the header + named '\033[35mtheGroups\033[0m' then do this: \033[36m--idp-h-grp theGroup\033[0m + + if the list of groups is separated by '\033[35m%\033[0m' then \033[36m--idp-gsep %\033[0m + + if the middleware is providing a header named '\033[35mAccount\033[0m' + and the value is '\033[35malice@forest.net\033[0m' but the username is + actually '\033[35mmarisa\033[0m' then do this for each user: + \033[36m--idp-hm-usr ^Account^alice@forest.net^marisa\033[0m + (the separator '\033[35m^\033[0m' can be any character) + + make ABSOLUTELY SURE that the header can only be set by your + middleware and not by clients! and, as an extra precaution, + send a header named '\033[36mfinalmasterspark\033[0m' (a secret keyword) + and then \033[36m--idp-h-key finalmasterspark\033[0m to require that + + the login/logout links/buttons can be replaced with links + going to your IdP's UI; \033[36m--idp-login /login/?redir={dst}\033[0m + will expand \033[36m{dst}\033[0m to the URL of the current page, so + the IdP can redirect the user back to where they were + """ + ), + ], [ "urlform", "how to handle url-form POSTs", @@ -1006,6 +1150,7 @@ def add_general(ap, nc, srvname): ap2.add_argument("-v", metavar="VOL", type=u, action="append", help="\033[34mREPEATABLE:\033[0m add volume, \033[33mSRC\033[0m:\033[33mDST\033[0m:\033[33mFLAG\033[0m; examples [\033[32m.::r\033[0m], [\033[32m/mnt/nas/music:/music:r:aed\033[0m], see --help-accounts") ap2.add_argument("--grp", metavar="G:N,N", type=u, action="append", help="\033[34mREPEATABLE:\033[0m add group, \033[33mNAME\033[0m:\033[33mUSER1\033[0m,\033[33mUSER2\033[0m,\033[33m...\033[0m; example [\033[32madmins:ed,foo,bar\033[0m]") ap2.add_argument("--usernames", action="store_true", help="require username and password for login; default is just password") + ap2.add_argument("--chdir", metavar="PATH", type=u, help="change working-directory to \033[33mPATH\033[0m before mapping volumes") ap2.add_argument("-ed", action="store_true", help="enable the ?dots url parameter / client option which allows clients to see dotfiles / hidden files (volflag=dots)") ap2.add_argument("--urlform", metavar="MODE", type=u, default="print,xm", help="how to handle url-form POSTs; see \033[33m--help-urlform\033[0m") ap2.add_argument("--wintitle", metavar="TXT", type=u, default="cpp @ $pub", help="server terminal title, for example [\033[32m$ip-10.1.2.\033[0m] or [\033[32m$ip-]") @@ -1019,14 +1164,19 @@ def add_general(ap, nc, srvname): def add_qr(ap, tty): ap2 = ap.add_argument_group("qr options") - ap2.add_argument("--qr", action="store_true", help="show http:// QR-code on startup") - ap2.add_argument("--qrs", action="store_true", help="show https:// QR-code on startup") + ap2.add_argument("--qr", action="store_true", help="show QR-code on startup") + ap2.add_argument("--qrs", action="store_true", help="change the QR-code URL to https://") ap2.add_argument("--qrl", metavar="PATH", type=u, default="", help="location to include in the url, for example [\033[32mpriv/?pw=hunter2\033[0m]") ap2.add_argument("--qri", metavar="PREFIX", type=u, default="", help="select IP which starts with \033[33mPREFIX\033[0m; [\033[32m.\033[0m] to force default IP when mDNS URL would have been used instead") - ap2.add_argument("--qr-fg", metavar="COLOR", type=int, default=0 if tty else 16, help="foreground; try [\033[32m0\033[0m] if the qr-code is unreadable") + ap2.add_argument("--qr-fg", metavar="COLOR", type=int, default=0 if tty else 16, help="foreground; try [\033[32m0\033[0m] or [\033[32m-1\033[0m] if the qr-code is unreadable") ap2.add_argument("--qr-bg", metavar="COLOR", type=int, default=229, help="background (white=255)") ap2.add_argument("--qrp", metavar="CELLS", type=int, default=4, help="padding (spec says 4 or more, but 1 is usually fine)") ap2.add_argument("--qrz", metavar="N", type=int, default=0, help="[\033[32m1\033[0m]=1x, [\033[32m2\033[0m]=2x, [\033[32m0\033[0m]=auto (try [\033[32m2\033[0m] on broken fonts)") + ap2.add_argument("--qr-pin", metavar="N", type=int, default=0, help="sticky/pin the qr-code to always stay on-screen; [\033[32m0\033[0m]=disabled, [\033[32m1\033[0m]=with-url, [\033[32m2\033[0m]=just-qr") + ap2.add_argument("--qr-wait", metavar="SEC", type=float, default=0, help="wait \033[33mSEC\033[0m before printing the qr-code to the log") + ap2.add_argument("--qr-every", metavar="SEC", type=float, default=0, help="print the qr-code every \033[33mSEC\033[0m (try this with/without --qr-pin in case of issues)") + ap2.add_argument("--qr-winch", metavar="SEC", type=float, default=0, help="when --qr-pin is enabled, check for terminal size change every \033[33mSEC\033[0m") + ap2.add_argument("--qr-file", metavar="TXT", type=u, action="append", help="\033[34mREPEATABLE:\033[0m write qr-code to file.\n โ””โ”€To create txt or svg, \033[33mTXT\033[0m is Filepath:Zoom:Pad, for example [\033[32mqr.txt:1:2\033[0m]\n โ””โ”€To create png or gif, \033[33mTXT\033[0m is Filepath:Zoom:Pad:Foreground:Background, for example [\033[32mqr.png:8:2:333333:ffcc55\033[0m], or [\033[32mqr.png:8:2::ffcc55\033[0m] for transparent") def add_fs(ap): @@ -1043,6 +1193,7 @@ def add_share(ap): ap2 = ap.add_argument_group("share-url options") ap2.add_argument("--shr", metavar="DIR", type=u, default="", help="toplevel virtual folder for shared files/folders, for example [\033[32m/share\033[0m]") ap2.add_argument("--shr-db", metavar="FILE", type=u, default=db_path, help="database to store shares in") + ap2.add_argument("--shr-who", metavar="TXT", type=u, default="auth", help="who can create a share? [\033[32mno\033[0m]=nobody, [\033[32ma\033[0m]=admin-permission, [\033[32mauth\033[0m]=authenticated (volflag=shr_who)") ap2.add_argument("--shr-adm", metavar="U,U", type=u, default="", help="comma-separated list of users allowed to view/delete any share") ap2.add_argument("--shr-rt", metavar="MIN", type=int, default=1440, help="shares can be revived by their owner if they expired less than MIN minutes ago; [\033[32m60\033[0m]=hour, [\033[32m1440\033[0m]=day, [\033[32m10080\033[0m]=week") ap2.add_argument("--shr-v", action="store_true", help="debug") @@ -1056,6 +1207,7 @@ def add_upload(ap): ap2.add_argument("--put-ck", metavar="ALG", type=u, default="sha512", help="default checksum-hasher for PUT/WebDAV uploads: no / md5 / sha1 / sha256 / sha512 / b2 / blake2 / b2s / blake2s (volflag=put_ck)") 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("--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") @@ -1149,7 +1301,8 @@ def add_auth(ap): idp_db = os.path.join(E.cfg, "idp.db") ses_db = os.path.join(E.cfg, "sessions.db") ap2 = ap.add_argument_group("IdP / identity provider / user authentication options") - ap2.add_argument("--idp-h-usr", metavar="HN", type=u, default="", help="bypass the copyparty authentication checks if the request-header \033[33mHN\033[0m contains a username to associate the request with (for use with authentik/oauth/...)\n\033[1;31mWARNING:\033[0m if you enable this, make sure clients are unable to specify this header themselves; must be washed away and replaced by a reverse-proxy") + ap2.add_argument("--idp-h-usr", metavar="HN", type=u, action="append", help="\033[34mREPEATABLE:\033[0m bypass the copyparty authentication checks if the request-header \033[33mHN\033[0m contains a username to associate the request with (for use with authentik/oauth/...)\n\033[1;31mWARNING:\033[0m if you enable this, make sure clients are unable to specify this header themselves; must be washed away and replaced by a reverse-proxy") + ap2.add_argument("--idp-hm-usr", metavar="T", type=u, action="append", help="\033[34mREPEATABLE:\033[0m bypass the copyparty authentication checks if the request-header \033[33mT\033[0m is provided, and its value exists in a mapping defined by this option; see --help-idp") 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") @@ -1157,12 +1310,24 @@ def add_auth(ap): 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)") ap2.add_argument("--idp-cookie", metavar="S", type=int, default=0, help="generate a session-token for IdP users which is written to cookie \033[33mcppws\033[0m (or \033[33mcppwd\033[0m if plaintext), to reduce the load on the IdP server, lifetime \033[33mS\033[0m seconds.\n โ””โ”€note: The expiration time is a client hint only; the actual lifetime of the session-token is infinite (until next restart with \033[33m--ses-db\033[0m wiped)") + ap2.add_argument("--idp-login", metavar="L", type=u, default="", help="replace all login-buttons with a link to URL \033[33mL\033[0m (unless \033[32mpw\033[0m is in \033[33m--auth-ord\033[0m then both will be shown); [\033[32m{dst}\033[0m] expands to url of current page") + 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("--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)") ap2.add_argument("--ses-len", metavar="CHARS", type=int, default=20, help="session key length; default is 120 bits ((20//4)*4*6)") ap2.add_argument("--no-ses", action="store_true", help="disable sessions; use plaintext passwords in cookies") + ap2.add_argument("--grp-all", metavar="NAME", type=u, default="acct", help="the name of the auto-generated group which contains every username which is known") ap2.add_argument("--ipu", metavar="CIDR=USR", type=u, action="append", help="\033[34mREPEATABLE:\033[0m users with IP matching \033[33mCIDR\033[0m are auto-authenticated as username \033[33mUSR\033[0m; example: [\033[32m172.16.24.0/24=dave]") + ap2.add_argument("--ipr", metavar="CIDR=USR", type=u, action="append", help="\033[34mREPEATABLE:\033[0m username \033[33mUSR\033[0m can only connect from an IP matching one or more \033[33mCIDR\033[0m (comma-sep.); example: [\033[32m192.168.123.0/24,172.16.0.0/16=dave]") + ap2.add_argument("--have-idp-hdrs", type=u, default="", help=argparse.SUPPRESS) + ap2.add_argument("--have-ipu-or-ipr", type=u, default="", help=argparse.SUPPRESS) + ap2.add_argument("--ao-idp-before-pw", type=u, default="", help=argparse.SUPPRESS) + ap2.add_argument("--ao-h-before-hm", type=u, default="", help=argparse.SUPPRESS) + ap2.add_argument("--ao-ipu-wins", type=u, default="", help=argparse.SUPPRESS) + ap2.add_argument("--ao-have-pw", type=u, default="", help=argparse.SUPPRESS) def add_chpw(ap): @@ -1201,6 +1366,7 @@ def add_zc_mdns(ap): ap2.add_argument("--zm-lh", metavar="PATH", type=u, default="", help="link a specific folder for http shares") ap2.add_argument("--zm-lf", metavar="PATH", type=u, default="", help="link a specific folder for ftp shares") ap2.add_argument("--zm-ls", metavar="PATH", type=u, default="", help="link a specific folder for smb shares") + ap2.add_argument("--zm-fqdn", metavar="FQDN", type=u, default="--name.local", help="the domain to announce; NOTE: using anything other than .local is nonstandard and could cause problems") ap2.add_argument("--zm-mnic", action="store_true", help="merge NICs which share subnets; assume that same subnet means same network") ap2.add_argument("--zm-msub", action="store_true", help="merge subnets on each NIC -- always enabled for ipv6 -- reduces network load, but gnome-gvfs clients may stop working, and clients cannot be in subnets that the server is not") ap2.add_argument("--zm-noneg", action="store_true", help="disable NSEC replies -- try this if some clients don't see copyparty") @@ -1307,6 +1473,7 @@ def add_yolo(ap): ap2.add_argument("--no-fnugg", action="store_true", help="disable the smoketest for caching-related issues in the web-UI") ap2.add_argument("--getmod", action="store_true", help="permit ?move=[...] and ?delete as GET") ap2.add_argument("--wo-up-readme", action="store_true", help="allow users with write-only access to upload logues and readmes without adding the _wo_ filename prefix (volflag=wo_up_readme)") + ap2.add_argument("--unsafe-state", action="store_true", help="when one of the emergency fallback locations are used for runtime state ($TMPDIR, /tmp), certain features will be force-disabled for security reasons by default. This option overrides that safeguard and allows unsafe storage of secrets") def add_optouts(ap): @@ -1317,9 +1484,10 @@ def add_optouts(ap): ap2.add_argument("--no-del", action="store_true", help="disable delete operations") ap2.add_argument("--no-mv", action="store_true", help="disable move/rename operations") ap2.add_argument("--no-cp", action="store_true", help="disable copy operations") + ap2.add_argument("--no-fs-abrt", action="store_true", help="disable ability to abort ongoing copy/move") ap2.add_argument("-nth", action="store_true", help="no title hostname; don't show \033[33m--name\033[0m in ") ap2.add_argument("-nih", action="store_true", help="no info hostname -- don't show in UI") - ap2.add_argument("-nid", action="store_true", help="no info disk-usage -- don't show in UI") + ap2.add_argument("-nid", action="store_true", help="no info disk-usage -- don't show in UI. This is the same as --du-who no") ap2.add_argument("-nb", action="store_true", help="no powered-by-copyparty branding in UI") ap2.add_argument("--zipmaxn", metavar="N", type=u, default="0", help="reject download-as-zip if more than \033[33mN\033[0m files in total; optionally takes a unit suffix: [\033[32m256\033[0m], [\033[32m9K\033[0m], [\033[32m4G\033[0m] (volflag=zipmaxn)") ap2.add_argument("--zipmaxs", metavar="SZ", type=u, default="0", help="reject download-as-zip if total download size exceeds \033[33mSZ\033[0m bytes; optionally takes a unit suffix: [\033[32m256M\033[0m], [\033[32m4G\033[0m], [\033[32m2T\033[0m] (volflag=zipmaxs)") @@ -1338,7 +1506,7 @@ def add_optouts(ap): def add_safety(ap): ap2 = ap.add_argument_group("safety options") ap2.add_argument("-s", action="count", default=0, help="increase safety: Disable thumbnails / potentially dangerous software (ffmpeg/pillow/vips), hide partial uploads, avoid crawlers.\n โ””โ”€Alias of\033[32m --dotpart --no-thumb --no-mtag-ff --no-robots --force-js") - ap2.add_argument("-ss", action="store_true", help="further increase safety: Prevent js-injection, accidental move/delete, broken symlinks, webdav, 404 on 403, ban on excessive 404s.\n โ””โ”€Alias of\033[32m -s --unpost=0 --no-del --no-mv --hardlink --vague-403 -nih") + ap2.add_argument("-ss", action="store_true", help="further increase safety: Prevent js-injection, accidental move/delete, broken symlinks, webdav requires login, 404 on 403, ban on excessive 404s.\n โ””โ”€Alias of\033[32m -s --unpost=0 --no-del --no-mv --hardlink --dav-auth --vague-403 -nih") ap2.add_argument("-sss", action="store_true", help="further increase safety: Enable logging to disk, scan for dangerous symlinks.\n โ””โ”€Alias of\033[32m -ss --no-dav --no-logues --no-readme -lo=cpp-%%Y-%%m%%d-%%H%%M%%S.txt.xz --ls=**,*,ln,p,r") ap2.add_argument("--ls", metavar="U[,V[,F]]", type=u, default="", help="do a sanity/safety check of all volumes on startup; arguments \033[33mUSER\033[0m,\033[33mVOL\033[0m,\033[33mFLAGS\033[0m (see \033[33m--help-ls\033[0m); example [\033[32m**,*,ln,p,r\033[0m]") ap2.add_argument("--xvol", action="store_true", help="never follow symlinks leaving the volume root, unless the link is into another volume where the user has similar access (volflag=xvol)") @@ -1360,6 +1528,8 @@ def add_safety(ap): ap2.add_argument("--sus-urls", metavar="R", type=u, default=r"\.php$|(^|/)wp-(admin|content|includes)/", help="URLs which are considered sus / eligible for banning; disable with blank or [\033[32mno\033[0m]") ap2.add_argument("--nonsus-urls", metavar="R", type=u, default=r"^(favicon\.ico|robots\.txt)$|^apple-touch-icon|^\.well-known", help="harmless URLs ignored from 404-bans; disable with blank or [\033[32mno\033[0m]") ap2.add_argument("--early-ban", action="store_true", help="if a client is banned, reject its connection as soon as possible; not a good idea to enable when proxied behind cloudflare since it could ban your reverse-proxy") + ap2.add_argument("--cookie-nmax", metavar="N", type=int, default=50, help="reject HTTP-request from client if they send more than N cookies") + ap2.add_argument("--cookie-cmax", metavar="N", type=int, default=8192, help="reject HTTP-request from client if more than N characters in Cookie header") ap2.add_argument("--aclose", metavar="MIN", type=int, default=10, help="if a client maxes out the server connection limit, downgrade it from connection:keep-alive to connection:close for \033[33mMIN\033[0m minutes (and also kill its active connections) -- disable with 0") ap2.add_argument("--loris", metavar="B", type=int, default=60, help="if a client maxes out the server connection limit without sending headers, ban it for \033[33mB\033[0m minutes; disable with [\033[32m0\033[0m]") ap2.add_argument("--acao", metavar="V[,V]", type=u, default="*", help="Access-Control-Allow-Origin; list of origins (domains/IPs without port) to accept requests from; [\033[32mhttps://1.2.3.4\033[0m]. Default [\033[32m*\033[0m] allows requests from all sites but removes cookies and http-auth; only ?pw=hunter2 survives") @@ -1390,7 +1560,7 @@ def add_shutdown(ap): def add_logging(ap): ap2 = ap.add_argument_group("logging options") ap2.add_argument("-q", action="store_true", help="quiet; disable most STDOUT messages") - ap2.add_argument("-lo", metavar="PATH", type=u, default="", help="logfile, example: \033[32mcpp-%%Y-%%m%%d-%%H%%M%%S.txt.xz\033[0m (NB: some errors may appear on STDOUT only)") + ap2.add_argument("-lo", metavar="PATH", type=u, default="", help="logfile; use .txt for plaintext or .xz for compressed. Example: \033[32mcpp-%%Y-%%m%%d-%%H%%M%%S.txt.xz\033[0m (NB: some errors may appear on STDOUT only)") ap2.add_argument("--no-ansi", action="store_true", default=not VT100, help="disable colors; same as environment-variable NO_COLOR") ap2.add_argument("--ansi", action="store_true", help="force colors; overrides environment-variable NO_COLOR") ap2.add_argument("--no-logflush", action="store_true", help="don't flush the logfile after each write; tiny bit faster") @@ -1398,6 +1568,7 @@ def add_logging(ap): 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-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") ap2.add_argument("--log-htp", action="store_true", help="debug: print http-server threadpool scaling") ap2.add_argument("--ihead", metavar="HEADER", type=u, action='append', help="print request \033[33mHEADER\033[0m; [\033[32m*\033[0m]=all") @@ -1426,11 +1597,12 @@ def add_thumbnail(ap): ap2.add_argument("--no-athumb", action="store_true", help="disable audio thumbnails (spectrograms) (volflag=dathumb)") ap2.add_argument("--th-size", metavar="WxH", default="320x256", help="thumbnail res (volflag=thsize)") ap2.add_argument("--th-mt", metavar="CORES", type=int, default=CORES, help="num cpu cores to use for generating thumbnails") - ap2.add_argument("--th-convt", metavar="SEC", type=float, default=60.0, help="conversion timeout in seconds (volflag=convt)") + ap2.add_argument("--th-convt", metavar="SEC", type=float, default=60.0, help="convert-to-image timeout in seconds (volflag=convt)") + ap2.add_argument("--ac-convt", metavar="SEC", type=float, default=150.0, help="convert-to-audio timeout in seconds (volflag=aconvt)") 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-dec", metavar="LIBS", default="vips,pil,ff", help="image decoders, in order of preference") + 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") ap2.add_argument("--th-ff-jpg", action="store_true", help="force jpg output for video thumbs (avoids issues on some FFmpeg builds)") @@ -1439,16 +1611,19 @@ def add_thumbnail(ap): ap2.add_argument("--th-clean", metavar="SEC", type=int, default=43200, help="cleanup interval; 0=disabled") ap2.add_argument("--th-maxage", metavar="SEC", type=int, default=604800, help="max folder age -- folders which haven't been poked for longer than \033[33m--th-poke\033[0m seconds will get deleted every \033[33m--th-clean\033[0m seconds") ap2.add_argument("--th-covers", metavar="N,N", type=u, default="folder.png,folder.jpg,cover.png,cover.jpg", help="folder thumbnails to stat/look for; enabling \033[33m-e2d\033[0m will make these case-insensitive, and try them as dotfiles (.folder.jpg), and also automatically select thumbnails for all folders that contain pics, even if none match this pattern") + ap2.add_argument("--th-spec-p", metavar="N", type=u, default=1, help="for music, do spectrograms or embedded coverart? [\033[32m0\033[0m]=only-art, [\033[32m1\033[0m]=prefer-art, [\033[32m2\033[0m]=only-spec") # https://pillow.readthedocs.io/en/stable/handbook/image-file-formats.html # https://github.com/libvips/libvips + # https://stackoverflow.com/a/47612661 # ffmpeg -hide_banner -demuxers | awk '/^ D /{print$2}' | while IFS= read -r x; do ffmpeg -hide_banner -h demuxer=$x; done | grep -E '^Demuxer |extensions:' - ap2.add_argument("--th-r-pil", metavar="T,T", type=u, default="avif,avifs,blp,bmp,cbz,dcx,dds,dib,emf,eps,fits,flc,fli,fpx,gif,heic,heics,heif,heifs,icns,ico,im,j2p,j2k,jp2,jpeg,jpg,jpx,pbm,pcx,pgm,png,pnm,ppm,psd,qoi,sgi,spi,tga,tif,tiff,webp,wmf,xbm,xpm", help="image formats to decode using pillow") - ap2.add_argument("--th-r-vips", metavar="T,T", type=u, default="avif,exr,fit,fits,fts,gif,hdr,heic,jp2,jpeg,jpg,jpx,jxl,nii,pfm,pgm,png,ppm,svg,tif,tiff,webp", help="image formats to decode using pyvips") - ap2.add_argument("--th-r-ffi", metavar="T,T", type=u, default="apng,avif,avifs,bmp,cbz,dds,dib,fit,fits,fts,gif,hdr,heic,heics,heif,heifs,icns,ico,jp2,jpeg,jpg,jpx,jxl,pbm,pcx,pfm,pgm,png,pnm,ppm,psd,qoi,sgi,tga,tif,tiff,webp,xbm,xpm", help="image formats to decode using ffmpeg") + ap2.add_argument("--th-r-pil", metavar="T,T", type=u, default="avif,avifs,blp,bmp,cbz,dcx,dds,dib,emf,eps,epub,fits,flc,fli,fpx,gif,heic,heics,heif,heifs,icns,ico,im,j2p,j2k,jp2,jpeg,jpg,jpx,pbm,pcx,pgm,png,pnm,ppm,psd,qoi,sgi,spi,tga,tif,tiff,webp,wmf,xbm,xpm", help="image formats to decode using pillow") + ap2.add_argument("--th-r-vips", metavar="T,T", type=u, default="avif,exr,fit,fits,fts,gif,hdr,heic,heics,heif,heifs,jp2,jpeg,jpg,jpx,jxl,nii,pfm,pgm,png,ppm,svg,tif,tiff,webp", help="image formats to decode using pyvips") + ap2.add_argument("--th-r-raw", metavar="T,T", type=u, default="arw,cr2,cr3,crw,dcr,dng,erf,k25,kdc,mrw,nef,orf,pef,raf,raw,sr2,srf,x3f", help="image formats to decode using rawpy") + ap2.add_argument("--th-r-ffi", metavar="T,T", type=u, default="apng,avif,avifs,bmp,cbz,dds,dib,epub,fit,fits,fts,gif,hdr,heic,heics,heif,heifs,icns,ico,jp2,jpeg,jpg,jpx,jxl,pbm,pcx,pfm,pgm,png,pnm,ppm,psd,qoi,sgi,tga,tif,tiff,webp,xbm,xpm", help="image formats to decode using ffmpeg") ap2.add_argument("--th-r-ffv", metavar="T,T", type=u, default="3gp,asf,av1,avc,avi,flv,h264,h265,hevc,m4v,mjpeg,mjpg,mkv,mov,mp4,mpeg,mpeg2,mpegts,mpg,mpg2,mts,nut,ogm,ogv,rm,ts,vob,webm,wmv", help="video formats to decode using ffmpeg") ap2.add_argument("--th-r-ffa", metavar="T,T", type=u, default="aac,ac3,aif,aiff,alac,alaw,amr,apac,ape,au,bonk,dfpwm,dts,flac,gsm,ilbc,it,itgz,itxz,itz,m4a,mdgz,mdxz,mdz,mo3,mod,mp2,mp3,mpc,mptm,mt2,mulaw,oga,ogg,okt,opus,ra,s3m,s3gz,s3xz,s3z,tak,tta,ulaw,wav,wma,wv,xm,xmgz,xmxz,xmz,xpk", help="audio formats to decode using ffmpeg") ap2.add_argument("--th-spec-cnv", metavar="T", type=u, default="it,itgz,itxz,itz,mdgz,mdxz,mdz,mo3,mod,s3m,s3gz,s3xz,s3z,xm,xmgz,xmxz,xmz,xpk", help="audio formats which provoke https://trac.ffmpeg.org/ticket/10797 (huge ram usage for s3xmodit spectrograms)") - ap2.add_argument("--au-unpk", metavar="E=F.C", type=u, default="mdz=mod.zip, mdgz=mod.gz, mdxz=mod.xz, s3z=s3m.zip, s3gz=s3m.gz, s3xz=s3m.xz, xmz=xm.zip, xmgz=xm.gz, xmxz=xm.xz, itz=it.zip, itgz=it.gz, itxz=it.xz, cbz=jpg.cbz", help="audio/image formats to decompress before passing to ffmpeg") + ap2.add_argument("--au-unpk", metavar="E=F.C", type=u, default="mdz=mod.zip, mdgz=mod.gz, mdxz=mod.xz, s3z=s3m.zip, s3gz=s3m.gz, s3xz=s3m.xz, xmz=xm.zip, xmgz=xm.gz, xmxz=xm.xz, itz=it.zip, itgz=it.gz, itxz=it.xz, cbz=jpg.cbz, epub=jpg.epub", help="audio/image formats to decompress before passing to ffmpeg") def add_transcoding(ap): @@ -1493,8 +1668,8 @@ def add_db_general(ap, hcores): ap2.add_argument("-e2vp", action="store_true", help="on hash mismatch: panic and quit copyparty") ap2.add_argument("--hist", metavar="PATH", type=u, default="", help="where to store volume data (db, thumbs); default is a folder named \".hist\" inside each volume (volflag=hist)") ap2.add_argument("--dbpath", metavar="PATH", type=u, default="", help="override where the volume databases are to be placed; default is the same as \033[33m--hist\033[0m (volflag=dbpath)") - ap2.add_argument("--no-hash", metavar="PTN", type=u, default="", help="regex: disable hashing of matching absolute-filesystem-paths during e2ds folder scans (volflag=nohash)") - ap2.add_argument("--no-idx", metavar="PTN", type=u, default=noidx, help="regex: disable indexing of matching absolute-filesystem-paths during e2ds folder scans (volflag=noidx)") + ap2.add_argument("--no-hash", metavar="PTN", type=u, default="", help="regex: disable hashing of matching absolute-filesystem-paths during e2ds folder scans (must be specified as one big regex, not multiple times) (volflag=nohash)") + ap2.add_argument("--no-idx", metavar="PTN", type=u, default=noidx, help="regex: disable indexing of matching absolute-filesystem-paths during e2ds folder scan (must be specified as one big regex, not multiple times) (volflag=noidx)") ap2.add_argument("--no-dirsz", action="store_true", help="do not show total recursive size of folders in listings, show inode size instead; slightly faster (volflag=nodirsz)") ap2.add_argument("--re-dirsz", action="store_true", help="if the directory-sizes in the UI are bonkers, use this along with \033[33m-e2dsa\033[0m to rebuild the index from scratch") ap2.add_argument("--no-dhash", action="store_true", help="disable rescan acceleration; do full database integrity check -- makes the db ~5%% smaller and bootup/rescans 3~10x slower") @@ -1531,6 +1706,7 @@ def add_db_metadata(ap): def add_txt(ap): ap2 = ap.add_argument_group("textfile options") + ap2.add_argument("--md-no-br", action="store_true", help="markdown: disable newline-is-newline; will only render a newline into the html given two trailing spaces or a double-newline (volflag=md_no_br)") ap2.add_argument("--md-hist", metavar="TXT", type=u, default="s", help="where to store old version of markdown files; [\033[32ms\033[0m]=subfolder, [\033[32mv\033[0m]=volume-histpath, [\033[32mn\033[0m]=nope/disabled (volflag=md_hist)") ap2.add_argument("--txt-eol", metavar="TYPE", type=u, default="", help="enable EOL conversion when writing documents; supported: CRLF, LF (volflag=txt_eol)") ap2.add_argument("-mcr", metavar="SEC", type=int, default=60, help="the textfile editor will check for serverside changes every \033[33mSEC\033[0m seconds") @@ -1560,13 +1736,14 @@ def add_og(ap): def add_ui(ap, retry): + THEMES = 10 ap2 = ap.add_argument_group("ui options") ap2.add_argument("--grid", action="store_true", help="show grid/thumbnails by default (volflag=grid)") ap2.add_argument("--gsel", action="store_true", help="select files in grid by ctrl-click (volflag=gsel)") ap2.add_argument("--localtime", action="store_true", help="default to local timezone instead of UTC") - ap2.add_argument("--lang", metavar="LANG", type=u, default="eng", help="language; one of the following: \033[32meng nor chi\033[0m") - ap2.add_argument("--theme", metavar="NUM", type=int, default=0, help="default theme to use (0..7)") - ap2.add_argument("--themes", metavar="NUM", type=int, default=8, help="number of themes installed") + ap2.add_argument("--lang", metavar="LANG", type=u, default="eng", help="language, for example \033[32meng\033[0m / \033[32mnor\033[0m / ...") + ap2.add_argument("--theme", metavar="NUM", type=int, default=0, help="default theme to use (0..%d)" % (THEMES - 1,)) + ap2.add_argument("--themes", metavar="NUM", type=int, default=THEMES, help="number of themes installed") ap2.add_argument("--au-vol", metavar="0-100", type=int, default=50, choices=range(0, 101), help="default audio/video volume percent") ap2.add_argument("--sort", metavar="C,C,C", type=u, default="href", help="default sort order, comma-separated column IDs (see header tooltips), prefix with '-' for descending. Examples: \033[32mhref -href ext sz ts tags/Album tags/.tn\033[0m (volflag=sort)") ap2.add_argument("--nsort", action="store_true", help="default-enable natural sort of filenames with leading numbers (volflag=nsort)") @@ -1588,7 +1765,11 @@ def add_ui(ap, retry): 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") - ap2.add_argument("--ver", action="store_true", help="show version on the control panel (incompatible with \033[33m-nb\033[0m)") + ap2.add_argument("--ver", action="store_true", help="show version on the control panel (incompatible with \033[33m-nb\033[0m). This is the same as --ver-who all") + ap2.add_argument("--ver-who", metavar="TXT", type=u, default="no", help="only show version for: [\033[32ma\033[0m]=admin-permission-anywhere, [\033[32mauth\033[0m]=authenticated, [\033[32mall\033[0m]=anyone") + ap2.add_argument("--du-who", metavar="TXT", type=u, default="all", help="only show disk usage for: [\033[32mno\033[0m]=nobody, [\033[32ma\033[0m]=admin-permission, [\033[32mrw\033[0m]=read-write, [\033[32mw\033[0m]=write, [\033[32mauth\033[0m]=authenticated, [\033[32mall\033[0m]=anyone (volflag=du_who)") + ap2.add_argument("--ver-iwho", type=int, default=0, help=argparse.SUPPRESS) + ap2.add_argument("--du-iwho", type=int, default=0, help=argparse.SUPPRESS) ap2.add_argument("--k304", metavar="NUM", type=int, default=0, help="configure the option to enable/disable k304 on the controlpanel (workaround for buggy reverse-proxies); [\033[32m0\033[0m] = hidden and default-off, [\033[32m1\033[0m] = visible and default-off, [\033[32m2\033[0m] = visible and default-on") ap2.add_argument("--no304", metavar="NUM", type=int, default=0, help="configure the option to enable/disable no304 on the controlpanel (workaround for buggy caching in browsers); [\033[32m0\033[0m] = hidden and default-off, [\033[32m1\033[0m] = visible and default-off, [\033[32m2\033[0m] = visible and default-on") ap2.add_argument("--ctl-re", metavar="SEC", type=int, default=1, help="the controlpanel Refresh-button will autorefresh every SEC; [\033[32m0\033[0m] = just once") @@ -1767,16 +1948,7 @@ def main(argv: Optional[list[str]] = None) -> None: ensure_webdeps() - for k, v in zip(argv[1:], argv[2:]): - if k == "-c" and os.path.isfile(v): - supp = args_from_cfg(v) - argv.extend(supp) - - for k in argv[1:]: - v = k[2:] - if k.startswith("-c") and v and os.path.isfile(v): - supp = args_from_cfg(v) - argv.extend(supp) + argv = expand_cfg(argv) deprecated: list[tuple[str, str]] = [ ("--salt", "--warksalt"), @@ -1845,6 +2017,9 @@ def main(argv: Optional[list[str]] = None) -> None: except: sys.exit(1) + if al.chdir: + os.chdir(al.chdir) + if al.ansi: al.no_ansi = False elif not al.no_ansi: @@ -1872,7 +2047,7 @@ def main(argv: Optional[list[str]] = None) -> None: if not HAVE_IPV6 and al.i == "::": al.i = "0.0.0.0" - al.i = al.i.split(",") + al.i = [x.strip() for x in al.i.split(",")] try: if "-" in al.p: lo, hi = [int(x) for x in al.p.split("-")] diff --git a/copyparty/__version__.py b/copyparty/__version__.py index 3350f413..eb605668 100644 --- a/copyparty/__version__.py +++ b/copyparty/__version__.py @@ -1,8 +1,8 @@ # coding: utf-8 -VERSION = (1, 19, 0) +VERSION = (1, 19, 8) CODENAME = "usernames" -BUILD_DT = (2025, 8, 7) +BUILD_DT = (2025, 9, 7) S_VERSION = ".".join(map(str, VERSION)) S_BUILD_DT = "{0:04d}-{1:02d}-{2:02d}".format(*BUILD_DT) diff --git a/copyparty/authsrv.py b/copyparty/authsrv.py index 0f1c94f9..88aa7314 100644 --- a/copyparty/authsrv.py +++ b/copyparty/authsrv.py @@ -431,6 +431,8 @@ class VFS(object): self.get_dbv = self._get_dbv self.ls = self._ls + self.canonical = self._canonical + self.dcanonical = self._dcanonical def __repr__(self) -> str: return "VFS(%s)" % ( @@ -624,7 +626,7 @@ class VFS(object): vrem = vjoin(self.vpath[len(dbv.vpath) :].lstrip("/"), vrem) return dbv, vrem - def canonical(self, rem: str, resolve: bool = True) -> str: + def _canonical(self, rem: str, resolve: bool = True) -> str: """returns the canonical path (fully-resolved absolute fs path)""" ap = self.realpath if rem: @@ -632,7 +634,7 @@ class VFS(object): return absreal(ap) if resolve else ap - def dcanonical(self, rem: str) -> str: + def _dcanonical(self, rem: str) -> str: """resolves until the final component (filename)""" ap = self.realpath if rem: @@ -641,6 +643,44 @@ class VFS(object): ad, fn = os.path.split(ap) return os.path.join(absreal(ad), fn) + def _canonical_shr(self, rem: str, resolve: bool = True) -> str: + """returns the canonical path (fully-resolved absolute fs path)""" + ap = self.realpath + if rem: + ap += "/" + rem + + rap = absreal(ap) + if self.shr_files: + assert self.shr_src # !rm + vn, rem = self.shr_src + chk = absreal(os.path.join(vn.realpath, rem)) + if chk != rap: + # not the dir itself; assert file allowed + ad, fn = os.path.split(rap) + if chk != ad or fn not in self.shr_files: + return "\n\n" + + return rap if resolve else ap + + def _dcanonical_shr(self, rem: str) -> str: + """resolves until the final component (filename)""" + ap = self.realpath + if rem: + ap += "/" + rem + + ad, fn = os.path.split(ap) + ad = absreal(ad) + if self.shr_files: + assert self.shr_src # !rm + vn, rem = self.shr_src + chk = absreal(os.path.join(vn.realpath, rem)) + if chk != absreal(ap): + # not the dir itself; assert file allowed + if ad != chk or fn not in self.shr_files: + return "\n\n" + + return os.path.join(ad, fn) + def _ls_nope( self, *a, **ka ) -> tuple[str, list[tuple[str, os.stat_result]], dict[str, "VFS"]]: @@ -881,6 +921,15 @@ class VFS(object): return None if "xvol" in self.flags: + self_ap = self.realpath + os.sep + if aps.startswith(self_ap): + vp = aps[len(self_ap) :] + if ANYWIN: + vp = vp.replace(os.sep, "/") + vn2, _ = self._find(vp) + if self == vn2: + return self + all_aps = self.shr_all_aps or self.root.all_aps for vap, vns in all_aps: @@ -967,6 +1016,14 @@ class AuthSrv(object): self.indent = "" self.is_lxc = args.c == ["/z/initcfg"] + self._vf0b = { + "tcolor": self.args.tcolor, + "du_iwho": self.args.du_iwho, + "shr_who": self.args.shr_who if self.args.shr else "no", + } + self._vf0 = self._vf0b.copy() + self._vf0["d2d"] = True + # fwd-decl self.vfs = VFS(log_func, "", "", "", AXS(), {}) self.acct: dict[str, str] = {} # uname->pw @@ -1005,7 +1062,10 @@ class AuthSrv(object): yield prev, True def vf0(self): - return {"d2d": True, "tcolor": self.args.tcolor} + return self._vf0.copy() + + def vf0b(self): + return self._vf0b.copy() def idp_checkin( self, broker: Optional["BrokerCli"], uname: str, gname: str @@ -1099,6 +1159,9 @@ class AuthSrv(object): if rejected: continue + if gn == self.args.grp_all: + gn = "" + # if ap/vp has a user/group placeholder, make sure to keep # track so the same user/group is mapped when setting perms; # otherwise clear un/gn to indicate it's a regular volume @@ -1208,6 +1271,7 @@ class AuthSrv(object): self.load_idp_db(bool(self.idp_accs)) ret = {un: gns[:] for un, gns in self.idp_accs.items()} ret.update({zs: [""] for zs in acct if zs not in ret}) + grps[self.args.grp_all] = list(ret.keys()) for gn, uns in grps.items(): for un in uns: try: @@ -1315,6 +1379,10 @@ class AuthSrv(object): zt = split_cfg_ln(ln) for zs, za in zt.items(): zs = zs.lstrip("-") + if "=" in zs: + t = "WARNING: found an option named [%s] in your [global] config; did you mean to say [%s: %s] instead?" + zs1, zs2 = zs.split("=", 1) + self.log(t % (zs, zs1, zs2), 3) if za is True: self._e("โ””โ”€argument [{}]".format(zs)) else: @@ -1324,6 +1392,10 @@ class AuthSrv(object): if cat == cata: try: u, p = [zs.strip() for zs in ln.split(":", 1)] + if "=" in u and not p: + t = "WARNING: found username [%s] in your [accounts] config; did you mean to say [%s: %s] instead?" + zs1, zs2 = u.split("=", 1) + self.log(t % (u, zs1, zs2), 3) self._l(ln, 5, "account [{}], password [{}]".format(u, p)) acct[u] = p except: @@ -1394,6 +1466,10 @@ class AuthSrv(object): zd = split_cfg_ln(ln) fstr = "" for sk, sv in zd.items(): + if "=" in sk: + t = "WARNING: found a volflag named [%s] in your config; did you mean to say [%s: %s] instead?" + zs1, zs2 = sk.split("=", 1) + self.log(t % (sk, zs1, zs2), 3) bad = re.sub(r"[a-z0-9_-]", "", sk).lstrip("-") if bad: err = "bad characters [{}] in volflag name [{}]; " @@ -1634,6 +1710,7 @@ class AuthSrv(object): # accept both , and : as separators between usernames zs1, zs2 = x.replace("=", ":").split(":", 1) grps[zs1] = zs2.replace(":", ",").split(",") + grps[zs1] = [x.strip() for x in grps[zs1]] except: t = '\n invalid value "{}" for argument --grp, must be groupname:username1,username2,...' raise Exception(t.format(x)) @@ -1685,6 +1762,10 @@ class AuthSrv(object): self.log("\n{0}\n{1}{0}".format(t, "\n".join(slns))) raise + self.args.have_idp_hdrs = bool(self.args.idp_h_usr or self.args.idp_hm_usr) + self.args.have_ipu_or_ipr = bool(self.args.ipu or self.args.ipr) + self.setup_auth_ord() + self.setup_pwhash(acct) defpw = acct.copy() self.setup_chpw(acct) @@ -1697,7 +1778,7 @@ class AuthSrv(object): mount = cased - if not mount and not self.args.idp_h_usr: + if not mount and not self.args.have_idp_hdrs: # -h says our defaults are CWD at root and read/write for everyone axs = AXS(["*"], ["*"], None, None) ehint = "" @@ -1721,12 +1802,15 @@ class AuthSrv(object): files = os.listdir(E.cfg) except: files = [] - hits = [x for x in files if x.lower().endswith(".conf")] + hits = [ + x + for x in files + if x.lower().endswith(".conf") and not x.startswith(".") + ] if hits: t = "Hint: Found some config files in [%s], but these were not automatically loaded because they are in the wrong place%s %s\n" self.log(t % (E.cfg, ehint, ", ".join(hits)), 3) - zvf = {"tcolor": self.args.tcolor} - vfs = VFS(self.log_func, absreal("."), "", "", axs, zvf) + vfs = VFS(self.log_func, absreal("."), "", "", axs, self.vf0b()) if not axs.uread: self.badcfg1 = True elif "" not in mount: @@ -1870,7 +1954,7 @@ class AuthSrv(object): if missing_users: zs = ", ".join(k for k in sorted(missing_users)) - if self.args.idp_h_usr: + if self.args.have_idp_hdrs: t = "the following users are unknown, and assumed to come from IdP: " self.log(t + zs, c=6) else: @@ -1881,6 +1965,16 @@ class AuthSrv(object): if LEELOO_DALLAS in all_users: raise Exception("sorry, reserved username: " + LEELOO_DALLAS) + zsl = [] + for usr in list(acct)[:]: + zs = acct[usr].strip() + if not zs: + zs = ub64enc(os.urandom(48)).decode("ascii") + zsl.append(usr) + acct[usr] = zs + if zsl: + self.log("generated random passwords for users %r" % (zsl,), 6) + seenpwds = {} for usr, pwd in acct.items(): if pwd in seenpwds: @@ -2201,12 +2295,12 @@ class AuthSrv(object): if vf not in vol.flags: vol.flags[vf] = getattr(self.args, ga) - zs = "forget_ip gid nrand tail_who u2abort u2ow uid ups_who zip_who" + zs = "forget_ip gid nrand tail_who 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]) - zs = "convt tail_fd tail_rate tail_tmax" + zs = "aconvt convt tail_fd tail_rate tail_tmax" for k in zs.split(): if k in vol.flags: vol.flags[k] = float(vol.flags[k]) @@ -2250,6 +2344,11 @@ class AuthSrv(object): vol.lim.uid = vol.flags["uid"] vol.lim.gid = vol.flags["gid"] + vol.flags["du_iwho"] = n_du_who(vol.flags["du_who"]) + + if not enshare: + vol.flags["shr_who"] = "no" + if vol.flags.get("og"): self.args.uqe = True @@ -2537,7 +2636,7 @@ class AuthSrv(object): if not self.args.no_voldump: self.log(t) - if have_e2d or self.args.idp_h_usr: + if have_e2d or self.args.have_idp_hdrs: t = self.chk_sqlite_threadsafe() if t: self.log("\n\033[{}\033[0m\n".format(t)) @@ -2692,6 +2791,8 @@ class AuthSrv(object): shn.shr_files = set(fns) shn.ls = shn._ls_shr + shn.canonical = shn._canonical_shr + shn.dcanonical = shn._dcanonical_shr else: shn.ls = shn._ls @@ -2756,6 +2857,7 @@ class AuthSrv(object): "dcrop": vf["crop"], "dth3x": vf["th3x"], "u2ts": vf["u2ts"], + "shr_who": vf["shr_who"], "frand": bool(vf.get("rand")), "lifetime": vf.get("lifetime") or 0, "unlist": vf.get("unlist") or "", @@ -2764,16 +2866,19 @@ class AuthSrv(object): js_htm = { "SPINNER": self.args.spinner, "s_name": self.args.bname, + "idp_login": self.args.idp_login, "have_up2k_idx": "e2d" in vf, "have_acode": not self.args.no_acode, "have_c2flac": self.args.allow_flac, "have_c2wav": self.args.allow_wav, "have_shr": self.args.shr, + "shr_who": vf["shr_who"], "have_zip": not self.args.no_zip, "have_mv": not self.args.no_mv, "have_del": not self.args.no_del, "have_unpost": int(self.args.unpost), - "have_emp": self.args.emp, + "have_emp": int(self.args.emp), + "md_no_br": int(vf.get("md_no_br") or 0), "ext_th": vf.get("ext_th_d") or {}, "sb_md": "" if "no_sb_md" in vf else (vf.get("md_sbf") or "y"), "sba_md": vf.get("md_sba") or "", @@ -2824,10 +2929,22 @@ class AuthSrv(object): zs = str(vol.flags.get("tcolor") or self.args.tcolor) vol.flags["tcolor"] = zs.lstrip("#") + def setup_auth_ord(self) -> None: + ao = [x.strip() for x in self.args.auth_ord.split(",")] + if "idp" in ao: + zi = ao.index("idp") + ao = ao[:zi] + ["idp-hm", "idp-h"] + ao[zi:] + zsl = "pw idp-h idp-hm ipu".split() + pw, h, hm, ipu = [ao.index(x) if x in ao else 99 for x in zsl] + self.args.ao_idp_before_pw = min(h, hm) < pw + self.args.ao_h_before_hm = h < hm + self.args.ao_ipu_wins = ipu == 0 + self.args.ao_have_pw = pw < 99 or not self.args.have_idp_hdrs + def load_idp_db(self, quiet=False) -> None: # mutex me level = self.args.idp_store - if level < 2 or not self.args.idp_h_usr: + if level < 2 or not self.args.have_idp_hdrs: return assert sqlite3 # type: ignore # !rm @@ -2884,7 +3001,7 @@ class AuthSrv(object): n = [] q = "insert into us values (?,?,?)" accs = list(self.acct) - if self.args.idp_h_usr and self.args.idp_cookie: + if self.args.have_idp_hdrs and self.args.idp_cookie: accs.extend(self.idp_accs.keys()) for uname in accs: if uname not in ases: @@ -3416,6 +3533,30 @@ class AuthSrv(object): self.log("generated config:\n\n" + "\n".join(ret)) +def n_du_who(s: str) -> int: + if s == "all": + return 9 + if s == "auth": + return 7 + if s == "w": + return 5 + if s == "rw": + return 4 + if s == "a": + return 3 + return 0 + + +def n_ver_who(s: str) -> int: + if s == "all": + return 9 + if s == "auth": + return 6 + if s == "a": + return 3 + return 0 + + def split_cfg_ln(ln: str) -> dict[str, Any]: # "a, b, c: 3" => {a:true, b:true, c:3} ret = {} @@ -3448,7 +3589,9 @@ def expand_config_file( if os.path.isdir(fp): names = list(sorted(os.listdir(fp))) - cnames = [x for x in names if x.lower().endswith(".conf")] + cnames = [ + x for x in names if x.lower().endswith(".conf") and not x.startswith(".") + ] if not cnames: t = "warning: tried to read config-files from folder '%s' but it does not contain any " if names: diff --git a/copyparty/bos/bos.py b/copyparty/bos/bos.py index 6c876e04..408244b9 100644 --- a/copyparty/bos/bos.py +++ b/copyparty/bos/bos.py @@ -2,18 +2,22 @@ from __future__ import print_function, unicode_literals import os +import time from ..util import SYMTIME, fsdec, fsenc from . import path as path if True: # pylint: disable=using-constant-test - from typing import Any, Optional + from typing import Any, Optional, Union + + from ..util import NamedLogger MKD_755 = {"chmod_d": 0o755} MKD_700 = {"chmod_d": 0o700} +UTIME_CLAMPS = ((max, -2147483647), (max, 1), (min, 4294967294), (min, 2147483646)) -_ = (path, MKD_755, MKD_700) -__all__ = ["path", "MKD_755", "MKD_700"] +_ = (path, MKD_755, MKD_700, UTIME_CLAMPS) +__all__ = ["path", "MKD_755", "MKD_700", "UTIME_CLAMPS"] # grep -hRiE '(^|[^a-zA-Z_\.-])os\.' . | gsed -r 's/ /\n/g;s/\(/(\n/g' | grep -hRiE '(^|[^a-zA-Z_\.-])os\.' | sort | uniq -c # printf 'os\.(%s)' "$(grep ^def bos/__init__.py | gsed -r 's/^def //;s/\(.*//' | tr '\n' '|' | gsed -r 's/.$//')" @@ -99,6 +103,40 @@ def utime( return os.utime(fsenc(p), times) +def utime_c( + log: Union["NamedLogger", Any], p: str, ts: int, follow_symlinks: bool = True, throw: bool = False +) -> Optional[int]: + clamp = 0 + ov = ts + bp = fsenc(p) + now = int(time.time()) + while True: + try: + if SYMTIME: + os.utime(bp, (now, ts), follow_symlinks=follow_symlinks) + else: + os.utime(bp, (now, ts)) + if clamp: + t = "filesystem rejected utime(%r); clamped %s to %s" + log(t % (p, ov, ts)) + return ts + except Exception as ex: + pv = ts + while clamp < len(UTIME_CLAMPS): + fun, cv = UTIME_CLAMPS[clamp] + ts = fun(ts, cv) + clamp += 1 + if ts != pv: + break + if clamp >= len(UTIME_CLAMPS): + if throw: + raise + else: + t = "could not utime(%r) to %s; %s, %r" + log(t % (p, ov, ex, ex)) + return None + + if hasattr(os, "lstat"): def lstat(p: str) -> os.stat_result: diff --git a/copyparty/broker_util.py b/copyparty/broker_util.py index ea987200..10c778dc 100644 --- a/copyparty/broker_util.py +++ b/copyparty/broker_util.py @@ -2,7 +2,6 @@ from __future__ import print_function, unicode_literals import argparse -import traceback from queue import Queue diff --git a/copyparty/cert.py b/copyparty/cert.py index a22d2857..49fa9ea2 100644 --- a/copyparty/cert.py +++ b/copyparty/cert.py @@ -130,6 +130,7 @@ def _gen_srv(log: "RootLogger", args, netdevs: dict[str, Netdev]): nlog: "NamedLogger" = lambda msg, c=0: log("cert-gen-srv", msg, c) names = args.crt_ns.split(",") if args.crt_ns else [] + names = [x.strip() for x in names] if not args.crt_exact: for n in names[:]: names.append("*.{}".format(n)) diff --git a/copyparty/cfg.py b/copyparty/cfg.py index 8a07f956..2fcb3d4e 100644 --- a/copyparty/cfg.py +++ b/copyparty/cfg.py @@ -44,6 +44,7 @@ def vf_bmap() -> dict[str, str]: "gsel", "hardlink", "magic", + "md_no_br", "no_db_ip", "no_sb_md", "no_sb_lg", @@ -68,6 +69,7 @@ def vf_bmap() -> dict[str, str]: def vf_vmap() -> dict[str, str]: """argv-to-volflag: simple values""" ret = { + "ac_convt": "aconvt", "no_hash": "nohash", "no_idx": "noidx", "re_maxage": "scan", @@ -82,6 +84,7 @@ def vf_vmap() -> dict[str, str]: "chmod_d", "chmod_f", "dbd", + "du_who", "forget_ip", "hsortn", "html_head", @@ -105,18 +108,21 @@ def vf_vmap() -> dict[str, str]: "put_name", "mv_retry", "rm_retry", + "shr_who", "sort", "tail_fd", "tail_rate", "tail_tmax", "tail_who", "tcolor", + "th_spec_p", "txt_eol", "unlist", "u2abort", "u2ts", "uid", "gid", + "unp_who", "ups_who", "zip_who", "zipmaxn", @@ -260,7 +266,9 @@ flagcats = { "thsize": "thumbnail res; WxH", "crop": "center-cropping (y/n/fy/fn)", "th3x": "3x resolution (y/n/fy/fn)", - "convt": "conversion timeout in seconds", + "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", "ext_th=s=/b.png": "use /b.png as thumbnail for file-extension s", }, "handlers\n(better explained in --help-handlers)": { @@ -290,6 +298,7 @@ flagcats = { "html_head=TXT": "includes TXT in the <head>, or @PATH for file at PATH", "tcolor=#fc0": "theme color (a hint for webbrowsers, discord, etc.)", "nodirsz": "don't show total folder size", + "du_who=all": "show disk-usage info to everyone", "robots": "allows indexing by search engines (default)", "norobots": "kindly asks search engines to leave", "unlistcr": "don't list read-access in controlpanel", @@ -319,6 +328,7 @@ flagcats = { "og_ua": "if defined: only send OG html if useragent matches this regex", }, "textfiles": { + "md_no_br": "newline only on double-newline or two tailing spaces", "md_hist": "where to put markdown backups; s=subfolder, v=volHist, n=nope", "exp": "enable textfile expansion; see --help-exp", "exp_md": "placeholders to expand in markdown files; see --help", @@ -341,6 +351,8 @@ flagcats = { "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", "ups_who=2": "restrict viewing the list of recent uploads", "zip_who=2": "restrict access to download-as-zip/tar", "zipmaxn=9k": "reject download-as-zip if more than 9000 files", diff --git a/copyparty/dxml.py b/copyparty/dxml.py index b49f060b..6271da8e 100644 --- a/copyparty/dxml.py +++ b/copyparty/dxml.py @@ -65,6 +65,9 @@ DXMLParser = _DXMLParser def parse_xml(txt: str) -> ET.Element: + """ + Parse XML into an xml.etree.ElementTree.Element while defusing some unsafe parts. + """ parser = DXMLParser() parser.feed(txt) return parser.close() # type: ignore diff --git a/copyparty/ftpd.py b/copyparty/ftpd.py index 004fb492..ab8db97f 100644 --- a/copyparty/ftpd.py +++ b/copyparty/ftpd.py @@ -68,13 +68,13 @@ class FtpAuth(DummyAuthorizer): if ip.startswith("::ffff:"): ip = ip[7:] - ip = ipnorm(ip) + ipn = ipnorm(ip) bans = self.hub.bans - if ip in bans: - rt = bans[ip] - time.time() + if ipn in bans: + rt = bans[ipn] - time.time() if rt < 0: logging.info("client unbanned") - del bans[ip] + del bans[ipn] else: raise AuthenticationFailed("banned") @@ -96,6 +96,10 @@ class FtpAuth(DummyAuthorizer): if args.ipu and uname == "*": uname = args.ipu_iu[args.ipu_nm.map(ip)] + if args.ipr and uname in args.ipr_u: + if not args.ipr_u[uname].map(ip): + logging.warning("username [%s] rejected by --ipr", uname) + uname = "*" if not uname or not (asrv.vfs.aread.get(uname) or asrv.vfs.awrite.get(uname)): g = self.hub.gpwd @@ -148,10 +152,6 @@ class FtpFs(AbstractedFS): self.cwd = "/" # pyftpdlib convention of leading slash self.root = "/var/lib/empty" - self.can_read = self.can_write = self.can_move = False - self.can_delete = self.can_get = self.can_upget = False - self.can_admin = self.can_dot = False - self.listdirinfo = self.listdir self.chdir(".") @@ -214,7 +214,7 @@ class FtpFs(AbstractedFS): m: bool = False, d: bool = False, ) -> tuple[str, VFS, str]: - return self.v2a(os.path.join(self.cwd, vpath), r, w, m, d) + return self.v2a(join(self.cwd, vpath), r, w, m, d) def ftp2fs(self, ftppath: str) -> str: # return self.v2a(ftppath) @@ -285,21 +285,14 @@ class FtpFs(AbstractedFS): # returning 550 is library-default and suitable raise FSE("No such file or directory") - avfs = vfs.chk_ap(ap, st) - if not avfs: - raise FSE("Permission denied", 1) + if vfs.realpath: + avfs = vfs.chk_ap(ap, st) + if not avfs: + raise FSE("Permission denied", 1) + else: + avfs = vfs self.cwd = nwd - ( - self.can_read, - self.can_write, - self.can_move, - self.can_delete, - self.can_get, - self.can_upget, - self.can_admin, - self.can_dot, - ) = avfs.can_access("", self.h.uname) def mkdir(self, path: str) -> None: ap, vfs, _ = self.rv2a(path, w=True) @@ -322,7 +315,7 @@ class FtpFs(AbstractedFS): vfs_ls = [x[0] for x in vfs_ls1] vfs_ls.extend(vfs_virt.keys()) - if not self.can_dot: + if self.uname not in vfs.axs.udot: vfs_ls = exclude_dotfiles(vfs_ls) vfs_ls.sort() @@ -370,16 +363,13 @@ class FtpFs(AbstractedFS): raise FSE(str(ex)) def rename(self, src: str, dst: str) -> None: - if not self.can_move: - raise FSE("Not allowed for user " + self.h.uname) - if self.args.no_mv: raise FSE("The rename/move feature is disabled in server config") svp = join(self.cwd, src).lstrip("/") dvp = join(self.cwd, dst).lstrip("/") try: - self.hub.up2k.handle_mv(self.uname, self.h.cli_ip, svp, dvp) + self.hub.up2k.handle_mv("", self.uname, self.h.cli_ip, svp, dvp) except Exception as ex: raise FSE(str(ex)) @@ -403,7 +393,7 @@ class FtpFs(AbstractedFS): def utime(self, path: str, timeval: float) -> None: ap = self.rv2a(path, w=True)[0] - return bos.utime(ap, (timeval, timeval)) + bos.utime_c(logging.warning, ap, int(timeval), False) def lstat(self, path: str) -> os.stat_result: ap = self.rv2a(path)[0] @@ -492,7 +482,11 @@ class FtpHandler(FTPHandler): def ftp_STOR(self, file: str, mode: str = "w") -> Any: # Optional[str] vp = join(self.fs.cwd, file).lstrip("/") - ap, vfs, rem = self.fs.v2a(vp, w=True) + try: + ap, vfs, rem = self.fs.v2a(vp, w=True) + except Exception as ex: + self.respond("550 %s" % (ex,), logging.info) + return self.vfs_map[ap] = vp xbu = vfs.flags.get("xbu") if xbu and not runhook( diff --git a/copyparty/httpcli.py b/copyparty/httpcli.py index 402d623f..26b85a18 100644 --- a/copyparty/httpcli.py +++ b/copyparty/httpcli.py @@ -12,7 +12,6 @@ import random import re import socket import stat -import string import sys import threading # typechk import time @@ -31,7 +30,7 @@ try: except: pass -from .__init__ import ANYWIN, PY2, RES, TYPE_CHECKING, EnvParams, unicode +from .__init__ import ANYWIN, RES, TYPE_CHECKING, EnvParams, unicode from .__version__ import S_VERSION from .authsrv import LEELOO_DALLAS, VFS # typechk from .bos import bos @@ -66,6 +65,7 @@ from .util import ( exclude_dotfiles, formatdate, fsenc, + gen_content_disposition, gen_filekey, gen_filekey_dbg, gencookie, @@ -394,10 +394,10 @@ class HttpCli(object): zsl = [ " rproxy: %d if this client's IP-address is [%s]" % (-1 - zd, zs.strip()) - for zd, zs in enumerate(zsl) + for zd, zs in enumerate(zsl[::-1]) ] - 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! 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,) + 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) pip = self.conn.addr[0] @@ -562,19 +562,23 @@ class HttpCli(object): zso = self.headers.get("cookie") if zso: - if len(zso) > 8192: + if len(zso) > self.args.cookie_cmax: self.loud_reply("cookie header too big", status=400) return False zsll = [x.split("=", 1) for x in zso.split(";") if "=" in x] cookies = {k.strip(): unescape_cookie(zs) for k, zs in zsll} - cookie_pw = cookies.get("cppws") or cookies.get("cppwd") or "" + cookie_pw = cookies.get("cppws" if self.is_https else "cppwd") or "" if "b" in cookies and "b" not in uparam: uparam["b"] = cookies["b"] + if len(cookies) > self.args.cookie_nmax: + self.loud_reply("too many cookies", status=400) else: cookies = {} cookie_pw = "" - if len(uparam) > 10 or len(cookies) > 50: + if len(uparam) > 12: + t = "http-request rejected; num.params: %d %r" + self.log(t % (len(uparam), self.req), 3) self.loud_reply("u wot m8", status=400) return False @@ -620,8 +624,24 @@ class HttpCli(object): or "*" ) - if self.args.idp_h_usr: - idp_usr = self.headers.get(self.args.idp_h_usr) or "" + if self.args.have_idp_hdrs and ( + self.uname == "*" or self.args.ao_idp_before_pw + ): + idp_usr = "" + if self.args.idp_hm_usr: + for hn, hmv in self.args.idp_hm_usr_p.items(): + zs = self.headers.get(hn) + if zs: + for zs1, zs2 in hmv.items(): + if zs == zs1: + idp_usr = zs2 + break + if idp_usr: + break + for hn in self.args.idp_h_usr: + if idp_usr and not self.args.ao_h_before_hm: + break + idp_usr = self.headers.get(hn) or idp_usr if idp_usr: idp_grp = ( self.headers.get(self.args.idp_h_grp) or "" @@ -670,15 +690,24 @@ class HttpCli(object): if idp_usr in self.asrv.vfs.aread: self.pw = "" self.uname = idp_usr - self.html_head += "<script>var is_idp=1</script>\n" + if self.args.ao_have_pw or self.args.idp_logout: + self.html_head += "<script>var is_idp=1</script>\n" + else: + self.html_head += "<script>var is_idp=2</script>\n" zs = self.asrv.ases.get(idp_usr) if zs: self.set_idp_cookie(zs) else: self.log("unknown username: %r" % (idp_usr,), 1) - if self.args.ipu and self.uname == "*": - self.uname = self.conn.ipu_iu[self.conn.ipu_nm.map(self.ip)] + if self.args.have_ipu_or_ipr: + if self.args.ipu and (self.uname == "*" or self.args.ao_ipu_wins): + self.uname = self.conn.ipu_iu[self.conn.ipu_nm.map(self.ip)] + ipr = self.conn.hsrv.ipr + if ipr and self.uname in ipr: + if not ipr[self.uname].map(self.ip): + self.log("username [%s] rejected by --ipr" % (self.uname,), 3) + self.uname = "*" self.rvol = self.asrv.vfs.aread[self.uname] self.wvol = self.asrv.vfs.awrite[self.uname] @@ -700,7 +729,7 @@ class HttpCli(object): cookies["b"] = "" vn, rem = self.asrv.vfs.get(self.vpath, self.uname, False, False) - if "xdev" in vn.flags or "xvol" in vn.flags: + if vn.realpath and ("xdev" in vn.flags or "xvol" in vn.flags): ap = vn.canonical(rem) avn = vn.chk_ap(ap) else: @@ -790,6 +819,15 @@ class HttpCli(object): 6 if em.startswith("client d/c ") else 3, ) + if self.hint and self.hint.startswith("<xml> "): + if self.args.log_badxml: + t = "invalid XML received from client: %r" + self.log(t % (self.hint[6:],), 6) + else: + t = "received invalid XML from client; enable --log-badxml to see the whole XML in the log" + self.log(t, 6) + self.hint = "" + msg = "%s\r\nURL: %s\r\n" % (em, self.vpath) if self.hint: msg += "hint: %s\r\n" % (self.hint,) @@ -1217,7 +1255,7 @@ class HttpCli(object): res_path = "web/" + self.vpath[5:] if res_path in RES: - ap = os.path.join(self.E.mod, res_path) + ap = self.E.mod_ + res_path if bos.path.exists(ap) or bos.path.exists(ap + ".gz"): return self.tx_file(ap) else: @@ -1506,7 +1544,9 @@ class HttpCli(object): if not rbuf or len(buf) >= 32768: break - xroot = parse_xml(buf.decode(enc, "replace")) + sbuf = buf.decode(enc, "replace") + self.hint = "<xml> " + sbuf + xroot = parse_xml(sbuf) xtag = next((x for x in xroot if x.tag.split("}")[-1] == "prop"), None) if xtag is not None: props = set([y.tag.split("}")[-1] for y in xtag]) @@ -1592,13 +1632,24 @@ class HttpCli(object): self.log("inaccessible: %r" % ("/" + self.vpath,)) raise Pebkac(401, "authenticate") - if "quota-available-bytes" in props and not self.args.nid: + zi = vn.flags["du_iwho"] if "quota-available-bytes" in props else 0 + if zi and ( + zi == 9 + or (zi == 7 and self.uname != "*") + or (zi == 5 and self.can_write) + or (zi == 4 and self.can_write and self.can_read) + or (zi == 3 and self.can_admin) + ): bfree, btot, _ = get_df(vn.realpath, False) if btot: df = { "quota-available-bytes": str(bfree), "quota-used-bytes": str(btot - bfree), } + if "quotaused" in props: # macos finder crazytalk + df["quotaused"] = df["quota-used-bytes"] + if "quota" in props: + df["quota"] = df["quota-available-bytes"] # idk, makes it happy else: df = {} else: @@ -1708,6 +1759,7 @@ class HttpCli(object): uenc = enc.upper() txt = buf.decode(enc, "replace") + self.hint = "<xml> " + txt ET.register_namespace("D", "DAV:") xroot = mkenod("D:orz") xroot.insert(0, parse_xml(txt)) @@ -1768,6 +1820,7 @@ class HttpCli(object): uenc = enc.upper() txt = buf.decode(enc, "replace") + self.hint = "<xml> " + txt ET.register_namespace("D", "DAV:") lk = parse_xml(txt) assert lk.tag == "{DAV:}lockinfo" @@ -1983,6 +2036,9 @@ class HttpCli(object): if "eshare" in self.uparam: return self.handle_eshare() + if "fs_abrt" in self.uparam: + return self.handle_fs_abrt() + if "application/octet-stream" in ctype: return self.handle_post_binary() @@ -2280,12 +2336,7 @@ class HttpCli(object): at = mt = time.time() - lifetime cli_mt = self.headers.get("x-oc-mtime") if cli_mt: - try: - mt = int(cli_mt) - times = (int(time.time()), mt) - bos.utime(path, times, False) - except: - pass + bos.utime_c(self.log, path, int(cli_mt), False) if nameless and "magic" in vfs.flags: try: @@ -3007,7 +3058,7 @@ class HttpCli(object): self.asrv.forget_session(self.conn.hsrv.broker, self.uname) self.get_pwd_cookie("x") - dst = self.args.SRS + "?h" + dst = self.args.idp_logout or (self.args.SRS + "?h") h2 = '<a href="' + dst + '">continue</a>' html = self.j2s("msg", h1="ok bye", h2=h2, redir=dst) self.reply(html.encode("utf-8")) @@ -3020,6 +3071,11 @@ class HttpCli(object): uname = self.asrv.iacct.get(hpwd) if uname: pwd = self.asrv.ases.get(uname) or pwd + if uname and self.conn.hsrv.ipr: + znm = self.conn.hsrv.ipr.get(uname) + if znm and not znm.map(self.ip): + self.log("username [%s] rejected by --ipr" % (self.uname,), 3) + uname = "" if uname: msg = "hi " + uname dur = int(60 * 60 * self.args.logout) @@ -3363,8 +3419,6 @@ class HttpCli(object): sz, sha_hex, sha_b64 = copier( p_data, f, hasher, max_sz, self.args.s_wr_slp ) - if sz == 0: - raise Pebkac(400, "empty files in post") finally: f.close() @@ -3982,6 +4036,13 @@ class HttpCli(object): if not editions: return self.tx_404() + # + # force download + + if "dl" in self.ouparam: + cdis = gen_content_disposition(os.path.basename(req_path)) + self.out_headers["Content-Disposition"] = cdis + # # if-modified @@ -4150,6 +4211,13 @@ class HttpCli(object): if not editions: return self.tx_404() + # + # force download + + if "dl" in self.ouparam: + cdis = gen_content_disposition(os.path.basename(req_path)) + self.out_headers["Content-Disposition"] = cdis + # # if-modified @@ -4698,24 +4766,7 @@ class HttpCli(object): if maxn < nf: raise Pebkac(400, t) - safe = (string.ascii_letters + string.digits).replace("%", "") - afn = "".join([x if x in safe.replace('"', "") else "_" for x in fn]) - bascii = unicode(safe).encode("utf-8") - zb = fn.encode("utf-8", "xmlcharrefreplace") - if not PY2: - zbl = [ - chr(x).encode("utf-8") - if x in bascii - else "%{:02x}".format(x).encode("ascii") - for x in zb - ] - else: - zbl = [unicode(x) if x in bascii else "%{:02x}".format(ord(x)) for x in zb] - - ufn = b"".join(zbl).decode("ascii") - - cdis = "attachment; filename=\"{}.{}\"; filename*=UTF-8''{}.{}" - cdis = cdis.format(afn, ext, ufn, ext) + cdis = gen_content_disposition("%s.%s" % (fn, ext)) self.log(repr(cdis)) self.send_headers(None, mime=mime, headers={"Content-Disposition": cdis}) @@ -4902,7 +4953,8 @@ class HttpCli(object): "lastmod": int(ts_md * 1000), "lang": self.args.lang, "favico": self.args.favico, - "have_emp": self.args.emp, + "have_emp": int(self.args.emp), + "md_no_br": int(vn.flags.get("md_no_br") or 0), "md_chk_rate": self.args.mcr, "md": boundary, "arg_base": arg_base, @@ -4970,10 +5022,20 @@ class HttpCli(object): else: rip = host + defpw = "dave:hunter2" if self.args.usernames else "hunter2" + vp = (self.uparam["hc"] or "").lstrip("/") - pw = self.pw or "hunter2" + pw = self.ouparam.get("pw") or defpw if pw in self.asrv.sesa: - pw = "hunter2" + pw = defpw + + unpw = pw + try: + un, pw = unpw.split(":") + except: + un = "" + if self.args.usernames: + un = "dave" html = self.j2s( "svcs", @@ -4987,7 +5049,10 @@ class HttpCli(object): host=html_sh_esc(host), hport=html_sh_esc(hport), aname=aname, + b_un=("<b>%s</b>" % (html_sh_esc(un),)) if un else "k", + un=html_sh_esc(un), pw=html_sh_esc(pw), + unpw=html_sh_esc(unpw), ) self.reply(html.encode("utf-8")) return True @@ -5112,6 +5177,11 @@ class HttpCli(object): elif nre: re_btn = "&re=%s" % (nre,) + zi = self.args.ver_iwho + show_ver = zi and ( + zi == 9 or (zi == 6 and self.uname != "*") or (zi == 3 and avol) + ) + html = self.j2s( "splash", this=self, @@ -5134,7 +5204,7 @@ class HttpCli(object): no304=self.no304(), k304vis=self.args.k304 > 0, no304vis=self.args.no304 > 0, - ver=S_VERSION if self.args.ver 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, ) @@ -5349,8 +5419,9 @@ class HttpCli(object): if dk_sz and fsroot: kdirs = [] + fsroot_ = os.path.join(fsroot, "") for dn in dirs: - ap = os.path.join(fsroot, dn) + ap = fsroot_ + dn zs = self.gen_fk(2, self.args.dk_salt, ap, 0, 0)[:dk_sz] kdirs.append(dn + "?k=" + zs) dirs = kdirs @@ -5470,6 +5541,10 @@ class HttpCli(object): and ("*" in x.axs.uwrite or self.uname in x.axs.uwrite or x == shr_dbv) ] + q = "" + qp = (0,) + q_c = -1 + for vol in allvols: cur = idx.get_cur(vol) if not cur: @@ -5477,17 +5552,31 @@ class HttpCli(object): nfk, fk_alg = fk_vols.get(vol) or (0, 0) + zi = vol.flags["unp_who"] + if q_c != zi: + q_c = zi + q = "select sz, rd, fn, at from up where " + if zi == 1: + q += "ip=? and un=?" + qp = (self.ip, self.uname, lim) + elif zi == 2: + q += "ip=?" + qp = (self.ip, lim) + if zi == 3: + q += "un=?" + qp = (self.uname, lim) + q += " and at>? order by at desc" + n = 2000 - q = "select sz, rd, fn, at from up where ip=? and at>? order by at desc" - for sz, rd, fn, at in cur.execute(q, (self.ip, lim)): + for sz, rd, fn, at in cur.execute(q, qp): vp = "/" + "/".join(x for x in [vol.vpath, rd, fn] if x) - if nfi == 0 or (nfi == 1 and vfi in vp): + if nfi == 0 or (nfi == 1 and vfi in vp.lower()): pass elif nfi == 2: - if not vp.startswith(vfi): + if not vp.lower().startswith(vfi): continue elif nfi == 3: - if not vp.endswith(vfi): + if not vp.lower().endswith(vfi): continue else: continue @@ -5604,16 +5693,16 @@ class HttpCli(object): continue n = 1000 - q = "select sz, rd, fn, ip, at from up where at>0 order by at desc" - for sz, rd, fn, ip, at in cur.execute(q): + q = "select sz, rd, fn, ip, at, un from up where at>0 order by at desc" + for sz, rd, fn, ip, at, un in cur.execute(q): vp = "/" + "/".join(x for x in [vol.vpath, rd, fn] if x) - if nfi == 0 or (nfi == 1 and vfi in vp): + if nfi == 0 or (nfi == 1 and vfi in vp.lower()): pass elif nfi == 2: - if not vp.startswith(vfi): + if not vp.lower().startswith(vfi): continue elif nfi == 3: - if not vp.endswith(vfi): + if not vp.lower().endswith(vfi): continue else: continue @@ -5626,6 +5715,7 @@ class HttpCli(object): "sz": sz, "ip": ip, "at": at, + "un": un, "nfk": nfk, "adm": adm, } @@ -5670,12 +5760,16 @@ class HttpCli(object): adm = rv.pop("adm") if not adm: rv["ip"] = "(You)" if rv["ip"] == self.ip else "(?)" + if rv["un"] not in ("*", self.uname): + rv["un"] = "(?)" else: for rv in ret: adm = rv.pop("adm") if not adm: rv["ip"] = "(You)" if rv["ip"] == self.ip else "(?)" rv["at"] = 0 + if rv["un"] not in ("*", self.uname): + rv["un"] = "(?)" if self.is_vproxied: for v in ret: @@ -5859,6 +5953,14 @@ class HttpCli(object): except: raise Pebkac(400, "you dont have all the perms you tried to grant") + zs = vfs.flags["shr_who"] + if zs == "auth" and self.uname != "*": + pass + elif zs == "a" and self.uname in vfs.axs.uadmin: + pass + else: + raise Pebkac(400, "you dont have perms to create shares from this volume") + ap, reals, _ = vfs.ls( rem, self.uname, not self.args.no_scandir, [[s_rd, s_wr, s_mv, s_del]] ) @@ -5954,7 +6056,9 @@ class HttpCli(object): self.asrv.vfs.get(vdst, self.uname, False, True, False, True) wunlink(self.log, dabs, dvn.flags) - x = self.conn.hsrv.broker.ask("up2k.handle_mv", self.uname, self.ip, vsrc, vdst) + x = self.conn.hsrv.broker.ask( + "up2k.handle_mv", self.ouparam.get("akey"), self.uname, self.ip, vsrc, vdst + ) self.loud_reply(x.get(), status=201) return True @@ -5984,10 +6088,21 @@ class HttpCli(object): self.asrv.vfs.get(vdst, self.uname, False, True, False, True) wunlink(self.log, dabs, dvn.flags) - x = self.conn.hsrv.broker.ask("up2k.handle_cp", self.uname, self.ip, vsrc, vdst) + x = self.conn.hsrv.broker.ask( + "up2k.handle_cp", self.ouparam.get("akey"), self.uname, self.ip, vsrc, vdst + ) self.loud_reply(x.get(), status=201) return True + def handle_fs_abrt(self): + if self.args.no_fs_abrt: + t = "aborting an ongoing copy/move is disabled in server config" + raise Pebkac(403, t) + + self.conn.hsrv.broker.say("up2k.handle_fs_abrt", self.uparam["fs_abrt"]) + self.loud_reply("aborting", status=200) + return True + def tx_ls(self, ls: dict[str, Any]) -> bool: dirs = ls["dirs"] files = ls["files"] @@ -6107,16 +6222,13 @@ class HttpCli(object): add_og = "og" in vn.flags if add_og: if "th" in self.uparam or "raw" in self.uparam: - og_ua = add_og = False - elif self.args.og_ua: - og_ua = add_og = self.args.og_ua.search(self.ua) - else: - og_ua = False - add_og = True + add_og = False + elif vn.flags["og_ua"]: + add_og = vn.flags["og_ua"].search(self.ua) og_fn = "" if "v" in self.uparam: - add_og = og_ua = True + add_og = True if "b" in self.uparam: self.out_headers["X-Robots-Tag"] = "noindex, nofollow" @@ -6237,7 +6349,7 @@ class HttpCli(object): is_md = abspath.lower().endswith(".md") if add_og and not is_md: - if og_ua or self.host not in self.headers.get("referer", ""): + if self.host not in self.headers.get("referer", ""): self.vpath, og_fn = vsplit(self.vpath) vpath = self.vpath vn, rem = self.asrv.vfs.get(self.vpath, self.uname, False, False) @@ -6277,7 +6389,14 @@ class HttpCli(object): except: self.log("#wow #whoa") - if not self.args.nid: + zi = vn.flags["du_iwho"] + if zi and ( + zi == 9 + or (zi == 7 and self.uname != "*") + or (zi == 5 and self.can_write) + or (zi == 4 and self.can_write and self.can_read) + or (zi == 3 and self.can_admin) + ): free, total, zs = get_df(abspath, False) if total: h1 = humansize(free or 0) @@ -6584,13 +6703,15 @@ class HttpCli(object): tags = {k: v for k, v in r} if is_admin: - q = "select ip, at from up where rd=? and fn=?" + q = "select ip, at, un from up where rd=? and fn=?" try: - zs1, zs2 = icur.execute(q, erd_efn).fetchone() + zs1, zs2, zs3 = icur.execute(q, erd_efn).fetchone() if zs1: tags["up_ip"] = zs1 if zs2: tags[".up_at"] = zs2 + if zs3: + tags["up_by"] = zs3 except: pass elif add_up_at: @@ -6611,7 +6732,7 @@ class HttpCli(object): lmte = list(mte) if self.can_admin: - lmte.extend(("up_ip", ".up_at")) + lmte.extend(("up_by", "up_ip", ".up_at")) if "nodirsz" not in vf: tagset.add(".files") diff --git a/copyparty/httpsrv.py b/copyparty/httpsrv.py index ddf5e7bc..8e4e4a4e 100644 --- a/copyparty/httpsrv.py +++ b/copyparty/httpsrv.py @@ -70,6 +70,7 @@ from .util import ( build_netmap, has_resource, ipnorm, + load_ipr, load_ipu, load_resource, min_ex, @@ -193,6 +194,11 @@ class HttpSrv(object): else: self.ipu_iu = self.ipu_nm = None + if self.args.ipr: + self.ipr = load_ipr(self.log, self.args.ipr) + else: + self.ipr = None + self.ipa_nm = build_netmap(self.args.ipa) self.xff_nm = build_netmap(self.args.xff_src) self.xff_lan = build_netmap("lan") @@ -565,7 +571,7 @@ class HttpSrv(object): v = self.E.t0 try: - with os.scandir(os.path.join(self.E.mod, "web")) as dh: + with os.scandir(self.E.mod_ + "web") as dh: for fh in dh: inf = fh.stat() v = max(v, inf.st_mtime) diff --git a/copyparty/mdns.py b/copyparty/mdns.py index a9d36fe7..d80e5142 100644 --- a/copyparty/mdns.py +++ b/copyparty/mdns.py @@ -27,7 +27,7 @@ from .stolen.dnslib import ( DNSRecord, set_avahi_379, ) -from .util import CachedSet, Daemon, Netdev, list_ips, min_ex +from .util import IP6_LL, CachedSet, Daemon, Netdev, list_ips, min_ex if TYPE_CHECKING: from .svchub import SvcHub @@ -76,7 +76,8 @@ class MDNS(MCast): if not self.args.zm_nwa_1: set_avahi_379() - zs = self.args.name + ".local." + zs = self.args.zm_fqdn or (self.args.name + ".local") + zs = zs.replace("--name", self.args.name).rstrip(".") + "." zs = zs.encode("ascii", "replace").decode("ascii", "replace") self.hn = "-".join(x for x in zs.split("?") if x) or ( "vault-{}".format(random.randint(1, 255)) @@ -374,7 +375,7 @@ class MDNS(MCast): cip = addr[0] v6 = ":" in cip if (cip.startswith("169.254") and not self.ll_ok) or ( - v6 and not cip.startswith("fe80") + v6 and not cip.startswith(IP6_LL) ): return diff --git a/copyparty/mtag.py b/copyparty/mtag.py index e9428b79..29750733 100644 --- a/copyparty/mtag.py +++ b/copyparty/mtag.py @@ -29,7 +29,7 @@ from .util import ( ) if True: # pylint: disable=using-constant-test - from typing import Any, Optional, Union + from typing import IO, Any, Optional, Union from .util import NamedLogger, RootLogger @@ -176,6 +176,9 @@ def au_unpk( raise Exception("no images inside cbz") fi = zf.open(using) + elif pk == "epub": + fi = get_cover_from_epub(log, abspath) + else: raise Exception("unknown compression %s" % (pk,)) @@ -205,7 +208,7 @@ def au_unpk( def ffprobe( abspath: str, timeout: int = 60 -) -> tuple[dict[str, tuple[int, Any]], dict[str, list[Any]]]: +) -> tuple[dict[str, tuple[int, Any]], dict[str, list[Any]], list[Any], dict[str, Any]]: cmd = [ b"ffprobe", b"-hide_banner", @@ -219,8 +222,17 @@ def ffprobe( return parse_ffprobe(so) -def parse_ffprobe(txt: str) -> tuple[dict[str, tuple[int, Any]], dict[str, list[Any]]]: - """ffprobe -show_format -show_streams""" +def parse_ffprobe( + txt: str, +) -> tuple[dict[str, tuple[int, Any]], dict[str, list[Any]], list[Any], dict[str, Any]]: + """ + txt: output from ffprobe -show_format -show_streams + returns: + * normalized tags + * original/raw tags + * list of streams + * format props + """ streams = [] fmt = {} g = {} @@ -313,7 +325,7 @@ def parse_ffprobe(txt: str) -> tuple[dict[str, tuple[int, Any]], dict[str, list[ ret[rk] = v1 if ret.get("vc") == "ansi": # shellscript - return {}, {} + return {}, {}, [], {} for strm in streams: for sk, sv in strm.items(): @@ -362,7 +374,77 @@ def parse_ffprobe(txt: str) -> tuple[dict[str, tuple[int, Any]], dict[str, list[ zero = int("0") zd = {k: (zero, v) for k, v in ret.items()} - return zd, md + return zd, md, streams, fmt + + +def get_cover_from_epub(log: "NamedLogger", abspath: str) -> Optional[IO[bytes]]: + import zipfile + + from .dxml import parse_xml + + try: + from urlparse import urljoin # Python2 + except ImportError: + from urllib.parse import urljoin # Python3 + + with zipfile.ZipFile(abspath, "r") as z: + # First open the container file to find the package document (.opf file) + try: + container_root = parse_xml(z.read("META-INF/container.xml").decode()) + except KeyError: + log("epub: no container file found in %s" % (abspath,)) + return None + + # https://www.w3.org/TR/epub-33/#sec-container.xml-rootfile-elem + container_ns = {"": "urn:oasis:names:tc:opendocument:xmlns:container"} + # One file could contain multiple package documents, default to the first one + rootfile_path = container_root.find("./rootfiles/rootfile", container_ns).get( + "full-path" + ) + + # Then open the first package document to find the path of the cover image + try: + package_root = parse_xml(z.read(rootfile_path).decode()) + except KeyError: + log("epub: no package document found in %s" % (abspath,)) + return None + + # https://www.w3.org/TR/epub-33/#sec-package-doc + package_ns = {"": "http://www.idpf.org/2007/opf"} + # https://www.w3.org/TR/epub-33/#sec-cover-image + coverimage_path_node = package_root.find( + "./manifest/item[@properties='cover-image']", package_ns + ) + if coverimage_path_node is not None: + coverimage_path = coverimage_path_node.get("href") + else: + # This might be an EPUB2 file, try the legacy way of specifying covers + coverimage_path = _get_cover_from_epub2(log, package_root, package_ns) + + # This url is either absolute (in the .epub) or relative to the package document + adjusted_cover_path = urljoin(rootfile_path, coverimage_path) + + return z.open(adjusted_cover_path) + + +def _get_cover_from_epub2( + log: "NamedLogger", package_root, package_ns +) -> Optional[str]: + # <meta name="cover" content="id-to-cover-image"> in <metadata>, then + # <item> in <manifest> + cover_id = package_root.find("./metadata/meta[@name='cover']", package_ns).get( + "content" + ) + + if not cover_id: + return None + + for node in package_root.iterfind("./manifest/item", package_ns): + if node.get("id") == cover_id: + cover_path = node.get("href") + return cover_path + + return None class MTag(object): @@ -633,7 +715,7 @@ class MTag(object): if not bos.path.isfile(abspath): return {} - ret, md = ffprobe(abspath, self.args.mtag_to) + ret, md, _, _ = ffprobe(abspath, self.args.mtag_to) if self.args.mtag_vv: for zd in (ret, dict(md)): diff --git a/copyparty/multicast.py b/copyparty/multicast.py index 766dc15d..ea2f223e 100644 --- a/copyparty/multicast.py +++ b/copyparty/multicast.py @@ -15,7 +15,7 @@ from ipaddress import ( ) from .__init__ import MACOS, TYPE_CHECKING -from .util import Daemon, Netdev, find_prefix, min_ex, spack +from .util import IP6_LL, IP64_LL, Daemon, Netdev, find_prefix, min_ex, spack if TYPE_CHECKING: from .svchub import SvcHub @@ -145,7 +145,7 @@ class MCast(object): all_selected = ips[:] # discard non-linklocal ipv6 - ips = [x for x in ips if ":" not in x or x.startswith("fe80")] + ips = [x for x in ips if ":" not in x or x.startswith(IP6_LL)] if not ips: raise NoIPs() @@ -183,7 +183,7 @@ class MCast(object): srv.ips[oth_ip.split("/")[0]] = ipaddress.ip_network(oth_ip, False) # gvfs breaks if a linklocal ip appears in a dns reply - ll = {k: v for k, v in srv.ips.items() if k.startswith(("169.254", "fe80"))} + ll = {k: v for k, v in srv.ips.items() if k.startswith(IP64_LL)} rt = {k: v for k, v in srv.ips.items() if k not in ll} if self.args.ll or not rt: diff --git a/copyparty/pwhash.py b/copyparty/pwhash.py index d4455ca8..d642d3c0 100644 --- a/copyparty/pwhash.py +++ b/copyparty/pwhash.py @@ -25,6 +25,7 @@ class PWHash(object): self.args = args zsl = args.ah_alg.split(",") + zsl = [x.strip() for x in zsl] alg = zsl[0] if alg == "none": alg = "" diff --git a/copyparty/smbd.py b/copyparty/smbd.py index 2b9b3d77..3fbc1130 100644 --- a/copyparty/smbd.py +++ b/copyparty/smbd.py @@ -318,7 +318,7 @@ class SMB(object): t = "blocked rename (no-move-acc %s): /%s @%s" yeet(t % (vfs1.axs.umove, vp1, uname)) - self.hub.up2k.handle_mv(uname, "1.7.6.2", vp1, vp2) + self.hub.up2k.handle_mv("", uname, "1.7.6.2", vp1, vp2) try: bos.makedirs(ap2, vf=vfs2.flags) except: @@ -373,7 +373,7 @@ class SMB(object): t = "blocked utime (no-write-acc %s): /%s @%s" yeet(t % (vfs.axs.uwrite, vpath, uname)) - return bos.utime(ap, times) + bos.utime_c(info, ap, int(times[1]), False) def _p_exists(self, vpath: str) -> bool: # ap = "?" diff --git a/copyparty/stolen/qrcodegen.py b/copyparty/stolen/qrcodegen.py index 38296359..8790e859 100644 --- a/copyparty/stolen/qrcodegen.py +++ b/copyparty/stolen/qrcodegen.py @@ -200,6 +200,25 @@ class QrCode(object): return "\n".join(rows) + def to_png(self, zoom, pad, bg, fg, ap) -> None: + from PIL import Image + + tab = self.modules + sz = self.size + psz = sz + pad * 2 + if bg: + img = Image.new("RGB", (psz, psz), bg) + else: + img = Image.new("RGBA", (psz, psz), (0, 0, 0, 0)) + fg = (fg[0], fg[1], fg[2], 255) + for y in range(sz): + for x in range(sz): + if tab[y][x]: + img.putpixel((x + pad, y + pad), fg) + if zoom != 1: + img = img.resize((sz * zoom, sz * zoom), Image.Resampling.NEAREST) + img.save(ap) + def _draw_function_patterns(self) -> None: # Draw horizontal and vertical timing patterns for i in range(self.size): diff --git a/copyparty/svchub.py b/copyparty/svchub.py index 1af13276..75a269ad 100644 --- a/copyparty/svchub.py +++ b/copyparty/svchub.py @@ -2,6 +2,7 @@ from __future__ import print_function, unicode_literals import argparse +import atexit import errno import logging import os @@ -26,7 +27,7 @@ if True: # pylint: disable=using-constant-test from typing import Any, Optional, Union from .__init__ import ANYWIN, EXE, MACOS, PY2, TYPE_CHECKING, E, EnvParams, unicode -from .authsrv import BAD_CFG, AuthSrv +from .authsrv import BAD_CFG, AuthSrv, n_du_who, n_ver_who from .bos import bos from .cert import ensure_cert from .mtag import HAVE_FFMPEG, HAVE_FFPROBE, HAVE_MUTAGEN @@ -38,6 +39,7 @@ from .th_srv import ( HAVE_FFPROBE, HAVE_HEIF, HAVE_PIL, + HAVE_RAW, HAVE_VIPS, HAVE_WEBP, ThumbSrv, @@ -64,6 +66,7 @@ from .util import ( build_netmap, expat_ver, gzip, + load_ipr, load_ipu, lock_file, min_ex, @@ -72,6 +75,7 @@ from .util import ( pybin, start_log_thrs, start_stackmon, + termsize, ub64enc, ) @@ -130,6 +134,7 @@ class SvcHub(object): self.nsigs = 3 self.retcode = 0 self.httpsrv_up = 0 + self.qr_tsz = None self.log_mutex = threading.Lock() self.cday = 0 @@ -152,6 +157,7 @@ class SvcHub(object): args.no_del = True args.no_mv = True args.hardlink = True + args.dav_auth = True args.vague_403 = True args.nih = True @@ -240,7 +246,7 @@ class SvcHub(object): t = "WARNING: --th-ram-max is very small (%.2f GiB); will not be able to %s" self.log("root", t % (args.th_ram_max, zs), 3) - if args.chpw and args.idp_h_usr: + if args.chpw and args.have_idp_hdrs: t = "ERROR: user-changeable passwords is incompatible with IdP/identity-providers; you must disable either --chpw or --idp-h-usr" self.log("root", t, 1) raise Exception(t) @@ -256,6 +262,10 @@ class SvcHub(object): setattr(args, "ipu_iu", iu) setattr(args, "ipu_nm", nm) + if args.ipr: + ipr = load_ipr(self.log, args.ipr, True) + setattr(args, "ipr_u", ipr) + for zs in "ah_salt fk_salt dk_salt".split(): if getattr(args, "show_%s" % (zs,)): self.log("root", "effective %s is %s" % (zs, getattr(args, zs))) @@ -265,7 +275,7 @@ class SvcHub(object): args.no_ses = True args.shr = "" - if args.idp_store and args.idp_h_usr: + if args.idp_store and args.have_idp_hdrs: self.setup_db("idp") if not self.args.no_ses: @@ -279,6 +289,14 @@ class SvcHub(object): ch = "abcdefghijklmnopqrstuvwx"[int(args.theme / 2)] args.theme = "{0}{1} {0} {1}".format(ch, bri) + if args.nid: + args.du_who = "no" + args.du_iwho = n_du_who(args.du_who) + + if args.ver and args.ver_who == "no": + args.ver_who = "all" + args.ver_iwho = n_ver_who(args.ver_who) + if args.nih: args.vname = "" args.doctitle = args.doctitle.replace(" @ --name", "") @@ -316,11 +334,13 @@ class SvcHub(object): self._feature_test() - decs = {k: 1 for k in self.args.th_dec.split(",")} + decs = {k.strip(): 1 for k in self.args.th_dec.split(",")} if not HAVE_VIPS: decs.pop("vips", None) if not HAVE_PIL: decs.pop("pil", None) + if not HAVE_RAW: + decs.pop("raw", None) if not HAVE_FFMPEG or not HAVE_FFPROBE: decs.pop("ff", None) @@ -422,11 +442,14 @@ class SvcHub(object): # create netmaps early to avoid firewall gaps, # but the mutex blocks multiprocessing startup - for zs in "ipu_iu ftp_ipa_nm tftp_ipa_nm".split(): + for zs in "ipu_nm ftp_ipa_nm tftp_ipa_nm".split(): try: getattr(args, zs).mutex = threading.Lock() except: pass + if args.ipr: + for nm in args.ipr_u.values(): + nm.mutex = threading.Lock() def _db_onfail_ses(self) -> None: self.args.no_ses = True @@ -772,6 +795,80 @@ class SvcHub(object): def sigterm(self) -> None: self.signal_handler(signal.SIGTERM, None) + def sticky_qr(self) -> None: + self._sticky_qr() + + def _unsticky_qr(self, flush=True) -> None: + print("\033[s\033[J\033[r\033[u", file=sys.stderr, end="") + if flush: + sys.stderr.flush() + + def _sticky_qr(self, force: bool = False) -> None: + sz = termsize() + if self.qr_tsz == sz: + if not force: + return + else: + force = False + + if self.qr_tsz: + self._unsticky_qr(False) + else: + atexit.register(self._unsticky_qr) + + tw, th = self.qr_tsz = sz + zs1, qr = self.tcpsrv.qr.split("\n", 1) + url, colr = zs1.split(" ", 1) + nl = len(qr.split("\n")) # numlines + lp = 3 if nl * 2 + 4 < tw else 0 # leftpad + lp0 = lp + if self.args.qr_pin == 2: + url = "" + else: + while lp and (nl + lp) * 2 + len(url) + 1 > tw: + lp -= 1 + if (nl + lp) * 2 + len(url) + 1 > tw: + qr = url + "\n" + qr + url = "" + nl += 1 + lp = lp0 + sh = 1 + th - nl + if lp: + zs = " " * lp + qr = zs + qr.replace("\n", "\n" + zs) + if url: + url = "%s\033[%d;%dH%s\033[0m" % (colr, sh + 1, (nl + lp) * 2, url) + qr = colr + qr + + t = "%s\033[%dA" % ("\n" * nl, nl) + t = "%s\033[s\033[1;%dr\033[%dH%s%s\033[u" % (t, sh - 1, sh, qr, url) + if not force: + self.log("qr", "sticky-qrcode %sx%s,%s" % (tw, th, sh), 6) + self.pr(t, file=sys.stderr, end="") + + def _qr_thr(self): + qr = self.tcpsrv.qr + w8 = self.args.qr_wait + if w8: + time.sleep(w8) + self.log("qr-code", qr) + w8 = self.args.qr_every + msg = "%s\033[%dA" % (qr, len(qr.split("\n"))) + while w8: + time.sleep(w8) + if self.stopping: + break + if self.args.qr_pin: + self._sticky_qr(True) + else: + self.log("qr-code", msg) + w8 = self.args.qr_winch + while w8: + time.sleep(w8) + if self.stopping: + break + self._sticky_qr() + def cb_httpsrv_up(self) -> None: self.httpsrv_up += 1 if self.httpsrv_up != self.broker.num_workers: @@ -784,7 +881,12 @@ class SvcHub(object): break if self.tcpsrv.qr: - self.log("qr-code", self.tcpsrv.qr) + if self.args.qr_pin: + self.sticky_qr() + if self.args.qr_wait or self.args.qr_every or self.args.qr_winch: + Daemon(self._qr_thr, "qr") + elif not self.args.qr_pin: + self.log("qr-code", self.tcpsrv.qr) else: self.log("root", "workers OK\n") @@ -811,6 +913,7 @@ class SvcHub(object): (HAVE_ZMQ, "pyzmq", "send zeromq messages from event-hooks"), (HAVE_HEIF, "pillow-heif", "read .heif images with pillow (rarely useful)"), (HAVE_AVIF, "pillow-avif", "read .avif images with pillow (rarely useful)"), + (HAVE_RAW, "rawpy", "read RAW images"), ] if ANYWIN: to_check += [ @@ -881,6 +984,24 @@ class SvcHub(object): t = "WARNING:\nDisabling WebDAV support because dxml selftest failed. Please report this bug;\n%s\n...and include the following information in the bug-report:\n%s | expat %s\n" self.log("root", t % (URL_BUG, VERSIONS, expat_ver()), 1) + if not E.scfg and not al.unsafe_state and not os.getenv("PRTY_UNSAFE_STATE"): + t = "because runtime config is currently being stored in an untrusted emergency-fallback location. Please fix your environment so either XDG_CONFIG_HOME or ~/.config can be used instead, or disable this safeguard with --unsafe-state or env-var PRTY_UNSAFE_STATE=1." + if not al.no_ses: + al.no_ses = True + t2 = "A consequence of this misconfiguration is that passwords will now be sent in the HTTP-header of every request!" + self.log("root", "WARNING:\nWill disable sessions %s %s" % (t, t2), 1) + if al.idp_store == 1: + al.idp_store = 0 + self.log("root", "WARNING:\nDisabling --idp-store %s" % (t,), 3) + if al.idp_store: + t2 = "ERROR: Cannot enable --idp-store %s" % (t,) + self.log("root", t2, 1) + raise Exception(t2) + if al.shr: + t2 = "ERROR: Cannot enable shares %s" % (t,) + self.log("root", t2, 1) + raise Exception(t2) + def _process_config(self) -> bool: al = self.args @@ -969,10 +1090,23 @@ class SvcHub(object): al.sus_urls = None al.xff_hdr = al.xff_hdr.lower() - al.idp_h_usr = al.idp_h_usr.lower() + al.idp_h_usr = [x.lower() for x in al.idp_h_usr or []] al.idp_h_grp = al.idp_h_grp.lower() al.idp_h_key = al.idp_h_key.lower() + al.idp_hm_usr_p = {} + for zs0 in al.idp_hm_usr or []: + try: + sep = zs0[:1] + hn, zs1, zs2 = zs0[1:].split(sep) + hn = hn.lower() + if hn in al.idp_hm_usr_p: + al.idp_hm_usr_p[hn][zs1] = zs2 + else: + al.idp_hm_usr_p[hn] = {zs1: zs2} + 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) @@ -1024,7 +1158,7 @@ class SvcHub(object): al.tcolor = "".join([x * 2 for x in al.tcolor]) zs = al.u2sz - zsl = zs.split(",") + zsl = [x.strip() for x in zs.split(",")] if len(zsl) not in (1, 3): t = "invalid --u2sz; must be either one number, or a comma-separated list of three numbers (min,default,max)" raise Exception(t) @@ -1400,7 +1534,14 @@ class SvcHub(object): fmt = "\033[36m%s \033[33m%-21s \033[0m%s\n" if self.no_ansi: - fmt = "%s %-21s %s\n" + if c == 1: + fmt = "%s %-21s CRIT: %s\n" + elif c == 3: + fmt = "%s %-21s WARN: %s\n" + elif c == 6: + fmt = "%s %-21s BTW: %s\n" + else: + fmt = "%s %-21s LOG: %s\n" if "\033" in msg: msg = RE_ANSI.sub("", msg) if "\033" in src: diff --git a/copyparty/tcpsrv.py b/copyparty/tcpsrv.py index 6c61fe61..6d294336 100644 --- a/copyparty/tcpsrv.py +++ b/copyparty/tcpsrv.py @@ -9,13 +9,14 @@ import time from .__init__ import ANYWIN, PY2, TYPE_CHECKING, unicode from .cert import gencert -from .stolen.qrcodegen import QrCode +from .stolen.qrcodegen import QrCode, qr2svg from .util import ( E_ACCESS, E_ADDR_IN_USE, E_ADDR_NOT_AVAIL, E_UNREACH, HAVE_IPV6, + IP6_LL, IP6ALL, VF_CAREFUL, Netdev, @@ -140,12 +141,12 @@ class TcpSrv(object): # keep IPv6 LL-only nics ll_ok: set[str] = set() for ip, nd in self.netdevs.items(): - if not ip.startswith("fe80"): + if not ip.startswith(IP6_LL): continue just_ll = True for ip2, nd2 in self.netdevs.items(): - if nd == nd2 and ":" in ip2 and not ip2.startswith("fe80"): + if nd == nd2 and ":" in ip2 and not ip2.startswith(IP6_LL): just_ll = False if just_ll or self.args.ll: @@ -164,7 +165,7 @@ class TcpSrv(object): title_vars = [x[1:] for x in self.args.wintitle.split(" ") if x.startswith("$")] t = "available @ {}://{}:{}/ (\033[33m{}\033[0m)" for ip, desc in sorted(eps.items(), key=lambda x: x[1]): - if ip.startswith("fe80") and ip not in ll_ok: + if ip.startswith(IP6_LL) and ip not in ll_ok: continue for port in sorted(self.args.p): @@ -304,6 +305,10 @@ class TcpSrv(object): if os.path.exists(ip): os.unlink(ip) srv.bind(ip) + if uds_gid != -1: + os.chown(ip, -1, uds_gid) + if uds_perm != -1: + os.chmod(ip, uds_perm) else: tf = "%s.%d" % (ip, os.getpid()) if os.path.exists(tf): @@ -614,9 +619,17 @@ class TcpSrv(object): fg = self.args.qr_fg bg = self.args.qr_bg + nocolor = fg == -1 + if nocolor: + fg = 0 + pad = self.args.qrp zoom = self.args.qrz qrc = QrCode.encode_binary(btxt) + + for zs in self.args.qr_file or []: + self._qr2file(qrc, zs) + if zoom == 0: try: tw, th = termsize() @@ -632,6 +645,8 @@ class TcpSrv(object): halfc = "\033[40;48;5;{0}m{1}\033[47;48;5;{2}m" if not fg: halfc = "\033[0;40m{1}\033[0;47m" + if nocolor: + halfc = "\033[0;7m{1}\033[0m" def ansify(m: re.Match) -> str: return halfc.format(fg, " " * len(m.group(1)), bg) @@ -641,6 +656,8 @@ class TcpSrv(object): qr = qr.replace("\n", "\033[K\n") + "\033[K" # win10do cc = " \033[0;38;5;{0};47;48;5;{1}m" if fg else " \033[0;30;47m" + if nocolor: + cc = " \033[0m" t = cc + "\n{2}\033[999G\033[0m\033[J" t = t.format(fg, bg, qr) if ANYWIN: @@ -648,3 +665,29 @@ class TcpSrv(object): t = t.replace("\n", "`\n`") return txt + t + + def _qr2file(self, qrc: QrCode, txt: str): + if ".txt:" in txt or ".svg:" in txt: + ap, zs1, zs2 = txt.rsplit(":", 2) + bg = fg = "" + else: + ap, zs1, zs2, bg, fg = txt.rsplit(":", 4) + zoom = int(zs1) + pad = int(zs2) + + if ap.endswith(".txt"): + if zoom not in (1, 2): + raise Exception("invalid zoom for qr.txt; must be 1 or 2") + with open(ap, "wb") as f: + f.write(qrc.render(zoom, pad).encode("utf-8")) + elif ap.endswith(".svg"): + with open(ap, "wb") as f: + f.write(qr2svg(qrc, pad).encode("utf-8")) + else: + qrc.to_png(zoom, pad, self._h2i(bg), self._h2i(fg), ap) + + def _h2i(self, hs): + try: + return tuple(int(hs[i : i + 2], 16) for i in (0, 2, 4)) + except: + return None diff --git a/copyparty/th_cli.py b/copyparty/th_cli.py index 6c2632b5..384316ee 100644 --- a/copyparty/th_cli.py +++ b/copyparty/th_cli.py @@ -36,11 +36,15 @@ class ThumbCli(object): if not c: raise Exception() except: - c = {k: set() for k in ["thumbable", "pil", "vips", "ffi", "ffv", "ffa"]} + c = { + k: set() + for k in ["thumbable", "pil", "vips", "raw", "ffi", "ffv", "ffa"] + } self.thumbable = c["thumbable"] self.fmt_pil = c["pil"] self.fmt_vips = c["vips"] + self.fmt_raw = c["raw"] self.fmt_ffi = c["ffi"] self.fmt_ffv = c["ffv"] self.fmt_ffa = c["ffa"] diff --git a/copyparty/th_srv.py b/copyparty/th_srv.py index 7b07376a..86c3c469 100644 --- a/copyparty/th_srv.py +++ b/copyparty/th_srv.py @@ -2,6 +2,7 @@ from __future__ import print_function, unicode_literals import hashlib +import io import logging import os import re @@ -85,7 +86,10 @@ try: if os.environ.get("PRTY_NO_PIL_HEIF"): raise Exception() - from pyheif_pillow_opener import register_heif_opener + try: + from pillow_heif import register_heif_opener + except ImportError: + from pyheif_pillow_opener import register_heif_opener register_heif_opener() HAVE_HEIF = True @@ -112,14 +116,28 @@ except: try: if os.environ.get("PRTY_NO_VIPS"): - raise Exception() + raise ImportError() HAVE_VIPS = True import pyvips logging.getLogger("pyvips").setLevel(logging.WARNING) -except: +except Exception as e: HAVE_VIPS = False + if not isinstance(e, ImportError): + logging.warning("libvips found, but failed to load: " + str(e)) + + +try: + if os.environ.get("PRTY_NO_RAW"): + raise Exception() + + HAVE_RAW = True + import rawpy + + logging.getLogger("rawpy").setLevel(logging.WARNING) +except: + HAVE_RAW = False th_dir_cache = {} @@ -205,11 +223,19 @@ class ThumbSrv(object): if self.args.th_clean: Daemon(self.cleaner, "thumb.cln") - self.fmt_pil, self.fmt_vips, self.fmt_ffi, self.fmt_ffv, self.fmt_ffa = [ + ( + self.fmt_pil, + self.fmt_vips, + self.fmt_raw, + self.fmt_ffi, + self.fmt_ffv, + self.fmt_ffa, + ) = [ set(y.split(",")) for y in [ self.args.th_r_pil, self.args.th_r_vips, + self.args.th_r_raw, self.args.th_r_ffi, self.args.th_r_ffv, self.args.th_r_ffa, @@ -232,6 +258,9 @@ class ThumbSrv(object): if "vips" in self.args.th_dec: self.thumbable |= self.fmt_vips + if "raw" in self.args.th_dec: + self.thumbable |= self.fmt_raw + if "ff" in self.args.th_dec: for zss in [self.fmt_ffi, self.fmt_ffv, self.fmt_ffa]: self.thumbable |= zss @@ -313,6 +342,7 @@ class ThumbSrv(object): "thumbable": self.thumbable, "pil": self.fmt_pil, "vips": self.fmt_vips, + "raw": self.fmt_raw, "ffi": self.fmt_ffi, "ffv": self.fmt_ffv, "ffa": self.fmt_ffa, @@ -368,6 +398,8 @@ class ThumbSrv(object): funs.append(self.conv_pil) elif lib == "vips" and ext in self.fmt_vips: funs.append(self.conv_vips) + elif lib == "raw" and ext in self.fmt_raw: + funs.append(self.conv_raw) elif can_au and (want_png or want_au): if want_opus: funs.append(self.conv_opus) @@ -480,35 +512,38 @@ class ThumbSrv(object): return im + def conv_image_pil(self, im: "Image.Image", tpath: str, fmt: str, vn: VFS) -> None: + try: + im = self.fancy_pillow(im, fmt, vn) + except Exception as ex: + self.log("fancy_pillow {}".format(ex), "90") + im.thumbnail(self.getres(vn, fmt)) + + fmts = ["RGB", "L"] + args = {"quality": 40} + + if tpath.endswith(".webp"): + # quality 80 = pillow-default + # quality 75 = ffmpeg-default + # method 0 = pillow-default, fast + # method 4 = ffmpeg-default + # method 6 = max, slow + fmts.extend(("RGBA", "LA")) + args["method"] = 6 + else: + # default q = 75 + args["progressive"] = True + + if im.mode not in fmts: + # print("conv {}".format(im.mode)) + im = im.convert("RGB") + + im.save(tpath, **args) + def conv_pil(self, abspath: str, tpath: str, fmt: str, vn: VFS) -> None: self.wait4ram(0.2, tpath) with Image.open(fsenc(abspath)) as im: - try: - im = self.fancy_pillow(im, fmt, vn) - except Exception as ex: - self.log("fancy_pillow {}".format(ex), "90") - im.thumbnail(self.getres(vn, fmt)) - - fmts = ["RGB", "L"] - args = {"quality": 40} - - if tpath.endswith(".webp"): - # quality 80 = pillow-default - # quality 75 = ffmpeg-default - # method 0 = pillow-default, fast - # method 4 = ffmpeg-default - # method 6 = max, slow - fmts.extend(("RGBA", "LA")) - args["method"] = 6 - else: - # default q = 75 - args["progressive"] = True - - if im.mode not in fmts: - # print("conv {}".format(im.mode)) - im = im.convert("RGB") - - im.save(tpath, **args) + self.conv_image_pil(im, tpath, fmt, vn) def conv_vips(self, abspath: str, tpath: str, fmt: str, vn: VFS) -> None: self.wait4ram(0.2, tpath) @@ -531,9 +566,53 @@ class ThumbSrv(object): assert img # type: ignore # !rm img.write_to_file(tpath, Q=40) + def conv_raw(self, abspath: str, tpath: str, fmt: str, vn: VFS) -> None: + self.wait4ram(0.2, tpath) + with rawpy.imread(abspath) as raw: + thumb = raw.extract_thumb() + if thumb.format == rawpy.ThumbFormat.JPEG and tpath.endswith(".jpg"): + # if we have a jpg thumbnail and no webp output is available, + # just write the jpg directly (it'll be the wrong size, but it's fast) + with open(tpath, "wb") as f: + f.write(thumb.data) + if HAVE_VIPS: + crops = ["centre", "none"] + if "f" in fmt: + crops = ["none"] + w, h = self.getres(vn, fmt) + kw = {"height": h, "size": "down", "intent": "relative"} + + for c in crops: + try: + kw["crop"] = c + if thumb.format == rawpy.ThumbFormat.BITMAP: + img = pyvips.Image.new_from_array( + thumb.data, interpretation="rgb" + ) + img = img.thumbnail_image(w, **kw) + else: + img = pyvips.Image.thumbnail_buffer(thumb.data, w, **kw) + break + except: + if c == crops[-1]: + raise + + assert img # type: ignore # !rm + img.write_to_file(tpath, Q=40) + elif HAVE_PIL: + if thumb.format == rawpy.ThumbFormat.BITMAP: + im = Image.fromarray(thumb.data, "RGB") + else: + im = Image.open(io.BytesIO(thumb.data)) + self.conv_image_pil(im, tpath, fmt, vn) + else: + raise Exception( + "either pil or vips is needed to process embedded bitmap thumbnails in raw files" + ) + def conv_ffmpeg(self, abspath: str, tpath: str, fmt: str, vn: VFS) -> None: self.wait4ram(0.2, tpath) - ret, _ = ffprobe(abspath, int(vn.flags["convt"] / 2)) + ret, _, _, _ = ffprobe(abspath, int(vn.flags["convt"] / 2)) if not ret: return @@ -544,6 +623,17 @@ class ThumbSrv(object): dur = ret[".dur"][1] if ".dur" in ret else 4 seek = [b"-ss", "{:.0f}".format(dur / 3).encode("utf-8")] + self._ffmpeg_im(abspath, tpath, fmt, vn, seek, b"0:v:0") + + def _ffmpeg_im( + self, + abspath: str, + tpath: str, + fmt: str, + vn: VFS, + seek: list[bytes], + imap: bytes, + ) -> None: scale = "scale={0}:{1}:force_original_aspect_ratio=" if "f" in fmt: scale += "decrease,setsar=1:1" @@ -562,7 +652,7 @@ class ThumbSrv(object): cmd += seek cmd += [ b"-i", fsenc(abspath), - b"-map", b"0:v:0", + b"-map", imap, b"-vf", bscale, b"-frames:v", b"1", b"-metadata:s:v:0", b"rotate=0", @@ -583,11 +673,11 @@ class ThumbSrv(object): ] cmd += [fsenc(tpath)] - self._run_ff(cmd, vn) + self._run_ff(cmd, vn, "convt") - def _run_ff(self, cmd: list[bytes], vn: VFS, oom: int = 400) -> None: + def _run_ff(self, cmd: list[bytes], vn: VFS, kto: str, oom: int = 400) -> None: # self.log((b" ".join(cmd)).decode("utf-8")) - ret, _, serr = runcmd(cmd, timeout=vn.flags["convt"], nice=True, oom=oom) + ret, _, serr = runcmd(cmd, timeout=vn.flags[kto], nice=True, oom=oom) if not ret: return @@ -631,7 +721,7 @@ class ThumbSrv(object): raise sp.CalledProcessError(ret, (cmd[0], b"...", cmd[-1])) def conv_waves(self, abspath: str, tpath: str, fmt: str, vn: VFS) -> None: - ret, _ = ffprobe(abspath, int(vn.flags["convt"] / 2)) + ret, _, _, _ = ffprobe(abspath, int(vn.flags["convt"] / 2)) if "ac" not in ret: raise Exception("not audio") @@ -669,7 +759,7 @@ class ThumbSrv(object): # fmt: on cmd += [fsenc(tpath)] - self._run_ff(cmd, vn) + self._run_ff(cmd, vn, "convt") if "pngquant" in vn.flags: wtpath = tpath + ".png" @@ -690,11 +780,31 @@ class ThumbSrv(object): else: atomic_move(self.log, wtpath, tpath, vn.flags) + def conv_emb_cv( + self, abspath: str, tpath: str, fmt: str, vn: VFS, strm: dict[str, Any] + ) -> None: + self.wait4ram(0.2, tpath) + self._ffmpeg_im( + abspath, tpath, fmt, vn, [], b"0:" + strm["index"].encode("ascii") + ) + def conv_spec(self, abspath: str, tpath: str, fmt: str, vn: VFS) -> None: - ret, _ = ffprobe(abspath, int(vn.flags["convt"] / 2)) + ret, raw, strms, ctnr = ffprobe(abspath, int(vn.flags["convt"] / 2)) if "ac" not in ret: raise Exception("not audio") + want_spec = vn.flags.get("th_spec_p", 1) + if want_spec < 2: + for strm in strms: + if ( + strm.get("codec_type") == "video" + and strm.get("DISPOSITION:attached_pic") == "1" + ): + return self.conv_emb_cv(abspath, tpath, fmt, vn, strm) + + if not want_spec: + raise Exception("spectrograms forbidden by volflag") + fext = abspath.split(".")[-1].lower() # https://trac.ffmpeg.org/ticket/10797 @@ -730,7 +840,7 @@ class ThumbSrv(object): b"-y", fsenc(infile), ] # fmt: on - self._run_ff(cmd, vn) + self._run_ff(cmd, vn, "convt") fc = "[0:a:0]aresample=48000{},showspectrumpic=s=" if "3" in fmt: @@ -772,7 +882,7 @@ class ThumbSrv(object): ] cmd += [fsenc(tpath)] - self._run_ff(cmd, vn) + self._run_ff(cmd, vn, "convt") def conv_mp3(self, abspath: str, tpath: str, fmt: str, vn: VFS) -> None: quality = self.args.q_mp3.lower() @@ -780,7 +890,7 @@ class ThumbSrv(object): raise Exception("disabled in server config") self.wait4ram(0.2, tpath) - tags, rawtags = ffprobe(abspath, int(vn.flags["convt"] / 2)) + tags, rawtags, _, _ = ffprobe(abspath, int(vn.flags["convt"] / 2)) if "ac" not in tags: raise Exception("not audio") @@ -811,14 +921,14 @@ class ThumbSrv(object): fsenc(tpath) ] # fmt: on - self._run_ff(cmd, vn, oom=300) + self._run_ff(cmd, vn, "aconvt", oom=300) def conv_flac(self, abspath: str, tpath: str, fmt: str, vn: VFS) -> None: if self.args.no_acode or not self.args.allow_flac: raise Exception("flac not permitted in server config") self.wait4ram(0.2, tpath) - tags, rawtags = ffprobe(abspath, int(vn.flags["convt"] / 2)) + tags, _, _, _ = ffprobe(abspath, int(vn.flags["convt"] / 2)) if "ac" not in tags: raise Exception("not audio") @@ -836,14 +946,14 @@ class ThumbSrv(object): fsenc(tpath) ] # fmt: on - self._run_ff(cmd, vn, oom=300) + self._run_ff(cmd, vn, "aconvt", oom=300) def conv_wav(self, abspath: str, tpath: str, fmt: str, vn: VFS) -> None: if self.args.no_acode or not self.args.allow_wav: raise Exception("wav not permitted in server config") self.wait4ram(0.2, tpath) - tags, rawtags = ffprobe(abspath, int(vn.flags["convt"] / 2)) + tags, _, _, _ = ffprobe(abspath, int(vn.flags["convt"] / 2)) if "ac" not in tags: raise Exception("not audio") @@ -871,14 +981,14 @@ class ThumbSrv(object): fsenc(tpath) ] # fmt: on - self._run_ff(cmd, vn, oom=300) + self._run_ff(cmd, vn, "aconvt", oom=300) def conv_opus(self, abspath: str, tpath: str, fmt: str, vn: VFS) -> None: if self.args.no_acode or not self.args.q_opus: raise Exception("disabled in server config") self.wait4ram(0.2, tpath) - tags, rawtags = ffprobe(abspath, int(vn.flags["convt"] / 2)) + tags, rawtags, _, _ = ffprobe(abspath, int(vn.flags["convt"] / 2)) if "ac" not in tags: raise Exception("not audio") @@ -927,7 +1037,7 @@ class ThumbSrv(object): fsenc(tpath) ] # fmt: on - self._run_ff(cmd, vn, oom=300) + self._run_ff(cmd, vn, "aconvt", oom=300) def _conv_caf( self, @@ -967,7 +1077,7 @@ class ThumbSrv(object): fsenc(tmp_opus) ] # fmt: on - self._run_ff(cmd, vn, oom=300) + self._run_ff(cmd, vn, "aconvt", oom=300) # iOS fails to play some "insufficiently complex" files # (average file shorter than 8 seconds), so of course we @@ -994,7 +1104,7 @@ class ThumbSrv(object): fsenc(tpath) ] # fmt: on - self._run_ff(cmd, vn, oom=300) + self._run_ff(cmd, vn, "aconvt", oom=300) else: # simple remux should be safe @@ -1013,7 +1123,7 @@ class ThumbSrv(object): fsenc(tpath) ] # fmt: on - self._run_ff(cmd, vn, oom=300) + self._run_ff(cmd, vn, "aconvt", oom=300) try: wunlink(self.log, tmp_opus, vn.flags) diff --git a/copyparty/u2idx.py b/copyparty/u2idx.py index ff0e329f..45b479c5 100644 --- a/copyparty/u2idx.py +++ b/copyparty/u2idx.py @@ -391,7 +391,7 @@ class U2idx(object): fk_alg = 2 if "fka" in flags else 1 c = cur.execute(uq, tuple(vuv)) for hit in c: - w, ts, sz, rd, fn, ip, at = hit[:7] + w, ts, sz, rd, fn = hit[:5] if rd.startswith("//") or fn.startswith("//"): rd, fn = s3dec(rd, fn) diff --git a/copyparty/up2k.py b/copyparty/up2k.py index b233cecb..3f8e8f95 100644 --- a/copyparty/up2k.py +++ b/copyparty/up2k.py @@ -60,6 +60,7 @@ from .util import ( sfsenc, spack, statdir, + trystat_shutil_copy2, ub64enc, unhumanize, vjoin, @@ -77,7 +78,7 @@ except: if HAVE_SQLITE3: import sqlite3 -DB_VER = 5 +DB_VER = 6 if True: # pylint: disable=using-constant-test from typing import Any, Optional, Pattern, Union @@ -91,6 +92,9 @@ ICV_EXTS = set(zsg.split(",")) zsg = "3gp,asf,av1,avc,avi,flv,m4v,mjpeg,mjpg,mkv,mov,mp4,mpeg,mpeg2,mpegts,mpg,mpg2,mts,nut,ogm,ogv,rm,vob,webm,wmv" VCV_EXTS = set(zsg.split(",")) +zsg = "aif,aiff,alac,ape,flac,m4a,mp3,oga,ogg,opus,tak,tta,wav,wma,wv" +ACV_EXTS = set(zsg.split(",")) + zsg = "nohash noidx xdev xvol" VF_AFFECTS_INDEXING = set(zsg.split(" ")) @@ -144,6 +148,7 @@ class Up2k(object): self.salt = self.args.warksalt self.r_hash = re.compile("^[0-9a-zA-Z_-]{44}$") + self.abrt_key = "" self.gid = 0 self.gt0 = 0 @@ -410,10 +415,11 @@ class Up2k(object): ret: list[tuple[int, str, int, int, int]] = [] userset = set([(uname or "\n"), "*"]) + e_d = {} n = 1000 try: for ptop, tab2 in self.registry.items(): - cfg = self.flags.get(ptop, {}).get("u2abort", 1) + cfg = self.flags.get(ptop, e_d).get("u2abort", 1) if not cfg: continue addr = (ip or "\n") if cfg in (1, 2) else "" @@ -902,7 +908,7 @@ class Up2k(object): self.iacct = self.asrv.iacct self.grps = self.asrv.grps - have_e2d = self.args.idp_h_usr or self.args.chpw or self.args.shr + have_e2d = self.args.have_idp_hdrs or self.args.chpw or self.args.shr vols = list(all_vols.values()) t0 = time.time() @@ -922,6 +928,12 @@ class Up2k(object): with self.mutex, self.reg_mutex: # only need to protect register_vpath but all in one go feels right for vol in vols: + if bos.path.isfile(vol.realpath): + self.volstate[vol.vpath] = "online (just-a-file)" + t = "NOTE: volume [/%s] is a file, not a folder" + self.log(t % (vol.vpath,)) + continue + try: # mkdir gonna happen at snap anyways; bos.makedirs(vol.realpath, vf=vol.flags) @@ -1128,7 +1140,7 @@ class Up2k(object): ft = "\033[0;32m{}{:.0}" ff = "\033[0;35m{}{:.0}" fv = "\033[0;36m{}:\033[90m{}" - zs = "ext_th_d html_head 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 = "du_iwho ext_th_d html_head 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()) @@ -1482,7 +1494,8 @@ class Up2k(object): unreg: list[str] = [] files: list[tuple[int, int, str]] = [] fat32 = True - cv = vcv = "" + cv = vcv = acv = "" + e_d = {} th_cvd = self.args.th_coversd th_cvds = self.args.th_coversd_set @@ -1592,9 +1605,11 @@ class Up2k(object): cv = iname elif not vcv and ext in VCV_EXTS and not iname.startswith("."): vcv = iname + elif not acv and ext in ACV_EXTS and not iname.startswith("."): + acv = iname if not cv: - cv = vcv + cv = vcv or acv if not self.args.no_dirsz: tnf += len(files) @@ -1654,7 +1669,7 @@ class Up2k(object): abspath = cdirs + fn nohash = reh.search(abspath) if reh else False - sql = "select w, mt, sz, ip, at from up where rd = ? and fn = ?" + sql = "select w, mt, sz, ip, at, un from up where rd = ? and fn = ?" try: c = db.c.execute(sql, (rd, fn)) except: @@ -1663,7 +1678,7 @@ class Up2k(object): in_db = list(c.fetchall()) if in_db: self.pp.n -= 1 - dw, dts, dsz, ip, at = in_db[0] + dw, dts, dsz, ip, at, un = in_db[0] if len(in_db) > 1: t = "WARN: multiple entries: %r => %r |%d|\n%r" rep_db = "\n".join([repr(x) for x in in_db]) @@ -1676,9 +1691,12 @@ class Up2k(object): if dts == lmod and dsz == sz and (nohash or dw[0] != "#" or not sz): continue + if un is None: + un = "" + t = "reindex %r => %r mtime(%s/%s) size(%s/%s)" self.log(t % (top, rp, dts, lmod, dsz, sz)) - self.db_rm(db.c, rd, fn, 0) + self.db_rm(db.c, e_d, rd, fn, 0) tfa += 1 db.n += 1 in_db = [] @@ -1686,6 +1704,7 @@ class Up2k(object): dw = "" ip = "" at = 0 + un = "" self.pp.msg = "a%d %s" % (self.pp.n, abspath) @@ -1711,9 +1730,10 @@ class Up2k(object): if dw and dw != wark: ip = "" at = 0 + un = "" # skip upload hooks by not providing vflags - self.db_add(db.c, {}, rd, fn, lmod, sz, "", "", wark, wark, "", "", ip, at) + self.db_add(db.c, e_d, rd, fn, lmod, sz, "", "", wark, wark, "", un, ip, at) db.n += 1 db.nf += 1 tfa += 1 @@ -1773,7 +1793,7 @@ class Up2k(object): rm_files = [x for x in hits if x not in seen_files] n_rm = len(rm_files) for fn in rm_files: - self.db_rm(db.c, rd, fn, 0) + self.db_rm(db.c, e_d, rd, fn, 0) if n_rm: self.log("forgot {} deleted files".format(n_rm)) @@ -2150,8 +2170,8 @@ class Up2k(object): with self.mutex: try: - q = "select rd, fn, ip, at from up where substr(w,1,16)=? and +w=?" - rd, fn, ip, at = cur.execute(q, (w16, w)).fetchone() + q = "select rd, fn, ip, at, un from up where substr(w,1,16)=? and +w=?" + rd, fn, ip, at, un = cur.execute(q, (w16, w)).fetchone() except: # file modified/deleted since spooling continue @@ -2170,12 +2190,15 @@ class Up2k(object): abspath = djoin(ptop, rd, fn) self.pp.msg = "c%d %s" % (nq, abspath) if not mpool: - n_tags = self._tagscan_file(cur, entags, w, abspath, ip, at) + n_tags = self._tagscan_file(cur, entags, w, abspath, ip, at, un) else: + oth_tags = {} if ip: - oth_tags = {"up_ip": ip, "up_at": at} - else: - oth_tags = {} + oth_tags["up_ip"] = ip + if at: + oth_tags["up_at"] = at + if un: + oth_tags["up_by"] = un mpool.put(Mpqe({}, entags, w, abspath, oth_tags)) with self.mutex: @@ -2331,8 +2354,8 @@ class Up2k(object): if w in in_progress: continue - q = "select rd, fn, ip, at from up where substr(w,1,16)=? limit 1" - rd, fn, ip, at = cur.execute(q, (w,)).fetchone() + q = "select rd, fn, ip, at, un from up where substr(w,1,16)=? limit 1" + rd, fn, ip, at, un = cur.execute(q, (w,)).fetchone() rd, fn = s3dec(rd, fn) abspath = djoin(ptop, rd, fn) @@ -2356,7 +2379,10 @@ class Up2k(object): if ip: oth_tags["up_ip"] = ip + if at: oth_tags["up_at"] = at + if un: + oth_tags["up_by"] = un jobs.append(Mpqe(parsers, set(), w, abspath, oth_tags)) in_progress[w] = True @@ -2545,6 +2571,7 @@ class Up2k(object): abspath: str, ip: str, at: float, + un: Optional[str], ) -> int: """will mutex(main)""" assert self.mtag # !rm @@ -2565,7 +2592,10 @@ class Up2k(object): if ip: tags["up_ip"] = ip + if at: tags["up_at"] = at + if un: + tags["up_by"] = un with self.mutex: return self._tag_file(write_cur, entags, wark, abspath, tags) @@ -2669,16 +2699,19 @@ class Up2k(object): if not existed and ver is None: return self._try_create_db(db_path, cur) - if ver == 4: + for upver in (4, 5): + if ver != upver: + continue try: t = "creating backup before upgrade: " cur = self._backup_db(db_path, cur, ver, t) - self._upgrade_v4(cur) - ver = 5 + getattr(self, "_upgrade_v%d" % (upver,))(cur) + ver += 1 # type: ignore except: - self.log("WARN: failed to upgrade from v4", 3) + self.log("WARN: failed to upgrade from v%d" % (ver,), 3) if ver == DB_VER: + # these no longer serve their intended purpose but they're great as additional sanchks self._add_dhash_tab(cur) self._add_xiu_tab(cur) self._add_cv_tab(cur) @@ -2736,7 +2769,7 @@ class Up2k(object): cur.close() db.close() - shutil.copy2(fsenc(db_path), fsenc(bak)) + trystat_shutil_copy2(self.log, fsenc(db_path), fsenc(bak)) return self._orz(db_path) def _read_ver(self, cur: "sqlite3.Cursor") -> Optional[int]: @@ -2780,7 +2813,7 @@ class Up2k(object): idx = r"create index up_w on up(w)" for cmd in [ - r"create table up (w text, mt int, sz int, rd text, fn text, ip text, at int)", + r"create table up (w text, mt int, sz int, rd text, fn text, ip text, at int, un text)", r"create index up_vp on up(rd, fn)", r"create index up_fn on up(fn)", r"create index up_ip on up(ip)", @@ -2813,6 +2846,15 @@ class Up2k(object): cur.connection.commit() + def _upgrade_v5(self, cur: "sqlite3.Cursor") -> None: + for cmd in [ + r"alter table up add column un text", + r"update kv set v=6 where k='sver'", + ]: + cur.execute(cmd) + + cur.connection.commit() + def _add_dhash_tab(self, cur: "sqlite3.Cursor") -> None: # v5 -> v5a try: @@ -3010,7 +3052,7 @@ class Up2k(object): argv = [dwark[:16], dwark] c2 = cur.execute(q, tuple(argv)) - for _, dtime, dsize, dp_dir, dp_fn, ip, at in c2: + for _, dtime, dsize, dp_dir, dp_fn, ip, at, _ in c2: if dp_dir.startswith("//") or dp_fn.startswith("//"): dp_dir, dp_fn = s3dec(dp_dir, dp_fn) @@ -3102,7 +3144,7 @@ class Up2k(object): for cur, dp_dir, dp_fn in lost: t = "forgetting desynced db entry: %r" self.log(t % ("/" + vjoin(vjoin(vfs.vpath, dp_dir), dp_fn))) - self.db_rm(cur, dp_dir, dp_fn, cj["size"]) + self.db_rm(cur, vfs.flags, dp_dir, dp_fn, cj["size"]) if c2 and c2 != cur: c2.connection.commit() @@ -3396,10 +3438,9 @@ class Up2k(object): cur.connection.commit() ap = djoin(job["ptop"], job["prel"], job["name"]) - times = (int(time.time()), int(cj["lmod"])) - bos.utime(ap, times, False) + mt = bos.utime_c(self.log, ap, int(cj["lmod"]), False, True) - self.log("touched %r from %d to %d" % (ap, job["lmod"], cj["lmod"])) + self.log("touched %r from %d to %d" % (ap, job["lmod"], mt)) except Exception as ex: self.log("umod failed, %r" % (ex,), 3) @@ -3432,8 +3473,8 @@ class Up2k(object): try: vrel = vjoin(job["prel"], fname) xlink = bool(vf.get("xlink")) - cur, wark, _, _, _, _ = self._find_from_vpath(ptop, vrel) - self._forget_file(ptop, vrel, cur, wark, True, st.st_size, xlink) + cur, wark, _, _, _, _, _ = self._find_from_vpath(ptop, vrel) + self._forget_file(ptop, vrel, vf, cur, wark, True, st.st_size, xlink) except Exception as ex: self.log("skipping replace-relink: %r" % (ex,)) finally: @@ -3551,11 +3592,10 @@ class Up2k(object): t = "BUG: no valid sources to link from! orig(%r) fsrc(%r) link(%r)" self.log(t, 1) raise Exception(t % (src, fsrc, dst)) - shutil.copy2(fsenc(csrc), fsenc(dst)) + trystat_shutil_copy2(self.log, fsenc(csrc), fsenc(dst)) if lmod and (not linked or SYMTIME): - times = (int(time.time()), int(lmod)) - bos.utime(dst, times, False) + bos.utime_c(self.log, dst, int(lmod), False) def handle_chunks( self, ptop: str, wark: str, chashes: list[str] @@ -3728,10 +3768,8 @@ class Up2k(object): times = (int(time.time()), int(job["lmod"])) t = "no more chunks, setting times %s (%d) on %r" self.log(t % (times, bos.path.getsize(dst), dst)) - try: - bos.utime(dst, times) - except: - self.log("failed to utime (%r, %s)" % (dst, times)) + bos.utime_c(self.log, dst, times[1], False) + # the above logmsg (and associated logic) is retained due to unforget.py zs = "prel name lmod size ptop vtop wark dwrk host user addr" z2 = [job[x] for x in zs.split()] @@ -3850,7 +3888,9 @@ class Up2k(object): return True - def db_rm(self, db: "sqlite3.Cursor", rd: str, fn: str, sz: int) -> None: + def db_rm( + self, db: "sqlite3.Cursor", vflags: dict[str, Any], rd: str, fn: str, sz: int + ) -> None: sql = "delete from up where rd = ? and fn = ?" try: r = db.execute(sql, (rd, fn)) @@ -3858,9 +3898,22 @@ class Up2k(object): assert self.mem_cur # !rm r = db.execute(sql, s3enc(self.mem_cur, rd, fn)) - if r.rowcount: - self.volsize[db] -= sz - self.volnfiles[db] -= 1 + if not r.rowcount: + return + + self.volsize[db] -= sz + self.volnfiles[db] -= 1 + + if "nodirsz" not in vflags: + try: + q = "update ds set nf=nf-1, sz=sz-? where rd=?" + while True: + db.execute(q, (sz, rd)) + if not rd: + break + rd = rd.rsplit("/", 1)[0] if "/" in rd else "" + except: + pass def db_add( self, @@ -3881,7 +3934,7 @@ class Up2k(object): skip_xau: bool = False, ) -> None: """mutex(main) me""" - self.db_rm(db, rd, fn, sz) + self.db_rm(db, vflags, rd, fn, sz) if not ip: db_ip = "" @@ -3889,14 +3942,14 @@ class Up2k(object): # plugins may expect this to look like an actual IP db_ip = "1.1.1.1" if "no_db_ip" in vflags else ip - sql = "insert into up values (?,?,?,?,?,?,?)" - v = (dwark, int(ts), sz, rd, fn, db_ip, int(at or 0)) + sql = "insert into up values (?,?,?,?,?,?,?,?)" + v = (dwark, int(ts), sz, rd, fn, db_ip, int(at or 0), usr) try: db.execute(sql, v) except: assert self.mem_cur # !rm rd, fn = s3enc(self.mem_cur, rd, fn) - v = (dwark, int(ts), sz, rd, fn, db_ip, int(at or 0)) + v = (dwark, int(ts), sz, rd, fn, db_ip, int(at or 0), usr) db.execute(sql, v) self.volsize[db] += sz @@ -3988,6 +4041,9 @@ class Up2k(object): except: pass + def handle_fs_abrt(self, akey: str) -> None: + self.abrt_key = akey + def handle_rm( self, uname: str, @@ -4034,7 +4090,7 @@ class Up2k(object): vn, rem = vn0.get_dbv(rem0) ptop = vn.realpath with self.mutex, self.reg_mutex: - abrt_cfg = self.flags.get(ptop, {}).get("u2abort", 1) + abrt_cfg = vn.flags.get("u2abort", 1) addr = (ip or "\n") if abrt_cfg in (1, 2) else "" user = ((uname or "\n"), "*") if abrt_cfg in (1, 3) else None reg = self.registry.get(ptop, {}) if abrt_cfg else {} @@ -4055,17 +4111,22 @@ class Up2k(object): if partial: dip = ip dat = time.time() + dun = uname + un_cfg = 1 else: - if not self.args.unpost: + un_cfg = vn.flags["unp_who"] + if not self.args.unpost or not un_cfg: t = "the unpost feature is disabled in server config" raise Pebkac(400, t) - _, _, _, _, dip, dat = self._find_from_vpath(ptop, rem) + _, _, _, _, dip, dat, dun = self._find_from_vpath(ptop, rem) t = "you cannot delete this: " if not dip: t += "file not found" - elif dip != ip: + elif dip != ip and un_cfg in (1, 2): + t += "not uploaded by (You)" + elif dun != uname and un_cfg in (1, 3): t += "not uploaded by (You)" elif dat < time.time() - self.args.unpost: t += "uploaded too long ago" @@ -4154,9 +4215,9 @@ class Up2k(object): try: ptop = dbv.realpath xlink = bool(dbv.flags.get("xlink")) - cur, wark, _, _, _, _ = self._find_from_vpath(ptop, volpath) + cur, wark, _, _, _, _, _ = self._find_from_vpath(ptop, volpath) self._forget_file( - ptop, volpath, cur, wark, True, st.st_size, xlink + ptop, volpath, dbv.flags, cur, wark, True, st.st_size, xlink ) finally: if cur: @@ -4197,7 +4258,7 @@ class Up2k(object): return n_files, ok + ok2, ng + ng2 - def handle_cp(self, uname: str, ip: str, svp: str, dvp: str) -> str: + def handle_cp(self, abrt: str, uname: str, ip: str, svp: str, dvp: str) -> str: if svp == dvp or dvp.startswith(svp + "/"): raise Pebkac(400, "cp: cannot copy parent into subfolder") @@ -4244,6 +4305,8 @@ class Up2k(object): dvpf = dvp + svpf[len(svp) :] self._cp_file(uname, ip, svpf, dvpf, curs) + if abrt and abrt == self.abrt_key: + raise Pebkac(400, "filecopy aborted by http-api") for v in curs: v.connection.commit() @@ -4313,7 +4376,7 @@ class Up2k(object): bos.makedirs(os.path.dirname(dabs), vf=dvn.flags) - c1, w, ftime_, fsize_, ip, at = self._find_from_vpath( + c1, w, ftime_, fsize_, ip, at, un = self._find_from_vpath( svn_dbv.realpath, srem_dbv ) c2 = self.cur.get(dvn.realpath) @@ -4338,7 +4401,7 @@ class Up2k(object): w, w, "", - "", + un or "", ip or "", at or 0, ) @@ -4365,7 +4428,7 @@ class Up2k(object): b1, b2 = fsenc(sabs), fsenc(dabs) is_link = os.path.islink(b1) # due to _relink try: - shutil.copy2(b1, b2) + trystat_shutil_copy2(self.log, b1, b2) except: try: wunlink(self.log, dabs, dvn.flags) @@ -4411,7 +4474,7 @@ class Up2k(object): return "k" - def handle_mv(self, uname: str, ip: str, svp: str, dvp: str) -> str: + def handle_mv(self, abrt: str, uname: str, ip: str, svp: str, dvp: str) -> str: if svp == dvp or dvp.startswith(svp + "/"): raise Pebkac(400, "mv: cannot move parent into subfolder") @@ -4466,6 +4529,8 @@ class Up2k(object): dvpf = dvp + svpf[len(svp) :] self._mv_file(uname, ip, svpf, dvpf, curs) + if abrt and abrt == self.abrt_key: + raise Pebkac(400, "filemove aborted by http-api") for v in curs: v.connection.commit() @@ -4597,7 +4662,7 @@ class Up2k(object): return "k" - c1, w, ftime_, fsize_, ip, at = self._find_from_vpath(svn.realpath, srem) + c1, w, ftime_, fsize_, ip, at, un = self._find_from_vpath(svn.realpath, srem) c2 = self.cur.get(dvn.realpath) has_dupes = False @@ -4610,7 +4675,14 @@ class Up2k(object): with self.reg_mutex: has_dupes = self._forget_file( - svn.realpath, srem, c1, w, is_xvol, fsize_ or fsize, xlink + svn.realpath, + srem, + svn.flags, + c1, + w, + is_xvol, + fsize_ or fsize, + xlink, ) if not is_xvol: @@ -4631,7 +4703,7 @@ class Up2k(object): w, w, "", - "", + un or "", ip or "", at or 0, ) @@ -4662,7 +4734,7 @@ class Up2k(object): b1, b2 = fsenc(sabs), fsenc(dabs) is_link = os.path.islink(b1) # due to _relink try: - shutil.copy2(b1, b2) + trystat_shutil_copy2(self.log, b1, b2) except: try: wunlink(self.log, dabs, dvn.flags) @@ -4731,13 +4803,14 @@ class Up2k(object): Optional[int], str, Optional[int], + str, ]: cur = self.cur.get(ptop) if not cur: - return None, None, None, None, "", None + return None, None, None, None, "", None, "" rd, fn = vsplit(vrem) - q = "select w, mt, sz, ip, at from up where rd=? and fn=? limit 1" + q = "select w, mt, sz, ip, at, un from up where rd=? and fn=? limit 1" try: c = cur.execute(q, (rd, fn)) except: @@ -4746,14 +4819,15 @@ class Up2k(object): hit = c.fetchone() if hit: - wark, ftime, fsize, ip, at = hit - return cur, wark, ftime, fsize, ip, at - return cur, None, None, None, "", None + wark, ftime, fsize, ip, at, un = hit + return cur, wark, ftime, fsize, ip, at, un + return cur, None, None, None, "", None, "" def _forget_file( self, ptop: str, vrem: str, + vflags: dict[str, Any], cur: Optional["sqlite3.Cursor"], wark: Optional[str], drop_tags: bool, @@ -4778,7 +4852,7 @@ class Up2k(object): q = "delete from mt where w=?" cur.execute(q, (wark[:16],)) - self.db_rm(cur, srd, sfn, sz) + self.db_rm(cur, vflags, srd, sfn, sz) reg = self.registry.get(ptop) if reg: @@ -4867,7 +4941,10 @@ class Up2k(object): mt = bos.path.getmtime(slabs, False) flags = self.flags.get(ptop) or {} atomic_move(self.log, sabs, slabs, flags) - bos.utime(slabs, (int(time.time()), int(mt)), False) + try: + bos.utime(slabs, (int(time.time()), int(mt)), False) + except: + self.log("relink: failed to utime(%r, %s)" % (slabs, mt), 3) self._symlink(slabs, sabs, flags, False, is_mv=True) full[slabs] = (ptop, rem) sabs = slabs diff --git a/copyparty/util.py b/copyparty/util.py index b5a2c45e..da6bbce4 100644 --- a/copyparty/util.py +++ b/copyparty/util.py @@ -52,6 +52,7 @@ from .__init__ import ( VT100, WINDOWS, EnvParams, + unicode, ) from .__version__ import S_BUILD_DT, S_VERSION from .stolen import surrogateescape @@ -112,7 +113,13 @@ E_ACCESS = _ens("EACCES WSAEACCES") E_UNREACH = _ens("EHOSTUNREACH WSAEHOSTUNREACH ENETUNREACH WSAENETUNREACH") IP6ALL = "0:0:0:0:0:0:0:0" +IP6_LL = ("fe8", "fe9", "fea", "feb") +IP64_LL = ("fe8", "fe9", "fea", "feb", "169.254") +UC_CDISP = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789._" +BC_CDISP = UC_CDISP.encode("ascii") +UC_CDISP_SET = set(UC_CDISP) +BC_CDISP_SET = set(BC_CDISP) try: import fcntl @@ -399,6 +406,9 @@ application swf=x-shockwave-flash m3u=vnd.apple.mpegurl db3=vnd.sqlite3 sqlite=v text ass=plain ssa=plain image jpg=jpeg xpm=x-xpixmap psd=vnd.adobe.photoshop jpf=jpx tif=tiff ico=x-icon djvu=vnd.djvu image heic=heic-sequence heif=heif-sequence hdr=vnd.radiance svg=svg+xml +image arw=x-sony-arw cr2=x-canon-cr2 crw=x-canon-crw dcr=x-kodak-dcr dng=x-adobe-dng erf=x-epson-erf +image k25=x-kodak-k25 kdc=x-kodak-kdc mrw=x-minolta-mrw nef=x-nikon-nef orf=x-olympus-orf +image pef=x-pentax-pef raf=x-fuji-raf raw=x-panasonic-raw sr2=x-sony-sr2 srf=x-sony-srf x3f=x-sigma-x3f audio caf=x-caf mp3=mpeg m4a=mp4 mid=midi mpc=musepack aif=aiff au=basic qcp=qcelp video mkv=x-matroska mov=quicktime avi=x-msvideo m4v=x-m4v ts=mp2t video asf=x-ms-asf flv=x-flv 3gp=3gpp 3g2=3gpp2 rmvb=vnd.rn-realmedia-vbr @@ -2068,6 +2078,29 @@ def gencookie( ) +def gen_content_disposition(fn: str) -> str: + safe = UC_CDISP_SET + bsafe = BC_CDISP_SET + fn = fn.replace("/", "_").replace("\\", "_") + zb = fn.encode("utf-8", "xmlcharrefreplace") + if not PY2: + zbl = [ + chr(x).encode("utf-8") + if x in bsafe + else "%{:02X}".format(x).encode("ascii") + for x in zb + ] + else: + zbl = [unicode(x) if x in bsafe else "%{:02X}".format(ord(x)) for x in zb] + + ufn = b"".join(zbl).decode("ascii") + afn = "".join([x if x in safe else "_" for x in fn]).lstrip(".") + while ".." in afn: + afn = afn.replace("..", ".") + + return "attachment; filename=\"%s\"; filename*=UTF-8''%s" % (afn, ufn) + + def humansize(sz: float, terse: bool = False) -> str: for unit in HUMANSIZE_UNITS: if sz < 1024: @@ -2606,6 +2639,24 @@ def set_fperms(f: Union[typing.BinaryIO, typing.IO[Any]], vf: dict[str, Any]) -> os.fchown(fno, vf["uid"], vf["gid"]) +def trystat_shutil_copy2(log: "NamedLogger", src: bytes, dst: bytes) -> bytes: + try: + return shutil.copy2(src, dst) + except: + # ignore failed mtime on linux+ntfs; for example: + # shutil.py:437 <copy2>: copystat(src, dst, follow_symlinks=follow_symlinks) + # shutil.py:376 <copystat>: lookup("utime")(dst, ns=(st.st_atime_ns, st.st_mtime_ns), + # [PermissionError] [Errno 1] Operation not permitted, '/windows/_videos' + _, _, tb = sys.exc_info() + for _, _, fun, _ in traceback.extract_tb(tb): + if fun == "copystat": + if log: + t = "warning: failed to retain some file attributes (timestamp and/or permissions) during copy from %r to %r:\n%s" + log(t % (src, dst, min_ex()), 3) + return dst # close enough + raise + + def _fs_mvrm( log: "NamedLogger", src: str, dst: str, atomic: bool, flags: dict[str, Any] ) -> bool: @@ -2951,6 +3002,27 @@ def load_ipu( return ip_u, nm +def load_ipr( + log: "RootLogger", iprs: list[str], defer_mutex: bool = False +) -> dict[str, NetMap]: + ret = {} + for ipr in iprs: + try: + zs, uname = ipr.split("=") + cidrs = zs.split(",") + except: + t = "\n invalid value %r for argument --ipr; must be CIDR[,CIDR[,...]]=UNAME (192.168.0.0/16=amelia)" + raise Exception(t % (ipr,)) + try: + nm = NetMap(["::"], cidrs, True, True, defer_mutex) + except Exception as ex: + t = "failed to translate --ipr into netmap, probably due to invalid config: %r" + log("root", t % (ex,), 1) + raise + ret[uname] = nm + return ret + + def yieldfile(fn: str, bufsz: int) -> Generator[bytes, None, None]: readsz = min(bufsz, 128 * 1024) with open(fsenc(fn), "rb", bufsz) as f: @@ -3135,8 +3207,9 @@ def statdir( else: src = "listdir" fun: Any = os.lstat if lstat else os.stat + btop_ = os.path.join(btop, b"") for name in os.listdir(btop): - abspath = os.path.join(btop, name) + abspath = btop_ + name try: yield (fsdec(name), fun(abspath)) except Exception as ex: @@ -3175,7 +3248,9 @@ def rmdirs( stats = statdir(logger, scandir, lstat, top, False) dirs = [x[0] for x in stats if stat.S_ISDIR(x[1].st_mode)] - dirs = [os.path.join(top, x) for x in dirs] + if dirs: + top_ = os.path.join(top, "") + dirs = [top_ + x for x in dirs] ok = [] ng = [] for d in reversed(dirs): @@ -3578,7 +3653,7 @@ def runihook( verbose: bool, cmd: str, vol: "VFS", - ups: list[tuple[str, int, int, str, str, str, int]], + ups: list[tuple[str, int, int, str, str, str, int, str]], ) -> bool: _, chk, fork, jtxt, wait, sp_ka, acmd = _parsehook(log, cmd) bcmd = [sfsenc(x) for x in acmd] @@ -4151,7 +4226,7 @@ def _pkg_resource_exists(pkg: str, name: str) -> bool: def stat_resource(E: EnvParams, name: str): - path = os.path.join(E.mod, name) + path = E.mod_ + name if os.path.exists(path): return os.stat(fsenc(path)) return None @@ -4198,7 +4273,7 @@ def _has_resource(name: str): def has_resource(E: EnvParams, name: str): - return _has_resource(name) or os.path.exists(os.path.join(E.mod, name)) + return _has_resource(name) or os.path.exists(E.mod_ + name) def load_resource(E: EnvParams, name: str, mode="rb") -> IO[bytes]: @@ -4223,7 +4298,7 @@ def load_resource(E: EnvParams, name: str, mode="rb") -> IO[bytes]: stream = codecs.getreader(enc)(stream) return stream - ap = os.path.join(E.mod, name) + ap = E.mod_ + name if PY2: return codecs.open(ap, "r", encoding=enc) # type: ignore diff --git a/copyparty/web/baguettebox.js b/copyparty/web/baguettebox.js index adcf3239..931d5195 100644 --- a/copyparty/web/baguettebox.js +++ b/copyparty/web/baguettebox.js @@ -44,10 +44,12 @@ window.baguetteBox = (function () { loopA = null, loopB = null, url_ts = null, + un_pp = 0, resume_mp = false; var onFSC = function (e) { isFullscreen = !!document.fullscreenElement; + clmod(document.documentElement, 'bb_fsc', isFullscreen); }; var overlayClickHandler = function (e) { @@ -277,23 +279,30 @@ window.baguetteBox = (function () { if (modal.busy) return; - if (e.key == '?') - return halp(); - if (anymod(e, true)) return; - var k = (e.code || e.key) + '', v = vid(), pos = -1; + var k = (e.key || e.code) + '', v = vid(); - if (k == "BracketLeft") + if (k.startsWith('Key')) + k = k.slice(3); + else if (k.startsWith('Digit')) + k = k.slice(5); + + var kl = k.toLowerCase(); + + if (k == '?') + return halp(); + + if (k == "[" || k == "BracketLeft") setloop(1); - else if (k == "BracketRight") + else if (k == "]" || k == "BracketRight") setloop(2); - else if (e.shiftKey && k != "KeyR" && k != "R") + else if (e.shiftKey && kl != "r") return; - else if (k == "ArrowLeft" || k == "KeyJ" || k == "Left" || k == "j") + else if (k == "ArrowLeft" || k == "Left" || kl == "j") showPreviousImage(); - else if (k == "ArrowRight" || k == "KeyL" || k == "Right" || k == "l") + else if (k == "ArrowRight" || k == "Right" || kl == "l") showNextImage(); else if (k == "Escape" || k == "Esc") hideOverlay(); @@ -301,34 +310,37 @@ window.baguetteBox = (function () { showFirstImage(e); else if (k == "End") showLastImage(e); - else if (k == "Space" || k == "KeyP" || k == "KeyK") + else if (k == "Space" || k == "Spacebar" || kl == " " || kl == "p" || kl == "k") playpause(); - else if (k == "KeyU" || k == "KeyO") - relseek(k == "KeyU" ? -10 : 10); - else if (k.indexOf('Digit') === 0 && v) - v.currentTime = v.duration * parseInt(k.slice(-1)) * 0.1; - else if (k == "KeyM" && v) { + else if (kl == "u" || kl == "o") + relseek(kl == "u" ? -10 : 10); + else if (v && /^[0-9]$/.test(k)) + v.currentTime = v.duration * parseInt(k) * 0.1; + else if (kl == "m" && v) { v.muted = vmute = !vmute; mp_ctl(); } - else if (k == "KeyV" && v) { + else if (kl == "v" && v) { vloop = !vloop; vnext = vnext && !vloop; setVmode(); } - else if (k == "KeyC" && v) { + else if (kl == "c" && v) { vnext = !vnext; vloop = vloop && !vnext; setVmode(); } - else if (k == "KeyF") + else if (kl == "f") tglfull(); - else if (k == "KeyS" || k == "s") + else if (kl == "s") tglsel(); - else if (k == "KeyR" || k == "r" || k == "R") + else if (kl == "r") rotn(e.shiftKey ? -1 : 1); - else if (k == "KeyY") + else if (kl == "y") dlpic(); + else + return; + return ev(e); } function anim() { @@ -402,7 +414,7 @@ window.baguetteBox = (function () { if (isFullscreen) document.exitFullscreen(); else - (vid() || ebi('bbox-overlay')).requestFullscreen(); + ebi('bbox-overlay').requestFullscreen(); } catch (ex) { if (IPHONE) @@ -449,10 +461,12 @@ window.baguetteBox = (function () { if (anymod(e)) return; - var k = e.code + ''; + var k = (e.key || e.code) + ''; - if (k == "Space") - ev(e); + if (k == "Space" || k == "Spacebar" || k == " ") { + un_pp = Date.now(); + return ev(e); + } } var passiveSupp = false; @@ -777,8 +791,7 @@ window.baguetteBox = (function () { image.setAttribute('playsinline', '1'); // ios ignores poster image.onended = vidEnd; - image.onplay = function () { show_buttons(1); }; - image.onpause = function () { show_buttons(); }; + image.onplay = image.onpause = ppHandler; } image.alt = thumbnailElement ? thumbnailElement.alt || '' : ''; if (options.titleTag && imageCaption) @@ -793,6 +806,15 @@ window.baguetteBox = (function () { callback(); } + function ppHandler() { + var now = Date.now(); + if (now - un_pp < 50) { + un_pp = 0; + return playpause(); // browser undid space hotkey + } + show_buttons(this.paused ? 1 : 0); + } + function showNextImage(e) { ev(e); return show(currentIndex + 1); diff --git a/copyparty/web/browser.css b/copyparty/web/browser.css index b5506f39..35c822cc 100644 --- a/copyparty/web/browser.css +++ b/copyparty/web/browser.css @@ -571,6 +571,7 @@ pre, code, tt, #doc, #doc>code { overflow: hidden; width: 0; height: 0; + left: -10em; color: var(--bg); } html .ayjump:focus { @@ -881,6 +882,9 @@ html.y #path a:hover { #flogout { display: inline; } +html.dz #flogout { + margin-left: 1em; +} #goh+span { color: var(--bg-u5); padding-left: .5em; @@ -1388,6 +1392,7 @@ html.y #ops svg circle { .opview select { padding: .3em; margin: .2em .4em; + background: var(--bg-u3); } .opview input.err { color: var(--err-fg); @@ -1553,11 +1558,13 @@ html { #treepar { z-index: 1; position: fixed; + background: #fff; background: var(--tree-bg); left: -.96em; width: calc(.3em + var(--nav-sz) - var(--sbw)); border-bottom: 1px solid var(--bg-u5); overflow: hidden; + border-right: .5em solid #999\9; } #treepar.off { display: none; @@ -1930,6 +1937,11 @@ html.y #tree.nowrap .ntree a+a:hover { padding: 0; font-size: 1.5em; } +#fs_abrt { + margin-top: 1em; + text-shadow: 0; + box-shadow: 1px 1px 0 var(--bg-d3); +} #doc { overflow: visible; background: #fff; @@ -1988,6 +2000,7 @@ html.y #doc .line-highlight { } #seldoc.sel { color: var(--fg2-max); + background: #f0f; background: var(--g-sel-b1); } #pvol, @@ -2007,6 +2020,7 @@ a.btn, user-select: none; } #hkhelp { + background: #fff; background: var(--bg); } #hkhelp table { @@ -2120,6 +2134,13 @@ html.noscroll .sbar::-webkit-scrollbar { vertical-align: middle; transition: transform .23s, left .23s, top .23s, width .23s, height .23s; } +html.bb_fsc .full-image img, +html.bb_fsc .full-image video { + max-height: 100%; +} +html.bb_fsc figcaption { + display: none; +} .full-image img.nt, .full-image video.nt { transition: none; @@ -3272,3 +3293,793 @@ html.d #treepar { transition: background-color .3s ease, color .3s ease; } } +html.ey { + --negative-space: 0em; /* Use this to change the global spacing of the 95 theme */ + --font-main: consolas; + --font-serif: consolas; + --font-mono: consolas; + --w: #fff; + --w2: #dfdfdf; + --w3: grey; + --fg: #000; + --fg-max: #0000ff; + --fg-weak: #0000ff; + --bg: #c6c3c6; + --bg-d3: #ff0; + --bg-d2: var(--w3); + --bg-d1: var(--bg); + --bg-u2: var(--bg); + --bg-u3: var(--bg); + --bg-u5: var(--shadow-color-2); + --tab-alt: #00f; + --g-fsel-bg: #00f; + --g-sel-bg: #00f; + --g-fsel-b1: #fff; + --row-alt: var(--w); + --scroll: var(--silver); + --f-sel-sh: transparent; + --a: #000; + --a-b: #fff; + --a-hil: #fff; + --a-h-bg: var(--bg); + --a-dark: var(--a); + --a-gray: var(--fg-weak); + --btn-fg: var(--fg); + --btn-bg: var(--bg); + --btn-h-fg: var(--fg); + --btn-h-bg: var(--bg); + --btn-1-fg: var(--fg); + --btn-1-bg: var(--bg); + --btn-1h-bg: var(--bg-d3); + --txt-sh: a; + --txt-bg: var(--white); + --u2-b1-bg: var(--w2); + --u2-b2-bg: var(--w2); + --u2-txt-bg: var(--w2); + --u2-tab-bg: a; + --u2-tab-1-bg: var(--w2); + --sort-1: var(--fg-weak); + --tree-bg: var(--w); + --g-b1: a; + --g-b2: a; + --g-f-bg: var(--w2); + --f-sh1: 0.1; + --f-sh2: 0.02; + --f-sh3: 0.1; + --f-h-b1: a; + --srv-1: var(--w); + --srv-3: var(--a); + --mp-sh: a; + --black: #000; + --white: #fff; + --grey: grey; + --silver: silver; + --transparent: transparent; + --shadow-color-1: #0a0a0a; + --shadow-color-2: #808080; + --border-dashed-black: 1px dashed var(--black); + --radius: 0; + --focus-outline: 1px dashed var(--black); + --hover-outline: 1px dotted var(--black); + --fm-off: var(--w3); + --ttlbar: linear-gradient(90deg, navy, #1084d0); + --inset-bg: var(--white); + --scroll-bkg: var(--white); + + /*All sides*/ + --shadow-outset: inset -1px -1px var(--shadow-color-1), + inset 1px 1px var(--white), inset -2px -2px var(--grey), + inset 2px 2px var(--w2); + + --shadow-inset: inset -1px -1px var(--white), + inset 1px 1px var(--shadow-color-1), inset -2px -2px var(--w2), + inset 2px 2px var(--shadow-color-2); + + --shadow-input: inset -1px -1px var(--white), inset 1px 1px var(--grey), + inset -2px -2px var(--w2), inset 2px 2px var(--shadow-color-1); + + /*Indiv sides*/ + --shadow-outset-bottom: inset 0 -1px var(--shadow-color-1), + inset 0 -2px var(--grey); + --shadow-outset-right: inset -1px 0 var(--shadow-color-1), + inset -2px 0 var(--grey); + --shadow-outset-left: inset 1px 0 var(--white), inset 2px 0 var(--w2); + --shadow-outset-top: inset 0 1px var(--white), inset 0 2px var(--w2); + + --shadow-inset-bottom: inset 0 -1px var(--white), inset 0 -2px var(--w2); + --shadow-inset-right: inset -1px 0 var(--white), inset -2px 0 var(--w2); + --shadow-inset-left: inset 1px 0 var(--shadow-color-1), + inset 2px 0 var(--shadow-color-2); + --shadow-inset-top: inset 0 1px var(--shadow-color-1), + inset 0 2px var(--shadow-color-2); +} +html.ez { + --negative-space: 0em; /* Use this to change the global spacing of your theme :) */ + --font-main: consolas; + --font-serif: consolas; + --font-mono: consolas; + --w: #fff; + --w2: var(--inset-bg); + --w3: grey; + --fg: #cfcfcf; + --fg-max: #47b8ff; + --fg-weak: #47b8ff; + --bg: #383838; + --bg-d3: #600000; + --bg-d2: var(--shadow-color-1); + --bg-d1: var(--bg); + --u2-tab-1-fg: #ff0; + --bg-u2: var(--bg); + --bg-u3: var(--bg); + --bg-u5: var(--shadow-color-2); + --tab-alt: #47b8ff; + --g-fsel-bg: #0000b7; + --g-sel-bg: #00f; + --g-fsel-b1: #fff; + --row-alt: #555555; + --scroll: #555555; + --f-sel-sh: transparent; + --a: var(--fg); + --a-b: var(--fg); + --a-hil: var(--fg); + --btn-1h-bg: var(--bg-d3); + --a-h-bg: var(--bg); + --a-dark: var(--a); + --a-gray: var(--fg-weak); + --btn-fg: var(--white); + --btn-bg: var(--bg); + --btn-h-fg: var(--white); + --btn-h-bg: var(--bg); + --btn-1-fg: var(--white); + --btn-1-bg: var(--bg); + --txt-sh: a; + --u2-b1-bg: var(--w2); + --u2-b2-bg: var(--w2); + --u2-txt-bg: var(--w2); + --u2-tab-bg: a; + --u2-tab-1-bg: var(--w2); + --sort-1: var(--fg-weak); + --g-b1: a; + --g-b2: a; + --g-f-bg: var(--w2); + --f-sh1: 0.1; + --f-sh2: 0.02; + --f-sh3: 0.1; + --f-h-b1: a; + --srv-1: var(--w); + --srv-3: var(--a); + --mp-sh: a; + --black: #000; + --white: #fff; + --grey: grey; + --silver: #858585; + --transparent: transparent; + --shadow-color-1: #101010; + --shadow-color-2: #1f1f1f; + --border-dashed-black: 1px dashed var(--shadow-color-1); + --radius: 0; + --focus-outline: 1px dashed var(--white); + --hover-outline: 1px dotted var(--white); + --fm-off: var(--w3); + --ttlbar: linear-gradient(90deg, var(--shadow-color-1) 20%, #888888); + --inset-bg: #3f3f3f; + --tree-bg: var(--inset-bg); + --txt-bg: var(--inset-bg); + --scroll-bkg: var(--black); + + /*All sides*/ + --shadow-outset: inset -1px -1px var(--shadow-color-1), inset 1px 1px #878787, + inset -2px -2px var(--shadow-color-2), inset 2px 2px #575757; + + --shadow-inset: inset -1px -1px #878787, inset 1px 1px var(--shadow-color-1), + inset -2px -2px #575757, inset 2px 2px var(--shadow-color-2); + + --shadow-input: inset -1px -1px var(--white), + inset 1px 1px var(--shadow-color-2), inset -2px -2px #575757, + inset 2px 2px var(--shadow-color-1); + + --shadow-outset-bottom: inset 0 -1px var(--shadow-color-1), + inset 0 -2px var(--shadow-color-2); + --shadow-outset-right: inset -1px 0 var(--shadow-color-1), + inset -2px 0 var(--shadow-color-2); + --shadow-outset-left: inset 1px 0 #878787, inset 2px 0 #575757; + --shadow-outset-top: inset 0 1px #878787, inset 0 2px #575757; + + --shadow-inset-bottom: inset 0 -1px #878787, inset 0 -2px #575757; + --shadow-inset-right: inset -1px 0 #878787, inset -2px 0 #575757; + --shadow-inset-left: inset 1px 0 var(--shadow-color-1), + inset 2px 0 var(--shadow-color-2); + --shadow-inset-top: inset 0 1px var(--shadow-color-1), + inset 0 2px var(--shadow-color-2); +} + +html.e { + text-shadow: none; +} +html.e #files, +html.e #u2conf input[type="checkbox"]:hover + label, +html.e .tgl.btn.on:hover, +html.e body { + background: var(--bg); +} +html.e #pctl a, +html.e #repl, +html.e #u2conf a, +html.e #u2conf input[type="checkbox"] + label, +html.e #wfp a, +html.e .btn, +html.e .eq_step, +html.e input[type="submit"] { + box-shadow: var(--shadow-outset); + border-radius: var(--radius); + background: var(--bg); + border: 0; +} +a.s0r, +html.e #ghead a.s0, +html.e #u2conf input[type="checkbox"]:checked + label, +html.e .tgl.btn.on, +html.e input[type="submit"]:active { + box-shadow: var(--shadow-inset) !important; +} +html.e #ops a:hover, +html.e #pctl a:hover, +html.e #repl:hover, +html.e #u2conf a:hover, +html.e #u2conf input[type="checkbox"]:hover + label, +html.e #wfp a:hover, +html.e .btn:hover, +html.e .eq_step:hover, +html.e input[type="submit"]:hover { + outline: var(--hover-outline); + outline-offset: -4px; +} +html.e .ntree a:hover, +html.e :focus, +html.e :focus + label, +html.e a:active, +html.e tr:focus, +input[type="text"]:focus { + outline: var(--focus-outline) !important; +} +html.e tr:focus { + box-shadow: none; +} +html.e #pctl a:focus, +html.e #repl:hover, +html.e #u2conf input[type="checkbox"]:focus + label, +html.e #wfp a:focus, +html.e .btn:focus, +html.e .eq_step:focus { + border: 0 !important; + outline: var(--focus-outline) !important; + outline-offset: 2px; + box-shadow: var(--shadow-outset) !important; +} +html.e #files tbody, +html.e #u2cards a.act { + box-shadow: var(--shadow-inset); +} +html.e #files { + border: 2px groove var(--transparent); + box-sizing: border-box; + width: 100%; + padding: 0.3em; + top: 0; + border: 0; +} +html.e #files tbody tr td, +html.e #files thead th { + border-radius: var(--radius); +} +#files td { + background: var(--w2); +} +html.e #files tr { + background-color: var(--black); +} +html.e #srv_info span, +html.e label { + color: var(--btn-fg) !important; +} +html.e #acc_info { + background: var(--transparent); + color: var(--white); + height: 2em; + left: 1em; + width: fit-content; +} +html.e #acc_info, +html.e #ops, +html.e #srv_info { + display: flex; + align-items: center; +} +html.e #acc_info span.warn, +html.e #acc_info a { + color: var(--white); +} +html.e #flogout:before { + padding-left: 0.2em; + padding-right: 0.4em; + content: " | "; +} +html.e #blogout { + color: var(--w); + box-shadow: none; + background: transparent; +} +html.e .opwide > div { + border-left: 1px solid var(--fg); +} +html.e #srv_info { + background: var(--transparent); + color: var(--white); + height: fit-content; + top: 3.2em; + left: 1em; + gap: 0.2em; +} +html.e #u2cards a.act { + padding: 0.2em 1em; +} +html.e #u2btn { + border: var(--border-dashed-black); + border-radius: var(--border-radius); + transform: translateY(30%); +} +html.e #ops, +html.e #ops a { + border-radius: var(--radius); +} +@media only screen and (max-width: 600px) { + html.e #acc_info { + background: var(--transparent); + color: var(--white); + height: fit-content; + align-items: center; + top: 3.2em; + right: 1em; + left: auto; + display: flex; + gap: 0.2em; + } + html.e #u2btn { + transform: none; + } +} +html.e #ops { + background: var(--ttlbar); + /*HC*/ + box-shadow: inset 0-1px grey, inset 0-2px var(--shadow-color-1); + height: 2em; + gap: 0.6em; + padding: 0.2em; + flex-direction: row-reverse; + margin-bottom: 1.2em; +} +html.e #srch_form, +html.e .opbox { + padding-bottom: 1em; + padding-top: 1em; + max-width: 100vw; +} +html.e #ghead, +html.e #ops a { + align-items: center; + display: flex; +} +html.e #ops a { + text-shadow: 1px 1px 0 rgba(0, 0, 0, 0.5); + height: 1.4em; + padding: 0; + box-shadow: var(--shadow-outset); + background: var(--bg); + aspect-ratio: 1/1; + justify-content: center; + font-size: 1.25em; + z-index: 4; +} +html.e #blogout:focus, +html.e #ops a:focus { + outline: 1px dashed var(--w) !important; +} + +html.e #blogout:hover { + text-decoration: underline; +} + +html.e #ops > a:not(:first-child).act { + height: 1.4em; + width: 1.4em; + padding-bottom: 0.3em; + margin-top: 0.3em; + border-top-left-radius: 3px; + border-top-right-radius: 3px; + box-shadow: var(--shadow-inset-left), var(--shadow-inset-top), + var(--shadow-inset-right); + z-index: 6; +} +html.e #ops a.act { + box-shadow: var(--shadow-inset); + border-bottom: 0; +} +html.e a:active { + border: 0; +} +html.e :focus, +html.e :focus + label { + border: 0 !important; + outline-offset: 1px; + border-radius: var(--radius) !important; + box-shadow: inherit; +} +html.e #opa_x { + text-shadow: 0 0 0 var(--transparent) !important; + color: var(--bg) !important; + display: flex; +} +html.e #opa_x:before { + content: "โจฏ"; + color: var(--fg) !important; + margin-top: -0.1em; + font-size: 1.75em; + position: absolute; +} +html.e .opbox { + margin: -1.2em 0 0; + box-shadow: var(--shadow-inset-bottom), var(--shadow-inset-left), + var(--shadow-inset-right); + border-radius: var(--radius); + z-index: 5; + background: var(--bg); +} +html.e #srch_form { + margin: 0; + border-radius: var(--radius); +} +html.e #op_unpost { + max-width: 100vw; + margin: 0; +} +html.e label:focus { + box-shadow: 0 0; +} +html.e #tree { + box-shadow: none; + padding-right: 5px; +} +html.e #tt { + background: var(--w2); +} +html.e .mdo a { + background: 0 0; + text-decoration: underline; +} +html.e .mdo code, +html.e .mdo pre { + color: var(--white); + background: var(--w2); + border: 0; +} +html.e .mdo h1, +html.e .mdo h2 { + background: 0 0; + border-color: var(--w2); +} +html.e #tt, +html.e .mdo ol ol, +html.e .mdo ol ul, +html.e .mdo ul ol, +html.e .mdo ul ul { + border-color: var(--w2); +} +html.e .mdo li > em, +html.e .mdo p > em, +html.e .mdo td > em { + color: #fd0; +} +html.e input.txtbox, +html.e input[type="text"], +html.e select { + background-color: var(--txt-bg); + box-shadow: var(--shadow-input) !important; + box-sizing: border-box; + padding: 3px 4px; + border-radius: var(--radius); + border: 0; +} +html.e #gfiles { + box-shadow: var(--shadow-outset); + background: var(--bg); + padding: 0.4em; + display: flex; + flex-direction: column; + gap: 0.3em; +} +html.e #ggrid { + background-color: var(--inset-bg); + box-shadow: var(--shadow-input); + padding: 1.5em; + margin: 0; + overflow-x: scroll; +} +html.e #ghead { + margin: 0; + justify-content: flex-end; + gap: 0.4em; + padding: 0; + overflow: auto; + top: 0px; + border-radius: 0px; +} +html.e #ghead a { + margin: 0; + border-radius: var(--radius); +} +html.e ::-webkit-scrollbar, +html.e::-webkit-scrollbar { + width: 16px !important; + height: 16px !important; + background: var(--transparent) !important; +} +html.e ::-webkit-scrollbar-button, +html.e ::-webkit-scrollbar-thumb, +html.e::-webkit-scrollbar-button, +html.e::-webkit-scrollbar-thumb { + width: 16px !important; + height: 16px !important; + background: var(--scroll) !important; + /*HC*/ + box-shadow: var(--shadow-outset); + border: 1px solid !important; + border-color: var(--silver) var(--black) var(--black) var(--silver) !important; +} +html.e ::-webkit-scrollbar-track, +html.e::-webkit-scrollbar-track { + image-rendering: optimize-contrast !important; + background-image: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgLTAuNSAyIDIiIHNoYXBlLXJlbmRlcmluZz0iY3Jpc3BFZGdlcyI+CjxtZXRhZGF0YT5NYWRlIHdpdGggUGl4ZWxzIHRvIFN2ZyBodHRwczovL2NvZGVwZW4uaW8vc2hzaGF3L3Blbi9YYnh2Tmo8L21ldGFkYXRhPgo8cGF0aCBzdHJva2U9IiNjMGMwYzAiIGQ9Ik0wIDBoMU0xIDFoMSIgLz4KPC9zdmc+) !important; + background-position: 0 0 !important; + background-repeat: repeat !important; + background-size: 2px !important; + background: var(--scroll-bkg); +} +#tree::-webkit-scrollbar, +#tree::-webkit-scrollbar-track { + background: var(--scroll-bkg); +} +html.e ::-webkit-scrollbar-button, +html.e::-webkit-scrollbar-button { + background-repeat: no-repeat !important; + background-size: 16px !important; +} +html.e ::-webkit-scrollbar-button:single-button:vertical:decrement, +html.e::-webkit-scrollbar-button:single-button:vertical:decrement { + background-image: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgLTAuNSAxNiAxNiIgc2hhcGUtcmVuZGVyaW5nPSJjcmlzcEVkZ2VzIj4KPG1ldGFkYXRhPk1hZGUgd2l0aCBQaXhlbHMgdG8gU3ZnIGh0dHBzOi8vY29kZXBlbi5pby9zaHNoYXcvcGVuL1hieHZOajwvbWV0YWRhdGE+CjxwYXRoIHN0cm9rZT0iIzAwMDAwMCIgZD0iTTcgNWgxTTYgNmgzTTUgN2g1TTQgOGg3IiAvPgo8L3N2Zz4=) !important; +} +html.e ::-webkit-scrollbar-button:single-button:vertical:increment, +html.e::-webkit-scrollbar-button:single-button:vertical:increment { + background-image: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgLTAuNSAxNiAxNiIgc2hhcGUtcmVuZGVyaW5nPSJjcmlzcEVkZ2VzIj4KPG1ldGFkYXRhPk1hZGUgd2l0aCBQaXhlbHMgdG8gU3ZnIGh0dHBzOi8vY29kZXBlbi5pby9zaHNoYXcvcGVuL1hieHZOajwvbWV0YWRhdGE+CjxwYXRoIHN0cm9rZT0iIzAwMDAwMCIgZD0iTTQgNWg3TTUgNmg1TTYgN2gzTTcgOGgxIiAvPgo8L3N2Zz4=) !important; +} +html.e ::-webkit-scrollbar-button:single-button:horizontal:decrement, +html.e::-webkit-scrollbar-button:single-button:horizontal:decrement { + background-image: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgLTAuNSAxNiAxNiIgc2hhcGUtcmVuZGVyaW5nPSJjcmlzcEVkZ2VzIj4KPG1ldGFkYXRhPk1hZGUgd2l0aCBQaXhlbHMgdG8gU3ZnIGh0dHBzOi8vY29kZXBlbi5pby9zaHNoYXcvcGVuL1hieHZOajwvbWV0YWRhdGE+CjxwYXRoIHN0cm9rZT0iIzAwMDAwMCIgZD0iTTggM2gxTTcgNGgyTTYgNWgzTTUgNmg0TTYgN2gzTTcgOGgyTTggOWgxIiAvPgo8L3N2Zz4=) !important; +} +html.e ::-webkit-scrollbar-button:single-button:horizontal:increment, +html.e::-webkit-scrollbar-button:single-button:horizontal:increment { + background-image: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgLTAuNSAxNiAxNiIgc2hhcGUtcmVuZGVyaW5nPSJjcmlzcEVkZ2VzIj4KPG1ldGFkYXRhPk1hZGUgd2l0aCBQaXhlbHMgdG8gU3ZnIGh0dHBzOi8vY29kZXBlbi5pby9zaHNoYXcvcGVuL1hieHZOajwvbWV0YWRhdGE+CjxwYXRoIHN0cm9rZT0iIzAwMDAwMCIgZD0iTTYgM2gxTTYgNGgyTTYgNWgzTTYgNmg0TTYgN2gzTTYgOGgyTTYgOWgxIiAvPgo8L3N2Zz4=) !important; +} +html.e ::-webkit-scrollbar-corner, +html.e::-webkit-scrollbar-corner { + background: var(--silver) !important; +} +html, +html.e #tree { + scrollbar-color: inherit !important; +} +html.e #tree { + background: var(--bg); + padding-left: 0.4em; + padding-top: 0; + margin-left: var(--negative-space); +} +html.e.noscroll #tree { + /*HC*/ + box-shadow: 1px 1px var(--grey), 2px 2px var(--shadow-color-1), + var(--shadow-outset-bottom); +} +html.e #treeh { + background: var(--bg); + box-shadow: var(--shadow-outset-top), var(--shadow-outset-bottom); + width: calc(1.5em + var(--nav-sz) - var(--sbw)); + height: 2.4em; + border: none; + top: -2px; + display: flex; + align-items: center; + gap: 0.6em; +} +html.e #treeh .btn { + margin: 0px; + top: auto; +} +html.e #tree ul { + border-left: var(--border-dashed-black); + margin-left: 2.15em; +} +html.e .ntree a:first-child { + font-family: scp, monospace, monospace; + font-size: 1.2em; + line-height: 0; + background: var(--inset-bg); + aspect-ratio: 1/1; + text-align: center; + align-content: center; + border-radius: var(--radius) !important; + padding: 0.057em; + border: 1px solid var(--black); +} +html.e .ntree a:first-child:after { + content: "."; + position: absolute; + border-top: var(--border-dashed-black); + color: var(--transparent); + font-size: 0.9em; + margin-left: 0.13em; +} +html.e #treeul { + border: 0 !important; + position: static; + margin: 0 !important; + min-height: 100%; + height: max-content; +} +html.e .ntree a:last-of-type:before { + content: "๐Ÿ“"; + margin-left: 0.3em; +} +html.e .ntree { + padding-left: 1em !important; + padding-top: 0.3em !important; + background: var(--inset-bg); + box-shadow: var(--shadow-inset-left), var(--shadow-inset-bottom); +} +html.e #tree li { + margin-left: -0.5em; + border-top: 0; +} +html.e .ntree a:hover { + outline-offset: -2px; + color: var(--fg); + border-radius: var(--radius) !important; +} +html.e #treepar { + width: calc(-1em + var(--nav-sz) - var(--sbw)); + overflow: hidden; + left: -0.7em; + box-shadow: var(--shadow-inset-left), var(--shadow-inset-top); + border-left: 0 !important; + border-bottom: var(--border-dashed-black); + margin-left: calc(2.1em - (1em - var(--negative-space))) !important; +} +html.e #path, +html.e #widgeti, +html.e #wtoggle, +html.e #wtoggle a, +html.e #files, +html.e #files thead th, +html.e #ghead a, +html.e #tree { + box-shadow: var(--shadow-outset); +} +html.e.noscroll #treepar { + width: calc(var(--nav-sz) - 1em); +} +html.e #docul { + border-left: 0 !important; + margin-left: 0 !important; +} +html.e #wrap { + transform: translateX(calc((var(--negative-space) * 2) - 1.2em)); + padding-right: var(--negative-space); + position: relative; + margin-right: calc((var(--negative-space) * 2) - 1.2em); + margin-top: var(--negative-space); + margin-left: 1.2em; + /*overflow-x: auto; fix for OOB table when screen space is limited (mobile), but removes sticky header*/ +} +html.e input[type="radio"] { + accent-color: #232323; +} +html.e #path { + width: calc(100% - 0.4em); + display: flex; + align-items: center; + margin: 0; + padding: 0.2em; + overflow-x: auto; +} +html.e #path i { + border: 1px solid var(--w); + border-color: var(--w); + margin: 0; + border-width: 0.1em 0.1em 0 0; + height: 0.5em; + width: 0.5em; +}/* +html.e #hovertree:after { + color: red; + content: "BUGGY"; +html.ez #hovertree:after { + color: rgb(255 98 98); + content: "BUGGY"; +} +}*/ +html.e #widget { + box-shadow: 0 0; + border: 0 !important; +} +html.e #wtico, +html.e #zip1 { + box-shadow: 0 0 !important; +} +html.e #wtgrid { + top: -0.09em; +} +html.e #wfs, +html.e #wm3u, +html.e #wnp, +html.e #wzip { + border-width: 0 1px 0 0; +} +html.e #wfm.act + #wzip1 + #wzip, +html.e #wfm.act + #wzip1 + #wzip + #wnp { + border-left-width: 1px; +} +html.e #barpos { + /* border-radius: var(--radius); */ + box-shadow: var(--shadow-inset); +} +html.e #goh + span { + border-left: 0.1em solid var(--bg-u5); +} +html.e #wfp { + margin: var(--negative-space); + font-size: 0; + display: inline-block; +} +html.e #wfp a { + font-size: large; + display: inline-block; +} +html.e #repl { + font-size: large; + padding: 0.33em; + right: calc(var(--negative-space) * 0.89); + position: absolute; +} +html.e #epi { + text-align: center; + text-wrap-mode: nowrap; + margin: 0px; +} + +html.e #epi.logue:not(.mdo) { + padding: 0.8em; + box-shadow: var(--shadow-outset); +} + +html.e #epi.logue.mdo { + padding-left: 3px; +} + +html.e #doc { + box-shadow: var(--shadow-inset); + background: var(--inset-bg); + margin: 0.2em; + border-radius: var(--radius); +} + +html.e #detree { + padding: 0px; +} diff --git a/copyparty/web/browser.html b/copyparty/web/browser.html index ebf41f8e..3b5c89ba 100644 --- a/copyparty/web/browser.html +++ b/copyparty/web/browser.html @@ -9,7 +9,7 @@ <meta name="theme-color" content="#{{ tcolor }}"> <link rel="stylesheet" media="screen" href="{{ r }}/.cpr/ui.css?_={{ ts }}"> <link rel="stylesheet" media="screen" href="{{ r }}/.cpr/browser.css?_={{ ts }}"> -{{ html_head }} +{{- html_head }} {%- if css %} <link rel="stylesheet" media="screen" href="{{ css }}_={{ ts }}"> {%- endif %} diff --git a/copyparty/web/browser.js b/copyparty/web/browser.js index ec39438a..9315e3a0 100644 --- a/copyparty/web/browser.js +++ b/copyparty/web/browser.js @@ -114,6 +114,7 @@ var Ls = { "gou": 'parent folder">up', "gon": 'next folder">next', "logout": "Logout ", + "login": "Login", "access": " access", "ot_close": "close submenu", "ot_search": "search for files by attributes, path / name, music tags, or any combination of those$N$N<code>foo bar</code> = must contain both ยซfooยป and ยซbarยป,$N<code>foo -bar</code> = must contain ยซfooยป but not ยซbarยป,$N<code>^yana .opus$</code> = start with ยซyanaยป and be an ยซopusยป file$N<code>"try unite"</code> = contain exactly ยซtry uniteยป$N$Nthe date format is iso-8601, like$N<code>2009-12-31</code> or <code>2020-09-12 23:30:00</code>", @@ -300,9 +301,9 @@ var Ls = { "mt_mloop": "loop the open folder\">๐Ÿ” loop", "mt_mnext": "load the next folder and continue\">๐Ÿ“‚ next", "mt_mstop": "stop playback\">โธ stop", - "mt_cflac": "convert flac / wav to opus\">flac", - "mt_caac": "convert aac / m4a to opus\">aac", - "mt_coth": "convert all others (not mp3) to opus\">oth", + "mt_cflac": "convert flac / wav to {0}\">flac", + "mt_caac": "convert aac / m4a to {0}\">aac", + "mt_coth": "convert all others (not mp3) to {0}\">oth", "mt_c2opus": "best choice for desktops, laptops, android\">opus", "mt_c2owa": "opus-weba, for iOS 17.5 and newer\">owa", "mt_c2caf": "opus-caf, for iOS 11 through 17\">caf", @@ -427,6 +428,7 @@ var Ls = { "fcp_ok": "copy OK", "fp_busy": "moving {0} items...\n\n{1}", "fcp_busy": "copying {0} items...\n\n{1}", + "fp_abrt": "aborting...", "fp_err": "move failed:\n", "fcp_err": "copy failed:\n", "fp_confirm": "move these {0} items here?", @@ -744,6 +746,7 @@ var Ls = { "gou": 'naviger ett nivรฅ opp">opp', "gon": 'naviger til mappen etter denne">neste', "logout": "Logg ut ", + "login": "Logg inn", "access": " tilgang", "ot_close": "lukk verktรธy", "ot_search": "sรธk etter filer ved รฅ angi filnavn, mappenavn, tid, stรธrrelse, eller metadata som sangtittel / artist / osv.$N$N<code>foo bar</code> = inneholder bรฅde ยซfooยป og ยซbarยป,$N<code>foo -bar</code> = inneholder ยซfooยป men ikke ยซbarยป,$N<code>^yana .opus$</code> = starter med ยซyanaยป, filtype ยซopusยป$N<code>"try unite"</code> = ยซtry uniteยป eksakt$N$Ndatoformat er iso-8601, sรฅ f.eks.$N<code>2009-12-31</code> eller <code>2020-09-12 23:30:00</code>", @@ -853,7 +856,7 @@ var Ls = { "cl_hcancel": "kolonne-skjuling avbrutt", "ct_grid": '็”ฐ ikoner', - "ct_ttips": 'hvis hjelpetekst ved รฅ holde musen over ting">โ„น๏ธ tips', + "ct_ttips": 'vis hjelpetekst ved รฅ holde musen over ting">โ„น๏ธ tips', "ct_thumb": 'vis miniatyrbilder istedenfor ikoner$NSnarvei: T">๐Ÿ–ผ๏ธ bilder', "ct_csel": 'bruk tastene CTRL og SHIFT for markering av filer i ikonvisning">merk', "ct_ihop": 'bla ned til sist viste bilde nรฅr bildeviseren lukkes">gโฎฏ', @@ -878,8 +881,8 @@ var Ls = { "cut_az": "last opp filer i alfabetisk rekkefรธlge, istedenfor minste-fil-fรธrst$N$Nalfabetisk kan gjรธre det lettere รฅ anslรฅ om alt gikk bra, men er bittelitt tregere pรฅ fiber / LAN", - "cut_nag": "meldingsvarsel nรฅr opplastning er ferdig$N(kun on nettleserfanen ikke er synlig)", - "cut_sfx": "lydvarsel nรฅr opplastning er ferdig$N(kun on nettleserfanen ikke er synlig)", + "cut_nag": "meldingsvarsel nรฅr opplastning er ferdig$N(kun om nettleserfanen ikke er synlig)", + "cut_sfx": "lydvarsel nรฅr opplastning er ferdig$N(kun om nettleserfanen ikke er synlig)", "cut_mt": "raskere befaring ved รฅ bruke hele CPU'en$N$Ndenne funksjonen anvender web-workers$Nog krever mer RAM (opptil 512 MiB ekstra)$N$Ngjรธr https 30% raskere, http 4.5x raskere\">mt", @@ -913,7 +916,7 @@ var Ls = { "mt_loop": "spill den samme sangen om og om igjen\">๐Ÿ”", "mt_one": "spill kun รฉn sang\">1๏ธโƒฃ", "mt_shuf": "sangene i hver mappe$Nspilles i tilfeldig rekkefรธlge\">๐Ÿ”€", - "mt_aplay": "forsรธk รฅ starte avspilling hvis linken du klikket pรฅ for รฅ รฅpne nettsiden inneholder en sang-ID$N$Nhvis denne deaktiveres sรฅ vil heller ikke nettside-URLen bli oppdatert med sang-ID'er nรฅr musikk spilles, i tilfelle innstillingene skulle gรฅ tapt og nettsiden lastes pรฅ ny\">aโ–ถ", + "mt_aplay": "forsรธk รฅ starte avspilling hvis linken du klikket pรฅ for รฅ รฅpne nettsiden inneholder en sang-ID$N$Nhvis denne deaktiveres sรฅ vil heller ikke nettside-URL'en bli oppdatert med sang-ID'er nรฅr musikk spilles, i tilfelle innstillingene skulle gรฅ tapt og nettsiden lastes pรฅ ny\">aโ–ถ", "mt_preload": "hent ned litt av neste sang i forkant,$Nslik at pausen i overgangen blir mindre\">forles", "mt_prescan": "ved behov, bla til neste mappe$Nslik at nettleseren lar oss$Nfortsette รฅ spille musikk\">bla", "mt_fullpre": "hent ned hele neste sang, ikke bare litt:$Nโœ… skru pรฅ hvis nettet ditt er <b>ustabilt</b>,$NโŒ skru av hvis nettet ditt er <b>tregt</b>\">full", @@ -930,9 +933,9 @@ var Ls = { "mt_mloop": "repeter hele mappen\">๐Ÿ” gjenta", "mt_mnext": "hopp til neste mappe og fortsett\">๐Ÿ“‚ neste", "mt_mstop": "stopp avspilling\">โธ stopp", - "mt_cflac": "konverter flac / wav-filer til opus\">flac", - "mt_caac": "konverter aac / m4a-filer til to opus\">aac", - "mt_coth": "konverter alt annet (men ikke mp3) til opus\">andre", + "mt_cflac": "konverter flac / wav-filer til {0}\">flac", + "mt_caac": "konverter aac / m4a-filer til to {0}\">aac", + "mt_coth": "konverter alt annet (men ikke mp3) til {0}\">andre", "mt_c2opus": "det beste valget for alle PCer og Android\">opus", "mt_c2owa": "opus-weba, for iOS 17.5 og nyere\">owa", "mt_c2caf": "opus-caf, for iOS 11 tilogmed 17\">caf", @@ -1057,6 +1060,7 @@ var Ls = { "fcp_ok": "kopiering OK", "fp_busy": "flytter {0} filer...\n\n{1}", "fcp_busy": "kopierer {0} filer...\n\n{1}", + "fp_abrt": "avbryter...", "fp_err": "flytting feilet:\n", "fcp_err": "kopiering feilet:\n", "fp_confirm": "flytt disse {0} filene hit?", @@ -1373,6 +1377,7 @@ var Ls = { "gou": '้กถ้ƒจ">up', "gon": 'ไธ‹ไธ€้กน">next', "logout": " ็™ปๅ‡บ", + "login": "็™ปๅฝ•", //m "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> = ไปฅ ยซyamaยป ไธบๅผ€ๅคด็š„ ยซopusยป ๆ–‡ไปถ$N<code>"try unite"</code> = ๆญฃๅฅฝๅŒ…ๅซ ยซtry uniteยป$N$Nๆ—ถ้—ดๆ ผๅผไธบ iso-8601, ๆฏ”ๅฆ‚:$N<code>2009-12-31</code> or <code>2020-09-12 23:30:00</code>", @@ -1559,9 +1564,9 @@ var Ls = { "mt_mloop": "ๅพช็Žฏๆ‰“ๅผ€็š„ๆ–‡ไปถๅคน\">๐Ÿ” ๅพช็Žฏ", "mt_mnext": "ๅŠ ่ฝฝไธ‹ไธ€ไธชๆ–‡ไปถๅคนๅนถ็ปง็ปญ\">๐Ÿ“‚ ไธ‹ไธ€้ฆ–", "mt_mstop": "ๅœๆญขๆ’ญๆ”พ\">โธ ๅœๆญข", //m - "mt_cflac": "ๅฐ† flac / wav ่ฝฌๆขไธบ opus\">flac", - "mt_caac": "ๅฐ† aac / m4a ่ฝฌๆขไธบ opus\">aac", - "mt_coth": "ๅฐ†ๆ‰€ๆœ‰ๅ…ถไป–๏ผˆไธๆ˜ฏ mp3๏ผ‰่ฝฌๆขไธบ opus\">oth", + "mt_cflac": "ๅฐ† flac / wav ่ฝฌๆขไธบ {0}\">flac", + "mt_caac": "ๅฐ† aac / m4a ่ฝฌๆขไธบ {0}\">aac", + "mt_coth": "ๅฐ†ๆ‰€ๆœ‰ๅ…ถไป–๏ผˆไธๆ˜ฏ mp3๏ผ‰่ฝฌๆขไธบ {0}\">oth", "mt_c2opus": "้€‚ๅˆๆกŒ้ข็”ต่„‘ใ€็ฌ”่ฎฐๆœฌ็”ต่„‘ๅ’Œๅฎ‰ๅ“่ฎพๅค‡็š„ๆœ€ไฝณ้€‰ๆ‹ฉ\">opus", //m "mt_c2owa": "opus-weba๏ผˆ้€‚็”จไบŽ iOS 17.5 ๅŠๆ›ดๆ–ฐ็‰ˆๆœฌ๏ผ‰\">owa", //m "mt_c2caf": "opus-caf๏ผˆ้€‚็”จไบŽ iOS 11 ๅˆฐ iOS 17๏ผ‰\">caf", //m @@ -1611,7 +1616,7 @@ var Ls = { "f_dls": 'ๅฝ“ๅ‰ๆ–‡ไปถๅคนไธญ็š„ๆ–‡ไปถ้“พๆŽฅๅทฒ\nๆ›ดๆ”นไธบไธ‹่ฝฝ้“พๆŽฅ', - "f_partial": "่ฆๅฎ‰ๅ…จไธ‹่ฝฝๆญฃๅœจไธŠไผ ็š„ๆ–‡ไปถ๏ผŒ่ฏท็‚นๅ‡ปๆฒกๆœ‰ <code>.PARTIAL</code> ๆ–‡ไปถๆ‰ฉๅฑ•ๅ็š„ๅŒๅๆ–‡ไปถใ€‚่ฏทๆŒ‰ๅ–ๆถˆๆˆ– Escape ๆ‰ง่กŒๆญคๆ“ไฝœใ€‚\n\nๆŒ‰ OK / Enter ๅฐ†ๅฟฝ็•ฅๆญค่ญฆๅ‘Šๅนถ็ปง็ปญไธ‹่ฝฝ <code>.PARTIAL</code> ไธดๆ—ถๆ–‡ไปถ๏ผŒ่ฟ™ๅ‡ ไนŽ่‚ฏๅฎšไผšๅฏผ่‡ดๆ•ฐๆฎๆŸๅใ€‚", + "f_partial": "่ฆๅฎ‰ๅ…จไธ‹่ฝฝๆญฃๅœจไธŠไผ ็š„ๆ–‡ไปถ๏ผŒ่ฏท็‚นๅ‡ปๆฒกๆœ‰ <code>.PARTIAL</code> ๆ–‡ไปถๆ‰ฉๅฑ•ๅ็š„ๅŒๅๆ–‡ไปถใ€‚่ฏทๆŒ‰ๅ–ๆถˆๆˆ– Escape ๆ‰ง่กŒๆญคๆ“ไฝœใ€‚\n\nๆŒ‰ ็กฎๅฎš / Enter ๅฐ†ๅฟฝ็•ฅๆญค่ญฆๅ‘Šๅนถ็ปง็ปญไธ‹่ฝฝ <code>.PARTIAL</code> ไธดๆ—ถๆ–‡ไปถ๏ผŒ่ฟ™ๅ‡ ไนŽ่‚ฏๅฎšไผšๅฏผ่‡ดๆ•ฐๆฎๆŸๅใ€‚", "ft_paste": "็ฒ˜่ดด {0} ้กน$Nๅฟซๆท้”ฎ: ctrl-V", "fr_eperm": 'ๆ— ๆณ•้‡ๅ‘ฝๅ๏ผš\nไฝ ๅœจๆญคๆ–‡ไปถๅคนไธญๆฒกๆœ‰ โ€œ็งปๅŠจโ€ ๆƒ้™', @@ -1686,6 +1691,7 @@ var Ls = { "fcp_ok": "ๅคๅˆถๆˆๅŠŸ", //m "fp_busy": "ๆญฃๅœจ็งปๅŠจ {0} ้กน...\n\n{1}", "fcp_busy": "ๆญฃๅœจๅคๅˆถ {0} ้กน...\n\n{1}", //m + "fp_abrt": "ๆญฃๅœจไธญๆญข...", //m "fp_err": "็งปๅŠจๅคฑ่ดฅ๏ผš\n", "fcp_err": "ๅคๅˆถๅคฑ่ดฅ๏ผš\n", //m "fp_confirm": "ๅฐ†่ฟ™ไบ› {0} ้กน็งปๅŠจๅˆฐ่ฟ™้‡Œ๏ผŸ", @@ -2006,6 +2012,7 @@ var Ls = { "gou": 'nadล™azenรก sloลพka">nahoru', "gon": 'nรกsledujรญcรญ sloลพka">nรกsledujรญcรญ', "logout": "Odhlรกsit ", + "login": "Pล™ihlรกsit se", //m "access": " pล™รญstup", "ot_close": "zavล™รญt podnabรญdku", "ot_search": "hledat soubory podle atributลฏ, cesty / nรกzvu, hudebnรญch tagลฏ nebo jejich kombinace$N$N<code>foo bar</code> = musรญ obsahovat jak ยซfooยป tak ยซbarยป,$N<code>foo -bar</code> = musรญ obsahovat ยซfooยป ale ne ยซbarยป,$N<code>^yana .opus$</code> = zaฤรญnรก na ยซyanaยป a je to ยซopusยป soubor$N<code>"try unite"</code> = obsahuje pล™esnฤ› ยซtry uniteยป$N$Nformรกt data je iso-8601, jako$N<code>2009-12-31</code> nebo <code>2020-09-12 23:30:00</code>", @@ -2192,9 +2199,9 @@ var Ls = { "mt_mloop": "opakovat otevล™enou sloลพku\">๐Ÿ” loop", "mt_mnext": "naฤรญst dalลกรญ sloลพku a pokraฤovat\">๐Ÿ“‚ next", "mt_mstop": "zastavit pล™ehrรกvรกnรญ\">โธ stop", - "mt_cflac": "pล™evรฉst flac / wav na opus\">flac", - "mt_caac": "pล™evรฉst aac / m4a na opus\">aac", - "mt_coth": "pล™evรฉst vลกechny ostatnรญ (ne mp3) na opus\">oth", + "mt_cflac": "pล™evรฉst flac / wav na {0}\">flac", + "mt_caac": "pล™evรฉst aac / m4a na {0}\">aac", + "mt_coth": "pล™evรฉst vลกechny ostatnรญ (ne mp3) na {0}\">oth", "mt_c2opus": "nejlepลกรญ volba pro desktopy, laptopy, android\">opus", "mt_c2owa": "opus-weba, pro iOS 17.5 a novฤ›jลกรญ\">owa", "mt_c2caf": "opus-caf, pro iOS 11 aลพ 17\">caf", @@ -2211,7 +2218,7 @@ var Ls = { "mb_play": "pล™ehrรกt", "mm_hashplay": "pล™ehrรกt tento audio soubor?", - "mm_m3u": "stisknฤ›te <code>Enter/OK</code> pro Pล™ehrรกnรญ\nstisknฤ›te <code>ESC/Cancel</code> pro รšpravu", + "mm_m3u": "stisknฤ›te <code>Enter/OK</code> pro Pล™ehrรกnรญ\nstisknฤ›te <code>ESC/Zruลกit</code> pro รšpravu", "mp_breq": "potล™ebujete firefox 82+ nebo chrome 73+ nebo iOS 15+", "mm_bload": "nynรญ se naฤรญtรก...", "mm_bconv": "pล™evรกdรญ se na {0}, ฤekejte prosรญm...", @@ -2244,7 +2251,7 @@ var Ls = { "f_dls": 'odkazy na soubory v aktuรกlnรญ sloลพce byly\nzmฤ›nฤ›ny na odkazy ke staลพenรญ', - "f_partial": "Pro bezpeฤnรฉ staลพenรญ souboru, kterรฝ se aktuรกlnฤ› nahrรกvรก, kliknฤ›te prosรญm na soubor se stejnรฝm nรกzvem, ale bez pล™รญpony <code>.PARTIAL</code>. Stisknฤ›te prosรญm CANCEL nebo Escape.\n\nStisknutรญm OK / Enter ignorujete toto varovรกnรญ a pokraฤujete ve stahovรกnรญ <code>.PARTIAL</code> doฤasnรฉho souboru, coลพ tรฉmฤ›ล™ jistฤ› vyรบstรญ jako poลกkozenรก data.", + "f_partial": "Pro bezpeฤnรฉ staลพenรญ souboru, kterรฝ se aktuรกlnฤ› nahrรกvรก, kliknฤ›te prosรญm na soubor se stejnรฝm nรกzvem, ale bez pล™รญpony <code>.PARTIAL</code>. Stisknฤ›te prosรญm Zruลกit nebo Escape.\n\nStisknutรญm OK / Enter ignorujete toto varovรกnรญ a pokraฤujete ve stahovรกnรญ <code>.PARTIAL</code> doฤasnรฉho souboru, coลพ tรฉmฤ›ล™ jistฤ› vyรบstรญ jako poลกkozenรก data.", "ft_paste": "vloลพit {0} poloลพek$NKlรกvesovรก zkratka: ctrl-V", "fr_eperm": 'nelze pล™ejmenovat:\nnemรกte oprรกvnฤ›nรญ โ€œpล™esunoutโ€ v tรฉto sloลพce', @@ -2319,6 +2326,7 @@ var Ls = { "fcp_ok": "kopรญrovรกnรญ OK", "fp_busy": "pล™esouvรกm {0} poloลพek...\n\n{1}", "fcp_busy": "kopรญruji {0} poloลพek...\n\n{1}", + "fp_abrt": "pล™eruลกuji...", //m "fp_err": "pล™esun selhal:\n", "fcp_err": "kopรญrovรกnรญ selhalo:\n", "fp_confirm": "pล™esunout tฤ›chto {0} poloลพek sem?", @@ -2463,7 +2471,7 @@ var Ls = { "u_enoow": "pล™epsรกnรญ zde nebude fungovat; je vyลพadovรกno oprรกvnฤ›nรญ k mazรกnรญ", "u_badf": "Tฤ›chto {0} souborลฏ (z celkem {1}) bylo pล™eskoฤeno, pravdฤ›podobnฤ› kvลฏli oprรกvnฤ›nรญm v souborovรฉm systรฉmu:\n\n", "u_blankf": "Tฤ›chto {0} souborลฏ (z celkem {1}) je prรกzdnรฝch; pล™esto je nahrรกt?\n\n", - "u_applef": "Tฤ›chto {0} souborลฏ (z celkem {1}) je pravdฤ›podobnฤ› neลพรกdoucรญch;\nStisknฤ›te <code>OK/Enter</code> pro Pล˜ESKOฤŒENร nรกsledujรญcรญch souborลฏ,\nStisknฤ›te <code>Cancel/ESC</code> pro Zahrnutรญ a NAHRรNร i tฤ›chto souborลฏ:\n\n", + "u_applef": "Tฤ›chto {0} souborลฏ (z celkem {1}) je pravdฤ›podobnฤ› neลพรกdoucรญch;\nStisknฤ›te <code>OK/Enter</code> pro Pล˜ESKOฤŒENร nรกsledujรญcรญch souborลฏ,\nStisknฤ›te <code>Zruลกit/ESC</code> pro Zahrnutรญ a NAHRรNร i tฤ›chto souborลฏ:\n\n", "u_just1": "\nMoลพnรก to bude fungovat lรฉpe, kdyลพ vyberete pouze jeden soubor", "u_ff_many": "pokud pouลพรญvรกte <b>Linux / MacOS / Android,</b> takovรฉ mnoลพstvรญ souborลฏ <a href=\"https://bugzilla.mozilla.org/show_bug.cgi?id=1790500\" target=\"_blank\"><em>mลฏลพe</em> shodit Firefox!</a>\npokud se to stane, zkuste to prosรญm znovu (nebo pouลพijte Chrome).", "u_up_life": "Tento upload bude smazรกn ze serveru\n{0} po jeho dokonฤenรญ", @@ -2635,6 +2643,7 @@ var Ls = { "gou": 'zum รผbergeordneter Ordner springen">hoch', "gon": 'zum nรคchsten Ordner springen">nรคchst.', "logout": "Abmelden ", + "login": "Anmelden", //m "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>", @@ -2821,9 +2830,9 @@ var Ls = { "mt_mloop": "offenen Ordner wiederholen\">๐Ÿ” Schleife", "mt_mnext": "nรคchsten Ordner laden und fortfahren\">๐Ÿ“‚ nรคchster", "mt_mstop": "Wiedergabe beenden\">โธ Stop", - "mt_cflac": "FLAC / WAV zu OPUS konvertierebn\">flac", - "mt_caac": "AAC / M4A zu OPUS konvertieren\">aac", - "mt_coth": "Convertiere alle Dateien (die nicht MP3 sind) zu OPUS\">oth", + "mt_cflac": "FLAC / WAV zu {0} konvertierebn\">flac", + "mt_caac": "AAC / M4A zu {0} konvertieren\">aac", + "mt_coth": "Convertiere alle Dateien (die nicht MP3 sind) zu {0}\">oth", "mt_c2opus": "Beste Wahl fรผr Desktops, Laptops, Android\">opus", "mt_c2owa": "opus-weba, fรผr iOS 17.5 und neuer\">owa", "mt_c2caf": "opus-caf, fรผr iOS 11 bis 17\">caf", @@ -2948,6 +2957,7 @@ var Ls = { "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_err": "Verschieben fehlgeschlagen:\n", "fcp_err": "Kopieren fehlgeschlagen:\n", "fp_confirm": "Diese {0} Elemente hierher verschieben?", @@ -3264,6 +3274,7 @@ var Ls = { "gou": 'ylempi hakemisto">ylรถs', "gon": 'seuraava hakemisto">seur', "logout": "Kirjaudu ulos ", + "login": "Kirjaudu sisรครคn", //m "access": " -oikeudet", "ot_close": "sulje alavalikko", "ot_search": "etsi tiedostoja ominaisuuksien, tiedostopolun tai -nimen, musiikkitรคgien tai nรคiden yhdistelmรคn perusteella$N$N<code>foo bar</code> = tรคytyy sisรคltรครค sekรค ยซfooยป ettรค ยซbarยป,$N<code>foo -bar</code> = tรคytyy sisรคltรครค ยซfooยป mutta ei ยซbarยป,$N<code>^yana .opus$</code> = alkaa ยซyanaยป ja on ยซopusยป-tiedosto$N<code>"try unite"</code> = sisรคltรครค tรคsmรคlleen ยซtry uniteยป$N$Npรคivรคmรครคrรคn muoto on iso-8601, kuten$N<code>2009-12-31</code> tai <code>2020-09-12 23:30:00</code>", @@ -3450,9 +3461,9 @@ var Ls = { "mt_mloop": "toista avoinna olevaa hakemistoa loputtomasti\">๐Ÿ” alkuun", "mt_mnext": "lataa seuraava hakemisto ja jatka\">๐Ÿ“‚ seuraava", "mt_mstop": "pysรคytรค toisto\">โธ pysรคytรค", - "mt_cflac": "muunna flac / wav opus-muotoon\">flac", - "mt_caac": "muunna aac / m4a opus-muotoon\">aac", - "mt_coth": "muunna kaikki muut paitsi mp3 opus-muotoon\">muut", + "mt_cflac": "muunna flac / wav {0}-muotoon\">flac", + "mt_caac": "muunna aac / m4a {0}-muotoon\">aac", + "mt_coth": "muunna kaikki muut paitsi mp3 {0}-muotoon\">muut", "mt_c2opus": "paras valinta pรถytรคkoneille, kannettaville, androidille\">opus", "mt_c2owa": "opus-weba, iOS 17.5:lle ja uudemmille\">owa", "mt_c2caf": "opus-caf, iOS 11:lle - 17:lle\">caf", @@ -3532,7 +3543,7 @@ var Ls = { "fs_tsrc": "jaettava tiedosto tai hakemisto", "fs_ppwd": "valinnainen salasana", "fs_w8": "luodaan sharea...", - "fs_ok": "paina <code>Enter/OK</code> lisรคtรคksesi leikepรถydรคlle\npaina <code>ESC/Cancel</code> sulkeaksesi", + "fs_ok": "paina <code>Enter/OK</code> lisรคtรคksesi leikepรถydรคlle\npaina <code>ESC/Peruuta</code> sulkeaksesi", "frt_dec": "saattaa korjata joitakin rikkinรคisiรค tiedostonimiรค\">url-decode", "frt_rst": "palauta muokatut tiedostonimet takaisin alkuperรคisiksi\">โ†บ palauta", @@ -3577,6 +3588,7 @@ var Ls = { "fcp_ok": "kopiointi OK", "fp_busy": "siirretรครคn {0} kohdetta...\n\n{1}", "fcp_busy": "kopioidaan {0} kohdetta...\n\n{1}", + "fp_abrt": "keskeytetรครคn...", //m "fp_err": "siirto epรคonnistui:\n", "fcp_err": "kopiointi epรคonnistui:\n", "fp_confirm": "siirrรค nรคmรค {0} kohdetta tรคnne?", @@ -3721,7 +3733,7 @@ var Ls = { "u_enoow": "ylikirjoitus ei toimi tรครคllรค; tarvitaan โ€œDeleteโ€-oikeus", "u_badf": 'Nรคmรค {0} tiedostoa ({1} yhteensรค) ohitettiin, mahdollisesti tiedostojรคrjestelmรคn oikeuksien vuoksi:\n\n', "u_blankf": 'Nรคmรค {0} tiedostoa ({1} yhteensรค) ovat tyhjiรค; ladataanko ne silti?\n\n', - "u_applef": 'Nรคmรค {0} tiedostoa ({1} yhteensรค) ovat todennรคkรถisesti ei-toivottuja;\nPaina <code>OK/Enter</code> OHITTAAKSESI seuraavat tiedostot,\nPaina <code>Cancel/ESC</code> jos ET halua sulkea pois, ja LATAA nekin:\n\n', + "u_applef": 'Nรคmรค {0} tiedostoa ({1} yhteensรค) ovat todennรคkรถisesti ei-toivottuja;\nPaina <code>OK/Enter</code> OHITTAAKSESI seuraavat tiedostot,\nPaina <code>Peruuta/ESC</code> jos ET halua sulkea pois, ja LATAA nekin:\n\n', "u_just1": '\nEhkรค toimii paremmin jos valitset vain yhden tiedoston', "u_ff_many": "jos kรคytรคt <b>Linux / MacOS / Android,</b> niin tรคmรค mรครคrรค tiedostoja <a href=\"https://bugzilla.mozilla.org/show_bug.cgi?id=1790500\" target=\"_blank\"><em>saattaa</em> kaataa Firefoxin!</a>\njos niin kรคy, kokeile uudelleen (tai kรคytรค Chromea).", "u_up_life": "Tรคmรค lataus poistetaan palvelimelta\n{0} sen valmistumisen jรคlkeen", @@ -3785,6 +3797,637 @@ var Ls = { "lang_set": "ladataanko sivu uudestaan kielen vaihtamiseksi?", }, + "fra": { + "tt": "franรงais", + + "cols": { + "c": "bouton d'action", + "dur": "durรฉe", + "q": "qualitรฉ / dรฉbit binaire", + "Ac": "codec audio", + "Vc": "codec vidรฉo", + "Fmt": "format / conteneur", + "Ahash": "somme de contrรดle audio", + "Vhash": "somme de contrรดle vidรฉo", + "Res": "rรฉsolution", + "T": "type de fichier", + "aq": "qualitรฉ audio / dรฉbit binaire", + "vq": "qualitรฉ vidรฉo / dรฉbit binaire", + "pixfmt": "sous-รฉchantillonnage / structure de pixel", + "resw": "rรฉsolution horizontale", + "resh": "rรฉsolution verticale", + "chs": "canaux audio", + "hz": "frรฉquence" + }, + + "hks": [ + [ + "misc", + ["ร‰chap", "ferme divers menus"], + + "gestionaire de fichiers", + ["G", "activer vue en liste / vue en grille"], + ["T", "activer les miniatures / icรดnes"], + ["โ‡ง A/D", "taille des miniatures"], + ["ctrl-K", "suprimer la sรฉlection"], + ["ctrl-X", "couper la sรฉlection au presse-papier"], + ["ctrl-C", "copier la sรฉlection au presse-papier"], + ["ctrl-V", "coller (dรฉplacer/copier) ici"], + ["Y", "tรฉlรฉcharger la sรฉlection"], + ["F2", "renomer la sรฉlection"], + + "file-list-sel", + ["Espace", "activer la sรฉlection de fichiers"], + ["โ†‘/โ†“", "dรฉplacer le selecteur"], + ["ctrl โ†‘/โ†“", "dรฉplacer le curseur et la zone d'affichage"], + ["โ‡ง โ†‘/โ†“", "sรฉlectioner le fichier prรฉcรฉdent/suivant"], + ["ctrl-A", "sรฉlectionner tout les fichiers / dossiers"], + ], [ + "navigation", + ["B", "basculer la vue en fil d'Ariane / panneau de navigation"], + ["I/K", "dossier prรฉcรฉdent/suivant"], + ["M", "dossier parent (ou rรฉduire le dossier actuel)"], + ["V", "activer les dossiers / fichiers texte dans le volet de navigation"], + ["A/D", "taille du volet de navigation"], + ], [ + "lecteur-audio", + ["J/L", "chanson prรฉcรฉdente/suivante"], + ["U/O", "sauter 10s en arriรจre/avant"], + ["0..9", "sauter ร  0%..90%"], + ["P", "lecture/pause (dรฉmarre รฉgalement la lecture)"], + ["S", "sรฉlectionner la chanson en cours"], + ["Y", "tรฉlรฉcharger le morceau"], + ], [ + "visionneuse d'image", + ["J/L, โ†/โ†’", "image prรฉcรฉdente/suivante"], + ["Dรฉbut/Fin, โญฆ/Fin", "premiรจre/derniรจre image"], + ["F", "plein รฉcran"], + ["R", "rotation horaire"], + ["โ‡ง R", "rotation antihoraire"], + ["S", "sรฉlectionner l'image"], + ["Y", "tรฉlรฉcharger l'image"], + ], [ + "lecteur vidรฉo", + ["U/O", "sauter 10s en arriรจre/avant"], + ["P/K/Espace", "lecture/pause"], + ["C", "continuer de lire la suivante"], + ["V", "lire en boucle"], + ["M", "couper le son"], + ["[ and ]", "dรฉfinir l'intervalle de boucle"], + ], [ + "visionneuse de texte", + ["I/K", "fichier prรฉcรฉdent/suivant"], + ["M", "fermer le fichier texte"], + ["E", "modifier le fichier texte"], + ["S", "sรฉlectioner le fichier (pour le couper/copier/renommer)"], + ] + ], + + "m_ok": "OK", + "m_ng": "Annuler", + + "enable": "Activer", + "danger": "DANGER", + "clipped": "copiรฉ dans le presse-papier", + + "ht_s1": "seconde", + "ht_s2": "secondes", + "ht_m1": "minute", + "ht_m2": "minutes", + "ht_h1": "heure", + "ht_h2": "heures", + "ht_d1": "jour", + "ht_d2": "jours", + "ht_and": " et ", + + "goh": "panneau-de-commande", + "gop": 'รฉlรฉment "frรจre" prรฉcรฉdent">prรฉcรฉdent', + "gou": 'dossier parent">haut', + "gon": 'dossier suivant">suivant', + "logout": "Dรฉconnexion ", + "login": "Se connecter", //m + "access": " accรจs", + "ot_close": "fermer le sous-menu", + "ot_search": "chercher des fichiers par leurs attributs, chemin / nom, tag musicaux, ou nimporte quelle combinaison de ces options$N$N<code>foo bar</code> = doit contenir ร  la fois ยซfooยป et ยซbarยป,$N<code>foo -bar</code> = doit contenir ยซfooยป mais pas ยซbarยป,$N<code>^yana .opus$</code> = commence par ยซyanaยป et est un fichier ยซopusยป$N<code>"try unite"</code> = contient exactement ยซtry uniteยป$N$Nle format de date est iso-8601, comme$N<code>2009-12-31</code> ou <code>2020-09-12 23:30:00</code>", + "ot_unpost": "unpost: supprimer vos tรฉlรฉchargements rรฉcents, ou annuler ceux en cours", + "ot_bup": "bup: tรฉlรฉverseur de base, prend mรชme en charge netscape 4.0", + "ot_mkdir": "mkdir: crรฉer un nouveau rรฉpertoire", + "ot_md": "new-md: crรฉer un nouveau document markdown", + "ot_msg": "msg: envoyer un message au journal du serveur", + "ot_mp": "options du lecteur multimedia", + "ot_cfg": "options de configuration", + "ot_u2i": 'up2k : tรฉlรฉverser des fichiers (si vous avez un accรจs en รฉcriture) ou basculer en mode recherche pour voir s\'ils existent quelque part sur le serveur$N$Nles tรฉlรฉversements peuvent รชtre repris, ils sont multithreadรฉ, et les horodatages des fichiers sont prรฉservรฉs, mais cela utilise plus de CPU que [๐ŸŽˆ]  (le tรฉlรฉverseur de base)<br /><br />pendant les tรฉlรฉversements, cette icรดne devient un indicateur de progression!', + "ot_u2w": 'up2k : tรฉlรฉverser des fichiers avec prise en charge de la reprise (fermez votre navigateur et dรฉposez les mรชmes fichiers plus tard)$N$multithreadรฉ, et les horodatages des fichiers sont prรฉservรฉs, mais cela utilise plus de CPU que [๐ŸŽˆ]  (le tรฉlรฉverseur de base)<br /><br />pendant les tรฉlรฉversements, cette icรดne devient un indicateur de progression!', + "ot_noie": 'Utilisez Chrome / Firefox / Edge', + + "ab_mkdir": "crรฉer un nouveau rรฉpertoire", + "ab_mkdoc": "faire un nouveau document markdown", + "ab_msg": "envoyer un message au journal du serveur", + + "ay_path": "passer aux dossiers", + "ay_files": "passer aux fichiers", + + "wt_ren": "renommer les รฉlรฉments sรฉlectionnรฉs$NHotkey: F2", + "wt_del": "supprimer les รฉlรฉments sรฉlectionnรฉs$NHotkey: ctrl-K", + "wt_cut": "couper les รฉlรฉments sรฉlectionnรฉs <small>(puis coller ailleurs)</small>$NHotkey: ctrl-X", + "wt_cpy": "copier les รฉlรฉments sรฉlectionnรฉs dans le presse-papiers$N(pour les coller ailleurs)$NHotkey: ctrl-C", + "wt_pst": "coller une sรฉlection prรฉcรฉdemment coupรฉe / copiรฉe$NHotkey: ctrl-V", + "wt_selall": "sรฉlectionner tous les fichiers$NHotkey: ctrl-A (lorsque le fichier est sรฉlectionnรฉ)", + "wt_selinv": "inverser la sรฉlection", + "wt_zip1": "tรฉlรฉcharger ce dossier en tant qu'archive", + "wt_selzip": "tรฉlรฉcharger la sรฉlection en tant qu'archive", + "wt_seldl": "tรฉlรฉcharger la sรฉlection en tant que fichiers sรฉparรฉs$NHotkey: Y", + "wt_npirc": "copier les informations de la musique au format irc", + "wt_nptxt": "copier les informations de la musique en texte brut", + "wt_m3ua": "ajouter ร  la playlist m3u (cliquez sur <code>๐Ÿ“ปcopier</code> plus tard)", + "wt_m3uc": "copier la playlist m3u dans le presse-papiers", + "wt_grid": "basculer entre la vue en grille / liste$NHotkey: G", + "wt_prev": "musique prรฉcรฉdente$NHotkey: J", + "wt_play": "lecture / pause$NHotkey: P", + "wt_next": "musique suivante$NHotkey: L", + + "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_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", + "ut_srch": "ne pas rรฉellement tรฉlรฉverser, mais vรฉrifier si les fichiers existent dรฉjร $N sur le serveur (scannera tous les dossiers que vous pouvez lire)", + "ut_par": "mettre en pause les tรฉlรฉversements en le rรฉglant sur 0$N$Naugmenter si votre connexion est lente / ร  forte latence$N$Nle garder ร  1 sur le LAN ou si le HDD du serveur est un goulot d'รฉtranglement", + "ul_btn": "dรฉposer des fichiers / dossiers<br>ici (ou cliquez sur moi)", + "ul_btnu": "T ร‰ L ร‰ V E R S E R", + "ul_btns": "C H E R C H E R", + + "ul_hash": "somme de contrรดle", + "ul_send": "envoyer", + "ul_done": "terminรฉ", + "ul_idle1": "aucun tรฉlรฉversement n'est encore dans la file d'attente", + "ut_etah": "moyenne <em>hashing</em> vitesse, et temps estimรฉ jusqu'ร  la fin", + "ut_etau": "moyenne <em>upload</em> vitesse et temps estimรฉ jusqu'ร  la fin", + "ut_etat": "moyenne <em>total</em> vitesse et temps estimรฉ jusqu'ร  la fin", + + "uct_ok": "terminรฉ avec succรจs", + "uct_ng": "non rรฉussi : รฉchouรฉ / rejetรฉ / non trouvรฉ", + "uct_done": "terminรฉs et รฉchouรฉ combinรฉs", + "uct_bz": "hachage ou tรฉlรฉversement", + "uct_q": "inactif, en attente", + + "utl_name": "nom de fichier", + "utl_ulist": "liste", + "utl_ucopy": "copie", + "utl_links": "liens", + "utl_stat": "รฉtat", + "utl_prog": "progrรจs", + + // keep short: + "utl_404": "404", + "utl_err": "ERREUR", + "utl_oserr": "OS-ERREUR", + "utl_found": "trouvรฉ", + "utl_defer": "รฉtat", + "utl_yolo": "YOLO", + "utl_done": "terminรฉ", + + "ul_flagblk": "les fichiers ont รฉtรฉ ajoutรฉs ร  la file d'attente</b><br>cependant, il y a un processus up2k actif dans un autre onglet du navigateur,<br>en attente qu'il finisse d'abord", + "ul_btnlk": "la configuration du serveur a verrouillรฉ cette options dans cet รฉtat", + + "udt_up": "Tรฉlรฉverser", + "udt_srch": "Chercher", + "udt_drop": "dรฉposer ici", + + "u_nav_m": '<h6>aight, ques-que tu ร  ?</h6><code>Enter</code> = Fichiers (un ou plus)\n<code>ESC</code> = Un dossier (sous-dossiers inclus)', + "u_nav_b": '<a href="#" id="modal-ok">Fichiers</a><a href="#" id="modal-ng">Un dossier</a>', + + "cl_opts": "options", + "cl_themes": "thรจme", + "cl_langs": "langue", + "cl_ziptype": "tรฉlรฉchargement de dossier", + "cl_uopts": "up2k", + "cl_favico": "favicon", + "cl_bigdir": "gros dossiers", + "cl_hsort": "#sort", + "cl_keytype": "notation des touches", + "cl_hiddenc": "colonnes masquรฉes", + "cl_hidec": "masquer", + "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รฉ", + + "ct_grid": '็”ฐ grille', + "ct_ttips": 'โ—” โ—ก โ—”">โ„น๏ธ infobulles', + "ct_thumb": 'vue en grille, activer les icรดnes ou les miniatures$NHotkey: T">๐Ÿ–ผ๏ธ minia', + "ct_csel": 'utiliser CTRL et MAJ pour selectioner des fichiers en vue en grille">sel', + "ct_ihop": 'quand le visionneuse d\'image est fermรฉ, faire defiller vers le bas jusqu\'au dernier fichier">gโฎฏ', + "ct_dots": 'voir les fichiers cachรฉ (si le serveur le permet)">dotfiles', + "ct_qdel": 'ne demander qu\'une confirmation lors de la suppression de fichiers>qdel', + "ct_dir1st": 'trier les dossiers avant les fichiers">๐Ÿ“ first', + "ct_nsort": 'triage par numรฉrotation (pour les nom de fichiers qui sont numรฉrotรฉs)">nsort', + "ct_utc": 'voir tout les horodatage en format UTC">UTC', + "ct_readme": 'voir le fichier README.md dans le listage des dossiers">๐Ÿ“œ readme', + "ct_idxh": 'voir une version html (index.html) au-lieu du listage des dossiers normal">htm', + "ct_sbars": 'montrer la barre de defilement">โŸŠ', + + "cut_umod": "si un fichier existe dรฉjร  sur le server, mettre ร  jour l'horodatage de derniรจre modification du serveur pour qu'il corresponde ร  votre fichier local (nรฉcessite des autorisations d'รฉcriture et de suppression)\">re๐Ÿ“…", + + "cut_turbo": "le bouton yolo, vous ne voulez probablement PAS activer ceci:$N$Nutilisez ceci si vous tรฉlรฉchargez une grande quantitรฉ de fichiers et que vous devez redรฉmarrer pour une raison quelconque, et que vous souhaitez continuer le tรฉlรฉchargement dรจs que possible$N$Ncela remplace la vรฉrification de hachage par une simple <em>"est-ce que cela a la mรชme taille de fichier sur le serveur?"</em> donc si le contenu du fichier est diffรฉrent, il ne sera PAS tรฉlรฉchargรฉ$N$Nvous devriez dรฉsactiver cela lorsque le tรฉlรฉchargement est terminรฉ, puis "tรฉlรฉcharger" les mรชmes fichiers ร  nouveau pour laisser le client les vรฉrifier\">turbo", + + "cut_datechk": "n'a aucun effet ร  moins que le bouton turbo ne soit activรฉ$N$Nrรฉduit le facteur yolo d'un tout petit peu ; vรฉrifie si les horodatages des fichiers sur le serveur correspondent aux vรดtres$N$Ndevrait <em>thรฉoriquement</em> attraper la plupart des tรฉlรฉchargements inachevรฉs / corrompus, mais n'est pas un substitut ร  un passage de vรฉrification avec turbo dรฉsactivรฉ par la suite\">date-chk", + + "cut_u2sz": "taille (en MiB) de chaque morceau de tรฉlรฉversement; des grosse valeurs vont mieux passer si la distance entre le serveur et vous est trรฉs grande. Si vous avez une connection trรฉs instable, essayer de plus petites valeurs", + + "cut_flag": "s'assurer qu'un seul onglet est entrain de mettre un fichier en ligne a la fois $N -- les autres onglets doivent avoir cette option activรฉ aussi $N -- affecte seulement les onglets qui sont sur le mรชme domaine", + + "cut_az": "mettre en ligne les fichiers dans l'ordre alphabรฉtique, plutรดt que le plus petit fichier en premier$N$Nl'ordre alphabรฉtique peut rendre la lecture plus douce sur pour les yeux si quelque chose s'est mal passรฉ sur le serveur, mais cela rend le tรฉlรฉversement lรฉgรจrement plus lent sur fibre / LAN", + + "cut_nag": "recevoir une notification via l'OS quand un tรฉlรฉversement finit$N(seulement si le navigateur ou l'onglet n'est pas actif)", + "cut_sfx": "alerte audible quand le tรฉlรฉversement finit$N(seulement si le navigateur ou l'onglet n'est pas actif)", + + "cut_mt": "utiliser le calcul de somme de contrรดle multithreadรฉ pour accelerer le processus$N$Ncela utilise des web-workers et nรฉcessite$Nplus de RAM (jusqu'ร  512 MiB supplรฉmentaires)$N$NCela rend https 30% plus rapide, http 4.5x plus rapide\">mt", + + "cut_wasm": "utiliser wasm au lieu du hachage intรฉgrรฉ du navigateur; amรฉliore la vitesse sur les navigateurs basรฉs sur chrome mais augmente la charge CPU, et de nombreuses anciennes versions de chrome ont des bugs qui font que le navigateur consomme toute la RAM et plante si cela est activรฉ\">wasm", + + "cft_text": "text favicon (laisser vide et rafraรฎchir pour dรฉsactiver)", + "cft_fg": "couleur de premier plan", + "cft_bg": "couleur d'arriรจre-plan", + + "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.", + + "tt_entree": "afficher le panneau de navigation (arborescence des dossiers)$NHotkey: B", + "tt_detree": "afficher le fil dโ€™Ariane$NHotkey: B", + "tt_visdir": "faire dรฉfiler jusqu'au dossier sรฉlectionnรฉ", + "tt_ftree": "basculer l'arborescence des dossiers / fichiers texte$NHotkey: V", + "tt_pdock": "afficher les dossiers parents dans un panneau ancrรฉ en haut", + "tt_dynt": "croissance automatique ร  mesure que l'arborescence s'รฉtend", + "tt_wrap": "retour ร  la ligne", + "tt_hover": "rรฉvรฉler les lignes dรฉbordantes au survol$N( interrompt le dรฉfilement ร  moins que le curseur de la souris ne soit dans la gouttiรจre gauche )", + + "ml_pmode": "ร  la fin du dossierโ€ฆ", + "ml_btns": "cmds", + "ml_tcode": "transcoder", + "ml_tcode2": "transcoder vers", + "ml_tint": "teinte", + "ml_eq": "รฉgaliseur audio", + "ml_drc": "compresseur de plage dynamique", + + "mt_loop": "rรฉpรฉter en boucle une musique\">๐Ÿ”", + "mt_one": "stopper aprรจs une musique\">1๏ธโƒฃ", + "mt_shuf": "mรฉlanger les musiques dans chaque dossiers\">๐Ÿ”€", + "mt_aplay": "jouer automatiquement si le lien utilisรฉ pour accรฉder au serveur a un song-ID $N$N, dรฉsactiver cela arrรชtera รฉgalement la mise ร  jour de l'URL de la page avec les song-IDs lors de la lecture de la musique, pour รฉviter la lecture automatique si ces paramรจtres sont perdus mais que l'URL reste\">aโ–ถ", + "mt_preload": "commencer ร  charger la prochaine chanson prรจs de la fin pour une lecture sans interruption\">preload", + "mt_prescan": "explorer le dossier suivant avant la derniรจre musique$Nne finisse, pour garder le navigateur content$Npour qu'il n'arrรชte pas la lecture\">nav", + "mt_fullpre": "essayer de prรฉ-charger la musique entiรจre;$Nโœ… activer en cas de connection instable,$NโŒ dรฉsactiver en revanche sur une connection lente va probablement รชtre mieux\">full", + "mt_fau": "sur tรฉlรฉphone, empรชche la musique de s'arrรชter de jouer si la prochaine n'est pas prรฉ-chargรฉe assez rapidement (peut rendre l'affichage des tags buggรฉ)\">โ˜•๏ธ", + "mt_waves": "barre de progression en spectrograme:$Nmontrer l'amplitude audio dans la miniature\">~s", + "mt_npclip": "montrer les boutons pour copier le morceau en cours de lecture\">/np", + "mt_m3u_c": "montrer les boutons pour copier les$morceaux sรฉlectionnรฉes en tant qu'entrรฉes de playlist m3u8\">๐Ÿ“ป", + "mt_octl": "intรฉgration os (touches de raccourci multimรฉdia / osd)\">os-ctl", + "mt_oseek": "permettre la recherche via l'intรฉgration os$N$Nremarque : sur certains appareils (iPhones),$Ncela remplace le bouton de la chanson suivante\">seek", + "mt_oscv": "montrer la couverture de l'album dans l'osd\">art", + "mt_follow": "garder la piste en cours dรฉfilรฉe dans la vue\">๐ŸŽฏ", + "mt_compact": "contrรดles compacts\">โŸŽ", + "mt_uncache": "effacer le cache  (essayez ceci si votre navigateur a mis en cache$Nun copie dรฉfectueuse d'une chanson, ce qui empรชche sa lecture)\">uncache", + "mt_mloop": "lire en boucle le dossier ouvert\">๐Ÿ” loop", + "mt_mnext": "charger le dossier suivant et continuer\">๐Ÿ“‚ next", + "mt_mstop": "arrรชter la lecture\">โธ stop", + "mt_cflac": "convertir flac / wav en {0}\">flac", + "mt_caac": "convertir aac / m4a en {0}\">aac", + "mt_coth": "convertir tout les autres (pas mp3) en {0}\">oth", + "mt_c2opus": "meilleur choix pour PC fixe, PC portable, android\">opus", + "mt_c2owa": "opus-weba, pour iOS 17.5 et supรฉrieur\">owa", + "mt_c2caf": "opus-caf, pour iOS 11 ร  17\">caf", + "mt_c2mp3": "utilisez ceci sur des appareils trรจs anciens\">mp3", + "mt_c2flac": "meilleure qualitรฉ sonore, mais tรฉlรฉchargements รฉnormes\">flac", + "mt_c2wav": "lecture non compressรฉe (encore plus gros)\">wav", + "mt_c2ok": "bien, bon choix", + "mt_c2nd": "ce n'est pas le format de sortie recommandรฉ pour votre appareil, mais รงa devrait aller", + "mt_c2ng": "votre appareil ne semble pas prendre en charge ce format de sortie, mais essayons quand mรชme", + "mt_xowa": "il y a des bugs dans iOS qui empeche d'avoir une lecture en ariere plan en utilisant ce format; utilisez caf ou mp3 ร  la place", + "mt_tint": "niveau dโ€™arriรจre-plan (0โ€“100) de la barre de progression$Npour rendre la mise en mรฉmoire tampon moins gรชnante", + "mt_eq": "active l'รฉgaliseur et le contrรดle de gain;$N$Nboost <code>0</code> = volume standard 100% (non modifiรฉ)$N$Nwidth <code>1  </code> = stรฉrรฉo standard (non modifiรฉ)$Nwidth <code>0.5</code> = 50% de crossfeed gauche-droite$Nwidth <code>0  </code> = mono$N$Nboost <code>-0.8</code> & width <code>10</code> = suppression vocale :^)$N$Nl'activation de l'รฉgaliseur rend les albums gapless entiรจrement gapless, alors laissez-le activรฉ avec toutes les valeurs ร  zรฉro (sauf largeur = 1) si vous vous en souciez", + "mt_drc": "active le compresseur de plage dynamique (aplanisseur de volume / brickwaller); activera รฉgalement l'EQ pour รฉquilibrer les choses, donc dรฉfinissez tous les champs EQ sauf 'width' sur 0 si vous ne le voulez pas$N$Ndiminue le volume de l'audio au-dessus de THRESHOLD dB; pour chaque RATIO dB au-delร  de THRESHOLD, il y a 1 dB de sortie, donc des valeurs par dรฉfaut de tresh -24 et ratio 12 signifient qu'il ne devrait jamais รชtre plus fort que -22 dB et qu'il est sรปr d'augmenter le boost de l'รฉgaliseur ร  0.8, ou mรชme 1.8 avec ATK 0 et un รฉnorme RLS comme 90 (ne fonctionne que dans firefox; RLS est max 1 dans les autres navigateurs)$N$N(voir wikipedia, ils expliquent cela beaucoup mieux)", + + "mb_play": "lecture", + "mm_hashplay": "lire ce fichier audio ?", + "mm_m3u": "appuyez sur <code>Entrรฉe/OK</code> pour lire\nappuyez sur <code>ร‰chap/Annuler</code> pour modifier", + "mp_breq": "nรฉcessite firefox 82+ ou chrome 73+ ou iOS 15+", + "mm_bload": "chargement en coursโ€ฆ", + "mm_bconv": "conversion en {0}, veuillez patienterโ€ฆ", + "mm_opusen": "votre navigateur ne peut pas lire les fichiers aac / m4a ;\nle transcodage en opus est maintenant activรฉ", + "mm_playerr": "รฉchec de la lecture : ", + "mm_eabrt": "La tentative de lecture a รฉtรฉ annulรฉe", + "mm_enet": "Votre connexion internet est instable ou inexistante", + "mm_edec": "Ce fichier est supposรฉment corrompu??", + "mm_esupp": "Votre navigateur ne comprend pas ce format audio", + "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_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", + "mm_prescan": "En recherche d'une autre musique ร  lireโ€ฆ", + "mm_scank": "Prochaine musique trouvรฉe :", + "mm_uncache": "cache vidรฉ ; toutes les chansons seront retรฉlรฉchargรฉes lors de la prochaine lecture", + "mm_hnf": "cette chanson n'existe plus", + + "im_hnf": "cette image n'existe plus", + + "f_empty": 'ce dossier est vide', + "f_chide": 'ceci va cacher les colonnes ยซ{0}ยป\n\ntu peut les rรฉafficher dans les options', + "f_bigtxt": "ce fichier fait {0} MiB -- tu veut vraiment le voir en tant que texte ?", + "f_bigtxt2": "voir seulement la fin du fichier ร  la place ? ceci activera aussi le suivi en temps rรฉel, affichant les nouvelles lignes de texte au fur et ร  mesure", + "fbd_more": '<div id="blazy">showing <code>{0}</code> of <code>{1}</code> files; <a href="#" id="bd_more">show {2}</a> or <a href="#" id="bd_all">show all</a></div>', + "fbd_all": '<div id="blazy">showing <code>{0}</code> of <code>{1}</code> files; <a href="#" id="bd_all">show all</a></div>', + "f_anota": "seulement {0} des {1} elements sont selectionรฉ;\npour selectioner le dossier entier, fait dรฉfiler jusqu'au fond", + + "f_dls": 'le lien de fichier dans le rรฉpertoire actuel\nร  รฉtรฉ changรฉ en lien de tรฉlรฉchargement', + + "f_partial": "Pour tรฉlรฉcharger de faรงon sรฉcurisรฉe un fichier qui est entrain de se faire tรฉlรฉverser, cliquez sur le fichier qui a le mรชme nom, mais sans l'extension de fichier <code>.PARTIAL</code>. Choisissez ANNULER ou appuiez sur la touche ร‰chap pour faire cela.\n\nAppuyer sur OK / Entrรฉe ignorera cet avertissement et continuera ร  tรฉlรฉcharger le fichier temporaire <code>.PARTIAL</code> ร  la place, ce qui donnera presque certainement des donnรฉes corrompues.", + + "ft_paste": "coller {0} รฉlรฉments$NHotkey: ctrl-V", + "fr_eperm": 'impossible de renommer:\n vous n\'avez pas la permission โ€œmoveโ€ dans ce dossier', + "fd_eperm": 'impossible de supprimer:\nvous n\'avez pas la permission โ€œdeleteโ€ dans ce dossier', + "fc_eperm": 'impossible de couper:\nvous n\'avez pas la permission โ€œmoveโ€ dans ce dossier', + "fp_eperm": 'impossible de coller:\nvous n\'avez pas la permission โ€œwriteโ€ dans ce dossier', + "fr_emore": "sรฉlectionnez au moins un รฉlรฉment ร  renommer", + "fd_emore": "sรฉlectionnez au moins un รฉlรฉment ร  supprimer", + "fc_emore": "sรฉlectionnez au moins un รฉlรฉment ร  couper", + "fcp_emore": "sรฉlectionnez au moins un รฉlรฉment ร  copier dans le presse-papiers", + + "fs_sc": "partager le dossier dans lequel vous vous trouvez", + "fs_ss": "partager les fichiers sรฉlectionnรฉs", + "fs_just1d": "vous ne pouvez pas sรฉlectionner plus d'un dossier,\nou mรฉlanger des fichiers et des dossiers dans une seule sรฉlection", + "fs_abrt": "โŒ abandonner", + "fs_rand": "๐ŸŽฒ nom.aleatoire", + "fs_go": "โœ… crรฉer partage", + "fs_name": "nom", + "fs_src": "source", + "fs_pwd": "mdp", + "fs_exp": "expiration", + "fs_tmin": "min", + "fs_thrs": "heures", + "fs_tdays": "jours", + "fs_never": "รฉternel", + "fs_pname": "nom de lien optionnel ; sera alรฉatoire si vide", + "fs_tsrc": "le fichier ou le dossier ร  partager", + "fs_ppwd": "mot de passe optionnel", + "fs_w8": "crรฉation du partageโ€ฆ", + "fs_ok": "appuyez sur <code>Entrรฉe/OK</code> pour le Presse-papiers\nappuyez sur <code>ร‰chap/Annuler</code> pour fermer", + + "frt_dec": "peut potentiellement rรฉparer certaines instances de noms de fichiers cassรฉs\">url-decode", + "frt_rst": "rรฉinitialiser les noms de fichiers modifiรฉs ร  leurs originaux\">โ†บ reset", + "frt_abrt": "abandonner et fermer cette fenรชtre\">โŒ cancel", + "frb_apply": "APPLIQUER RENOMMER", + "fr_adv": "renommage par lot / mรฉtadonnรฉes / motif\">advanced", + "fr_case": "regex sensible ร  la casse\">case", + "fr_win": "noms windows-safe; remplacer <code><>:"\\|?*</code> par des caractรจres japonais en pleine largeur\">win", + "fr_slash": "remplacer <code>/</code> par un caractรจre qui ne provoque pas la crรฉation de nouveaux dossiers\">no /", + "fr_re": "modรจle de recherche regex ร  appliquer aux noms de fichiers originaux ; les groupes capturรฉs peuvent รชtre rรฉfรฉrencรฉs dans le champ de format ci-dessous comme <code>(1)</code> et <code>(2)</code> et ainsi de suite", + "fr_fmt": "inspirรฉ par foobar2000 : $N<code>(title)</code> est remplacรฉ par le titre de la chanson, $N<code>[(artist) - ](title)</code> saute [cette] partie si l'artiste est vide, $N<code>$lpad((tn),2,0)</code> remplit le numรฉro de piste ร  2 chiffres", + "fr_pdel": "supprimer", + "fr_pnew": "enregistrer sous", + "fr_pname": "donnez un nom pour le nouveau preset", + "fr_aborted": "abandonnรฉ", + "fr_lold": "ancien nom", + "fr_lnew": "nouveau nom", + "fr_tags": "tags pour les fichier selectionรฉ (lecture-seule, juste pour rรฉfรฉrence):", + "fr_busy": "renomage de {0} itemsโ€ฆ\n\n{1}", + "fr_efail": "renomage a รฉchouรฉ:\n", + "fr_nchg": "{0} des nouveaux noms ont รฉtรฉ modifiรฉs en raison de <code>win</code> et/ou <code>no /</code>\n\nOK pour continuer avec ces nouveaux noms modifiรฉs ?", + + "fd_ok": "suppression rรฉussie", + "fd_err": "impossible de supprimer:\n", + "fd_none": "rien n'a รฉtรฉ supprimรฉ ; peut-รชtre bloquรฉ par la configuration du serveur (xbd) ?", + "fd_busy": "suppression de {0} รฉlรฉmentsโ€ฆ\n\n{1}", + "fd_warn1": "SUPPRIMER ces {0} รฉlรฉments ?", + "fd_warn2": "<b>Derniรจre chance !</b> Impossible de revenir en arriรจre. Supprimer ?", + + "fc_ok": "couper {0} รฉlรฉments", + "fc_warn": 'couper {0} รฉlรฉments\n\nmais : seul <b>cet</b> onglets peut les coller\n(puisque la sรฉlection est si absolument massive)', + + "fcc_ok": "copiรฉ {0} รฉlรฉments dans le presse-papiers", + "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_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_emore": "il reste encore des collisions de noms de fichiers ร  corriger", + "fp_ok": "dรฉplacement OK", + "fcp_ok": "copie OK", + "fp_busy": "dรฉplacement de {0} รฉlรฉmentsโ€ฆ\n\n{1}", + "fcp_busy": "copie de {0} รฉlรฉmentsโ€ฆ\n\n{1}", + "fp_abrt": "abandon en cours...", //m + "fp_err": "deplacement รฉchouรฉ:\n", + "fcp_err": "copie รฉchouรฉe:\n", + "fp_confirm": "dรฉplacer ces {0} รฉlรฉments ici ?", + "fcp_confirm": "copier ces {0} รฉlรฉments ici ?", + "fp_etab": 'lecture du presse-papier venant d\'un autre onglet รฉchouรฉ', + "fp_name": "tรฉlรฉversement d'un fichier de votre apareil. Donnez lui un nom:", + "fp_both_m": '<h6>choisisez ce qu\'il faut coller</h6><code>Entrer</code> = Dรฉplacer {0} fichiers de ยซ{1}ยป\n<code>ESC</code> = Tรฉlรฉverser {2} fichiers de votre appareil', + "fcp_both_m": '<h6>choisissez ce qu\'il faut coller</h6><code>Entrer</code> = Copier {0} fichiers de ยซ{1}ยป\n<code>ESC</code> = Tรฉlรฉverser {2} fichiers de votre appareil', + "fp_both_b": '<a href="#" id="modal-ok">Dรฉplacer</a><a href="#" id="modal-ng">Tรฉlรฉverser</a>', + "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", + + "tv_load": "Chargement du document texte:\n\n{0}\n\n{1}% ({2} de {3} MiB chargรฉs)", + "tv_xe1": "impossible de charger le fichier texte:\n\nerreur", + "tv_xe2": "404, fichier introuvable", + "tv_lst": "liste des fichiers texte dans", + "tvt_close": "retour a la vue de dossier$NHotkey: M (ou ร‰chap)\">โŒ fermer", + "tvt_dl": "tรฉlรฉcharger ce fichier$NHotkey: Y\">๐Ÿ’พ tรฉlรฉcharger", + "tvt_prev": "montrer le document prรฉcรฉdent$NHotkey: i\">โฌ† prรฉcรฉdent", + "tvt_next": "montrer le document suivant$NHotkey: K\">โฌ‡ suivant", + "tvt_sel": "sรฉlectionner le fichier   ( pour couper / copier / supprimer / โ€ฆ )$NHotkey: S\">sel", + "tvt_edit": "ouvrir le fichier dans l'รฉditeur de texte$NHotkey: E\">โœ๏ธ modifier", + "tvt_tail": "surveiller le fichier pour les changements; montrer les nouvelles lignes en temps rรฉel\">๐Ÿ“ก suivre", + "tvt_wrap": "retour ร  la ligne\">โ†ต", + "tvt_atail": "ancrer le dรฉfilement au fond de la page\">โš“", + "tvt_ctail": "dรฉcoder les couleurs du terminal (ansi escape codes)\">๐ŸŒˆ", + "tvt_ntail": "limite de dรฉfilement en arriรจre (combien d'octets de texte ร  garder chargรฉ)", + + "m3u_add1": "musique ajoutรฉe ร  la playlist m3u", + "m3u_addn": "{0} musiques ajoutรฉes ร  la playlist m3u", + "m3u_clip": "la playlist m3u est maintenant copiรฉe dans le presse-papier\n\nvous devriez crรฉer un nouveau fichier texte nommรฉ par exemple playlist.m3u et coller la playlist dans ce fichier ; cela la rendra lisible en tant que playlist", + + "gt_vau": "ne pas voir les vidรฉos, juste jouer l'audio\">๐ŸŽง", + "gt_msel": "activer la sรฉlรฉction de fichiers ; ctrl-clic sur un fichier pour override รฉcraser$N$N<em>quand actif : double-cliquer sur un fichier / dossier pour l'ouvrir</em>$N$NHotkey: S\">multisรฉlรฉction", + "gt_crop": "rogner les miniatures au centre\">rogner", + "gt_3x": "miniatures haute rรฉsolution\">3x", + "gt_zoom": "zoomer", + "gt_chop": "rogner", + "gt_sort": "trier par", + "gt_name": "nom", + "gt_sz": "taille", + "gt_ts": "date", + "gt_ext": "type", + "gt_c1": "tronquer les noms de fichiers (montrer moins)", + "gt_c2": "tronquer les noms de fichiers (montrer plus)", + + "sm_w8": "rechercheโ€ฆ", + "sm_prev": "les rรฉsultats de recherche ci-dessous proviennent d'une requรชte prรฉcรฉdente:\n ", + "sl_close": "fermer les rรฉsultats de recherche", + "sl_hits": "affichage de {0} rรฉsultats", + "sl_moar": "chercher plus", + + "s_sz": "taille", + "s_dt": "date", + "s_rd": "chemin", + "s_fn": "nom", + "s_ta": "tags", + "s_ua": "up@", + "s_ad": "adv.", + "s_s1": "minimum MiB", + "s_s2": "maximum MiB", + "s_d1": "min. iso8601", + "s_d2": "max. iso8601", + "s_u1": "tรฉlรฉverser aprรจs", + "s_u2": "et/ou avant", + "s_r1": "le chemin contient   (sรฉparรฉ par des espaces)", + "s_f1": "le nom contient   (nรฉgation avec -nope)", + "s_t1": "les tags contiennent   (^=dรฉbut, fin=$)", + "s_a1": "propriรฉtรฉs de mรฉtadonnรฉes spรฉcifiques", + + "md_eshow": "impossible d'afficher le rendu ", + "md_off": "[๐Ÿ“œ<em>readme</em>] disabled in [โš™๏ธ] -- document cachรฉ", + + "badreply": "ร‰chec de l'analyse de la rรฉponse du serveur", + + "xhr403": "403: Accรจs refusรฉ\n\nessayez d'appuyer sur F5, peut-รชtre que vous avez รฉtรฉ dรฉconnectรฉ", + "xhr0": "inconnu (vous avez probablement perdu la connexion au serveur, ou le serveur est hors ligne)", + "cf_ok": "dรฉsolรฉ pour cela -- la protection DD" + wah + "oS a รฉtรฉ dรฉclenchรฉ\n\nles choses devraient reprendre dans environ 30 secondes\n\nsi rien ne se passe, appuyez sur F5 pour recharger la page", + "tl_xe1": "impossible de lister les sous-dossiers:\n\nerreur ", + "tl_xe2": "404: Dossier introuvable", + "fl_xe1": "impossible de lister les fichiers dans le dossier:\n\nerreur ", + "fl_xe2": "404: Dossier introuvable", + "fd_xe1": "impossible de crรฉer le sous-dossier:\n\nerreur ", + "fd_xe2": "404: Dossier parent introuvable", + "fsm_xe1": "impossible d'envoyer le message:\n\nerreur ", + "fsm_xe2": "404: Dossier parent introuvable", + "fu_xe1": "รฉchec du chargement de la liste des unpost du serveur:\n\nerreur ", + "fu_xe2": "404: Fichier introuvable??", + + "fz_tar": "fichier gnu-tar non compressรฉ (linux / mac)", + "fz_pax": "tar au format pax non compressรฉ (plus lent)", + "fz_targz": "gnu-tar avec compression gzip niveau 3$N$Ncela est gรฉnรฉralement trรจs lent, donc$Nutilisez plutรดt tar non compressรฉ", + "fz_tarxz": "gnu-tar avec compression xz niveau 1$N$Ncela est gรฉnรฉralement trรจs lent, donc$Nutilisez plutรดt tar non compressรฉ", + "fz_zip8": "zip avec noms de fichiers utf8 (peut รชtre instable sur windows 7 et versions antรฉrieures)", + "fz_zipd": "zip avec noms de fichiers cp437 traditionnels, pour les trรจs anciens logiciels", + "fz_zipc": "cp437 avec crc32 calculรฉ tรดt,$Nfor MS-DOS PKZIP v2.04g (octobre 1993)$N(prend plus de temps ร  charger avant que le tรฉlรฉchargement ne commence)", + + "un_m1": "vous pouvez supprimer vos tรฉlรฉchargements rรฉcents (ou annuler ceux en cours) ci-dessous", + "un_upd": "rafraรฎchir", + "un_m4": "ou partager les fichiers visibles ci-dessous:", + "un_ulist": "montrer", + "un_ucopy": "copier", + "un_flt": "filtre optionnel:  l'URL doit contenir", + "un_fclr": "effacer le filtre", + "un_derr": 'รฉchec de l\'unpost-delete:\n', + "un_f5": 'quelque chose a cassรฉ, veuillez essayer de rafraรฎchir ou d\'appuyer sur F5', + "un_uf5": "dรฉsolรฉ mais vous devez rafraรฎchir la page (par exemple en appuyant sur F5 ou CTRL-R) avant que ce tรฉlรฉchargement puisse รชtre annulรฉ", + "un_nou": '<b>warning:</b> serveur trop occupรฉ pour afficher les tรฉlรฉversements non finis; cliquez sur le lien "rafraรฎchir" dans un instant', + "un_noc": '<b>warning:</b> unpost des fichiers entiรจrement tรฉlรฉchargรฉs n\'est pas activรฉ/permis dans la configuration du serveur', + "un_max": "affichage des 2000 premiers fichiers (utilisez le filtre)", + "un_avail": "{0} tรฉlรฉchargements rรฉcents peuvent รชtre supprimรฉs<br />{1} ceux en cours peuvent รชtre annulรฉs", + "un_m2": "triรฉs par date de tรฉlรฉchargement; les plus rรฉcents en premier:", + "un_no1": "sike! aucun tรฉlรฉchargement n'est suffisamment rรฉcent", + "un_no2": "sike! aucun tรฉlรฉchargement correspondant ร  ce filtre n'est suffisamment rรฉcent", + "un_next": "supprimer les {0} fichiers suivants ci-dessous", + "un_abrt": "abandonner", + "un_del": "supprimer", + "un_m3": "chargement de vos tรฉlรฉchargements rรฉcentsโ€ฆ", + "un_busy": "suppression de {0} fichiersโ€ฆ", + "un_clip": "{0} liens copiรฉs dans le presse-papiers", + + "u_https1": "vous devriez", + "u_https2": "passer ร  https", + "u_https3": "pour de meilleure performances", + "u_ancient": 'votre navigateur est impressionnamment ancien -- vous devriez peut-รชtre <a href="#" onclick="goto(\'bup\')">utiliser bup ร  la place</a>', + "u_nowork": "nรฉcessite firefox 53+ ou chrome 57+ ou iOS 11+", + "tail_2old": "nรฉcessite firefox 105+ ou chrome 71+ ou iOS 14.5+", + "u_nodrop": 'votre navigateur est trop ancien pour le tรฉlรฉversement par glisser-dรฉposer', + "u_notdir": "ce n'est pas un dossier!\n\nvotre navigateur est trop ancien,\nveuillez essayer le glisser-dรฉposer ร  la place", + "u_uri": "pour glisser-dรฉposer des images depuis d'autres fenรชtres de navigateur,\nveuillez les dรฉposer sur le gros bouton de tรฉlรฉversement", + "u_enpot": 'passer ร  <a href="#">l\'interface utilisateur potato</a> (peut amรฉliorer la vitesse de tรฉlรฉversement)', + "u_depot": 'passer ร  <a href="#">l\'interface utilisateur fancy</a> (peut rรฉduire la vitesse de tรฉlรฉversement)', + "u_gotpot": 'passage ร  l\'interface utilisateur potato pour une vitesse de tรฉlรฉversement amรฉliorรฉe,\n\nn\'hรฉsitez pas ร  revenir en arriรจre si รงa ne vous plaรฎt pas !', + "u_pott": "<p>fichiers:   <b>{0}</b> fini,   <b>{1}</b> รฉchouรฉ,   <b>{2}</b> en cours,   <b>{3}</b> en attente</p>", + "u_ever": "ceci est le tรฉlรฉverseur de base ; up2k nรฉcessite au moins chrome 21 // firefox 13 // edge 12 // opera 12 // safari 5.1", + "u_su2k": 'ceci est le tรฉlรฉverseur de base; <a href="#" id="u2yea">up2k</a> est meilleur', + "u_uput": 'optimiser pour la vitesse (ignorer la somme de contrรดle)', + "u_ewrite": 'vous n\'avez pas accรจs en รฉcriture ร  ce dossier', + "u_eread": 'vous n\'avez pas accรจs en lecture ร  ce dossier', + "u_enoi": 'la recherche de fichiers n\'est pas activรฉe dans la configuration du serveur', + "u_enoow": "l'รฉcrasage ne fonctionnera pas ici; besoin de permissions de suppression", + "u_badf": 'Ces {0} fichiers (sur {1} au total) ont รฉtรฉ ignorรฉs, probablement en raison de permissions systรจme de fichiers:\n\n', + "u_blankf": 'Ces {0} fichiers (sur {1} au total) sont vides; les tรฉlรฉverser quand mรชme ?\n\n', + "u_applef": 'Ces {0} fichiers (sur {1} au total) sont probablement indรฉsirables;\nAppuyez sur <code>OK/Enter</code> pour IGNORER les fichiers suivants,\nAppuyez sur <code>Annuler/ร‰chap</code> pour NE PAS exclure, et Tร‰Lร‰VERSER ceux-ci รฉgalement:\n\n', + "u_just1": '\nPeut-รชtre que cela fonctionne mieux si vous sรฉlectionnez juste un fichier', + "u_ff_many": "si vous utilisez <b>Linux / MacOS / Android,</b> alors ce nombre de fichiers <a href=\"https://bugzilla.mozilla.org/show_bug.cgi?id=1790500\" target=\"_blank\"><em>peut</em> faire planter Firefox!</a>\nSi cela se produit, veuillez rรฉessayer (ou utiliser Chrome).", + "u_up_life": "Ce tรฉlรฉversement va รชtre supprimรฉ du serveur\n{0} aprรจs son achรจvement", + "u_asku": 'tรฉlรฉverser ces {0} fichiers vers <code>{1}</code>', + "u_unpt": "vous pouvez dรฉfaire / supprimer ce tรฉlรฉversement en utilisant le ๐Ÿงฏ en haut ร  gauche", + "u_bigtab": 'sur le point d\'afficher {0} fichiers\n\ncela peut faire planter votre navigateur, รชtes-vous sรปr ?', + "u_scan": 'Analyse des fichiersโ€ฆ', + "u_dirstuck": 'l\'itรฉrateur de rรฉpertoire est bloquรฉ en essayant d\'accรฉder aux {0} รฉlรฉments suivants ; il sera ignorรฉ :', + "u_etadone": 'Terminรฉ ({0}, {1} fichiers)', + "u_etaprep": '(prรฉparation au tรฉlรฉversement)', + "u_hashdone": 'calcul de la somme de contrรดle terminรฉ', + "u_hashing": 'calcul de la somme de contrรดle', + "u_hs": 'รฉtablissement d\'une liaisonโ€ฆ', + "u_started": "les fichiers sont maintenant en cours de tรฉlรฉversement ; voir [๐Ÿš€]", + "u_dupdefer": "dupliquรฉ ; sera traitรฉ aprรจs tous les autres fichiers", + "u_actx": "cliquez sur ce texte pour รฉviter la perte de<br />performance lors du passage ร  d'autres fenรชtres/onglets", + "u_fixed": "OK!  Rรฉsolu ๐Ÿ‘", + "u_cuerr": "echec du tรฉlรฉversement du morceau {0} de {1};\nprobablement inoffensif, poursuite\n\nfichier : {2}", + "u_cuerr2": "le serveur a rejetรฉ le tรฉlรฉversement (morceau {0} de {1});\nrรฉessaiera plus tard\n\nfichier : {2}\n\nerreur ", + "u_ehstmp": "rรฉessaiera ; voir en bas ร  droite", + "u_ehsfin": "le serveur a rejetรฉ la demande de finalisation du tรฉlรฉversement ; nouvelle tentativeโ€ฆ", + "u_ehssrch": "le serveur a rejetรฉ la demande d'effectuer une recherche ; nouvelle tentativeโ€ฆ", + "u_ehsinit": "le serveur a rejetรฉ la demande d'initier le tรฉlรฉversement ; nouvelle tentativeโ€ฆ", + "u_eneths": "erreur rรฉseau lors de l'exรฉcution de l'initialisation du tรฉlรฉversement ; nouvelle tentativeโ€ฆ", + "u_enethd": "erreur rรฉseau lors du test de l'existence de la cible ; nouvelle tentativeโ€ฆ", + "u_cbusy": "attente que le serveur nous fasse ร  nouveau confiance aprรจs un problรจme rรฉseauโ€ฆ", + "u_ehsdf": "le serveur est ร  court d'espace disque !\n\nil va continuer de rรฉessayer, au cas oรน quelqu'un\nlibรฉrerait suffisamment d'espace pour continuer", + "u_emtleak1": "il semble que votre navigateur web ait une fuite de mรฉmoire ;\nveuillez", + "u_emtleak2": ' <a href="{0}">passer ร  https (recommandรฉ)</a> ou ', + "u_emtleak3": ' ', + "u_emtleakc": 'essayez la solution suivante:\n<ul><li>appuyez sur <code>F5</code> pour rafraรฎchir la page</li><li>ensuite dรฉsactivez le bouton  <code>mt</code>  dans les  <code>โš™๏ธ paramรจtres</code></li><li>et rรฉessayez ce tรฉlรฉversement</li></ul>Les tรฉlรฉversements seront un peu plus lents, mais tant pis.\nDรฉsolรฉ pour le dรฉrangement !\n\nPS : chrome v107 <a href="https://bugs.chromium.org/p/chromium/issues/detail?id=1354816" target="_blank">a un correctif</a> pour cela', + "u_emtleakf": 'essayez la solution suivante:\n<ul><li>appuyez sur <code>F5</code> pour rafraรฎchir la page</li><li>ensuite activez <code>๐Ÿฅ”</code> (pomme de terre) dans l\'interface de tรฉlรฉversement</li><li>et rรฉessayez ce tรฉlรฉversement</li></ul>\nPS : firefox <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=1790500" target="_blank">aura probablement un correctif</a> ร  un moment donnรฉ', + "u_s404": "pas trouvรฉ sur le serveur", + "u_expl": "expliquer", + "u_maxconn": "la plupart des navigateur limite ceci ร  6, mais firefox vous permet de l'augmenter avec <code>connections-per-server</code> dans <code>about:config</code>", + "u_tu": '<p class="warn">WARNING: turbo enclenchรฉ, <span> le client peut ne pas dรฉtecter et reprendre les tรฉlรฉversements incomplets ; voir l\'info-bulle du bouton turbo</span></p>', + "u_ts": '<p class="warn">WARNING: turbo enclenchรฉ, <span> les rรฉsultats de recherche peuvent รชtre incorrects ; voir l\'info-bulle du bouton turbo</span></p>', + "u_turbo_c": "turbo est dรฉsactivรฉ dans la configuration du serveur", + "u_turbo_g": "dรฉsactivation de turbo car vous n'avez pas de\nprivilรจges de listing de rรฉpertoires dans ce volume", + "u_life_cfg": 'suppression automatique aprรจs <input id="lifem" p="60" /> min (ou <input id="lifeh" p="3600" /> heures)', + "u_life_est": 'le tรฉlรฉversement sera supprimรฉ <span id="lifew" tt="local time">---</span>', + "u_life_max": 'ce dossier impose une\ndurรฉe de vie maximale de {0}', + "u_unp_ok": 'unpost est autorisรฉ pour {0}', + "u_unp_ng": 'unpost ne sera PAS autorisรฉ', + "ue_ro": 'votre accรจs ร  ce dossier est en lecture seule\n\n', + "ue_nl": 'vous n\'รชtes actuellement pas connectรฉ', + "ue_la": 'vous รชtes actuellement connectรฉ en tant que "{0}"', + "ue_sr": 'vous รชtes actuellement en mode recherche de fichiers\n\nchangez en mode tรฉlรฉversement en cliquant sur la loupe ๐Ÿ”Ž (ร  cรดtรฉ du grand bouton RECHERCHER), et essayez de tรฉlรฉverser ร  nouveau\n\ndรฉsolรฉ', + "ue_ta": 'essayez de tรฉlรฉverser ร  nouveau, cela devrait fonctionner maintenant', + "ue_ab": "ce fichier a dรฉjร  รฉtรฉ tรฉlรฉversรฉ dans un autre dossier, et ce tรฉlรฉversement doit รชtre terminรฉ avant que le fichier puisse รชtre tรฉlรฉversรฉ ailleurs.\n\nVous pouvez annuler et oublier le tรฉlรฉversement initial en utilisant le bouton ๐Ÿงฏ en haut ร  gauche.", + "ur_1uo": "OK: Fichier tรฉlรฉversรฉ avec succรจs", + "ur_auo": "OK: Tous les {0} fichiers tรฉlรฉversรฉs avec succรจs", + "ur_1so": "OK: Fichier trouvรฉ sur le serveur", + "ur_aso": "OK: Tous les {0} fichiers trouvรฉs sur le serveur", + "ur_1un": "ร‰chec du tรฉlรฉversement, dรฉsolรฉ", + "ur_aun": "Tous les {0} tรฉlรฉversements ont รฉchouรฉ, dรฉsolรฉ", + "ur_1sn": "Fichier NON trouvรฉ sur le serveur", + "ur_asn": "Les {0} fichiers n'ont PAS ร‰Tร‰ trouvรฉs sur le serveur", + "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", + + "lang_set": "rafraรฎchir pour que les changements prennent effet ?", + }, "grc": { "tt": "ฮ•ฮปฮปฮทฮฝฮนฮบฮฌ", @@ -3893,6 +4536,7 @@ var Ls = { "gou": 'ฮณฮฟฮฝฮนฮบฯŒฯ‚ ฯ†ฮฌฮบฮตฮปฮฟฯ‚">ฯ€ฮฌฮฝฯ‰', "gon": 'ฮตฯ€ฯŒฮผฮตฮฝฮฟฯ‚ ฯ†ฮฌฮบฮตฮปฮฟฯ‚">ฮตฯ€ฯŒฮผฮตฮฝฮฟ', "logout": "ฮ‘ฯ€ฮฟฯƒฯฮฝฮดฮตฯƒฮท ", + "login": "ฮฃฯฮฝฮดฮตฯƒฮท", //m "access": " ฯ€ฯฯŒฯƒฮฒฮฑฯƒฮท", "ot_close": "ฮบฮปฮตฮฏฯƒฮนฮผฮฟ ฯ…ฯ€ฮฟฮผฮตฮฝฮฟฯ", "ot_search": "ฮฑฮฝฮฑฮถฮฎฯ„ฮทฯƒฮท ฮฑฯฯ‡ฮตฮฏฯ‰ฮฝ ฮผฮต ฮฒฮฌฯƒฮท ฯ‡ฮฑฯฮฑฮบฯ„ฮทฯฮนฯƒฯ„ฮนฮบฮฌ, ฮดฮนฮฑฮดฯฮฟฮผฮฎ / ฯŒฮฝฮฟฮผฮฑ, ฮผฮฟฯ…ฯƒฮนฮบฮฌ tags ฮฎ ฮฟฯ€ฮฟฮนฮฟฮฝฮดฮฎฯ€ฮฟฯ„ฮต ฯƒฯ…ฮฝฮดฯ…ฮฑฯƒฮผฯŒ$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>", @@ -4079,9 +4723,9 @@ var Ls = { "mt_mloop": "ฯ„ฯ…ฯ‡ฮฑฮฏฮฑ ฮฑฮฝฮฑฯ€ฮฑฯฮฑฮณฯ‰ฮณฮฎ ฯƒฯ„ฮฟฮฝ ฮฑฮฝฮฟฮนฮบฯ„ฯŒ ฯ†ฮฌฮบฮตฮปฮฟ\">๐Ÿ” ฯ„ฯ…ฯ‡ฮฑฮฏฮฑ ฮฑฮฝฮฑฯ€ฮฑฯฮฑฮณฯ‰ฮณฮฎ", "mt_mnext": "ฯ†ฯŒฯฯ„ฯ‰ฯƒฮท ฮตฯ€ฯŒฮผฮตฮฝฮฟฯ… ฯ†ฮฑฮบฮญฮปฮฟฯ… ฮบฮฑฮน ฯƒฯ…ฮฝฮญฯ‡ฮนฯƒฮท\">๐Ÿ“‚ ฮตฯ€ฯŒฮผฮตฮฝฮฟ", "mt_mstop": "ฯƒฯ„ฮฑฮผฮฌฯ„ฮทฯƒฮต ฯ„ฮทฮฝ ฮฑฮฝฮฑฯ€ฮฑฯฮฑฮณฯ‰ฮณฮฎ\">โธ ฯƒฯ„ฮฑฮผฮฌฯ„ฮทฮผฮฑ", - "mt_cflac": "ฮผฮตฯ„ฮฑฯ„ฯฮฟฯ€ฮฎ flac / wav ฯƒฮต opus\">flac", - "mt_caac": "ฮผฮตฯ„ฮฑฯ„ฯฮฟฯ€ฮฎ aac / m4a ฯƒฮต opus\">aac", - "mt_coth": "ฮผฮตฯ„ฮฑฯ„ฯฮฟฯ€ฮฎ ฯŒฮปฯ‰ฮฝ ฯ„ฯ‰ฮฝ ฮฌฮปฮปฯ‰ฮฝ (ฮตฮบฯ„ฯŒฯ‚ ฯ„ฯ‰ฮฝ mp3) ฯƒฮต opus\">ฮฌฮปฮปฮฟ", + "mt_cflac": "ฮผฮตฯ„ฮฑฯ„ฯฮฟฯ€ฮฎ flac / wav ฯƒฮต {0}\">flac", + "mt_caac": "ฮผฮตฯ„ฮฑฯ„ฯฮฟฯ€ฮฎ aac / m4a ฯƒฮต {0}\">aac", + "mt_coth": "ฮผฮตฯ„ฮฑฯ„ฯฮฟฯ€ฮฎ ฯŒฮปฯ‰ฮฝ ฯ„ฯ‰ฮฝ ฮฌฮปฮปฯ‰ฮฝ (ฮตฮบฯ„ฯŒฯ‚ ฯ„ฯ‰ฮฝ mp3) ฯƒฮต {0}\">ฮฌฮปฮปฮฟ", "mt_c2opus": "ฮบฮฑฮปฯฯ„ฮตฯฮท ฮตฯ€ฮนฮปฮฟฮณฮฎ ฮณฮนฮฑ desktop, laptop, android\">opus", "mt_c2owa": "opus-weba, ฮณฮนฮฑ iOS 17.5 ฮบฮฑฮน ฮฝฮตฯŒฯ„ฮตฯฮฑ\">owa", "mt_c2caf": "opus-caf, ฮณฮนฮฑ iOS 11 ฮญฯ‰ฯ‚ 17\">caf", @@ -4098,7 +4742,7 @@ var Ls = { "mb_play": "ฯ€ฮฑฮฏฮพฮต", "mm_hashplay": "ฮฝฮฑ ฯ€ฮฑฮฏฮพฯ‰ ฮฑฯ…ฯ„ฯŒ ฯ„ฮฟ ฮฑฯฯ‡ฮตฮฏฮฟ ฮฎฯ‡ฮฟฯ…;", - "mm_m3u": "ฯ€ฮฌฯ„ฮฑ <code>Enter/OK</code> ฮณฮนฮฑ ฮ‘ฮฝฮฑฯ€ฮฑฯฮฑฮณฯ‰ฮณฮฎ\nฯ€ฮฌฯ„ฮฑ <code>ESC/ฮ†ฮบฯ…ฯฮฟ</code> ฮณฮนฮฑ ฮ•ฯ€ฮตฮพฮตฯฮณฮฑฯƒฮฏฮฑ", + "mm_m3u": "ฯ€ฮฌฯ„ฮฑ <code>Enter/ฮ•ฮฝฯ„ฮฌฮพฮตฮน</code> ฮณฮนฮฑ ฮ‘ฮฝฮฑฯ€ฮฑฯฮฑฮณฯ‰ฮณฮฎ\nฯ€ฮฌฯ„ฮฑ <code>ESC/ฮ†ฮบฯ…ฯฮฟ</code> ฮณฮนฮฑ ฮ•ฯ€ฮตฮพฮตฯฮณฮฑฯƒฮฏฮฑ", "mp_breq": "ฯ‡ฯฮตฮนฮฌฮถฮตฯ„ฮฑฮน firefox 82+ ฮฎ chrome 73+ ฮฎ iOS 15+", "mm_bload": "ฯ†ฮฟฯฯ„ฯŽฮฝฮตฮน...", "mm_bconv": "ฮผฮตฯ„ฮฑฯ„ฯฮฟฯ€ฮฎ ฯƒฮต {0}, ฯ€ฮตฯฮฏฮผฮตฮฝฮต...", @@ -4131,7 +4775,7 @@ var Ls = { "f_dls": 'ฮฟฮน ฯƒฯฮฝฮดฮตฯƒฮผฮฟฮน ฮฑฯฯ‡ฮตฮฏฯ‰ฮฝ ฯƒฯ„ฮฟฮฝ ฯ„ฯฮญฯ‡ฮฟฮฝฯ„ฮฑ ฯ†ฮฌฮบฮตฮปฮฟ ฮญฯ‡ฮฟฯ…ฮฝ\nฮผฮตฯ„ฮฑฯ„ฯฮฑฯ€ฮตฮฏ ฯƒฮต ฯƒฯ…ฮฝฮดฮญฯƒฮผฮฟฯ…ฯ‚ ฮปฮฎฯˆฮทฯ‚', - "f_partial": "ฮ“ฮนฮฑ ฮฝฮฑ ฮบฮฑฯ„ฮตฮฒฮฌฯƒฮตฮนฯ‚ ฮผฮต ฮฑฯƒฯ†ฮฌฮปฮตฮนฮฑ ฮญฮฝฮฑ ฮฑฯฯ‡ฮตฮฏฮฟ ฯ€ฮฟฯ… ฮฑฮฝฮตฮฒฮฑฮฏฮฝฮตฮน, ฮบฮปฮฏฮบฮฑฯฮต ฯ„ฮฟ ฮฑฯฯ‡ฮตฮฏฮฟ ฮผฮต ฯ„ฮฟ ฮฏฮดฮนฮฟ ฯŒฮฝฮฟฮผฮฑ, ฮฑฮปฮปฮฌ ฯ‡ฯ‰ฯฮฏฯ‚ ฯ„ฮทฮฝ ฮบฮฑฯ„ฮฌฮปฮทฮพฮท <code>.PARTIAL</code>. ฮ ฮฌฯ„ฮฑ ฮ†ฮบฯ…ฯฮฟ ฮฎ Escape ฮณฮนฮฑ ฮฝฮฑ ฯƒฯ„ฮฑฮผฮฑฯ„ฮฎฯƒฮตฮนฯ‚.\n\nฮ ฮฌฯ„ฮฑ OK / Enter ฮฑฮฝ ฮฑฮณฮฝฮฟฮตฮฏฯ‚ ฯ„ฮทฮฝ ฯ€ฯฮฟฮตฮนฮดฮฟฯ€ฮฟฮฏฮทฯƒฮท ฮบฮฑฮน ฮบฮฑฯ„ฮญฮฒฮฑฯƒฮต ฯ„ฮฟ <code>.PARTIAL</code> ฮฑฯฯ‡ฮตฮฏฮฟ, ฯ€ฮฟฯ… ฯƒฯ‡ฮตฮดฯŒฮฝ ฯƒฮฏฮณฮฟฯ…ฯฮฑ ฮธฮฑ ฮตฮฏฮฝฮฑฮน ฮบฮฑฯ„ฮตฯƒฯ„ฯฮฑฮผฮผฮญฮฝฮฟ.", + "f_partial": "ฮ“ฮนฮฑ ฮฝฮฑ ฮบฮฑฯ„ฮตฮฒฮฌฯƒฮตฮนฯ‚ ฮผฮต ฮฑฯƒฯ†ฮฌฮปฮตฮนฮฑ ฮญฮฝฮฑ ฮฑฯฯ‡ฮตฮฏฮฟ ฯ€ฮฟฯ… ฮฑฮฝฮตฮฒฮฑฮฏฮฝฮตฮน, ฮบฮปฮฏฮบฮฑฯฮต ฯ„ฮฟ ฮฑฯฯ‡ฮตฮฏฮฟ ฮผฮต ฯ„ฮฟ ฮฏฮดฮนฮฟ ฯŒฮฝฮฟฮผฮฑ, ฮฑฮปฮปฮฌ ฯ‡ฯ‰ฯฮฏฯ‚ ฯ„ฮทฮฝ ฮบฮฑฯ„ฮฌฮปฮทฮพฮท <code>.PARTIAL</code>. ฮ ฮฌฯ„ฮฑ ฮ†ฮบฯ…ฯฮฟ ฮฎ Escape ฮณฮนฮฑ ฮฝฮฑ ฯƒฯ„ฮฑฮผฮฑฯ„ฮฎฯƒฮตฮนฯ‚.\n\nฮ ฮฌฯ„ฮฑ ฮ•ฮฝฯ„ฮฌฮพฮตฮน / Enter ฮฑฮฝ ฮฑฮณฮฝฮฟฮตฮฏฯ‚ ฯ„ฮทฮฝ ฯ€ฯฮฟฮตฮนฮดฮฟฯ€ฮฟฮฏฮทฯƒฮท ฮบฮฑฮน ฮบฮฑฯ„ฮญฮฒฮฑฯƒฮต ฯ„ฮฟ <code>.PARTIAL</code> ฮฑฯฯ‡ฮตฮฏฮฟ, ฯ€ฮฟฯ… ฯƒฯ‡ฮตฮดฯŒฮฝ ฯƒฮฏฮณฮฟฯ…ฯฮฑ ฮธฮฑ ฮตฮฏฮฝฮฑฮน ฮบฮฑฯ„ฮตฯƒฯ„ฯฮฑฮผฮผฮญฮฝฮฟ.", "ft_paste": "ฮตฯ€ฮนฮบฯŒฮปฮปฮทฯƒฮต {0} ฮฑฮฝฯ„ฮนฮบฮตฮฏฮผฮตฮฝฮฑ$Nฮ ฮปฮฎฮบฯ„ฯฮฟ ฯƒฯ…ฮฝฯ„ฯŒฮผฮตฯ…ฯƒฮทฯ‚: ctrl-V", "fr_eperm": 'ฮดฮตฮฝ ฮผฯ€ฮฟฯฮตฮฏ ฮฝฮฑ ฮผฮตฯ„ฮฟฮฝฮฟฮผฮฑฯƒฯ„ฮตฮฏ:\nฮดฮตฮฝ ฮญฯ‡ฮตฮนฯ‚ ฮดฮนฮบฮฑฮฏฯ‰ฮผฮฑ โ€œฮผฮตฯ„ฮฑฮบฮฏฮฝฮทฯƒฮทฯ‚โ€ ฯƒฮต ฮฑฯ…ฯ„ฯŒฮฝ ฯ„ฮฟ ฯ†ฮฌฮบฮตฮปฮฟ', @@ -4161,7 +4805,7 @@ var Ls = { "fs_tsrc": "ฯ„ฮฟ ฮฑฯฯ‡ฮตฮฏฮฟ ฮฎ ฮฟ ฯ†ฮฌฮบฮตฮปฮฟฯ‚ ฯ€ฯฮฟฯ‚ ฮบฮฟฮนฮฝฮฎ ฯ‡ฯฮฎฯƒฮท", "fs_ppwd": "ฯ€ฯฮฟฮฑฮนฯฮตฯ„ฮนฮบฯŒฯ‚ ฮบฯ‰ฮดฮนฮบฯŒฯ‚", "fs_w8": "ฮดฮทฮผฮนฮฟฯ…ฯฮณฮฏฮฑ ฮบฮฟฮนฮฝฮฎฯ‚ ฯ‡ฯฮฎฯƒฮทฯ‚...", - "fs_ok": "ฯ€ฮฌฯ„ฮฑ <code>Enter/OK</code> ฮณฮนฮฑ ฮ ฯฯŒฯ‡ฮตฮนฯฮฟ\nฯ€ฮฌฯ„ฮฑ <code>ESC/ฮ†ฮบฯ…ฯฮฟ</code> ฮณฮนฮฑ ฮšฮปฮตฮฏฯƒฮนฮผฮฟ", + "fs_ok": "ฯ€ฮฌฯ„ฮฑ <code>Enter/ฮ•ฮฝฯ„ฮฌฮพฮตฮน</code> ฮณฮนฮฑ ฮ ฯฯŒฯ‡ฮตฮนฯฮฟ\nฯ€ฮฌฯ„ฮฑ <code>ESC/ฮ†ฮบฯ…ฯฮฟ</code> ฮณฮนฮฑ ฮšฮปฮตฮฏฯƒฮนฮผฮฟ", "frt_dec": "ฮผฯ€ฮฟฯฮตฮฏ ฮฝฮฑ ฮดฮนฮฟฯฮธฯŽฯƒฮตฮน ฮผฮตฯฮนฮบฮญฯ‚ ฯ€ฮตฯฮนฯ€ฯ„ฯŽฯƒฮตฮนฯ‚ ฮบฮฑฯ„ฮตฯƒฯ„ฯฮฑฮผฮผฮญฮฝฯ‰ฮฝ ฮฟฮฝฮฟฮผฮฌฯ„ฯ‰ฮฝ ฮฑฯฯ‡ฮตฮฏฯ‰ฮฝ\">ฮฑฯ€ฮฟฮบฯ‰ฮดฮนฮบฮฟฯ€ฮฟฮฏฮทฯƒฮท url", "frt_rst": "ฮตฯ€ฮฑฮฝฮญฯ†ฮตฯฮต ฯ„ฮฑ ฮฟฮฝฯŒฮผฮฑฯ„ฮฑ ฮฑฯฯ‡ฮตฮฏฯ‰ฮฝ ฯƒฯ„ฮฑ ฮฑฯฯ‡ฮนฮบฮฌ ฯ„ฮฟฯ…ฯ‚\">โ†บ ฮตฯ€ฮฑฮฝฮฑฯ†ฮฟฯฮฌ", @@ -4206,6 +4850,7 @@ var Ls = { "fcp_ok": "ฮฑฮฝฯ„ฮนฮณฯฮฑฯ†ฮฎ OK", "fp_busy": "ฮผฮตฯ„ฮฑฮบฮฏฮฝฮทฯƒฮท {0} ฮฑฮฝฯ„ฮนฮบฮตฮนฮผฮญฮฝฯ‰ฮฝ...\n\n{1}", "fcp_busy": "ฮฑฮฝฯ„ฮนฮณฯฮฑฯ†ฮฎ {0} ฮฑฮฝฯ„ฮนฮบฮตฮนฮผฮญฮฝฯ‰ฮฝ...\n\n{1}", + "fp_abrt": "ฮณฮฏฮฝฮตฯ„ฮฑฮน ฮฑฮบฯฯฯ‰ฯƒฮท...", //m "fp_err": "ฮฑฯ€ฮฟฯ„ฯ…ฯ‡ฮฏฮฑ ฮผฮตฯ„ฮฑฮบฮฏฮฝฮทฯƒฮทฯ‚:\n", "fcp_err": "ฮฑฯ€ฮฟฯ„ฯ…ฯ‡ฮฏฮฑ ฮฑฮฝฯ„ฮนฮณฯฮฑฯ†ฮฎฯ‚:\n", "fp_confirm": "ฮฝฮฑ ฮผฮตฯ„ฮฑฮบฮนฮฝฮทฮธฮฟฯฮฝ ฮฑฯ…ฯ„ฮฌ ฯ„ฮฑ {0} ฮฑฮฝฯ„ฮนฮบฮตฮฏฮผฮตฮฝฮฑ ฮตฮดฯŽ;", @@ -4350,7 +4995,7 @@ var Ls = { "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>Cancel/ESC</code> ฮณฮนฮฑ ฮฮ‘ ฮœฮ—ฮ ฮ‘ฮ ฮŸฮšฮ›ฮ•ฮ™ฮฃฮคฮŸฮฅฮ ฮบฮฑฮน ฮฝฮฑ ฮœฮ•ฮคฮ‘ฮฆฮŸฮกฮคฮฉฮ˜ฮŸฮŽฮ ฮบฮน ฮฑฯ…ฯ„ฮฌ:\n\n", + "u_applef": "ฮ‘ฯ…ฯ„ฮฌ ฯ„ฮฑ {0} ฮฑฯฯ‡ฮตฮฏฮฑ (ฮฑฯ€ฯŒ {1} ฯƒฯ…ฮฝฮฟฮปฮนฮบฮฌ) ฯ€ฮนฮธฮฑฮฝฯŽฯ‚ ฮดฮตฮฝ ฮตฮฏฮฝฮฑฮน ฮตฯ€ฮนฮธฯ…ฮผฮทฯ„ฮฌ;\nฮ ฮฌฯ„ฮฑ <code>ฮ•ฮฝฯ„ฮฌฮพฮตฮน/Enter</code> ฮณฮนฮฑ ฮฮ‘ ฮ‘ฮ“ฮฮŸฮ—ฮ˜ฮŸฮฅฮ ฯ„ฮฑ ฯ€ฮฑฯฮฑฮบฮฌฯ„ฯ‰ ฮฑฯฯ‡ฮตฮฏฮฑ,\nฮ ฮฌฯ„ฮฑ <code>ฮ†ฮบฯ…ฯฮฟ/ESC</code> ฮณฮนฮฑ ฮฮ‘ ฮœฮ—ฮ ฮ‘ฮ ฮŸฮšฮ›ฮ•ฮ™ฮฃฮคฮŸฮฅฮ ฮบฮฑฮน ฮฝฮฑ ฮœฮ•ฮคฮ‘ฮฆฮŸฮกฮคฮฉฮ˜ฮŸฮŽฮ ฮบฮน ฮฑฯ…ฯ„ฮฌ:\n\n", "u_just1": "\nฮŠฯƒฯ‰ฯ‚ ฮดฮฟฯ…ฮปฮญฯˆฮตฮน ฮบฮฑฮปฯฯ„ฮตฯฮฑ ฮฑฮฝ ฮตฯ€ฮนฮปฮญฮพฮตฮนฯ‚ ฮผฯŒฮฝฮฟ ฮญฮฝฮฑ ฮฑฯฯ‡ฮตฮฏฮฟ", "u_ff_many": "ฮฑฮฝ ฯ‡ฯฮทฯƒฮนฮผฮฟฯ€ฮฟฮนฮตฮฏฯ‚ <b>Linux / MacOS / Android,</b> ฯ„ฯŒฯ„ฮต ฯ„ฯŒฯƒฮฑ ฮฑฯฯ‡ฮตฮฏฮฑ <a href=\"https://bugzilla.mozilla.org/show_bug.cgi?id=1790500\" target=\"_blank\"><em>ฮผฯ€ฮฟฯฮตฮฏ</em> ฮฝฮฑ ฮบฮฑฯ„ฮฌฯฯฮตฯ…ฯƒฮท ฯ„ฮฟฯ… Firefox!</a>\nฮฑฮฝ ฮณฮฏฮฝฮตฮน ฮฑฯ…ฯ„ฯŒ, ฮดฮฟฮบฮฏฮผฮฑฯƒฮต ฮพฮฑฮฝฮฌ (ฮฎ ฯ‡ฯฮทฯƒฮนฮผฮฟฯ€ฮฟฮฏฮทฯƒฮต ฯ„ฮฟฮฝ Chrome).", "u_up_life": "ฮ‘ฯ…ฯ„ฮฎ ฮท ฮผฮตฯ„ฮฑฯ†ฯŒฯฯ„ฯ‰ฯƒฮท ฮธฮฑ ฮดฮนฮฑฮณฯฮฑฯ†ฮตฮฏ ฮฑฯ€ฯŒ ฯ„ฮฟ ฮดฮนฮฑฮบฮฟฮผฮนฯƒฯ„ฮฎ\n{0} ฮผฮตฯ„ฮฌ ฯ„ฮทฮฝ ฮฟฮปฮฟฮบฮปฮฎฯฯ‰ฯƒฮฎ ฯ„ฮทฯ‚", @@ -4522,6 +5167,7 @@ var Ls = { "gou": 'cartella genitore">su', "gon": 'prossima cartella">succ', "logout": "Logout ", + "login": "Accedi", //m "access": " accesso", "ot_close": "chiudi sottomenu", "ot_search": "cerca file per attributi, percorso / nome, tag musicali, o qualsiasi combinazione di questi$N$N<code>foo bar</code> = deve contenere sia ยซfooยป che ยซbarยป,$N<code>foo -bar</code> = deve contenere ยซfooยป ma non ยซbarยป,$N<code>^yana .opus$</code> = inizia con ยซyanaยป ed รจ un file ยซopusยป$N<code>"try unite"</code> = contiene esattamente ยซtry uniteยป$N$Nil formato data รจ iso-8601, come$N<code>2009-12-31</code> o <code>2020-09-12 23:30:00</code>", @@ -4708,9 +5354,9 @@ var Ls = { "mt_mloop": "loop della cartella aperta\">๐Ÿ” loop", "mt_mnext": "carica la prossima cartella e continua\">๐Ÿ“‚ succ", "mt_mstop": "ferma riproduzione\">โธ stop", - "mt_cflac": "converti flac / wav in opus\">flac", - "mt_caac": "converti aac / m4a in opus\">aac", - "mt_coth": "converti tutti gli altri (non mp3) in opus\">oth", + "mt_cflac": "converti flac / wav in {0}\">flac", + "mt_caac": "converti aac / m4a in {0}\">aac", + "mt_coth": "converti tutti gli altri (non mp3) in {0}\">oth", "mt_c2opus": "scelta migliore per desktop, laptop, android\">opus", "mt_c2owa": "opus-weba, per iOS 17.5 e piรน recenti\">owa", "mt_c2caf": "opus-caf, per iOS 11 fino a 17\">caf", @@ -4835,6 +5481,7 @@ var Ls = { "fcp_ok": "copia OK", "fp_busy": "spostando {0} elementi...\n\n{1}", "fcp_busy": "copiando {0} elementi...\n\n{1}", + "fp_abrt": "annullamento in corso...", //m "fp_err": "spostamento fallito:\n", "fcp_err": "copia fallita:\n", "fp_confirm": "spostare questi {0} elementi qui?", @@ -5043,6 +5690,637 @@ var Ls = { "lang_set": "aggiornare per rendere effettivo il cambiamento?", }, + "kor": { + "tt": "ํ•œ๊ตญ์–ด", + + "cols": { + "c": "์ž‘์—… ๋ฒ„ํŠผ", + "dur": "๊ธธ์ด", + "q": "ํ’ˆ์งˆ/๋น„ํŠธ๋ ˆ์ดํŠธ", + "Ac": "์˜ค๋””์˜ค ์ฝ”๋ฑ", + "Vc": "๋น„๋””์˜ค ์ฝ”๋ฑ", + "Fmt": "ํ˜•์‹/์ปจํ…Œ์ด๋„ˆ", + "Ahash": "์˜ค๋””์˜ค ์ฒดํฌ์„ฌ", + "Vhash": "๋น„๋””์˜ค ์ฒดํฌ์„ฌ", + "Res": "ํ•ด์ƒ๋„", + "T": "ํŒŒ์ผ ์œ ํ˜•", + "aq": "์˜ค๋””์˜ค ํ’ˆ์งˆ/๋น„ํŠธ๋ ˆ์ดํŠธ", + "vq": "๋น„๋””์˜ค ํ’ˆ์งˆ/๋น„ํŠธ๋ ˆ์ดํŠธ", + "pixfmt": "์„œ๋ธŒ์ƒ˜ํ”Œ๋ง/ํ”ฝ์…€ ๊ตฌ์กฐ", + "resw": "๊ฐ€๋กœ ํ•ด์ƒ๋„", + "resh": "์„ธ๋กœ ํ•ด์ƒ๋„", + "chs": "์˜ค๋””์˜ค ์ฑ„๋„", + "hz": "์ƒ˜ํ”Œ๋ ˆ์ดํŠธ" + }, + + "hks": [ + [ + "๊ธฐํƒ€", + ["ESC", "๋‹ค์–‘ํ•œ ์ฐฝ ๋‹ซ๊ธฐ"], + + "ํŒŒ์ผ ๊ด€๋ฆฌ์ž", + ["G", "๋ชฉ๋ก/๊ทธ๋ฆฌ๋“œ ๋ณด๊ธฐ ์ „ํ™˜"], + ["T", "์ธ๋„ค์ผ/์•„์ด์ฝ˜ ์ „ํ™˜"], + ["โ‡ง A/D", "์ธ๋„ค์ผ ์ด๋ฏธ์ง€ ํฌ๊ธฐ"], + ["ctrl-K", "์„ ํƒ ํ•ญ๋ชฉ ์‚ญ์ œ"], + ["ctrl-X", "์„ ํƒ ํ•ญ๋ชฉ ์ž˜๋ผ๋‚ด๊ธฐ"], + ["ctrl-C", "์„ ํƒ ํ•ญ๋ชฉ ๋ณต์‚ฌ"], + ["ctrl-V", "์—ฌ๊ธฐ์— ๋ถ™์—ฌ๋„ฃ๊ธฐ (์ด๋™/๋ณต์‚ฌ)"], + ["Y", "์„ ํƒ ํ•ญ๋ชฉ ๋‹ค์šด๋กœ๋“œ"], + ["F2", "์„ ํƒ ํ•ญ๋ชฉ ์ด๋ฆ„ ๋ฐ”๊พธ๊ธฐ"], + + "ํŒŒ์ผ ๋ชฉ๋ก ์„ ํƒ", + ["space", "ํŒŒ์ผ ์„ ํƒ/ํ•ด์ œ"], + ["โ†‘/โ†“", "์„ ํƒ ์ปค์„œ ์ด๋™"], + ["ctrl โ†‘/โ†“", "์ปค์„œ์™€ ๋ทฐํฌํŠธ ๋™์‹œ ์ด๋™"], + ["โ‡ง โ†‘/โ†“", "์ด์ „/๋‹ค์Œ ํŒŒ์ผ ์„ ํƒ"], + ["ctrl-A", "๋ชจ๋“  ํŒŒ์ผ/ํด๋” ์„ ํƒ"], + ], [ + "ํƒ์ƒ‰", + ["B", "๋ธŒ๋ ˆ๋“œํฌ๋Ÿผ/ํƒ์ƒ‰์ฐฝ ์ „ํ™˜"], + ["I/K", "์ด์ „/๋‹ค์Œ ํด๋”"], + ["M", "์ƒ์œ„ ํด๋” (๋˜๋Š” ํ˜„์žฌ ํ•ญ๋ชฉ ๋‹ซ๊ธฐ)"], + ["V", "ํƒ์ƒ‰์ฐฝ์— ํด๋”/ํ…์ŠคํŠธ ํŒŒ์ผ ํ‘œ์‹œ ์ „ํ™˜"], + ["A/D", "ํƒ์ƒ‰์ฐฝ ํฌ๊ธฐ"], + ], [ + "์˜ค๋””์˜ค ํ”Œ๋ ˆ์ด์–ด", + ["J/L", "์ด์ „/๋‹ค์Œ ๊ณก"], + ["U/O", "10์ดˆ ๋’ค๋กœ/์•ž์œผ๋กœ ๊ฑด๋„ˆ๋›ฐ๊ธฐ"], + ["0..9", "0%..90% ์ง€์ ์œผ๋กœ ์ด๋™"], + ["P", "์žฌ์ƒ/์ผ์‹œ์ •์ง€ (์‹œ์ž‘ ํฌํ•จ)"], + ["S", "์žฌ์ƒ ์ค‘์ธ ๊ณก ์„ ํƒ"], + ["Y", "๊ณก ๋‹ค์šด๋กœ๋“œ"], + ], [ + "์ด๋ฏธ์ง€ ๋ทฐ์–ด", + ["J/L, โ†/โ†’", "์ด์ „/๋‹ค์Œ ์ด๋ฏธ์ง€"], + ["Home/End", "์ฒซ/๋งˆ์ง€๋ง‰ ์ด๋ฏธ์ง€"], + ["F", "์ „์ฒด ํ™”๋ฉด"], + ["R", "์‹œ๊ณ„ ๋ฐฉํ–ฅ์œผ๋กœ ํšŒ์ „"], + ["โ‡ง R", "๋ฐ˜์‹œ๊ณ„ ๋ฐฉํ–ฅ์œผ๋กœ ํšŒ์ „"], + ["S", "์ด๋ฏธ์ง€ ์„ ํƒ"], + ["Y", "์ด๋ฏธ์ง€ ๋‹ค์šด๋กœ๋“œ"], + ], [ + "๋น„๋””์˜ค ํ”Œ๋ ˆ์ด์–ด", + ["U/O", "10์ดˆ ๋’ค๋กœ/์•ž์œผ๋กœ ๊ฑด๋„ˆ๋›ฐ๊ธฐ"], + ["P/K/Space", "์žฌ์ƒ/์ผ์‹œ์ •์ง€"], + ["C", "๋‹ค์Œ ํŒŒ์ผ ๊ณ„์† ์žฌ์ƒ"], + ["V", "๋ฐ˜๋ณต"], + ["M", "์Œ์†Œ๊ฑฐ"], + ["[ ์™€ ]", "๋ฐ˜๋ณต ๊ตฌ๊ฐ„ ์„ค์ •"], + ], [ + "ํ…์ŠคํŠธ ํŒŒ์ผ ๋ทฐ์–ด", + ["I/K", "์ด์ „/๋‹ค์Œ ํŒŒ์ผ"], + ["M", "ํ…์ŠคํŠธ ํŒŒ์ผ ๋‹ซ๊ธฐ"], + ["E", "ํ…์ŠคํŠธ ํŒŒ์ผ ํŽธ์ง‘"], + ["S", "ํŒŒ์ผ ์„ ํƒ (์ž˜๋ผ๋‚ด๊ธฐ/๋ณต์‚ฌ/์ด๋ฆ„ ๋ฐ”๊พธ๊ธฐ์šฉ)"], + ] + ], + + "m_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": '์ด์ „ ํ˜•์ œ ํด๋”">์ด์ „', + "gou": '์ƒ์œ„ ํด๋”">์œ„๋กœ', + "gon": '๋‹ค์Œ ํด๋”">๋‹ค์Œ', + "logout": "๋กœ๊ทธ์•„์›ƒ ", + "login": "๋กœ๊ทธ์ธ", //m + "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": "์ฃผ์›Œ๋‹ด๊ธฐ: ์ตœ๊ทผ ์—…๋กœ๋“œํ•œ ํ•ญ๋ชฉ์„ ์‚ญ์ œํ•˜๊ฑฐ๋‚˜ ๋ฏธ์™„๋ฃŒ๋œ ์—…๋กœ๋“œ๋ฅผ ์ค‘๋‹จํ•ฉ๋‹ˆ๋‹ค", + "ot_bup": "bup: ๊ธฐ๋ณธ ์—…๋กœ๋”. ๋„ท์Šค์ผ€์ดํ”„ 4.0๋„ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค", + "ot_mkdir": "mkdir: ์ƒˆ ๋””๋ ‰ํ„ฐ๋ฆฌ๋ฅผ ๋งŒ๋“ญ๋‹ˆ๋‹ค", + "ot_md": "new-md: ์ƒˆ ๋งˆํฌ๋‹ค์šด ๋ฌธ์„œ๋ฅผ ๋งŒ๋“ญ๋‹ˆ๋‹ค", + "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โ™ป๏ธ: ํŒŒ์ผ์ด ๋‹ค๋ฅด๋ฉด ํ•ญ์ƒ ๋ฎ์–ด์“ฐ๊ธฐ", + "ut_mt": "์—…๋กœ๋“œ ์ค‘ ๋‹ค๋ฅธ ํŒŒ์ผ ํ•ด์‹ฑ ๊ณ„์†ํ•˜๊ธฐ$N$NCPU ๋˜๋Š” HDD๊ฐ€ ๋ณ‘๋ชฉ ํ˜„์ƒ์„ ์ผ์œผํ‚ค๋Š” ๊ฒฝ์šฐ ๋น„ํ™œ์„ฑํ™”ํ•˜์„ธ์š”", + "ut_ask": '์—…๋กœ๋“œ ์‹œ์ž‘ ์ „ ํ™•์ธ ์š”์ฒญ">๐Ÿ’ญ', + "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": "๋ฌธ์ œ ๋ฐœ์ƒ: ์‹คํŒจ/๊ฑฐ๋ถ€/์ฐพ์„ ์ˆ˜ ์—†์Œ", + "uct_done": "์™„๋ฃŒ๋จ (์„ฑ๊ณต ๋ฐ ๋ฌธ์ œ ๋ฐœ์ƒ ํฌํ•จ)", + "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> = ํŒŒ์ผ (ํ•˜๋‚˜ ์ด์ƒ)\n<code>ESC</code> = ํด๋” ํ•˜๋‚˜ (ํ•˜์œ„ ํด๋” ํฌํ•จ)', + "u_nav_b": '<a href="#" id="modal-ok">ํŒŒ์ผ</a><a href="#" id="modal-ng">ํด๋” ํ•˜๋‚˜</a>', + + "cl_opts": "์Šค์œ„์น˜", + "cl_themes": "ํ…Œ๋งˆ", + "cl_langs": "์–ธ์–ด", + "cl_ziptype": "ํด๋” ๋‹ค์šด๋กœ๋“œ", + "cl_uopts": "up2k ์Šค์œ„์น˜", + "cl_favico": "ํŒŒ๋น„์ฝ˜", + "cl_bigdir": "ํฐ ๋””๋ ‰ํ„ฐ๋ฆฌ", + "cl_hsort": "#sort", + "cl_keytype": "์กฐ์„ฑ ํ‘œ๊ธฐ๋ฒ•", + "cl_hiddenc": "์ˆจ๊ฒจ์ง„ ์—ด", + "cl_hidec": "์ˆจ๊ธฐ๊ธฐ", + "cl_reset": "์ดˆ๊ธฐํ™”", + "cl_hpick": "์•„๋ž˜ ํ…Œ์ด๋ธ”์—์„œ ์ˆจ๊ธฐ๊ณ  ์‹ถ์€ ์—ด์˜ ํ—ค๋”๋ฅผ ํƒญํ•˜์„ธ์š”", + "cl_hcancel": "์—ด ์ˆจ๊ธฐ๊ธฐ๊ฐ€ ์ค‘๋‹จ๋˜์—ˆ์Šต๋‹ˆ๋‹ค", + + "ct_grid": "็”ฐ ๊ทธ๋ฆฌ๋“œ", + "ct_ttips": 'โ—” โ—ก โ—”">โ„น๏ธ ๋„์›€๋ง', + "ct_thumb": '๊ทธ๋ฆฌ๋“œ ๋ณด๊ธฐ์—์„œ ์•„์ด์ฝ˜ ๋˜๋Š” ๋ฏธ๋ฆฌ๋ณด๊ธฐ ์ด๋ฏธ์ง€ ์ „ํ™˜$N๋‹จ์ถ•ํ‚ค: T">๐Ÿ–ผ๏ธ ๋ฏธ๋ฆฌ๋ณด๊ธฐ', + "ct_csel": '๊ทธ๋ฆฌ๋“œ ๋ณด๊ธฐ์—์„œ CTRL๊ณผ SHIFT๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ํŒŒ์ผ ์„ ํƒ">์„ ํƒ', + "ct_ihop": '์ด๋ฏธ์ง€ ๋ทฐ์–ด๋ฅผ ๋‹ซ์œผ๋ฉด ๋งˆ์ง€๋ง‰์œผ๋กœ ๋ณธ ํŒŒ์ผ๋กœ ์Šคํฌ๋กค">gโฎฏ', + "ct_dots": '์ˆจ๊น€ ํŒŒ์ผ ํ‘œ์‹œ (์„œ๋ฒ„๊ฐ€ ํ—ˆ์šฉํ•˜๋Š” ๊ฒฝ์šฐ)">์ˆจ๊น€ํŒŒ์ผ', + "ct_qdel": 'ํŒŒ์ผ ์‚ญ์ œ ์‹œ ํ•œ ๋ฒˆ๋งŒ ํ™•์ธ ์š”์ฒญ">๋น ๋ฅธ์‚ญ์ œ', + "ct_dir1st": 'ํด๋”๋ฅผ ํŒŒ์ผ๋ณด๋‹ค ๋จผ์ € ์ •๋ ฌ">๐Ÿ“ ๋จผ์ €', + "ct_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": 'ํ„ฐ๋ณด ๋ฒ„ํŠผ์ด ํ™œ์„ฑํ™”๋˜์–ด ์žˆ์ง€ ์•Š์œผ๋ฉด ํšจ๊ณผ๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค.$N$NYOLO์˜ ์œ„ํ—˜์„ฑ์„ ์•ฝ๊ฐ„ ์ค„์—ฌ์ค๋‹ˆ๋‹ค. ์„œ๋ฒ„์˜ ํŒŒ์ผ ํƒ€์ž„์Šคํƒฌํ”„๊ฐ€ ์‚ฌ์šฉ์ž์˜ ๊ฒƒ๊ณผ ์ผ์น˜ํ•˜๋Š”์ง€ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค.$N$N<em>์ด๋ก ์ ์œผ๋กœ๋Š”</em> ๋Œ€๋ถ€๋ถ„์˜ ๋ฏธ์™„๋ฃŒ/์†์ƒ๋œ ์—…๋กœ๋“œ๋ฅผ ์žก์•„๋‚ด์ง€๋งŒ, ํ„ฐ๋ณด๋ฅผ ๋น„ํ™œ์„ฑํ™”ํ•˜๊ณ  ๊ฒ€์ฆ ๊ณผ์ •์„ ๊ฑฐ์น˜๋Š” ๊ฒƒ์„ ๋Œ€์ฒดํ•  ์ˆ˜๋Š” ์—†์Šต๋‹ˆ๋‹ค.\">๋‚ ์งœํ™•์ธ', + + "cut_u2sz": "๊ฐ ์—…๋กœ๋“œ ์ฒญํฌ์˜ ํฌ๊ธฐ (MiB)์ž…๋‹ˆ๋‹ค. ํฐ ๊ฐ’์€ ํƒœํ‰์–‘์„ ๊ฑด๋„ˆ๋Š” ๋ฐ ๋” ์œ ๋ฆฌํ•ฉ๋‹ˆ๋‹ค. ๋งค์šฐ ๋ถˆ์•ˆ์ •ํ•œ ์—ฐ๊ฒฐ์—์„œ๋Š” ๋‚ฎ์€ ๊ฐ’์„ ์‹œ๋„ํ•ด๋ณด์„ธ์š”.", + + "cut_flag": 'ํ•œ ๋ฒˆ์— ํ•˜๋‚˜์˜ ํƒญ๋งŒ ์—…๋กœ๋“œํ•˜๋„๋ก ๋ณด์žฅํ•ฉ๋‹ˆ๋‹ค.$N-- ๋‹ค๋ฅธ ํƒญ๋„ ์ด ์˜ต์…˜์„ ํ™œ์„ฑํ™”ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.$N-- ๋™์ผํ•œ ๋„๋ฉ”์ธ์˜ ํƒญ์—๋งŒ ์˜ํ–ฅ์„ ๋ฏธ์นฉ๋‹ˆ๋‹ค.', + + "cut_az": '๊ฐ€์žฅ ์ž‘์€ ํŒŒ์ผ ์šฐ์„ ์ด ์•„๋‹Œ ์•ŒํŒŒ๋ฒณ ์ˆœ์„œ๋กœ ํŒŒ์ผ์„ ์—…๋กœ๋“œํ•ฉ๋‹ˆ๋‹ค.$N$N์•ŒํŒŒ๋ฒณ ์ˆœ์„œ๋Š” ์„œ๋ฒ„์—์„œ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ–ˆ๋Š”์ง€ ๋ˆˆ์œผ๋กœ ํ™•์ธํ•˜๊ธฐ ์‰ฝ๊ฒŒ ํ•ด์ฃผ์ง€๋งŒ, ๊ด‘๋žœ/LAN ํ™˜๊ฒฝ์—์„œ๋Š” ์—…๋กœ๋“œ ์†๋„๊ฐ€ ์•ฝ๊ฐ„ ๋А๋ ค์ง‘๋‹ˆ๋‹ค.', + + "cut_nag": '์—…๋กœ๋“œ ์™„๋ฃŒ ์‹œ OS ์•Œ๋ฆผ$N(๋ธŒ๋ผ์šฐ์ €๋‚˜ ํƒญ์ด ํ™œ์„ฑํ™”๋˜์ง€ ์•Š์€ ๊ฒฝ์šฐ์—๋งŒ)', + "cut_sfx": '์—…๋กœ๋“œ ์™„๋ฃŒ ์‹œ ์†Œ๋ฆฌ ์•Œ๋ฆผ$N(๋ธŒ๋ผ์šฐ์ €๋‚˜ ํƒญ์ด ํ™œ์„ฑํ™”๋˜์ง€ ์•Š์€ ๊ฒฝ์šฐ์—๋งŒ)', + + "cut_mt": '๋ฉ€ํ‹ฐ์Šค๋ ˆ๋”ฉ์„ ์‚ฌ์šฉํ•˜์—ฌ ํŒŒ์ผ ํ•ด์‹ฑ ์†๋„๋ฅผ ๋†’์ž…๋‹ˆ๋‹ค.$N$N์ด ๊ธฐ๋Šฅ์€ ์›น ์›Œ์ปค๋ฅผ ์‚ฌ์šฉํ•˜๋ฉฐ$N๋” ๋งŽ์€ RAM์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค (์ถ”๊ฐ€์ ์œผ๋กœ ์ตœ๋Œ€ 512 MiB).$N$Nhttps๋Š” 30% ๋” ๋น ๋ฅด๊ฒŒ, http๋Š” 4.5๋ฐฐ ๋” ๋น ๋ฅด๊ฒŒ ๋งŒ๋“ญ๋‹ˆ๋‹ค.\">mt', + + "cut_wasm": '๋ธŒ๋ผ์šฐ์ € ๋‚ด์žฅ ํ•ด์…” ๋Œ€์‹  wasm์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. ํฌ๋กฌ ๊ธฐ๋ฐ˜ ๋ธŒ๋ผ์šฐ์ €์—์„œ ์†๋„๋ฅผ ํ–ฅ์ƒ์‹œํ‚ค์ง€๋งŒ CPU ๋ถ€ํ•˜๋ฅผ ์ฆ๊ฐ€์‹œํ‚ค๋ฉฐ, ๋งŽ์€ ๊ตฌ๋ฒ„์ „ ํฌ๋กฌ์—๋Š” ์ด ๊ธฐ๋Šฅ์„ ํ™œ์„ฑํ™”ํ•˜๋ฉด ๋ชจ๋“  RAM์„ ์†Œ๋ชจํ•˜๊ณ  ์ถฉ๋Œํ•˜๋Š” ๋ฒ„๊ทธ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.\">wasm', + + "cft_text": "ํŒŒ๋น„์ฝ˜ ํ…์ŠคํŠธ (๋น„์›Œ๋‘๊ณ  ์ƒˆ๋กœ๊ณ ์นจํ•˜๋ฉด ๋น„ํ™œ์„ฑํ™”๋จ)", + "cft_fg": "์ „๊ฒฝ์ƒ‰", + "cft_bg": "๋ฐฐ๊ฒฝ์ƒ‰", + + "cdt_lim": "ํด๋”์— ํ‘œ์‹œํ•  ์ตœ๋Œ€ ํŒŒ์ผ ์ˆ˜", + "cdt_ask": "๋งจ ์•„๋ž˜๋กœ ์Šคํฌ๋กคํ•  ๋•Œ$N๋” ๋งŽ์€ ํŒŒ์ผ์„ ๋ถˆ๋Ÿฌ์˜ค๋Š” ๋Œ€์‹ $N๋ฌด์—‡์„ ํ• ์ง€ ๋ฌป๊ธฐ", + "cdt_hsort": "๋ฏธ๋””์–ด URL์— ํฌํ•จํ•  ์ •๋ ฌ ๊ทœ์น™ (<code>,sorthref</code>)์˜ ์ˆ˜. 0์œผ๋กœ ์„ค์ •ํ•˜๋ฉด ๋ฏธ๋””์–ด ๋งํฌ๋ฅผ ํด๋ฆญํ•  ๋•Œ ํฌํ•จ๋œ ์ •๋ ฌ ๊ทœ์น™๋„ ๋ฌด์‹œ๋ฉ๋‹ˆ๋‹ค.", + + "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": "ํ•œ ๊ณก ๋ฐ˜๋ณต ์žฌ์ƒ\">๐Ÿ”", + "mt_one": "ํ•œ ๊ณก ์žฌ์ƒ ํ›„ ์ค‘์ง€\">1๏ธโƒฃ", + "mt_shuf": "๊ฐ ํด๋”์˜ ๊ณก์„ ๋ฌด์ž‘์œ„ ์žฌ์ƒ\">๐Ÿ”€", + "mt_aplay": "์„œ๋ฒ„์— ์ ‘์†ํ•œ ๋งํฌ์— ๊ณก ID๊ฐ€ ์žˆ์œผ๋ฉด ์ž๋™ ์žฌ์ƒ$N$N์ด๊ฒƒ์„ ๋น„ํ™œ์„ฑํ™”ํ•˜๋ฉด ์Œ์•… ์žฌ์ƒ ์‹œ ํŽ˜์ด์ง€ URL์ด ๊ณก ID๋กœ ์—…๋ฐ์ดํŠธ๋˜์ง€ ์•Š์•„, ์ด ์„ค์ •์ด ์†์‹ค๋˜๊ณ  URL์ด ๋‚จ์•„์žˆ์„ ๊ฒฝ์šฐ ์ž๋™ ์žฌ์ƒ๋˜๋Š” ๊ฒƒ์„ ๋ฐฉ์ง€ํ•ฉ๋‹ˆ๋‹ค.\">aโ–ถ", + "mt_preload": "๋Š๊น€ ์—†๋Š” ์žฌ์ƒ์„ ์œ„ํ•ด ๋‹ค์Œ ๊ณก์„ ๋ฏธ๋ฆฌ ๋ถˆ๋Ÿฌ์˜ค๊ธฐ ์‹œ์ž‘\">๋ฏธ๋ฆฌ๋กœ๋“œ", + "mt_prescan": "๋งˆ์ง€๋ง‰ ๊ณก์ด ๋๋‚˜๊ธฐ ์ „์— ๋‹ค์Œ ํด๋”๋กœ ์ด๋™ํ•˜์—ฌ$N์›น๋ธŒ๋ผ์šฐ์ €๊ฐ€ ์žฌ์ƒ์„ ๋ฉˆ์ถ”์ง€ ์•Š๋„๋ก ํ•ฉ๋‹ˆ๋‹ค.\">ํƒ์ƒ‰", + "mt_fullpre": "์ „์ฒด ๊ณก์„ ๋ฏธ๋ฆฌ ๋ถˆ๋Ÿฌ์˜ค๊ธฐ ์‹œ๋„;$Nโœ… <b>๋ถˆ์•ˆ์ •ํ•œ</b> ์—ฐ๊ฒฐ์—์„œ ํ™œ์„ฑํ™”,$NโŒ <b>๋А๋ฆฐ</b> ์—ฐ๊ฒฐ์—์„œ๋Š” ์•„๋งˆ๋„ ๋น„ํ™œ์„ฑํ™”\">์ „์ฒด", + "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์— ์•จ๋ฒ” ์ปค๋ฒ„ ํ‘œ์‹œ\">์•„ํŠธ", + "mt_follow": "์žฌ์ƒ ์ค‘์ธ ํŠธ๋ž™์ด ๋ณด์ด๋„๋ก ์Šคํฌ๋กค ์œ ์ง€\">๐ŸŽฏ", + "mt_compact": "์ปดํŒฉํŠธ ์ปจํŠธ๋กค\">โŸŽ", + "mt_uncache": "์บ์‹œ ์ง€์šฐ๊ธฐ (๋ธŒ๋ผ์šฐ์ €๊ฐ€ ๊ณก์˜ ๊นจ์ง„ ์‚ฌ๋ณธ์„ ์บ์‹œํ•˜์—ฌ$N์žฌ์ƒ์ด ์•ˆ๋˜๋Š” ๊ฒฝ์šฐ ์‹œ๋„ํ•ด๋ณด์„ธ์š”)\">์บ์‹œ์‚ญ์ œ", + "mt_mloop": "์—ด๋ฆฐ ํด๋” ๋ฐ˜๋ณต\">๐Ÿ” ๋ฐ˜๋ณต", + "mt_mnext": "๋‹ค์Œ ํด๋” ๋ถˆ๋Ÿฌ์˜ค๊ณ  ๊ณ„์†\">๐Ÿ“‚ ๋‹ค์Œ", + "mt_mstop": "์žฌ์ƒ ์ค‘์ง€\">โธ ์ค‘์ง€", + "mt_cflac": "flac/wav๋ฅผ {0}๋กœ ๋ณ€ํ™˜\">flac", + "mt_caac": "aac/m4a๋ฅผ {0}๋กœ ๋ณ€ํ™˜\">aac", + "mt_coth": "๋‹ค๋ฅธ ๋ชจ๋“  ๊ฒƒ (mp3 ์ œ์™ธ)์„ {0}๋กœ ๋ณ€ํ™˜\">๊ธฐํƒ€", + "mt_c2opus": "๋ฐ์Šคํฌํ†ฑ, ๋…ธํŠธ๋ถ, ์•ˆ๋“œ๋กœ์ด๋“œ ํ™˜๊ฒฝ์— ์ตœ์ \">opus", + "mt_c2owa": "iOS 17.5 ์ด์ƒ์šฉ opus-weba\">owa", + "mt_c2caf": "iOS 11๋ถ€ํ„ฐ 17๊นŒ์ง€์šฉ opus-caf\">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$Nboost <code>0</code> = ํ‘œ์ค€ 100% ๋ณผ๋ฅจ (์ˆ˜์ • ์—†์Œ)$N$Nwidth <code>1  </code> = ํ‘œ์ค€ ์Šคํ…Œ๋ ˆ์˜ค (์ˆ˜์ • ์—†์Œ)$Nwidth <code>0.5</code> = 50% ์ขŒ์šฐ ํฌ๋กœ์Šคํ”ผ๋“œ$Nwidth <code>0  </code> = ๋ชจ๋…ธ$N$Nboost <code>-0.8</code> & width <code>10</code> = ๋ณด์ปฌ ์ œ๊ฑฐ :^)$N$N์ดํ€„๋ผ์ด์ €๋ฅผ ํ™œ์„ฑํ™”ํ•˜๋ฉด ๋Š๊น€ ์—†๋Š” ์•จ๋ฒ”์ด ์˜จ์ „ํžˆ ๋Š๊น€ ์—†์ด ์žฌ์ƒ๋˜๋ฏ€๋กœ, ๊ทธ ์ ์ด ์ค‘์š”ํ•˜๋‹ค๋ฉด ๋ชจ๋“  ๊ฐ’์„ 0์œผ๋กœ ๋‘๊ณ  (width=1 ์ œ์™ธ) ์ผœ๋‘์„ธ์š”.", + "mt_drc": "๋‹ค์ด๋‚ด๋ฏน ๋ ˆ์ธ์ง€ ์ปดํ”„๋ ˆ์„œ(๋ณผ๋ฅจ ํ‰ํƒ„ํ™”/๋ฒฝ๋Œํ™”)๋ฅผ ํ™œ์„ฑํ™”ํ•ฉ๋‹ˆ๋‹ค. ์ŠคํŒŒ๊ฒŒํ‹ฐ์˜ ๊ท ํ˜•์„ ๋งž์ถ”๊ธฐ ์œ„ํ•ด EQ๋„ ํ™œ์„ฑํ™”๋˜๋ฏ€๋กœ, ์›ํ•˜์ง€ ์•Š์œผ๋ฉด 'width'๋ฅผ ์ œ์™ธํ•œ ๋ชจ๋“  EQ ํ•„๋“œ๋ฅผ 0์œผ๋กœ ์„ค์ •ํ•˜์„ธ์š”.$N$NTHRESHOLD dB ์ด์ƒ์˜ ์˜ค๋””์˜ค ๋ณผ๋ฅจ์„ ๋‚ฎ์ถฅ๋‹ˆ๋‹ค. THRESHOLD๋ฅผ ์ดˆ๊ณผํ•˜๋Š” ๋ชจ๋“  RATIO dB์— ๋Œ€ํ•ด 1dB์˜ ์ถœ๋ ฅ์ด ์žˆ์œผ๋ฏ€๋กœ, ๊ธฐ๋ณธ๊ฐ’์ธ tresh -24 ๋ฐ ratio 12๋Š” ๋ณผ๋ฅจ์ด -22dB๋ณด๋‹ค ์ปค์ง€์ง€ ์•Š์Œ์„ ์˜๋ฏธํ•˜๋ฉฐ, ์ดํ€„๋ผ์ด์ € ๋ถ€์ŠคํŠธ๋ฅผ 0.8 ๋˜๋Š” ATK 0๊ณผ ํฐ RLS (์˜ˆ: 90)๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ 1.8๊นŒ์ง€ ์•ˆ์ „ํ•˜๊ฒŒ ๋†’์ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค (firefox์—์„œ๋งŒ ์ž‘๋™, ๋‹ค๋ฅธ ๋ธŒ๋ผ์šฐ์ €์—์„œ๋Š” RLS ์ตœ๋Œ€ 1).$N$N(์œ„ํ‚ค๋ฐฑ๊ณผ๋ฅผ ์ฐธ์กฐํ•˜์„ธ์š”, ํ›จ์”ฌ ๋” ์ž˜ ์„ค๋ช…๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค)", + + "mb_play": "์žฌ์ƒ", + "mm_hashplay": "์ด ์˜ค๋””์˜ค ํŒŒ์ผ์„ ์žฌ์ƒํ• ๊นŒ์š”?", + "mm_m3u": "<code>Enter/ํ™•์ธ</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_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>{1}</code>๊ฐœ ํŒŒ์ผ ์ค‘ <code>{0}</code>๊ฐœ ํ‘œ์‹œ ์ค‘; <a href="#" id="bd_more">{2}๊ฐœ ๋” ๋ณด๊ธฐ</a> ๋˜๋Š” <a href="#" id="bd_all">๋ชจ๋‘ ๋ณด๊ธฐ</a></div>', + "fbd_all": '<div id="blazy"><code>{1}</code>๊ฐœ ํŒŒ์ผ ์ค‘ <code>{0}</code>๊ฐœ ํ‘œ์‹œ ์ค‘; <a href="#" id="bd_all">๋ชจ๋‘ ๋ณด๊ธฐ</a></div>', + "f_anota": "{1}๊ฐœ ํ•ญ๋ชฉ ์ค‘ {0}๊ฐœ๋งŒ ์„ ํƒ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.\n์ „์ฒด ํด๋”๋ฅผ ์„ ํƒํ•˜๋ ค๋ฉด ๋จผ์ € ๋งจ ์•„๋ž˜๋กœ ์Šคํฌ๋กคํ•˜์„ธ์š”.", + + "f_dls": 'ํ˜„์žฌ ํด๋”์˜ ํŒŒ์ผ ๋งํฌ๊ฐ€\n๋‹ค์šด๋กœ๋“œ ๋งํฌ๋กœ ๋ณ€๊ฒฝ๋˜์—ˆ์Šต๋‹ˆ๋‹ค', + + "f_partial": "ํ˜„์žฌ ์—…๋กœ๋“œ ์ค‘์ธ ํŒŒ์ผ์„ ์•ˆ์ „ํ•˜๊ฒŒ ๋‹ค์šด๋กœ๋“œํ•˜๋ ค๋ฉด, ํŒŒ์ผ ์ด๋ฆ„์ด ๊ฐ™์ง€๋งŒ <code>.PARTIAL</code> ํ™•์žฅ์ž๊ฐ€ ์—†๋Š” ํŒŒ์ผ์„ ํด๋ฆญํ•˜์„ธ์š”. ์ด ๊ฒฝ๊ณ ๋ฅผ ๋ฌด์‹œํ•˜๋ ค๋ฉด \"์ทจ์†Œ\" ๋˜๋Š” ESC๋ฅผ ๋ˆ„๋ฅด์„ธ์š”.\n\n\"ํ™•์ธ\"/Enter๋ฅผ ๋ˆ„๋ฅด๋ฉด ์ด ๊ฒฝ๊ณ ๋ฅผ ๋ฌด์‹œํ•˜๊ณ  <code>.PARTIAL</code> ์ž„์‹œ ํŒŒ์ผ์„ ๊ณ„์† ๋‹ค์šด๋กœ๋“œํ•˜๋ฉฐ, ์ด ๊ฒฝ์šฐ ๊ฑฐ์˜ ํ™•์‹คํžˆ ์†์ƒ๋œ ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ›๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.", + + "ft_paste": "{0}๊ฐœ ํ•ญ๋ชฉ ๋ถ™์—ฌ๋„ฃ๊ธฐ$N๋‹จ์ถ•ํ‚ค: ctrl-V", + "fr_eperm": "์ด๋ฆ„์„ ๋ฐ”๊ฟ€ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค:\n์ด ํด๋”์— \"์ด๋™\" ๊ถŒํ•œ์ด ์—†์Šต๋‹ˆ๋‹ค", + "fd_eperm": "์‚ญ์ œํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค:\n์ด ํด๋”์— \"์‚ญ์ œ\" ๊ถŒํ•œ์ด ์—†์Šต๋‹ˆ๋‹ค", + "fc_eperm": "์ž˜๋ผ๋‚ผ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค:\n์ด ํด๋”์— \"์ด๋™\" ๊ถŒํ•œ์ด ์—†์Šต๋‹ˆ๋‹ค", + "fp_eperm": "๋ถ™์—ฌ๋„ฃ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค\n์ด ํด๋”์— \"์“ฐ๊ธฐ\" ๊ถŒํ•œ์ด ์—†์Šต๋‹ˆ๋‹ค", + "fr_emore": "์ด๋ฆ„์„ ๋ฐ”๊ฟ€ ํ•ญ๋ชฉ์„ ํ•˜๋‚˜ ์ด์ƒ ์„ ํƒํ•˜์„ธ์š”", + "fd_emore": "์‚ญ์ œํ•  ํ•ญ๋ชฉ์„ ํ•˜๋‚˜ ์ด์ƒ ์„ ํƒํ•˜์„ธ์š”", + "fc_emore": "์ž˜๋ผ๋‚ผ ํ•ญ๋ชฉ์„ ํ•˜๋‚˜ ์ด์ƒ ์„ ํƒํ•˜์„ธ์š”", + "fcp_emore": "ํด๋ฆฝ๋ณด๋“œ์— ๋ณต์‚ฌํ•  ํ•ญ๋ชฉ์„ ํ•˜๋‚˜ ์ด์ƒ ์„ ํƒํ•˜์„ธ์š”", + + "fs_sc": "ํ˜„์žฌ ํด๋” ๊ณต์œ ", + "fs_ss": "์„ ํƒํ•œ ํŒŒ์ผ ๊ณต์œ ", + "fs_just1d": "ํ•˜๋‚˜ ์ด์ƒ์˜ ํด๋”๋ฅผ ์„ ํƒํ•˜๊ฑฐ๋‚˜,\nํŒŒ์ผ๊ณผ ํด๋”๋ฅผ ํ•œ ๋ฒˆ์— ์„ž์–ด ์„ ํƒํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค", + "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/Cancel</code>๋ฅผ ๋ˆŒ๋Ÿฌ ๋‹ซ๊ธฐ", + + "frt_dec": "๊นจ์ง„ ํŒŒ์ผ ์ด๋ฆ„์˜ ์ผ๋ถ€ ๊ฒฝ์šฐ๋ฅผ ์ˆ˜์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค\">url-๋””์ฝ”๋“œ", + "frt_rst": "์ˆ˜์ •๋œ ํŒŒ์ผ ์ด๋ฆ„์„ ์›๋ž˜๋Œ€๋กœ ๋˜๋Œ๋ฆฝ๋‹ˆ๋‹ค\">โ†บ ์ดˆ๊ธฐํ™”", + "frt_abrt": "์ด ์ฐฝ์„ ์ค‘๋‹จํ•˜๊ณ  ๋‹ซ์Šต๋‹ˆ๋‹ค\">โŒ ์ทจ์†Œ", + "frb_apply": "์ด๋ฆ„ ๋ฐ”๊พธ๊ธฐ ์ ์šฉ", + "fr_adv": "๋ฐฐ์น˜/๋ฉ”ํƒ€๋ฐ์ดํ„ฐ/ํŒจํ„ด ์ด๋ฆ„ ๋ฐ”๊พธ๊ธฐ\">๊ณ ๊ธ‰", + "fr_case": "๋Œ€์†Œ๋ฌธ์ž ๊ตฌ๋ถ„ ์ •๊ทœ์‹\">๋Œ€์†Œ๋ฌธ์ž", + "fr_win": "Windows ์•ˆ์ „ ์ด๋ฆ„; <code><>:"\\|?*</code>๋ฅผ ์ผ๋ณธ์–ด ์ „๊ฐ ๋ฌธ์ž๋กœ ๋ฐ”๊ฟ‰๋‹ˆ๋‹ค\">win", + "fr_slash": "<code>/</code>๋ฅผ ์ƒˆ ํด๋”๋ฅผ ๋งŒ๋“ค์ง€ ์•Š๋Š” ๋ฌธ์ž๋กœ ๋ฐ”๊ฟ‰๋‹ˆ๋‹ค\">/ ์—†์Œ", + "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": "<code>win</code> ๋ฐ/๋˜๋Š” <code>/ ์—†์Œ</code>์œผ๋กœ ์ธํ•ด ์ƒˆ ์ด๋ฆ„ ์ค‘ {0}๊ฐœ๊ฐ€ ๋ณ€๊ฒฝ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.\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> ๋ธŒ๋ผ์šฐ์ € ํƒญ์—์„œ๋งŒ ๋ถ™์—ฌ๋„ฃ์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค", + + "fcc_ok": "{0}๊ฐœ ํ•ญ๋ชฉ์„ ํด๋ฆฝ๋ณด๋“œ์— ๋ณต์‚ฌํ–ˆ์Šต๋‹ˆ๋‹ค", + "fcc_warn": "{0}๊ฐœ ํ•ญ๋ชฉ์„ ํด๋ฆฝ๋ณด๋“œ์— ๋ณต์‚ฌํ–ˆ์Šต๋‹ˆ๋‹ค\n\nํ•˜์ง€๋งŒ: ์„ ํƒ ํ•ญ๋ชฉ์ด ๋„ˆ๋ฌด ์ปค์„œ <b>์ด</b> ๋ธŒ๋ผ์šฐ์ € ํƒญ์—์„œ๋งŒ ๋ถ™์—ฌ๋„ฃ์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค", + + "fp_apply": "์ด ์ด๋ฆ„ ์‚ฌ์šฉ", + "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", + + "tv_load": "ํ…์ŠคํŠธ ๋ฌธ์„œ ๋ถˆ๋Ÿฌ์˜ค๋Š” ์ค‘:\n\n{0}\n\n{1}% ({3} MiB ์ค‘ {2} 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_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\n something.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": "์—…๋กœ๋“œ ์‹œ์ ", + "s_ad": "๊ณ ๊ธ‰", + "s_s1": "์ตœ์†Œ MiB", + "s_s2": "์ตœ๋Œ€ MiB", + "s_d1": "์ตœ์†Œ ISO-8601", + "s_d2": "์ตœ๋Œ€ ISO-8601", + "s_u1": "์ดํ›„", + "s_u2": "์ด์ „", + "s_r1": "๊ฒฝ๋กœ์— ํฌํ•จ   (๊ณต๋ฐฑ์œผ๋กœ ๊ตฌ๋ถ„)", + "s_f1": "์ด๋ฆ„์— ํฌํ•จ   (-๋กœ ์ œ์™ธ)", + "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 (windows 7 ๋ฐ ์ด์ „ ๋ฒ„์ „์—์„œ ๋ฌธ์ œ๊ฐ€ ์žˆ์„ ์ˆ˜ ์žˆ์Œ)", + "fz_zipd": "์ •๋ง ์˜ค๋ž˜๋œ ์†Œํ”„ํŠธ์›จ์–ด๋ฅผ ์œ„ํ•œ ์ „ํ†ต์ ์ธ cp437 ํŒŒ์ผ ์ด๋ฆ„์ด ํฌํ•จ๋œ zip", + "fz_zipc": "MS-DOS PKZIP v2.04g (1993๋…„ 10์›”)์šฉ์œผ๋กœ$Ncrc32๊ฐ€ ๋ฏธ๋ฆฌ ๊ณ„์‚ฐ๋œ cp437$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="#">๋‹จ์ˆœ UI๋กœ ์ „ํ™˜</a> (์—…๋กœ๋“œ ์†๋„๊ฐ€ ํ–ฅ์ƒ๋  ์ˆ˜ ์žˆ์Œ)', + "u_depot": '<a href="#">ํ™”๋ คํ•œ 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": '์ด {1}๊ฐœ ์ค‘ ๋‹ค์Œ {0}๊ฐœ์˜ ํŒŒ์ผ์€ ํŒŒ์ผ ์‹œ์Šคํ…œ ๊ถŒํ•œ ๋ฌธ์ œ ๋“ฑ์œผ๋กœ ๊ฑด๋„ˆ๋›ฐ์—ˆ์Šต๋‹ˆ๋‹ค:\n\n', + "u_blankf": '์ด {1}๊ฐœ ์ค‘ ๋‹ค์Œ {0}๊ฐœ์˜ ํŒŒ์ผ์€ ๋น„์–ด์žˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋ž˜๋„ ์—…๋กœ๋“œํ•˜์‹œ๊ฒ ์Šต๋‹ˆ๊นŒ?\n\n', + "u_applef": '์ด {1}๊ฐœ ์ค‘ ๋‹ค์Œ {0}๊ฐœ์˜ ํŒŒ์ผ์€ ์•„๋งˆ๋„ ๋ถˆํ•„์š”ํ•œ ํŒŒ์ผ์ผ ๊ฒƒ์ž…๋‹ˆ๋‹ค.\n๋‹ค์Œ ํŒŒ์ผ์„ ๊ฑด๋„ˆ๋›ฐ๋ ค๋ฉด <code>ํ™•์ธ/Enter</code>๋ฅผ ๋ˆ„๋ฅด์„ธ์š”,\nํ•ด๋‹น ํŒŒ์ผ๋„ ์—…๋กœ๋“œํ•˜๋ ค๋ฉด <code>์ทจ์†Œ/ESC</code>๋ฅผ ๋ˆ„๋ฅด์„ธ์š”:\n\n', + "u_just1": '\nํŒŒ์ผ์„ ํ•˜๋‚˜๋งŒ ์„ ํƒํ•˜๋ฉด ๋” ์ž˜ ์ž‘๋™ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค', + "u_ff_many": '<b>๋ฆฌ๋ˆ…์Šค/macOS/์•ˆ๋“œ๋กœ์ด๋“œ</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": "{1} ์ค‘ ์ฒญํฌ {0} ์—…๋กœ๋“œ ์‹คํŒจ;\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>โš™๏ธ ์„ค์ •</code>์—์„œ  <code>mt</code>  ๋ฒ„ํŠผ์„ ๋น„ํ™œ์„ฑํ™”ํ•˜์„ธ์š”</li><li>๊ทธ๋ฆฌ๊ณ  ๋‹ค์‹œ ๊ทธ ์—…๋กœ๋“œ๋ฅผ ์‹œ๋„ํ•ด๋ณด์„ธ์š”</li></ul>์—…๋กœ๋“œ๊ฐ€ ์กฐ๊ธˆ ๋А๋ ค์ง€๊ฒ ์ง€๋งŒ, ์–ด์ฉ” ์ˆ˜ ์—†์ฃ .\n๋ถˆํŽธ์„ ๋“œ๋ ค ์ฃ„์†กํ•ฉ๋‹ˆ๋‹ค!\n\nPS: ์ด ๋ฒ„๊ทธ๋Š” 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>(๋‹จ์ˆœ UI)๋ฅผ ํ™œ์„ฑํ™”ํ•˜์„ธ์š”<li>๊ทธ๋ฆฌ๊ณ  ๋‹ค์‹œ ๊ทธ ์—…๋กœ๋“œ๋ฅผ ์‹œ๋„ํ•ด๋ณด์„ธ์š”</li></ul>\nPS: 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="ํ˜„์ง€ ์‹œ๊ฐ„">---</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}๊ฐœ ์—…๋กœ๋“œ ์„ฑ๊ณต,\n{1}๊ฐœ ์—…๋กœ๋“œ ์‹คํŒจ, ์ฃ„์†ก", + "ur_sm": "์™„๋ฃŒ;\n์„œ๋ฒ„์—์„œ {0}๊ฐœ ํŒŒ์ผ ์ฐพ์Œ,\n์„œ๋ฒ„์—์„œ {1}๊ฐœ ํŒŒ์ผ ์ฐพ์ง€ ๋ชปํ•จ", + + "lang_set": '๋ณ€๊ฒฝ ์‚ฌํ•ญ์„ ์ ์šฉํ•˜๊ธฐ ์œ„ํ•ด ์ƒˆ๋กœ๊ณ ์นจํ•˜์‹œ๊ฒ ์Šต๋‹ˆ๊นŒ?' + }, "nld": { "tt": "Nederlands", @@ -5151,6 +6429,7 @@ var Ls = { "gou": 'Bovenligende map">Omhoog', "gon": 'Volgende map">Volgende', "logout": "Uitloggen ", + "login": "Inloggen", //m "access": " Toegang", "ot_close": "Sluit onder-menu", "ot_search": "Zoek voor bestanden bij attributes, pad / naam, muziek tags, of elk andere combinatie tussen$N$N<code>foo bar</code> = moet beide ยซfooยป en ยซbarยป bevatten,$N<code>foo -bar</code> = moet ยซfooยป bevatten maar geen ยซbarยป,$N<code>^yana .opus$</code> = start met ยซyanaยป en moet een ยซopusยป bestand zijn$N<code>"try unite"</code> = moet precies ยซtry uniteยป bevatten$N$Nde datum formaat is iso-8601, zoals$N<code>2009-12-31</code> of <code>2020-09-12 23:30:00</code>", @@ -5204,12 +6483,12 @@ var Ls = { "ul_btnu": "U P L O A D", "ul_btns": "Z O E K E N", - "ul_hash": "Gash", - "ul_send": "Verstuur", + "ul_hash": "Hashing", + "ul_send": "Versturen", "ul_done": "Klaar", "ul_idle1": "Geen uploads in wachtrij", "ut_etah": "Gemiddelde <em>hashing</em> snelheid en geschatte tijd tot de voltooiing", - "ut_etau": "Gemiddelde <em>upload</em> snelheid en geschatte tijd tot voltooiing", + "ut_etau": "Gemiddelde <em>verzend</em> snelheid en geschatte tijd tot voltooiing", "ut_etat": "Gemiddelde <em>totale</em> snelheid en geschatte tijd tot voltooiing", "uct_ok": "Succesvol afgerond", @@ -5337,9 +6616,9 @@ var Ls = { "mt_mloop": "De open map herhalen\">๐Ÿ” loop", "mt_mnext": "Laad de volgende map en ga verder\">๐Ÿ“‚ next", "mt_mstop": "Stoppen met afspelen\">โธ stop", - "mt_cflac": "flac / wav omzetten naar opus\">flac", - "mt_caac": "aac / m4a omzetten naar opus\">aac", - "mt_coth": "Alle andere bestanden (geen mp3) converteren naar opus\">oth", + "mt_cflac": "flac / wav omzetten naar {0}\">flac", + "mt_caac": "aac / m4a omzetten naar {0}\">aac", + "mt_coth": "Alle andere bestanden (geen mp3) converteren naar {0}\">oth", "mt_c2opus": "Beste keuze voor computers, laptops, android\">opus", "mt_c2owa": "opus-weba, voor iOS 17.5 en nieuwer\">owa", "mt_c2caf": "opus-caf, voor iOS 11 tot en met iOS 17\">caf", @@ -5356,7 +6635,7 @@ var Ls = { "mb_play": "Afspelen", "mm_hashplay": "Deze audio bestand afspelen?", - "mm_m3u": "Druk op <code>Enter/OK</code> om af te spelen\nDruk op <code>ESC/Cancel</code> om te bewerken", + "mm_m3u": "Druk op <code>Enter/OK</code> om af te spelen\nDruk op <code>ESC/Annuleren</code> om te bewerken", "mp_breq": "Heeft firefox 82+ of chrome 73+ of iOS 15+", "mm_bload": "Aan het laden...", "mm_bconv": "Opmzetten naar {0}, even geduld...", @@ -5419,7 +6698,7 @@ var Ls = { "fs_tsrc": "Het bestand of de map die u wilt delen", "fs_ppwd": "Optioneel wachtwoord", "fs_w8": "Delen...", - "fs_ok": "Druk op <code>Enter/OK</code> naar klembord te zetten\Druk op <code>ESC/Cancel</code> om te sluiten", + "fs_ok": "Druk op <code>Enter/OK</code> naar klembord te zetten\Druk op <code>ESC/Annuleren</code> om te sluiten", "frt_dec": "Kan sommige gevallen van gebroken bestandsnamen oplossen\">url-decode", "frt_rst": "Gewijzigde bestandsnamen terugzetten naar de oorspronkelijke namen\">โ†บ reset", @@ -5464,6 +6743,7 @@ var Ls = { "fcp_ok": "Kopiรซren OK", "fp_busy": "{0} items verplaatsen...\n\n{1}", "fcp_busy": "{0} items kopiรซren...\n\n{1}", + "fp_abrt": "afbreken...", //m "fp_err": "Verplaatsen mislukt:\n", "fcp_err": "Kopieรซren mislukt:\n", "fp_confirm": "Verplaats deze {0} items hierheen?", @@ -5608,7 +6888,7 @@ var Ls = { "u_enoow": "Overschrijven zal hier niet werken; je heb verwijder toestemming nodig", "u_badf": 'Deze {0} bestanden (van {1} totaal) zijn overgeslagen, mogelijk door bestandssysteemmachtigingen:\n\n', "u_blankf": 'Deze {0} bestanden (van {1} totaal) zijn leeg; alsnog uploaden?\n\n', - "u_applef": 'Deze {0} bestanden (van {1} totaal) zijn waarschijnlijk ongewenst;\nKlik op <code>OK/Enter</code> om de volgende bestanden over te slaan,\Klik op <code>Cancel/ESC</code> niet uit te sluiten en deze ook te uploaden:\n\n', + "u_applef": 'Deze {0} bestanden (van {1} totaal) zijn waarschijnlijk ongewenst;\nKlik op <code>OK/Enter</code> om de volgende bestanden over te slaan,\Klik op <code>Annuleren/ESC</code> niet uit te sluiten en deze ook te uploaden:\n\n', "u_just1": '\nMisschien werkt het beter als je slechts รฉรฉn bestand selecteert', "u_ff_many": "Als je <b>Linux / MacOS / Android,</b> gebruikt dan <em>kan</em> deze hoeveelheid bestanden <a href=\"https://bugzilla.mozilla.org/show_bug.cgi?id=1790500\" target=\"_blank\">Firefox crashen!</a>\nals dat gebeurt, probeer het dan opnieuw (of gebruik Chrome).", "u_up_life": "Deze upload wordt verwijderd van de server\n{0} nadat het is voltooid", @@ -5672,6 +6952,1900 @@ var Ls = { "lang_set": "Vernieuw de pagina om de wijziging door te voeren?", }, + "nno": { + "tt": "Nynorsk", + + "cols": { + "c": "handlingsknappar", + "dur": "varigheit", + "q": "kvalitet / bitrate", + "Ac": "lydformat", + "Vc": "videoformat", + "Fmt": "format / innpakning", + "Ahash": "lydkontrollsum", + "Vhash": "videokontrollsum", + "Res": "opplรธysing", + "T": "filtype", + "aq": "lydkvalitet / bitrate", + "vq": "videokvalitet / bitrate", + "pixfmt": "fargekoding / detaljnivรฅ", + "resw": "horisontal opplรธysing", + "resh": "vertikal opplรธysing", + "chs": "lydkanaler", + "hz": "lydopplรธsing" + }, + + "hks": [ + [ + "ymse", + ["ESC", "lukk saker og ting"], + + "filbehandlar", + ["G", "listevisning eller ikon"], + ["T", "miniatyrbilder pรฅ/av"], + ["โ‡ง A/D", "ikonstorleik"], + ["ctrl-K", "slett valde"], + ["ctrl-X", "klipp ut valde"], + ["ctrl-C", "kopiรฉr รฅt utklippstavle"], + ["ctrl-V", "lim inn (flytt/kopiรฉr)"], + ["Y", "last ned valde"], + ["F2", "endre namn pรฅ valde"], + + "filmarkering", + ["space", "markรฉr fil"], + ["โ†‘/โ†“", "flytt markรธr"], + ["ctrl โ†‘/โ†“", "flytt markรธr og scroll"], + ["โ‡ง โ†‘/โ†“", "velg forr./neste fil"], + ["ctrl-A", "velg alle filer / mapper"], + ], [ + "navigering", + ["B", "mappehierarki eller filsti"], + ["I/K", "forr./neste mappe"], + ["M", "eitt nivรฅ opp (eller lukk)"], + ["V", "vis mapper eller tekstfiler"], + ["A/D", "panelstorleik"], + ], [ + "musikkspelar", + ["J/L", "forr./neste song"], + ["U/O", "hopp 10sek bak/fram"], + ["0..9", "hopp รฅt 0%..90%"], + ["P", "pause, eller start / fortsett"], + ["S", "marker spelande song"], + ["Y", "last ned song"], + ], [ + "bildevisar", + ["J/L, โ†/โ†’", "forr./neste bilde"], + ["Home/End", "fรธrste/siste bilde"], + ["F", "fullskjermvisning"], + ["R", "rotรฉr รฅt hรธyre"], + ["โ‡ง R", "rotรฉr รฅt venstre"], + ["S", "markรฉr bilde"], + ["Y", "last ned bilde"], + ], [ + "videospelar", + ["U/O", "hopp 10sek bak/fram"], + ["P/K/Space", "pause / fortsett"], + ["C", "fortsett รฅt neste fil"], + ["V", "gjenta avspeling"], + ["M", "lyd av/pรฅ"], + ["[ og ]", "gjentaksintervall"], + ], [ + "dokumentvisar", + ["I/K", "forr./neste fil"], + ["M", "lukk tekstdokument"], + ["E", "redigรฉr tekstdokument"], + ["S", "markรฉr fil (for F2/ctrl-x/...)"], + ["Y", "last ned tekstfil"], + ] + ], + + "m_ok": "OK", + "m_ng": "Avbryt", + + "enable": "Aktiv", + "danger": "VARSKU", + "clipped": "kopiert รฅt utklippstavla", + + "ht_s1": "sekund", + "ht_s2": "sekund", + "ht_m1": "minutt", + "ht_m2": "minutt", + "ht_h1": "time", + "ht_h2": "timar", + "ht_d1": "dag", + "ht_d2": "dagar", + "ht_and": " og ", + + "goh": "kontrollpanel", + "gop": 'navigรฉr รฅt mappa fรธr den her">forr.', + "gou": 'navigรฉr eitt nivรฅ opp">opp', + "gon": 'navigรฉr รฅt mappa etter den her">neste', + "logout": "Logg ut ", + "login": "Logg inn", + "access": " รฅtgang", + "ot_close": "lukk reiskap", + "ot_search": "sรธk etter filer ved รฅ angje filnamn, mappenamn, tid, storleik, eller metadata som songtittel / artist / osv.$N$N<code>foo bar</code> = inneheld bรฅe ยซfooยป og ยซbarยป,$N<code>foo -bar</code> = innehold ยซfooยป men ikkje ยซbarยป,$N<code>^yana .opus$</code> = startar med ยซyanaยป, filtype ยซopusยป$N<code>"try unite"</code> = ยซtry uniteยป eksakt$N$Ndatoformat er iso-8601, sรฅ f.eks.$N<code>2009-12-31</code> eller <code>2020-09-12 23:30:00</code>", + "ot_unpost": "unpost: slett filer som du nyleg har lastet opp; ยซangre-knappenยป", + "ot_bup": "bup: tradisjonell / primitiv filopplasting,$N$Nfungerar i om lag samtlege nettlesarar", + "ot_mkdir": "mkdir: lag ei ny mappe", + "ot_md": "new-md: lag eit nytt markdown-dokument", + "ot_msg": "msg: send ein beskjed รฅt serverloggen", + "ot_mp": "musikkspelarinstillinger", + "ot_cfg": "andre innstillinger", + "ot_u2i": 'up2k: last opp filer (viss du har skriveรฅtgang) eller bytt รฅt sรธkemodus for รฅ sjekke om filene finnast ein eller annan plass pรฅ serveren$N$Nopplastinger kan startast opp att etter avbrot, skjer stykkevis for potensielt hรธgare ytelse, og ivaretek datostempling -- men bruker litt meir prosessorkraft enn [๐ŸŽˆ]  (den primitive opplastaren "bup")<br /><br />mens opplastinger fรธregรฅr sรฅ visast framdrifta her oppe!', + "ot_u2w": 'up2k: filopplasting med stรธtte for รฅ starte opp att avbrotne opplastinger -- steng ned nettlesaren og drage dei same filene inn i nettlesaren igjen for รฅ plukke opp att der du slapp$N$Nopplastinger skjer stykkevis for potensielt hรธgare ytelse, og ivaretek datostempling -- men bruker litt meir prosessorkraft enn [๐ŸŽˆ]  (den primitive opplastaren "bup")<br /><br />mens opplastinger fรธregรฅr sรฅ visast framdrifta her oppe!', + "ot_noie": 'Fungerer mye betre i Chrome / Firefox / Edge', + + "ab_mkdir": "lag mappe", + "ab_mkdoc": "nytt dokument", + "ab_msg": "send melding", + + "ay_path": "gรฅ videre รฅt mapper", + "ay_files": "gรฅ videre รฅt filer", + + "wt_ren": "gje nye namn รฅt dei valde filene$NSnarvei: F2", + "wt_del": "slett dei valde filene$NSnarvei: ctrl-K", + "wt_cut": "klipp ut dei valde filene <small>(for รฅ lime inn ein annan plass)</small>$NSnarvei: ctrl-X", + "wt_cpy": "kopiรฉr dei valde filene รฅt utklippstavla$N(for รฅ lime inn ein annan plass)$NSnarvei: ctrl-C", + "wt_pst": "lim inn filer (som tidligare blei klipt ut / kopiert ein annan plass)$NSnarvei: ctrl-V", + "wt_selall": "velg alle filer$NSnarvei: ctrl-A (mens fokus er pรฅ ei fil)", + "wt_selinv": "invertรฉr utval", + "wt_zip1": "last ned denne mappa som eit arkiv", + "wt_selzip": "last ned dei valde filene som eit arkiv", + "wt_seldl": "last ned dei valde filene$NSnarvei: Y", + "wt_npirc": "kopiรฉr songinfo (irc-formatert)", + "wt_nptxt": "kopiรฉr songinfo", + "wt_m3ua": "legg song รฅt i m3u-speleliste$N(husk รฅ klikk pรฅ <code>๐Ÿ“ปcopy</code> senere)", + "wt_m3uc": "kopiรฉr m3u-spelelista รฅt utklippstavla", + "wt_grid": "bytt mellom ikon og listevising$NSnarvei: G", + "wt_prev": "fรธrre song$NSnarvei: J", + "wt_play": "play / pause$NSnarvei: P", + "wt_next": "neste song$NSnarvei: L", + + "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_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", + "ut_srch": "gjer eit sรธk i staden for รฅ laste opp --$Nleitar gjennom alle mappane du har lov รฅt รฅ sjรฅ", + "ut_par": "sett รฅt 0 for รฅ midlertidig stoppe opplasting$N$Nhรธge verdier (4 eller 8) kan gje betre yting,$Nspesielt pรฅ treige internettlinjer$N$Nbรธr ikkje vere hรธgare enn 1 pรฅ LAN$Neller viss serveren sin harddisk er treig", + "ul_btn": "slepp filer / mapper<br>her (eller klikk meg)", + "ul_btnu": "L A S T   O P P", + "ul_btns": "F I L S ร˜ K", + + "ul_hash": "synfar", + "ul_send": " send", + "ul_done": "total", + "ul_idle1": "ingen handlinger i kรธen", + "ut_etah": "snitthastigheit for <em>synfaring</em> samt gjenstรฅande tid", + "ut_etau": "snitthastigheit for <em>opplasting</em> samt gjenstรฅande tid", + "ut_etat": "<em>total</em> snitthastigheit og gjenstรฅande tid", + + "uct_ok": "fullfรธrt uten problem", + "uct_ng": "fullfรธrt under tvil (duplikat, ikkje funne, ...)", + "uct_done": "fullfรธrt (enten <em>ok</em> eller <em>ng</em>)", + "uct_bz": "aktive handlinger (synfaring / opplasting)", + "uct_q": "kรธa", + + "utl_name": "filnamn", + "utl_ulist": "vis", + "utl_ucopy": "kopiรฉr", + "utl_links": "lenker", + "utl_stat": "status", + "utl_prog": "fremdrift", + + // mรฅ vere korte: + "utl_404": "404", + "utl_err": "FEIL!", + "utl_oserr": "OS-feil", + "utl_found": "funnet", + "utl_defer": "seinare", + "utl_yolo": "YOLO", + "utl_done": "ferdig", + + "ul_flagblk": "filene har blitt lagd i kรธa</b><br>men det er ein anna nettlesarfane som held pรฅ med synfaring eller opplasting akkurat no,<br>sรฅ venter รฅt den er ferdig fรธrst", + "ul_btnlk": "brytaren har blitt lรฅst รฅt denne tilstanden i serverens konfigurasjon", + + "udt_up": "Last opp", + "udt_srch": "Sรธk", + "udt_drop": "Slepp filene her", + + "u_nav_m": '<h6>kva har du?</h6><code>Enter</code> = Filer (รฉin eller fleire)\n<code>ESC</code> = ร‰i mappe (inkludert undermapper)', + "u_nav_b": '<a href="#" id="modal-ok">Filer</a><a href="#" id="modal-ng">ร‰i mappe</a>', + + "cl_opts": "brytarar", + "cl_themes": "utsjรฅnad", + "cl_langs": "sprรฅk", + "cl_ziptype": "nedlasting av mapper", + "cl_uopts": "up2k-brytarar", + "cl_favico": "favicon", + "cl_bigdir": "store mapper", + "cl_hsort": "#sort", + "cl_keytype": "notasjon for musikalsk dur", + "cl_hiddenc": "skjulte kolonner", + "cl_hidec": "skjul", + "cl_reset": "nullstill", + "cl_hpick": "klikk pรฅ overskrifta รฅt kolonnene du รธnskjer รฅ skjule i tabellen nedanfor", + "cl_hcancel": "kolonne-skjuling avbrote", + + "ct_grid": '็”ฐ ikon', + "ct_ttips": 'vis hjelpetekst ved รฅ holde musa over ting">โ„น๏ธ tips', + "ct_thumb": 'vis miniatyrbilder i staden for ikon$NSnarvei: T">๐Ÿ–ผ๏ธ bilder', + "ct_csel": 'bruk tastane CTRL og SHIFT for markering av filer i ikonvising">merk', + "ct_ihop": 'bla ned รฅt sist viste bilde nรฅr bildevisaren lukkast">gโฎฏ', + "ct_dots": 'vis skjulte filer (gitt at serveren tillรจt det)">.synlig', + "ct_qdel": 'sletteknappen spรธr berre รฉin gong om stadfesting">hurtig๐Ÿ—‘๏ธ', + "ct_dir1st": 'sortรฉr slik at mapper kjem framanfor filer">๐Ÿ“ fรธrst', + "ct_nsort": 'naturlig sortering (skjรธnar tal i filnamn)">nsort', + "ct_utc": 'bruk UTC for alle klokkeslett">UTC', + "ct_readme": 'vis README.md nedanfor filene">๐Ÿ“œ readme', + "ct_idxh": 'vis index.html i staden for filliste">htm', + "ct_sbars": 'vis rullgardiner / skrollefelt">โŸŠ', + + "cut_umod": 'i tilfelle ei fil du lastar opp alt finnast pรฅ serveren, sรฅ skal tidsstempelet รฅt serveren oppdaterast slik at det stemmer overeins med din lokale fil (krev rettigheitene write+delete)">re๐Ÿ“…', + + "cut_turbo": "forenkla synfaring ved opplasting; bรธr etter alt รฅ dรธme <em>ikkje</em> skruast pรฅ:$N$Nnyttig dersom du var midt i ei svรฆr opplasting som mรฅtte startast pรฅ nytt av ein eller annan grunn, og du vil komme i gang igjen sรฅ raskt som i det heile mulig.$N$Nnรฅr denne er skrudd pรฅ sรฅ forenklast synfaringa kraftig; i staden for รฅ utfรธre ein trygg sjekk pรฅ om filene finnast pรฅ serveren i god stand, sรฅ sjekkast det kun om <em>filstorleiken</em> stemmer. Sรฅ dersom ein korrupt fil vere pรฅ serveren allerede, pรฅ same plass, med same storleik og namn, sรฅ blir det <em>ikkje oppdaga</em>.$N$Ndet anbefalast รฅ kun benytte denne funksjonen for รฅ komme seg raskt gjennom sjรธlve opplastinga, for sรฅ รฅ skru den av, og รฅt slutt "laste opp" dei same filene รฉin gong รฅt -- slik at integriteten kan verifiserast\">turbo", + + "cut_datechk": "har ingen effekt dersom turbo er skrudd av$N$Ngjer turbo bittelitt tryggare ved รฅ sjekke datostemplinga pรฅ filene (i tillegg รฅt filstorleik)$N$N<em>burde</em> oppdage og gjenoppta dei fleste ufullstendige opplastinger, men er <em>ikkje</em> ein fullverdig erstatning for รฅ deaktivere turbo og gjere ein skikkeleg sjekk\">date-chk", + + "cut_u2sz": "storleik i megabyte for kvart bruddstykke for opplasting. Store verdiar flyg betre over atlanteren. Smรฅ verdiar kan vere betre pรฅ flettande ustabile samband", + + "cut_flag": "samkรธyrer nettlesarfaner slik at berre รฉin $N kan holde pรฅ med synfaring / opplasting $N -- andre faner mรฅ รณg ha denne skrudd pรฅ $N -- fungerar kun innanom same domene", + + "cut_az": "last opp filer i alfabetisk rekkefรธlge, i staden for minste-fil-fรธrst$N$Nalfabetisk kan gjere det lettare รฅ anslรฅ om alt gjekk bra, men er bittelitt treigare pรฅ fiber / LAN", + + "cut_nag": "meldingsvarsel nรฅr opplasting er ferdig$N(kun om nettlesarfana ikkje er synlig)", + "cut_sfx": "lydvarsel nรฅr opplasting er ferdig$N(kun om nettlesarfanen ikkje er synlig)", + + "cut_mt": "raskere synfaring ved รฅ bruke heile CPU'en$N$Ndenne funksjonen nytter web-workers$Nog krev meir RAM (opptil 512 MiB ekstra)$N$Ngjer https 30% raskare, http 4.5x raskare\">mt", + + "cut_wasm": "bruk wasm i staden for nettlesaren sin sha512-funksjon; gjev betre yting pรฅ chrome-baserte nettlesarar, men brukar meir CPU, og eldre versjoner av chrome toler det ikkje (et opp all RAM og krรฆsjer)\">wasm", + + "cft_text": "ikontekst (blank ut og last siden pรฅ nytt for รฅ deaktivere)", + "cft_fg": "farge", + "cft_bg": "bakgrunnsfarge", + + "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", + + "tt_entree": "bytt รฅt mappehierarki$NSnarvei: B", + "tt_detree": "bytt รฅt tradisjonell stivising$NSnarvei: B", + "tt_visdir": "bla ned รฅt den รฅpne mappa", + "tt_ftree": "bytt mellom filstruktur og tekstfiler$NSnarvei: V", + "tt_pdock": "vis dei overordna mappane i eit panel", + "tt_dynt": "รธk bredda pรฅ panelet ettersom treet utvider seg", + "tt_wrap": "linjebryting", + "tt_hover": "vis heile mappenamnet nรฅr musepeikaren treff mappa$N( gjer diverre at scrollhjulet fusker dersom musepeikaren ikkje finn seg i grรธfta )", + + "ml_pmode": "ved enden av mappa", + "ml_btns": "knapper", + "ml_tcode": "konvertering", + "ml_tcode2": "konvertรฉr til", + "ml_tint": "tint", + "ml_eq": "audio equalizer (tonejustering)", + "ml_drc": "compressor (volumutjevning)", + + "mt_loop": "spel den same songen om og om igjen\">๐Ÿ”", + "mt_one": "spel kun รฉin song\">1๏ธโƒฃ", + "mt_shuf": "songane i kvar mappe$Nspelast i tilfeldig rekkefรธlge\">๐Ÿ”€", + "mt_aplay": "prรธv รฅ starte avspeling viss linken du trykte pรฅ for รฅ รฅpne nettsida inneheld ein song-ID$N$Nviss denne deaktiverast sรฅ vil heller ikkje nettside-URL'en bli oppdatert med song-ID'er nรฅr musikk spelast, i tilfelle innstillingane skulle gรฅ tapt og nettsida lastast pรฅ ny\">aโ–ถ", + "mt_preload": "hent ned litt av neste song i forkant,$Nslik at pausa i overgangen blir mindre\">forsyn", + "mt_prescan": "ved behov, bla รฅt neste mappe$Nslik at nettlesaren lar oss$Nfortsetja รฅ spele musikk\">bla", + "mt_fullpre": "hent ned heile neste song, ikkje berre litt:$Nโœ… skru pรฅ viss nettet ditt er <b>ustabilt</b>,$NโŒ skru av viss nettet ditt er <b>treigt</b>\">full", + "mt_fau": "for telefoner: forhindre at avspeling stoppar viss nettet er for treigt รฅt รฅ laste neste song i tide. Viss pรฅskrudd kan det forรฅrsake at songinfo ikkje visast korrekt i OS'et\">โ˜•๏ธ", + "mt_waves": "waveform seekbar:$Nvis volumkurve i avspelingsfeltet\">~s", + "mt_npclip": "vis knappar for รฅ kopiere info om songen du hรธyrer pรฅ\">/np", + "mt_m3u_c": "vis knapper for รฅ kopiere dei valde$Nsongene som innslag i ei m3u8-speleliste\">๐Ÿ“ป", + "mt_octl": "integrering med operativsystemet (fjernkontroll, infoskjerm)\">os-ctl", + "mt_oseek": "gje lรธyve รฅt spoling med fjernkontroll$N$Nmerk: pรฅ nokon eininger (iPhones) sรฅ vil$Ndette erstatte knappen for neste song\">spoling", + "mt_oscv": "vis albumcover pรฅ infoskjermen\">bilde", + "mt_follow": "bla slik at songen som spelast alltid er synleg\">๐ŸŽฏ", + "mt_compact": "tettpakka spelarpanel\">โŸŽ", + "mt_uncache": "prรธv denne viss ein song ikkje spelar riktig\">oppfrisk", + "mt_mloop": "repetรฉr heile mappa\">๐Ÿ” gjenta", + "mt_mnext": "hopp รฅt neste mappe og fortsett\">๐Ÿ“‚ neste", + "mt_mstop": "stopp avspeling\">โธ stopp", + "mt_cflac": "konvertรฉr flac / wav-filer รฅt {0}\">flac", + "mt_caac": "konvertรฉr aac / m4a-filer รฅt to {0}\">aac", + "mt_coth": "konvertรฉr alt anna (men ikkje mp3) รฅt {0}\">andre", + "mt_c2opus": "det beste valget for alle PCar og Android\">opus", + "mt_c2owa": "opus-weba, for iOS 17.5 og nyare\">owa", + "mt_c2caf": "opus-caf, for iOS 11 รฅt og med 17\">caf", + "mt_c2mp3": "bra valg for steinalder-utstyr (slรฅr aldri feil)\">mp3", + "mt_c2flac": "gir best lydkvalitet, men et nettet ditt\">flac", + "mt_c2wav": "heilt rรฅ lydstrรธm (bruker enda meir data enn flac)\">wav", + "mt_c2ok": "bra valg!", + "mt_c2nd": "ikkje det fรธretrekte valget for din einheit, men funker sikkert greit", + "mt_c2ng": "ser verkelig ikkje ut som enheiten din taklar dette formatet... men ok, vi prรธver", + "mt_xowa": "iOS har fortsatt problem med avspeling av owa-musikk i bakgrunnen. Bruk caf eller mp3 i staden for", + "mt_tint": "nivรฅ av bakgrunnsfarge pรฅ sรธkestripa (0-100),$Ngjer oppdateringer mindre distraherande", + "mt_eq": "aktivรฉr tonekontroll og forsterker;$N$Nboost <code>0</code> = normal volumskala$N$Nwidth <code>1  </code> = normal stereo$Nwidth <code>0.5</code> = 50% blanding venstre-hรธgre$Nwidth <code>0  </code> = mono$N$Nboost <code>-0.8</code> & width <code>10</code> = instrumental :^)$N$Nreduserer รณg daudtid mellom songfiler", + "mt_drc": "aktivรฉr volum-utjevning (dynamic range compressor); vil รณg aktivere tonejustering, sรฅ sett alle EQ-feltene bortsett frรฅ 'width' รฅt 0 viss du ikkje vil ha nokon EQ$N$Nfilteret vil dempe volumet pรฅ alt som er hรธgare enn TRESH dB; for kvar RATIO dB over grensa er det 1dB som treff hรธgtalarane, sรฅ standardverdiane tresh -24 og ratio 12 skal bety at volumet ikkje gjeng hรธgare enn -22 dB, slik at ein trygt kan รธke boost-verdien i equalizeren รฅt rundt 0.8, eller 1.8 kombinert med ATK 0 og RLS 90 (berre mulig i firefox; andre nettlesarar tek ikkje hรธgare RLS enn 1)$N$Nwikipedia forklarar dette mykje betre forresten", + + "mb_play": "lytt", + "mm_hashplay": "spel denne songen?", + "mm_m3u": "trykk <code>Enter/OK</code> for รฅ spele\ntrykk <code>ESC/Avbryt</code> for รฅ redigere", + "mp_breq": "krev firefox 82+, chrome 73+, eller iOS 15+", + "mm_bload": "lastar inn...", + "mm_bconv": "konverterer รฅt {0}, vent litt...", + "mm_opusen": "nettlesaren din skjรธnar ikkje aac / m4a;\nkonvertering รฅt opus er no aktivert", + "mm_playerr": "avspeling feilet: ", + "mm_eabrt": "Avspelingsforespรธrselen blei avbroten", + "mm_enet": "Nettet ditt er ustabilt", + "mm_edec": "Noko er galt med musikkfila", + "mm_esupp": "Nettleseren din skjรธnar ikkje filtypen", + "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_e500": "Avspeling feilet: Rusk i maskineriet, sjekk serverloggen.", + "mm_e5xx": "Avspeling feilet: ", + "mm_nof": "finn ikkje flere songer i nรฆrheita", + "mm_prescan": "Leitar etter neste song...", + "mm_scank": "Fann neste song:", + "mm_uncache": "alle songer vil lastast pรฅ nytt ved neste avspeling", + "mm_hnf": "songen finnast ikkje lenger", + + "im_hnf": "bildet finnast ikkje lenger", + + "f_empty": 'denne mappa er tom', + "f_chide": 'dette vil skjule kolonna ยซ{0}ยป\n\nfana for "andre innstillinger" let deg vise kolonna igjen', + "f_bigtxt": "denne fila er heeile {0} MiB -- vis som tekst?", + "f_bigtxt2": "vil du sjรฅ bunnen av filen i staden for? du vil da รณg sjรฅ nye linjer som blir lagd รฅt pรฅ slutten av filen i sanntid", + "fbd_more": '<div id="blazy">visar <code>{0}</code> av <code>{1}</code> filer; <a href="#" id="bd_more">vis {2}</a> eller <a href="#" id="bd_all">vis alle</a></div>', + "fbd_all": '<div id="blazy">visar <code>{0}</code> av <code>{1}</code> filer; <a href="#" id="bd_all">vis alle</a></div>', + "f_anota": "kun {0} av totalt {1} element blei markert;\nfor รฅ velje alt mรฅ du bla รฅt bunnen av mappa fรธrst", + + "f_dls": 'lenkane i denne mappa er no\nomgjort รฅt nedlastingsknappar', + + "f_partial": "For รฅ laste ned ei fil som enda ikkje er ferdig opplasta, klikk pรฅ filen som har same filnamn som denne, men uten <code>.PARTIAL</code> pรฅ slutten. Da vil serveren passe pรฅ at nedlastinga gรฅr bra. Derfor anbefalast det sterkt รฅ trykkje AVBRYT eller Escape-tasten.\n\nViss du verkelig รธnskjer รฅ laste ned denne <code>.PARTIAL</code>-filen pรฅ ein ukontrollert mรฅte, trykk OK / Enter for รฅ ignorere denne advarselen. Slik vil du hรธgst sannsynleg motta korrupt data.", + + "ft_paste": "Lim inn {0} filer$NSnarvei: ctrl-V", + "fr_eperm": 'kan ikkje endre namn:\ndu har ikkje hรธve รฅt โ€œmoveโ€ i denne mappa', + "fd_eperm": 'kan ikkje slette:\ndu har ikkje hรธve รฅt โ€œdeleteโ€ i denne mappa', + "fc_eperm": 'kan ikkje klippe ut:\ndu har ikkje hรธve รฅt โ€œmoveโ€ i denne mappa', + "fp_eperm": 'kan ikkje lime inn:\ndu har ikkje hรธve รฅt โ€œwriteโ€ i denne mappa', + "fr_emore": "vel minst รฉi fil som skal fรฅ nytt namn", + "fd_emore": "vel minst รฉi fil som skal slettast", + "fc_emore": "vel minst รฉi fil som skal klippast ut", + "fcp_emore": "vel minst รฉi fil som skal kopierast รฅt utklippstavla", + + "fs_sc": "del mappa du er i no", + "fs_ss": "del dei valde filene", + "fs_just1d": "du kan ikkje markere flere mapper samtidig,\neller kombinere mapper og filer", + "fs_abrt": "โŒ avbryt", + "fs_rand": "๐ŸŽฒ tilfeldig namn", + "fs_go": "โœ… opprett deling", + "fs_name": "namn", + "fs_src": "kjelde", + "fs_pwd": "passord", + "fs_exp": "varigheit", + "fs_tmin": "min", + "fs_thrs": "timar", + "fs_tdays": "dagar", + "fs_never": "for evig", + "fs_pname": "valfri namn (blir litt tilfeldig ellers)", + "fs_tsrc": "fil/mappe som skal delast", + "fs_ppwd": "valfri passord", + "fs_w8": "opprettar deling...", + "fs_ok": "trykk <code>Enter/OK</code> for รฅ kopiere lenka (for CTRL-V)\ntrykk <code>ESC/Avbryt</code> for รฅ kun bekrefta", + + "frt_dec": "kan korrigere visse รธdelagte filnamn\">url-decode", + "frt_rst": "nullstillar endringar (tilbake รฅt dei originale filnamna)\">โ†บ reset", + "frt_abrt": "avbryt og lukk dette vindauget\">โŒ avbryt", + "frb_apply": "IVERKSETT", + "fr_adv": "automasjon basert pรฅ metadata<br>og / eller mรธnster (regulรฆre uttrykk)\">avansert", + "fr_case": "versalfรธlsomme uttrykk\">Aa", + "fr_win": "bytt ut bokstavane <code><>:"\\|?*</code> med$Ntilsvarande som windows ikkje fรฅr panikk av\">win", + "fr_slash": "bytt ut bokstaven <code>/</code> slik at den ikkje forรฅrsakar at nye mapper opprettes\">ikke /", + "fr_re": "regex-mรธnster som kรธyrast pรฅ kvart filnamn. Grupper kan leses ut i format-feltet nedanfor, f.eks. <code>(1)</code> og <code>(2)</code> osv.", + "fr_fmt": "inspirert av foobar2000:$N<code>(title)</code> byttast ut med songtittel,$N<code>[(artist) - ](title)</code> dropper [dette] viss artist er blank$N<code>$lpad((tn),2,0)</code> visar songnr. med 2 siffer", + "fr_pdel": "slett", + "fr_pnew": "lagre som", + "fr_pname": "gje innstillingane dine eit namn", + "fr_aborted": "avbrote", + "fr_lold": "gamalt namn", + "fr_lnew": "nytt namn", + "fr_tags": "metadata for dei valde filene (kun for referanse):", + "fr_busy": "endrar namn pรฅ {0} filer...\n\n{1}", + "fr_efail": "endring av namn feila:\n", + "fr_nchg": "{0} av namna blei justert pga. <code>win</code> og/eller <code>ikkje /</code>\n\nvil du fortsetja med dei nye namna som blei valde?", + + "fd_ok": "sletting OK", + "fd_err": "sletting feila:\n", + "fd_none": "ingenting blei sletta; kanskje avvist av serverkonfigurasjon (xbd)?", + "fd_busy": "slettar {0} filer...\n\n{1}", + "fd_warn1": "SLETT disse {0} filene?", + "fd_warn2": "<b>Siste sjanse!</b> Dette kan ikkje angrast. Slett?", + + "fc_ok": "klipte ut {0} filer", + "fc_warn": 'klipte ut {0} filer\n\nmen: kun <b>denne</b> nettlesarfana har muligheit รฅt รฅ lime dei inn ein annan plass, siden antallet filer er helt hinsides', + + "fcc_ok": "kopierte {0} filer รฅt utklippstavla", + "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_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_emore": "det er fortsatt fleire namn som mรฅ endrast", + "fp_ok": "flytting OK", + "fcp_ok": "kopiering OK", + "fp_busy": "flyttar {0} filer...\n\n{1}", + "fcp_busy": "kopierar {0} filer...\n\n{1}", + "fp_abrt": "avbryt...", + "fp_err": "flytting feila:\n", + "fcp_err": "kopiering feila:\n", + "fp_confirm": "flytt disse {0} filene hit?", + "fcp_confirm": "kopiรฉr disse {0} filene hit?", + "fp_etab": 'kunne ikkje lese lista med filer frรฅ den andre nettlesarfana', + "fp_name": "Lastar opp รฉi fil frรฅ einheita di. Velg filnamn:", + "fp_both_m": '<h6>kva skal limast inn her?</h6><code>Enter</code> = Flytt {0} filer frรฅ ยซ{1}ยป\n<code>ESC</code> = Last opp {2} filer frรฅ einheita din', + "fcp_both_m": '<h6>kva skal limes inn her?</h6><code>Enter</code> = Kopiรฉr {0} filer frรฅ ยซ{1}ยป\n<code>ESC</code> = Last opp {2} filer frรฅ einheita din', + "fp_both_b": '<a href="#" id="modal-ok">Flytt</a><a href="#" id="modal-ng">Last opp</a>', + "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", + + "tv_load": "Lastar inn tekstfil:\n\n{0}\n\n{1}% ({2} av {3} MiB lasta ned)", + "tv_xe1": "kunne ikkje laste tekstfil:\n\nfeil ", + "tv_xe2": "404, Fil ikkje funne", + "tv_lst": "tekstfiler i mappa", + "tvt_close": "gรฅ tilbake รฅt mappa$NSnarvei: M (eller Esc)\">โŒ lukk", + "tvt_dl": "last ned denne fila$NSnarvei: Y\">๐Ÿ’พ last ned", + "tvt_prev": "vis fรธrre dokument$NSnarvei: i\">โฌ† forr.", + "tvt_next": "vis neste dokument$NSnarvei: K\">โฌ‡ neste", + "tvt_sel": "markรฉr fila   ( for utklipp / sletting / ... )$NSnarvei: S\">merk", + "tvt_edit": "redigรฉr fila$NSnarvei: E\">โœ๏ธ endre", + "tvt_tail": "overvak fila for endringar og vis nye linjer i sanntid\">๐Ÿ“ก fรธlg", + "tvt_wrap": "tekstbryting\">โ†ต", + "tvt_atail": "hald dei nyaste linjene synlege (lรฅs รฅt botnen av sida)\">โš“", + "tvt_ctail": "skjรธn og vis terminalfargar (ansi-sekvensar)\">๐ŸŒˆ", + "tvt_ntail": "maksgrense for antal bokstavar som skal visast i vindauget", + + "m3u_add1": "songen blei lagd รฅt i m3u-spelelista", + "m3u_addn": "{0} songer blei lagde รฅt i m3u-spelelista", + "m3u_clip": "m3u-spelelista blei kopiert รฅt utklippstavla\n\nneste steg er รฅ oppretta eit tekstdokument med filnamn som sluttar pรฅ <code>.m3u</code> og lime inn spelelista der", + + "gt_vau": "ikkje vis videofiler, berre spel lyden\">๐ŸŽง", + "gt_msel": "markรฉr filer i staden for รฅ รฅpne dei; ctrl-klikk filer for รฅ overstyre$N$N<em>nรฅr aktiv: dobbelklikk ei fil / mappe for รฅ รฅpne</em>$N$NSnarvei: S\">markering", + "gt_crop": "skjer ikona slik at dei passar betre\">โœ‚", + "gt_3x": "hรธgare opplรธysing pรฅ ikon\">3x", + "gt_zoom": "zoom", + "gt_chop": "trim", + "gt_sort": "sortรฉr", + "gt_name": "namn", + "gt_sz": "stรธrr.", + "gt_ts": "dato", + "gt_ext": "type", + "gt_c1": "redusรฉr makslengde pรฅ filnamn", + "gt_c2": "auk makslengde pรฅ filnamn", + + "sm_w8": "sรธker...", + "sm_prev": "sรธkeresultata er frรฅ eit tidlegare sรธk:\n ", + "sl_close": "lukk sรธkeresultat", + "sl_hits": "visar {0} treff", + "sl_moar": "hent fleire", + + "s_sz": "stรธrr.", + "s_dt": "dato", + "s_rd": "sti", + "s_fn": "namn", + "s_ta": "meta", + "s_ua": "up@", + "s_ad": "avns.", + "s_s1": "stรธrre enn โ†“ MiB", + "s_s2": "mindre enn โ†“ MiB", + "s_d1": "nyare enn <dato>", + "s_d2": "eldre enn", + "s_u1": "lasta opp etter", + "s_u2": "og/eller fรธr", + "s_r1": "mappaamn inneheld", + "s_f1": "filnamn inneheld", + "s_t1": "song-info inneheld", + "s_a1": "konkrete eigenskapar", + + "md_eshow": "visar forenkla ", + "md_off": "[๐Ÿ“œ<em>readme</em>] er skrudd av i [โš™๏ธ] -- dokument skjult", + + "badreply": "Ugyldig svar frรฅ serveren", + + "xhr403": "403: Hรธve nekta\n\nkanskje du blei logga ut? prรธv รฅ trykk F5", + "xhr0": "ukjend (enten nettverksproblem eller serverkrรฆsj)", + "cf_ok": "om orsak -- liten tilfeldig kontroll, alt OK\n\nting skal fortsetja om ca. 30 sekund\n\nviss ikkje noko skjer, trykk F5 for รฅ laste sida pรฅ nytt", + "tl_xe1": "kunne ikkje hente undermapper:\n\nfeil ", + "tl_xe2": "404: Mappa finnast ikkje", + "fl_xe1": "kunne ikkje hente filer i mappa:\n\nfeil ", + "fl_xe2": "404: Mappa finnast ikkje", + "fd_xe1": "kan ikkje opprette ny mappe:\n\nfeil ", + "fd_xe2": "404: Den overordna mappa finnast ikkje", + "fsm_xe1": "kunne ikkje sende melding:\n\nfeil ", + "fsm_xe2": "404: Den overordna mappa finnast ikkje", + "fu_xe1": "kunne ikkje hente lista med nyleg opplastede filer frรฅ serveren:\n\nfeil ", + "fu_xe2": "404: Fila finnast ikkje??", + + "fz_tar": "ukomprimert gnu-tar arkiv, for linux og mac", + "fz_pax": "ukomprimert pax-tar arkiv, litt treigare", + "fz_targz": "gnu-tar pakket med gzip (nivรฅ 3)$N$NNB: denne er veldig treig;$Nukomprimert tar er betre", + "fz_tarxz": "gnu-tar pakket med xz (nivรฅ 1)$N$NNB: denne er veldig treig;$Nukomprimert tar er betre", + "fz_zip8": "zip med filnamn i utf8 (noko problematisk pรฅ windows 7 og eldre)", + "fz_zipd": "zip med filnamn i cp437, for hรธggamle maskiner", + "fz_zipc": "cp437 med tidlig crc32,$Nfor MS-DOS PKZIP v2.04g (oktober 1993)$N(รธker behandlingstid pรฅ server)", + + "un_m1": "nedanfor kan du angre / slette filer som du nyleg har lastet opp, eller avbryte ufullstendige opplastinger", + "un_upd": "oppdater", + "un_m4": "eller viss du vil dele nedlastings-lenkene:", + "un_ulist": "vis", + "un_ucopy": "kopiรฉr", + "un_flt": "valgfritt filter:  filnamn / filsti mรฅ inneholde", + "un_fclr": "nullstill filter", + "un_derr": 'unpost-sletting feilet:\n', + "un_f5": 'noko gjekk galt, prรธv รฅ oppdatere lista eller trykk F5', + "un_uf5": "om orsak, men du mรฅ laste sida pรฅ nytt (f.eks. ved รฅ trykkje F5 eller CTRL-R) fรธr denne opplastinga kan avbrytast", + "un_nou": '<b>advarsel:</b> kan ikkje vise ufullstendige opplastingar akkurat no; klikk pรฅ oppdater-lenka om litt', + "un_noc": '<b>advarsel:</b> angring av fullfรธrte opplastingar er deaktivert i serverkonfigurasjonen', + "un_max": "visar dei fรธrste 2000 filene (bruk filteret for รฅ snevre inn)", + "un_avail": "{0} nyleg opplasta filer kan slettast<br />{1} ufullstendige opplastingar kan avbrytast", + "un_m2": "sortert etter opplastingstid; nyaste fรธrst:", + "un_no1": "men nei, her var det jaggu ikkje noko som slettast kan", + "un_no2": "men nei, her var det jaggu ingenting som passa overens med filteret", + "un_next": "slett dei neste {0} filene nedanfor", + "un_abrt": "avbryt", + "un_del": "slett", + "un_m3": "hentar lista med nyleg opplasta filer...", + "un_busy": "slettar {0} filer...", + "un_clip": "{0} lenkar kopiert รฅt utklippstavla", + + "u_https1": "du burde", + "u_https2": "bytte รฅt https", + "u_https3": "for hรธgare hastigheit", + "u_ancient": 'nettlesaren din er prehistorisk -- mulig du burde <a href="#" onclick="goto(\'bup\')">bruke bup i staden for</a>', + "u_nowork": "krev firefox 53+, chrome 57+, eller iOS 11+", + "tail_2old": "krev firefox 105+, chrome 71+, eller iOS 14.5+", + "u_nodrop": 'nettlesaren din er for gamal รฅt รฅ laste opp filer ved รฅ drage dei inn i vindauget', + "u_notdir": "mottok ikkje mappa!\n\nnettlesaren din er for gamal,\nprรธv รฅ drage mappa inn i vindauget i staden for", + "u_uri": "for รฅ laste opp bilder frรฅ andre nettlesarvindauge,\nslipp bildet rett pรฅ den store last-opp-knappen", + "u_enpot": 'bytt รฅt <a href="#">enkelt UI</a> (gir sannsynleg raskere opplasting)', + "u_depot": 'bytt รฅt <a href="#">snรฆsent UI</a> (gir sannsynleg treigare opplasting)', + "u_gotpot": 'bytta รฅt eit enklare UI for รฅ laste opp raskere,\n\ndu kan gjerne bytte tilbake altsรฅ!', + "u_pott": "<p>filer:   <b>{0}</b> ferdig,   <b>{1}</b> feilet,   <b>{2}</b> behandlast,   <b>{3}</b> i kรธ</p>", + "u_ever": "dette er den primitive opplastaren; up2k krev minst:<br>chrome 21 // firefox 13 // edge 12 // opera 12 // safari 5.1", + "u_su2k": 'dette er den primitive opplastaren; <a href="#" id="u2yea">up2k</a> er betre', + "u_uput": 'litt raskare (uten sha512)', + "u_ewrite": 'du har ikkje hรธve til รฅ skrive i denne mappa', + "u_eread": 'du har ikkje hรธve til รฅ lese i denne mappa', + "u_enoi": 'filsรธk er deaktivert i serverkonfigurasjonen', + "u_enoow": "kan ikkje overskrive filer her (Delete-rettigheiten er nรธdvendig)", + "u_badf": 'Disse {0} filene (av totalt {1}) kan ikkje leses, kanskje pga rettigheitsproblem i filsystemet pรฅ datamaskinen din:\n\n', + "u_blankf": 'Disse {0} filene (av totalt {1}) er blanke / uten innhald; รธnskjer du รฅ laste dei opp uansett?\n\n', + "u_applef": 'Disse {0} filene (av totalt {1}) er antakeleg uรธnska;\nTrykk <code>OK/Enter</code> for รฅ HOPPE OVER disse filene,\nTrykk <code>Avbryt/ESC</code> for รฅ LASTE OPP disse filene รณg:\n\n', + "u_just1": '\nFunkar kanskje betre viss du berre tar รฉi fil om gangen', + "u_ff_many": 'Viss du bruker <b>Linux / MacOS / Android,</b> sรฅ kan dette antalet filer<br /><a href="https://bugzilla.mozilla.org/show_bug.cgi?id=1790500" target="_blank"><em>kanskje</em> krรฆsje Firefox!</a> Viss det skjer, sรฅ prรธv igjen (eller bruk Chrome).', + "u_up_life": "Filene slettast frรฅ serveren {0}\netter at opplastingen er fullfรธrt", + "u_asku": 'Laste opp disse {0} filene รฅt <code>{1}</code>', + "u_unpt": "Du kan angre / slette opplastinga med ๐Ÿงฏ oppe รฅt venstre", + "u_bigtab": 'Vil no vise {0} filer...\n\nDette kan krรฆsje nettlesaren din. Fortsette?', + "u_scan": 'Les mappane...', + "u_dirstuck": 'Nettleseren din fekk ikkje hรธve รฅt รฅ lese fรธlgande {0} filer/mapper, sรฅ dei blir hoppa over:', + "u_etadone": 'Ferdig ({0}, {1} filer)', + "u_etaprep": '(forberedar opplasting)', + "u_hashdone": 'synfaring ferdig', + "u_hashing": 'les', + "u_hs": 'serveren tenkjer...', + "u_started": "filene blir no lasta opp ๐Ÿš€", + "u_dupdefer": "duplikat; vil bli hรฅndtert รฅt slutt", + "u_actx": "klikk her for รฅ forhindre tap av<br />yting ved bytte รฅt andre vindauge/faner", + "u_fixed": "OK!  Lรธyste seg ๐Ÿ‘", + "u_cuerr": "kunne ikkje laste opp del {0} av {1};\nsikkert greit, fortsetjar\n\nfil: {2}", + "u_cuerr2": "server nekta opplastinga (del {0} av {1});\nprรธver igjen senere\n\nfil: {2}\n\nerror ", + "u_ehstmp": "prรธver igjen; se mld nederst", + "u_ehsfin": "server nekta forespรธrselen om รฅ ferdigstille filen; prรธver igjen...", + "u_ehssrch": "server nekta forespรธrselen om รฅ utfรธre sรธk; prรธver igjen...", + "u_ehsinit": "server nekta forespรธrselen om รฅ begynne ei ny opplasting; prรธver igjen...", + "u_eneths": "eit problem med nettverket gjorde at avtale om opplasting ikkje kunne inngรฅs; prรธver igjen...", + "u_enethd": "eit problem med nettverket gjorde at filsjekk ikkje kunne utfรธrast; prรธver igjen...", + "u_cbusy": "ventar pรฅ klarering frรฅ server etter eit lite nettverksglipp...", + "u_ehsdf": "serveren er full!\n\nprรธver igjen regelmessig,\ni tilfelle nokon ryddar litt...", + "u_emtleak1": "uff, det er mulig at nettlesaren din har ei minnelekkasje...\nForeslรฅr", + "u_emtleak2": ' helst at du <a href="{0}">byttar รฅt https</a>, eller ', + "u_emtleak3": ' at du ', + "u_emtleakc": 'prรธver fรธlgande:\n<ul><li>trykk F5 for รฅ laste sida pรฅ nytt</li><li>sรฅ skru av  <code>mt</code>  brytaren under  <code>โš™๏ธ innstillinger</code></li><li>og prรธv den same opplastinga igjen</li></ul>Opplasting vil gรฅ litt treigare, men det fรฅr sรฅ vere.\nBeklager bryderiet!\n\nPS: feilen <a href="https://bugs.chromium.org/p/chromium/issues/detail?id=1354816" target="_blank">skal vere fikset</a> i chrome v107', + "u_emtleakf": 'prรธver fรธlgende:\n<ul><li>trykk F5 for รฅ laste sida pรฅ nytt</li><li>sรฅ skru pรฅ <code>๐Ÿฅ”</code> ("enkelt UI") i opplastaren</li><li>og prรธv den same opplastingen igjen</li></ul>\nPS: Firefox <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=1790500" target="_blank">fiksar forhรฅpentligvis feilen</a> ein eller annen gong', + "u_s404": "ikkje funne pรฅ serveren", + "u_expl": "forklar", + "u_maxconn": "dei fleste nettlesarar tillet ikkje meir enn 6, men firefox lar deg รธke grensen med <code>connections-per-server</code> i <code>about:config</code>", + "u_tu": '<p class="warn">ADVARSEL: turbo er pรฅ, <span> avbrotne opplastingar vil muligens ikkje oppdagast og gjenopptakast; hald musepeikaren over turbo-knappen for meir info</span></p>', + "u_ts": '<p class="warn">ADVARSEL: turbo er pรฅ, <span> sรธkeresultat kan vere feil; hold musepeikaren over turbo-knappen for meir info</span></p>', + "u_turbo_c": "turbo er deaktivert i serverkonfigurasjonen", + "u_turbo_g": 'turbo blei deaktivert fordi du ikkje har\nhรธve รฅt รฅ sjรฅ mappeinnhold i dette volumet', + "u_life_cfg": 'slett opplasting etter <input id="lifem" p="60" /> min (eller <input id="lifeh" p="3600" /> timar)', + "u_life_est": 'opplastingen slettast <span id="lifew" tt="lokal tid">---</span>', + "u_life_max": 'denne mappa tillet ikkje รฅ \noppbevare filer i meir enn {0}', + "u_unp_ok": 'opplasting kan angrast i {0}', + "u_unp_ng": 'opplasting kan IKKE angrast', + "ue_ro": 'du har ikkje hรธve รฅt skriving i denne mappa\n\n', + "ue_nl": 'du er ikkje logga inn', + "ue_la": 'du er logga inn som "{0}"', + "ue_sr": 'du er i filsรธk-modus\n\nbytt รฅt opplasting ved รฅ klikke pรฅ forstรธrringsglaset ๐Ÿ”Ž (ved siden av den store FILSร˜K-knappen) og prรธv igjen\n\nsorry', + "ue_ta": 'prรธv รฅ last opp igjen, det burde fungere no', + "ue_ab": "den same filen er under opplasting รฅt ei anna mappe, og den mรฅ fullfรธrast der fรธr fila kan lastast opp andre plassar.\n\nDu kan avbryte og glรธyme den pรฅbegynte opplastinga ved hjelp av ๐Ÿงฏ oppe รฅt venstre", + "ur_1uo": "OK: Fila blei lastet opp", + "ur_auo": "OK: Alle {0} filene blei lastet opp", + "ur_1so": "OK: Fila blei funne pรฅ serveren", + "ur_aso": "OK: Alle {0} filene blei funne pรฅ serveren", + "ur_1un": "Opplasting feila!", + "ur_aun": "Alle {0} opplastingene gjekk feil!", + "ur_1sn": "Fila finnast IKKE pรฅ serveren", + "ur_asn": "Fann INGEN av dei {0} filene pรฅ serveren", + "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", + + "lang_set": "passar det รฅ laste sida pรฅ nytt?", + }, + "pol": { + "tt": "Polski", + "cols": { + "c": "przyciski akcji", + "dur": "czas trwania", + "q": "jakoล›ฤ‡ / bitrate", + "Ac": "kodek audio", + "Vc": "kodek wideo", + "Fmt": "format / kontener", + "Ahash": "suma kontrolna audio", + "Vhash": "suma kontrolna wideo", + "Res": "rozdzielczoล›ฤ‡", + "T": "rodzaj pliku", + "aq": "jakoล›ฤ‡ / bitrate audio", + "vq": "jakoล›ฤ‡ / bitrate wideo", + "pixfmt": "podprรณbkowanie / struktura pikseli", + "resw": "rozdzielczoล›ฤ‡ pozioma", + "resh": "rozdzielczoล›ฤ‡ pionowa", + "chs": "kanaล‚y audio", + "hz": "czฤ™stotliwoล›ฤ‡ prรณbkowania" + }, + + "hks": [ + [ + "misc", + ["ESC", "zamknij rรณลผne rzeczy"], + + "file-manager", + ["G", "przeล‚ฤ…cz widok lista / siatka"], + ["T", "przeล‚ฤ…cz miniaturki / ikony"], + ["โ‡ง A/D", "wielkoล›ฤ‡ miniaturki"], + ["ctrl-K", "usuล„ zaznaczone"], + ["ctrl-X", "wytnij zaznaczone do schowka"], + ["ctrl-C", "skopiuj zaznaczone do schowka"], + ["ctrl-V", "wklej (przenieล›/skopiuj) tutaj"], + ["Y", "pobierz zaznaczone"], + ["F2", "zmieล„ nazwฤ™ zaznaczonych"], + + "file-list-sel", + ["spacja", "przeล‚ฤ…cz zaznaczanie plikรณw"], + ["โ†‘/โ†“", "przenieล› kursor zaznaczenia"], + ["ctrl โ†‘/โ†“", "przenieล› kursor i widok"], + ["โ‡ง โ†‘/โ†“", "wybierz poprzedni/nastฤ™pny plik"], + ["ctrl-A", "wybierz wszystkie pliki/foldery"], + ], [ + "navigation", + ["B", "przeล‚ฤ…cz ล›cieลผkฤ™ nawigacyjnฤ… / panel nawigacyjny"], + ["I/K", "poprzedni/nastฤ™pny folder"], + ["M", "folder nadrzฤ™dny (lub zwiล„ aktualny)"], + ["V", "przeล‚ฤ…cz foldery / pliki tekstowe w panelu nawigacyjnym"], + ["A/D", "rozmiar panelu nawigacyjnego"], + ], [ + "audio-player", + ["J/L", "poprzedni/nastฤ™pny utwรณr"], + ["U/O", "przejdลบ 10 sek. do tyล‚u/przodu"], + ["0..9", "przeskocz do 0%..90%"], + ["P", "odtwรณrz/pauza (rรณwnieลผ rozpoczyna)"], + ["S", "wybierz odtwarzany utwรณr"], + ["Y", "pobierz utwรณr"], + ], [ + "image-viewer", + ["J/L, โ†/โ†’", "poprzednie/nastฤ™pne zdjฤ™cie"], + ["Home/End", "pierwsze/ostatnie zdjฤ™cie"], + ["F", "peล‚ny ekran"], + ["R", "obrรณฤ‡ zgodnie ze wskaz. zegara"], + ["โ‡ง R", "obrรณฤ‡ przeciwnie do ruchu wskaz. zegara"], + ["S", "wybierz zdjฤ™cie"], + ["Y", "pobierz zdjฤ™cie"], + ], [ + "video-player", + ["U/O", "przejdลบ 10 sek. do tyล‚u/przodu"], + ["P/K/Spacja", "odtwรณrz/pauza"], + ["C", "odtwarzaj nastฤ™pne po zakoล„czeniu"], + ["V", "odtwarzaj w pฤ™tli"], + ["M", "wycisz"], + ["[ i ]", "ustaw opรณลบnienie pฤ™tli"], + ], [ + "textfile-viewer", + ["I/K", "poprzedni/nastฤ™pny plik"], + ["M", "zamknij plik"], + ["E", "edytuj plik"], + ["S", "wybierz plik (do wyciฤ™cia/skopiowania/zmiany nazwy)"], + ] + ], + + "m_ok": "OK", + "m_ng": "Anuluj", + + "enable": "Wล‚ฤ…cz", + "danger": "NIEBEZPIECZEลƒSTWO", + "clipped": "skopiowano do schowka", + + "ht_s1": "sekunda", + "ht_s2": "sekundy", + "ht_s5": "sekund", + "ht_m1": "minuta", + "ht_m2": "minuty", + "ht_m5": "minut", + "ht_h1": "godzina", + "ht_h2": "godziny", + "ht_h5": "godzin", + "ht_d1": "dzieล„", + "ht_d2": "dni", + "ht_and": " i ", + + "goh": "panel sterowania", + "gop": 'poprzedni plik/folder">poprzedni', + "gou": 'nadrzฤ™dny folder">w gรณrฤ™', + "gon": 'nastฤ™pny folder">nastฤ™pny', + "logout": "Wyloguj ", + "login": "Zaloguj siฤ™", //m + "access": " dostฤ™p", + "ot_close": "zamknij pod-menu", + "ot_search": "szukaj plikรณw po atrybutach, ล›cieลผce / nazwie, tagach muzyki, bฤ…dลบ dowolnej ich kombinacji$N$N<code>foo bar</code> = musi zawieraฤ‡ ยซfooยป oraz ยซbarยป,$N<code>foo -bar</code> = musi zawieraฤ‡ ยซfooยป, lecz nie ยซbarยป,$N<code>^yana .opus$</code> = musi zaczynaฤ‡ siฤ™ od ยซyanaยป i byฤ‡ plikiem ยซopusยป$N<code>"try unite"</code> = zawieraฤ‡ dokล‚adnie ยซtry uniteยป$N$Nformatem daty jest iso-8601, czyli$N<code>2009-12-31</code> lub <code>2020-09-12 23:30:00</code>", + "ot_unpost": "unpost: usuล„ ostatnio przesล‚ane pliki lub przerwij przesyล‚anie", + "ot_bup": "bup: podstawowe przesyล‚anie danych, wspiera nawet netscape 4.0", + "ot_mkdir": "mkdir: tworzy nowy folder", + "ot_md": "new-md: tworzy nowy dokument markdown", + "ot_msg": "msg: wysyล‚a wiadomoล›ฤ‡ do loga serwera", + "ot_mp": "opcje odtwarzacza multimediรณw", + "ot_cfg": "opcje konfiguracji", + "ot_u2i": 'up2k: przesyล‚a pliki (jeลผeli masz dostฤ™p do zapisu) lub uruchomia tryb wyszukiwania, aby sprawdziฤ‡ czy juลผ istniejฤ… na serwerze$N$Nprzesyล‚anie moลผna wznowiฤ‡, jest wielowฤ…tkowe i znaczniki czasu sฤ… zachowywane, lecz zuลผywa wiฤ™cej procesora niลผ [๐ŸŽˆ]  (podstawowe przesyล‚anie)<br /><br />podczas przesyล‚ania ta ikona zamienia siฤ™ w wskaลบnik postฤ™pu!', + "ot_u2w": 'up2k: przesyล‚a pliki z moลผliwoล›ciฤ… wznowienia (moลผna zamknฤ…ฤ‡ przeglฤ…darkฤ™ i dokoล„czyฤ‡ przesyล‚anie plikรณw pรณลบniej)$N$Njest wielowฤ…tkowy i zachowuje znaczniki czasu plikรณw, lecz zuลผywa wiฤ™cej procesora od [๐ŸŽˆ]  (podstawowego przesyล‚ania)<br /><br />podczas przesyล‚ania ta ikona zamienia siฤ™ w wskaลบnik postฤ™pu!', + "ot_noie": 'Uลผyj przeglฤ…darki Chrome / Firefox / Edge', + + "ab_mkdir": "stwรณrz folder", + "ab_mkdoc": "stwรณrz dok. markdown", + "ab_msg": "wyล›lij wiad. do logรณw serwera", + + "ay_path": "przejdลบ do folderรณw", + "ay_files": "przejdลบ do plikรณw", + + "wt_ren": "zmieล„ nazwฤ™ zaznaczonych elementรณw$NSkrรณt: F2", + "wt_del": "usuล„ zaznaczone elementy$NSkrรณt: ctrl-K", + "wt_cut": "wytnij zaznaczone elementy <small>(aby wkleiฤ‡ gdzie indziej)</small>$NSkrรณt: ctrl-X", + "wt_cpy": "skopiuj zaznaczone pliki do schowka$N(aby wkleiฤ‡ gdzie indziej)$NSkrรณt: ctrl-C", + "wt_pst": "wklej wczeล›niej wyciฤ™te/skopiowane zaznaczenie$NSkrรณt: ctrl-V", + "wt_selall": "zaznacz wszystko$NHotkey: ctrl-A (when file focused)", + "wt_selinv": "odwrรณฤ‡ zaznaczenie", + "wt_zip1": "pobierz folder jako archiwum", + "wt_selzip": "pobierz zaznaczone jako archiwum", + "wt_seldl": "pobierz zaznaczenie jako oddzielne pliki$NSkrรณt: Y", + "wt_npirc": "skopiuj informacje o utworze w formacie irc", + "wt_nptxt": "skopiuj informacje o utworze jako zwykล‚y tekst", + "wt_m3ua": "dodaj to playlisty m3u (kliknij <code>๐Ÿ“ปcopy</code> kliknij)", + "wt_m3uc": "skopiuj playlistฤ™ m3u do schowka", + "wt_grid": "przeล‚ฤ…cz widok siatki / listy$NSkrรณt: G", + "wt_prev": "poprzeni utwรณr$NSkrรณt: J", + "wt_play": "odtwรณrz / pauza$NSkrรณt: P", + "wt_next": "nastฤ™pny utwรณr$NSkrรณt: L", + + "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_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", + "ut_srch": "nie przesyล‚aj plikรณw, jedynie sprawdลบ czy istniejฤ…$Njuลผ na serwerze (przeskanuje wszystkie foldery dostฤ™pne do odczytu)", + "ut_par": "zatrzymuje przesyล‚anie jeล›li wynosi 0$N$Nzwiฤ™ksz w przypadku jeล›li twoja sieฤ‡ jest wolna / ma duลผe opรณลบnienia$N$Nustaw wartoล›ฤ‡ 1 w sieci lokalnej lub w przypadku wolnego dysku serwerowego", + "ul_btn": "upuล›ฤ‡ pliki / foldery<br>tutaj (lub kliknij mnie)", + "ul_btnu": "P R Z E ลš L I J", + "ul_btns": "S Z U K A J", + + "ul_hash": "hashowanie", + "ul_send": "przesyล‚anie", + "ul_done": "gotowe", + "ul_idle1": "nic siฤ™ jeszcze nie przesyล‚a", + "ut_etah": "ล›rednia prฤ™dkoล›ฤ‡ <em>hashowania</em> i przewidywany czas do koล„ca", + "ut_etau": "ล›rednia prฤ™dkoล›ฤ‡ <em>przesyล‚ania</em> i przewidywany czas do koล„ca", + "ut_etat": "ล›rednia prฤ™dkoล›ฤ‡ <em>ogรณlna</em> i przewidywany czas do koล„ca", + + "uct_ok": "zakoล„czone pomyล›lnie", + "uct_ng": "zakoล„czono niepowodzeniem (odrzucono, nie znaleziono, itp.)", + "uct_done": "zakoล„czono z bล‚ฤ™dami", + "uct_bz": "w trakcie (oblicznie sumy kontrolnej, przesyล‚anie)", + "uct_q": "oczekujฤ…ce", + + "utl_name": "nazwa pliku", + "utl_ulist": "lista", + "utl_ucopy": "kopia", + "utl_links": "linki", + "utl_stat": "status", + "utl_prog": "postฤ™p", + + // keep short: + "utl_404": "404", + "utl_err": "Bลฤ„D", + "utl_oserr": "bล‚ฤ…d OS", + "utl_found": "znaleziono", + "utl_defer": "opรณลบnij", + "utl_yolo": "YOLO", + "utl_done": "gotowe", + + "ul_flagblk": "pliki zostaล‚y zakolejkowane,</b><br>lecz przesyล‚anie up2k juลผ trwa (w innej zakล‚adce),<br>oczekujฤ™ na zakoล„czenie", + "ul_btnlk": "przeล‚ฤ…cznik zablokowany przez konfiguracjฤ™ serwera", + + "udt_up": "Przeล›lij", + "udt_srch": "Szukaj", + "udt_drop": "upuล›ฤ‡ tutaj", + + "u_nav_m": '<h6>co my tu mamy?</h6><code>Enter</code> = Pliki (jeden lub wiecej)\n<code>ESC</code> = Jeden folder (wล‚ฤ…cznie z podfolderami)', + "u_nav_b": '<a href="#" id="modal-ok">Pliki</a><a href="#" id="modal-ng">Jeden folder</a>', + + "cl_opts": "przeล‚ฤ…czniki", + "cl_themes": "motyw", + "cl_langs": "jฤ™zyk", + "cl_ziptype": "pobieranie folderรณw", + "cl_uopts": "przeล‚ฤ…czniki przesyล‚ania (up2k)", + "cl_favico": "favicon (ikona w przeglฤ…darce)", + "cl_bigdir": "duลผe foldery", + "cl_hsort": "#sortowanie", + "cl_keytype": "notacja klucza", // not sure + "cl_hiddenc": "ukryte kolumny", + "cl_hidec": "ukryj", + "cl_reset": "zresetuj", + "cl_hpick": "kliknij nagล‚รณwki kolumn, aby ukryฤ‡ je w tabeli niลผej", + "cl_hcancel": "ukrywanie kolumn przerwane", + + "ct_grid": '็”ฐ siatka', + "ct_ttips": 'โ—” โ—ก โ—”">โ„น๏ธ podpowiedzi', + "ct_thumb": 'w widoku siatki, przeล‚ฤ…cz ikony i miniaturki$NSkrรณt: T">๐Ÿ–ผ๏ธ miniaturki', + "ct_csel": 'uลผyj CTRL i SHIFT do wybierania plikรณw w widoku siatki">wybierz', + "ct_ihop": 'przejdลบ do ostatniego pliku po zamkniฤ™ciu przeglฤ…darki obrazรณw">gโฎฏ', + "ct_dots": 'pokaลผ ukryte pliki (jeล›li pozwala serwer)">ukryte', + "ct_qdel": 'pytaj o potwierdzenie przy usuwaniu tylko raz">pyt. us.', + "ct_dir1st": 'pokazuj foldery na poczฤ…tku">๐Ÿ“ najpierw', + "ct_nsort": 'naturalne sortowanie (dla numerowanych plikรณw)">nsort', + "ct_utc": 'pokaลผ wszystkie daty/czas w UTC">UTC', + "ct_readme": 'pokazuj README.md w folderach">๐Ÿ“œ readme', + "ct_idxh": 'pokazuj plik index.html zamiast zawartoล›ci folderu">htm', + "ct_sbars": 'pokazuj paski przewijania">โŸŠ', + + "cut_umod": "uaktualnij znacznik ostatniej modyfikacji pliku, tak aby pasowaล‚ do pliku lokalnego jeลผeli plik juลผ istnieje na serwerze (wymaga dostฤ™pu zapisu i usuwania)\">๐Ÿ“… ponownie", + + "cut_turbo": "przycisk โ€žraz siฤ™ ลผyjeโ€, raczej NIE POWINIENEลš tego wล‚ฤ…czaฤ‡:$N$Nuลผywaj jeล›li przesyล‚ano ogromnฤ… liczbฤ™ plikรณw i z jakiegoล› powodu musisz przesล‚aฤ‡ pliki ponownie, kontynuujฤ…c jak najszybciej$N$Nzamienia sprawdzanie sumy kontrolnej plikรณw prostym <em>"czy ten plik jest tego samego rozmiaru jak ten na serwerze?"</em> wiฤ™c jeล›li pliki rรณลผniฤ… siฤ™ zawartoล›ciฤ…, ale sฤ… tego samego rozmiaru, NIE ZOSTANฤ„ przesล‚ane ponownie$N$Nta opcja powinna zostaฤ‡ wyล‚ฤ…czona po zakoล„czeniu przesyล‚ania, i potem "przesล‚aฤ‡" te same pliki ponownie w celu weryfikacji\">turbo", + + "cut_datechk": "przy wyล‚ฤ…czonym przycisku turbo nic nie robi$N$Nleciutko zmniejsza czynnik โ€žraz siฤ™ ลผyjeโ€; dodatkowo sprawdza czy znaczniki modyfikacji pliku przesyล‚anego zgadzajฤ… siฤ™ z serwerem$N$N<em>teorytycznie</em> powinno zล‚apaฤ‡ to wiฤ™kszoล›ฤ‡ niedokoล„czonych / uszkodzonych plikรณw, lecz nie jest zamiennikiem wykonania ponownego sprawdzenia bez wล‚ฤ…czonego trybu turbo\">spr-daty", + + "cut_u2sz": "rozmiar (w MiB) kaลผdego kawaล‚ka do przesล‚ania; wiฤ™ksze wartoล›ci szybciej latajฤ… po Atlantyku. Mniejsze wartoล›ci dziaล‚ajฤ… lepiej na bardzo niestabilnych poล‚ฤ…czeniach (neostrada?)", + + "cut_flag": "zapewnia, ลผe tylko jedna karta przesyล‚a dane w danym momencie$N -- opcja musi byฤ‡ wล‚ฤ…czona na innych kartach $N - dotyczy tylko kart w tej samej domenie", + + "cut_az": "przesyล‚a pliki w kolejnoล›ci alfabetycznej, zamiast rozpoczฤ…ฤ‡ od najmniejszego pliku$N$Nkolejnoล›ฤ‡ alfabetyczna moลผe uล‚atwiฤ‡ oszacowanie, co mogล‚o pรณjล›ฤ‡ nie tak na serwerze, lecz lekko spowalnia przesyล‚anie po ล›wiatล‚owodzie lub w sieci lokalnej", + + "cut_nag": "powiadomienie systemowe po zakoล„czeniu przesyล‚ania$N(tylko jeล›li przeglฤ…darka lub karta nie jest aktywna)", + "cut_sfx": "sygnaล‚ dลบwiฤ™kowy po zakoล„czeniu przesyล‚ania$N(tylko jeล›li przeklฤ…darka lub karta nie jest aktywna)", + + "cut_mt": "uลผywaj wielowฤ…tkowoล›ci, aby przyspieszyฤ‡ obliczanie sumy kontrolnej plikรณw$N$Nuลผywa web workerรณw i wymaga$Nwiฤ™cej pamiฤ™ci RAM (do 512 MiB)$N$Nprzyspiesza https o 30% i http 4,5-krotnie\">ww", + + "cut_wasm": "uลผywaj WASM zamiast wbudowanego hashera przeglฤ…darki; zwiฤ™ksza prฤ™dkoล›ฤ‡ na Chrome'o-pochodnych przeglฤ…darkach, zwiฤ™kszajฤ…c zuลผycie procesora, ponadto wiele starszych wersji Chrome'a zawiera bล‚ฤ™dy powodujฤ…ce zeลผarcie caล‚ej pamiฤ™ci RAM komputera i przymusowe zamkniฤ™cie przeglฤ…darki jeลผeli ta opcja jest wล‚ฤ…czona\">wasm", + + "cft_text": "tekst favicon (aby wyล‚ฤ…czyฤ‡, usuล„ zawartoล›ฤ‡ i przeล‚aduj stronฤ™)", + "cft_fg": "kolor tekstu", + "cft_bg": "kolor tล‚a", + + "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", + + "tt_entree": "pokaลผ panel nawigacyjny (panel boczny z drzewem folderรณw)$NSkrรณt: B", + "tt_detree": "pokaลผ ล›lad nawigacyjny$NSkrรณt: B", + "tt_visdir": "przewiล„ do wybranego folderu", + "tt_ftree": "przeล‚ฤ…cz drzewo folderรณw / pliki tekstowe$NSkrรณt: V", + "tt_pdock": "pokaลผ foldery nadrzฤ™dne w przypiฤ™tym u gรณry panelu", + "tt_dynt": "rozszerzaj panel wraz z drzewem", + "tt_wrap": "zawijaj tekst", + "tt_hover": "pokazuj za dล‚ugie linie po najechaniu kursorem$N( psuje przewijanie gdy $N  kursor nie jest w lewym marginesie )", + + "ml_pmode": "na koล„cu folderu...", + "ml_btns": "komendy", + "ml_tcode": "transkoduj", + "ml_tcode2": "transkoduj do", + "ml_tint": "odcieล„", + "ml_eq": "korektor dลบwiฤ™ku (equalizer)", + "ml_drc": "kompresor zasiฤ™gu dynamiki", + + "mt_loop": "pฤ™tla/powtarzaj jeden utwรณr\">๐Ÿ”", + "mt_one": "zatrzymaj po jednym utworze\">1๏ธโƒฃ", + "mt_shuf": "odtwarzaj losowo w kaลผdym folderze\">๐Ÿ”€", + "mt_aplay": "autoodtwarzanie po klikniฤ™ciu linku do tego serwera, zawierajฤ…cego identyfikator utworu$N$Nwyล‚ฤ…czenie tej opcji zapobiegnie aktualizowaniu adresu strony podczas odtwarzania muzyki, aby zapobiec autoodtwarzaniu przy utracie ustawieล„\">aโ–ถ", + "mt_preload": "rozpocznij ล‚adowanie kolejnego utworu blisko koล„ca aktualnego w celu uzyskania odtwarzania bez przerw\">preload", + "mt_prescan": "przechodzi do nastฤ™pnego folderu przed zakoล„czeniem ostatniego utworu,$Naby udobruchaฤ‡ przeglฤ…darkฤ™,$Nลผeby nie zatrzymaล‚a odtwarzania\">naw", + "mt_fullpre": "prรณbuj zbuforowaฤ‡ caล‚y utwรณr;$Nโœ… wล‚ฤ…cz na <b>niestabilnych</b> poล‚ฤ…czeniach,$NโŒ <b>wyล‚ฤ…cz</b> na wolnych poล‚ฤ…czeniach\">peล‚nebuf", + "mt_fau": "nie zatrzymuj muzyki jeล›li nastฤ™pna piosenka bฤ™dzie siฤ™ zbyt wolno buforowaฤ‡ na telefonach (moลผe sprawiฤ‡, ลผe tagi bฤ™dฤ… siฤ™ niepoprawnie wyล›wietlaฤ‡)\">โ˜•๏ธ", + "mt_waves": "falisty pasek:$Npokazuj amplitudฤ™ dลบwiฤ™ku w pasku utworu\">~s", + "mt_npclip": "pokaลผ przyciski kopiowania aktualnie odtwarzanego utworu\">/np", + "mt_m3u_c": "pokaลผ przyciski kopiowania$Nwybranych piosenek jako playlista m3u8\">๐Ÿ“ป", + "mt_octl": "integracja z systemem operacyjnym (przyciski multimedialne / informacje o utworze)\">os-int", + "mt_oseek": "zezwรณl na przewijanie utworu poprzez integracjฤ™ z systemem$N$Nuwaga: na niektรณrych urzฤ…dzeniach (iPhone'y),$Nzamienia przycisk nastฤ™pnej piosenki\">seek", + "mt_oscv": "pokaลผ okล‚adkฤ™ albumu w widoku systemu\">okล‚adka", + "mt_follow": "podฤ…ลผaj za odtwarzanym utworem przewijajฤ…c widok\">๐ŸŽฏ", + "mt_compact": "kompaktowe sterowanie\">โŸŽ", + "mt_uncache": "wyczyล›ฤ‡ pamiฤ™ฤ‡ podrฤ™cznฤ…  (sprรณbuj jeล›li przeglฤ…darka$Nzachowaล‚a zepsutฤ… kopiฤ™ utworu, przez co nie odtwarza siฤ™ ona)\">uncache", + "mt_mloop": "odtwarzaj utwory w folderze w pฤ™tli\">๐Ÿ” loop", + "mt_mnext": "wczytaj nastฤ™pny folder i kontynuuj\">๐Ÿ“‚ next", + "mt_mstop": "zatrzymaj odtwarzanie\">โธ stop", + "mt_cflac": "przekonwertuj format flac / wav na {0}\">flac", + "mt_caac": "przekonwertuj format aac / m4a na {0}\">aac", + "mt_coth": "przekonwertuj wszystkie inne formaty (nie bฤ™dฤ…ce mp3) na {0}\">oth", + "mt_c2opus": "najlepszy wybรณr dla komputerรณw, laptopรณw i urzฤ…dzeล„ z androidem\">opus", + "mt_c2owa": "opus-weba, dla iOS 17.5 i nowszych\">owa", + "mt_c2caf": "opus-caf, dla iOS 11 do 17\">caf", + "mt_c2mp3": "uลผywaj na bardzo starych urzฤ…dzeniach\">mp3", + "mt_c2ok": "cudownie, dobry wybรณr", + "mt_c2nd": "ten format nie jest rekomendowany dla twojego urzฤ…dzenia, ale nadal jest w porzฤ…dku", + "mt_c2ng": "wyglฤ…da na to, ลผe to urzฤ…dzenie nie wspiera tego formatu, lecz sprรณbujmy i tak", + "mt_xowa": "iOS zawiera bล‚ฤ™dy uniemoลผliwiajฤ…ce odtwarzanie w tle uลผywajฤ…c tego formatu; wybierz caf lub mp3", + "mt_tint": "jasnoล›ฤ‡ tล‚a (0-100) paska,$Naby zmniejszyฤ‡ widocznoล›ฤ‡ buforowania", + "mt_eq": "wล‚ฤ…cza korektor dลบwiฤ™ku (equalizer) i kontrolฤ™ wzmocnienia dลบwiฤ™ku;$N$Nboost <code>0</code> = standardowa gล‚oล›noล›ฤ‡ 100% (niezmodyfikowana)$N$Nwidth <code>1  </code> = standardowe stereo (niezmodyfikowane)$Nwidth <code>0.5</code> = 50% crossfeed lewo-prawo$Nwidth <code>0  </code> = mono$N$Nboost <code>-0.8</code> & width <code>10</code> = usuwanie wokalu :^)$N$Nwล‚ฤ…czenie korektora sprawia, ลผe albumy bezprzerwowe sฤ… w peล‚ni bez przerw, wiฤ™c jeล›li jest to dla ciebie waลผne, zostaw wszystko na 0 (poza width = 1)", + "mt_drc": "wล‚ฤ…cza kompresor zakresu dynamiki (normalizacja gล‚oล›noล›ci); wล‚ฤ…cza rรณwnieลผ korektor w celu zbalansowania tego spaghetti, wiฤ™c ustaw wszystkie opcje korektora, oprรณcz 'width',na 0, jeล›li go nie chcesz$N$Nobniลผa gล‚oล›noล›ฤ‡ audio nad THRESHOLD (prรณg) dB; dla kaลผdego RATIO (wspรณล‚czynnika) dB, bฤ™dฤ…cego ponad THRESHOLDem jest 1 dB wyjล›cia, wiฤ™c domyล›lne wartoล›ci progu -24 i wspรณล‚czynnika 12 znaczฤ…, ลผe nigdy nie powinno byฤ‡ gล‚oล›niej niลผ -22 dB i bezpieczne jest zwiฤ™kszenie wzmocnienia korektora do 0.8, lub nawet 1.8 z ATK 0 i ogromnym RLS, jak 90 (dziaล‚a tylko na firefoxie, inne przeglฤ…darki majฤ… limit RLS 1)$N$N(na wikipedii tล‚umaczฤ… to duลผo lepiej)", + + "mb_play": "odtwรณrz", + "mm_hashplay": "odtworzyฤ‡ ten plik audio?", + "mm_m3u": "naciล›nij <code>Enter/OK</code>, aby odtworzyฤ‡\nnaciล›nij <code>ESC/Anuluj</code>, aby edytowaฤ‡", + "mp_breq": "wymagany jest Firefox 82+, Chrome 73+ lub iOS 15+", + "mm_bload": "wczytywanie...", + "mm_bconv": "konwertowanie do {0}, proszฤ™ czekaฤ‡...", + "mm_opusen": "ta przeglฤ…darka nie moลผe odtwarzaฤ‡ plikรณw aac / m4a;\ntranskodowanie do formatu opus wล‚ฤ…czone", + "mm_playerr": "odtwarzanie nie powiodล‚o siฤ™: ", + "mm_eabrt": "Odtwarzanie zostaล‚o przerwane", + "mm_enet": "Poล‚ฤ…czenie z internetem jest sล‚abe", + "mm_edec": "Ten plik wydaje siฤ™ uszkodzony??", + "mm_esupp": "Twoja przeglฤ…darka nie rozumie tego formatu audio", + "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_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", + "mm_prescan": "Szukanie kolejnego utworu...", + "mm_scank": "Znaleziono nastฤ™pnฤ… piosenkฤ™:", + "mm_uncache": "wyczyszczono pamiฤ™ฤ‡ podrฤ™cznฤ…; wszystkie utwory zostanฤ… pobrane ponownie przy nastฤ™pnym odtworzeniu", + "mm_hnf": "ten utwรณr juลผ nie istnieje", + + "im_hnf": "ten obraz juลผ nie istnieje", + + "f_empty": 'ten folder jest pusty', + "f_chide": 'schowa kolumnฤ™ ยซ{0}ยป\n\nkolumny moลผna ponownie pokazaฤ‡ w zakล‚adce ustwaieล„', + "f_bigtxt": "ten plik waลผy {0} MiB -- na pewno pokazaฤ‡ jako tekst?", + "f_bigtxt2": "odczytaฤ‡ jedynie koniec pliku? wล‚ฤ…czy rรณwnieลผ ล›ledzenie, pokazujฤ…c nowo-dodane linie tekstu w czasie rzeczywistym", + "fbd_more": '<div id="blazy">pokazujฤ™ <code>{0}</code> z <code>{1}</code> plikรณw; <a href="#" id="bd_more">pokaลผ {2}</a> lub <a href="#" id="bd_all">pokaลผ wszystko</a></div>', + "fbd_all": '<div id="blazy">pokazujฤ™ <code>{0}</code> z <code>{1}</code> files; <a href="#" id="bd_all">pokaลผ wszystko</a></div>', + "f_anota": "{0} z {1} elementรณw zostaล‚o wybranych;\naby pokazaฤ‡ caล‚y folder, zjedลบ na dรณล‚", + + "f_dls": 'linki do plikรณw w aktualnym folderze\nzostaล‚y zmienione w linki pobierania', + + "f_partial": "Aby bezpiecznie pobraฤ‡ plik, ktรณry aktualnie jest przesyล‚any, wybierz plik o tej samej nazwie, lecz bez rozszerzenia <code>.PARTIAL</code>. ลปeby to zrobiฤ‡, naciล›nij ANULUJ lub klawisz ESC.\n\nWciล›niฤ™cie OK / Enter zignoruje to ostrzeลผenie i pobierze plik tymczasowy <code>.PARTIAL</code>, ktรณry prawie z pewnoล›ciฤ… bฤ™dzie zepsuty", + + "ft_paste": "wklej {0} elementรณw$NSkrรณt: ctrl-V", + "fr_eperm": 'nie moลผna zmieniฤ‡ nazwy:\nnie posiadasz uprawnienia โ€žmoveโ€ w tym folderze', + "fd_eperm": 'nie moลผna usunฤ…ฤ‡:\nnie posiadasz uprawnienia โ€ždeleteโ€ w tym folderze', + "fc_eperm": 'nie moลผna wyciฤ…ฤ‡:\nnie posiadasz uprawnienia โ€žmoveโ€ w tym folderze', + "fp_eperm": 'nie moลผna wkleiฤ‡:\nnie posiadasz uprawnienia โ€žwriteโ€ w tym folderze', + "fr_emore": "wybierz przynajmniej jeden element do zmiany nazwy", + "fd_emore": "wybierz przynajmniej jeden element do usuniฤ™cia", + "fc_emore": "wybierz przynajmniej jeden element do wyciฤ™cia", + "fcp_emore": "wybierz przynajmniej jeden element do skopiowania", + + "fs_sc": "udostฤ™pnij ten folder", + "fs_ss": "udostฤ™pnij zaznaczone pliki", + "fs_just1d": "nie moลผna wybraฤ‡ wiฤ™cej niลผ jednego folderu,\nani mieszaฤ‡ plikรณw i folderรณw w jednym zaznaczeniu", + "fs_abrt": "โŒ przerwij", + "fs_rand": "๐ŸŽฒ losuj nazwฤ™", + "fs_go": "โœ… stwรณrz udostฤ™pnienie", + "fs_name": "nazwa", + "fs_src": "ลบrรณdล‚o", + "fs_pwd": "hasล‚o", + "fs_exp": "wygaล›niฤ™cie", + "fs_tmin": "min", + "fs_thrs": "godz.", + "fs_tdays": "dni", + "fs_never": "na zawsze", + "fs_pname": "opcjonalna nazwa linku; zostanie wylosowana jeล›li pusta", + "fs_tsrc": "plik lub folder do udostฤ™pnienia", + "fs_ppwd": "hasล‚o (opcjonalnie)", + "fs_w8": "udostฤ™pnianie...", + "fs_ok": "naciล›nij <code>Enter/OK</code>, aby skopiowaฤ‡ do schowka\nnaciล›nij <code>ESC/Anuluj</code>, aby zamknฤ…ฤ‡", + + "frt_dec": "moลผe naprawiฤ‡ niektรณre zepsute nazwy plikรณw\">dekoduj-url", + "frt_rst": "zresetuj zmodyfikowane nazwy plikรณw do oryginalnych\">โ†บ zresetuj", + "frt_abrt": "przerwij i zamknij to okno\">โŒ anuluj", + "frb_apply": "ZASTOSUJ ZMIANฤ˜ NAZWY", + "fr_adv": "zmiana nazwy hurtowa / metadanych / wzorcem\">zaawansowane", + "fr_case": "rozrรณลผnianie wielkoล›ci liter w regex\">wlit", + "fr_win": "nazwy bezpieczne dla systemu Windows; zamienia symbole <code><>:"\\|?*</code> na japoล„skie odpowiedniki\">win", + "fr_slash": "zamienia <code>/</code> symbolem, ktรณry nie tworzy nowych folderรณw\">brak /", + "fr_re": "wzorzec wyszukiwania regex stosowany do oryginalnych nazw plikรณw; do grup przechwytywania moลผna siฤ™ odwoล‚aฤ‡ w polu formatu poniลผej, np. <code>(1)</code> i <code>(2)</code> itd.", + "fr_fmt": "inspirowane programem foobar2000:$N<code>(title)</code> zostaje zamienione na tytuล‚ utworu,$N<code>[(artist) - ](title)</code> pomija [tฤ…] czฤ™ล›ฤ‡ jeล›li pole artysty jest puste$N<code>$lpad((tn),2,0)</code> wyrรณwnuje numer utworu do 2 cyfr (np. 01, 06, 09, 16)", + "fr_pdel": "usuล„", + "fr_pnew": "zapisz jako", + "fr_pname": "podaj nazwฤ™ nowego szablonu", + "fr_aborted": "anulowano", + "fr_lold": "poprzednia nazwa", + "fr_lnew": "nowa nazwa", + "fr_tags": "znaczniki dla wybranych plikรณw (tylko do odczytu, w celach informacyjnych):", + "fr_busy": "zmienianie nazwy {0} plikรณw...\n\n{1}", + "fr_efail": "zmiana nazwy zakoล„czona niepowodzeniem:\n", + "fr_nchg": "{0} nowych nazw zostaล‚o zmienionych przez opcje <code>win</code> i/lub <code>brak /</code>\n\nKontynuowaฤ‡ ze zmienionymi nazwami?", + + "fd_ok": "usuniฤ™to", + "fd_err": "usuwanie zakoล„czone niepowodzeniem:\n", + "fd_none": "nie usuniฤ™to nic; usuniฤ™cie mogล‚o zostaฤ‡ zablokowane przez konfiguracjฤ™ serwera (xbd)?", + "fd_busy": "usuwanie {0} elementรณw...\n\n{1}", + "fd_warn1": "USUNฤ„ฤ† {0} elementรณw?", + "fd_warn2": "<b>OSTATNIA SZANSA!</b> Tej operacji nie da siฤ™ cofnฤ…ฤ‡. Usunฤ…ฤ‡?", + + "fc_ok": "wyciฤ™to {0} elementรณw", + "fc_warn": 'wyciฤ™to {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)', + + "fcc_ok": "skopiowano {0} elementรณw do schowka", + "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_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_emore": "pozostaล‚o jeszcze kilka kolizji nazw plikรณw do poprawy", + "fp_ok": "przeniesiono", + "fcp_ok": "przekopiowano", + "fp_busy": "przenoszenie {0} elementรณw...\n\n{1}", + "fcp_busy": "kopiowanie {0} elementรณw...\n\n{1}", + "fp_abrt": "przerywanie...", //m + "fp_err": "nie udaล‚o siฤ™ przenieล›ฤ‡:\n", + "fcp_err": "nie udaล‚o siฤ™ skopiowaฤ‡:\n", + "fp_confirm": "przenieล›ฤ‡ tutaj {0} elementy(รณw)?", + "fcp_confirm": "skopiowaฤ‡ tutaj {0} elementy(รณw)?", + "fp_etab": 'nie udaล‚o siฤ™ odczytaฤ‡ schowka z innej karty przeglฤ…darki', + "fp_name": "przesyล‚anie pliku z twojego urzฤ…dzenia. Nadaj nazwฤ™:", + "fp_both_m": '<h6>wybierz metodฤ™ wklejenia</h6><code>Enter</code> = Przenieล› {0} pliki(รณw) z ยซ{1}ยป\n<code>ESC</code> = Przeล›lij {2} pliki(รณw) z twojego urzฤ…dzenia', + "fcp_both_m": '<h6>wybierz metodฤ™ wklejenia</h6><code>Enter</code> = Skopiuj {0} pliki(รณw) z ยซ{1}ยป\n<code>ESC</code> = Przeล›lij {2} pliki(รณw) z twojego urzฤ…dzenia', + "fp_both_b": '<a href="#" id="modal-ok">Przenieล›</a><a href="#" id="modal-ng">Przeล›lij</a>', + "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", + + "tv_load": "Wczytywanie pliku tekstowego:\n\n{0}\n\n{1}% (wczytano {2} z {3} MiB)", + "tv_xe1": "nie udaล‚o siฤ™ wczytaฤ‡ pliku:\n\nbล‚ฤ…d ", + "tv_xe2": "404, nie znaleziono pliku", + "tv_lst": "lista plikรณw tekstowych w", + "tvt_close": "powrรณฤ‡ do widoku folderรณw$NSkrรณt: M (lub Esc)\">โŒ zamknij", + "tvt_dl": "pobierz ten plik$NHotkey: Y\">๐Ÿ’พ pobierz", + "tvt_prev": "pokaลผ poprzedni dokument$NSkrรณt: i\">โฌ† poprzedni", + "tvt_next": "pokaลผ nastฤ™pny dokument$NSkrรณt: K\">โฌ‡ nastฤ™pny", + "tvt_sel": "wybierz plik   ( do wyciฤ™cia / skopiowania / usuniฤ™cia / itp. )$NSkrรณt: S\">wyb", + "tvt_edit": "otwรณrz plik w edytorze tekstu$NSkrรณt: E\">โœ๏ธ edytuj", + "tvt_tail": "ล›ledลบ zmiany w pliku; pokazuj nowe linie w czasie rzeczywistym\">๐Ÿ“ก ล›ledลบ", + "tvt_wrap": "zawijaj tekst\">โ†ต", + "tvt_atail": "utrzymuj widok na dole strony\">โš“", + "tvt_ctail": "dekoduj kolory terminala (sekwencje sterujฤ…ce ANSI)\">๐ŸŒˆ", + "tvt_ntail": "limit przewijania (ile bajtรณw tekstu przechowywaฤ‡ w pamiฤ™ci)", + + "m3u_add1": "dodano utwรณr do playlisty m3u", + "m3u_addn": "dodano {0} utwory(รณw) do playlisty m3u", + "m3u_clip": "skopiowano playlistฤ™ m3u do schowka\n\nutwรณrz", + + "gt_vau": "nie pokazuj obrazu, odtwarzaj tylko dลบwiฤ™k\">๐ŸŽง", + "gt_msel": "wybierz pliki; kliknij plik z wciล›niฤ™tym klawiszem CTRL, aby zastฤ…piฤ‡$N$N<em>gdy tryb jest aktywny, kliknij dwukrotnie na plik / folder, ลผeby go otworzyฤ‡</em>$N$NSkrรณt: S\">wybierz wiele", + "gt_crop": "kadruj miniaturki do ล›rodka\">kadruj", + "gt_3x": "miniaturki w wysokiej rozdzielczoล›ci\">3x", + "gt_zoom": "przybliลผ", + "gt_chop": "przytnij", + "gt_sort": "sortuj wedล‚ug", + "gt_name": "nazwa", + "gt_sz": "rozmiar", + "gt_ts": "data", + "gt_ext": "typ", + "gt_c1": "przycinaj wiฤ™kszฤ… czฤ™ล›ฤ‡ nazw plikรณw (pokazuj mniej)", + "gt_c2": "przycinaj mniejszฤ… czฤ™ล›ฤ‡ nazw plikรณw (pokazuj wiฤ™cej)", + + "sm_w8": "wyszukiwanie...", + "sm_prev": "wyniki wyszukiwania poniลผej pochodzฤ… z poprzedniego zapytania:\n ", + "sl_close": "zamknij wyniki wyszukiwania", + "sl_hits": "pokazujฤ™ {0} wyniki(รณw)", + "sl_moar": "pokaลผ wiฤ™cej", + + "s_sz": "rozmiar", + "s_dt": "data", + "s_rd": "ล›cieลผka", + "s_fn": "nazwa", + "s_ta": "znaczniki", + "s_ua": "data przesล‚ania", + "s_ad": "zaawansowane", + "s_s1": "min. rozmiar (MiB)", + "s_s2": "maks. rozmiar (MiB)", + "s_d1": "min. data iso8601", + "s_d2": "maks. data iso8601", + "s_u1": "przesล‚ane po", + "s_u2": "i/lub przed", + "s_r1": "ล›cieลผka zawiera   (oddzielone spacjฤ…)", + "s_f1": "nazwa zawiera   (odwrรณฤ‡ za pomocฤ… -nope)", + "s_t1": "znaczniki zawierajฤ…   (^=start, koniec=$)", + "s_a1": "dokล‚adne wล‚aล›ciwoล›ci metadanych", + + "md_eshow": "nie moลผna wyล›wietliฤ‡ ", + "md_off": "[๐Ÿ“œ<em>readme</em>] wyล‚ฤ…czone w [โš™๏ธ] -- dokument ukryty", + + "badreply": "Nie udaล‚o siฤ™ przeanalizowaฤ‡ odpowiedzi serwera", + + "xhr403": "403: Odmowa dostฤ™pu.\n\nSprรณbuj przeล‚adowaฤ‡ stronฤ™ (F5), moลผliwe, ลผe ciฤ™ wylogowano", + "xhr0": "nieznany (byฤ‡ moลผe utracono poล‚ฤ…czenie z serwerem, lub jest on nieaktywny)", + "cf_ok": "przepraszamy, wล‚ฤ…czyล‚a siฤ™ ochrona przed DD" + wah + "oS\n\nwszystko powinno wrรณciฤ‡ do normy za okoล‚o 30 sekund\n\njeล›li nic siฤ™ nie zmieni, naciล›nij klawisz F5, aby przeล‚adowaฤ‡ stronฤ™", + "tl_xe1": "nie moลผna wyล›wietliฤ‡ podfolderรณw:\n\nbล‚ฤ…d ", + "tl_xe2": "404: Nie znaleziono folderu", + "fl_xe1": "nie moลผna wyล›wietliฤ‡ plikรณw w folderze:\n\nbล‚ฤ…d ", + "fl_xe2": "404: Nie znaleziono folderu", + "fd_xe1": "nie moลผna stworzyฤ‡ podfolderu:\n\nbล‚ฤ…d ", + "fd_xe2": "404: Nie znaleziono folderu nadrzฤ™dnego", + "fsm_xe1": "nie moลผna wysล‚aฤ‡ wiadomoล›ci:\n\nbล‚ฤ…d ", + "fsm_xe2": "404: Nie znaleziono folderu nadrzฤ™dnego", + "fu_xe1": "nie udaล‚o siฤ™ wczytaฤ‡ listy unpost z serwera:\n\nbล‚ฤ…d ", + "fu_xe2": "404: Nie znaleziono pliku??", + + "fz_tar": "nieskompresowane archiwum gnu-tar (linux / mac)", + "fz_pax": "nieskompresowane archiwum tar w formacie pax (wolniejsze)", + "fz_targz": "gnu-tar z kompresjฤ… gzip poziomu 3.,$N$Nzazwyczaj bardzo wolne, uลผywaj nieskompresowanego tar", + "fz_tarxz": "gnu-tar z kompresjฤ… xz poziomu 3.$N$Nzazwyczaj bardzo wolne, uลผywaj nieksompresowanego tar", + "fz_zip8": "zip z nazwami plikรณw UTF-8 (moลผe dziaล‚aฤ‡ nieprawidล‚owo na systemie Windows 7 i starszych)", + "fz_zipd": "zip z nazwami plikรณw cp437, dobre dla bardzo starego oprogramowania", + "fz_zipc": "cp437 z CRC32 obliczonym wczeล›niej,$Ndla MS-DOS PKZIP v2.04g (paลบdziernik 1993)$N(przetwarzanie do pobrania trwa dล‚uลผej)", + + "un_m1": "moลผna usunฤ…ฤ‡ ostatnio przesล‚ane pliki (lub przerwaฤ‡ trwajฤ…ce) poniลผej", + "un_upd": "odล›wieลผ", + "un_m4": "lub udostฤ™pnij pliki widoczne poniลผej:", + "un_ulist": "pokaลผ", + "un_ucopy": "kopiuj", + "un_flt": "filtruj (opcjonalnie):  URL musi zawieraฤ‡", + "un_fclr": "wyczyล›ฤ‡ kryteria filtrowania", + "un_derr": 'nie udaล‚o siฤ™ usunฤ…ฤ‡ unpost:\n', + "un_f5": 'coล› poszล‚o nie tak, sprรณbuj odล›wieลผyฤ‡ lub wciล›nij F5', + "un_uf5": "przed przerwaniem przesyล‚ania trzeba odล›wieลผyฤ‡ stronฤ™ (za pomocฤ… CTRL-R lub F5)", + "un_nou": '<b>ostrzeลผenie:</b> serwer jest aktualnie zbyt obciฤ…ลผony, ลผeby pokazaฤ‡ niedokoล„czone przesล‚ania; kliknij link "odล›wieลผ" za chwilฤ™', + "un_noc": '<b>ostrzeลผenie:</b> unpost w peล‚ni przesล‚anych plikรณw jest wyล‚ฤ…czone/zabronione w konfiguracji serwera', + "un_max": "pokazujฤ™ pierwsze 2000 plikรณw (uลผyj filtrowania)", + "un_avail": "{0} ostatnio przesล‚anych elementรณw moลผe zostaฤ‡ usuniฤ™te<br />{1} niedokoล„czonych moลผna przerwaฤ‡", + "un_m2": "przesortowano po czasie przesล‚ania; najnowsze elementy pierwsze: ", + "un_no1": "cholibka! ลผaden przesล‚any element nie jest wystarczajฤ…co niedawny", + "un_no2": "cholibka! ลผaden przesล‚any element pasujฤ…cy do filtra nie jest wystarczajฤ…co niedawny", + "un_next": "usuล„ nastฤ™pne {0} pliki(รณw) poniลผej", + "un_abrt": "przerwij", + "un_del": "usuล„", + "un_m3": "wczytywanie ostatnio przesล‚anych elementรณw...", + "un_busy": "usuwanie {0} plikรณw...", + "un_clip": "skopiowano {0} linkรณw do schowka", + + "u_https1": "powinieneล› przejล›ฤ‡", + "u_https2": "na HTTPS w celu", + "u_https3": "uzyskania lepszej wydajnoล›ci", + "u_ancient": 'twoja przeglฤ…darka jest niezwykle zabytkowa -- powinieneล› zamiast tego <a href="#" onclick="goto(\'bup\')">uลผyฤ‡ bup</a>', + "u_nowork": "wymaga Firefox 53+, Chrome 57+ lub iOS 11+", + "tail_2old": "wymaga Firefox 105+, Chrome 71+ lub iOS 14.5+", + "u_nodrop": 'ta przeglฤ…darka jest za stara, nie wspiera przesyล‚ania "przeciฤ…gnij i upuล›ฤ‡"', + "u_notdir": "to nie jest folder!\n\nta przeglฤ…darka jest za stara\nsprรณbuj przeciฤ…gnฤ…ฤ‡ i upuล›ciฤ‡", + "u_uri": "aby przeciฤ…gnฤ…ฤ‡ i upuล›ciฤ‡ obrazy z innych okien przeglฤ…darki,\nupuล›ฤ‡ je na duลผy przycisk przesyล‚ania", + "u_enpot": 'przeล‚ฤ…cz na <a href="#">lekki interfejs</a> (moลผe zwiฤ™kszyฤ‡ prฤ™dkoล›ฤ‡ przesyล‚ania)', + "u_depot": 'przeล‚ฤ…cz na <a href="#">ล‚adny interfejs</a> (moลผe zmniejszyฤ‡ prฤ™dkoล›c przesyล‚ania)', + "u_gotpot": 'przeล‚ฤ…czanie na lekki interfejs w celu poprawy prฤ™dkoล›ci przesyล‚ania,\n\nzawsze moลผna przeล‚ฤ…czyฤ‡ siฤ™ na ล‚adny interfejs!', + "u_pott": "<p>pliki:   <b>{0}</b> ukoล„czonych,   <b>{1}</b> nie powiodล‚o siฤ™,   <b>{2}</b> w trakcie,   <b>{3}</b> oczekujฤ…cych</p>", + "u_ever": "podstawowe przesyล‚anie; up2k wymaga minimalnie przeglฤ…darek:<br>Chrome 21 // Firefox 13 // Edge 12 // Opera 12 // Safari 5.1", + "u_su2k": 'podstawowe przesyล‚anie; <a href="#" id="u2yea">up2k</a> jest lepszy', + "u_uput": 'optymalizuj dla prฤ™dkoล›ci (pomijajฤ…c spr. sum kontrolnych)', + "u_ewrite": 'nie masz dostฤ™pu do zapisu (write) w tym folderze', + "u_eread": 'nie masz dostฤ™pu do odczytu (read) tego folderu', + "u_enoi": 'wyszukiwanie plikรณw jest wyล‚ฤ…czone w konfiguracji serwera', + "u_enoow": "nadpisanie nie zadziaล‚a, wymagany dostฤ™p do usuwania (delete)", + "u_badf": '{0} (z {1}) plikรณw zostaล‚o pominiฤ™tych, prawdopodobnie przez opcje dostฤ™pu systemu plikรณw:\n\n', + "u_blankf": '{0} (z {1}) plikรณw jest pustych; przesล‚aฤ‡ mimo to?\n\n', + "u_applef": '{0} (z {1}) plikรณw moลผe byฤ‡ niepoลผฤ…dane;\nNaciล›nij <code>OK/Enter</code>, aby pominฤ…ฤ‡ je (wypisane poniลผej);\nNaciล›nij <code>Anuluj/ESC</code>, by je przesล‚aฤ‡ mimo to:\n\n', + "u_just1": '\nTa funkcja moลผe dziaล‚aฤ‡ lepiej z wybranym jednym plikiem', + "u_ff_many": "na systemach <b>Linux / MacOS / Android,</b> ta iloล›c plikรณw <a href=\"https://bugzilla.mozilla.org/show_bug.cgi?id=1790500\" target=\"_blank\"><em>moลผe</em> spowodowaฤ‡ przymusowe zamkniฤ™cie przeglฤ…darki Firefox</a>\nw takim przypadku, sprรณbuj ponownie (lub uลผyj Chrome'a).", + "u_up_life": "Ten przesyล‚any plik zostanie usuniฤ™ty z serwera\n{0} po zakoล„czeniu przesyล‚ania", + "u_asku": 'przeล›lij {0} pliki(รณw) do <code>{1}</code>', + "u_unpt": "moลผna cofnฤ…ฤ‡ / usunฤ…ฤ‡ ten przesล‚any plik za pomocฤ… ๐Ÿงฏ w lewym gรณrnym rogu", + "u_bigtab": 'zaraz pokaลผฤ™ {0} plikรณw\n\nta operacja moลผe zawiesiฤ‡ twojฤ… przeglฤ…darkฤ™, na pewno kontynuowaฤ‡?', + "u_scan": 'Skanowanie plikรณw...', + "u_dirstuck": 'iterator katalogรณw utknฤ…ล‚ podczas prรณby dostฤ™pu poniลผszych {0} elementรณw, pominiฤ™to:', + "u_etadone": 'Ukoล„czono ({0}, {1} plikรณw)', + "u_etaprep": '(przygotowywanie do przesล‚ania)', + "u_hashdone": 'obliczono sumฤ™ kontrolnฤ…', + "u_hashing": 'obliczanie sumy kontrolnej', + "u_hs": 'nawiฤ…zywanie poล‚ฤ…czenia...', + "u_started": "rozpoczฤ™to przesyล‚anie; zobacz w [๐Ÿš€]", + "u_dupdefer": "duplikat; zostanie przetworzony na koล„cu", + "u_actx": "kliknij ten napis, aby zapobiec spadkowi <br />wydajnoล›ci po zmianie aktywnego okna/karty przeglฤ…darki", + "u_fixed": "OK!  Naprawiono ๐Ÿ‘", + "u_cuerr": "nie udaล‚o siฤ™ przesล‚aฤ‡ fragmentu {0} z {1};\nprawdopodobnie niegroลบne, kontynuowanie\n\nplik: {2}", + "u_cuerr2": "serwer odrzuciล‚ przesyล‚anie (kawaล‚ek {0} z {1});\nsprรณbujฤ™ ponownie pรณลบniej\n\nplik: {2}\n\nbล‚ฤ…d ", + "u_ehstmp": "sprรณbujฤ™ ponownie; wiฤ™cej informacji w prawym dolnym rogu", + "u_ehsfin": "serwer odrzuciล‚ proล›bฤ™ o zakoล„czenie przesyล‚ania; prรณbujฤ™ ponownie...", + "u_ehssrch": "serwer odrzuciล‚ proล›bฤ™ o wykonanie wyszukania; prรณbujฤ™ ponownie...", + "u_ehsinit": "serwer odrzuciล‚ proล›bฤ™ o rozpoczฤ™cie przesyล‚ania; prรณbujฤ™ ponownie...", + "u_eneths": "bล‚ฤ…d sieci podczas negocjacji warunkรณw przesyล‚ania; prรณbujฤ™ ponownie...", + "u_enethd": "bล‚ฤ…d sieci podczas sprawdzania istnienia celu; prรณbujฤ™ ponownie...", + "u_cbusy": "oczekiwanie na ponowne zaufanie serwera po bล‚ฤ™dzie sieci...", + "u_ehsdf": "brak miejsca na dysku serwera!\n\nprรณby bฤ™dฤ… ponawiane na wypadek\nzwolnienia wystarczajฤ…co duลผo miejsca aby kontynuowaฤ‡", + "u_emtleak1": "wyglฤ…da na to, ลผe twoja przeglฤ…darka moลผe mieฤ‡ wyciek pamiฤ™ci;\n", + "u_emtleak2": ' <a href="{0}">przejdลบ na HTTPS (zalecane)</a> lub ', + "u_emtleak3": ' ', + "u_emtleakc": 'sprรณbuj:\n<ul><li>wciล›nij <code>F5</code>, aby odล›wieลผyฤ‡ stronฤ™</li><li>wyล‚ฤ…cz przycisk  <code>ww</code>  w menu <code>โš™๏ธ ustawienia</code></li><li>i sprรณbuj przesล‚aฤ‡ ponownie</li></ul>Prฤ™dkoล›ฤ‡ przesyล‚ania bฤ™dzie niลผsza, ale cรณลผ zrobisz.\nPrzepraszamy za problemu!\n\nPS: Chrome v107 <a href="https://bugs.chromium.org/p/chromium/issues/detail?id=1354816" target="_blank">ma poprawkฤ™ tego bล‚ฤ™du</a>.', + "u_emtleakf": 'sprรณbuj:\n<ul><li>wciล›nij <code>F5</code>, aby odล›wieลผyฤ‡ stronฤ™</li><li>wล‚ฤ…cz tryb <code>๐Ÿฅ”</code> (lekkiego interfejsu) w interfejsie przesyล‚ania<li>i sprรณbuj przesล‚aฤ‡ ponownie</li></ul>\nPS: Firefox moลผe kiedyล› mieฤ‡ <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=1790500" target="_blank">poprawkฤ™ย tego bล‚ฤ™du</a>', + "u_s404": "nie znaleziono na serwerze", + "u_expl": "wytล‚umacz", + "u_maxconn": "wiฤ™kszoล›ฤ‡ przeglฤ…darek ogranicza to do 6, ale Firefox pozwala zwiฤ™kszyฤ‡ tฤ… wartoล›ฤ‡, ustawiajฤ…c <code>connections-per-server</code> w <code>about:config</code>", + "u_tu": '<p class="warn">UWAGA: tryb turbo wล‚ฤ…czony, <span> klient moลผe nie wykryฤ‡ i nie kontynuowaฤ‡ niedokoล„czonych przesล‚aล„; patrz wskazรณwka przycisku turbo</span></p>', + "u_ts": '<p class="warn">UWAGA: tryb turbo wล‚ฤ…czony, <span> wyniki wyszukiwania mogฤ… byฤ‡ niepoprawne; patrz wskazรณwka przycisku turbo</span></p>', + "u_turbo_c": "tryb turbo jest wyล‚ฤ…czony w konfiguracji serwera", + "u_turbo_g": "wyล‚ฤ…czanie trybu turbo, nie posiadasz dostฤ™pu\ndo listy katalogu w tym wolumenie", + "u_life_cfg": 'autousuwanie po <input id="lifem" p="60" /> min (lub <input id="lifeh" p="3600" /> godz.)', + "u_life_est": 'przesล‚any plik zostanie usuniฤ™ty <span id="lifew" tt="local time">---</span>', + "u_life_max": 'ten folder wymaga\nmaks. czasu do usuniฤ™cia rรณwnego {0}', + "u_unp_ok": 'unpost jest dozwolony przez {0}', + "u_unp_ng": 'unpost NIE jest dozwolony', + "ue_ro": 'dostฤ™p tylko-do-odczytu\n\n', + "ue_nl": 'nie jesteล› zalogowany', + "ue_la": 'zalogowano jako "{0}"', + "ue_sr": 'jesteล› w trybie wyszukiwania\n\nprzeล‚ฤ…cz siฤ™ na tryb przesyล‚ania, klikajฤ…c lupฤ™ ๐Ÿ”Ž (obok przycisku Szukaj), i sprรณbuj ponownie', + "ue_ta": 'sprรณbuj przesล‚aฤ‡ ponownie, wszystko powinno byฤ‡ w porzฤ…dku', + "ue_ab": "ten plik juลผ jest przesyล‚any do innego folderu, przesyล‚anie musi siฤ™ zakoล„czyฤ‡, zanim bฤ™dzie mรณgล‚ byฤ‡ on przesล‚any gdzie indziej.\n\nMoลผna przerwaฤ‡ pierwsze przesyล‚anie za pomocฤ… ๐Ÿงฏ w lewym gรณrnym rogu", + "ur_1uo": "OK: Plik przesล‚any pomyล›lnie", + "ur_auo": "OK: Wszystkie ({0}) pliki zostaล‚y przesล‚ane pomyล›lnie", + "ur_1so": "OK: Znaleziono plik na serwerze", + "ur_aso": "OK: Znaleziono wszystkie ({0}) pliki na serwerze", + "ur_1un": "Przesyล‚anie nie powiodล‚o siฤ™", + "ur_aun": "Wszystkie ({0}) przesล‚ania nie powiodล‚y siฤ™", + "ur_1sn": "NIE znaleziono pliku na serwerze", + "ur_asn": "NIE znaleziono {0} plikรณw na serwerze", + "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", + + "lang_set": "odล›wieลผ stronฤ™ (F5), aby zastosowaฤ‡ zmianฤ™.", + }, + "por": { + "tt": "Portuguรชs", + + "cols": { + "c": "botรตes de aรงรฃo", + "dur": "duraรงรฃo", + "q": "qualidade / bitrate", + "Ac": "codec de รกudio", + "Vc": "codec de vรญdeo", + "Fmt": "formato / contรชiner", + "Ahash": "checksum de รกudio", + "Vhash": "checksum de vรญdeo", + "Res": "resoluรงรฃo", + "T": "tipo de arquivo", + "aq": "qualidade / bitrate de รกudio", + "vq": "qualidade / bitrate de vรญdeo", + "pixfmt": "subamostragem / estrutura de pixel", + "resw": "resoluรงรฃo horizontal", + "resh": "resoluรงรฃo vertical", + "chs": "canais de รกudio", + "hz": "taxa de amostragem" + }, + + "hks": [ + [ + "diversos", + ["ESC", "fechar vรกrias coisas"], + + "gerenciador de arquivos", + ["G", "alternar entre visualizaรงรฃo de lista / grade"], + ["T", "alternar entre miniaturas / รญcones"], + ["โ‡ง A/D", "tamanho da miniatura"], + ["ctrl-K", "excluir selecionados"], + ["ctrl-X", "recortar seleรงรฃo para a รกrea de transferรชncia"], + ["ctrl-C", "copiar seleรงรฃo para a รกrea de transferรชncia"], + ["ctrl-V", "colar (mover/copiar) aqui"], + ["Y", "baixar selecionado"], + ["F2", "renomear selecionado"], + + "seleรงรฃo de lista de arquivos", + ["espaรงo", "alternar seleรงรฃo de arquivo"], + ["โ†‘/โ†“", "mover cursor de seleรงรฃo"], + ["ctrl โ†‘/โ†“", "mover cursor e visualizaรงรฃo"], + ["โ‡ง โ†‘/โ†“", "selecionar arquivo anterior/prรณximo"], + ["ctrl-A", "selecionar todos os arquivos / pastas"], + ], [ + "navegaรงรฃo", + ["B", "alternar entre breadcrumbs / painel de navegaรงรฃo"], + ["I/K", "pasta anterior/prรณxima"], + ["M", "pasta pai (ou desexpandir a atual)"], + ["V", "alternar entre pastas / arquivos de texto no painel de navegaรงรฃo"], + ["A/D", "tamanho do painel de navegaรงรฃo"], + ], [ + "reprodutor de รกudio", + ["J/L", "mรบsica anterior/prรณxima"], + ["U/O", "pular 10 segundos para trรกs/frente"], + ["0..9", "pular para 0%..90%"], + ["P", "reproduzir/pausar (tambรฉm inicia)"], + ["S", "selecionar a mรบsica que estรก tocando"], + ["Y", "baixar mรบsica"], + ], [ + "visualizador de imagens", + ["J/L, โ†/โ†’", "imagem anterior/prรณxima"], + ["Home/End", "primeira/รบltima imagem"], + ["F", "tela cheia"], + ["R", "girar no sentido horรกrio"], + ["โ‡ง R", "girar no sentido anti-horรกrio"], + ["S", "selecionar imagem"], + ["Y", "baixar imagem"], + ], [ + "reprodutor de vรญdeo", + ["U/O", "pular 10 segundos para trรกs/frente"], + ["P/K/Espaรงo", "reproduzir/pausar"], + ["C", "continuar reproduzindo o prรณximo"], + ["V", "loop"], + ["M", "mudo"], + ["[ e ]", "definir intervalo de loop"], + ], [ + "visualizador de arquivos de texto", + ["I/K", "arquivo anterior/prรณximo"], + ["M", "fechar arquivo de texto"], + ["E", "editar arquivo de texto"], + ["S", "selecionar arquivo (para recortar/copiar/renomear)"], + ] + ], + + "m_ok": "OK", + "m_ng": "Cancelar", + + "enable": "Ativar", + "danger": "PERIGO", + "clipped": "copiado para a รกrea de transferรชncia", + + "ht_s1": "segundo", + "ht_s2": "segundos", + "ht_m1": "minuto", + "ht_m2": "minutos", + "ht_h1": "hora", + "ht_h2": "horas", + "ht_d1": "dia", + "ht_d2": "dias", + "ht_and": " e ", + + "goh": "painel de controle", + "gop": 'pai anterior">anterior', + "gou": 'pasta pai">acima', + "gon": 'prรณxima pasta">prรณximo', + "logout": "Sair ", + "login": "Fazer login", + "access": " acesso", + "ot_close": "fechar submenu", + "ot_search": "procurar arquivos por atributos, caminho / nome, tags de mรบsica ou qualquer combinaรงรฃo deles$N$N<code>foo bar</code> = deve conter ambos ยซfooยป e ยซbarยป,$N<code>foo -bar</code> = deve conter ยซfooยป mas nรฃo ยซbarยป,$N<code>^yana .opus$</code> = comeรงar com ยซyanaยป e ser um arquivo ยซopusยป$N<code>"try unite"</code> = conter exatamente ยซtry uniteยป$N$No formato de data รฉ iso-8601, como$N<code>2009-12-31</code> ou <code>2020-09-12 23:30:00</code>", + "ot_unpost": "despublicar: excluir seus uploads recentes, ou abortar os que nรฃo foram concluรญdos", + "ot_bup": "bup: uploader bรกsico, atรฉ suporta netscape 4.0", + "ot_mkdir": "mkdir: criar um novo diretรณrio", + "ot_md": "new-md: criar um novo documento markdown", + "ot_msg": "msg: enviar uma mensagem para o log do servidor", + "ot_mp": "opรงรตes do reprodutor de mรญdia", + "ot_cfg": "opรงรตes de configuraรงรฃo", + "ot_u2i": 'up2k: fazer upload de arquivos (se vocรช tiver acesso de escrita) ou alternar para o modo de busca para ver se eles jรก existem em algum lugar no servidor$N$Nuploads sรฃo reiniciรกveis, multithread, e os carimbos de data/hora dos arquivos sรฃo preservados, mas usa mais CPU que [๐ŸŽˆ]  (o uploader bรกsico)<br /><br />durante os uploads, este รญcone se torna um indicador de progresso!', + "ot_u2w": 'up2k: fazer upload de arquivos com suporte a retomada (feche seu navegador e solte os mesmos arquivos mais tarde)$N$Nmultithread, e os carimbos de data/hora dos arquivos sรฃo preservados, mas usa mais CPU que [๐ŸŽˆ]  (o uploader bรกsico)<br /><br />durante os uploads, este รญcone se torna um indicador de progresso!', + "ot_noie": 'Por favor, use Chrome / Firefox / Edge', + + "ab_mkdir": "criar diretรณrio", + "ab_mkdoc": "novo documento markdown", + "ab_msg": "enviar msg para o log do srv", + + "ay_path": "pular para pastas", + "ay_files": "pular para arquivos", + + "wt_ren": "renomear itens selecionados$NHotkey: F2", + "wt_del": "excluir itens selecionados$NHotkey: ctrl-K", + "wt_cut": "recortar itens selecionados <small>(depois colar em outro lugar)</small>$NHotkey: ctrl-X", + "wt_cpy": "copiar itens selecionados para a รกrea de transferรชncia$N(para colรก-los em outro lugar)$NHotkey: ctrl-C", + "wt_pst": "colar uma seleรงรฃo previamente recortada / copiada$NHotkey: ctrl-V", + "wt_selall": "selecionar todos os arquivos$NHotkey: ctrl-A (quando o arquivo estiver em foco)", + "wt_selinv": "inverter seleรงรฃo", + "wt_zip1": "baixar esta pasta como um arquivo compactado", + "wt_selzip": "baixar seleรงรฃo como um arquivo compactado", + "wt_seldl": "baixar seleรงรฃo como arquivos separados$NHotkey: Y", + "wt_npirc": "copiar informaรงรตes da faixa em formato irc", + "wt_nptxt": "copiar informaรงรตes da faixa em texto simples", + "wt_m3ua": "adicionar ร  playlist m3u (clique em <code>๐Ÿ“ปcopiar</code> depois)", + "wt_m3uc": "copiar playlist m3u para a รกrea de transferรชncia", + "wt_grid": "alternar entre visualizaรงรฃo de grade / lista$NHotkey: G", + "wt_prev": "faixa anterior$NHotkey: J", + "wt_play": "reproduzir / pausar$NHotkey: P", + "wt_next": "prรณxima faixa$NHotkey: L", + + "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_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", + "ut_srch": "nรฃo fazer upload, em vez disso verificar se os arquivos jรก$N existem no servidor (irรก escanear todas as pastas que vocรช pode ler)", + "ut_par": "pausar uploads definindo para 0$N$naumentar se sua conexรฃo for lenta / alta latรชncia$N$nmanter em 1 em LAN ou se o HDD do servidor for um gargalo", + "ul_btn": "soltar arquivos / pastas<br>aqui (ou clique em mim)", + "ul_btnu": "U P L O A D", + "ul_btns": "B U S C A R", + + "ul_hash": "hash", + "ul_send": "enviar", + "ul_done": "feito", + "ul_idle1": "nenhum upload estรก na fila ainda", + "ut_etah": "velocidade mรฉdia de <em>hash</em>, e tempo estimado atรฉ o fim", + "ut_etau": "velocidade mรฉdia de <em>upload</em> e tempo estimado atรฉ o fim", + "ut_etat": "velocidade mรฉdia <em>total</em> e tempo estimado atรฉ o fim", + + "uct_ok": "concluรญdo com sucesso", + "uct_ng": "ruim: falhou / rejeitado / nรฃo encontrado", + "uct_done": "ok e ruim combinados", + "uct_bz": "fazendo hash ou upload", + "uct_q": "ocioso, pendente", + + "utl_name": "nome do arquivo", + "utl_ulist": "lista", + "utl_ucopy": "copiar", + "utl_links": "links", + "utl_stat": "status", + "utl_prog": "progresso", + + // mantenha curto: + "utl_404": "404", + "utl_err": "ERRO", + "utl_oserr": "Erro-SO", + "utl_found": "encontrado", + "utl_defer": "adiar", + "utl_yolo": "YOLO", + "utl_done": "feito", + + "ul_flagblk": "os arquivos foram adicionados ร  fila</b><br>no entanto, hรก um up2k ocupado em outra aba do navegador,<br>entรฃo esperando que ele termine primeiro", + "ul_btnlk": "a configuraรงรฃo do servidor bloqueou este interruptor neste estado", + + "udt_up": "Upload", + "udt_srch": "Buscar", + "udt_drop": "solte aqui", + + "u_nav_m": '<h6>certo, o que vocรช tem?</h6><code>Enter</code> = Arquivos (um ou mais)\n<code>ESC</code> = Uma pasta (incluindo subpastas)', + "u_nav_b": '<a href="#" id="modal-ok">Arquivos</a><a href="#" id="modal-ng">Uma pasta</a>', + + "cl_opts": "interruptores", + "cl_themes": "tema", + "cl_langs": "idioma", + "cl_ziptype": "download de pasta", + "cl_uopts": "interruptores up2k", + "cl_favico": "favicon", + "cl_bigdir": "grandes dirs", + "cl_hsort": "#sort", + "cl_keytype": "notaรงรฃo de tecla", + "cl_hiddenc": "colunas ocultas", + "cl_hidec": "ocultar", + "cl_reset": "resetar", + "cl_hpick": "toque nos cabeรงalhos das colunas para ocultรก-los na tabela abaixo", + "cl_hcancel": "ocultar coluna abortado", + + "ct_grid": '็”ฐ a grade', + "ct_ttips": 'โ—” โ—ก โ—”">โ„น๏ธ dicas de ferramentas', + "ct_thumb": 'na visualizaรงรฃo de grade, alternar entre รญcones ou miniaturas$NHotkey: T">๐Ÿ–ผ๏ธ miniaturas', + "ct_csel": 'usar CTRL e SHIFT para seleรงรฃo de arquivo na visualizaรงรฃo de grade">sel', + "ct_ihop": 'quando o visualizador de imagens for fechado, rolar para o รบltimo arquivo visualizado">gโฎฏ', + "ct_dots": 'mostrar arquivos ocultos (se o servidor permitir)">dotfiles', + "ct_qdel": 'ao excluir arquivos, pedir confirmaรงรฃo apenas uma vez">qdel', + "ct_dir1st": 'ordenar pastas antes de arquivos">๐Ÿ“ primeiro', + "ct_nsort": 'ordem natural (para nomes de arquivos com dรญgitos iniciais)">nsort', + "ct_utc": 'mostrar todas as datas/horas em UTC">UTC', + "ct_readme": 'mostrar README.md nas listas de pastas">๐Ÿ“œ readme', + "ct_idxh": 'mostrar index.html em vez de lista de pastas">htm', + "ct_sbars": 'mostrar barras de rolagem">โŸŠ', + + "cut_umod": "se um arquivo jรก existe no servidor, atualizar o carimbo de data/hora de รบltima modificaรงรฃo do servidor para corresponder ao seu arquivo local (requer permissรตes de escrita+exclusรฃo)\">re๐Ÿ“…", + + "cut_turbo": "o botรฃo yolo, vocรช provavelmente NรƒO quer habilitar isso:$N$Nuse isto se vocรช estava fazendo upload de uma enorme quantidade de arquivos e teve que reiniciar por algum motivo, e quer continuar o upload o mais rรกpido possรญvel$N$Nisto substitui a verificaรงรฃo de hash por uma simples <em>"este arquivo tem o mesmo tamanho no servidor?"</em> entรฃo se o conteรบdo do arquivo for diferente ele NรƒO serรก enviado$N$Nvocรช deve desativar isso quando o upload estiver concluรญdo, e entรฃo "enviar" os mesmos arquivos novamente para permitir que o cliente os verifique\">turbo", + + "cut_datechk": "nรฃo tem efeito a menos que o botรฃo turbo esteja ativado$N$Nreduz o fator yolo por uma pequena quantidade; verifica se os carimbos de data/hora dos arquivos no servidor correspondem aos seus$N$ndeve <em>teoricamente</em> pegar a maioria dos uploads incompletos / corrompidos, mas nรฃo รฉ um substituto para fazer uma verificaรงรฃo com o turbo desativado depois\">date-chk", + + "cut_u2sz": "tamanho (em MiB) de cada bloco de upload; valores grandes voam melhor pelo atlรขntico. Tente valores baixos em conexรตes muito nรฃo confiรกveis", + + "cut_flag": "garantir que apenas uma aba esteja fazendo upload por vez $N -- outras abas devem ter isso ativado tambรฉm $N -- sรณ afeta abas no mesmo domรญnio", + + "cut_az": "enviar arquivos em ordem alfabรฉtica, em vez de o menor primeiro$N$na ordem alfabรฉtica pode tornar mais fรกcil de verificar se algo deu errado no servidor, mas torna o upload um pouco mais lento em fibra / LAN", + + "cut_nag": "notificaรงรฃo do SO quando o upload for concluรญdo$N(somente se o navegador ou aba nรฃo estiver ativo)", + "cut_sfx": "alerta audรญvel quando o upload for concluรญdo$N(somente se o navegador ou aba nรฃo estiver ativo)", + + "cut_mt": "usar multithreading para acelerar o hash de arquivos$N$nisto usa web-workers e requer$Nmais RAM (atรฉ 512 MiB extras)$N$ntorna https 30% mais rรกpido, http 4.5x mais rรกpido\">mt", + + "cut_wasm": "usar wasm em vez do hasher embutido do navegador; melhora a velocidade em navegadores baseados em chrome mas aumenta a carga da CPU, e muitas versรตes antigas do chrome tรชm bugs que fazem o navegador consumir toda a RAM e travar se isso for ativado\">wasm", + + "cft_text": "texto do favicon (deixe em branco e atualize para desativar)", + "cft_fg": "cor do primeiro plano", + "cft_bg": "cor do fundo", + + "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", + + "tt_entree": "mostrar painel de navegaรงรฃo (รกrvore de diretรณrios)$NHotkey: B", + "tt_detree": "mostrar breadcrumbs$NHotkey: B", + "tt_visdir": "rolar para a pasta selecionada", + "tt_ftree": "alternar entre รกrvore de pastas / arquivos de texto$NHotkey: V", + "tt_pdock": "mostrar pastas pai em um painel acoplado no topo", + "tt_dynt": "crescer automaticamente ร  medida que a รกrvore se expande", + "tt_wrap": "quebra de linha", + "tt_hover": "revelar linhas transbordando ao passar o mouse$N( quebra a rolagem a menos que o cursor do mouse $N  esteja na margem esquerda )", + + "ml_pmode": "ao final da pasta...", + "ml_btns": "comandos", + "ml_tcode": "transcodificar", + "ml_tcode2": "transcodificar para", + "ml_tint": "matiz", + "ml_eq": "equalizador de รกudio", + "ml_drc": "compressor de faixa dinรขmica", + + "mt_loop": "loop/repetir uma mรบsica\">๐Ÿ”", + "mt_one": "parar depois de uma mรบsica\">1๏ธโƒฃ", + "mt_shuf": "embaralhar as mรบsicas em cada pasta\">๐Ÿ”€", + "mt_aplay": "reproduzir automaticamente se houver um ID de mรบsica no link que vocรช clicou para acessar o servidor$N$ndesativar isso tambรฉm impedirรก que a URL da pรกgina seja atualizada com IDs de mรบsica ao tocar mรบsica, para evitar a reproduรงรฃo automรกtica se essas configuraรงรตes forem perdidas mas a URL permanecer\">aโ–ถ", + "mt_preload": "comeรงar a carregar a prรณxima mรบsica perto do final para uma reproduรงรฃo sem interrupรงรตes\">preload", + "mt_prescan": "ir para a prรณxima pasta antes que a รบltima mรบsica$Ntermine, mantendo o navegador feliz$Npara que ele nรฃo pare a reproduรงรฃo\">nav", + "mt_fullpre": "tentar prรฉ-carregar a mรบsica inteira;$Nโœ… ativar em conexรตes <b>nรฃo confiรกveis</b>,$NโŒ <b>desativar</b> em conexรตes lentas provavelmente\">full", + "mt_fau": "em telefones, evitar que a mรบsica pare se a prรณxima mรบsica nรฃo prรฉ-carregar rรกpido o suficiente (pode fazer as tags aparecerem com falhas)\">โ˜•๏ธ", + "mt_waves": "barra de busca de forma de onda:$Nmostrar amplitude de รกudio no scrubber\">~s", + "mt_npclip": "mostrar botรตes para copiar a mรบsica que estรก tocando para a รกrea de transferรชncia\">/np", + "mt_m3u_c": "mostrar botรตes para copiar as$Nmรบsicas selecionadas como entradas de playlist m3u8 para a รกrea de transferรชncia\">๐Ÿ“ป", + "mt_octl": "integraรงรฃo com o SO (atalhos de mรญdia / osd)\">os-ctl", + "mt_oseek": "permitir busca atravรฉs da integraรงรฃo com o SO$N$nnota: em alguns dispositivos (iPhones),$nisto substitui o botรฃo de prรณxima mรบsica\">seek", + "mt_oscv": "mostrar capa do รกlbum no osd\">art", + "mt_follow": "manter a faixa que estรก tocando rolando ร  vista\">๐ŸŽฏ", + "mt_compact": "controles compactos\">โŸŽ", + "mt_uncache": "limpar cache  (tente isso se seu navegador armazenou em cache$Numa cรณpia quebrada de uma mรบsica e se recusa a tocar)\">uncache", + "mt_mloop": "loop na pasta aberta\">๐Ÿ” loop", + "mt_mnext": "carregar a prรณxima pasta e continuar\">๐Ÿ“‚ prรณximo", + "mt_mstop": "parar reproduรงรฃo\">โธ parar", + "mt_cflac": "converter flac / wav para {0}\">flac", + "mt_caac": "converter aac / m4a para {0}\">aac", + "mt_coth": "converter todos os outros (nรฃo mp3) para {0}\">oth", + "mt_c2opus": "melhor escolha para desktops, laptops, android\">opus", + "mt_c2owa": "opus-weba, para iOS 17.5 e mais recentes\">owa", + "mt_c2caf": "opus-caf, para iOS 11 a 17\">caf", + "mt_c2mp3": "use isso em dispositivos muito antigos\">mp3", + "mt_c2flac": "melhor qualidade de som, mas downloads enormes\">flac", + "mt_c2wav": "reproduรงรฃo nรฃo comprimida (ainda maior)\">wav", + "mt_c2ok": "legal, boa escolha", + "mt_c2nd": "esse nรฃo รฉ o formato de saรญda recomendado para o seu dispositivo, mas tudo bem", + "mt_c2ng": "seu dispositivo nรฃo parece suportar este formato de saรญda, mas vamos tentar mesmo assim", + "mt_xowa": "existem bugs no iOS que impedem a reproduรงรฃo em segundo plano usando este formato; por favor, use caf ou mp3 em vez disso", + "mt_tint": "nรญvel de fundo (0-100) na barra de busca$Npara tornar o buffer menos distrativo", + "mt_eq": "ativa o equalizador e o controle de ganho;$N$nimpulsรฃo <code>0</code> = volume padrรฃo de 100% (nรฃo modificado)$N$nlargura <code>1  </code> = estรฉreo padrรฃo (nรฃo modificado)$nlargura <code>0.5</code> = 50% de crossfeed esquerda-direita$nlargura <code>0  </code> = mono$N$nimpulsรฃo <code>-0.8</code> & largura <code>10</code> = remoรงรฃo de vocal :^)$N$natvar o equalizador torna os รกlbuns sem interrupรงรตes totalmente sem interrupรงรตes, entรฃo deixe-o ligado com todos os valores em zero (exceto largura = 1) se vocรช se importa com isso", + "mt_drc": "ativa o compressor de faixa dinรขmica (nivelador de volume / brickwaller); tambรฉm ativarรก o EQ para equilibrar o spaghetti, entรฃo defina todos os campos EQ exceto 'width' para 0 se vocรช nรฃo quiser$N$nabaixa o volume do รกudio acima do THRESHOLD dB; para cada RATIO dB apรณs o THRESHOLD hรก 1 dB de saรญda, entรฃo os valores padrรฃo de tresh -24 e ratio 12 significam que nunca deve ficar mais alto que -22 dB e รฉ seguro aumentar o impulso do equalizador para 0.8, ou atรฉ 1.8 com ATK 0 e um enorme RLS como 90 (sรณ funciona no firefox; RLS รฉ no mรกximo 1 em outros navegadores)$N$n(veja a wikipedia, eles explicam muito melhor)", + + "mb_play": "reproduzir", + "mm_hashplay": "reproduzir este arquivo de รกudio?", + "mm_m3u": "pressione <code>Enter/OK</code> para Reproduzir\npressione <code>ESC/Cancelar</code> para Editar", + "mp_breq": "precisa do firefox 82+ ou chrome 73+ ou iOS 15+", + "mm_bload": "carregando...", + "mm_bconv": "convertendo para {0}, por favor, espere...", + "mm_opusen": "seu navegador nรฃo pode reproduzir arquivos aac / m4a;\na transcodificaรงรฃo para opus agora estรก ativada", + "mm_playerr": "reproduรงรฃo falhou: ", + "mm_eabrt": "A tentativa de reproduรงรฃo foi cancelada", + "mm_enet": "Sua conexรฃo de internet estรก instรกvel", + "mm_edec": "Este arquivo estรก supostamente corrompido??", + "mm_esupp": "Seu navegador nรฃo entende este formato de รกudio", + "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_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", + "mm_prescan": "Procurando mรบsica para tocar a seguir...", + "mm_scank": "Encontrei a prรณxima mรบsica:", + "mm_uncache": "cache limpo; todas as mรบsicas serรฃo baixadas novamente na prรณxima reproduรงรฃo", + "mm_hnf": "essa mรบsica nรฃo existe mais", + + "im_hnf": "essa imagem nรฃo existe mais", + + "f_empty": 'esta pasta estรก vazia', + "f_chide": 'isso irรก ocultar a coluna ยซ{0}ยป\n\nvocรช pode reexibir as colunas na aba de configuraรงรตes', + "f_bigtxt": "este arquivo tem {0} MiB de tamanho -- realmente ver como texto?", + "f_bigtxt2": "ver apenas o final do arquivo em vez disso? isso tambรฉm ativarรก o acompanhamento/tailing, mostrando linhas de texto recรฉm-adicionadas em tempo real", + "fbd_more": '<div id="blazy">mostrando <code>{0}</code> de <code>{1}</code> arquivos; <a href="#" id="bd_more">mostrar {2}</a> ou <a href="#" id="bd_all">mostrar todos</a></div>', + "fbd_all": '<div id="blazy">mostrando <code>{0}</code> de <code>{1}</code> arquivos; <a href="#" id="bd_all">mostrar todos</a></div>', + "f_anota": "apenas {0} dos {1} itens foram selecionados;\npara selecionar a pasta inteira, primeiro role para o final", + + "f_dls": 'os links de arquivo na pasta atual foram\nalterados para links de download', + + "f_partial": "Para baixar com seguranรงa um arquivo que estรก sendo enviado, por favor, clique no arquivo que tem o mesmo nome, mas sem a extensรฃo <code>.PARTIAL</code>. Por favor, pressione CANCELAR ou Escape para fazer isso.\n\nPressionar OK / Enter irรก ignorar este aviso e continuar baixando o arquivo temporรกrio <code>.PARTIAL</code>, o que quase certamente lhe darรก dados corrompidos.", + + "ft_paste": "colar {0} itens$NHotkey: ctrl-V", + "fr_eperm": 'nรฃo รฉ possรญvel renomear:\nvocรช nรฃo tem permissรฃo de โ€œmoverโ€ nesta pasta', + "fd_eperm": 'nรฃo รฉ possรญvel excluir:\nvocรช nรฃo tem permissรฃo de โ€œexcluirโ€ nesta pasta', + "fc_eperm": 'nรฃo รฉ possรญvel recortar:\nvocรช nรฃo tem permissรฃo de โ€œmoverโ€ nesta pasta', + "fp_eperm": 'nรฃo รฉ possรญvel colar:\nvocรช nรฃo tem permissรฃo de โ€œescreverโ€ nesta pasta', + "fr_emore": "selecione pelo menos um item para renomear", + "fd_emore": "selecione pelo menos um item para excluir", + "fc_emore": "selecione pelo menos um item para recortar", + "fcp_emore": "selecione pelo menos um item para copiar para a รกrea de transferรชncia", + + "fs_sc": "compartilhar a pasta em que vocรช estรก", + "fs_ss": "compartilhar os arquivos selecionados", + "fs_just1d": "vocรช nรฃo pode selecionar mais de uma pasta,\nou misturar arquivos e pastas em uma seleรงรฃo", + "fs_abrt": "โŒ abortar", + "fs_rand": "๐ŸŽฒ nome aleatรณrio", + "fs_go": "โœ… criar compartilhamento", + "fs_name": "nome", + "fs_src": "fonte", + "fs_pwd": "senha", + "fs_exp": "expira", + "fs_tmin": "min", + "fs_thrs": "horas", + "fs_tdays": "dias", + "fs_never": "eterno", + "fs_pname": "nome do link opcional; serรก aleatรณrio se em branco", + "fs_tsrc": "o arquivo ou pasta a ser compartilhado", + "fs_ppwd": "senha opcional", + "fs_w8": "criando compartilhamento...", + "fs_ok": "pressione <code>Enter/OK</code> para Copiar para a รrea de Transferรชncia\npressione <code>ESC/Cancelar</code> para Fechar", + + "frt_dec": "pode consertar alguns casos de nomes de arquivos quebrados\">url-decode", + "frt_rst": "resetar nomes de arquivos modificados de volta para os originais\">โ†บ resetar", + "frt_abrt": "abortar e fechar esta janela\">โŒ cancelar", + "frb_apply": "APLICAR RENOMEAร‡รƒO", + "fr_adv": "renomeaรงรฃo em lote / metadados / padrรฃo\">avanรงado", + "fr_case": "regex sensรญvel a maiรบsculas e minรบsculas\">case", + "fr_win": "nomes seguros para windows; substituir <code><>:"\\|?*</code> por caracteres japoneses de largura total\">win", + "fr_slash": "substituir <code>/</code> por um caractere que nรฃo cause a criaรงรฃo de novas pastas\">no /", + "fr_re": "padrรฃo de busca regex para aplicar aos nomes de arquivos originais; grupos de captura podem ser referenciados no campo de formato abaixo como <code>(1)</code> e <code>(2)</code> e assim por diante", + "fr_fmt": "inspirado por foobar2000:$N<code>(tรญtulo)</code> รฉ substituรญdo pelo tรญtulo da mรบsica,$N<code>[(artista) - ](tรญtulo)</code> pula esta parte se o artista estiver em branco$N<code>$lpad((tn),2,0)</code> preenche o nรบmero da faixa com 2 dรญgitos", + "fr_pdel": "excluir", + "fr_pnew": "salvar como", + "fr_pname": "forneรงa um nome para seu novo preset", + "fr_aborted": "abortado", + "fr_lold": "nome antigo", + "fr_lnew": "novo nome", + "fr_tags": "tags para os arquivos selecionados (somente leitura, apenas para referรชncia):", + "fr_busy": "renomeando {0} itens...\n\n{1}", + "fr_efail": "renomeaรงรฃo falhou:\n", + "fr_nchg": "{0} dos novos nomes foram alterados devido a <code>win</code> e/ou <code>no /</code>\n\nOK para continuar com estes novos nomes alterados?", + + "fd_ok": "exclusรฃo OK", + "fd_err": "exclusรฃo falhou:\n", + "fd_none": "nada foi excluรญdo; talvez bloqueado pela configuraรงรฃo do servidor (xbd)?", + "fd_busy": "excluindo {0} itens...\n\n{1}", + "fd_warn1": "EXCLUIR estes {0} itens?", + "fd_warn2": "<b>รšltima chance!</b> Nรฃo hรก como desfazer. Excluir?", + + "fc_ok": "recortar {0} itens", + "fc_warn": 'recortar {0} itens\n\nmas: apenas <b>esta</b> aba do navegador pode colรก-los\n(jรก que a seleรงรฃo รฉ tรฃo absolutamente massiva)', + + "fcc_ok": "copiado {0} itens para a รกrea de transferรชncia", + "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_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_emore": "ainda hรก algumas colisรตes de nome de arquivo para consertar", + "fp_ok": "movimento OK", + "fcp_ok": "cรณpia OK", + "fp_busy": "movendo {0} itens...\n\n{1}", + "fcp_busy": "copiando {0} itens...\n\n{1}", + "fp_abrt": "abortando...", + "fp_err": "movimento falhou:\n", + "fcp_err": "cรณpia falhou:\n", + "fp_confirm": "mover estes {0} itens para cรก?", + "fcp_confirm": "copiar estes {0} itens para cรก?", + "fp_etab": 'falha ao ler a รกrea de transferรชncia de outra aba do navegador', + "fp_name": "enviando um arquivo do seu dispositivo. Dรช-lhe um nome:", + "fp_both_m": '<h6>escolha o que colar</h6><code>Enter</code> = Mover {0} arquivos de ยซ{1}ยป\n<code>ESC</code> = Enviar {2} arquivos do seu dispositivo', + "fcp_both_m": '<h6>escolha o que colar</h6><code>Enter</code> = Copiar {0} arquivos de ยซ{1}ยป\n<code>ESC</code> = Enviar {2} arquivos do seu dispositivo', + "fp_both_b": '<a href="#" id="modal-ok">Mover</a><a href="#" id="modal-ng">Enviar</a>', + "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", + + "tv_load": "Carregando documento de texto:\n\n{0}\n\n{1}% ({2} de {3} MiB carregados)", + "tv_xe1": "nรฃo foi possรญvel carregar o arquivo de texto:\n\nerro ", + "tv_xe2": "404, arquivo nรฃo encontrado", + "tv_lst": "lista de arquivos de texto em", + "tvt_close": "voltar para a visualizaรงรฃo da pasta$NHotkey: M (ou Esc)\">โŒ fechar", + "tvt_dl": "baixar este arquivo$NHotkey: Y\">๐Ÿ’พ baixar", + "tvt_prev": "mostrar documento anterior$NHotkey: i\">โฌ† anterior", + "tvt_next": "mostrar prรณximo documento$NHotkey: K\">โฌ‡ prรณximo", + "tvt_sel": "selecionar arquivo   ( para recortar / copiar / excluir / ... )$NHotkey: S\">sel", + "tvt_edit": "abrir arquivo no editor de texto$NHotkey: E\">โœ๏ธ editar", + "tvt_tail": "monitorar arquivo para alteraรงรตes; mostrar novas linhas em tempo real\">๐Ÿ“ก seguir", + "tvt_wrap": "quebra de linha\">โ†ต", + "tvt_atail": "fixar rolagem no final da pรกgina\">โš“", + "tvt_ctail": "decodificar cores do terminal (cรณdigos de escape ansi)\">๐ŸŒˆ", + "tvt_ntail": "limite de rolagem para trรกs (quantos bytes de texto manter carregados)", + + "m3u_add1": "mรบsica adicionada ร  playlist m3u", + "m3u_addn": "{0} mรบsicas adicionadas ร  playlist m3u", + "m3u_clip": "playlist m3u agora copiada para a รกrea de transferรชncia\n\nvocรช deve criar um novo arquivo de texto chamado something.m3u e colar a playlist nesse documento; isso a tornarรก reproduzรญvel", + + "gt_vau": "nรฃo mostrar vรญdeos, apenas tocar o รกudio\">๐ŸŽง", + "gt_msel": "ativar seleรงรฃo de arquivo; ctrl-clique em um arquivo para substituir$N$n<em>quando ativo: clique duas vezes em um arquivo / pasta para abri-lo&>t;/em>$N$nHotkey: S\">multisseleรงรฃo", + "gt_crop": "cortar miniaturas ao centro\">cortar", + "gt_3x": "miniaturas de alta resoluรงรฃo\">3x", + "gt_zoom": "zoom", + "gt_chop": "picar", + "gt_sort": "ordenar por", + "gt_name": "nome", + "gt_sz": "tamanho", + "gt_ts": "data", + "gt_ext": "tipo", + "gt_c1": "truncar nomes de arquivos mais (mostrar menos)", + "gt_c2": "truncar nomes de arquivos menos (mostrar mais)", + + "sm_w8": "buscando...", + "sm_prev": "os resultados da busca abaixo sรฃo de uma consulta anterior:\n ", + "sl_close": "fechar resultados da busca", + "sl_hits": "mostrando {0} resultados", + "sl_moar": "carregar mais", + + "s_sz": "tamanho", + "s_dt": "data", + "s_rd": "caminho", + "s_fn": "nome", + "s_ta": "tags", + "s_ua": "up@", + "s_ad": "adv.", + "s_s1": "MiB mรญnimo", + "s_s2": "MiB mรกximo", + "s_d1": "iso8601 min.", + "s_d2": "iso8601 max.", + "s_u1": "enviado depois de", + "s_u2": "e/ou antes de", + "s_r1": "caminho contรฉm   (separado por espaรงo)", + "s_f1": "nome contรฉm   (negar com -nope)", + "s_t1": "tags contรฉm   (^=inรญcio, fim=$)", + "s_a1": "propriedades de metadados especรญficas", + + "md_eshow": "nรฃo รฉ possรญvel renderizar ", + "md_off": "[๐Ÿ“œ<em>readme</em>] desativado em [โš™๏ธ] -- documento oculto", + + "badreply": "Falha ao analisar a resposta do servidor", + + "xhr403": "403: Acesso negado\n\ntente pressionar F5, talvez vocรช tenha saรญdo da conta", + "xhr0": "desconhecido (provavelmente perdeu a conexรฃo com o servidor, ou o servidor estรก offline)", + "cf_ok": "desculpe por isso -- a proteรงรฃo DD" + "oS foi ativada\n\nas coisas devem ser retomadas em cerca de 30 segundos\n\nse nada acontecer, pressione F5 para recarregar a pรกgina", + "tl_xe1": "nรฃo foi possรญvel listar as subpastas:\n\nerro ", + "tl_xe2": "404: Pasta nรฃo encontrada", + "fl_xe1": "nรฃo foi possรญvel listar os arquivos na pasta:\n\nerro ", + "fl_xe2": "404: Pasta nรฃo encontrada", + "fd_xe1": "nรฃo foi possรญvel criar a subpasta:\n\nerro ", + "fd_xe2": "404: Pasta pai nรฃo encontrada", + "fsm_xe1": "nรฃo foi possรญvel enviar a mensagem:\n\nerro ", + "fsm_xe2": "404: Pasta pai nรฃo encontrada", + "fu_xe1": "falha ao carregar a lista de despublicaรงรฃo do servidor:\n\nerro ", + "fu_xe2": "404: Arquivo nรฃo encontrado??", + + "fz_tar": "arquivo gnu-tar nรฃo comprimido (linux / mac)", + "fz_pax": "tar de formato pax nรฃo comprimido (mais lento)", + "fz_targz": "gnu-tar com compressรฃo gzip nรญvel 3$N$nisto รฉ geralmente muito lento, entรฃo$nuse tar nรฃo comprimido em vez disso", + "fz_tarxz": "gnu-tar com compressรฃo xz nรญvel 1$N$nisto รฉ geralmente muito lento, entรฃo$nuse tar nรฃo comprimido em vez disso", + "fz_zip8": "zip com nomes de arquivos utf8 (pode ser instรกvel no windows 7 e mais antigos)", + "fz_zipd": "zip com nomes de arquivos cp437 tradicionais, para software realmente antigo", + "fz_zipc": "cp437 com crc32 calculado antecipadamente,$npara MS-DOS PKZIP v2.04g (outubro de 1993)$n(leva mais tempo para processar antes que o download possa comeรงar)", + + "un_m1": "vocรช pode excluir seus uploads recentes (ou abortar os que nรฃo foram concluรญdos) abaixo", + "un_upd": "atualizar", + "un_m4": "ou compartilhar os arquivos visรญveis abaixo:", + "un_ulist": "mostrar", + "un_ucopy": "copiar", + "un_flt": "filtro opcional:  a URL deve conter", + "un_fclr": "limpar filtro", + "un_derr": 'a exclusรฃo da despublicaรงรฃo falhou:\n', + "un_f5": 'algo quebrou, por favor, tente uma atualizaรงรฃo ou pressione F5', + "un_uf5": "desculpe, mas vocรช tem que atualizar a pรกgina (por exemplo, pressionando F5 ou CTRL-R) antes que este upload possa ser abortado", + "un_nou": "<b>aviso:</b> o servidor estรก muito ocupado para mostrar uploads incompletos; clique no link \"atualizar\" em um momento", + "un_noc": "<b>aviso:</b> a despublicaรงรฃo de arquivos totalmente enviados nรฃo estรก ativada/permitida na configuraรงรฃo do servidor", + "un_max": "mostrando os primeiros 2000 arquivos (use o filtro)", + "un_avail": "{0} uploads recentes podem ser excluรญdos<br />{1} incompletos podem ser abortados", + "un_m2": "ordenado por tempo de upload; o mais recente primeiro:", + "un_no1": "sike! nenhum upload รฉ suficientemente recente", + "un_no2": "sike! nenhum upload que corresponda a esse filtro รฉ suficientemente recente", + "un_next": "excluir os prรณximos {0} arquivos abaixo", + "un_abrt": "abortar", + "un_del": "excluir", + "un_m3": "carregando seus uploads recentes...", + "un_busy": "excluindo {0} arquivos...", + "un_clip": "{0} links copiados para a รกrea de transferรชncia", + + "u_https1": "vocรช deveria", + "u_https2": "mudar para https", + "u_https3": "para um melhor desempenho", + "u_ancient": 'seu navegador รฉ impressionantemente antigo -- talvez vocรช devesse <a href="#" onclick="goto(\'bup\')">usar o bup em vez disso</a>', + "u_nowork": "precisa do firefox 53+ ou chrome 57+ ou iOS 11+", + "tail_2old": "precisa do firefox 105+ ou chrome 71+ ou iOS 14.5+", + "u_nodrop": 'seu navegador รฉ muito antigo para upload de arrastar e soltar', + "u_notdir": "isso nรฃo รฉ uma pasta!\n\nseu navegador รฉ muito antigo,\npor favor, tente arrastar e soltar em vez disso", + "u_uri": "para arrastar e soltar imagens de outras janelas do navegador,\npor favor, solte-as no grande botรฃo de upload", + "u_enpot": 'mudar para <a href="#">UI batata</a> (pode melhorar a velocidade de upload)', + "u_depot": 'mudar para <a href="#">UI chique</a> (pode reduzir a velocidade de upload)', + "u_gotpot": 'mudando para a UI batata para uma velocidade de upload melhorada,\n\nsinta-se ร  vontade para discordar e voltar!', + "u_pott": "<p>arquivos:   <b>{0}</b> concluรญdos,   <b>{1}</b> falhados,   <b>{2}</b> ocupados,   <b>{3}</b> na fila</p>", + "u_ever": "este รฉ o uploader bรกsico; up2k precisa de pelo menos<br>chrome 21 // firefox 13 // edge 12 // opera 12 // safari 5.1", + "u_su2k": 'este รฉ o uploader bรกsico; <a href="#" id="u2yea">up2k</a> รฉ melhor', + "u_uput": 'otimizar para velocidade (pular checksum)', + "u_ewrite": 'vocรช nรฃo tem acesso de escrita a esta pasta', + "u_eread": 'vocรช nรฃo tem acesso de leitura a esta pasta', + "u_enoi": 'a busca de arquivos nรฃo estรก ativada na configuraรงรฃo do servidor', + "u_enoow": "substituir nรฃo funcionarรก aqui; precisa de permissรฃo de Excluir", + "u_badf": 'Estes {0} arquivos (de um total de {1}) foram ignorados, possivelmente devido a permissรตes do sistema de arquivos:\n\n', + "u_blankf": 'Estes {0} arquivos (de um total de {1}) estรฃo em branco / vazios; enviรก-los de qualquer maneira?\n\n', + "u_applef": 'Estes {0} arquivos (de um total de {1}) sรฃo provavelmente indesejรกveis;\nPressione <code>OK/Enter</code> para PULAR os seguintes arquivos,\nPressione <code>Cancelar/ESC</code> para NรƒO excluir, e ENVIAR esses tambรฉm:\n\n', + "u_just1": '\nTalvez funcione melhor se vocรช selecionar apenas um arquivo', + "u_ff_many": "se vocรช estiver usando <b>Linux / MacOS / Android,</b> entรฃo essa quantidade de arquivos <a href=\"https://bugzilla.mozilla.org/show_bug.cgi?id=1790500\" target=\"_blank\"><em>pode</em> travar o Firefox!</a>\nse isso acontecer, por favor, tente novamente (ou use o Chrome).", + "u_up_life": "Este upload serรก excluรญdo do servidor\n{0} apรณs ser concluรญdo", + "u_asku": 'enviar estes {0} arquivos para <code>{1}</code>', + "u_unpt": "vocรช pode desfazer / excluir este upload usando o ๐Ÿงฏ no canto superior esquerdo", + "u_bigtab": 'prestes a mostrar {0} arquivos\n\nisto pode travar seu navegador, tem certeza?', + "u_scan": 'Escaneando arquivos...', + "u_dirstuck": 'o iterador de diretรณrio travou ao tentar acessar os seguintes {0} itens; irรก pular:', + "u_etadone": 'Concluรญdo ({0}, {1} arquivos)', + "u_etaprep": '(preparando para enviar)', + "u_hashdone": 'hash concluรญdo', + "u_hashing": 'hash', + "u_hs": 'handshaking...', + "u_started": "os arquivos estรฃo sendo enviados agora; veja [๐Ÿš€]", + "u_dupdefer": "duplicado; serรก processado apรณs todos os outros arquivos", + "u_actx": "clique neste texto para evitar perda de<br />desempenho ao mudar para outras janelas/abas", + "u_fixed": "OK!  Consertado ๐Ÿ‘", + "u_cuerr": "falha ao enviar o bloco {0} de {1};\nprovavelmente inofensivo, continuando\n\narquivo: {2}", + "u_cuerr2": "o servidor rejeitou o upload (bloco {0} de {1});\ntentarรก novamente mais tarde\n\narquivo: {2}\n\nerro ", + "u_ehstmp": "tentarรก novamente; veja no canto inferior direito", + "u_ehsfin": "o servidor rejeitou a solicitaรงรฃo para finalizar o upload; tentando novamente...", + "u_ehssrch": "o servidor rejeitou a solicitaรงรฃo para realizar a busca; tentando novamente...", + "u_ehsinit": "o servidor rejeitou a solicitaรงรฃo para iniciar o upload; tentando novamente...", + "u_eneths": "erro de rede ao realizar o handshake de upload; tentando novamente...", + "u_enethd": "erro de rede ao testar a existรชncia do alvo; tentando novamente...", + "u_cbusy": "esperando o servidor confiar em nรณs novamente apรณs uma falha de rede...", + "u_ehsdf": "o servidor ficou sem espaรงo em disco!\n\ncontinuarรก tentando novamente, caso alguรฉm\nlibere espaรงo suficiente para continuar", + "u_emtleak1": "parece que seu navegador pode ter um vazamento de memรณria;\npor favor,", + "u_emtleak2": ' <a href="{0}">mude para https (recomendado)</a> ou ', + "u_emtleak3": ' ', + "u_emtleakc": 'tente o seguinte:\n<ul><li>pressione <code>F5</code> para atualizar a pรกgina</li><li>depois desative o botรฃo  <code>mt</code>  nas  <code>โš™๏ธ configuraรงรตes</code></li><li>e tente o upload novamente</li></ul>Os uploads serรฃo um pouco mais lentos, mas tudo bem.\nDesculpe pelo problema !\n\nPS: chrome v107 <a href="https://bugs.chromium.org/p/chromium/issues/detail?id=1354816" target="_blank">tem uma correรงรฃo de bug</a> para isso', + "u_emtleakf": 'tente o seguinte:\n<ul><li>pressione <code>F5</code> para atualizar a pรกgina</li><li>depois ative <code>๐Ÿฅ”</code> (batata) na UI de upload<li>e tente o upload novamente</li></ul>\nPS: o firefox <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=1790500" target="_blank">esperanรงosamente terรก uma correรงรฃo de bug</a> em algum momento', + "u_s404": "nรฃo encontrado no servidor", + "u_expl": "explicar", + "u_maxconn": "a maioria dos navegadores limita isso a 6, mas o firefox permite que vocรช aumente com <code>connections-per-server</code> em <code>about:config</code>", + "u_tu": '<p class="warn">AVISO: turbo ativado, <span> o cliente pode nรฃo detectar e retomar uploads incompletos; veja a dica de ferramenta do botรฃo turbo</span></p>', + "u_ts": '<p class="warn">AVISO: turbo ativado, <span> os resultados da busca podem estar incorretos; veja a dica de ferramenta do botรฃo turbo</span></p>', + "u_turbo_c": "o turbo estรก desativado na configuraรงรฃo do servidor", + "u_turbo_g": "desativando o turbo porque vocรช nรฃo tem\n privilรฉgios de listagem de diretรณrio neste volume", + "u_life_cfg": 'excluir automaticamente depois de <input id="lifem" p="60" /> min (ou <input id="lifeh" p="3600" /> horas)', + "u_life_est": 'o upload serรก excluรญdo <span id="lifew" tt="local time">---</span>', + "u_life_max": 'esta pasta impรตe um\n tempo de vida mรกximo de {0}', + "u_unp_ok": 'a despublicaรงรฃo รฉ permitida para {0}', + "u_unp_ng": 'a despublicaรงรฃo NรƒO serรก permitida', + "ue_ro": 'seu acesso a esta pasta รฉ Somente Leitura\n\n', + "ue_nl": 'vocรช nรฃo estรก logado no momento', + "ue_la": 'vocรช estรก logado no momento como "{0}"', + "ue_sr": 'vocรช estรก no modo de busca de arquivos no momento\n\nmude para o modo de upload clicando na lupa ๐Ÿ”Ž (ao lado do grande botรฃo BUSCAR), e tente enviar novamente\n\ndesculpe', + "ue_ta": 'tente enviar novamente, deve funcionar agora', + "ue_ab": "este arquivo jรก estรก sendo enviado para outra pasta, e esse upload deve ser concluรญdo antes que o arquivo possa ser enviado para outro lugar.\n\nVocรช pode abortar e esquecer o upload inicial usando o ๐Ÿงฏ no canto superior esquerdo", + "ur_1uo": "OK: Arquivo enviado com sucesso", + "ur_auo": "OK: Todos os {0} arquivos enviados com sucesso", + "ur_1so": "OK: Arquivo encontrado no servidor", + "ur_aso": "OK: Todos os {0} arquivos encontrados no servidor", + "ur_1un": "O upload falhou, desculpe", + "ur_aun": "Todos os {0} uploads falharam, desculpe", + "ur_1sn": "O arquivo NรƒO foi encontrado no servidor", + "ur_asn": "Os {0} arquivos NรƒO foram encontrados no servidor", + "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", + + "lang_set": "atualizar para a mudanรงa ter efeito?" + }, "rus": { "tt": "ะ ัƒััะบะธะน", @@ -5780,6 +8954,7 @@ var Ls = { "gou": 'ั€ะพะดะธั‚ะตะปัŒัะบะฐั ะฟะฐะฟะบะฐ">ะฒะฒะตั€ั…', "gon": 'ัะปะตะดัƒัŽั‰ะฐั ะฟะฐะฟะบะฐ">ัะปะตะด', "logout": "ะ’ั‹ะนั‚ะธ ", + "login": "ะ’ะพะนั‚ะธ", //m "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>", @@ -5966,9 +9141,9 @@ var Ls = { "mt_mloop": "ะฟะพะฒั‚ะพั€ัั‚ัŒ ั‚ั€ะตะบะธ ะฒ ะฟะฐะฟะบะต\">๐Ÿ” ั†ะธะบะป", "mt_mnext": "ะทะฐะณั€ัƒะทะธั‚ัŒ ัะปะตะดัƒัŽั‰ัƒัŽ ะฟะฐะฟะบัƒ ะธ ะฟั€ะพะดะพะปะถะธั‚ัŒ ะฒ ะฝะตะน\">๐Ÿ“‚ ัะปะตะด.", "mt_mstop": "ะฟั€ะธะพัั‚ะฐะฝะพะฒะธั‚ัŒ ะฒะพัะฟั€ะพะธะทะฒะตะดะตะฝะธะต\">โธ ัั‚ะพะฟ", - "mt_cflac": "ะบะพะฝะฒะตั€ั‚ะธั€ะพะฒะฐั‚ัŒ flac / wav ะฒ opus\">flac", - "mt_caac": "ะบะพะฝะฒะตั€ั‚ะธั€ะพะฒะฐั‚ัŒ aac / m4a ะฒ opus\">aac", - "mt_coth": "ะบะพะฝะฒะตั€ั‚ะธั€ะพะฒะฐั‚ัŒ ะฒัั‘ ะพัั‚ะฐะปัŒะฝะพะต (ะบั€ะพะผะต mp3) ะฒ opus\">ะดั€.", + "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", @@ -6048,7 +9223,7 @@ var Ls = { "fs_tsrc": "ะฟัƒั‚ัŒ ะบ ั„ะฐะนะปัƒ ะธะปะธ ะฟะฐะฟะบะต, ะบะพั‚ะพั€ั‹ะผะธ ะฝัƒะถะฝะพ ะฟะพะดะตะปะธั‚ัŒัั", "fs_ppwd": "ะฟะฐั€ะพะปัŒ (ะฝะตะพะฑัะทะฐั‚ะตะปัŒะฝะพ)", "fs_w8": "ัะพะทะดะฐัŽ ะดะพัั‚ัƒะฟ...", - "fs_ok": "ะฝะฐะถะผะธั‚ะต <code>Enter/OK</code>, ั‡ั‚ะพะฑั‹ ัะบะพะฟะธั€ะพะฒะฐั‚ัŒ\nะฝะฐะถะผะธั‚ะต <code>ESC/Cancel</code>, ั‡ั‚ะพะฑั‹ ะทะฐะบั€ั‹ั‚ัŒ", + "fs_ok": "ะฝะฐะถะผะธั‚ะต <code>Enter/OK</code>, ั‡ั‚ะพะฑั‹ ัะบะพะฟะธั€ะพะฒะฐั‚ัŒ\nะฝะฐะถะผะธั‚ะต <code>ESC/ะžั‚ะผะตะฝะฐ</code>, ั‡ั‚ะพะฑั‹ ะทะฐะบั€ั‹ั‚ัŒ", "frt_dec": "ะผะพะถะตั‚ ะธัะฟั€ะฐะฒะธั‚ัŒ ะฝะตะบะพั‚ะพั€ั‹ะต ัะปัƒั‡ะฐะธ ั ะฝะตะบะพั€ั€ะตะบั‚ะฝั‹ะผะธ ะธะผะตะฝะฐะผะธ ั„ะฐะนะปะพะฒ\">ะดะตะบะพะดะธั€ะพะฒะฐั‚ัŒ url", "frt_rst": "ัะฑั€ะพัะธั‚ัŒ ะธะทะผะตะฝั‘ะฝะฝั‹ะต ะธะผะตะฝะฐ ะพะฑั€ะฐั‚ะฝะพ ะบ ะพั€ะธะณะธะฝะฐะปัŒะฝั‹ะผ\">โ†บ ัะฑั€ะพั", @@ -6093,6 +9268,7 @@ var Ls = { "fcp_ok": "ัƒัะฟะตัˆะฝะพ ัะบะพะฟะธั€ะพะฒะฐะฝะพ", "fp_busy": "ะฟะตั€ะตะผะตั‰ะฐัŽ {0} ั„ะฐะนะปะพะฒ...\n\n{1}", "fcp_busy": "ะบะพะฟะธั€ัƒัŽ {0} ั„ะฐะนะปะพะฒ...\n\n{1}", + "fp_abrt": "ะฟั€ะตั€ั‹ะฒะฐะฝะธะต...", //m "fp_err": "ะพัˆะธะฑะบะฐ ะฟะตั€ะตะผะตั‰ะตะฝะธั:\n", "fcp_err": "ะพัˆะธะฑะบะฐ ะบะพะฟะธั€ะพะฒะฐะฝะธั:\n", "fp_confirm": "ะฟะตั€ะตะผะตัั‚ะธั‚ัŒ ัั‚ะธ {0} ั„ะฐะนะปะพะฒ ััŽะดะฐ?", @@ -6409,6 +9585,7 @@ var Ls = { "gou": 'carpeta de nivel superior">subir', "gon": 'siguiente carpeta">siguiente', "logout": "Cerrar sesiรณn ", + "login": "Iniciar sesiรณn", //m "access": " acceso", "ot_close": "cerrar submenรบ", "ot_search": "buscar archivos por atributos, ruta / nombre, etiquetas de mรบsica, o cualquier combinaciรณn$N$N<code>foo bar</code> = debe contener ยซfooยป y ยซbarยป,$N<code>foo -bar</code> = debe contener ยซfooยป pero no ยซbarยป,$N<code>^yana .opus$</code> = empieza con ยซyanaยป y es un archivo ยซopusยป$N<code>"try unite"</code> = contiene exactamente ยซtry uniteยป$N$Nel formato de fecha es iso-8601, como$N<code>2009-12-31</code> o <code>2020-09-12 23:30:00</code>", @@ -6594,9 +9771,9 @@ var Ls = { "mt_mloop": 'repetir la carpeta actual">๐Ÿ” bucle', "mt_mnext": 'cargar la siguiente carpeta y continuar">๐Ÿ“‚ sig', "mt_mstop": 'detener reproducciรณn">โธ parar', - "mt_cflac": 'convertir flac / wav a opus">flac', - "mt_caac": 'convertir aac / m4a a opus">aac', - "mt_coth": 'convertir todos los demรกs (no mp3) a opus">oth', + "mt_cflac": 'convertir flac / wav a {0}">flac', + "mt_caac": 'convertir aac / m4a a {0}">aac', + "mt_coth": 'convertir todos los demรกs (no mp3) a {0}">oth', "mt_c2opus": 'la mejor opciรณn para ordenadores, portรกtiles, android">opus', "mt_c2owa": 'opus-weba, para iOS 17.5 y superior">owa', "mt_c2caf": 'opus-caf, para iOS 11 a 17">caf', @@ -6721,6 +9898,7 @@ var Ls = { "fcp_ok": "copia correcta", "fp_busy": "moviendo {0} elementos...\n\n{1}", "fcp_busy": "copiando {0} elementos...\n\n{1}", + "fp_abrt": "cancelando...", //m "fp_err": "fallo al mover:\n", "fcp_err": "fallo al copiar:\n", "fp_confirm": "ยฟmover estos {0} elementos aquรญ?", @@ -8297,6 +11475,7 @@ var Ls = { "gou": 'ะฑะฐั‚ัŒะบั–ะฒััŒะบะฐ ะฟะฐะฟะบะฐ">ะฒะณะพั€ัƒ', "gon": 'ะฝะฐัั‚ัƒะฟะฝะฐ ะฟะฐะฟะบะฐ">ะดะฐะปั–', "logout": "ะ’ะธะนั‚ะธ ", + "login": "ัƒะฒั–ะนั‚ะธ", //m "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>", @@ -8483,9 +11662,9 @@ var Ls = { "mt_mloop": "ะทะฐั†ะธะบะปะธั‚ะธ ะฒั–ะดะบั€ะธั‚ัƒ ะฟะฐะฟะบัƒ\">๐Ÿ” loop", "mt_mnext": "ะทะฐะฒะฐะฝั‚ะฐะถะธั‚ะธ ะฝะฐัั‚ัƒะฟะฝัƒ ะฟะฐะฟะบัƒ ั– ะฟั€ะพะดะพะฒะถะธั‚ะธ\">๐Ÿ“‚ next", "mt_mstop": "ะทัƒะฟะธะฝะธั‚ะธ ะฒั–ะดั‚ะฒะพั€ะตะฝะฝั\">โธ stop", - "mt_cflac": "ะบะพะฝะฒะตั€ั‚ัƒะฒะฐั‚ะธ flac / wav ะฒ opus\">flac", - "mt_caac": "ะบะพะฝะฒะตั€ั‚ัƒะฒะฐั‚ะธ aac / m4a ะฒ opus\">aac", - "mt_coth": "ะบะพะฝะฒะตั€ั‚ัƒะฒะฐั‚ะธ ะฒัั– ั–ะฝัˆั– (ะฝะต mp3) ะฒ opus\">oth", + "mt_cflac": "ะบะพะฝะฒะตั€ั‚ัƒะฒะฐั‚ะธ flac / wav ะฒ {0}\">flac", + "mt_caac": "ะบะพะฝะฒะตั€ั‚ัƒะฒะฐั‚ะธ aac / m4a ะฒ {0}\">aac", + "mt_coth": "ะบะพะฝะฒะตั€ั‚ัƒะฒะฐั‚ะธ ะฒัั– ั–ะฝัˆั– (ะฝะต mp3) ะฒ {0}\">oth", "mt_c2opus": "ะฝะฐะนะบั€ะฐั‰ะธะน ะฒะธะฑั–ั€ ะดะปั ั€ะพะฑะพั‡ะธั… ัั‚ะพะปั–ะฒ, ะฝะพัƒั‚ะฑัƒะบั–ะฒ, android\">opus", "mt_c2owa": "opus-weba, ะดะปั iOS 17.5 ั– ะฝะพะฒั–ัˆะธั…\">owa", "mt_c2caf": "opus-caf, ะดะปั iOS 11 ะดะพ 17\">caf", @@ -8502,7 +11681,7 @@ var Ls = { "mb_play": "ะฒั–ะดั‚ะฒะพั€ะธั‚ะธ", "mm_hashplay": "ะฒั–ะดั‚ะฒะพั€ะธั‚ะธ ั†ะตะน ะฐัƒะดั–ะพ ั„ะฐะนะป?", - "mm_m3u": "ะฝะฐั‚ะธัะฝั–ั‚ัŒ <code>Enter/OK</code> ะดะปั ะฒั–ะดั‚ะฒะพั€ะตะฝะฝั\nะฝะฐั‚ะธัะฝั–ั‚ัŒ <code>ESC/Cancel</code> ะดะปั ั€ะตะดะฐะณัƒะฒะฐะฝะฝั", + "mm_m3u": "ะฝะฐั‚ะธัะฝั–ั‚ัŒ <code>Enter/ะ“ะฐั€ะฐะทะด</code> ะดะปั ะฒั–ะดั‚ะฒะพั€ะตะฝะฝั\nะฝะฐั‚ะธัะฝั–ั‚ัŒ <code>ESC/ะกะบะฐััƒะฒะฐั‚ะธ</code> ะดะปั ั€ะตะดะฐะณัƒะฒะฐะฝะฝั", "mp_breq": "ะฟะพั‚ั€ั–ะฑะตะฝ firefox 82+ ะฐะฑะพ chrome 73+ ะฐะฑะพ iOS 15+", "mm_bload": "ะทะฐั€ะฐะท ะทะฐะฒะฐะฝั‚ะฐะถัƒั”ั‚ัŒัั...", "mm_bconv": "ะบะพะฝะฒะตั€ั‚ัƒั”ั‚ัŒัั ะฒ {0}, ะฑัƒะดัŒ ะปะฐัะบะฐ, ะทะฐั‡ะตะบะฐะนั‚ะต...", @@ -8535,7 +11714,7 @@ var Ls = { "f_dls": 'ะฟะพัะธะปะฐะฝะฝั ะฝะฐ ั„ะฐะนะปะธ ะฒ ะฟะพั‚ะพั‡ะฝั–ะน ะฟะฐะฟั†ั– ะฑัƒะปะธ\nะทะผั–ะฝะตะฝั– ะฝะฐ ะฟะพัะธะปะฐะฝะฝั ะดะปั ะทะฐะฒะฐะฝั‚ะฐะถะตะฝะฝั', - "f_partial": "ะฉะพะฑ ะฑะตะทะฟะตั‡ะฝะพ ะทะฐะฒะฐะฝั‚ะฐะถะธั‚ะธ ั„ะฐะนะป, ัะบะธะน ะทะฐั€ะฐะท ะทะฐะฒะฐะฝั‚ะฐะถัƒั”ั‚ัŒัั, ะฑัƒะดัŒ ะปะฐัะบะฐ, ะบะปะฐั†ะฝั–ั‚ัŒ ะฝะฐ ั„ะฐะนะป, ัะบะธะน ะผะฐั” ั‚ะฐะบะต ัะฐะผะต ั–ะผ'ั, ะฐะปะต ะฑะตะท ั€ะพะทัˆะธั€ะตะฝะฝั <code>.PARTIAL</code>. ะ‘ัƒะดัŒ ะปะฐัะบะฐ, ะฝะฐั‚ะธัะฝั–ั‚ัŒ CANCEL ะฐะฑะพ Escape, ั‰ะพะฑ ะทั€ะพะฑะธั‚ะธ ั†ะต.\n\nะะฐั‚ะธัะฝะตะฝะฝั OK / Enter ะฟั€ะพั–ะณะฝะพั€ัƒั” ั†ะต ะฟะพะฟะตั€ะตะดะถะตะฝะฝั ั– ะฟั€ะพะดะพะฒะถะธั‚ัŒ ะทะฐะฒะฐะฝั‚ะฐะถะตะฝะฝั <code>.PARTIAL</code> ั€ะพะฑะพั‡ะพะณะพ ั„ะฐะนะปัƒ ะทะฐะผั–ัั‚ัŒ ั†ัŒะพะณะพ, ั‰ะพ ะผะฐะนะถะต ะฝะฐะฟะตะฒะฝะพ ะดะฐัั‚ัŒ ะฒะฐะผ ะฟะพัˆะบะพะดะถะตะฝั– ะดะฐะฝั–.", + "f_partial": "ะฉะพะฑ ะฑะตะทะฟะตั‡ะฝะพ ะทะฐะฒะฐะฝั‚ะฐะถะธั‚ะธ ั„ะฐะนะป, ัะบะธะน ะทะฐั€ะฐะท ะทะฐะฒะฐะฝั‚ะฐะถัƒั”ั‚ัŒัั, ะฑัƒะดัŒ ะปะฐัะบะฐ, ะบะปะฐั†ะฝั–ั‚ัŒ ะฝะฐ ั„ะฐะนะป, ัะบะธะน ะผะฐั” ั‚ะฐะบะต ัะฐะผะต ั–ะผ'ั, ะฐะปะต ะฑะตะท ั€ะพะทัˆะธั€ะตะฝะฝั <code>.PARTIAL</code>. ะ‘ัƒะดัŒ ะปะฐัะบะฐ, ะฝะฐั‚ะธัะฝั–ั‚ัŒ ะกะบะฐััƒะฒะฐั‚ะธ ะฐะฑะพ Escape, ั‰ะพะฑ ะทั€ะพะฑะธั‚ะธ ั†ะต.\n\nะะฐั‚ะธัะฝะตะฝะฝั ะ“ะฐั€ะฐะทะด / Enter ะฟั€ะพั–ะณะฝะพั€ัƒั” ั†ะต ะฟะพะฟะตั€ะตะดะถะตะฝะฝั ั– ะฟั€ะพะดะพะฒะถะธั‚ัŒ ะทะฐะฒะฐะฝั‚ะฐะถะตะฝะฝั <code>.PARTIAL</code> ั€ะพะฑะพั‡ะพะณะพ ั„ะฐะนะปัƒ ะทะฐะผั–ัั‚ัŒ ั†ัŒะพะณะพ, ั‰ะพ ะผะฐะนะถะต ะฝะฐะฟะตะฒะฝะพ ะดะฐัั‚ัŒ ะฒะฐะผ ะฟะพัˆะบะพะดะถะตะฝั– ะดะฐะฝั–.", "ft_paste": "ะฒัั‚ะฐะฒะธั‚ะธ {0} ะตะปะตะผะตะฝั‚ั–ะฒ$Nะ“ะฐั€ัั‡ะฐ ะบะปะฐะฒั–ัˆะฐ: ctrl-V", "fr_eperm": 'ะฝะต ะผะพะถัƒ ะฟะตั€ะตะนะผะตะฝัƒะฒะฐั‚ะธ:\nัƒ ะฒะฐั ะฝะตะผะฐั” ะดะพะทะฒะพะปัƒ โ€œะฟะตั€ะตะผั–ั‰ะตะฝะฝัโ€œ ะฒ ั†ั–ะน ะฟะฐะฟั†ั–', @@ -8565,7 +11744,7 @@ var Ls = { "fs_tsrc": "ั„ะฐะนะป ะฐะฑะพ ะฟะฐะฟะบะฐ ะดะปั ัะฟั–ะปัŒะฝะพะณะพ ะดะพัั‚ัƒะฟัƒ", "fs_ppwd": "ะฝะตะพะฑะพะฒ'ัะทะบะพะฒะธะน ะฟะฐั€ะพะปัŒ", "fs_w8": "ัั‚ะฒะพั€ะตะฝะฝั ัะฟั–ะปัŒะฝะพะณะพ ะดะพัั‚ัƒะฟัƒ...", - "fs_ok": "ะฝะฐั‚ะธัะฝั–ั‚ัŒ <code>Enter/OK</code> ะดะปั ะบะพะฟั–ัŽะฒะฐะฝะฝั ะดะพ ะฑัƒั„ะตั€ะฐ\nะฝะฐั‚ะธัะฝั–ั‚ัŒ <code>ESC/Cancel</code> ะดะปั ะทะฐะบั€ะธั‚ั‚ั", + "fs_ok": "ะฝะฐั‚ะธัะฝั–ั‚ัŒ <code>Enter/ะ“ะฐั€ะฐะทะด</code> ะดะปั ะบะพะฟั–ัŽะฒะฐะฝะฝั ะดะพ ะฑัƒั„ะตั€ะฐ\nะฝะฐั‚ะธัะฝั–ั‚ัŒ <code>ESC/ะกะบะฐััƒะฒะฐั‚ะธ</code> ะดะปั ะทะฐะบั€ะธั‚ั‚ั", "frt_dec": "ะผะพะถะต ะฒะธะฟั€ะฐะฒะธั‚ะธ ะดะตัะบั– ะฒะธะฟะฐะดะบะธ ะฟะพัˆะบะพะดะถะตะฝะธั… ั–ะผะตะฝ ั„ะฐะนะปั–ะฒ\">url-decode", "frt_rst": "ัะบะธะฝัƒั‚ะธ ะทะผั–ะฝะตะฝั– ั–ะผะตะฝะฐ ั„ะฐะนะปั–ะฒ ะฝะฐะทะฐะด ะดะพ ะพั€ะธะณั–ะฝะฐะปัŒะฝะธั…\">โ†บ reset", @@ -8610,6 +11789,7 @@ var Ls = { "fcp_ok": "ะบะพะฟั–ัŽะฒะฐะฝะฝั OK", "fp_busy": "ะฟะตั€ะตะผั–ั‰ะตะฝะฝั {0} ะตะปะตะผะตะฝั‚ั–ะฒ...\n\n{1}", "fcp_busy": "ะบะพะฟั–ัŽะฒะฐะฝะฝั {0} ะตะปะตะผะตะฝั‚ั–ะฒ...\n\n{1}", + "fp_abrt": "ะฟะตั€ะตั€ะธะฒะฐะฝะฝั...", //m "fp_err": "ะฟะตั€ะตะผั–ั‰ะตะฝะฝั ะฝะตะฒะดะฐะปะต:\n", "fcp_err": "ะบะพะฟั–ัŽะฒะฐะฝะฝั ะฝะตะฒะดะฐะปะต:\n", "fp_confirm": "ะฟะตั€ะตะผั–ัั‚ะธั‚ะธ ั†ั– {0} ะตะปะตะผะตะฝั‚ะธ ััŽะดะธ?", @@ -8754,7 +11934,7 @@ var Ls = { "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>Cancel/ESC</code> ั‰ะพะฑ ะะ• ะฒะธะบะปัŽั‡ะฐั‚ะธ, ั– ะ—ะะ’ะะะขะะ–ะ˜ะขะ˜ ั—ั… ั‚ะฐะบะพะถ:\n\n', + "u_applef": 'ะฆั– {0} ั„ะฐะนะปะธ (ะท {1} ะทะฐะณะฐะปัŒะฝะธั…), ะนะผะพะฒั–ั€ะฝะพ, ะฝะตะฑะฐะถะฐะฝั–;\nะะฐั‚ะธัะฝั–ั‚ัŒ <code>ะ“ะฐั€ะฐะทะด/Enter</code> ั‰ะพะฑ ะŸะ ะžะŸะฃะกะขะ˜ะขะ˜ ะฝะฐัั‚ัƒะฟะฝั– ั„ะฐะนะปะธ,\nะะฐั‚ะธัะฝั–ั‚ัŒ <code>ะกะบะฐััƒะฒะฐั‚ะธ/ESC</code> ั‰ะพะฑ ะะ• ะฒะธะบะปัŽั‡ะฐั‚ะธ, ั– ะ—ะะ’ะะะขะะ–ะ˜ะขะ˜ ั—ั… ั‚ะฐะบะพะถ:\n\n', "u_just1": '\nะœะพะถะปะธะฒะพ, ั†ะต ัะฟั€ะฐั†ัŽั” ะบั€ะฐั‰ะต, ัะบั‰ะพ ะฒะธ ะฒะธะฑะตั€ะตั‚ะต ะปะธัˆะต ะพะดะธะฝ ั„ะฐะนะป', "u_ff_many": "ัะบั‰ะพ ะฒะธ ะฒะธะบะพั€ะธัั‚ะพะฒัƒั”ั‚ะต <b>Linux / MacOS / Android,</b> ั‚ะพ ั‚ะฐะบะฐ ะบั–ะปัŒะบั–ัั‚ัŒ ั„ะฐะนะปั–ะฒ <a href=\"https://bugzilla.mozilla.org/show_bug.cgi?id=1790500\" target=\"_blank\"><em>ะผะพะถะต</em> ะทะฐะฒั–ัะธั‚ะธ Firefox!</a>\nัะบั‰ะพ ั†ะต ัั‚ะฐะฝะตั‚ัŒัั, ะฑัƒะดัŒ ะปะฐัะบะฐ, ัะฟั€ะพะฑัƒะนั‚ะต ะทะฝะพะฒัƒ (ะฐะฑะพ ะฒะธะบะพั€ะธัั‚ะพะฒัƒะนั‚ะต Chrome).", "u_up_life": "ะฆะต ะทะฐะฒะฐะฝั‚ะฐะถะตะฝะฝั ะฑัƒะดะต ะฒะธะดะฐะปะตะฝะพ ะท ัะตั€ะฒะตั€ะฐ\n{0} ะฟั–ัะปั ะนะพะณะพ ะทะฐะฒะตั€ัˆะตะฝะฝั", @@ -8820,7 +12000,7 @@ var Ls = { }, }; -var LANGS = ["eng", "nor", "chi", "cze", "deu", "fin", "grc", "ita", "nld", "rus", "spa", "ukr"]; +var LANGS = ["eng", "nor", "chi", "cze", "deu", "fin", "fra", "grc", "ita", "kor", "nld", "nno", "pol", "por", "rus", "spa", "swe", "ukr"]; if (window.langmod) langmod(); @@ -9257,6 +12437,9 @@ var ACtx = !IPHONE && (window.AudioContext || window.webkitAudioContext), hash0 = location.hash, sloc0 = '' + location, noih = /[?&]v\b/.exec(sloc0), + dbg_kbd = /[?&]dbgkbd\b/.exec(sloc0), + abrt_key = "", + can_shr = false, rtt = null, ldks = [], dks = {}, @@ -9532,6 +12715,9 @@ var mpl = (function () { clmod(btns[a], 'on', fmts[a] == v) r.ac2 = v; + ebi('ac_flac').setAttribute('tt', L.mt_cflac.split('"')[0].format(v)); + ebi('ac_aac').setAttribute('tt', L.mt_caac.split('"')[0].format(v)); + ebi('ac_oth').setAttribute('tt', L.mt_coth.split('"')[0].format(v)); }; r.pp = function () { @@ -10434,6 +13620,10 @@ function song_skip(n, dirskip) { } function next_song(e) { ev(e); + if (QS('.dumb_loader_thing')) { + treectl.ls_cb = next_song; + return; + } if (mp.order.length) { var dirskip = mpl.traversals; mpl.traversals = 0; @@ -10467,6 +13657,10 @@ function prev_song(e) { if (mp.au && !mp.au.paused && mp.au.currentTime > 3) return seek_au_sec(0); + if (QS('.dumb_loader_thing')) { + treectl.ls_cb = function () { song_skip(-1); }; + return; + } return song_skip(-1); } function dl_song() { @@ -11516,9 +14710,11 @@ function eval_hash() { d.onclick = function (e) { ev(e); if (a) - QS(treectl.hidden ? '#path a:nth-last-child(2)' : '#treeul a.hl').focus(); + d = QS(treectl.hidden ? '#path a:nth-last-child(2)' : '#treeul a.hl'); else - QS(thegrid.en ? '#ggrid a' : '#files tbody tr[tabindex]').focus(); + d = QS(thegrid.en ? '#ggrid a' : '#files tbody tr[tabindex]'); + if (d) + d.focus(); }; })(a); @@ -11786,6 +14982,16 @@ function fmt_ren(re, md, fmt) { } +function fs_abrt() { + toast.inf(30, L.fp_abrt); + fileman.sn++; + fileman.f.length = 0; + var xhr = new XHR(); + xhr.open('POST', '/?fs_abrt=' + abrt_key, true); + xhr.send(); +} + + var fileman = (function () { var bren = ebi('fren'), bdel = ebi('fdel'), @@ -11796,6 +15002,8 @@ var fileman = (function () { t_paste, r = {}; + r.f = []; + r.sn = 1; r.clip = null; try { r.bus = new BroadcastChannel("fileman_bus"); @@ -11821,7 +15029,7 @@ var fileman = (function () { hdel = !(have_del && has(perms, 'delete')), hcut = !(have_mv && has(perms, 'move')), hpst = !(have_mv && has(perms, 'write')), - hshr = !(have_shr && acct != '*' && (has(perms, 'read') || has(perms, 'write'))); + hshr = !can_shr; if (!(enren || endel || encut || enpst)) hren = hdel = hcut = hpst = true; @@ -11982,7 +15190,7 @@ var fileman = (function () { exm.onkeydown = exh.onkeydown = exd.onkeydown = sh_k.onkeydown = sh_pw.onkeydown = function (e) { - var kc = (e.code || e.key) + ''; + var kc = (e.key || e.code) + ''; if (kc.endsWith('Enter')) sh_apply.click(); }; @@ -12080,9 +15288,12 @@ var fileman = (function () { return toast.err(3, L.fr_eperm); var f = [], + sn = ++r.sn, base = vsplit(sel[0].vp)[0], mkeys; + r.f = f; + for (var a = 0; a < sel.length; a++) { var vp = sel[a].vp; if (vp.endsWith('/')) @@ -12189,7 +15400,7 @@ var fileman = (function () { (function (a) { f[a].inew.onkeydown = function (e) { rn_ok(a, true); - var kc = (e.code || e.key) + ''; + var kc = (e.key || e.code) + ''; if (kc.endsWith('Enter')) return rn_apply(); }; @@ -12287,7 +15498,7 @@ var fileman = (function () { spresets(); ire.onkeydown = ifmt.onkeydown = function (e) { - var k = (e.code || e.key) + ''; + var k = (e.key || e.code) + ''; if (k == 'Escape' || k == 'Esc') return rn_cancel(); @@ -12362,7 +15573,9 @@ var fileman = (function () { return rn_cancel(); } - toast.show('inf r', 0, esc(L.fr_busy.format(f.length, f[0].ofn))); + var msg = esc(L.fr_busy.format(f.length, f[0].ofn)); + msg += '\n<a id="fs_abrt" class="btn" href="#" onclick="fs_abrt()">' + L.fs_abrt + '</a>'; + toast.show('inf r', 0, msg); var dst = base + uricom_enc(f[0].inew.value, false); function rename_cb() { @@ -12371,13 +15584,17 @@ var fileman = (function () { toast.err(9, L.fr_efail + msg); return; } + if (r.sn != sn) + return modal.confirm('WARNING: the rename was aborted'); f.shift().inew.value = '( OK )'; return rn_apply_loop(); } + abrt_key = randstr(9); + var xhr = new XHR(); - xhr.open('POST', f[0].src + '?move=' + dst, true); + xhr.open('POST', f[0].src + '?move=' + dst + '&akey=' + abrt_key, true); xhr.onload = xhr.onerror = rename_cb; xhr.send(); } @@ -12385,6 +15602,7 @@ var fileman = (function () { r.delete = function (e) { var sel = msel.getsel(), + sn = ++r.sn, vps = []; for (var a = 0; a < sel.length; a++) @@ -12421,6 +15639,9 @@ var fileman = (function () { toast.err(9, L.fd_err + msg); return; } + if (r.sn != sn) + return modal.confirm('WARNING: the delete was aborted'); + if (this.responseText.indexOf('deleted 0 files (and 0') + 1) { toast.err(9, L.fd_none); return deleter('xbd'); @@ -12633,12 +15854,15 @@ var fileman = (function () { '<div><table id="rn_f" class="m">', '<tr><td>' + L.fr_lnew + '</td><td>' + L.fr_lold + '</td></tr>', ], + sn = ++r.sn, ui = false, f = [], indir = [], srcdir = vsplit(r.clip[0])[0], links = QSA('#files tbody td:nth-child(2) a'); + r.f = f; + for (var a = 0, aa = links.length; a < aa; a++) indir.push(uricom_dec(vsplit(noq_href(links[a]))[1])); @@ -12670,13 +15894,17 @@ var fileman = (function () { if (!t.dst) return paster(); - toast.show('inf r', 0, esc((r.ccp ? L.fcp_busy : L.fp_busy).format(f.length + 1, uricom_dec(t.src)))); + var msg = esc((r.ccp ? L.fcp_busy : L.fp_busy).format(f.length + 1, uricom_dec(t.src))); + msg += '\n<a id="fs_abrt" class="btn" href="#" onclick="fs_abrt()">' + L.fs_abrt + '</a>'; + toast.show('inf r', 0, msg); var xhr = new XHR(), act = r.ccp ? '?copy=' : '?move=', dst = get_evpath() + uricom_enc(t.dst); - xhr.open('POST', t.src + act + dst, true); + abrt_key = randstr(9); + + xhr.open('POST', t.src + act + dst + '&akey=' + abrt_key, true); xhr.onload = xhr.onerror = paste_cb; xhr.send(); } @@ -12686,6 +15914,9 @@ var fileman = (function () { toast.err(9, (r.ccp ? L.fcp_err : L.fp_err) + msg); return; } + if (r.sn != sn) + return modal.confirm('WARNING: the paste was aborted'); + paster(); } function okgo() { @@ -12749,7 +15980,7 @@ var fileman = (function () { (function (a) { var inew = ebi('rn_new_' + a); inew.onkeydown = function (e) { - if (((e.code || e.key) + '').endsWith('Enter')) + if (((e.key || e.code) + '').endsWith('Enter')) return rn_apply(); }; inew.oninput = function (e) { @@ -13182,7 +16413,8 @@ var showfile = (function () { }; r.mktree = function () { - var crumbs = linksplit(get_evpath()).join('<span>/</span>'), + var top = get_evpath().slice(SR.length), + crumbs = linksplit(top).join('<span>/</span>'), html = ['<li class="bn">' + L.tv_lst + '<br />' + crumbs + '</li>']; for (var a = 0; a < r.files.length; a++) { var file = r.files[a]; @@ -13806,7 +17038,10 @@ function tree_scrolltoo(q) { } -function tree_neigh(n) { +function tree_neigh(n, ratelimit) { + if (ratelimit && QS('.dumb_loader_thing') && Date.now() - treectl.busied < 5) + return; + var links = QSA(showfile.active() || treectl.texts ? '#docul li>a' : '#treeul li>a+a'); if (!links.length) { treectl.dir_cb = function () { @@ -13924,6 +17159,13 @@ function fselfunw(e, ae, d, rem) { } selfun(); } +var konmai = 0, konmak = (function() { + var u = "arrowup", + d = "arrowdown", + l = "arrowleft", + r = "arrowright"; + return [u, u, d, d, l, r, l, r, "b", "a", "enter"]; +})(); var ahotkeys = function (e) { if (e.altKey || e.isComposing) return; @@ -13931,10 +17173,38 @@ var ahotkeys = function (e) { if (QS('#bbox-overlay.visible') || modal.busy) return; - var k = (e.code || e.key) + '', pos = -1, n, + var k = (e.key || e.code) + '', pos = -1, n, ae = document.activeElement, aet = ae && ae != document.body ? ae.nodeName.toLowerCase() : ''; + if (k.startsWith('Key')) + k = k.slice(3); + else if (k.startsWith('Digit')) + k = k.slice(5); + + var kl = k.toLowerCase(); + + if (dbg_kbd) + console.log('KBD', k, kl, e.key, e.code, e.keyCode, e.which); + + if (konmai < 0) + noop(); + else if (konmak[konmai] != kl) + konmai = konmai && kl == konmak[0] ? (konmai<3?konmai:1):0; + else if (++konmai >= konmak.length) { + konmai = -1; + document.documentElement.scrollTop = 0; + settheme.go(6); + start_actx(); + sfx_nice(); + toast.inf(9, 'omega clearance granted', null, 'top'); + setTimeout(function() { + apply_perms(treectl.lsc); + fileman.render(); + }, 573); + return ev(e); + } + if (k == 'Escape' || k == 'Esc') { ae && ae.blur(); tt.hide(); @@ -13982,7 +17252,7 @@ var ahotkeys = function (e) { fselfunw(e, ae, d, rem); return ev(e); } - if (k == 'Space' || k == 'Spacebar') { + if (k == 'Space' || k == 'Spacebar' || k == ' ') { clmod(ae, 'sel', 't'); msel.origin_tr(ae); msel.selui(); @@ -13990,7 +17260,7 @@ var ahotkeys = function (e) { } } if (in_ftab || !aet || (ae && ae.closest('#ggrid'))) { - if ((k == 'KeyA' || k == 'a') && ctrl(e)) { + if ((kl == 'a') && ctrl(e)) { var ntot = treectl.lsc.files.length + treectl.lsc.dirs.length, sel = msel.getsel(), all = msel.getall(); @@ -14006,7 +17276,7 @@ var ahotkeys = function (e) { } if (ae && ae.closest('pre')) { - if ((k == 'KeyA' || k == 'a') && ctrl(e)) { + if ((kl == 'a') && ctrl(e)) { var sel = document.getSelection(), ran = document.createRange(); @@ -14023,107 +17293,105 @@ var ahotkeys = function (e) { if (aet && aet != 'a' && aet != 'tr' && aet != 'td' && aet != 'div' && aet != 'pre') return; - if (e.key == '?') + if (k == '?') return hkhelp(); if (!e.shiftKey && ctrl(e)) { var sel = window.getSelection && window.getSelection() || {}; sel = sel && !sel.isCollapsed && sel.direction != 'none'; - if (k == 'KeyX' || k == 'x') + if (kl == 'x') return fileman.cut(e); - if ((k == 'KeyC' || k == 'c') && !sel) + if (kl == 'c' && !sel) return fileman.cpy(e); - if (k == 'KeyV' || k == 'v') + if (kl == 'v') return fileman.d_paste(e); - if (k == 'KeyK' || k == 'k') + if (kl == 'k') return fileman.delete(e); return; } - if (e.shiftKey && k != 'KeyA' && k != 'KeyD' && k != 'A' && k != 'D') + if (e.shiftKey && kl != 'a' && kl != 'd') return; - if (k.indexOf('Digit') === 0) - pos = parseInt(k.slice(-1)) * 0.1; + if (/^[0-9]$/.test(k)) + pos = parseInt(k) * 0.1; if (pos !== -1) return seek_au_mul(pos) || true; - if (k == 'KeyJ' || k == 'j') + if (kl == 'j') return prev_song() || true; - if (k == 'KeyL' || k == 'l') + if (kl == 'l') return next_song() || true; - if (k == 'KeyP' || k == 'p') + if (kl == 'p') return playpause() || true; - n = (k == 'KeyU' || k == 'u') ? -10 : - (k == 'KeyO' || k == 'o') ? 10 : 0; + n = kl == 'u' ? -10 : kl == 'o' ? 10 : 0; if (n !== 0) return seek_au_rel(n) || true; - if (k == 'KeyY') + if (kl == 'y') return msel.getsel().length ? ebi('seldl').click() : showfile.active() ? ebi('dldoc').click() : dl_song(); - n = (k == 'KeyI' || k == 'i') ? -1 : - (k == 'KeyK' || k == 'k') ? 1 : 0; + n = kl == 'i' ? -1 : kl == 'k' ? 1 : 0; if (n !== 0) - return tree_neigh(n); + return tree_neigh(n, 1); - if (k == 'KeyM' || k == 'm') + if (kl == 'm') return tree_up(); - if (k == 'KeyB' || k == 'b') + if (kl == 'b') return treectl.hidden ? treectl.entree() : treectl.detree(); - if (k == 'KeyG' || k == 'g') + if (kl == 'g') return ebi('griden').click(); - if (k == 'KeyT' || k == 't') + if (kl == 't') return ebi('thumbs').click(); - if (k == 'KeyV' || k == 'v') + if (kl == 'v') return ebi('filetree').click(); if (k == 'F2') return fileman.rename(); if (!treectl.hidden && (!e.shiftKey || !thegrid.en)) { - if (k == 'KeyA' || k == 'a') + if (kl == 'a') return QS('#twig').click(); - if (k == 'KeyD' || k == 'd') + if (kl == 'd') return QS('#twobytwo').click(); } if (showfile.active()) { - if (k == 'KeyS' || k == 's') + if (kl == 's') showfile.tglsel(); - if ((k == 'KeyE' || k == 'e') && ebi('editdoc').style.display != 'none') + if (kl == 'e' && ebi('editdoc').style.display != 'none') ebi('editdoc').click(); } if (mp && mp.au && !mp.au.paused) { - if (k == 'KeyS') + if (kl == 's') return sel_song(); } if (thegrid.en) { - if (k == 'KeyS' || k == 's') + if (kl == 's') return ebi('gridsel').click(); - if (k == 'KeyA' || k == 'a') + if (kl == 'a') return QSA('#ghead a[z]')[0].click(); - if (k == 'KeyD' || k == 'd') + if (kl == 'd') return QSA('#ghead a[z]')[1].click(); } }; @@ -14881,7 +18149,7 @@ var treectl = (function () { xhr.top = top; xhr.dst = dst; xhr.rst = rst; - xhr.ts = Date.now(); + xhr.ts = r.busied = Date.now(); xhr.open('GET', addq(dst, 'tree=' + top + (r.dots ? '&dots' : '') + k), true); xhr.onload = xhr.onerror = r.recvtree; xhr.send(); @@ -14920,13 +18188,22 @@ var treectl = (function () { } ebi('treeul').setAttribute('ts', ts); + if (SR && !top0) { + var x = SR.slice(1).split('/'); + while (x[0]) { + res = res['k' + x.shift()]; + if (!res) + throw 'invalid --rp-loc (or bug?)'; + } + } + var top = (top0 == '.' ? dst : top0).split('?')[0], name = uricom_dec(top.split('/').slice(-2)[0]), rtop = top.replace(/^\/+/, ""), - html = parsetree(res, rtop); + html = parsetree(res, rtop.slice(SR.length)); if (!top0) { - html = '<li><a href="#">-</a><a href="/">[root]</a>\n<ul>' + html; + html = '<li><a href="#">-</a><a href="' + SR + '/">[root]</a>\n<ul>' + html; if (rst || !ebi('treeul').getElementsByTagName('li').length) ebi('treeul').innerHTML = html + '</ul></li>'; } @@ -15069,10 +18346,6 @@ var treectl = (function () { return; } var href = this.getAttribute('href'); - if (R && !href.startsWith(SR)) { - location = href; - return; - } r.reqls(href, true); r.dir_cb = tree_scrollto; thegrid.setvis(true); @@ -15095,7 +18368,7 @@ var treectl = (function () { xhr.back = back xhr.hpush = hpush; xhr.hydrate = hydrate; - xhr.ts = Date.now(); + xhr.ts = r.busied = Date.now(); xhr.open('GET', xhr.top + '?ls' + uq, true); xhr.setRequestHeader('Fnugg', '' + xhr.ts); xhr.onload = xhr.onerror = recvls; @@ -15247,7 +18520,7 @@ var treectl = (function () { fun(); } - if (window.have_shr && QS('#op_unpost.act') && (cdir.startsWith(SR + have_shr) || get_evpath().startsWith(SR + have_shr))) + if (can_shr && QS('#op_unpost.act') && (cdir.startsWith(SR + have_shr) || get_evpath().startsWith(SR + have_shr))) goto('unpost'); } @@ -15416,6 +18689,7 @@ var treectl = (function () { return r.reqls(get_evpath(), false, undefined, true); } ls0.unlist = unlist0; + ls0.u2ts = u2ts; var top = get_evpath(); if (r.chk_index_html(top, ls0)) @@ -15540,7 +18814,7 @@ var treectl = (function () { url = '/' + (top ? top + uek : uek) + '/', sym = res[kk] ? '-' : '+', link = '<a href="#">' + sym + '</a><a href="' + - url + kdk + '">' + hek + '</a>'; + SR + url + kdk + '">' + hek + '</a>'; if (res[kk]) { var subtree = parsetree(res[kk], url.slice(1)); @@ -15651,6 +18925,19 @@ var wfp_debounce = (function () { function apply_perms(res) { perms = res.perms || []; + var axs = [], + aclass = '>', + chk = ['read', 'write', 'move', 'delete', 'get', 'admin']; + + if (konmai < 0) { + acct = 'Ted Faro'; + srvinf = 'FAS Nexus</span> // <span>57.3 EiB free of 127 EiB'; + res.shr_who = 'auth'; + perms = res.perms = chk; + have_up2k_idx = have_tags_idx = 1; + have_mv = have_del = true; + } + var a = QS('#ops a[data-dest="up2k"]'); if (have_up2k_idx) { a.removeAttribute('data-perm'); @@ -15665,10 +18952,6 @@ function apply_perms(res) { a.style.display = ''; tt.att(QS('#ops')); - var axs = [], - aclass = '>', - chk = ['read', 'write', 'move', 'delete', 'get', 'admin']; - for (var a = 0; a < chk.length; a++) if (has(perms, chk[a])) axs.push(chk[a].slice(0, 1).toUpperCase() + chk[a].slice(1)); @@ -15679,10 +18962,14 @@ function apply_perms(res) { axs += '-Only'; } + var dst = "?h"; + if (idp_login && acct == "*") + dst = idp_login.replace(/\{dst\}/g, get_evpath()); + ebi('acc_info').innerHTML = '<span id="srv_info2"><span>' + srvinf + '</span></span><span' + aclass + axs + L.access + '</span>' + (acct != '*' ? - '<form id="flogout" method="post" enctype="multipart/form-data"><input type="hidden" name="act" value="logout" /><input id="blogout" type="submit" value="' + (window.is_idp ? '' : L.logout) + acct + '"></form>' : - '<a href="?h">Login</a>'); + '<form id="flogout" method="post" enctype="multipart/form-data"><input type="hidden" name="act" value="logout" /><input id="blogout" type="submit" value="' + L.logout + acct + '"></form>' : + '<a href="' + dst + '">' + L.login + '</a>'); var o = QSA('#ops>a[data-perm]'); for (var a = 0; a < o.length; a++) { @@ -15713,6 +19000,11 @@ function apply_perms(res) { de = document.documentElement, tds = QSA('#u2conf td'); + shr_who = res.shr_who || shr_who; + can_shr = acct != '*' && (have_read || have_write) && ( + (shr_who == 'a' && has(perms, 'admin')) || + (shr_who == 'auth')); + clmod(de, "read", have_read); clmod(de, "write", have_write); clmod(de, "nread", !have_read); @@ -16121,7 +19413,7 @@ var settheme = (function () { var html = [], cb = ebi('themes'), itheme = ax.indexOf(theme[0]) * 2 + (light ? 1 : 0), - names = ['classic dark', 'classic light', 'pm-monokai', 'flat light', 'vice', 'hotdog stand', 'hacker', 'hi-con']; + names = ['classic dark', 'classic light', 'pm-monokai', 'flat light', 'vice', 'hotdog stand', 'hacker', 'hi-con', 'phi95 dark', 'phi95']; for (var a = 0; a < themes; a++) html.push('<option value="{0}">{0} โ”ƒ {1}</option>'.format(a, names[a] || 'custom')); @@ -16728,7 +20020,7 @@ function show_md(md, name, div, url, depth) { var marked_opts = { headerPrefix: 'md-', - breaks: true, + breaks: !md_no_br, gfm: true }; var ext = md_plug.pre; diff --git a/copyparty/web/idp.html b/copyparty/web/idp.html index b67dd2f1..8a58f6be 100644 --- a/copyparty/web/idp.html +++ b/copyparty/web/idp.html @@ -10,7 +10,7 @@ <meta name="theme-color" content="#{{ tcolor }}"> <link rel="stylesheet" media="screen" href="{{ r }}/.cpr/shares.css?_={{ ts }}"> <link rel="stylesheet" media="screen" href="{{ r }}/.cpr/ui.css?_={{ ts }}"> -{{ html_head }} +{{- html_head }} </head> <body> @@ -23,17 +23,17 @@ <th>user</th> <th>groups</th> </tr></thead><tbody> - {% for un, gn in rows %} + {%- for un, gn in rows %} <tr> <td><a href="{{ r }}/?idp=rm={{ un|e }}">forget</a></td> <td>{{ un|e }}</td> <td>{{ gn|e }}</td> </tr> - {% endfor %} + {%- endfor %} </tbody></table> - {% if not rows %} + {%- if not rows %} (there are no IdP users in the cache) - {% endif %} + {%- endif %} </div> <a href="#" id="repl">ฯ€</a> <script> diff --git a/copyparty/web/md.html b/copyparty/web/md.html index c3e2dfce..429a1eff 100644 --- a/copyparty/web/md.html +++ b/copyparty/web/md.html @@ -9,7 +9,7 @@ {%- if edit %} <link rel="stylesheet" href="{{ r }}/.cpr/md2.css?_={{ ts }}"> {%- endif %} -{{ html_head }} +{{- html_head }} </head> <body> <div id="mn"></div> @@ -130,7 +130,8 @@ write markdown (most html is ๐Ÿ™† too) var SR = "{{ r }}", last_modified = {{ lastmod }}, - have_emp = {{ "true" if have_emp else "false" }}, + have_emp = {{ have_emp }}, + md_no_br = {{ md_no_br }}, dfavico = "{{ favico }}"; var md_opt = { diff --git a/copyparty/web/md.js b/copyparty/web/md.js index 0109d453..66be69bb 100644 --- a/copyparty/web/md.js +++ b/copyparty/web/md.js @@ -201,7 +201,7 @@ function convert_markdown(md_text, dest_dom) { var marked_opts = { //headerPrefix: 'h-', - breaks: true, + breaks: !md_no_br, gfm: true }; @@ -217,7 +217,7 @@ function convert_markdown(md_text, dest_dom) { catch (ex) { if (IE) { dest_dom.innerHTML = 'IE cannot into markdown ;_;'; - return; + return false; } if (ext) @@ -344,6 +344,8 @@ function convert_markdown(md_text, dest_dom) { } catch (ex) { } }, 1); + + return true; } @@ -422,7 +424,7 @@ function init_toc() { } } - // hilight the correct toc items + scroll into view + // highlight the correct toc items + scroll into view function freshen_toclist() { if (anchors.length == 0) return; diff --git a/copyparty/web/md2.js b/copyparty/web/md2.js index 26b1e14b..45403f99 100644 --- a/copyparty/web/md2.js +++ b/copyparty/web/md2.js @@ -1,6 +1,10 @@ "use strict"; +var sloc0 = '' + location, + dbg_kbd = /[?&]dbgkbd\b/.exec(sloc0); + + // server state var server_md = dom_src.value; @@ -697,8 +701,14 @@ function reLastIndexOf(txt, ptn, end) { // table formatter function fmt_table(e) { if (e) e.preventDefault(); - //dom_tbox.className = ''; - + try { + fmt_table2(); + } + catch (ex) { + return toast.err(7, 'table-format (CTRL-K) failed:\n' + ex); + } +} +function fmt_table2() { var txt = dom_src.value, ofs = dom_src.selectionStart, //o0 = txt.lastIndexOf('\n\n', ofs), @@ -930,23 +940,30 @@ var set_lno = (function () { // hotkeys / toolbar (function () { - var keydown = function (ev) { - if (!ev && window.event) { - ev = window.event; + var keydown = function (e) { + if (!e && window.event) { + e = window.event; if (dev_fbw == 1) { toast.warn(10, 'hello from fallback code ;_;\ncheck console trace'); console.error('using window.event'); } } - var kc = ev.code || ev.keyCode || ev.which, + var k = (e.key || e.code) + '', editing = document.activeElement == dom_src; - //console.log(ev.key, ev.code, ev.keyCode, ev.which); - if (ctrl(ev) && (ev.code == "KeyS" || kc == 83)) { + if (k.startsWith('Key')) + k = k.slice(3); + + var kl = k.toLowerCase(); + + if (dbg_kbd) + console.log('KBD', k, kl, e.key, e.code, e.keyCode, e.which); + + if (ctrl(e) && kl == "s") { save(); return false; } - if (ev.code == "Escape" || kc == 27) { + if (k == "Escape" || k == "Esc") { var d = ebi('helpclose'); if (d) d.click(); @@ -954,46 +971,44 @@ var set_lno = (function () { if (editing) set_lno(); - if (ctrl(ev)) { - if (ev.code == "KeyE") { + if (ctrl(e)) { + if (kl == "e") { dom_nsbs.click(); return false; } if (!editing) return true; - if (ev.code == "KeyH" || kc == 72) { - md_header(ev.shiftKey); + if (kl == "h") { + md_header(e.shiftKey); return false; } - if (ev.code == "KeyZ" || kc == 90) { - if (ev.shiftKey) + if (kl == "z") { + if (e.shiftKey) action_stack.redo(); else action_stack.undo(); return false; } - if (ev.code == "KeyY" || kc == 89) { + if (kl == "y") { action_stack.redo(); return false; } - if (ev.code == "KeyK") { + if (kl == "k") { fmt_table(); return false; } - if (ev.code == "KeyU") { + if (kl == "u") { iter_uni(); return false; } - var up = ev.code == "ArrowUp" || kc == 38; - var dn = ev.code == "ArrowDown" || kc == 40; - if (up || dn) { - md_p_jump(dn); + if (k == "ArrowUp" || k == "ArrowDown") { + md_p_jump(k == "ArrowDown"); return false; } - if (ev.code == "KeyX" || ev.code == "KeyC") { - md_cut(ev.code == "KeyX"); + if (kl == "x" || kl == "c") { + md_cut(kl == "x"); return true; //sic } } @@ -1001,18 +1016,18 @@ var set_lno = (function () { if (!editing) return true; - if (ev.code == "Tab" || kc == 9) { - md_indent(ev.shiftKey); + if (k == "Tab") { + md_indent(e.shiftKey); return false; } - if (ev.code == "Home" || kc == 36) { - md_home(ev.shiftKey); + if (k == "Home") { + md_home(e.shiftKey); return false; } - if (!ev.shiftKey && ((ev.code + '').endsWith("Enter") || kc == 13)) { + if (!e.shiftKey && k.endsWith("Enter")) { return md_newline(); } - if (!ev.shiftKey && kc == 8) { + if (!e.shiftKey && k == "Backspace") { return md_backspace(); } } @@ -1036,7 +1051,9 @@ ebi('help').onclick = function (e) { var dom = ebi('helpbox'); var dtxt = dom.getElementsByTagName('textarea'); if (dtxt.length > 0) { - convert_markdown(dtxt[0].value, dom); + var txt = dtxt[0].value; + if (!convert_markdown(txt, dom)) + dom.innerText = txt.split('## markdown')[0]; dom.innerHTML = '<a href="#" id="helpclose">close</a>' + dom.innerHTML; } diff --git a/copyparty/web/mde.html b/copyparty/web/mde.html index adef6793..ac3377c6 100644 --- a/copyparty/web/mde.html +++ b/copyparty/web/mde.html @@ -8,7 +8,7 @@ <link rel="stylesheet" href="{{ r }}/.cpr/mde.css?_={{ ts }}"> <link rel="stylesheet" href="{{ r }}/.cpr/deps/mini-fa.css?_={{ ts }}"> <link rel="stylesheet" href="{{ r }}/.cpr/deps/easymde.css?_={{ ts }}"> -{{ html_head }} +{{- html_head }} </head> <body> <div id="mw"> @@ -28,7 +28,8 @@ var SR = "{{ r }}", last_modified = {{ lastmod }}, - have_emp = {{ "true" if have_emp else "false" }}, + have_emp = {{ have_emp }}, + md_no_br = {{ md_no_br }}, dfavico = "{{ favico }}"; var md_opt = { diff --git a/copyparty/web/msg.html b/copyparty/web/msg.html index 4ccac31f..0b966d11 100644 --- a/copyparty/web/msg.html +++ b/copyparty/web/msg.html @@ -8,7 +8,7 @@ <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 }}"> -{{ html_head }} +{{- html_head }} </head> <body> diff --git a/copyparty/web/rups.html b/copyparty/web/rups.html index 2cc0443a..fe344c12 100644 --- a/copyparty/web/rups.html +++ b/copyparty/web/rups.html @@ -10,7 +10,7 @@ <meta name="theme-color" content="#{{ tcolor }}"> <link rel="stylesheet" media="screen" href="{{ r }}/.cpr/rups.css?_={{ ts }}"> <link rel="stylesheet" media="screen" href="{{ r }}/.cpr/ui.css?_={{ ts }}"> -{{ html_head }} +{{- html_head }} </head> <body> diff --git a/copyparty/web/rups.js b/copyparty/web/rups.js index 17a44556..e4e8b3ce 100644 --- a/copyparty/web/rups.js +++ b/copyparty/web/rups.js @@ -1,5 +1,5 @@ function render() { - var html = ['<table id="tab"><thead><tr><th>size</th><th>who</th><th>when</th><th>age</th><th>dir</th><th>file</th></tr></thead><tbody>']; + 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; ebi('filter').value = V.filter; ebi('hits').innerHTML = 'showing ' + ups.length + ' files'; @@ -16,6 +16,7 @@ function render() { sz = ('' + f.sz).replace(/\B(?=(\d{3})+(?!\d))/g, " "); html.push('<tr><td>' + sz + + '</td><td>' + (f.un || '') + '</td><td>' + f.ip + '</td><td>' + ts + '</td><td>' + sa + diff --git a/copyparty/web/shares.html b/copyparty/web/shares.html index 381fe853..de128ab1 100644 --- a/copyparty/web/shares.html +++ b/copyparty/web/shares.html @@ -10,7 +10,7 @@ <meta name="theme-color" content="#{{ tcolor }}"> <link rel="stylesheet" media="screen" href="{{ r }}/.cpr/shares.css?_={{ ts }}"> <link rel="stylesheet" media="screen" href="{{ r }}/.cpr/ui.css?_={{ ts }}"> -{{ html_head }} +{{- html_head }} </head> <body> @@ -36,7 +36,7 @@ <th>hrs</th> <th>add time</th> </tr></thead><tbody> - {% for k, pw, vp, pr, st, un, t0, t1 in rows %} + {%- for k, pw, vp, pr, st, un, t0, t1 in rows %} <tr> <td> <a href="{{ r }}{{ shr }}{{ k }}?qr">qr</a> @@ -54,11 +54,11 @@ <td>{{ "inf" if not t1 else "dead" if t1 < now else ((t1 - now) / 3600) | round(1) }}</td> <td></td> </tr> - {% endfor %} + {%- endfor %} </tbody></table> - {% if not rows %} + {%- if not rows %} (you don't have any active shares btw) - {% endif %} + {%- endif %} </div> <a href="#" id="repl">ฯ€</a> <script> diff --git a/copyparty/web/splash.css b/copyparty/web/splash.css index 5ccebdc7..fb5d5075 100644 --- a/copyparty/web/splash.css +++ b/copyparty/web/splash.css @@ -24,6 +24,7 @@ h1 { li { margin: 1em 0; } +#lo, a { color: #047; background: #fff; @@ -37,6 +38,7 @@ a { td a { margin: 0; } +#wb, #w { color: #fff; background: #940; @@ -47,6 +49,7 @@ td a { float: right; margin: -.2em 0 0 .8em; } +#lo, .logout, a.r { color: #c04; @@ -176,12 +179,14 @@ html.z { html.z h1 { border-color: #777; } +html.z #lo, html.z a { color: #fff; background: #057; border-color: #37a; } html.z .logout, +html.z #lo, html.z a.r { background: #804; border-color: #c28; diff --git a/copyparty/web/splash.html b/copyparty/web/splash.html index 4363efd2..1b9a89be 100644 --- a/copyparty/web/splash.html +++ b/copyparty/web/splash.html @@ -9,7 +9,7 @@ <meta name="theme-color" content="#{{ tcolor }}"> <link rel="stylesheet" media="screen" href="{{ r }}/.cpr/splash.css?_={{ ts }}"> <link rel="stylesheet" media="screen" href="{{ r }}/.cpr/ui.css?_={{ ts }}"> -{{ html_head }} +{{- html_head }} </head> <body> @@ -18,12 +18,16 @@ <a id="a" href="{{ r }}/?h{{ re }}" class="af">refresh</a> <a id="v" href="{{ r }}/?hc" class="af">connect</a> - {%- if this.uname == '*' %} - <p id="b">howdy stranger   <small>(you're not logged in)</small></p> - {%- else %} - <a id="c" href="{{ r }}/?pw=x" class="logout">logout</a> - <p><span id="m">welcome back,</span> <strong>{{ this.uname|e }}</strong></p> - {%- endif %} + {%- if this.uname == '*' %} + <p id="b">howdy stranger   <small>(you're not logged in)</small></p> + {%- else %} + {%- if this.args.idp_logout %} + <a id="c" href="{{ this.args.idp_logout }}" class="logout">logout</a> + {%- else %} + <a id="c" href="{{ r }}/?pw=x" class="logout">logout</a> + {%- endif %} + <p><span id="m">welcome back,</span> <strong id="un">{{ this.uname|e }}</strong></p> + {%- endif %} {%- endif %} {%- if msg %} @@ -37,9 +41,9 @@ <table class="vols"> <thead><tr><th>%</th><th>speed</th><th>eta</th><th>idle</th><th>dir</th><th>file</th></tr></thead> <tbody> - {% for u in ups %} + {%- for u in ups %} <tr><td>{{ u[0] }}</td><td>{{ u[1] }}</td><td>{{ u[2] }}</td><td>{{ u[3] }}</td><td><a href="{{ u[4] }}">{{ u[5]|e }}</a></td><td>{{ u[6]|e }}</td></tr> - {% endfor %} + {%- endfor %} </tbody> </table> {%- endif %} @@ -49,9 +53,9 @@ <table class="vols"> <thead><tr><th>%</th><th>sent</th><th>speed</th><th>eta</th><th>idle</th><th></th><th>dir</th><th>file</th></tr></thead> <tbody> - {% for u in dls %} + {%- for u in dls %} <tr><td>{{ u[0] }}</td><td>{{ u[1] }}</td><td>{{ u[2] }}</td><td>{{ u[3] }}</td><td>{{ u[4] }}</td><td>{{ u[5] }}</td><td><a href="{{ u[6] }}">{{ u[7]|e }}</a></td><td>{{ u[8] }}</td></tr> - {% endfor %} + {%- endfor %} </tbody> </table> {%- endif %} @@ -70,11 +74,11 @@ <table class="vols"> <thead><tr><th>vol</th><th id="t">action</th><th>status</th></tr></thead> <tbody> - {% for mp in avol %} + {%- for mp in avol %} {%- if mp in vstate and vstate[mp] %} <tr><td><a href="{{ r }}{{ mp }}{{ url_suf }}">{{ mp }}</a></td><td><a class="s" href="{{ r }}{{ mp }}?scan">rescan</a></td><td>{{ vstate[mp] }}</td></tr> {%- endif %} - {% endfor %} + {%- endfor %} </tbody> </table> </td></tr></table> @@ -87,18 +91,18 @@ {%- if rvol %} <h1 id="f">you can browse:</h1> <ul> - {% for mp in rvol %} + {%- for mp in rvol %} <li><a href="{{ r }}{{ mp }}{{ url_suf }}">{{ mp }}</a></li> - {% endfor %} + {%- endfor %} </ul> {%- endif %} {%- if wvol %} <h1 id="g">you can upload to:</h1> <ul> - {% for mp in wvol %} + {%- for mp in wvol %} <li><a href="{{ r }}{{ mp }}{{ url_suf }}">{{ mp }}</a></li> - {% endfor %} + {%- endfor %} </ul> {%- endif %} @@ -110,64 +114,83 @@ <input type="password" id="lp" name="cppwd" placeholder=" password" /> <input type="hidden" name="uhash" id="uhash" value="x" /> <input type="submit" id="ls" value="Unlock" /> - {% if ahttps %} + {%- if ahttps %} <a id="w" href="{{ ahttps }}">switch to https</a> - {% endif %} + {%- endif %} </form> </div> {%- else %} <h1 id="l">login for more:</h1> <div> + {%- if this.args.idp_login %} + <ul><li> + <a href="{{ this.args.idp_login | replace("{dst}",r+"/"+qvpath) }}">{{ this.args.idp_login_t }}</a> + {%- if this.args.ao_have_pw %}or alternatively:{%- endif %} + </li></ul> + {%- endif %} + {%- if this.args.ao_have_pw %} <form id="lf" method="post" enctype="multipart/form-data" action="{{ r }}/{{ qvpath }}"> <input type="hidden" id="la" name="act" value="login" /> - {% if this.args.usernames %} + {%- if this.args.usernames %} <input type="text" id="lu" name="uname" placeholder=" username" size="12" /> <input type="password" id="lp" name="cppwd" placeholder=" password" size="12" /> - {% else %} + {%- else %} <input type="password" id="lp" name="cppwd" placeholder=" password" /> - {% endif %} + {%- endif %} <input type="hidden" name="uhash" id="uhash" value="x" /> <input type="submit" id="ls" value="login" /> - {% if chpw %} + {%- if chpw %} <a id="x" href="#">change password</a> - {% endif %} - {% if ahttps %} + {%- endif %} + {%- if ahttps %} <a id="w" href="{{ ahttps }}">switch to https</a> - {% endif %} + {%- endif %} </form> + {%- endif %} </div> {%- endif %} <h1 id="cc">other stuff:</h1> <ul> + {%- if ahttps %} + <li><a id="wb" href="{{ ahttps }}">switch to https</a></li> + {%- endif %} + {%- if this.uname in this.args.idp_adm_set %} <li><a id="ag" href="{{ r }}/?idp">view idp cache</a></li> - {% endif %} + {%- endif %} {%- if this.uname != '*' and this.args.shr %} <li><a id="y" href="{{ r }}/?shares">edit shares</a></li> - {% endif %} + {%- endif %} - {% if k304 or k304vis %} - {% if k304 %} + {%- if k304 or k304vis %} + {%- if k304 %} <li><a id="h" href="{{ r }}/?cc&setck=k304=n">disable k304</a> (currently enabled) {%- else %} <li><a id="i" href="{{ r }}/?cc&setck=k304=y" class="r">enable k304</a> (currently disabled) - {% endif %} + {%- endif %} <blockquote id="j">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</blockquote></li> - {% endif %} + {%- endif %} - {% if no304 or no304vis %} - {% if no304 %} + {%- if no304 or no304vis %} + {%- if no304 %} <li><a id="ab" href="{{ r }}/?cc&setck=no304=n">disable no304</a> (currently enabled) {%- else %} <li><a id="ac" href="{{ r }}/?cc&setck=no304=y" class="r">enable no304</a> (currently disabled) - {% endif %} + {%- endif %} <blockquote id="ad">enabling no304 will disable all caching; try this if k304 wasn't enough. This will waste a huge amount of network traffic!</blockquote></li> - {% endif %} + {%- endif %} <li><a id="af" href="{{ r }}/?ru">show recent uploads</a></li> <li><a id="k" href="{{ r }}/?reset" class="r" onclick="localStorage.clear();return true">reset client settings</a></li> + + {%- if this.uname != '*' and not in_shr %} + <li><form method="post" enctype="multipart/form-data"> + <input type="hidden" name="act" value="logout" /> + <input type="submit" id="lo" value="logout โ€œ{{ this.uname|e }}โ€ everywhere" /> + </form></li> + {%- endif %} </ul> </div> diff --git a/copyparty/web/splash.js b/copyparty/web/splash.js index 98394f24..05439e9a 100644 --- a/copyparty/web/splash.js +++ b/copyparty/web/splash.js @@ -17,6 +17,11 @@ var Ls = { "j1": "k304 bryter tilkoplingen for hver HTTP 304. Dette hjelper mot visse mellomtjenere som kan sette seg fast / plutselig slutter รฅ laste sider, men det reduserer ogsรฅ ytelsen betydelig", "k1": "nullstill innstillinger", "l1": "logg inn:", + "ls3": "logg inn", + "lu4": "brukernavn", + "lp4": "passord", + "lo3": "logg ut โ€œ{0}โ€ overalt", + "lo2": "avslutter รธkten pรฅ alle nettlesere", "m1": "velkommen tilbake,", "n1": "404: filen finnes ikke  โ”( ยด -`)โ”Œ", "o1": 'eller kanskje du ikke har tilgang? prรธv et passord eller <a href="' + SR + '/?h">gรฅ hjem</a>', @@ -46,6 +51,7 @@ var Ls = { "eng": { "d2": "shows the state of all active threads", "e2": "reload config files (accounts/volumes/volflags),$Nand rescan all e2ds volumes$N$Nnote: any changes to global settings$Nrequire a full restart to take effect", + "lo2": "ends the session on all browsers", "u2": "time since the last server write$N( upload / rename / ... )$N$N17d = 17 days$N1h23 = 1 hour 23 minutes$N4m56 = 4 minutes 56 seconds", "v2": "use this server as a local HDD", "ta1": "fill in your new password first", @@ -68,6 +74,11 @@ var Ls = { "j1": "k304 ไผšๅœจๆฏไธช HTTP 304 ๆ—ถๆ–ญๅผ€่ฟžๆŽฅใ€‚่ฟ™ๆœ‰ๅŠฉไบŽ้ฟๅ…ๆŸไบ›ไปฃ็†ๆœๅŠกๅ™จๅกไฝๆˆ–็ช็„ถๅœๆญขๅŠ ่ฝฝ้กต้ข๏ผŒไฝ†ไนŸไผšๆ˜พ่‘—้™ไฝŽๆ€ง่ƒฝใ€‚", "k1": "้‡็ฝฎ่ฎพ็ฝฎ", "l1": "็™ปๅฝ•๏ผš", + "ls3": "็™ปๅฝ•", //m + "lu4": "็”จๆˆทๅ", //m + "lp4": "ๅฏ†็ ", //m + "lo3": "ๅœจๆ‰€ๆœ‰ๅœฐๆ–นๆณจ้”€ {0}", //m + "lo2": "่ฟ™ๅฐ†็ป“ๆŸๅœจๆ‰€ๆœ‰ๆต่งˆๅ™จไธญ็š„ไผš่ฏ", //m "m1": "ๆฌข่ฟŽๅ›žๆฅ๏ผŒ", "n1": "404: ๆ–‡ไปถไธๅญ˜ๅœจ  โ”( ยด -`)โ”Œ", "o1": 'ๆˆ–่€…ไฝ ๅฏ่ƒฝๆฒกๆœ‰ๆƒ้™๏ผŸๅฐ่ฏ•่พ“ๅ…ฅๅฏ†็ ๆˆ– <a href="' + SR + '/?h">ๅ›žๅฎถ</a>', @@ -98,33 +109,38 @@ var Ls = { "a1": "obnovit", "b1": "ahoj cizinฤe   <small>(nejsi pล™ihlรกลกen)</small>", "c1": "odhlรกsit se", - "d1": "vypsat zรกsobnรญku", // TLNote: "d2" is the tooltip for this button + "d1": "vypsat zรกsobnรญku", "d2": "zobrazit stav vลกech aktivnรญch vlรกken", "e1": "znovu naฤรญst konfiguraci", "e2": "znovu naฤรญst konfiguraฤnรญ soubory (accounts/volumes/volflags),$Na prohledat vลกechny e2ds รบloลพiลกtฤ›$N$Npoznรกmka: vลกechny zmฤ›ny globรกlnรญch nastavenรญ$Nvyลพadujรญ รบplnรฉ restartovรกnรญ, aby se projevily", "f1": "mลฏลพeลก prochรกzet:", "g1": "mลฏลพeลก nahrรกvat do:", "cc1": "dalลกรญ vฤ›ci:", - "h1": "zakรกzat k304", // TLNote: "j1" explains what k304 is + "h1": "zakรกzat k304", "i1": "povolit k304", "j1": "povolenรญ k304 odpojรญ vaลกeho klienta pล™i kaลพdรฉm HTTP 304, coลพ mลฏลพe zabrรกnit nฤ›kterรฝm chybovรฝm proxy serverลฏm, aby se zasekly (nรกhle nenaฤรญtaly strรกnky), <em>ale</em> takรฉ to obecnฤ› zpomalรญ vฤ›ci", "k1": "resetovat nastavenรญ klienta", "l1": "pล™ihlaste se pro vรญce:", - "m1": "vรญtej zpฤ›t,", // TLNote: "welcome back, USERNAME" + "ls3": "pล™ihlรกsit se", //m + "lu4": "uลพivatelskรฉ jmรฉno", //m + "lp4": "heslo", //m + "lo3": "odhlรกsit โ€œ{0}โ€ vลกude", //m + "lo2": "tรญmto ukonฤรญte relaci ve vลกech prohlรญลพeฤรญch", //m + "m1": "vรญtej zpฤ›t,", "n1": "404 nenalezeno  โ”( ยด -`)โ”Œ", "o1": 'nebo moลพnรก nemรกลก pล™รญstup -- zkus heslo nebo <a href="' + SR + '/?h">jdi domลฏ</a>', "p1": "403 zakรกzรกno  ~โ”ปโ”โ”ป", "q1": 'pouลพij heslo nebo <a href="' + SR + '/?h">jdi domลฏ</a>', "r1": "jdi domลฏ", ".s1": "znovu prohledat", - "t1": "akce", // TLNote: this is the header above the "rescan" buttons + "t1": "akce", "u2": "ฤas od poslednรญho zรกpisu na server$N( upload / rename / ... )$N$N17d = 17 dnรญ$N1h23 = 1 hodina 23 minut$N4m56 = 4 minuty 56 sekund", "v1": "pล™ipojit", "v2": "pouลพรญt tento server jako mรญstnรญ HDD", "w1": "pล™epnout na https", "x1": "zmฤ›nit heslo", - "y1": "upravit sdรญlenรญ", // TLNote: shows the list of folders that the user has decided to share - "z1": "odblokovat toto sdรญlenรญ:", // TLNote: the password prompt to see a hidden share + "y1": "upravit sdรญlenรญ", + "z1": "odblokovat toto sdรญlenรญ:", "ta1": "nejprve vyplลˆte svรฉ novรฉ heslo", "ta2": "zopakujte pro potvrzenรญ novรฉho hesla:", "ta3": "nalezen pล™eklep; zkuste to prosรญm znovu", @@ -151,6 +167,11 @@ var Ls = { "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 "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>', @@ -192,6 +213,11 @@ var Ls = { "j1": "k304 katkaisee yhteytesi jokaisella HTTP 304:llรค, mikรค voi estรครค joitain bugisia vรคlityspalvelimia jumittumasta/lopettamasta sivujen lataamista, <em>mutta</em> se myรถs vรคhentรครค suorituskykyรค", "k1": "nollaa asetukset", "l1": "kirjaudu sisรครคn:", + "ls3": "kirjaudu sisรครคn", //m + "lu4": "kรคyttรคjรคtunnus", //m + "lp4": "salasana", //m + "lo3": "kirjaa โ€œ{0}โ€ ulos kaikkialta", //m + "lo2": "tรคmรค lopettaa istunnon kaikissa selaimissa", //m "m1": "tervetuloa takaisin,", "n1": "404: ei lรถytynyt mitรครคn  โ”( ยด -`)โ”Œ", "o1": 'tai ehkรค sinulla ei vain ole kรคyttรถoikeuksia? kokeile salasanaa tai <a href="' + SR + '/?h">mene kotiin</a>', @@ -218,6 +244,52 @@ var Ls = { "af1": "nรคytรค viimeaikaiset lataukset", "ag1": "nรคytรค tunnetut IdP-kรคyttรคjรคt", }, + "fra": { + "a1": "rafraรฎchir", + "b1": "salut รฉtranger   <small>(vous n'รชtes pas connectรฉ.)</small>", + "c1": "dรฉconnexion", + "d1": "vidange de la pile", + "d2": "affiche l'รฉtat de tous les threads actifs", + "e1": "recharger la configuration", + "e2": "recharger le fichier de configuration (comptes/volumes/indicateurs de volume),$Net rescanner tous les volumes e2ds$N$Nnote : n'importe quel changement aux paramรจtres globaux$Nnรฉcessite un redรฉmarrage complet pour prendre effet", + "f1": "vous pouvez naviguer :", + "g1": "vous pouvez tรฉlรฉcharger sur :", + "cc1": "autres choses :", + "h1": "dรฉsactiver k304", + "i1": "activer k304", + "j1": "activer k304 va dรฉconnecter votre client sur chaque HTTP 304, ce qui peut รฉviter ร  certains proxies dรฉfectueux de rester bloquรฉs (les pages ne se chargent soudainement plus), <em>mais</em> cela ralentira รฉgalement les choses en gรฉnรฉral", + "k1": "rรฉinitialiser les paramรจtres du client", + "l1": "connectez-vous pour en savoir plus :", + "ls3": "se connecter", //m + "lu4": "nom d'utilisateur", //m + "lp4": "mot de passe", //m + "lo3": "dรฉconnecter โ€œ{0}โ€ partout", //m + "lo2": "cela mettra fin ร  la session sur tous les navigateurs", //m + "m1": "heureux de vous revoir,", + "n1": "404 introuvable  โ”( ยด -`)โ”Œ", + "o1": 'ou peut-รชtre que vous n\'y avez pas accรจs -- essayer un mot de passe ou <a href="' + SR + '/?h">aller ร  la page d\'accueil</a>', + "p1": "403 interdit  ~โ”ปโ”โ”ป", + "q1": 'utiliser un mot de passe ou <a href="' + SR + '/?h">aller ร  la page d\'accueil</a>', + "r1": "aller ร  la page d\'accueil", + ".s1": "rescanner", + "t1": "action", + "u2": "temps รฉcoulรฉ depuis la derniรจre รฉcriture sur le serveur$N(tรฉlรฉchargement/renommage/...)$N$N17j = 17 jours$N1h23 = 1 heure 23 minutes$N4m56 = 4 minutes 56 secondes", + "v1": "connecter", + "v2": "utilisez ce serveur en tant que disque dur local", + "w1": "passer ร  https", + "x1": "changer mot de passe", + "y1": "modifier les partages", + "z1": "dรฉverrouiller ce partage :", + "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.", + "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", + }, "grc": { "a1": "ฮฑฮฝฮฑฮฝฮญฯ‰ฯƒฮท", "b1": "ฮณฮตฮนฮฑ ฯƒฮฟฯ… ฮพฮญฮฝฮต!   <small>(ฮดฮตฮฝ ฮตฮฏฯƒฮฑฮน ฯƒฯ…ฮฝฮดฮตฮดฮตฮผฮญฮฝฮฟฯ‚)</small>", @@ -234,6 +306,11 @@ var Ls = { "j1": "ฮท ฮตฮฝฮตฯฮณฮฟฯ€ฮฟฮฏฮทฯƒฮท ฯ„ฮฟฯ… k304 ฮธฮฑ ฮฑฯ€ฮฟฯƒฯ…ฮฝฮดฮญฯƒฮตฮน ฯ„ฮฟ ฯ€ฯฯŒฮณฯฮฑฮผฮผฮฑ ฯ€ฮตฮปฮฌฯ„ฮท ฯƒฮฟฯ… ฯƒฮต ฮบฮฌฮธฮต HTTP 304, ฮบฮฌฯ„ฮน ฯ€ฮฟฯ… ฮผฯ€ฮฟฯฮตฮฏ ฮฝฮฑ ฮฑฯ€ฮฟฯ„ฯฮญฯˆฮตฮน ฮบฮฌฯ€ฮฟฮนฮฑ ฯ€ฯฮฟฮฒฮปฮทฮผฮฑฯ„ฮนฮบฮฌ proxies ฮฑฯ€ฯŒ ฯ„ฮฟ ฮฝฮฑ ฮบฮฟฮปฮปฮฌฮฝฮต (ฮฝฮฑ ฮผฮทฮฝ ฯ†ฮฟฯฯ„ฯŽฮฝฮฟฯ…ฮฝ ฮพฮฑฯ†ฮฝฮนฮบฮฌ ฯƒฮตฮปฮฏฮดฮตฯ‚), <em>ฮฑฮปฮปฮฌ</em> ฮธฮฑ ฮบฮฌฮฝฮตฮน ฯ„ฮฑ ฯ€ฯฮฌฮณฮผฮฑฯ„ฮฑ, ฮณฮตฮฝฮนฮบฮฌ ฯ€ฮนฮฟ ฮฑฯฮณฮฌ", "k1": "ฮตฯ€ฮฑฮฝฮฑฯ†ฮฟฯฮฌ ฯฯ…ฮธฮผฮฏฯƒฮตฯ‰ฮฝ ฯƒฯ„ฮฟ ฯ€ฯฯŒฮณฯฮฑฮผฮผฮฑ ฯ€ฮตฮปฮฌฯ„ฮท", "l1": "ฯƒฯ…ฮฝฮดฮญฯƒฮฟฯ… ฮณฮนฮฑ ฯ€ฮตฯฮนฯƒฯƒฯŒฯ„ฮตฯฮฑ:", + "ls3": "ฯƒฯฮฝฮดฮตฯƒฮท", //m + "lu4": "ฯŒฮฝฮฟฮผฮฑ ฯ‡ฯฮฎฯƒฯ„ฮท", //m + "lp4": "ฮบฯ‰ฮดฮนฮบฯŒฯ‚ ฯ€ฯฯŒฯƒฮฒฮฑฯƒฮทฯ‚", //m + "lo3": "ฮฑฯ€ฮฟฯƒฯฮฝฮดฮตฯƒฮท ฯ„ฮฟฯ… โ€œ{0}โ€ ฮฑฯ€ฯŒ ฯ€ฮฑฮฝฯ„ฮฟฯ", //m + "lo2": "ฮฑฯ…ฯ„ฯŒ ฮธฮฑ ฯ„ฮตฯฮผฮฑฯ„ฮฏฯƒฮตฮน ฯ„ฮท ฯƒฯ…ฮฝฮตฮดฯฮฏฮฑ ฯƒฮต ฯŒฮปฮฟฯ…ฯ‚ ฯ„ฮฟฯ…ฯ‚ ฯ€ฮตฯฮนฮทฮณฮทฯ„ฮญฯ‚", //m "m1": "ฮบฮฑฮปฯŽฯ‚ ฮฎฯฮธฮตฯ‚,", "n1": "404 ฮดฮตฮฝ ฮฒฯฮญฮธฮทฮบฮต  โ”( ยด -`)โ”Œ", "o1": 'ยดฮท ฮผฮฎฯ€ฯ‰ฯ‚ ฮดฮตฮฝ ฮญฯ‡ฮตฮนฯ‚ ฯ€ฯฯŒฯƒฮฒฮฑฯƒฮท -- ฮดฮฟฮบฮฏฮผฮฑฯƒฮต ฮญฮฝฮฑฮฝ ฮบฯ‰ฮดฮนฮบฯŒ <a href="' + SR + '/?h">ฯ€ฮฎฮณฮฑฮนฮฝฮต ฯƒฯ„ฮทฮฝ ฮฑฯฯ‡ฮนฮบฮฎ</a>', @@ -275,6 +352,11 @@ var Ls = { "j1": "k304 interrompe la connessione per ogni HTTP 304. Questo aiuta contro alcuni proxy difettosi che possono bloccarsi o smettere improvvisamente di caricare pagine, ma riduce notevolmente le prestazioni", "k1": "resetta impostazioni", "l1": "accedi:", + "ls3": "accedi", //m + "lu4": "nome utente", //m + "lp4": "password", //m + "lo3": "disconnetti โ€œ{0}โ€ ovunque", //m + "lo2": "questo terminerร  la sessione su tutti i browser", //m "m1": "bentornato,", "n1": "404: file non trovato  โ”( ยด -`)โ”Œ", "o1": "oppure forse non hai accesso? prova una password o <a href=\"SR/?h\">torna alla home</a>", @@ -301,6 +383,53 @@ var Ls = { "af1": "mostra i file caricati di recente", "ag1": "mostra utenti IdP conosciuti" }, + "kor": { + "a1": "์ƒˆ๋กœ๊ณ ์นจ", + "b1": "์–ด์ด ์นœ๊ตฌ! ์ฒ˜์Œ ๋ณด๋Š” ์–ผ๊ตด์ธ๋ฐ?   <small>(๋กœ๊ทธ์ธ๋˜์–ด ์žˆ์ง€ ์•Š์Šต๋‹ˆ๋‹ค)</small>", + "c1": "๋กœ๊ทธ์•„์›ƒ", + "d1": "์Šคํƒ ๋คํ”„ํ•˜๊ธฐ", + "d2": "๋ชจ๋“  ํ™œ์„ฑ ์Šค๋ ˆ๋“œ์˜ ์ƒํƒœ๋ฅผ ํ‘œ์‹œํ•ฉ๋‹ˆ๋‹ค", + "e1": "์„ค์ • ๋‹ค์‹œ ๋ถˆ๋Ÿฌ์˜ค๊ธฐ", + "e2": "์„ค์ • ํŒŒ์ผ(๊ณ„์ •/๋ณผ๋ฅจ/๋ณผ๋ฅจ ํ”Œ๋ž˜๊ทธ)์„ ๋‹ค์‹œ ๋ถˆ๋Ÿฌ์˜ค๊ณ ,$N๋ชจ๋“  e2ds ๋ณผ๋ฅจ์„ ๋‹ค์‹œ ์Šค์บ”ํ•ฉ๋‹ˆ๋‹ค$N$N์ฐธ๊ณ : ์ „์—ญ ์„ค์ •์— ๋Œ€ํ•œ ๋ณ€๊ฒฝ ์‚ฌํ•ญ์€$N์ ์šฉํ•˜๋ ค๋ฉด ์ „์ฒด ์žฌ์‹œ์ž‘์ด ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค", + "f1": "ํƒ์ƒ‰ ๊ฐ€๋Šฅํ•œ ๊ณณ:", + "g1": "์—…๋กœ๋“œ ๊ฐ€๋Šฅํ•œ ๊ณณ:", + "cc1": "๊ธฐํƒ€ ํ•ญ๋ชฉ:", + "h1": "k304 ๋น„ํ™œ์„ฑํ™”", + "i1": "k304 ํ™œ์„ฑํ™”", + "j1": "k304๋ฅผ ํ™œ์„ฑํ™”ํ•˜๋ฉด ๋ชจ๋“  HTTP 304 ์‘๋‹ต ์‹œ ํด๋ผ์ด์–ธํŠธ ์—ฐ๊ฒฐ์ด ๋Š์–ด์ง‘๋‹ˆ๋‹ค. ์ด๋Š” ์ผ๋ถ€ ํ”„๋ก์‹œ๊ฐ€ ๋ฉˆ์ถ”๋Š” ํ˜„์ƒ(๊ฐ‘์ž๊ธฐ ํŽ˜์ด์ง€๊ฐ€ ๋กœ๋“œ๋˜์ง€ ์•Š์Œ)์„ ๋ฐฉ์ง€ํ•  ์ˆ˜ ์žˆ์ง€๋งŒ, <em>๋Œ€์‹  ์ „๋ฐ˜์ ์ธ ์†๋„๋Š” ๋А๋ ค์ง‘๋‹ˆ๋‹ค.</em>", + "k1": "ํด๋ผ์ด์–ธํŠธ ์„ค์ • ์ดˆ๊ธฐํ™”", + "l1": "๋กœ๊ทธ์ธํ•˜๊ธฐ:", + "ls3": "๋กœ๊ทธ์ธ", //m + "lu4": "์‚ฌ์šฉ์ž ์ด๋ฆ„", //m + "lp4": "๋น„๋ฐ€๋ฒˆํ˜ธ", //m + "lo3": "{0}์„(๋ฅผ) ๋ชจ๋“  ๊ณณ์—์„œ ๋กœ๊ทธ์•„์›ƒ", //m + "lo2": "์ด ์ž‘์—…์€ ๋ชจ๋“  ๋ธŒ๋ผ์šฐ์ €์—์„œ ์„ธ์…˜์„ ์ข…๋ฃŒํ•ฉ๋‹ˆ๋‹ค", //m + "m1": "๋˜ ์˜ค์…จ๋„ค์š”,", + "n1": "404 ์ฐพ์„ ์ˆ˜ ์—†์Œ  โ”( ยด -`)โ”Œ", + "o1": "๋˜๋Š” ์ ‘๊ทผ ๊ถŒํ•œ์ด ์—†์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ์ž…๋ ฅํ•˜๊ฑฐ๋‚˜ <a href=\"' + SR + '/?h\">ํ™ˆ์œผ๋กœ ์ด๋™</a>ํ•˜์„ธ์š”", + "p1": "403 ์ ‘๊ทผ ๊ธˆ์ง€  ~โ”ปโ”โ”ป", + "q1": "๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ์ž…๋ ฅํ•˜๊ฑฐ๋‚˜ <a href=\"' + SR + '/?h\">ํ™ˆ์œผ๋กœ ์ด๋™</a>ํ•˜์„ธ์š”", + "r1": "ํ™ˆ์œผ๋กœ ์ด๋™", + ".s1": "๋‹ค์‹œ ์Šค์บ”", + "t1": "์ž‘์—…", + "u2": "์„œ๋ฒ„์— ๋งˆ์ง€๋ง‰์œผ๋กœ ์“ฐ๊ธฐ ์ž‘์—…์„ ํ•œ ํ›„ ๊ฒฝ๊ณผ๋œ ์‹œ๊ฐ„$N(์—…๋กœ๋“œ / ์ด๋ฆ„ ๋ณ€๊ฒฝ / ๋“ฑ๋“ฑ...)$N$N17d = 17์ผ$N1h23 = 1์‹œ๊ฐ„ 23๋ถ„$N4m56 = 4๋ถ„ 56์ดˆ", + "v1": "์—ฐ๊ฒฐ", + "v2": "์ด ์„œ๋ฒ„๋ฅผ ๋กœ์ปฌ ํ•˜๋“œ๋””์Šคํฌ์ฒ˜๋Ÿผ ์‚ฌ์šฉํ•˜๊ธฐ", + "w1": "HTTPS๋กœ ์ „ํ™˜", + "x1": "๋น„๋ฐ€๋ฒˆํ˜ธ ๋ณ€๊ฒฝ", + "y1": "๊ณต์œ  ์„ค์ •", + "z1": "์ด ๊ณต์œ  ์ž ๊ธˆํ•ด์ œ:", + "ta1": "์ƒˆ ๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ๋จผ์ € ์ž…๋ ฅํ•˜์„ธ์š”", + "ta2": "์ƒˆ ๋น„๋ฐ€๋ฒˆํ˜ธ ํ™•์ธ์„ ์œ„ํ•ด ๋‹ค์‹œ ์ž…๋ ฅํ•˜์„ธ์š”:", + "ta3": "์˜คํƒ€๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. ๋‹ค์‹œ ์‹œ๋„ํ•ด์ฃผ์„ธ์š”", + "aa1": "์ˆ˜์‹  ์ค‘์ธ ํŒŒ์ผ:", + "ab1": "no304 ๋น„ํ™œ์„ฑํ™”", + "ac1": "no304 ํ™œ์„ฑํ™”", + "ad1": "no304๋ฅผ ํ™œ์„ฑํ™”ํ•˜๋ฉด ๋ชจ๋“  ์บ์‹ฑ์ด ๋น„ํ™œ์„ฑํ™”๋ฉ๋‹ˆ๋‹ค. k304๋กœ ์ถฉ๋ถ„ํ•˜์ง€ ์•Š์€ ๊ฒฝ์šฐ ์‹œ๋„ํ•ด๋ณด์„ธ์š”. ๋„คํŠธ์›Œํฌ ํŠธ๋ž˜ํ”ฝ์ด ๋Œ€๋Ÿ‰์œผ๋กœ ๋‚ญ๋น„๋ฉ๋‹ˆ๋‹ค!", + "ae1": "ํ™œ์„ฑ ๋‹ค์šด๋กœ๋“œ:", + "af1": "์ตœ๊ทผ ์—…๋กœ๋“œ ๋ณด๊ธฐ", + "ag1": "IdP ์บ์‹œ ๋ณด๊ธฐ" + }, "nld": { "a1": "Update", "b1": "Hallo, hoe gaat het met jou?   <small>(Je bent niet ingelogd)</small>", @@ -317,6 +446,11 @@ var Ls = { "j1": "k304 verbreekt de verbinding voor elke HTTP 304. Dit helpt tegen bepaalde proxy servers die kunnen vastlopen/plotseling stoppen met het laden van pagina's, maar het vermindert ook de prestaties aanzienlijk", "k1": "Instellingen resetten", "l1": "Inloggen:", + "ls3": "inloggen", //m + "lu4": "gebruikersnaam", //m + "lp4": "wachtwoord", //m + "lo3": "โ€œ{0}โ€ overal afmelden", //m + "lo2": "dit zal de sessie in alle browsers beรซindigen", //m "m1": "Welkom terug,", "n1": "404: bestand bestaat niet  โ”( ยด -`)โ”Œ", "o1": 'of misschien heb je geen toegang? probeer een wachtwoord of <a href="' + SR + '/?h">ga naar startscherm</a>', @@ -343,6 +477,147 @@ var Ls = { "af1": "Recent geรผploade bestanden weergeven", "ag1": "Bekende IdP-gebruikers weergeven", }, + "nno": { + "a1": "oppdatรฉr", + "b1": "heisann   <small>(du er ikkje logga inn)</small>", + "c1": "logg ut", + "d1": "tilstand", + "d2": "vis tilstanden รฅt alle trรฅdar", + "e1": "last innst.", + "e2": "les inn konfigurasjonsfiler pรฅ nytt$N(kontoer, volum, volumbrytarar)$Nog kartlegg alle e2ds-volum$N$Nmerk: endringer i globale parametrar$Nkrev ein full restart for รฅ gjelde", + "f1": "du kan sjรฅ pรฅ:", + "g1": "du kan laste opp รฅt:", + "cc1": "brytarar og slikt:", + "h1": "skru av k304", + "i1": "skru pรฅ k304", + "j1": "k304 bryt tilkoplinga for kvar HTTP 304. Dette hjelp mot visse mellomtjenarar som kan sette seg fast / plutselig sluttar รฅ laste sider, men det sett รณg ytinga ned betydelig", + "k1": "nullstill innstillinger", + "l1": "logg inn:", + "ls3": "logg inn", + "lu4": "brukarnamn", + "lp4": "passord", + "lo3": "logg ut โ€œ{0}โ€ overalt", + "lo2": "avslutt รธkta pรฅ alle nettlesarar", + "m1": "velkomen attende,", + "n1": "404: filen finnast ikkje  โ”( ยด -`)โ”Œ", + "o1": 'eller kanskje du ikkje har hรธve? prรธv eit passord eller <a href="' + SR + '/?h">gรฅ heim</a>', + "p1": "403: tilgang nektet  ~โ”ปโ”โ”ป", + "q1": 'prรธv eit passord eller <a href="' + SR + '/?h">gรฅ heim</a>', + "r1": "gรฅ heim", + ".s1": "kartlegg", + "t1": "handling", + "u2": "tid sidan nokon sist skreiv รฅt serveren$N( opplastning / namnendring / ... )$N$N17d = 17 dagar$N1h23 = 1 time 23 minutt$N4m56 = 4 minutt 56 sekund", + "v1": "kople รฅt", + "v2": "bruk denne serveren som ein lokal harddisk", + "w1": "bytt รฅt https", + "x1": "bytt passord", + "y1": "dine delinger", + "z1": "lรฅs opp omrรฅde:", + "ta1": "du mรฅ skrive eit nytt passord fรธrst", + "ta2": "gjenta for รฅ stadfeste nytt passord:", + "ta3": "fant ein skrivefeil; vennligst prรธv igjen", + "aa1": "innkommande:", + "ab1": "skru av no304", + "ac1": "skru pรฅ no304", + "ad1": "no304 stoppar all bruk av cache. Hvis ikkje k304 var nok, prรธv denne. Vil mangedoble dataforbruk!", + "ae1": "utgรฅende:", + "af1": "vis nylig opplasta filer", + "ag1": "vis kjente IdP-brukarar", + }, + "pol": { + "a1": "odล›wieลผ", + "b1": "witaj, nieznajomy   <small>(nie jesteล› zalogowany)</small>", + "c1": "wyloguj siฤ™", + "d1": "zrzut stosu", + "d2": "pokazuje status wszystkich aktywnych wฤ…tkรณw", + "e1": "przeล‚aduj konfiguracjฤ™", + "e2": "przeล‚aduj pliki konfiguracyjne (konta/wolumeny/flagi wolumenรณw),$Ni przeskanuje wszystkie wolumeny e2ds$N$Nnotka: zmiany konfiguracji globalnej$Nwymagajฤ… peล‚nego uruchomienia ponownie serwera, aby zaczฤ™ล‚y obowiฤ…zywaฤ‡", + "f1": "moลผesz przeglฤ…daฤ‡:", + "g1": "moลผesz przesyล‚aฤ‡ do:", + "cc1": "inne:", + "h1": "wyล‚ฤ…cz k304", + "i1": "wล‚ฤ…cz k304", + "j1": "wล‚ฤ…czenie k304 bฤ™dzie odล‚ฤ…czaล‚o klienta przy kaลผdorazowym otrzymaniu kodu HTTP 304, co moลผe zapobiec wieszaniu siฤ™ wadliwych proxy, <em>ale</em> spowolni ogรณlne dziaล‚anie", + "k1": "zresetuj ustawienia klienta", + "l1": "zaloguj siฤ™ po wiฤ™cej:", + "ls3": "zaloguj siฤ™", //m + "lu4": "nazwa uลผytkownika", //m + "lp4": "hasล‚o", //m + "lo3": "wyloguj โ€œ{0}โ€ wszฤ™dzie", //m + "lo2": "spowoduje to zakoล„czenie sesji we wszystkich przeglฤ…darkach", //m + "m1": "Witaj,", + "n1": "404 nie znaleziono  โ”( ยด -`)โ”Œ", + "o1": 'lub moลผesz nie mieฤ‡ dostฤ™pu -- sprรณbuj wprowadziฤ‡ hasล‚o lub <a href="' + SR + '/?h">przejdลบ do strony gล‚รณwnej</a>', + "p1": "403 odmowa dostฤ™pu  ~โ”ปโ”โ”ป", + "q1": 'uลผyj hasล‚a lub <a href="' + SR + '/?h">przejdลบ do strony gล‚รณwnej</a>', + "r1": "idลบ do strony gล‚รณwnej", + ".s1": "przeskanuj ponownie", + "t1": "akcje", + "u2": "czas od ostatniej interakcji z serwerem$N( przesyล‚ania / zmiany nazwy / ... )$N$N17d = 17 dni$N1h23 = 1 godzina 23 minuty$N4m56 = 4 minuty 56 sekund", + "v1": "poล‚ฤ…cz", + "v2": "uลผywaj tego serwera jako dysku lokalnego", + "w1": "przejdลบ na HTTPS", + "x1": "zmieล„ hasล‚o", + "y1": "edytuj udostฤ™pnione", + "z1": "odblokuj udostฤ™pnienie:", + "ta1": "najpierw wprowadลบ nowe hasล‚o", + "ta2": "powtรณrz hasล‚o dla potwierdzenia:", + "ta3": "znaleziono literรณwkฤ™, sprรณbuj ponownie", + "aa1": "pliki przychodzฤ…ce:", + "ab1": "wyล‚ฤ…cz no304", + "ac1": "wล‚ฤ…cz no304", + "ad1": "wล‚ฤ…czenie no304 wyล‚ฤ…czy przechowywanie jakiejkolwiek pamiฤ™ci podrฤ™cznej. Zmarnuje to olbrzymiฤ… iloล›ฤ‡ ruchu sieciowego!", + "ae1": "trwajฤ…ce pobierania:", + "af1": "pokaลผ ostatnio przesล‚ane pliki", + "ag1": "pokaลผ znanych uลผytkownikรณw IdP", + }, + "por": { + "a1": "atualizar", + "b1": "olรก   <small>(vocรช nรฃo estรก logado)</small>", + "c1": "encerrar sessรฃo", + "d1": "despejar o estado da pilha", + "d2": "mostra o estado de todos os threads ativos", + "e1": "recarregar configuraรงรฃo", + "e2": "recarregar arquivos de configuraรงรฃo (contas/volumes/indicadores de volume),$N e reescanear todos os volumes e2ds$N$Nnota: qualquer alteraรงรฃo na configuraรงรฃo global$N requer uma reinicializaรงรฃo completa para ter efeito", + "f1": "vocรช pode navegar:", + "g1": "vocรช pode fazer upload para:", + "cc1": "outras coisas:", + "h1": "desativar k304", + "i1": "ativar k304", + "j1": "ativar k304 irรก desconectar seu cliente em cada HTTP 304, o que pode evitar que alguns proxies com erros fiquem presos (parando de carregar pรกginas de repente), <em>mas</em> tambรฉm irรก desacelerar as coisas em geral", + "k1": "redefinir config. de cliente", + "l1": "faรงa login para mais:", + "ls3": "fazer login", + "lu4": "nome de usuรกrio", + "lp4": "senha", + "lo3": "encerrar sessรฃo de \"{0}\" em todos os lugares", + "lo2": "isso irรก encerrar a sessรฃo em todos os navegadores", + "m1": "bem-vindo de volta,", + "n1": "404 nรฃo encontrado  โ”( ยด -`)โ”Œ", + "o1": "ou talvez vocรช nรฃo tenha acesso? -- tente com uma senha ou volte para o inรญcio", + "p1": "403 proibido  ~โ”ปโ”โ”ป", + "q1": "use uma senha ou volte para o inรญcio", + "r1": "ir para o inรญcio", + ".s1": "reescanear", + "t1": "aรงรฃo", + "u2": "tempo desde a รบltima gravaรงรฃo no servidor$N( upload / renomear / ... )$N$N17d = 17 dias$N1h23 = 1 hora 23 minutos$N4m56 = 4 minutos 56 segundos", + "v1": "conectar", + "v2": "usar este servidor como um disco rรญgido local", + "w1": "mudar para https", + "x1": "mudar senha", + "y1": "editar recursos compartilhados", + "z1": "desbloquear este recurso compartilhado:", + "ta1": "primeiro digite sua nova senha", + "ta2": "repita para confirmar a nova senha:", + "ta3": "hรก um erro; por favor, tente novamente", + "aa1": "arquivos de entrada:", + "ab1": "desativar no304", + "ac1": "ativar no304", + "ad1": "ativar no304 irรก desabilitar todo o armazenamento em cache; tente isso se k304 nรฃo for suficiente. Isso irรก desperdiรงar uma grande quantidade de trรกfego de rede!", + "ae1": "downloads ativos:", + "af1": "mostrar uploads recentes", + "ag1": "mostrar usuรกrios IdP conhecidos" + }, "spa": { "a1": "actualizar", "b1": "hola   <small>(no has iniciado sesiรณn)</small>", @@ -359,6 +634,11 @@ var Ls = { "j1": "activar k304 desconectarรก tu cliente en cada HTTP 304, lo que puede evitar que algunos proxies con errores se atasquen (dejando de cargar pรกginas de repente), <em>pero</em> tambiรฉn ralentizarรก las cosas en general", "k1": "restablecer config. de cliente", "l1": "inicia sesiรณn para mรกs:", + "ls3": "iniciar sesiรณn", //m + "lu4": "nombre de usuario", //m + "lp4": "contraseรฑa", //m + "lo3": "cerrar sesiรณn de โ€œ{0}โ€ en todas partes", //m + "lo2": "esto finalizarรก la sesiรณn en todos los navegadores", //m "m1": "bienvenido de nuevo,", "n1": "404 no encontrado  โ”( ยด -`)โ”Œ", "o1": 'ยฟo quizรกs no tienes acceso? -- prueba con una contraseรฑa o <a href=\"' + SR + '/?h\">vuelve al inicio</a>', @@ -385,6 +665,53 @@ var Ls = { "af1": "mostrar subidas recientes", "ag1": "mostrar usuarios IdP conocidos" }, + "swe": { + "a1": "uppdatera", + "b1": "tjena frรคmling   <small>(du รคr inte inloggad)</small>", + "c1": "logga ut", + "d1": "dumpa stacken", + "d2": "visar tillstรฅndet pรฅ alla aktiva trรฅdar", + "e1": "ladda om konfig.", + "e2": "ladda om konfigurationsfiler (konton/volymer/volflaggor),$Noch skanna om alla e2ds-volymer$N$Nobs.: รคndrade globala instรคllningar$Nkrรคver en fullstรคndig omstart", + "f1": "du kan blรคddra:", + "g1": "du kan ladda upp till:", + "cc1": "annat:", + "h1": "avaktivera k304", + "i1": "aktivera k304", + "j1": "med k304 aktiverad kommer klienten att koppla bort sig vid varje HTTP 304-fel, vilket kan hindra vissa buggiga proxyservrar frรฅn att fastna (sidor slutar ladda), <em>men</em> saker kommer ocksรฅ att bli lรฅngsammare i allmรคnhet", + "k1": "รฅterstรคll klientinstรคllningar", + "l1": "logga in fรถr att se mer:", + "ls3": "logga in", //m + "lu4": "anvรคndarnamn", //m + "lp4": "lรถsenord", //m + "lo3": "logga ut โ€œ{0}โ€ รถverallt", //m + "lo2": "avsluta sessionen i alla webblรคsare", //m + "m1": "vรคlkommen tillbaka,", + "n1": "404 hittades inte  โ”( ยด -`)โ”Œ", + "o1": 'eller sรฅ har du kanske inte tillgรฅng -- prova ett lรถsenord eller <a href="' + SR + '/?h">รฅk hem</a>', + "p1": "403 nekat  ~โ”ปโ”โ”ป", + "q1": 'anvรคnd ett lรถsenord eller <a href="' + SR + '/?h">รฅk hem</a>', + "r1": "รฅk hem", + ".s1": "skanna om", + "t1": "รฅtgรคrd", + "u2": "tid sedan senaste serverskrivning$N( uppladdning / namnbyte / ... )$N$N17d = 17 dagar$N1h23 = 1 timme 23 minuter$N4m56 = 4 minuter 56 sekunder", + "v1": "koppla upp", + "v2": "anvรคnd denna server som en lokal disk", + "w1": "byt till https", + "x1": "byt lรถsenord", + "y1": "redigera utdelningar", + "z1": "lรฅs upp denna utdelning:", + "ta1": "fyll i ditt nya lรถsenord", + "ta2": "upprepa det nya lรถsenordet:", + "ta3": "det blev fel; vรคnligen fรถrsรถk igen", + "aa1": "inkommande filer:", + "ab1": "avaktivera no304", + "ac1": "aktivera no304", + "ad1": "detta stรคnger av all cachning; prova detta om k304 inte rรคckte till. Detta kommer att slรถsa enorma mรคngder nรคtverkstrafik!", + "ae1": "aktiva nedladdningar:", + "af1": "visa senaste uppladdningar", + "ag1": "visa idp-cache" + }, "ukr": { "a1": "ะพะฝะพะฒะธั‚ะธ", "b1": "ะฟั€ะธะฒั–ั‚ะธะบ, ะฝะตะทะฝะฐะนะพะผั†ัŽ   <small>(ะฒะธ ะฝะต ะฐะฒั‚ะพั€ะธะทะพะฒะฐะฝั–)</small>", @@ -401,6 +728,11 @@ var Ls = { "j1": "ัƒะฒั–ะผะบะฝะตะฝะฝั k304 ะฑัƒะดะต ะฒั–ะดะบะปัŽั‡ะฐั‚ะธ ะฒะฐัˆ ะบะปั–ั”ะฝั‚ ะฟั€ะธ ะบะพะถะฝะพะผัƒ HTTP 304, ั‰ะพ ะผะพะถะต ะทะฐะฟะพะฑั–ะณั‚ะธ ะทะฐะฒะธัะฐะฝะฝัŽ ะดะตัะบะธั… ะณะปัŽั‡ะฝะธั… ะฟั€ะพะบัั– (ั€ะฐะฟั‚ะพะฒะพ ะฟะตั€ะตัั‚ะฐัŽั‚ัŒ ะทะฐะฒะฐะฝั‚ะฐะถัƒะฒะฐั‚ะธ ัั‚ะพั€ั–ะฝะบะธ), <em>ะฐะปะต</em> ั†ะต ั‚ะฐะบะพะถ ะทั€ะพะฑะธั‚ัŒ ัƒัะต ะฟะพะฒั–ะปัŒะฝั–ัˆะธะผ ะทะฐะณะฐะปะพะผ", "k1": "ัะบะธะฝัƒั‚ะธ ะฝะฐะปะฐัˆั‚ัƒะฒะฐะฝะฝั ะบะปั–ั”ะฝั‚ะฐ", "l1": "ะฐะฒั‚ะพั€ะธะทัƒะนั‚ะตััŒ ะดะปั ั–ะฝัˆะธั… ะพะฟั†ั–ะน:", + "ls3": "ัƒะฒั–ะนั‚ะธ", //m + "lu4": "ั–ะผ'ั ะบะพั€ะธัั‚ัƒะฒะฐั‡ะฐ", //m + "lp4": "ะฟะฐั€ะพะปัŒ", //m + "lo3": "ะฒะธะนั‚ะธ ะท ะพะฑะปั–ะบะพะฒะพะณะพ ะทะฐะฟะธััƒ โ€œ{0}โ€ ะฒััŽะดะธ", //m + "lo2": "ั†ะต ะทะฐะฒะตั€ัˆะธั‚ัŒ ัะตะฐะฝั ัƒ ะฒัั–ั… ะฑั€ะฐัƒะทะตั€ะฐั…", //m "m1": "ะท ะฟะพะฒะตั€ะฝะตะฝะฝัะผ,", "n1": "404 ะฝะต ะทะฝะฐะนะดะตะฝะพ  โ”( ยด -`)โ”Œ", "o1": 'ะฐะฑะพ ัƒ ะฒะฐั ะฝะตะผะฐั” ะดะพัั‚ัƒะฟัƒ -- ัะฟั€ะพะฑัƒะนั‚ะต ะฐะฒั‚ะพั€ะธะทัƒะฒะฐั‚ะธัั ะฐะฑะพ <a href="' + SR + '/?h">ะฟะพะฒะตั€ะฝัƒั‚ะธัั ะฝะฐ ะณะพะปะพะฒะฝัƒ</a>', @@ -443,6 +775,11 @@ var Ls = { "j1": "ะฒะบะปัŽั‡ั‘ะฝะฝั‹ะน k304 ะฑัƒะดะตั‚ ะพั‚ะบะปัŽั‡ะฐั‚ัŒ ะฒะฐั ะฟั€ะธ ะฟะพะปัƒั‡ะตะฝะธะธ HTTP 304, ั‡ั‚ะพ ะผะพะถะตั‚ ะฟะพะผะพั‡ัŒ ะฟั€ะธ ั€ะฐะฑะพั‚ะต ั ะฝะตะบะพั‚ะพั€ั‹ะผะธ ะณะปัŽั‡ะฝั‹ะผะธ ะฟั€ะพะบัะธ (ะฟะตั€ะตัั‚ะฐัŽั‚ ะทะฐะณั€ัƒะถะฐั‚ัŒัั ัั‚ั€ะฐะฝะธั†ั‹), <em>ะฝะพ</em> ัั‚ะพ ั‚ะฐะบะถะต ัะดะตะปะฐะตั‚ ั€ะฐะฑะพั‚ัƒ ะบะปะธะตะฝั‚ะฐ ะผะตะดะปะตะฝะฝะตะต", "k1": "ัะฑั€ะพัะธั‚ัŒ ะปะพะบะฐะปัŒะฝั‹ะต ะฝะฐัั‚ั€ะพะนะบะธ", "l1": "ะฐะฒั‚ะพั€ะธะทัƒะนั‚ะตััŒ ะดะปั ะดั€ัƒะณะธั… ะพะฟั†ะธะน:", + "ls3": "ะฒะพะนั‚ะธ", //m + "lu4": "ะธะผั ะฟะพะปัŒะทะพะฒะฐั‚ะตะปั", //m + "lp4": "ะฟะฐั€ะพะปัŒ", //m + "lo3": "ะฒั‹ะนั‚ะธ ะธะท โ€œ{0}โ€ ะฒะตะทะดะต", //m + "lo2": "ัั‚ะพ ะทะฐะฒะตั€ัˆะธั‚ ัะตะฐะฝั ะฒะพ ะฒัะตั… ะฑั€ะฐัƒะทะตั€ะฐั…", //m "m1": "ั ะฒะพะทะฒั€ะฐั‰ะตะฝะธะตะผ,", "n1": "404 ะฝะต ะฝะฐะนะดะตะฝะพ  โ”( ยด -`)โ”Œ", "o1": 'ะธะปะธ ัƒ ะฒะฐั ะฝะตั‚ ะดะพัั‚ัƒะฟะฐ -- ะฟะพะฟั€ะพะฑัƒะนั‚ะต ะฐะฒั‚ะพั€ะธะทะพะฒะฐั‚ัŒัั ะธะปะธ <a href="' + SR + '/?h">ะฒะตั€ะฝัƒั‚ัŒัั ะฝะฐ ะณะปะฐะฒะฝัƒัŽ</a>', @@ -518,6 +855,8 @@ if (window.langmod) var d = Ls[sread("cpp_lang", Object.keys(Ls)) || lang] || Ls.eng || Ls.nor || Ls.chi; +d.wb = d.w; + for (var k in (d || {})) { var f = k.slice(-1), i = k.slice(0, -1), @@ -528,10 +867,17 @@ for (var k in (d || {})) { o[a].innerHTML = d[k]; else if (f == 2) o[a].setAttribute("tt", d[k]); + else if (f == 3) + o[a].setAttribute("value", d[k]); + else if (f == 4) + o[a].setAttribute("placeholder", " " + d[k]); } +var o1 = ebi('lo'), o2 = ebi('un'); +if (o1 && o2 && d.lo3) + o1.setAttribute("value", d.lo3.format(o2.textContent)); try { - if (is_idp) { + if (is_idp > 1) { var z = ['#l+div', '#l', '#c']; for (var a = 0; a < z.length; a++) QS(z[a]).style.display = 'none'; @@ -541,14 +887,16 @@ catch (ex) { } tt.init(); var o = QS('input[name="uname"]') || QS('input[name="cppwd"]'); -if (!ebi('c') && o.offsetTop + o.offsetHeight < window.innerHeight) +if (o && !MOBILE && !ebi('c') && o.offsetTop + o.offsetHeight < window.innerHeight) o.focus(); o = ebi('u'); if (o && /[0-9]+$/.exec(o.innerHTML)) o.innerHTML = shumantime(o.innerHTML); -ebi('uhash').value = '' + location.hash; +o = ebi('uhash') +if (o) + o.value = '' + location.hash; if (/\&re=/.test('' + location)) ebi('a').className = 'af g'; diff --git a/copyparty/web/svcs.html b/copyparty/web/svcs.html index 26fd5267..f63e670f 100644 --- a/copyparty/web/svcs.html +++ b/copyparty/web/svcs.html @@ -10,7 +10,7 @@ <link rel="stylesheet" media="screen" href="{{ r }}/.cpr/splash.css?_={{ ts }}"> <link rel="stylesheet" media="screen" href="{{ r }}/.cpr/ui.css?_={{ ts }}"> <style>ul{padding-left:1.3em}li{margin:.4em 0}.txa{float:right;margin:0 0 0 1em}</style> -{{ html_head }} +{{- html_head }} </head> <body> @@ -31,10 +31,10 @@ <br /> <span class="os win lin mac">placeholders:</span> <span class="os win"> - {% if accs %}<code><b id="pw0">{{ pw }}</b></code>=password, {% endif %}<code><b>W:</b></code>=mountpoint + {% if accs %}{% if un %}<code><b id="un0">{{ un }}</b></code>=username, <code><b id="up0">{{ unpw }}</b></code>=username:password, {% endif %}<code><b id="pw0">{{ pw }}</b></code>=password, {% endif %}<code><b>W:</b></code>=mountpoint </span> <span class="os lin mac"> - {% if accs %}<code><b id="pw0">{{ pw }}</b></code>=password, {% endif %}<code><b>mp</b></code>=mountpoint + {% if accs %}{% if un %}<code><b id="un0">{{ un }}</b></code>=username, <code><b id="up0">{{ unpw }}</b></code>=username:password, {% endif %}<code><b id="pw0">{{ pw }}</b></code>=password, {% endif %}<code><b>mp</b></code>=mountpoint </span> {% if accs %}<a href="#" id="setpw">use real password</a>{% endif %} <a href="#" id="qr">show qr</a> @@ -42,9 +42,9 @@ - {% if args.idp_h_usr %} + {% if args.have_idp_hdrs %} <p style="line-height:2em"><b>WARNING:</b> this server is using IdP-based authentication, so this stuff may not work as advertised. Depending on server config, these commands can probably only be used to access areas which don't require authentication, unless you auth using any non-IdP accounts defined in the copyparty config. Please see <a href="https://github.com/9001/copyparty/blob/hovudstraum/docs/idp.md#connecting-webdav-clients">the IdP docs</a></p> - {% endif %} + {%- endif %} @@ -54,50 +54,64 @@ <div class="os win"> <p>if you can, install <a href="https://winfsp.dev/rel/">winfsp</a>+<a href="https://downloads.rclone.org/rclone-current-windows-amd64.zip">rclone</a> and then paste this in cmd:</p> <pre> - rclone config create {{ aname }}-dav webdav url=http{{ s }}://{{ rip }}{{ hport }} vendor=owncloud pacer_min_sleep=0.01ms{% if accs %} user=k pass=<b>{{ pw }}</b>{% endif %} + rclone config create {{ aname }}-dav webdav url=http{{ s }}://{{ rip }}{{ hport }} vendor=owncloud pacer_min_sleep=0.01ms{% if accs %} user={{ b_un }} pass=<b>{{ pw }}</b>{% endif %} rclone mount --vfs-cache-mode writes --dir-cache-time 5s {{ aname }}-dav:{{ rvp }} <b>W:</b> </pre> <ul> - {% if s %} + {%- if s %} <li>running <code>rclone mount</code> on LAN (or just dont have valid certificates)? add <code>--no-check-certificate</code></li> - {% endif %} + {%- endif %} <li>old version of rclone? replace all <code>=</code> with <code> </code> (space)</li> </ul> <p>if you want to use the native WebDAV client in windows instead (slow and buggy), first run <a href="{{ r }}/.cpr/a/webdav-cfg.bat">webdav-cfg.bat</a> to remove the 47 MiB filesize limit (also fixes latency and password login), then connect:</p> <pre> + {%- if un %} + net use <b>w:</b> http{{ s }}://{{ ep }}/{{ rvp }}{% if accs %} <b>{{ pw }}</b> /user:{{ b_un }}{% endif %} + {%- else %} net use <b>w:</b> http{{ s }}://{{ ep }}/{{ rvp }}{% if accs %} k /user:<b>{{ pw }}</b>{% endif %} + {%- endif %} </pre> </div> <div class="os lin"> <p>rclone (v1.63 or later) is recommended:</p> <pre> - rclone config create {{ aname }}-dav webdav url=http{{ s }}://{{ rip }}{{ hport }} vendor=owncloud pacer_min_sleep=0.01ms{% if accs %} user=k pass=<b>{{ pw }}</b>{% endif %} + rclone config create {{ aname }}-dav webdav url=http{{ s }}://{{ rip }}{{ hport }} vendor=owncloud pacer_min_sleep=0.01ms{% if accs %} user={{ b_un }} pass=<b>{{ pw }}</b>{% endif %} rclone mount --vfs-cache-mode writes --dir-cache-time 5s {{ aname }}-dav:{{ rvp }} <b>mp</b> </pre> <ul> - {% if s %} + {%- if s %} <li>running <code>rclone mount</code> on LAN (or just dont have valid certificates)? add <code>--no-check-certificate</code></li> - {% endif %} + {%- endif %} <li>running <code>rclone mount</code> as root? add <code>--allow-other</code></li> <li>old version of rclone? replace all <code>=</code> with <code> </code> (space)</li> </ul> <p>alternatively use davfs2 (requires root, is slower, forgets lastmodified-timestamp on upload):</p> <pre> yum install davfs2 + {%- if un %} + {% if accs %}printf '%s\n' {{ b_un }} <b>{{ pw }}</b> | {% endif %}mount -t davfs -ouid=1000 http{{ s }}://{{ ep }}/{{ rvp }} <b>mp</b> + {%- else %} {% if accs %}printf '%s\n' <b>{{ pw }}</b> k | {% endif %}mount -t davfs -ouid=1000 http{{ s }}://{{ ep }}/{{ rvp }} <b>mp</b> + {%- endif %} </pre> + {%- if accs %} <p>make davfs2 automount on boot:</p> <pre> + {%- if un %} + printf '%s\n' "http{{ s }}://{{ ep }}/{{ rvp }} {{ b_un }} <b>{{ pw }}</b>" >> /etc/davfs2/secrets + {%- else %} printf '%s\n' "http{{ s }}://{{ ep }}/{{ rvp }} <b>{{ pw }}</b> k" >> /etc/davfs2/secrets + {%- endif %} printf '%s\n' "http{{ s }}://{{ ep }}/{{ rvp }} <b>mp</b> davfs rw,user,uid=1000,noauto 0 0" >> /etc/fstab </pre> + {%- endif %} <p>or the emergency alternative (gnome/gui-only):</p> <!-- gnome-bug: ignores vp --> <pre> {%- if accs %} - echo <b>{{ pw }}</b> | gio mount dav{{ s }}://k@{{ ep }}/{{ rvp }} + echo <b>{{ pw }}</b> | gio mount dav{{ s }}://{{ b_un }}@{{ ep }}/{{ rvp }} {%- else %} gio mount -a dav{{ s }}://{{ ep }}/{{ rvp }} {%- endif %} @@ -107,18 +121,18 @@ <div class="os mac"> <pre> - osascript -e ' mount volume "http{{ s }}://k:<b>{{ pw }}</b>@{{ ep }}/{{ rvp }}" ' + osascript -e ' mount volume "http{{ s }}://{{ b_un }}:<b>{{ pw }}</b>@{{ ep }}/{{ rvp }}" ' </pre> <p>or you can open up a Finder, press command-K and paste this instead:</p> <pre> - http{{ s }}://k:<b>{{ pw }}</b>@{{ ep }}/{{ rvp }} + http{{ s }}://{{ b_un }}:<b>{{ pw }}</b>@{{ ep }}/{{ rvp }} </pre> - {% if s %} + {%- if s %} <p><em>replace <code>https</code> with <code>http</code> if it doesn't work</em></p> - {% endif %} + {%- endif %} </div> - {% endif %} + {%- endif %} @@ -127,51 +141,71 @@ <div class="os win"> <p>if you can, install <a href="https://winfsp.dev/rel/">winfsp</a>+<a href="https://downloads.rclone.org/rclone-current-windows-amd64.zip">rclone</a> and then paste this in cmd:</p> - {% if args.ftp %} + {%- if args.ftp %} <p>connect with plaintext FTP:</p> <pre> + {%- if un %} + rclone config create {{ aname }}-ftp ftp host={{ rip }} port={{ args.ftp }} user={% if accs %}{{ b_un }} pass=<b>{{ pw }}</b>{% else %}anonymous pass=k{% endif %} tls=false + {%- else %} rclone config create {{ aname }}-ftp ftp host={{ rip }} port={{ args.ftp }} pass=k user={% if accs %}<b>{{ pw }}</b>{% else %}anonymous{% endif %} tls=false + {%- endif %} rclone mount --vfs-cache-mode writes --dir-cache-time 5s {{ aname }}-ftp:{{ rvp }} <b>W:</b> </pre> - {% endif %} - {% if args.ftps %} + {%- endif %} + {%- if args.ftps %} <p>connect with TLS-encrypted FTPS:</p> <pre> + {%- if un %} + rclone config create {{ aname }}-ftps ftp host={{ rip }} port={{ args.ftps }} user={% if accs %}{{ b_un }} pass=<b>{{ pw }}</b>{% else %}anonymous pass=k{% endif %} tls=false explicit_tls=true + {%- else %} rclone config create {{ aname }}-ftps ftp host={{ rip }} port={{ args.ftps }} pass=k user={% if accs %}<b>{{ pw }}</b>{% else %}anonymous{% endif %} tls=false explicit_tls=true + {%- endif %} rclone mount --vfs-cache-mode writes --dir-cache-time 5s {{ aname }}-ftps:{{ rvp }} <b>W:</b> </pre> - {% endif %} + {%- endif %} <ul> - {% if args.ftps %} + {%- if args.ftps %} <li>running on LAN (or just dont have valid certificates)? add <code>no_check_certificate=true</code> to the config command</li> - {% endif %} + {%- endif %} <li>old version of rclone? replace all <code>=</code> with <code> </code> (space)</li> </ul> <p>if you want to use the native FTP client in windows instead (please dont), press <code>win+R</code> and run this command:</p> <pre> + {%- if un %} + explorer {{ "ftp" if args.ftp else "ftps" }}://{% if accs %}{{ b_un }}:<b>{{ pw }}</b>@{% endif %}{{ host }}:{{ args.ftp or args.ftps }}/{{ rvp }} + {%- else %} explorer {{ "ftp" if args.ftp else "ftps" }}://{% if accs %}<b>{{ pw }}</b>:k@{% endif %}{{ host }}:{{ args.ftp or args.ftps }}/{{ rvp }} + {%- endif %} </pre> </div> <div class="os lin"> - {% if args.ftp %} + {%- if args.ftp %} <p>connect with plaintext FTP:</p> <pre> + {%- if un %} + rclone config create {{ aname }}-ftp ftp host={{ rip }} port={{ args.ftp }} user={% if accs %}{{ b_un }} pass=<b>{{ pw }}</b>{% else %}anonymous pass=k{% endif %} tls=false + {%- else %} rclone config create {{ aname }}-ftp ftp host={{ rip }} port={{ args.ftp }} pass=k user={% if accs %}<b>{{ pw }}</b>{% else %}anonymous{% endif %} tls=false + {%- endif %} rclone mount --vfs-cache-mode writes --dir-cache-time 5s {{ aname }}-ftp:{{ rvp }} <b>mp</b> </pre> - {% endif %} - {% if args.ftps %} + {%- endif %} + {%- if args.ftps %} <p>connect with TLS-encrypted FTPS:</p> <pre> + {%- if un %} + rclone config create {{ aname }}-ftps ftp host={{ rip }} port={{ args.ftps }} user={% if accs %}{{ b_un }} pass=<b>{{ pw }}</b>{% else %}anonymous pass=k{% endif %} tls=false explicit_tls=true + {%- else %} rclone config create {{ aname }}-ftps ftp host={{ rip }} port={{ args.ftps }} pass=k user={% if accs %}<b>{{ pw }}</b>{% else %}anonymous{% endif %} tls=false explicit_tls=true + {%- endif %} rclone mount --vfs-cache-mode writes --dir-cache-time 5s {{ aname }}-ftps:{{ rvp }} <b>mp</b> </pre> - {% endif %} + {%- endif %} <ul> - {% if args.ftps %} + {%- if args.ftps %} <li>running on LAN (or just dont have valid certificates)? add <code>no_check_certificate=true</code> to the config command</li> - {% endif %} + {%- endif %} <li>running <code>rclone mount</code> as root? add <code>--allow-other</code></li> <li>old version of rclone? replace all <code>=</code> with <code> </code> (space)</li> </ul> @@ -179,7 +213,7 @@ <!-- gnome-bug: ignores vp --> <pre> {%- if accs %} - echo <b>{{ pw }}</b> | gio mount ftp{{ "" if args.ftp else "s" }}://k@{{ host }}:{{ args.ftp or args.ftps }}/{{ rvp }} + echo <b>{{ pw }}</b> | gio mount ftp{{ "" if args.ftp else "s" }}://{{ b_un }}@{{ host }}:{{ args.ftp or args.ftps }}/{{ rvp }} {%- else %} gio mount -a ftp{{ "" if args.ftp else "s" }}://{{ host }}:{{ args.ftp or args.ftps }}/{{ rvp }} {%- endif %} @@ -189,10 +223,10 @@ <div class="os mac"> <p>note: FTP is read-only on macos; please use WebDAV instead</p> <pre> - open {{ "ftp" if args.ftp else "ftps" }}://{% if accs %}k:<b>{{ pw }}</b>@{% else %}anonymous:@{% endif %}{{ host }}:{{ args.ftp or args.ftps }}/{{ rvp }} + open {{ "ftp" if args.ftp else "ftps" }}://{% if accs %}{{ b_un }}:<b>{{ pw }}</b>@{% else %}anonymous:@{% endif %}{{ host }}:{{ args.ftp or args.ftps }}/{{ rvp }} </pre> </div> - {% endif %} + {%- endif %} @@ -204,11 +238,11 @@ <span class="os lin">doesn't need root</span> </p> <pre> - partyfuse.py{% if accs %} -a <b>{{ pw }}</b>{% endif %} http{{ s }}://{{ ep }}/{{ rvp }} <b><span class="os win">W:</span><span class="os lin mac">mp</span></b> + partyfuse.py{% if accs %} -a <b>{{ unpw }}</b>{% endif %} http{{ s }}://{{ ep }}/{{ rvp }} <b><span class="os win">W:</span><span class="os lin mac">mp</span></b> </pre> - {% if s %} + {%- if s %} <ul><li>if you are on LAN (or just dont have valid certificates), add <code>-td</code></li></ul> - {% endif %} + {%- endif %} <p> you can use <a href="{{ r }}/.cpr/a/u2c.py">u2c.py</a> to upload (sometimes faster than web-browsers) </p> @@ -217,6 +251,10 @@ {% if args.smb %} <h1>SMB / CIFS</h1> + {%- if un %} + <h2>not available on this server because <code>--usernames</code> is enabled in the server config</h2> + {%- else %} + <div class="os win"> <pre> net use <b>w:</b> \\{{ host }}\a{% if accs %} k /user:<b>{{ pw }}</b>{% endif %} @@ -234,7 +272,8 @@ <pre class="os mac"> open 'smb://<b>{{ pw }}</b>:k@{{ host }}/a' </pre> - {% endif %} + {%- endif %} + {%- endif %} @@ -247,7 +286,7 @@ { "Version": "15.0.0", "Name": "copyparty", "RequestURL": "http{{ s }}://{{ ep }}/{{ rvp }}", "Headers": { - {% if accs %}"pw": "<b>{{ pw }}</b>", {% endif %}"accept": "url" + {% if accs %}"pw": "<b>{{ unpw }}</b>", {% endif %}"accept": "url" }, "DestinationType": "ImageUploader, TextUploader, FileUploader", "Body": "MultipartFormData", "URL": "{response}", @@ -260,7 +299,7 @@ { "Name": "copyparty", "RequestURL": "http{{ s }}://{{ ep }}/{{ rvp }}", "Headers": { - {% if accs %}"pw": "<b>{{ pw }}</b>", {% endif %}"accept": "url" + {% if accs %}"pw": "<b>{{ unpw }}</b>", {% endif %}"accept": "url" }, "DestinationType": "ImageUploader, TextUploader, FileUploader", "FileFormName": "f" } @@ -278,7 +317,9 @@ { "Name": "copyparty", "RequestURL": "http{{ s }}://{{ ep }}/{{ rvp }}", "Headers": { - {% if accs %}"pw": "<b>{{ pw }}</b>",{% endif %} + {%- if accs %} + "pw": "<b>{{ unpw }}</b>", + {%- endif %} "accept": "json" }, "ResponseURL": "{{ '{{fileurl}}' }}", @@ -295,7 +336,7 @@ <pre class="dl" name="flameshot.sh"> #!/bin/bash - pw="<b>{{ pw }}</b>" + pw="<b>{{ unpw }}</b>" url="http{{ s }}://{{ ep }}/{{ rvp }}" filename="$(date +%Y-%m%d-%H%M%S).png" flameshot gui -s -r | curl -sT- "$url$filename?want=url&pw=$pw" | xsel -ib diff --git a/copyparty/web/svcs.js b/copyparty/web/svcs.js index dad7faab..0b0f6b91 100644 --- a/copyparty/web/svcs.js +++ b/copyparty/web/svcs.js @@ -49,21 +49,47 @@ function setos(os) { setos(WINDOWS ? 'win' : LINUX ? 'lin' : MACOS ? 'mac' : 'idk'); -var pw = ''; +var un, un0, pw, pw0, unpw, up0; function setpw(e) { ev(e); + if (!ebi('un0')) + return askpw(); + + modal.prompt('username:', '', function (v) { + if (!v) + return; + + un = v; + un0 = ebi('un0').innerHTML; + var oa = QSA('b'); + + for (var a = 0; a < oa.length; a++) + if (oa[a].innerHTML == un0) + oa[a].textContent = un; + + askpw(); + }); +} +function askpw() { modal.prompt('password:', '', function (v) { if (!v) return; pw = v; - var pw0 = ebi('pw0').innerHTML, - oa = QSA('b'); - + pw0 = ebi('pw0').innerHTML; + var oa = QSA('b'); + for (var a = 0; a < oa.length; a++) if (oa[a].innerHTML == pw0) - oa[a].textContent = v; + oa[a].textContent = pw; + if (un) { + unpw = un ? (un+':'+pw) : pw; + up0 = ebi('up0').innerHTML; + for (var a = 0; a < oa.length; a++) + if (oa[a].innerHTML == up0) + oa[a].textContent = unpw; + } add_dls(); }); } diff --git a/copyparty/web/ui.css b/copyparty/web/ui.css index 212a77fa..7181bbab 100644 --- a/copyparty/web/ui.css +++ b/copyparty/web/ui.css @@ -430,6 +430,15 @@ html.y textarea:focus { .mdo code { font-size: .96em; } +html.z .mdo a>code, +html.y .mdo a>code { + color: inherit; + background: inherit; + background: rgba(0, 0, 0, 0.2); + padding-top: 0; + padding-bottom: 0; + border: none; +} .mdo h1, .mdo h2 { line-height: 1.5em; diff --git a/copyparty/web/up2k.js b/copyparty/web/up2k.js index 2743ea36..dc2e8243 100644 --- a/copyparty/web/up2k.js +++ b/copyparty/web/up2k.js @@ -50,7 +50,7 @@ catch (ex) { } catch (ex) { console.log('up2k init failed:', ex); - toast.err(10, 'could not initialze up2k\n\n' + basenames(ex)); + toast.err(10, 'could not initialize up2k\n\n' + basenames(ex)); } } treectl.onscroll(); @@ -732,7 +732,7 @@ function Donut(uc, st) { tstrober = setInterval(strobe, 300); if (uc.upsfx && actx && actx.state != 'suspended') - sfx(); + sfx_nice(); // firefox may forget that filedrops are user-gestures so it can skip this: if (uc.upnag && Notification && Notification.permission == 'granted') @@ -745,8 +745,10 @@ function Donut(uc, st) { if (!txt) clearInterval(tstrober); } +} - function sfx() { +function sfx_nice() { + if (true) { var osc = actx.createOscillator(), gain = actx.createGain(), gg = gain.gain, @@ -2831,7 +2833,7 @@ function up2k_init(subtle) { if (!t.t_uploading) t.t_uploading = Date.now(); - pvis.seth(t.n, 1, "๐Ÿš€ send"); + pvis.seth(t.n, 1, "๐Ÿš€ " + L.ul_send); var chunksize = get_chunksize(t.size), car = pcar * chunksize, @@ -3037,10 +3039,12 @@ function up2k_init(subtle) { if (anymod(e)) return; - if (e.code == 'ArrowUp') + var k = e.key || e.code; + + if (k == 'ArrowUp') bumpthread(1); - if (e.code == 'ArrowDown') + if (k == 'ArrowDown') bumpthread(-1); } @@ -3103,7 +3107,8 @@ function up2k_init(subtle) { ebi('u2szg').addEventListener('blur', read_u2sz); ebi('u2szg').onkeydown = function (e) { if (anymod(e)) return; - var n = e.code == 'ArrowUp' ? 1 : e.code == 'ArrowDown' ? -1 : 0; + var k = e.key || e.code, + n = k == 'ArrowUp' ? 1 : k == 'ArrowDown' ? -1 : 0; if (!n) return; this.value = parseInt(this.value) + n; read_u2sz(); @@ -3180,7 +3185,8 @@ function up2k_init(subtle) { function kd_life(e) { var el = e.target, - d = e.code == 'ArrowUp' ? 1 : e.code == 'ArrowDown' ? -1 : 0; + k = e.key || e.code, + d = k == 'ArrowUp' ? 1 : k == 'ArrowDown' ? -1 : 0; if (anymod(e) || !d) return; @@ -3421,6 +3427,7 @@ if (QS('#op_up2k.act')) goto_up2k(); apply_perms({ "perms": perms, "frand": frand, "u2ts": u2ts }); +fileman.render(); (function () { diff --git a/copyparty/web/util.js b/copyparty/web/util.js index 489cd416..7bcc60ad 100644 --- a/copyparty/web/util.js +++ b/copyparty/web/util.js @@ -1263,10 +1263,13 @@ function sethash(hv) { function dl_file(url) { console.log('DL [%s]', url); - var o = mknod('a'); + qsr('#dlfth'); + var o = mknod('a', 'dlfth'); o.setAttribute('href', url); o.setAttribute('download', ''); - o.click(); + document.body.appendChild(o); + ebi('dlfth').click(); + qsr('#dlfth'); } @@ -1821,12 +1824,12 @@ var modal = (function () { }; var onkey = function (e) { - var k = (e.code || e.key) + '', + var k = (e.key || e.code) + '', eok = ebi('modal-ok'), eng = ebi('modal-ng'), ae = document.activeElement; - if (k == 'Space' && ae && (ae === eok || ae === eng)) + if ((k == 'Space' || k == 'Spacebar' || k == ' ') && ae && (ae === eok || ae === eng)) k = 'Enter'; if (k.endsWith('Enter')) { diff --git a/docs/changelog.md b/docs/changelog.md index a3c0aaae..ac773418 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,3 +1,209 @@ +โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€ +# 2025-0828-2014 `v1.19.7` chdir + +## ๐Ÿงช new features + +* new option `chdir` to change the PWD (process working-directory) before volumes are mapped 14555d58 + +## ๐Ÿฉน bugfixes + +* fix using empty folders as statefile storage ([v1.19.6](https://github.com/9001/copyparty/releases/tag/v1.19.6) made this a bit too strict) 0d96786e +* holding I/K to scroll through folders quickly now works better 914686ec + +## ๐Ÿ”ง other changes + +* #717 docker: fix the image repo metadata (thx @EmilyxFox!) 6f087117 +* docker: change `$HOME` to `/state` 01cf20a0 d1f75229 + * and use the new `chdir` option to preserve old config-file semantics 14555d58 + * helps avoid statefiles accidentally landing in `/w` as a consequence of misconfiguration + +## ๐ŸŒ  fun facts + +* this release was made at [RevSpace NL](https://a.ocv.me/pub/g/nerd-stuff/PXL_20250828_202820075.jpg?cache) + + + +โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€ +# 2025-0827-2038 `v1.19.6` auth-precedence + +## ๐Ÿงช new features + +* #673 add Portuguese translation (thx anonymous!) 4b8c2215 + * ...and enable the Polish translation (whoops) 8f235be6 +* #689 add option to control authentication priority/precedence 543b7ea9 +* url-parameter `?dl` forces file download instead of displaying in-browser 48d6224e +* #533 more ways to make the QR-code always-visible in the console 2848941e +* #695 option to log invalid xml from clients 28b93d79 +* #552 configurable markdown newline behavior 0491123b + * and tweak the styling of monospace in links 68503444 + +## ๐Ÿฉน bugfixes + +* #628 FTP-server now accepts connections from IPv6 link-local addresses 978801d0 +* incorrect assumption that all IPv6 link-local addresses start with `fe80` d39c74c1 +* ftp: fix file rename d40f061a +* u2c: couldn't upload files located at the very top of the unix file hierarchy 599e82f2 +* #699 markdown-editor: fix panic if the table-formatter is executed on something that isn't a table 4c042b3c + +## ๐Ÿ”ง other changes + +* #696 a volume can be one single file, not just folders aa1c9213 +* #442 strongly prefer XDG_CONFIG_HOME as config location 35472557 +* #691 album-art collected from audio-files can now become folder thumbnails 0b50fde3 +* allow spaces in more of the comma-separated options d30240b4 +* docs: + * mention config requirements for [syncing folders](https://github.com/9001/copyparty/#folder-sync) with u2c 6cd0a396 59f142cd + + + +โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€ +# 2025-0821-2319 `v1.19.5` it runs on iOS + +## ๐Ÿงช new features + +* #328 run copyparty on iPhones; see [install on iOS](https://github.com/9001/copyparty#install-on-iOS) in the readme ca98d54f + * cannot run in the background, doesn't have full access to your files, and is slightly buggy, but it *works* + * [running on android](https://github.com/9001/copyparty#install-on-android) gives you a much better experience +* save the qr-code to a file (txt/svg/png) 202ddeac + +## ๐Ÿฉน bugfixes + +* #661 fix incorrect `rproxy` hint in the logs 6c76614e +* #649 fix js-crash when tapping in the exactly correct place (thx @hahaslav for debugging!) 0de07d8e +* #628 ftpd: fix banning IPv6 clients 6d76254c + +## ๐Ÿ”ง other changes + +* #296 nixos: support non-flake setups (thx @Sorixelle!) 20ef74cd 32593670 +* config-parser catches and explains a few more common mistakes cc65b1b5 +* docs: + * #490, #199: readme: confirm that combining copyparty and syncthing is safe c51371c7 + * #377 improved authelia docker example (thx @xFuture603!) cd8771fa + * mention the homebrew formulae f9cb2c15 + * #651 versus.md: fix hfs3 comparison (thx @rejetto!) 7a4973fa + + + +โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€ +# 2025-0817-1556 `v1.19.4` take two (fix cfg vols) + +## โ„น๏ธ this upgrade is a one-way ticket + +* your up2k database (`.hist/up2k.db`), used by the `e2d` filesystem indexing feature, **will be upgraded to a new format** which older copyparty versions cannot read. A backup of each database will be created automatically, named `up2k.db.bak.SOMETHING.v5`. If you need to downgrade to a previous version: Shutdown copyparty, delete these files: `up2k.db up2k.db-shm up2k.db-wal` and then copy `up2k.db.bak.*.v5` to `up2k.db` + +## ๐Ÿงช new features + +* new translations: + * #551 Swedish (thx @Bevinsky!) d676a86f + * #551 Korean (thx @nyqui!) 4e878d2f +* #581 new theme: phi95 (thx @varphi-online!) d8662aeb +* #567 .raw image thumbnails (thx @ar-nelson!) 0177a9b4 + * available in docker-images `iv` and `dj` +* #561 epub thumbnails (thx @Scotsguy!) 9435e6b2 +* #252 music thumbnails use embdded coverart if available 98d117b8 + * thumbnails folder `.hist/th` must be deleted to take effect +* #530 show username of uploaders in file listings; requires `a` (admin) permission 4df033ec +* #604 a new group `@acct` which automatically contains all known usernames 68907eaf +* controlpanel has a dedicated "logout all sessions" button, similar to the logout-link in the browser f4a3fba2 +* #397 accounts can be restricted to certian IPs 62e072a2 +* #504 automatic login through tailscale auth a4649d1e +* #533 sticky qr-code with `--qr-pin 1` 1ebe06f5 +* #572 button to abort copy/move 715d374e +* #618 "download selected files" didn't work on firefox 52 (winxp) dcc6b1b4 +* max number of cookies to allow can be configured 6303effe + * good if you have too many selfhosted services on one domain (but will beware of the spec-mandataed max length of the cookie field!) + +## ๐Ÿฉน bugfixes + +* fix xvol/xdev edgecases: + * #603 rootless vfs 554cc2f3 + * false-positive with overlapping volumes d9046f7e +* #573 ftp: attempting an upload into read-only folder no longer kills the connection 3aa8b7aa +* #306 adjust navpane for `--rp-loc` (location-based proxying) +* #556 more sensible config expansion order f4727f8e + * #624 ...which broke things bf1fdcab +* the video player now stays fullscreen between videos 782e2f1d +* heif thumbnailing with libvips + +## ๐Ÿ”ง other changes + +* #253 build nix-packages from source (thx @toast003, @chinponya!) 187cae25 +* #616 logfiles will have a plaintext severity column if `--no-ansi` d4cf42e7 +* #598 separate option `--ac-convt` for audio transcoding timeout d5623057 +* #596 users with a blank password gets a strong random-generated one 7f448750 +* copyparty.exe: upgrade to python 3.13.7 + + + +โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€ +# 2025-0810-1226 `v1.19.1` archlinux fix + +## ๐Ÿงช new features + +* new translations: + * #486 French (thx @Tr3yWay996, @Packingdustry, @Alee14, @jakubiakfr, @Equinoxs!) e9ddfccf 7aa21483 b87f8f1b + * #463 Polish (thx @pufereq and @daimond113!) 392a4db5 + * #537 Nynorsk (thx @chinatsu!) 3931bc27 +* #549 custom mdns domain 3c78c6a8 + +## ๐Ÿฉน bugfixes + +* #539 FTP glitches when running on windows 8ba98877 +* #555 global-config didn't load through PRTY_CONFIG (thx @icxes!) 074e106e +* macos: could take a while to establish webdav connection from finder a01870b7 +* ux: + * dropdown colors 347cf6a5 + * case-sensitivity in filters e5e82295 + * iOS being too enthusiastic about using saved passwords 03acd65e + + + +โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€ +# 2025-0807-2213 `v1.19.0` usernames + +## ๐Ÿงช new features + +* #511 login with username and password (not just password) can now optionally be enabled with `--usernames` 346515cc + * if you have enabled password hashing (`ah-alg: argon2` or similar) then you will need to hash your passwords again after enabling usernames, hashing them as `username:password:` +* #468 add Greek translation (thx @chamdim!) 50f46187 392abd06 +* #471 add Czech translation (thx @kubakubakuba!) c9556583 +* #515 support systemd socket acivation (thx @mati1210!) 9b9d2a92 +* #523 add QR-code to the connectpage bcc3b156 +* #513 optional EOL-conversion for texteditor 8b31ed88 +* controlpanel refresh-button now toggles automatic refresh 7ae84dea + +## ๐Ÿฉน bugfixes + +* fix stuck uploads when the up2k database (`e2d`) is not enabled 4a043568 + * if more than 60'000 files were uploaded and there were several dupes of some files, they could get stuck and never upload + * upload performance is improved remarkably by enabling `e2d` so such huge uploads non-e2d had not been tested in a long time +* #467 #470 fix ui-crash when exporting links of all uploaded files to clipboard (thx @geekalaa!) 0df1901f +* #487 fix ui-crash when the location url-part is `//` 0f55a1ae +* fix viewing `.MD` files (8a0746c6) + +## ๐Ÿ”ง other changes + +* when a reverse-proxy is detected, force explicit configuration of `--rproxy` to obtain correct client IP 3f8cb7e8 + * a bit inconvenient, but helps prevent potentially-dangerous misconfiguration + * the necessary configuration changes are explained in the serverlog (you can't miss it) + * thanks to @person4268 for pointing out that there was room for improvements! +* failed login attempts now only log a sha512 hash of the provided password + * to see login-attempts with incorrect passwords as plaintext like before, `log-badpwd: 1` +* #502 add systemd user services and templated services (thx @icxes!) 34d98e99 +* #475 improve helptext for multivalue global-options c2ac57a2 +* #475 add [chungus.conf](https://github.com/9001/copyparty/blob/hovudstraum/docs/chungus.conf), massive extensive nonsensical demo config b664ebb0 +* try to detect proxies with incorrect caching behavior 9e980bb5 +* recent-uploads now support ie9 a57f7cc2 +* languages and themes are now dropdowns a9ee4f24 +* copyparty.exe: upgrade python to 3.13.6 a98360f2 +* introduce [copyparty-en.py](https://github.com/9001/copyparty/releases/latest/download/copyparty-en.py), english-only edition of copyparty-sfx.py to save space 33497e6b + +## ๐Ÿ—ฟ known issues + +* the `copyparty.pyz` in this release is english-only, and does not include the translations -- they got lost in transit while adjusting the buildscripts to make `copyparty-en.py` + + + โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€โ–€ # 2025-0804-0013 `v1.18.10` idp speedboost diff --git a/docs/chungus.conf b/docs/chungus.conf index 7ac7ba9a..a8e34516 100644 --- a/docs/chungus.conf +++ b/docs/chungus.conf @@ -1083,7 +1083,7 @@ th-x3: n # default # image decoders, in order of preference - th-dec: vips,pil,ff # default + th-dec: vips,pil,raw,ff # default # disable jpg output th-no-jpg @@ -1115,6 +1115,9 @@ # image formats to decode using pyvips th-r-vips: a,very,long,list,of,file,extensions # hint + # image formats to decode using rawpy + th-r-raw: a,very,long,list,of,file,extensions # hint + # image formats to decode using ffmpeg th-r-ffi: a,very,long,list,of,file,extensions # hint diff --git a/docs/devnotes.md b/docs/devnotes.md index 9fbb29df..5100471a 100644 --- a/docs/devnotes.md +++ b/docs/devnotes.md @@ -160,6 +160,7 @@ authenticate using header `Cookie: cppwd=foo` or url param `&pw=foo` | method | params | result | |--|--|--| +| GET | `?dl` | download file (don't show in-browser) | | GET | `?ls` | list files/folders at URL as JSON | | GET | `?ls&dots` | list files/folders at URL as JSON, including dotfiles | | GET | `?ls=t` | list files/folders at URL as plaintext | @@ -328,7 +329,7 @@ if you don't need all the features, you can repack the sfx and save a bunch of s the features you can opt to drop are * `cm`/easymde, the "fancy" markdown editor, saves ~89k -* `hl`, prism, the syntax hilighter, saves ~41k +* `hl`, prism, the syntax highlighter, saves ~41k * `fnt`, source-code-pro, the monospace font, saves ~9k for the `re`pack to work, first run one of the sfx'es once to unpack it @@ -354,7 +355,7 @@ pip install mutagen # audio metadata 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 -pip install Pillow pyheif-pillow-opener # thumbnails +pip install Pillow pillow-heif # thumbnails pip install pyvips # faster thumbnails pip install psutil # better cleanup of stuck metadata parsers on windows pip install black==21.12b0 click==8.0.2 bandit pylint flake8 isort mypy # vscode tooling diff --git a/docs/examples/docker/basic-docker-compose/copyparty.conf b/docs/examples/docker/basic-docker-compose/copyparty.conf index 59820103..ba751549 100644 --- a/docs/examples/docker/basic-docker-compose/copyparty.conf +++ b/docs/examples/docker/basic-docker-compose/copyparty.conf @@ -6,7 +6,7 @@ [global] e2dsa # enable file indexing and filesystem scanning e2ts # enable multimedia indexing - ansi # enable colors in log messages + ansi # enable colors in log messages (both in logfiles and stdout) # q, lo: /cfg/log/%Y-%m%d.log # log to file instead of docker diff --git a/docs/examples/docker/basic-docker-compose/docker-compose.yml b/docs/examples/docker/basic-docker-compose/docker-compose.yml index dd3c1f9a..d75d3117 100644 --- a/docs/examples/docker/basic-docker-compose/docker-compose.yml +++ b/docs/examples/docker/basic-docker-compose/docker-compose.yml @@ -1,5 +1,6 @@ -services: +--- +services: copyparty: image: copyparty/ac:latest container_name: copyparty diff --git a/docs/examples/docker/idp-authelia-traefik/README.md b/docs/examples/docker/idp-authelia-traefik/README.md index 7667cb00..bc7a7f2a 100644 --- a/docs/examples/docker/idp-authelia-traefik/README.md +++ b/docs/examples/docker/idp-authelia-traefik/README.md @@ -8,7 +8,7 @@ to try this out with minimal adjustments: * login to https://fs.example.com/ with username `authelia` password `authelia` to use this in a safe and secure manner: -* follow a guide on setting up authelia properly (TODO:link) and use the copyparty-specific parts of this folder as inspiration for your own config; namely the `cpp` subfolder and the `copyparty` service in `docker-compose.yml` +* follow a guide on setting up [authelia](https://www.authelia.com/integration/proxies/traefik/#docker-compose) properly and use the copyparty-specific parts of this folder as inspiration for your own config; namely the `cpp` subfolder and the `copyparty` service in `docker-compose.yml` this folder is based on: * https://github.com/authelia/authelia/tree/39763aaed24c4abdecd884b47357a052b235942d/examples/compose/lite @@ -16,20 +16,18 @@ this folder is based on: incomplete list of modifications made: * support for running with podman as root on fedora (`:z` volumes, `label:disable`) * explicitly using authelia `v4.38.0-beta3` because config syntax changed since last stable release -* disabled automatic letsencrypt certificate signing * reduced logging from debug to info -* added a warning that traefik is given access to the docker socket (as recommended by traefik docs) which means traefik is able to break out of the container and has full root access on the host machine +* implemented a docker socket-proxy to not bind the docker.socket directly to traefik +* using valkey instead of redis for caching # security there is probably/definitely room for improvement in this example setup. Some ideas taken from [github issue #62](https://github.com/9001/copyparty/issues/62): -* Add in a redis password to limit attacker lateral movement in the system -* Move redis to a private network shared with just authelia -* Pin to image hashes (or go all in on updates and add `watchtower`) +* Move valkey to a private network shared with just authelia +* Add `watchtower` to manage your image version updates * Drop bridge networking for just exposing traefik's public ports -* Configure docker for non-root access to docker socket and then move traefik to use [non-root perms](https://docs.docker.com/engine/security/rootless/) if you manage to improve on any of this, especially in a way that might be useful for other people, consider sending a PR :> @@ -47,4 +45,4 @@ currently **not optimal,** at least when compared to running the python sfx outs authelia is behaving strangely, handling 340 requests per second for a while, but then it suddenly drops to 75 and stays there... -I'm assuming all of the performance issues is due to a misconfiguration of authelia/traefik/docker on my end, but I don't relly know where to start +I'm assuming all of the performance issues is due to a misconfiguration of authelia/traefik/docker on my end, but I don't really know where to start diff --git a/docs/examples/docker/idp-authelia-traefik/authelia/configuration.yml b/docs/examples/docker/idp-authelia-traefik/authelia/configuration.yml index b6ca311a..b4352511 100644 --- a/docs/examples/docker/idp-authelia-traefik/authelia/configuration.yml +++ b/docs/examples/docker/idp-authelia-traefik/authelia/configuration.yml @@ -1,15 +1,14 @@ -# based on https://github.com/authelia/authelia/blob/39763aaed24c4abdecd884b47357a052b235942d/examples/compose/lite/authelia/configuration.yml - # Authelia configuration -# This secret can also be set using the env variables AUTHELIA_JWT_SECRET_FILE -jwt_secret: a_very_important_secret +identity_validation: + reset_password: + jwt_secret: 'a_very_important_secret_so_please_change_this' server: address: 'tcp://:9091' log: - level: info # debug + level: info totp: issuer: authelia.com @@ -21,29 +20,26 @@ authentication_backend: access_control: default_policy: deny rules: - # Rules applied to everyone - - domain: traefik.example.com - policy: one_factor + - domain: auth.example.com + policy: bypass # Allow access to the login UI - domain: fs.example.com policy: one_factor session: - # This secret can also be set using the env variables AUTHELIA_SESSION_SECRET_FILE secret: unsecure_session_secret - cookies: - name: authelia_session - domain: example.com # Should match whatever your root protected domain is + domain: example.com # this should match whatever your root protected domain is default_redirection_url: https://fs.example.com authelia_url: https://authelia.example.com/ expiration: 3600 # 1 hour inactivity: 300 # 5 minutes redis: - host: redis + host: valkey port: 6379 - # This secret can also be set using the env variables AUTHELIA_SESSION_REDIS_PASSWORD_FILE - # password: authelia + password: your_secure_password_here + regulation: max_retries: 3 @@ -58,9 +54,7 @@ storage: notifier: disable_startup_check: true smtp: - username: test - # This secret can also be set using the env variables AUTHELIA_NOTIFIER_SMTP_PASSWORD_FILE - password: password - host: mail.example.com - port: 25 - sender: admin@example.com + address: 'smtp://127.0.0.1:25' + username: 'test' + password: 'password' + sender: "Authelia <admin@example.com>" diff --git a/docs/examples/docker/idp-authelia-traefik/docker-compose.yml b/docs/examples/docker/idp-authelia-traefik/docker-compose.yml index 9ebd73ba..3a45dda4 100644 --- a/docs/examples/docker/idp-authelia-traefik/docker-compose.yml +++ b/docs/examples/docker/idp-authelia-traefik/docker-compose.yml @@ -1,4 +1,4 @@ -version: '3.3' +--- networks: net: @@ -6,7 +6,7 @@ networks: services: copyparty: - image: copyparty/ac + image: copyparty/ac:latest container_name: idp_copyparty user: "1000:1000" # should match the user/group of your fileshare volumes volumes: @@ -19,19 +19,19 @@ services: labels: - 'traefik.enable=true' - 'traefik.http.routers.copyparty.rule=Host(`fs.example.com`)' - - 'traefik.http.routers.copyparty.entrypoints=https' + - 'traefik.http.routers.copyparty.entrypoints=websecure' - 'traefik.http.routers.copyparty.tls=true' + - 'traefik.http.routers.copyparty.tls.certresolver=letsencrypt' # โ† THIS IS CRUCIAL - 'traefik.http.routers.copyparty.middlewares=authelia@docker' stop_grace_period: 15s # thumbnailer is allowed to continue finishing up for 10s after the shutdown signal environment: LD_PRELOAD: /usr/lib/libmimalloc-secure.so.NOPE # enable mimalloc by replacing "NOPE" with "2" for a nice speed-boost (will use twice as much ram) - PYTHONUNBUFFERED: 1 # ensures log-messages are not delayed (but can reduce speed a tiny bit) authelia: - image: authelia/authelia:v4.38.0-beta3 # the config files in the authelia folder use the new syntax + image: authelia/authelia:4.39.5@sha256:023e02e5203dfa0ebaee7a48b5bae34f393d1f9cada4a9df7fbf87eb1759c671 container_name: idp_authelia volumes: - ./authelia:/config:z @@ -40,25 +40,23 @@ services: labels: - 'traefik.enable=true' - 'traefik.http.routers.authelia.rule=Host(`authelia.example.com`)' - - 'traefik.http.routers.authelia.entrypoints=https' + - 'traefik.http.routers.authelia.entrypoints=websecure' - 'traefik.http.routers.authelia.tls=true' - #- 'traefik.http.routers.authelia.tls.certresolver=letsencrypt' # uncomment this to enable automatic certificate signing (1/2) + - 'traefik.http.routers.authelia.tls.certresolver=letsencrypt' - 'traefik.http.middlewares.authelia.forwardauth.address=http://authelia:9091/api/authz/forward-auth?authelia_url=https://authelia.example.com' - 'traefik.http.middlewares.authelia.forwardauth.trustForwardHeader=true' - 'traefik.http.middlewares.authelia.forwardauth.authResponseHeaders=Remote-User,Remote-Groups,Remote-Name,Remote-Email' expose: - 9091 restart: unless-stopped - healthcheck: - disable: true environment: - TZ=Etc/UTC - redis: - image: redis:7.2.4-alpine3.19 - container_name: idp_redis + valkey: + image: valkey/valkey:8.1.3-alpine3.22@sha256:0d27f0bca0249f61d060029a6aaf2e16b2c417d68d02a508e1dfb763fa2948b4 + container_name: idp_valkey volumes: - - ./redis:/data:z + - ./valkey:/data:z networks: - net expose: @@ -66,40 +64,55 @@ services: restart: unless-stopped environment: - TZ=Etc/UTC + - VALKEY_EXTRA_FLAGS=--requirepass your_secure_password_here + + socket-proxy: + image: lscr.io/linuxserver/socket-proxy:3.2.3@sha256:63d2e0ce6bb0d12dfdbde5c3af31d08fee343ec3801a050c8197a3f5ffae8bed + container_name: idp_socket_proxy + environment: + - CONTAINERS=1 + - NETWORKS=1 + - EVENTS=1 + - PING=1 + - VERSION=1 + - LOG_LEVEL=warning + volumes: + - /var/run/docker.sock:/var/run/docker.sock:ro + security_opt: + - no-new-privileges:true + read_only: true + tmpfs: + - /run + networks: + - net + restart: unless-stopped + expose: + - 2375 traefik: - image: traefik:2.11.0 + image: traefik:3.5.0@sha256:4e7175cfe19be83c6b928cae49dde2f2788fb307189a4dc9550b67acf30c11a5 container_name: idp_traefik volumes: - ./traefik:/etc/traefik:z - - /var/run/docker.sock:/var/run/docker.sock # WARNING: this gives traefik full root-access to the host OS, but is recommended/required(?) by traefik - security_opt: - - label:disable # disable selinux because it (rightly) blocks access to docker.sock networks: - net labels: - 'traefik.enable=true' - - 'traefik.http.routers.api.rule=Host(`traefik.example.com`)' - - 'traefik.http.routers.api.entrypoints=https' - - 'traefik.http.routers.api.service=api@internal' - - 'traefik.http.routers.api.tls=true' - #- 'traefik.http.routers.api.tls.certresolver=letsencrypt' # uncomment this to enable automatic certificate signing (2/2) - 'traefik.http.routers.api.middlewares=authelia@docker' ports: - '80:80' - '443:443' command: - - '--api' - - '--providers.docker=true' + - '--global.sendAnonymousUsage=false' + - '--providers.docker.endpoint=tcp://socket-proxy:2375' - '--providers.docker.exposedByDefault=false' - - '--entrypoints.http=true' - - '--entrypoints.http.address=:80' - - '--entrypoints.http.http.redirections.entrypoint.to=https' - - '--entrypoints.http.http.redirections.entrypoint.scheme=https' - - '--entrypoints.https=true' - - '--entrypoints.https.address=:443' + - '--entrypoints.web.address=:80' + - '--entrypoints.web.http.redirections.entrypoint.to=websecure' + - '--entrypoints.web.http.redirections.entrypoint.scheme=https' + - '--entrypoints.websecure.address=:443' - '--certificatesResolvers.letsencrypt.acme.email=your-email@your-domain.com' - '--certificatesResolvers.letsencrypt.acme.storage=/etc/traefik/acme.json' - - '--certificatesResolvers.letsencrypt.acme.httpChallenge.entryPoint=http' - - '--log=true' - - '--log.level=WARNING' # DEBUG + - '--certificatesResolvers.letsencrypt.acme.httpChallenge.entryPoint=web' + - '--log.level=INFO' + depends_on: + - socket-proxy diff --git a/docs/notes.sh b/docs/notes.sh index 3c8798cc..9152d4ac 100644 --- a/docs/notes.sh +++ b/docs/notes.sh @@ -71,7 +71,7 @@ avg() { awk 'function pr(ncsz) {if (nsmp>0) {printf "%3s %s\n", csz, sum/nsmp} c python3 -um copyparty -nw -v srv::rw -i 127.0.0.1 2>&1 | tee log cat log | awk '!/"purl"/{next} {s=$1;sub(/[^m]+m/,"");gsub(/:/," ");t=60*(60*$1+$2)+$3} t<p{t+=86400} !a{a=t;sa=s} {b=t;sb=s} END {print b-a,sa,sb}' -# or if the client youre measuring dies for ~15sec every once ina while and you wanna filter those out, +# or if the client you're measuring dies for ~15sec every once ina while and you wanna filter those out, cat log | awk '!/"purl"/{next} {s=$1;sub(/[^m]+m/,"");gsub(/:/," ");t=60*(60*$1+$2)+$3} t<p{t+=86400} !p{a=t;p=t;r=0;next} t-p>1{printf "%.3f += %.3f - %.3f (%.3f) # %.3f -> %.3f\n",r,p,a,p-a,p,t;r+=p-a;a=t} {p=t} END {print r+p-a}' @@ -337,3 +337,5 @@ mk && t0="$(date)" && while true; do date -s "$(date '+ 1 hour')"; systemd-tmpfi mk && sudo -u ed flock /tmp/foo sleep 40 & sleep 1; ps aux | grep -E 'sleep 40$' && t0="$(date)" && for n in {1..40}; do date -s "$(date '+ 1 day')"; systemd-tmpfiles --clean; ls -1 /tmp | grep foo || break; done; echo "$t0" mk && t0="$(date)" && for n in {1..40}; do date -s "$(date '+ 1 day')"; systemd-tmpfiles --clean; ls -1 /tmp | grep foo || break; tar -cf/dev/null /tmp/foo; done; echo "$t0" +# 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)}' diff --git a/docs/versus.md b/docs/versus.md index 34d4d4b0..685aa14b 100644 --- a/docs/versus.md +++ b/docs/versus.md @@ -68,7 +68,7 @@ currently up to date with [awesome-selfhosted](https://github.com/awesome-selfho * [kodbox](https://github.com/kalcaddle/kodbox) ([review](#kodbox)) appears to be a fantastic alternative if you're not worried about running chinese software, with several advantages over copyparty * but anything you want to share must be moved into the kodbox filesystem * [seafile](https://github.com/haiwen/seafile) ([review](#seafile)) and [nextcloud](https://github.com/nextcloud/server) ([review](#nextcloud)) could be decent alternatives if you need something heavier than copyparty - * but their [license](https://snyk.io/learn/agpl-license/) is [problematic](https://opensource.google/documentation/reference/using/agpl-policy) + * but their [license (AGPL)](https://snyk.io/learn/agpl-license/) is [thorny](https://opensource.google/documentation/reference/using/agpl-policy) * and copyparty is way better at uploads in particular (resumable, accelerated) * and anything you want to share must be moved into the respective filesystems * [filebrowser](https://github.com/filebrowser/filebrowser) ([review](#filebrowser)) and [dufs](https://github.com/sigoden/dufs) ([review](#dufs)) are simpler copyparties but with a settings gui @@ -123,14 +123,14 @@ symbol legend, | ----------------------- | - | - | - | - | - | - | - | - | - | - | - | - | - | | intuitive UX | | โ•ฑ | โ–ˆ | โ–ˆ | โ–ˆ | | โ–ˆ | โ–ˆ | โ–ˆ | โ–ˆ | โ–ˆ | โ–ˆ | โ–ˆ | | config GUI | | โ–ˆ | โ–ˆ | โ–ˆ | โ–ˆ | | | โ–ˆ | โ–ˆ | โ–ˆ | | โ–ˆ | โ–ˆ | -| good documentation | | | | โ–ˆ | โ–ˆ | โ–ˆ | โ–ˆ | | | โ–ˆ | โ–ˆ | โ•ฑ | โ•ฑ | +| good documentation | | | โ–ˆ | โ–ˆ | โ–ˆ | โ–ˆ | โ–ˆ | | | โ–ˆ | โ–ˆ | โ•ฑ | โ•ฑ | | runs on iOS | โ•ฑ | | | | | โ•ฑ | | | | | | | | -| runs on Android | โ–ˆ | | | | | โ–ˆ | | | | | | | | +| runs on Android | โ–ˆ | | โ–ˆ | | | โ–ˆ | | | | | | | | | runs on WinXP | โ–ˆ | โ–ˆ | | | | โ–ˆ | | | | | | | | | runs on Windows | โ–ˆ | โ–ˆ | โ–ˆ | โ–ˆ | โ–ˆ | โ–ˆ | โ–ˆ | โ•ฑ | โ–ˆ | โ–ˆ | โ–ˆ | โ–ˆ | โ•ฑ | | runs on Linux | โ–ˆ | โ•ฑ | โ–ˆ | โ–ˆ | โ–ˆ | โ–ˆ | โ–ˆ | โ–ˆ | โ–ˆ | โ–ˆ | โ–ˆ | โ–ˆ | โ–ˆ | | runs on Macos | โ–ˆ | | โ–ˆ | โ–ˆ | โ–ˆ | โ–ˆ | โ–ˆ | โ–ˆ | โ–ˆ | โ–ˆ | โ–ˆ | โ–ˆ | | -| runs on FreeBSD | โ–ˆ | | | โ€ข | โ–ˆ | โ–ˆ | โ–ˆ | โ€ข | โ–ˆ | โ–ˆ | | โ–ˆ | | +| runs on FreeBSD | โ–ˆ | | โ–ˆ | โ€ข | โ–ˆ | โ–ˆ | โ–ˆ | โ€ข | โ–ˆ | โ–ˆ | | โ–ˆ | | | runs on Risc-V | โ–ˆ | | | โ–ˆ | โ–ˆ | โ–ˆ | | โ€ข | | โ–ˆ | | | | | portable binary | โ–ˆ | โ–ˆ | โ–ˆ | | | โ–ˆ | โ–ˆ | | | โ–ˆ | | โ–ˆ | โ–ˆ | | zero setup, just go | โ–ˆ | โ–ˆ | โ–ˆ | | | โ•ฑ | โ–ˆ | | | โ–ˆ | | โ•ฑ | โ–ˆ | @@ -140,7 +140,7 @@ symbol legend, * `zero setup` = you can get a mostly working setup by just launching the app, without having to install any software or configure whatever * `a`/copyparty remarks: * no gui for server settings; only for client-side stuff - * can theoretically run on iOS / iPads using [iSH](https://ish.app/), but only the iPad will offer sufficient multitasking i think + * 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 * `b`/hfs2 runs on linux through wine @@ -161,7 +161,7 @@ symbol legend, | upload | โ–ˆ | โ–ˆ | โ–ˆ | โ–ˆ | โ–ˆ | โ–ˆ | โ–ˆ | โ–ˆ | โ–ˆ | โ–ˆ | โ•ฑ | โ–ˆ | โ–ˆ | | parallel uploads | โ–ˆ | | | โ–ˆ | โ–ˆ | | โ€ข | | โ–ˆ | โ–ˆ | โ–ˆ | | โ–ˆ | | resumable uploads | โ–ˆ | | โ–ˆ | | | | | | โ–ˆ | โ–ˆ | โ–ˆ | โ•ฑ | | -| upload segmenting | โ–ˆ | | | โ–ˆ | | | | โ–ˆ | โ–ˆ | โ–ˆ | โ–ˆ | โ•ฑ | โ–ˆ | +| upload segmenting | โ–ˆ | | โ–ˆ | โ–ˆ | | | | โ–ˆ | โ–ˆ | โ–ˆ | โ–ˆ | โ•ฑ | โ–ˆ | | upload acceleration | โ–ˆ | | | | | | | | โ–ˆ | | โ–ˆ | | | | upload verification | โ–ˆ | | | โ–ˆ | โ–ˆ | | | | โ–ˆ | | | | | | upload deduplication | โ–ˆ | | | | โ–ˆ | | | | โ–ˆ | | | | | @@ -169,7 +169,7 @@ symbol legend, | CTRL-V from device | โ–ˆ | | | โ–ˆ | | | | | | | | | | | race the beam ("p2p") | โ–ˆ | | | | | | | | | | | | | | "tail -f" streaming | โ–ˆ | | | | | | | | | | | | | -| keep last-modified time | โ–ˆ | | | โ–ˆ | โ–ˆ | โ–ˆ | | | | | | โ–ˆ | | +| keep last-modified time | โ–ˆ | | โ–ˆ | โ–ˆ | โ–ˆ | โ–ˆ | | | | | | โ–ˆ | | | upload rules | โ•ฑ | โ•ฑ | โ•ฑ | โ•ฑ | โ•ฑ | | | โ•ฑ | โ•ฑ | | โ•ฑ | โ•ฑ | โ•ฑ | | โ”— max disk usage | โ–ˆ | โ–ˆ | โ–ˆ | | โ–ˆ | | | | โ–ˆ | | | โ–ˆ | โ–ˆ | | โ”— max filesize | โ–ˆ | | | | | | | โ–ˆ | | | โ–ˆ | โ–ˆ | โ–ˆ | @@ -251,7 +251,7 @@ symbol legend, | feature / software | a | b | c | d | e | f | g | h | i | j | k | l | m | | ----------------------- | - | - | - | - | - | - | - | - | - | - | - | - | - | -| config from cmd args | โ–ˆ | | | | | โ–ˆ | โ–ˆ | | | โ–ˆ | | โ•ฑ | โ•ฑ | +| config from cmd args | โ–ˆ | | โ–ˆ | | | โ–ˆ | โ–ˆ | | | โ–ˆ | | โ•ฑ | โ•ฑ | | config files | โ–ˆ | โ–ˆ | โ–ˆ | โ•ฑ | โ•ฑ | โ–ˆ | | โ–ˆ | | โ–ˆ | โ€ข | โ•ฑ | โ•ฑ | | runtime config reload | โ–ˆ | โ–ˆ | โ–ˆ | | | | | โ–ˆ | โ–ˆ | โ–ˆ | โ–ˆ | | โ–ˆ | | same-port http / https | โ–ˆ | | | | | | | | | | | | | @@ -276,10 +276,10 @@ symbol legend, | per-account chroot | | | | | | | | | | | | โ–ˆ | | | single-sign-on | โ•ฑ | | | โ–ˆ | โ–ˆ | | | | โ€ข | | | | | | token auth | โ•ฑ | | | โ–ˆ | โ–ˆ | | | โ–ˆ | | | | | โ–ˆ | -| 2fa | โ•ฑ | | | โ–ˆ | โ–ˆ | | | | | | | โ–ˆ | โ•ฑ | +| 2fa | โ•ฑ | | / | โ–ˆ | โ–ˆ | | | | | | | โ–ˆ | โ•ฑ | | per-volume permissions | โ–ˆ | โ–ˆ | โ–ˆ | โ–ˆ | โ–ˆ | โ–ˆ | โ–ˆ | | โ–ˆ | โ–ˆ | โ•ฑ | โ–ˆ | โ–ˆ | -| per-folder permissions | โ•ฑ | | | โ–ˆ | โ–ˆ | | โ–ˆ | | โ–ˆ | โ–ˆ | โ•ฑ | โ–ˆ | โ–ˆ | -| per-file permissions | | | | โ–ˆ | โ–ˆ | | โ–ˆ | | โ–ˆ | | | | โ–ˆ | +| per-folder permissions | โ•ฑ | | โ–ˆ | โ–ˆ | โ–ˆ | | โ–ˆ | | โ–ˆ | โ–ˆ | โ•ฑ | โ–ˆ | โ–ˆ | +| per-file permissions | | | โ–ˆ | โ–ˆ | โ–ˆ | | โ–ˆ | | โ–ˆ | | | | โ–ˆ | | per-file passwords | โ–ˆ | | | โ–ˆ | โ–ˆ | | โ–ˆ | | โ–ˆ | | | | โ–ˆ | | unmap subfolders | โ–ˆ | | โ–ˆ | | | | โ–ˆ | | | โ–ˆ | โ•ฑ | โ€ข | | | index.html blocks list | โ•ฑ | | | | | | โ–ˆ | | | โ€ข | | | | @@ -297,13 +297,13 @@ symbol legend, | full sync | | | | โ–ˆ | โ–ˆ | | | | | | | | | | speed throttle | | โ–ˆ | โ–ˆ | | | โ–ˆ | | | โ–ˆ | | | โ–ˆ | | | anti-bruteforce | โ–ˆ | โ–ˆ | โ–ˆ | โ–ˆ | โ–ˆ | | | | โ€ข | | | โ–ˆ | โ€ข | -| dyndns updater | | โ–ˆ | | | | | | | | | | | | +| dyndns updater | | โ–ˆ | โ–ˆ | | | | | | | | | | | | self-updater | | | โ–ˆ | | | | | | | | | | โ–ˆ | | log rotation | โ–ˆ | | โ–ˆ | โ–ˆ | โ–ˆ | | | โ€ข | โ–ˆ | | | โ–ˆ | โ€ข | | upload tracking / log | โ–ˆ | โ–ˆ | โ€ข | โ–ˆ | โ–ˆ | | | โ–ˆ | โ–ˆ | | | โ•ฑ | โ–ˆ | | prometheus metrics | โ–ˆ | | | โ–ˆ | | | | | | | | โ–ˆ | | | curl-friendly ls | โ–ˆ | | | | | | | | | | | | | -| curl-friendly upload | โ–ˆ | | | | | โ–ˆ | โ–ˆ | โ€ข | | | | | | +| curl-friendly upload | โ–ˆ | | โ–ˆ | | | โ–ˆ | โ–ˆ | โ€ข | | | | | | * `unmap subfolders` = "shadowing"; mounting a local folder in the middle of an existing filesystem tree in order to disable access below that path * `files stored as-is` = uploaded files are trivially readable from the server HDD, not sliced into chunks or in weird folder structures or anything like that @@ -332,7 +332,8 @@ symbol legend, * `upload tracking / log` in main logfile * `m`/arozos: * `2fa` maybe possible through LDAP/Oauth - +* `c`/hfs3 + * `2fa` available by installing a plugin ## client features @@ -342,18 +343,18 @@ symbol legend, | themes | โ–ˆ | โ–ˆ | โ–ˆ | โ–ˆ | | | | | โ–ˆ | | | | | | directory tree nav | โ–ˆ | โ•ฑ | | | โ–ˆ | | | | โ–ˆ | | โ•ฑ | | | | multi-column sorting | โ–ˆ | | | | | | | | | | | | | -| thumbnails | โ–ˆ | | | โ•ฑ | โ•ฑ | | | โ–ˆ | โ–ˆ | โ•ฑ | | | โ–ˆ | -| โ”— image thumbnails | โ–ˆ | | | โ–ˆ | โ–ˆ | | | โ–ˆ | โ–ˆ | โ–ˆ | | | โ–ˆ | +| thumbnails | โ–ˆ | | / | โ•ฑ | โ•ฑ | | | โ–ˆ | โ–ˆ | โ•ฑ | | | โ–ˆ | +| โ”— image thumbnails | โ–ˆ | | / | โ–ˆ | โ–ˆ | | | โ–ˆ | โ–ˆ | โ–ˆ | | | โ–ˆ | | โ”— video thumbnails | โ–ˆ | | | โ–ˆ | โ–ˆ | | | | โ–ˆ | | | | โ–ˆ | | โ”— audio spectrograms | โ–ˆ | | | | | | | | | | | | | | audio player | โ–ˆ | | โ•ฑ | โ–ˆ | โ–ˆ | | | | โ–ˆ | โ•ฑ | | | โ–ˆ | | โ”— gapless playback | โ–ˆ | | | | | | | | โ€ข | | | | | | โ”— audio equalizer | โ–ˆ | | | | | | | | | | | | | | โ”— waveform seekbar | โ–ˆ | | | | | | | | | | | | | -| โ”— OS integration | โ–ˆ | | | | | | | | | | | | | +| โ”— OS integration | โ–ˆ | | โ–ˆ | | | | | | | | | | | | โ”— transcode to lossy | โ–ˆ | | | | | | | | | | | | | -| video player | โ–ˆ | | | โ–ˆ | โ–ˆ | | | | โ–ˆ | โ–ˆ | | | โ–ˆ | -| โ”— video transcoding | | | | | | | | | โ–ˆ | | | | | +| video player | โ–ˆ | | โ–ˆ | โ–ˆ | โ–ˆ | | | | โ–ˆ | โ–ˆ | | | โ–ˆ | +| โ”— video transcoding | | | / | | | | | | โ–ˆ | | | | | | audio BPM detector | โ–ˆ | | | | | | | | | | | | | | audio key detector | โ–ˆ | | | | | | | | | | | | | | search by path / name | โ–ˆ | โ–ˆ | โ–ˆ | โ–ˆ | โ–ˆ | | โ–ˆ | | โ–ˆ | โ–ˆ | โ•ฑ | | | @@ -366,15 +367,15 @@ symbol legend, | undo recent uploads | โ–ˆ | | | | | | | | | | | | | | create directories | โ–ˆ | | โ–ˆ | โ–ˆ | โ–ˆ | โ•ฑ | โ–ˆ | โ–ˆ | โ–ˆ | โ–ˆ | โ–ˆ | โ–ˆ | โ–ˆ | | image viewer | โ–ˆ | | โ–ˆ | โ–ˆ | โ–ˆ | | | | โ–ˆ | โ–ˆ | โ–ˆ | | โ–ˆ | -| markdown viewer | โ–ˆ | | | | โ–ˆ | | | | โ–ˆ | โ•ฑ | โ•ฑ | | โ–ˆ | +| markdown viewer | โ–ˆ | | / | | โ–ˆ | | | | โ–ˆ | โ•ฑ | โ•ฑ | | โ–ˆ | | markdown editor | โ–ˆ | | | | โ–ˆ | | | | โ–ˆ | โ•ฑ | โ•ฑ | | โ–ˆ | -| readme.md in listing | โ–ˆ | | | โ–ˆ | | | | | | | | | | +| readme.md in listing | โ–ˆ | | / | โ–ˆ | | | | | | | | | | | rename files | โ–ˆ | โ–ˆ | โ–ˆ | โ–ˆ | โ–ˆ | โ•ฑ | โ–ˆ | | โ–ˆ | โ–ˆ | โ–ˆ | โ–ˆ | โ–ˆ | | batch rename | โ–ˆ | | | | | | | | โ–ˆ | | | | | -| cut / paste files | โ–ˆ | โ–ˆ | | โ–ˆ | โ–ˆ | | | | โ–ˆ | | | | โ–ˆ | +| cut / paste files | โ–ˆ | โ–ˆ | โ–ˆ | โ–ˆ | โ–ˆ | | | | โ–ˆ | | | | โ–ˆ | | move files | โ–ˆ | โ–ˆ | โ–ˆ | โ–ˆ | โ–ˆ | | โ–ˆ | | โ–ˆ | โ–ˆ | โ–ˆ | | โ–ˆ | | delete files | โ–ˆ | โ–ˆ | โ–ˆ | โ–ˆ | โ–ˆ | โ•ฑ | โ–ˆ | โ–ˆ | โ–ˆ | โ–ˆ | โ–ˆ | โ–ˆ | โ–ˆ | -| copy files | | | | | โ–ˆ | | | | โ–ˆ | โ–ˆ | โ–ˆ | | โ–ˆ | +| copy files | | | / | | โ–ˆ | | | | โ–ˆ | โ–ˆ | โ–ˆ | | โ–ˆ | * `single-page app` = multitasking; possible to continue navigating while uploading * `audio player ยป os-integration` = use the [lockscreen](https://user-images.githubusercontent.com/241032/142711926-0700be6c-3e31-47b3-9928-53722221f722.png) or [media hotkeys](https://user-images.githubusercontent.com/241032/215347492-b4250797-6c90-4e09-9a4c-721edf2fb15c.png) to play/pause, prev/next song @@ -383,8 +384,6 @@ symbol legend, * `undo recent uploads` = accounts without delete permissions have a time window where they can undo their own uploads * `a`/copyparty has teeny-tiny skips playing gapless albums depending on audio codec (opus best) * `b`/hfs2 has a very basic directory tree view, not showing sibling folders -* `c`/hfs3 remarks: - * audio playback does not continue into next song * `f`/rclone can do some file management (mkdir, rename, delete) when hosting througn webdav * `j`/filebrowser remarks: * audio playback does not continue into next song @@ -396,9 +395,9 @@ symbol legend, | feature / software | a | b | c | d | e | f | g | h | i | j | k | l | m | | ----------------------- | - | - | - | - | - | - | - | - | - | - | - | - | - | -| OS alert on upload | โ–ˆ | | | | | | | | | โ•ฑ | | โ•ฑ | | -| discord | โ–ˆ | | | | | | | | | โ•ฑ | | โ•ฑ | | -| โ”— announce uploads | โ–ˆ | | | | | | | | | | | โ•ฑ | | +| OS alert on upload | โ•ฑ | | | | | | | | | โ•ฑ | | โ•ฑ | | +| discord | โ•ฑ | | | | | | | | | โ•ฑ | | โ•ฑ | | +| โ”— announce uploads | โ•ฑ | | | | | | | | | | | โ•ฑ | | | โ”— custom embeds | | | | | | | | | | | | โ•ฑ | | | sharex | โ–ˆ | | | โ–ˆ | | โ–ˆ | โ•ฑ | โ–ˆ | | | | | | | flameshot | | | | | | โ–ˆ | | | | | | | | @@ -471,10 +470,8 @@ symbol legend, * vfs with gui config, per-volume permissions * tested locally, v0.53.2 on archlinux * ๐Ÿ”ต uploads are resumable -* โš ๏ธ uploads are not segmented; max upload size 100 MiB on cloudflare * โš ๏ธ uploads are not accelerated (copyparty is 3x faster across the atlantic) * โš ๏ธ uploads are not integrity-checked -* โš ๏ธ copies the file after upload; need twice filesize free disk space * โš ๏ธ uploading small files is decent; `107` files per sec (copyparty does `670`/sec, 6x faster) * โš ๏ธ doesn't support crazy filenames * โœ… config GUI @@ -575,7 +572,7 @@ symbol legend, * โœ… file tags; file discussions!? * โœ… video transcoding * โœ… unzip uploaded archives -* โœ… IDE with syntax hilighting +* โœ… IDE with syntax highlighting * โœ… wysiwyg editor for openoffice files ## [filebrowser](https://github.com/filebrowser/filebrowser) diff --git a/flake.nix b/flake.nix index 5f39d605..336642ee 100644 --- a/flake.nix +++ b/flake.nix @@ -12,19 +12,7 @@ }: { nixosModules.default = ./contrib/nixos/modules/copyparty.nix; - overlays.default = final: prev: rec { - copyparty = final.python3.pkgs.callPackage ./contrib/package/nix/copyparty { - ffmpeg = final.ffmpeg-full; - }; - - partyfuse = prev.callPackage ./contrib/package/nix/partyfuse { - inherit copyparty; - }; - - u2c = prev.callPackage ./contrib/package/nix/u2c { - inherit copyparty; - }; - }; + overlays.default = import ./contrib/package/nix/overlay.nix; } // flake-utils.lib.eachDefaultSystem ( system: @@ -54,8 +42,6 @@ packages = { inherit (pkgs) copyparty - partyfuse - u2c ; default = self.packages.${system}.copyparty; }; diff --git a/scripts/docker/Dockerfile.ac b/scripts/docker/Dockerfile.ac index c06f99f8..54d6fef1 100644 --- a/scripts/docker/Dockerfile.ac +++ b/scripts/docker/Dockerfile.ac @@ -1,7 +1,7 @@ FROM alpine:latest WORKDIR /z LABEL org.opencontainers.image.url="https://github.com/9001/copyparty" \ - org.opencontainers.image.source="https://github.com/9001/copyparty/tree/hovudstraum/scripts/docker" \ + org.opencontainers.image.source="https://github.com/9001/copyparty" \ org.opencontainers.image.licenses="MIT" \ org.opencontainers.image.title="copyparty-ac" \ org.opencontainers.image.description="copyparty with Pillow and FFmpeg (image/audio/video thumbnails, audio transcoding, media tags)" @@ -16,6 +16,6 @@ COPY i/dist/copyparty-sfx.py innvikler.sh ./ ADD base ./base RUN ash innvikler.sh ac -WORKDIR /w +WORKDIR /state EXPOSE 3923 -ENTRYPOINT ["python3", "-m", "copyparty", "--no-crt", "-c", "/z/initcfg"] +ENTRYPOINT ["python3", "-m", "copyparty", "-c", "/z/initcfg"] diff --git a/scripts/docker/Dockerfile.dj b/scripts/docker/Dockerfile.dj index 8302a2cb..b377537b 100644 --- a/scripts/docker/Dockerfile.dj +++ b/scripts/docker/Dockerfile.dj @@ -1,7 +1,7 @@ FROM alpine:latest WORKDIR /z LABEL org.opencontainers.image.url="https://github.com/9001/copyparty" \ - org.opencontainers.image.source="https://github.com/9001/copyparty/tree/hovudstraum/scripts/docker" \ + org.opencontainers.image.source="https://github.com/9001/copyparty" \ org.opencontainers.image.licenses="MIT" \ org.opencontainers.image.title="copyparty-dj" \ org.opencontainers.image.description="copyparty with all optional dependencies, including musical key / bpm detection" @@ -19,13 +19,16 @@ RUN apk add -U !pyc \ vips-jxl vips-heif vips-poppler vips-magick \ py3-numpy fftw libsndfile \ vamp-sdk vamp-sdk-libs \ + libraw py3-numpy cython \ && apk add -t .bd \ bash wget gcc g++ make cmake patchelf \ python3-dev ffmpeg-dev fftw-dev libsndfile-dev \ py3-wheel py3-numpy-dev libffi-dev \ vamp-sdk-dev \ + libraw-dev py3-numpy-dev \ && rm -f /usr/lib/python3*/EXTERNALLY-MANAGED \ && python3 -m pip install pyvips \ + && python3 -m pip install "$(wget -O- https://api.github.com/repos/letmaik/rawpy/releases/latest | awk -F\" '$2=="tarball_url"{print$4}')" \ && bash install-deps.sh \ && apk del py3-pip .bd \ && chmod 777 /root \ @@ -35,6 +38,6 @@ COPY i/dist/copyparty-sfx.py innvikler.sh ./ ADD base ./base RUN ash innvikler.sh dj -WORKDIR /w +WORKDIR /state EXPOSE 3923 -ENTRYPOINT ["python3", "-m", "copyparty", "--no-crt", "-c", "/z/initcfg"] +ENTRYPOINT ["python3", "-m", "copyparty", "-c", "/z/initcfg"] diff --git a/scripts/docker/Dockerfile.im b/scripts/docker/Dockerfile.im index 94a7ad1a..f4f8124d 100644 --- a/scripts/docker/Dockerfile.im +++ b/scripts/docker/Dockerfile.im @@ -1,7 +1,7 @@ FROM alpine:latest WORKDIR /z LABEL org.opencontainers.image.url="https://github.com/9001/copyparty" \ - org.opencontainers.image.source="https://github.com/9001/copyparty/tree/hovudstraum/scripts/docker" \ + org.opencontainers.image.source="https://github.com/9001/copyparty" \ org.opencontainers.image.licenses="MIT" \ org.opencontainers.image.title="copyparty-im" \ org.opencontainers.image.description="copyparty with Pillow and Mutagen (image thumbnails, media tags)" @@ -15,6 +15,6 @@ COPY i/dist/copyparty-sfx.py innvikler.sh ./ ADD base ./base RUN ash innvikler.sh im -WORKDIR /w +WORKDIR /state EXPOSE 3923 -ENTRYPOINT ["python3", "-m", "copyparty", "--no-crt", "-c", "/z/initcfg"] +ENTRYPOINT ["python3", "-m", "copyparty", "-c", "/z/initcfg"] diff --git a/scripts/docker/Dockerfile.iv b/scripts/docker/Dockerfile.iv index d639d3df..6155b327 100644 --- a/scripts/docker/Dockerfile.iv +++ b/scripts/docker/Dockerfile.iv @@ -1,7 +1,7 @@ FROM alpine:latest WORKDIR /z LABEL org.opencontainers.image.url="https://github.com/9001/copyparty" \ - org.opencontainers.image.source="https://github.com/9001/copyparty/tree/hovudstraum/scripts/docker" \ + org.opencontainers.image.source="https://github.com/9001/copyparty" \ org.opencontainers.image.licenses="MIT" \ org.opencontainers.image.title="copyparty-iv" \ org.opencontainers.image.description="copyparty with Pillow, FFmpeg, libvips (image/audio/video thumbnails, audio transcoding, media tags)" @@ -14,17 +14,20 @@ RUN apk add -U !pyc \ ffmpeg \ py3-magic \ vips-jxl vips-heif vips-poppler vips-magick \ + libraw py3-numpy cython \ && apk add -t .bd \ bash wget gcc g++ make cmake patchelf \ python3-dev py3-wheel libffi-dev \ + libraw-dev py3-numpy-dev \ && rm -f /usr/lib/python3*/EXTERNALLY-MANAGED \ && python3 -m pip install pyvips \ + && python3 -m pip install "$(wget -O- https://api.github.com/repos/letmaik/rawpy/releases/latest | awk -F\" '$2=="tarball_url"{print$4}')" \ && apk del py3-pip .bd COPY i/dist/copyparty-sfx.py innvikler.sh ./ ADD base ./base RUN ash innvikler.sh iv -WORKDIR /w +WORKDIR /state EXPOSE 3923 -ENTRYPOINT ["python3", "-m", "copyparty", "--no-crt", "-c", "/z/initcfg"] +ENTRYPOINT ["python3", "-m", "copyparty", "-c", "/z/initcfg"] diff --git a/scripts/docker/Dockerfile.min b/scripts/docker/Dockerfile.min index 4712a8ad..8d8f380a 100644 --- a/scripts/docker/Dockerfile.min +++ b/scripts/docker/Dockerfile.min @@ -1,7 +1,7 @@ FROM alpine:latest WORKDIR /z LABEL org.opencontainers.image.url="https://github.com/9001/copyparty" \ - org.opencontainers.image.source="https://github.com/9001/copyparty/tree/hovudstraum/scripts/docker" \ + org.opencontainers.image.source="https://github.com/9001/copyparty" \ org.opencontainers.image.licenses="MIT" \ org.opencontainers.image.title="copyparty-min" \ org.opencontainers.image.description="just copyparty, no thumbnails / media tags / audio transcoding" @@ -13,6 +13,6 @@ RUN apk --no-cache add !pyc \ COPY i/dist/copyparty-sfx.py innvikler.sh ./ RUN ash innvikler.sh min -WORKDIR /w +WORKDIR /state EXPOSE 3923 -ENTRYPOINT ["python3", "-m", "copyparty", "--no-crt", "--no-thumb", "-c", "/z/initcfg"] +ENTRYPOINT ["python3", "-m", "copyparty", "--no-thumb", "-c", "/z/initcfg"] diff --git a/scripts/docker/innvikler.sh b/scripts/docker/innvikler.sh index 27a3d230..75b3ca3d 100644 --- a/scripts/docker/innvikler.sh +++ b/scripts/docker/innvikler.sh @@ -15,9 +15,15 @@ rm -rf /z/base rm -rf /var/cache/apk/* /root/.cache # initial config; common for all flavors -mkdir /cfg /w -chmod 777 /cfg /w -echo % /cfg > initcfg +mkdir /state /cfg /w +chmod 777 /state /cfg /w +cat >initcfg <<'EOF' +[global] + chdir: /w + no-crt + +% /cfg +EOF # unpack sfx and dive in python3 copyparty-sfx.py --version diff --git a/scripts/docker/make.sh b/scripts/docker/make.sh index 88758c2a..13a4a4bb 100755 --- a/scripts/docker/make.sh +++ b/scripts/docker/make.sh @@ -102,12 +102,18 @@ filt= # arm takes forever so make it top priority [ ${a::3} == arm ] && nice= || nice=-n20 + # not sure if this is necessary or if inherit-annotations=false was enough, but won't hurt + readarray -t annot < <(awk <Dockerfile.$i '/org.opencontainers.image/{sub(/[^\.]+/,"");sub(/[" \\]+$/,"");sub(/"/,"");print"--annotation";print"org"$0}') + annot+=( --annotation "org.opencontainers.image.created=$( date -u +%Y-%m-%dT%H:%M:%SZ )" ) + # --pull=never does nothing at all btw (set -x nice $nice podman build \ --squash \ --pull=never \ --from localhost/alpine-$a \ + --inherit-annotations=false \ + "${annot[@]}" \ -t copyparty-$i-$a$suf \ -f Dockerfile.$i . || (echo $? $i-$a >> err; printf '%096d\n' $(seq 1 42)) diff --git a/scripts/help2html.py b/scripts/help2html.py index 540091ec..4803632b 100755 --- a/scripts/help2html.py +++ b/scripts/help2html.py @@ -7,7 +7,7 @@ import subprocess as sp # to convert the copyparty --help to html, run this in xfce4-terminal @ 140x43: _ = r"""" -echo; for a in '' -bind -accounts -flags -handlers -hooks -urlform -exp -ls -dbd -chmod -pwhash -zm; do +echo; for a in '' -bind -accounts -auth -auth-ord -flags -handlers -hooks -idp -urlform -exp -ls -dbd -chmod -pwhash -zm; do ./copyparty-sfx.py --help$a 2>/dev/null; printf '\n\n\n%0139d\n\n\n'; done # xfce4-terminal @ 140x43 """ # click [edit] => [select all] diff --git a/scripts/help2txt.sh b/scripts/help2txt.sh index f5a12565..a046bda3 100755 --- a/scripts/help2txt.sh +++ b/scripts/help2txt.sh @@ -23,7 +23,7 @@ exit 0 # first open an infinitely wide console (this is why you own an ultrawide) and copypaste this into it: -for a in '' -bind -accounts -flags -handlers -hooks -urlform -exp -ls -dbd -chmod -pwhash -zm; do +for a in '' -bind -accounts -auth -auth-ord -flags -handlers -hooks -idp -urlform -exp -ls -dbd -chmod -pwhash -zm; do ./copyparty-sfx.py --help$a 2>/dev/null; printf '\n\n\n%0255d\n\n\n'; done # then copypaste all of the output by pressing ctrl-shift-a, ctrl-shift-c diff --git a/scripts/make-rpm.sh b/scripts/make-rpm.sh new file mode 100755 index 00000000..5dd6573c --- /dev/null +++ b/scripts/make-rpm.sh @@ -0,0 +1,66 @@ +#!/bin/bash +set -e + +#--localbuild to build webdeps and tar locally; otherwise just download prebuilt +#--pm change packagemanager; otherwise default to dnf + +while [ ! -z "$1" ]; do + case $1 in + local-build) local_build=1 ; ;; + pm) shift;packagemanager="$1"; ;; + esac + shift +done + +[ -e copyparty/__main__.py ] || cd .. +[ -e copyparty/__main__.py ] || +{ + echo "run me from within the project root folder" + echo + exit 1 +} + + +packagemanager=${packagemanager:-dnf} +ver=$(awk '/^VERSION/{gsub(/[^0-9]/," ");printf "%d.%d.%d\n",$1,$2,$3}' copyparty/__version__.py) +releasedir="dist/temp_copyparty_$ver" +sourcepkg="copyparty-$ver.tar.gz" + +#make temporary directory to build rpm in +mkdir -p $releasedir/{BUILD,BUILDROOT,RPMS,SOURCES,SPECS,SRPMS} +trap "rm -rf $releasedir" EXIT + +# make/get tarball +if [ $local_build ]; then + if [ ! -f "copyparty/web/deps/mini-fa.woff" ]; then + sudo $packagemanager update + sudo $packagemanager install podman-docker docker + make -C deps-docker + fi + if [ ! -f "dist/$sourcepkg" ]; then + ./$cppdir/scripts/make-sfx.sh gz fast # pulls some build-deps + good smoketest + ./$cppdir/scripts/make-tgz-release.sh "$ver" + fi +else + if [ ! -f "dist/$sourcepkg" ]; then + curl -OL https://github.com/9001/copyparty/releases/download/v$ver/$sourcepkg --output-dir dist + fi +fi + +cp dist/$sourcepkg "$releasedir/SOURCES/$sourcepkg" + +cp "contrib/package/rpm/copyparty.spec" "$releasedir/SPECS/" +sed -i "s/\$pkgver/$ver/g" "$releasedir/SPECS/copyparty.spec" +sed -i "s/\$pkgrel/1/g" "$releasedir/SPECS/copyparty.spec" + +sudo $packagemanager update +sudo $packagemanager install \ + rpmdevtools python-devel pyproject-rpm-macros \ + python-wheel python-setuptools python-jinja2 \ + make pigz +cd "$releasedir/" +rpmbuild --define "_topdir `pwd`" -bb SPECS/copyparty.spec +cd - + +rpm="copyparty-$ver-1.noarch.rpm" +mv "$releasedir/RPMS/noarch/$rpm" dist/$rpm diff --git a/scripts/make-sfx.sh b/scripts/make-sfx.sh index a1ee1a06..100792ca 100755 --- a/scripts/make-sfx.sh +++ b/scripts/make-sfx.sh @@ -41,7 +41,7 @@ help() { exec cat <<'EOF' # `no-cm` saves ~89k by removing easymde/codemirror # (the fancy markdown editor) # -# `no-hl` saves ~41k by removing syntax hilighting in the text viewer +# `no-hl` saves ~41k by removing syntax highlighting in the text viewer # # `no-fnt` saves ~9k by removing the source-code-pro font # (browsers will try to use 'Consolas' instead) @@ -217,6 +217,8 @@ necho() { tar -zxf $f mv pyftpdlib-*/pyftpdlib . rm -rf pyftpdlib-* pyftpdlib/test + patch -s -p1 <../scripts/patches/pyftpdlib-win313.patch + patch -s -p1 <../scripts/patches/pyftpdlib-fe80.patch for f in pyftpdlib/_async{hat,ore}.py; do [ -e "$f" ] || continue; iawk 'NR<4||NR>27||!/^#/;NR==4{print"# license: https://opensource.org/licenses/ISC\n"}' $f diff --git a/scripts/patches/pyftpdlib-fe80.patch b/scripts/patches/pyftpdlib-fe80.patch new file mode 100644 index 00000000..48b0cae1 --- /dev/null +++ b/scripts/patches/pyftpdlib-fe80.patch @@ -0,0 +1,37 @@ +accept connections from IPv6 link-local addresses + +diff -NarU1 a/pyftpdlib/handlers.py b/pyftpdlib2/handlers.py +--- a/pyftpdlib/handlers.py 2024-06-23 14:03:38 ++++ b/pyftpdlib/handlers.py 2025-08-22 21:59:40 +@@ -451,3 +451,4 @@ + +- local_ip = self.cmd_channel.socket.getsockname()[0] ++ sockname = list(self.cmd_channel.socket.getsockname()) ++ local_ip = sockname[0] + if local_ip in self.cmd_channel.masquerade_address_map: +@@ -459,3 +460,5 @@ + +- if self.cmd_channel.server.socket.family != socket.AF_INET: ++ if local_ip.startswith('fe') and local_ip[2:3] in "89ab": ++ af = socket.AF_INET6 # link-local ++ elif self.cmd_channel.server.socket.family != socket.AF_INET: + # dual stack IPv4/IPv6 support +@@ -472,3 +475,4 @@ + # free unprivileged random port. +- self.bind((local_ip, 0)) ++ sockname[1] = 0 ++ self.bind(tuple(sockname)) + else: +@@ -478,4 +482,5 @@ + self.set_reuse_addr() ++ sockname[1] = port + try: +- self.bind((local_ip, port)) ++ self.bind(tuple(sockname)) + except PermissionError: +@@ -495,3 +500,4 @@ + else: +- self.bind((local_ip, 0)) ++ sockname[1] = 0 ++ self.bind(tuple(sockname)) + self.cmd_channel.log( diff --git a/scripts/patches/pyftpdlib-win313.patch b/scripts/patches/pyftpdlib-win313.patch new file mode 100644 index 00000000..62601108 --- /dev/null +++ b/scripts/patches/pyftpdlib-win313.patch @@ -0,0 +1,41 @@ +Date: Tue, 22 Oct 2024 12:47:30 +0200 +Subject: Workaround for isabs() on Windows + Python 3.13 (#652) + +Starting from Python 3.13, `os.path.isabs("/foo")` on Windows return `False` + +diff --git a/pyftpdlib/filesystems.py b/pyftpdlib/filesystems.py +index 9b9326bf..320ffe40 100644 +--- a/pyftpdlib/filesystems.py ++++ b/pyftpdlib/filesystems.py +@@ -132,6 +132,16 @@ def cwd(self, path): + + # --- Pathname / conversion utilities + ++ @staticmethod ++ def _isabs(path, _windows=os.name == "nt"): ++ # Windows + Python 3.13: isabs() changed so that a path ++ # starting with "/" is no longer considered absolute. ++ # https://github.com/python/cpython/issues/44626 ++ # https://github.com/python/cpython/pull/113829/ ++ if _windows and path.startswith("/"): ++ return True ++ return os.path.isabs(path) ++ + def ftpnorm(self, ftppath): + """Normalize a "virtual" ftp pathname (typically the raw string + coming from client) depending on the current working directory. +@@ -146,3 +156,3 @@ + assert isinstance(ftppath, unicode), ftppath +- if os.path.isabs(ftppath): ++ if self._isabs(ftppath): + p = os.path.normpath(ftppath) +@@ -162,3 +172,3 @@ + # This is for extra protection, maybe not really necessary. +- if not os.path.isabs(p): ++ if not self._isabs(p): + p = u("/") +@@ -201,3 +211,3 @@ + assert isinstance(fspath, unicode), fspath +- if os.path.isabs(fspath): ++ if self._isabs(fspath): + p = os.path.normpath(fspath) diff --git a/scripts/prep.sh b/scripts/prep.sh index d522371e..4e9ae313 100755 --- a/scripts/prep.sh +++ b/scripts/prep.sh @@ -40,7 +40,7 @@ update_mpr_pkgbuild() { update_nixos_pin() { ( cd $self/../contrib/package/nix/copyparty; - ./update.py $self/../dist/copyparty-sfx.py ) + ./update.py $self/../dist/copyparty-$ver.tar.gz ) } update_arch_pkgbuild diff --git a/scripts/pyinstaller/deps.sha512 b/scripts/pyinstaller/deps.sha512 index 1d324c0e..5aa63686 100644 --- a/scripts/pyinstaller/deps.sha512 +++ b/scripts/pyinstaller/deps.sha512 @@ -30,5 +30,5 @@ a726fb46cce24f781fc8b55a3e6dea0a884ebc3b2b400ea74aa02333699f4955a5dc1e2ec5927ac7 3e39ea6e16b502d99a2e6544579095d0f7c6097761cd85135d5e929b9dec1b32e80669a846f94ee8c2cca9be2f5fe728625d09453988864c04e16bb8445c3f91 pillow-11.3.0-cp313-cp313-win_amd64.whl 59fbbcae044f4ee73d203ac74b553b27bfad3e6b2f3fb290fd3f8774753c6b545176b6b3399c240b092d131d152290ce732750accd962dc1e48e930be85f5e53 pyinstaller-6.14.1-py3-none-win_amd64.whl fc6f3e144c5f5b662412de07cb8bf0c2eb3b3be21d19ec448aef3c4244d779b9ab8027fd67a4871e6e13823b248ea0f5a7a9241a53aef30f3b51a6d3cb5bdb3f pyinstaller_hooks_contrib-2025.5-py3-none-any.whl -3c37ea72ab062f65116a5faaaccbaa960a971797c78dbe6a504f41e562b018e7f28924647b5577fdb4fedfa61ffe0f1153842a8585f93b0332ed4d97905f6609 python-3.13.6-amd64.exe +36db028e9f3d6805a57e89320283c07bd5eb0bb15c6edcd2ae4a7e46b06bfe6c96ed0793e8936cbb09b4f6b680a3f06dace2220a1e7d8b74ab6047698871db9e python-3.13.7-amd64.exe 2a0420f7faaa33d2132b82895a8282688030e939db0225ad8abb95a47bdb87b45318f10985fc3cee271a9121441c1526caa363d7f2e4a4b18b1a674068766e87 setuptools-80.9.0-py3-none-any.whl diff --git a/scripts/pyinstaller/notes.txt b/scripts/pyinstaller/notes.txt index 44cebd10..6cce39d0 100644 --- a/scripts/pyinstaller/notes.txt +++ b/scripts/pyinstaller/notes.txt @@ -40,7 +40,7 @@ fns=( pillow-11.3.0-cp313-cp313-win_amd64.whl pyinstaller-6.14.1-py3-none-win_amd64.whl pyinstaller_hooks_contrib-2025.5-py3-none-any.whl - python-3.13.6-amd64.exe + python-3.13.7-amd64.exe setuptools-80.9.0-py3-none-any.whl ) [ $w7 ] && fns+=( diff --git a/scripts/rls.sh b/scripts/rls.sh index 767ca2de..d18f12f2 100755 --- a/scripts/rls.sh +++ b/scripts/rls.sh @@ -43,6 +43,8 @@ while [ "$1" ]; do shift done +./make-pyz.sh + ./make-sfx.sh re lang eng "$@" mv ../dist/copyparty-{sfx,en}.py mv ../dist/copyparty-{int,sfx}.py diff --git a/scripts/tl.js b/scripts/tl.js index 97bf061d..4d4fcaba 100644 --- a/scripts/tl.js +++ b/scripts/tl.js @@ -81,6 +81,7 @@ var tl_cpanel = { "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", }, }; diff --git a/scripts/toc.sh b/scripts/toc.sh index 8c3e22e9..611e092a 100755 --- a/scripts/toc.sh +++ b/scripts/toc.sh @@ -20,7 +20,7 @@ cat $f | awk ' o{next} /^#/{s=1;rs=0;pr()} /^#* *(nix package)/{rs=1} - /^#* *(themes|install on android|dev env setup|just the sfx|complete release|optional gpl stuff|nixos module|reverse-proxy perf)|```/{s=rs} + /^#* *(themes|install on android|install on iOS|dev env setup|just the sfx|complete release|optional gpl stuff|nixos module|reverse-proxy perf)|```/{s=rs} /^#/{ lv=length($1); sub(/[^ ]+ /,""); diff --git a/tests/test_idp.py b/tests/test_idp.py index bd81378f..21d46758 100644 --- a/tests/test_idp.py +++ b/tests/test_idp.py @@ -63,7 +63,7 @@ class TestVFS(unittest.TestCase): cfgdir = os.path.join(here, "res", "idp") # globals are applied by main so need to cheat a little - xcfg = {"idp_h_usr": "x-idp-user", "idp_h_grp": "x-idp-group"} + xcfg = {"idp_h_usr": ["x-idp-user"], "idp_h_grp": "x-idp-group"} return here, cfgdir, xcfg diff --git a/tests/util.py b/tests/util.py index d3884ca9..cfb5979d 100644 --- a/tests/util.py +++ b/tests/util.py @@ -143,7 +143,7 @@ class Cfg(Namespace): def __init__(self, a=None, v=None, c=None, **ka0): ka = {} - ex = "allow_flac allow_wav chpw cookie_lax daw dav_auth dav_mac dav_rt e2d e2ds e2dsa e2t e2ts e2tsr e2v e2vu e2vp early_ban ed emp exp force_js getmod grid gsel hardlink hardlink_only ih ihead localtime magic nid nih no_acode no_athumb no_bauth no_clone no_cp no_dav no_db_ip no_del no_dirsz no_dupe no_fnugg no_lifetime no_logues no_mv no_pipe no_poll no_readme no_robots no_sb_md no_sb_lg no_scandir no_tail no_tarcmp no_thumb no_vthumb no_zip nrand nsort nw og og_no_head og_s_title ohead q rand re_dirsz reflink rmagic rss smb srch_dbg srch_excl stats uqe usernames vague_403 vc ver wo_up_readme write_uplog xdev xlink xvol zipmaxu zs" + ex = "allow_flac allow_wav chpw cookie_lax daw dav_auth dav_mac dav_rt e2d e2ds e2dsa e2t e2ts e2tsr e2v e2vu e2vp early_ban ed emp exp force_js getmod grid gsel hardlink hardlink_only ih ihead localtime log_badxml magic md_no_br nid nih no_acode no_athumb no_bauth no_clone no_cp no_dav no_db_ip no_del no_dirsz no_dupe no_fnugg no_lifetime no_logues no_mv no_pipe no_poll no_readme no_robots no_sb_md no_sb_lg no_scandir no_tail no_tarcmp no_thumb no_vthumb no_u2abrt no_zip nrand nsort nw og og_no_head og_s_title ohead q rand re_dirsz reflink rmagic rss smb srch_dbg srch_excl stats uqe usernames vague_403 vc ver wo_up_readme write_uplog xdev xlink xvol zipmaxu zs" ka.update(**{k: False for k in ex.split()}) ex = "dav_inf dedup dotpart dotsrch hook_v no_dhash no_fastboot no_fpool no_htp no_rescan no_sendfile no_ses no_snap no_up_list no_voldump re_dhash see_dots plain_ip" @@ -155,22 +155,22 @@ class Cfg(Namespace): ex = "gid uid" ka.update(**{k: -1 for k in ex.split()}) - ex = "hash_mt hsortn qdel safe_dedup srch_time tail_fd tail_rate u2abort u2j u2sz" + ex = "hash_mt hsortn qdel safe_dedup srch_time tail_fd tail_rate th_spec_p u2abort u2j u2sz unp_who" ka.update(**{k: 1 for k in ex.split()}) - ex = "au_vol dl_list mtab_age reg_cap s_thead s_tbody tail_tmax tail_who th_convt ups_who zip_who" + ex = "ac_convt au_vol dl_list du_iwho mtab_age reg_cap s_thead s_tbody tail_tmax tail_who th_convt ups_who ver_iwho zip_who" ka.update(**{k: 9 for k in ex.split()}) - ex = "ctl_re db_act forget_ip idp_cookie idp_store k304 loris no304 nosubtle re_maxage rproxy rsp_jtr rsp_slp s_wr_slp snap_wri theme themes turbo u2ow zipmaxn zipmaxs" + ex = "ctl_re db_act forget_ip idp_cookie idp_store k304 loris no304 nosubtle qr_pin qr_wait re_maxage rproxy rsp_jtr rsp_slp s_wr_slp snap_wri theme themes turbo u2ow zipmaxn zipmaxs" ka.update(**{k: 0 for k in ex.split()}) - ex = "ah_alg bname chmod_f chpw_db doctitle df exit favico idp_h_usr ipa html_head lg_sba lg_sbf log_fk md_sba md_sbf name og_desc og_site og_th og_title og_title_a og_title_v og_title_i shr tcolor textfiles txt_eol unlist vname xff_src zipmaxt R RS SR" + ex = "ah_alg bname chdir chmod_f chpw_db doctitle df exit favico ipa html_head idp_login idp_logout lg_sba lg_sbf log_fk md_sba md_sbf name og_desc og_site og_th og_title og_title_a og_title_v og_title_i shr tcolor textfiles txt_eol unlist vname xff_src zipmaxt R RS SR" ka.update(**{k: "" for k in ex.split()}) ex = "ban_403 ban_404 ban_422 ban_pw ban_pwc ban_url spinner" ka.update(**{k: "no" for k in ex.split()}) - ex = "ext_th grp on403 on404 xac xad xar xau xban xbc xbd xbr xbu xiu xm" + ex = "ext_th grp idp_h_usr idp_hm_usr ipr on403 on404 qr_file xac xad xar xau xban xbc xbd xbr xbu xiu xm" ka.update(**{k: [] for k in ex.split()}) ex = "exp_lg exp_md" @@ -183,11 +183,16 @@ class Cfg(Namespace): v=v or [], c=c, E=E, + auth_ord="idp,ipu", bup_ck="sha512", chmod_d="755", + cookie_cmax=8192, + cookie_nmax=50, dbd="wal", + du_who="all", dk_salt="b" * 16, fk_salt="a" * 16, + grp_all="acct", idp_gsep=re.compile("[|:;+,]"), iobuf=256 * 1024, lang="eng", @@ -203,6 +208,7 @@ class Cfg(Namespace): rm_retry="0/0", s_rd_sz=256 * 1024, s_wr_sz=256 * 1024, + shr_who="auth", sort="href", srch_hits=99999, SRS="/", @@ -216,6 +222,7 @@ class Cfg(Namespace): u2sort="s", u2ts="c", unpost=600, + ver_who="all", warksalt="hunter2", **ka ) @@ -285,6 +292,7 @@ class VHttpSrv(object): self.broker = NullBroker(args, asrv) self.prism = None + self.ipr = None self.bans = {} self.tdls = self.dls = {} self.tdli = self.dli = {} @@ -340,6 +348,7 @@ class VHttpConn(object): Ctor = VHttpSrvUp2k if use_up2k else VHttpSrv self.hsrv = Ctor(args, asrv, log) self.ico = Ico(args) + self.ipr = None self.ipa_nm = None self.lf_url = None self.log_func = log