Compare commits
29 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dc2ea20959 | ||
|
|
8eaea2bd17 | ||
|
|
58e559918f | ||
|
|
f38a3fca5b | ||
|
|
1ea145b384 | ||
|
|
0d9567575a | ||
|
|
e82f176289 | ||
|
|
d4b51c040e | ||
|
|
125d0efbd8 | ||
|
|
3215afc504 | ||
|
|
c73ff3ce1b | ||
|
|
f9c159a051 | ||
|
|
2ab1325c90 | ||
|
|
5b0f7ff506 | ||
|
|
9269bc84f2 | ||
|
|
4e8b651e18 | ||
|
|
65b4f79534 | ||
|
|
5dd43dbc45 | ||
|
|
5f73074c7e | ||
|
|
f5d6ba27b2 | ||
|
|
73fa70b41f | ||
|
|
2a1cda42e7 | ||
|
|
1bd7e31466 | ||
|
|
eb49e1fb4a | ||
|
|
9838c2f0ce | ||
|
|
6041df8370 | ||
|
|
2933dce3ef | ||
|
|
dab377d37b | ||
|
|
f35e41baf1 |
28
README.md
28
README.md
@@ -63,6 +63,7 @@ turn your phone or raspi into a portable file server with resumable uploads/down
|
|||||||
* [file parser plugins](#file-parser-plugins) - provide custom parsers to index additional tags, also see [./bin/mtag/README.md](./bin/mtag/README.md)
|
* [file parser plugins](#file-parser-plugins) - provide custom parsers to index additional tags, also see [./bin/mtag/README.md](./bin/mtag/README.md)
|
||||||
* [upload events](#upload-events) - trigger a script/program on each upload
|
* [upload events](#upload-events) - trigger a script/program on each upload
|
||||||
* [hiding from google](#hiding-from-google) - tell search engines you dont wanna be indexed
|
* [hiding from google](#hiding-from-google) - tell search engines you dont wanna be indexed
|
||||||
|
* [themes](#themes)
|
||||||
* [complete examples](#complete-examples)
|
* [complete examples](#complete-examples)
|
||||||
* [browser support](#browser-support) - TLDR: yes
|
* [browser support](#browser-support) - TLDR: yes
|
||||||
* [client examples](#client-examples) - interact with copyparty using non-browser clients
|
* [client examples](#client-examples) - interact with copyparty using non-browser clients
|
||||||
@@ -247,6 +248,8 @@ some improvement ideas
|
|||||||
|
|
||||||
## not my bugs
|
## not my bugs
|
||||||
|
|
||||||
|
* [Chrome issue 1317069](https://bugs.chromium.org/p/chromium/issues/detail?id=1317069) -- if you try to upload a folder which contains symlinks by dragging it into the browser, the symlinked files will not get uploaded
|
||||||
|
|
||||||
* iPhones: the volume control doesn't work because [apple doesn't want it to](https://developer.apple.com/library/archive/documentation/AudioVideo/Conceptual/Using_HTML5_Audio_Video/Device-SpecificConsiderations/Device-SpecificConsiderations.html#//apple_ref/doc/uid/TP40009523-CH5-SW11)
|
* iPhones: the volume control doesn't work because [apple doesn't want it to](https://developer.apple.com/library/archive/documentation/AudioVideo/Conceptual/Using_HTML5_Audio_Video/Device-SpecificConsiderations/Device-SpecificConsiderations.html#//apple_ref/doc/uid/TP40009523-CH5-SW11)
|
||||||
* *future workaround:* enable the equalizer, make it all-zero, and set a negative boost to reduce the volume
|
* *future workaround:* enable the equalizer, make it all-zero, and set a negative boost to reduce the volume
|
||||||
* "future" because `AudioContext` is broken in the current iOS version (15.1), maybe one day...
|
* "future" because `AudioContext` is broken in the current iOS version (15.1), maybe one day...
|
||||||
@@ -806,6 +809,29 @@ tell search engines you dont wanna be indexed, either using the good old [robot
|
|||||||
also, `--force-js` disables the plain HTML folder listing, making things harder to parse for search engines
|
also, `--force-js` disables the plain HTML folder listing, making things harder to parse for search engines
|
||||||
|
|
||||||
|
|
||||||
|
## themes
|
||||||
|
|
||||||
|
you can change the default theme with `--theme 2`, and add your own themes by modifying `browser.css` or providing your own css to `--css-browser`, then telling copyparty they exist by increasing `--themes`
|
||||||
|
|
||||||
|
<table><tr><td width="33%" align="center"><a href="https://user-images.githubusercontent.com/241032/165864907-17e2ac7d-319d-4f25-8718-2f376f614b51.png"><img src="https://user-images.githubusercontent.com/241032/165867551-fceb35dd-38f0-42bb-bef3-25ba651ca69b.png"></a>
|
||||||
|
0. classic dark</td><td width="33%" align="center"><a href="https://user-images.githubusercontent.com/241032/165864904-c5b67ddd-f383-4b9e-9f5a-a3bde183d256.png"><img src="https://user-images.githubusercontent.com/241032/165867556-077b6068-2488-4fae-bf88-1fce40e719bc.png"></a>
|
||||||
|
2. flat dark</td><td width="33%" align="center"><a href="https://user-images.githubusercontent.com/241032/165864901-db13a429-a5da-496d-8bc6-ce838547f69d.png"><img src="https://user-images.githubusercontent.com/241032/165867560-aa834aef-58dc-4abe-baef-7e562b647945.png"></a>
|
||||||
|
4. vice</td></tr><tr><td align="center"><a href="https://user-images.githubusercontent.com/241032/165864905-692682eb-6fb4-4d40-b6fe-27d2c7d3e2a7.png"><img src="https://user-images.githubusercontent.com/241032/165867555-080b73b6-6d85-41bb-a7c6-ad277c608365.png"></a>
|
||||||
|
1. classic light</td><td align="center"><a href="https://user-images.githubusercontent.com/241032/165864903-7fba1cb9-036b-4f11-90d5-28b7c0724353.png"><img src="https://user-images.githubusercontent.com/241032/165867557-b5cc0010-d880-48b1-8156-9c84f7bbc521.png"></a>
|
||||||
|
3. flat light
|
||||||
|
</td><td align="center"><a href="https://user-images.githubusercontent.com/241032/165864898-10ce7052-a117-4fcf-845b-b56c91687908.png"><img src="https://user-images.githubusercontent.com/241032/165867562-f3003d45-dd2a-4564-8aae-fed44c1ae064.png"></a>
|
||||||
|
5. <a href="https://blog.codinghorror.com/a-tribute-to-the-windows-31-hot-dog-stand-color-scheme/">hotdog stand</a></td></tr></table>
|
||||||
|
|
||||||
|
the classname of the HTML tag is set according to the selected theme, which is used to set colors as css variables ++
|
||||||
|
|
||||||
|
* each theme *generally* has a dark theme (even numbers) and a light theme (odd numbers), showing in pairs
|
||||||
|
* the first theme (theme 0 and 1) is `html.a`, second theme (2 and 3) is `html.b`
|
||||||
|
* if a light theme is selected, `html.y` is set, otherwise `html.z` is
|
||||||
|
* so if the dark edition of the 2nd theme is selected, you use any of `html.b`, `html.z`, `html.bz` to specify rules
|
||||||
|
|
||||||
|
see the top of [./copyparty/web/browser.css](./copyparty/web/browser.css) where the color variables are set, and there's layout-specific stuff near the bottom
|
||||||
|
|
||||||
|
|
||||||
## complete examples
|
## complete examples
|
||||||
|
|
||||||
* read-only music server with bpm and key scanning
|
* read-only music server with bpm and key scanning
|
||||||
@@ -1178,7 +1204,7 @@ python3 -m venv .venv
|
|||||||
pip install jinja2 # mandatory
|
pip install jinja2 # mandatory
|
||||||
pip install mutagen # audio metadata
|
pip install mutagen # audio metadata
|
||||||
pip install Pillow pyheif-pillow-opener pillow-avif-plugin # thumbnails
|
pip install Pillow pyheif-pillow-opener pillow-avif-plugin # thumbnails
|
||||||
pip install black bandit pylint flake8 # vscode tooling
|
pip install black==21.12b0 bandit pylint flake8 # vscode tooling
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -12,6 +12,8 @@
|
|||||||
# change '/mnt::rw' to another location or permission-set
|
# change '/mnt::rw' to another location or permission-set
|
||||||
# remove '-p 80,443,3923' to only listen on port 3923
|
# remove '-p 80,443,3923' to only listen on port 3923
|
||||||
# add '-i 127.0.0.1' to only allow local connections
|
# add '-i 127.0.0.1' to only allow local connections
|
||||||
|
# add '-e2dsa' to enable filesystem scanning + indexing
|
||||||
|
# add '-e2ts' to enable metadata indexing
|
||||||
#
|
#
|
||||||
# with `Type=notify`, copyparty will signal systemd when it is ready to
|
# with `Type=notify`, copyparty will signal systemd when it is ready to
|
||||||
# accept connections; correctly delaying units depending on copyparty.
|
# accept connections; correctly delaying units depending on copyparty.
|
||||||
@@ -34,7 +36,7 @@ SyslogIdentifier=copyparty
|
|||||||
Environment=PYTHONUNBUFFERED=x
|
Environment=PYTHONUNBUFFERED=x
|
||||||
ExecReload=/bin/kill -s USR1 $MAINPID
|
ExecReload=/bin/kill -s USR1 $MAINPID
|
||||||
ExecStartPre=/bin/bash -c 'mkdir -p /run/tmpfiles.d/ && echo "x /tmp/pe-copyparty*" > /run/tmpfiles.d/copyparty.conf'
|
ExecStartPre=/bin/bash -c 'mkdir -p /run/tmpfiles.d/ && echo "x /tmp/pe-copyparty*" > /run/tmpfiles.d/copyparty.conf'
|
||||||
ExecStart=/usr/bin/python3 /usr/local/bin/copyparty-sfx.py -q -p 80,443,3923 -v /mnt::rw
|
ExecStart=/usr/bin/python3 /usr/local/bin/copyparty-sfx.py -q -p 80,443,3923 -e2d -v /mnt::rw
|
||||||
|
|
||||||
[Install]
|
[Install]
|
||||||
WantedBy=multi-user.target
|
WantedBy=multi-user.target
|
||||||
|
|||||||
@@ -291,9 +291,9 @@ def run_argparse(argv, formatter):
|
|||||||
dedent(
|
dedent(
|
||||||
"""
|
"""
|
||||||
-a takes username:password,
|
-a takes username:password,
|
||||||
-v takes src:dst:perm1:perm2:permN:volflag1:volflag2:volflagN:...
|
-v takes src:dst:\033[33mperm\033[0m1:\033[33mperm\033[0m2:\033[33mperm\033[0mN:\033[32mvolflag\033[0m1:\033[32mvolflag\033[0m2:\033[32mvolflag\033[0mN:...
|
||||||
where "perm" is "permissions,username1,username2,..."
|
* "\033[33mperm\033[0m" is "permissions,username1,username2,..."
|
||||||
and "volflag" is config flags to set on this volume
|
* "\033[32mvolflag\033[0m" is config flags to set on this volume
|
||||||
|
|
||||||
list of permissions:
|
list of permissions:
|
||||||
"r" (read): list folder contents, download files
|
"r" (read): list folder contents, download files
|
||||||
@@ -365,6 +365,17 @@ def run_argparse(argv, formatter):
|
|||||||
generate ".bpm" tags from uploads (f = overwrite tags)
|
generate ".bpm" tags from uploads (f = overwrite tags)
|
||||||
\033[36mmtp=ahash,vhash=media-hash.py\033[35m collects two tags at once
|
\033[36mmtp=ahash,vhash=media-hash.py\033[35m collects two tags at once
|
||||||
|
|
||||||
|
\033[0mthumbnails:
|
||||||
|
\033[36mdthumb\033[35m disables all thumbnails
|
||||||
|
\033[36mdvthumb\033[35m disables video thumbnails
|
||||||
|
\033[36mdathumb\033[35m disables audio thumbnails (spectrograms)
|
||||||
|
\033[36mdithumb\033[35m disables image thumbnails
|
||||||
|
|
||||||
|
\033[0mclient and ux:
|
||||||
|
\033[36mhtml_head=TXT\033[35m includes TXT in the <head>
|
||||||
|
\033[36mrobots\033[35m allows indexing by search engines (default)
|
||||||
|
\033[36mnorobots\033[35m kindly asks search engines to leave
|
||||||
|
|
||||||
\033[0mothers:
|
\033[0mothers:
|
||||||
\033[36mfk=8\033[35m generates per-file accesskeys,
|
\033[36mfk=8\033[35m generates per-file accesskeys,
|
||||||
which will then be required at the "g" permission
|
which will then be required at the "g" permission
|
||||||
@@ -373,7 +384,7 @@ def run_argparse(argv, formatter):
|
|||||||
],
|
],
|
||||||
[
|
[
|
||||||
"urlform",
|
"urlform",
|
||||||
"",
|
"how to handle url-form POSTs",
|
||||||
dedent(
|
dedent(
|
||||||
"""
|
"""
|
||||||
values for --urlform:
|
values for --urlform:
|
||||||
@@ -412,40 +423,41 @@ def run_argparse(argv, formatter):
|
|||||||
ap2.add_argument("-c", metavar="PATH", type=u, action="append", help="add config file")
|
ap2.add_argument("-c", metavar="PATH", type=u, action="append", help="add config file")
|
||||||
ap2.add_argument("-nc", metavar="NUM", type=int, default=64, help="max num clients")
|
ap2.add_argument("-nc", metavar="NUM", type=int, default=64, help="max num clients")
|
||||||
ap2.add_argument("-j", metavar="CORES", type=int, default=1, help="max num cpu cores, 0=all")
|
ap2.add_argument("-j", metavar="CORES", type=int, default=1, help="max num cpu cores, 0=all")
|
||||||
ap2.add_argument("-a", metavar="ACCT", type=u, action="append", help="add account, USER:PASS; example [ed:wark")
|
ap2.add_argument("-a", metavar="ACCT", type=u, action="append", help="add account, USER:PASS; example [ed:wark]")
|
||||||
ap2.add_argument("-v", metavar="VOL", type=u, action="append", help="add volume, SRC:DST:FLAG; example [.::r], [/mnt/nas/music:/music:r:aed")
|
ap2.add_argument("-v", metavar="VOL", type=u, action="append", help="add volume, SRC:DST:FLAG; examples [.::r], [/mnt/nas/music:/music:r:aed]")
|
||||||
ap2.add_argument("-ed", action="store_true", help="enable ?dots")
|
ap2.add_argument("-ed", action="store_true", help="enable the ?dots url parameter / client option which allows clients to see dotfiles / hidden files")
|
||||||
ap2.add_argument("-emp", action="store_true", help="enable markdown plugins")
|
ap2.add_argument("-emp", action="store_true", help="enable markdown plugins -- neat but dangerous, big XSS risk")
|
||||||
ap2.add_argument("-mcr", metavar="SEC", type=int, default=60, help="md-editor mod-chk rate")
|
ap2.add_argument("-mcr", metavar="SEC", type=int, default=60, help="md-editor mod-chk rate")
|
||||||
ap2.add_argument("--urlform", metavar="MODE", type=u, default="print,get", help="how to handle url-forms; examples: [stash], [save,get]")
|
ap2.add_argument("--urlform", metavar="MODE", type=u, default="print,get", help="how to handle url-form POSTs; see --help-urlform")
|
||||||
ap2.add_argument("--wintitle", metavar="TXT", type=u, default="cpp @ $pub", help="window title, for example '$ip-10.1.2.' or '$ip-'")
|
ap2.add_argument("--wintitle", metavar="TXT", type=u, default="cpp @ $pub", help="window title, for example '$ip-10.1.2.' or '$ip-'")
|
||||||
|
|
||||||
ap2 = ap.add_argument_group('upload options')
|
ap2 = ap.add_argument_group('upload options')
|
||||||
ap2.add_argument("--dotpart", action="store_true", help="dotfile incomplete uploads")
|
ap2.add_argument("--dotpart", action="store_true", help="dotfile incomplete uploads, hiding them from clients unless -ed")
|
||||||
ap2.add_argument("--sparse", metavar="MiB", type=int, default=4, help="up2k min.size threshold (mswin-only)")
|
ap2.add_argument("--sparse", metavar="MiB", type=int, default=4, help="windows-only: minimum size of incoming uploads through up2k before they are made into sparse files")
|
||||||
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")
|
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")
|
||||||
ap2.add_argument("--no-fpool", action="store_true", help="disable file-handle pooling -- instead, repeatedly close and reopen files during upload")
|
ap2.add_argument("--no-fpool", action="store_true", help="disable file-handle pooling -- instead, repeatedly close and reopen files during upload")
|
||||||
ap2.add_argument("--use-fpool", action="store_true", help="force file-handle pooling, even if copyparty thinks you're better off without")
|
ap2.add_argument("--use-fpool", action="store_true", help="force file-handle pooling, even if copyparty thinks you're better off without -- probably useful on nfs and cow filesystems (zfs, btrfs)")
|
||||||
ap2.add_argument("--hardlink", action="store_true", help="prefer hardlinks instead of symlinks when possible (same filesystem)")
|
ap2.add_argument("--hardlink", action="store_true", help="prefer hardlinks instead of symlinks when possible (within same filesystem)")
|
||||||
ap2.add_argument("--never-symlink", action="store_true", help="do not fallback to symlinks when a hardlink cannot be made")
|
ap2.add_argument("--never-symlink", action="store_true", help="do not fallback to symlinks when a hardlink cannot be made")
|
||||||
ap2.add_argument("--no-dedup", action="store_true", help="disable symlink/hardlink creation; copy file contents instead")
|
ap2.add_argument("--no-dedup", action="store_true", help="disable symlink/hardlink creation; copy file contents instead")
|
||||||
ap2.add_argument("--reg-cap", metavar="N", type=int, default=9000, help="max number of uploads to keep in memory when running without -e2d")
|
ap2.add_argument("--reg-cap", metavar="N", type=int, default=9000, help="max number of uploads to keep in memory when running without -e2d")
|
||||||
|
ap2.add_argument("--turbo", metavar="LVL", type=int, default=0, help="configure turbo-mode in up2k client; 0 = off and warn if enabled, 1 = off, 2 = on, 3 = on and disable datecheck")
|
||||||
|
|
||||||
ap2 = ap.add_argument_group('network options')
|
ap2 = ap.add_argument_group('network options')
|
||||||
ap2.add_argument("-i", metavar="IP", type=u, default="0.0.0.0", help="ip to bind (comma-sep.)")
|
ap2.add_argument("-i", metavar="IP", type=u, default="0.0.0.0", help="ip to bind (comma-sep.)")
|
||||||
ap2.add_argument("-p", metavar="PORT", type=u, default="3923", help="ports to bind (comma/range)")
|
ap2.add_argument("-p", metavar="PORT", type=u, default="3923", help="ports to bind (comma/range)")
|
||||||
ap2.add_argument("--rproxy", metavar="DEPTH", type=int, default=1, help="which ip to keep; 0 = tcp, 1 = origin (first x-fwd), 2 = cloudflare, 3 = nginx, -1 = closest proxy")
|
ap2.add_argument("--rproxy", metavar="DEPTH", type=int, default=1, help="which ip to keep; 0 = tcp, 1 = origin (first x-fwd), 2 = cloudflare, 3 = nginx, -1 = closest proxy")
|
||||||
ap2.add_argument("--s-wr-sz", metavar="B", type=int, default=256*1024, help="socket write size in bytes")
|
ap2.add_argument("--s-wr-sz", metavar="B", type=int, default=256*1024, help="socket write size in bytes")
|
||||||
ap2.add_argument("--s-wr-slp", metavar="SEC", type=float, default=0, help="socket write delay in seconds")
|
ap2.add_argument("--s-wr-slp", metavar="SEC", type=float, default=0, help="debug: socket write delay in seconds")
|
||||||
ap2.add_argument("--rsp-slp", metavar="SEC", type=float, default=0, help="response delay in seconds")
|
ap2.add_argument("--rsp-slp", metavar="SEC", type=float, default=0, help="debug: response delay in seconds")
|
||||||
|
|
||||||
ap2 = ap.add_argument_group('SSL/TLS options')
|
ap2 = ap.add_argument_group('SSL/TLS options')
|
||||||
ap2.add_argument("--http-only", action="store_true", help="disable ssl/tls")
|
ap2.add_argument("--http-only", action="store_true", help="disable ssl/tls -- force plaintext")
|
||||||
ap2.add_argument("--https-only", action="store_true", help="disable plaintext")
|
ap2.add_argument("--https-only", action="store_true", help="disable plaintext -- force tls")
|
||||||
ap2.add_argument("--ssl-ver", metavar="LIST", type=u, help="set allowed ssl/tls versions; [help] shows available versions; default is what your python version considers safe")
|
ap2.add_argument("--ssl-ver", metavar="LIST", type=u, help="set allowed ssl/tls versions; [help] shows available versions; default is what your python version considers safe")
|
||||||
ap2.add_argument("--ciphers", metavar="LIST", type=u, help="set allowed ssl/tls ciphers; [help] shows available ciphers")
|
ap2.add_argument("--ciphers", metavar="LIST", type=u, help="set allowed ssl/tls ciphers; [help] shows available ciphers")
|
||||||
ap2.add_argument("--ssl-dbg", action="store_true", help="dump some tls info")
|
ap2.add_argument("--ssl-dbg", action="store_true", help="dump some tls info")
|
||||||
ap2.add_argument("--ssl-log", metavar="PATH", type=u, help="log master secrets")
|
ap2.add_argument("--ssl-log", metavar="PATH", type=u, help="log master secrets for later decryption in wireshark")
|
||||||
|
|
||||||
ap2 = ap.add_argument_group('FTP options')
|
ap2 = ap.add_argument_group('FTP options')
|
||||||
ap2.add_argument("--ftp", metavar="PORT", type=int, help="enable FTP server on PORT, for example 3921")
|
ap2.add_argument("--ftp", metavar="PORT", type=int, help="enable FTP server on PORT, for example 3921")
|
||||||
@@ -455,25 +467,25 @@ def run_argparse(argv, formatter):
|
|||||||
ap2.add_argument("--ftp-pr", metavar="P-P", type=u, help="the range of TCP ports to use for passive connections, for example 12000-13000")
|
ap2.add_argument("--ftp-pr", metavar="P-P", type=u, help="the range of TCP ports to use for passive connections, for example 12000-13000")
|
||||||
|
|
||||||
ap2 = ap.add_argument_group('opt-outs')
|
ap2 = ap.add_argument_group('opt-outs')
|
||||||
ap2.add_argument("-nw", action="store_true", help="disable writes (benchmark)")
|
ap2.add_argument("-nw", action="store_true", help="never write anything to disk (debug/benchmark)")
|
||||||
ap2.add_argument("--keep-qem", action="store_true", help="do not disable quick-edit-mode on windows")
|
ap2.add_argument("--keep-qem", action="store_true", help="do not disable quick-edit-mode on windows (it is disabled to avoid accidental text selection which will deadlock copyparty)")
|
||||||
ap2.add_argument("--no-del", action="store_true", help="disable delete operations")
|
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-mv", action="store_true", help="disable move/rename operations")
|
||||||
ap2.add_argument("-nih", action="store_true", help="no info hostname")
|
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")
|
ap2.add_argument("-nid", action="store_true", help="no info disk-usage -- don't show in UI")
|
||||||
ap2.add_argument("--no-zip", action="store_true", help="disable download as zip/tar")
|
ap2.add_argument("--no-zip", action="store_true", help="disable download as zip/tar")
|
||||||
ap2.add_argument("--no-lifetime", action="store_true", help="disable automatic deletion of uploads after a certain time (lifetime volflag)")
|
ap2.add_argument("--no-lifetime", action="store_true", help="disable automatic deletion of uploads after a certain time (lifetime volflag)")
|
||||||
|
|
||||||
ap2 = ap.add_argument_group('safety options')
|
ap2 = ap.add_argument_group('safety options')
|
||||||
ap2.add_argument("--ls", metavar="U[,V[,F]]", type=u, help="scan all volumes; arguments USER,VOL,FLAGS; example [**,*,ln,p,r]")
|
ap2.add_argument("--ls", metavar="U[,V[,F]]", type=u, help="do a sanity/safety check of all volumes on startup; arguments USER,VOL,FLAGS; example [**,*,ln,p,r]")
|
||||||
ap2.add_argument("--salt", type=u, default="hunter2", help="up2k file-hash salt")
|
ap2.add_argument("--salt", type=u, default="hunter2", help="up2k file-hash salt; used to generate unpredictable internal identifiers for uploads -- doesn't really matter")
|
||||||
ap2.add_argument("--fk-salt", metavar="SALT", type=u, default=fk_salt, help="per-file accesskey salt")
|
ap2.add_argument("--fk-salt", metavar="SALT", type=u, default=fk_salt, help="per-file accesskey salt; used to generate unpredictable URLs for hidden files -- this one DOES matter")
|
||||||
ap2.add_argument("--no-dot-mv", action="store_true", help="disallow moving dotfiles; makes it impossible to move folders containing dotfiles")
|
ap2.add_argument("--no-dot-mv", action="store_true", help="disallow moving dotfiles; makes it impossible to move folders containing dotfiles")
|
||||||
ap2.add_argument("--no-dot-ren", action="store_true", help="disallow renaming dotfiles; makes it impossible to make something a dotfile")
|
ap2.add_argument("--no-dot-ren", action="store_true", help="disallow renaming dotfiles; makes it impossible to make something a dotfile")
|
||||||
ap2.add_argument("--no-logues", action="store_true", help="disable rendering .prologue/.epilogue.html into directory listings")
|
ap2.add_argument("--no-logues", action="store_true", help="disable rendering .prologue/.epilogue.html into directory listings")
|
||||||
ap2.add_argument("--no-readme", action="store_true", help="disable rendering readme.md into directory listings")
|
ap2.add_argument("--no-readme", action="store_true", help="disable rendering readme.md into directory listings")
|
||||||
ap2.add_argument("--vague-403", action="store_true", help="send 404 instead of 403 (security through ambiguity, very enterprise)")
|
ap2.add_argument("--vague-403", action="store_true", help="send 404 instead of 403 (security through ambiguity, very enterprise)")
|
||||||
ap2.add_argument("--force-js", action="store_true", help="don't send HTML folder listings, force clients to use the embedded json instead")
|
ap2.add_argument("--force-js", action="store_true", help="don't send folder listings as HTML, force clients to use the embedded json instead -- slight protection against misbehaving search engines which ignore --no-robots")
|
||||||
ap2.add_argument("--no-robots", action="store_true", help="adds http and html headers asking search engines to not index anything")
|
ap2.add_argument("--no-robots", action="store_true", help="adds http and html headers asking search engines to not index anything")
|
||||||
|
|
||||||
ap2 = ap.add_argument_group('yolo options')
|
ap2 = ap.add_argument_group('yolo options')
|
||||||
@@ -484,8 +496,8 @@ def run_argparse(argv, formatter):
|
|||||||
ap2.add_argument("-q", action="store_true", help="quiet")
|
ap2.add_argument("-q", action="store_true", help="quiet")
|
||||||
ap2.add_argument("-lo", metavar="PATH", type=u, help="logfile, example: cpp-%%Y-%%m%%d-%%H%%M%%S.txt.xz")
|
ap2.add_argument("-lo", metavar="PATH", type=u, help="logfile, example: cpp-%%Y-%%m%%d-%%H%%M%%S.txt.xz")
|
||||||
ap2.add_argument("--no-voldump", action="store_true", help="do not list volumes and permissions on startup")
|
ap2.add_argument("--no-voldump", action="store_true", help="do not list volumes and permissions on startup")
|
||||||
ap2.add_argument("--log-conn", action="store_true", help="print tcp-server msgs")
|
ap2.add_argument("--log-conn", action="store_true", help="debug: print tcp-server msgs")
|
||||||
ap2.add_argument("--log-htp", action="store_true", help="print http-server threadpool scaling")
|
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="dump incoming header")
|
ap2.add_argument("--ihead", metavar="HEADER", type=u, action='append', help="dump incoming header")
|
||||||
ap2.add_argument("--lf-url", metavar="RE", type=u, default=r"^/\.cpr/|\?th=[wj]$", help="dont log URLs matching")
|
ap2.add_argument("--lf-url", metavar="RE", type=u, default=r"^/\.cpr/|\?th=[wj]$", help="dont log URLs matching")
|
||||||
|
|
||||||
@@ -502,15 +514,15 @@ def run_argparse(argv, formatter):
|
|||||||
ap2.add_argument("--th-mt", metavar="CORES", type=int, default=cores, help="num cpu cores to use for generating thumbnails")
|
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=int, default=60, help="conversion timeout in seconds")
|
ap2.add_argument("--th-convt", metavar="SEC", type=int, default=60, help="conversion timeout in seconds")
|
||||||
ap2.add_argument("--th-no-crop", action="store_true", help="dynamic height; show full image")
|
ap2.add_argument("--th-no-crop", action="store_true", help="dynamic height; show full image")
|
||||||
ap2.add_argument("--th-dec", metavar="LIBS", default="vips,pil,ff", help="decoders, in order of preference")
|
ap2.add_argument("--th-dec", metavar="LIBS", default="vips,pil,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-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-no-webp", action="store_true", help="disable webp output")
|
||||||
ap2.add_argument("--th-ff-jpg", action="store_true", help="force jpg for video thumbs")
|
ap2.add_argument("--th-ff-jpg", action="store_true", help="force jpg output for video thumbs")
|
||||||
ap2.add_argument("--th-ff-swr", action="store_true", help="use swresample instead of soxr for audio thumbs")
|
ap2.add_argument("--th-ff-swr", action="store_true", help="use swresample instead of soxr for audio thumbs")
|
||||||
ap2.add_argument("--th-poke", metavar="SEC", type=int, default=300, help="activity labeling cooldown")
|
ap2.add_argument("--th-poke", metavar="SEC", type=int, default=300, help="activity labeling cooldown -- avoids doing keepalive pokes (updating the mtime) on thumbnail folders more often than SEC seconds")
|
||||||
ap2.add_argument("--th-clean", metavar="SEC", type=int, default=43200, help="cleanup interval; 0=disabled")
|
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")
|
ap2.add_argument("--th-maxage", metavar="SEC", type=int, default=604800, help="max folder age -- folders which haven't been poked for longer than --th-poke seconds will get deleted every --th-clean 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 for")
|
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")
|
||||||
# https://pillow.readthedocs.io/en/stable/handbook/image-file-formats.html
|
# https://pillow.readthedocs.io/en/stable/handbook/image-file-formats.html
|
||||||
# https://github.com/libvips/libvips
|
# https://github.com/libvips/libvips
|
||||||
# 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:'
|
# 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:'
|
||||||
@@ -522,36 +534,36 @@ def run_argparse(argv, formatter):
|
|||||||
|
|
||||||
ap2 = ap.add_argument_group('transcoding options')
|
ap2 = ap.add_argument_group('transcoding options')
|
||||||
ap2.add_argument("--no-acode", action="store_true", help="disable audio transcoding")
|
ap2.add_argument("--no-acode", action="store_true", help="disable audio transcoding")
|
||||||
ap2.add_argument("--ac-maxage", metavar="SEC", type=int, default=86400, help="delete transcode output after SEC seconds")
|
ap2.add_argument("--ac-maxage", metavar="SEC", type=int, default=86400, help="delete cached transcode output after SEC seconds")
|
||||||
|
|
||||||
ap2 = ap.add_argument_group('general db options')
|
ap2 = ap.add_argument_group('general db options')
|
||||||
ap2.add_argument("-e2d", action="store_true", help="enable up2k database")
|
ap2.add_argument("-e2d", action="store_true", help="enable up2k database, making files searchable + enables upload deduplocation")
|
||||||
ap2.add_argument("-e2ds", action="store_true", help="enable up2k db-scanner, sets -e2d")
|
ap2.add_argument("-e2ds", action="store_true", help="scan writable folders for new files on startup; sets -e2d")
|
||||||
ap2.add_argument("-e2dsa", action="store_true", help="scan all folders (for search), sets -e2ds")
|
ap2.add_argument("-e2dsa", action="store_true", help="scans all folders on startup; sets -e2ds")
|
||||||
ap2.add_argument("--hist", metavar="PATH", type=u, help="where to store volume data (db, thumbs)")
|
ap2.add_argument("--hist", metavar="PATH", type=u, help="where to store volume data (db, thumbs)")
|
||||||
ap2.add_argument("--no-hash", metavar="PTN", type=u, help="regex: disable hashing of matching paths during e2ds folder scans")
|
ap2.add_argument("--no-hash", metavar="PTN", type=u, help="regex: disable hashing of matching paths during e2ds folder scans")
|
||||||
ap2.add_argument("--no-idx", metavar="PTN", type=u, help="regex: disable indexing of matching paths during e2ds folder scans")
|
ap2.add_argument("--no-idx", metavar="PTN", type=u, help="regex: disable indexing of matching paths during e2ds folder scans")
|
||||||
ap2.add_argument("--re-maxage", metavar="SEC", type=int, default=0, help="disk rescan volume interval, 0=off, can be set per-volume with the 'scan' volflag")
|
ap2.add_argument("--re-maxage", metavar="SEC", type=int, default=0, help="disk rescan volume interval, 0=off, can be set per-volume with the 'scan' volflag")
|
||||||
ap2.add_argument("--srch-time", metavar="SEC", type=int, default=30, help="search deadline")
|
ap2.add_argument("--srch-time", metavar="SEC", type=int, default=30, help="search deadline -- terminate searches running for more than SEC seconds")
|
||||||
ap2.add_argument("--srch-hits", metavar="N", type=int, default=1000, help="max search results")
|
ap2.add_argument("--srch-hits", metavar="N", type=int, default=7999, help="max search results to allow clients to fetch; 125 results will be shown initially")
|
||||||
|
|
||||||
ap2 = ap.add_argument_group('metadata db options')
|
ap2 = ap.add_argument_group('metadata db options')
|
||||||
ap2.add_argument("-e2t", action="store_true", help="enable metadata indexing")
|
ap2.add_argument("-e2t", action="store_true", help="enable metadata indexing; makes it possible to search for artist/title/codec/resolution/...")
|
||||||
ap2.add_argument("-e2ts", action="store_true", help="enable metadata scanner, sets -e2t")
|
ap2.add_argument("-e2ts", action="store_true", help="scan existing files on startup; sets -e2t")
|
||||||
ap2.add_argument("-e2tsr", action="store_true", help="rescan all metadata, sets -e2ts")
|
ap2.add_argument("-e2tsr", action="store_true", help="delete all metadata from DB and do a full rescan; sets -e2ts")
|
||||||
ap2.add_argument("--no-mutagen", action="store_true", help="use FFprobe for tags instead")
|
ap2.add_argument("--no-mutagen", action="store_true", help="use FFprobe for tags instead; will catch more tags")
|
||||||
ap2.add_argument("--no-mtag-ff", action="store_true", help="never use FFprobe as tag reader")
|
ap2.add_argument("--no-mtag-ff", action="store_true", help="never use FFprobe as tag reader; is probably safer")
|
||||||
ap2.add_argument("--mtag-mt", metavar="CORES", type=int, default=cores, help="num cpu cores to use for tag scanning")
|
ap2.add_argument("--mtag-mt", metavar="CORES", type=int, default=cores, help="num cpu cores to use for tag scanning")
|
||||||
ap2.add_argument("-mtm", metavar="M=t,t,t", type=u, action="append", help="add/replace metadata mapping")
|
ap2.add_argument("-mtm", metavar="M=t,t,t", type=u, action="append", help="add/replace metadata mapping")
|
||||||
ap2.add_argument("-mte", metavar="M,M,M", type=u, help="tags to index/display (comma-sep.)",
|
ap2.add_argument("-mte", metavar="M,M,M", type=u, help="tags to index/display (comma-sep.)",
|
||||||
default="circle,album,.tn,artist,title,.bpm,key,.dur,.q,.vq,.aq,vc,ac,res,.fps,ahash,vhash")
|
default="circle,album,.tn,artist,title,.bpm,key,.dur,.q,.vq,.aq,vc,ac,res,.fps,ahash,vhash")
|
||||||
ap2.add_argument("-mth", metavar="M,M,M", type=u, help="tags to hide by default (comma-sep.)",
|
ap2.add_argument("-mth", metavar="M,M,M", type=u, help="tags to hide by default (comma-sep.)",
|
||||||
default=".vq,.aq,vc,ac,res,.fps")
|
default=".vq,.aq,vc,ac,res,.fps")
|
||||||
ap2.add_argument("-mtp", metavar="M=[f,]bin", type=u, action="append", help="read tag M using bin")
|
ap2.add_argument("-mtp", metavar="M=[f,]BIN", type=u, action="append", help="read tag M using program BIN to parse the file")
|
||||||
|
|
||||||
ap2 = ap.add_argument_group('ui options')
|
ap2 = ap.add_argument_group('ui options')
|
||||||
ap2.add_argument("--theme", metavar="NUM", type=int, default=0, help="default theme to use")
|
ap2.add_argument("--theme", metavar="NUM", type=int, default=0, help="default theme to use")
|
||||||
ap2.add_argument("--themes", metavar="NUM", type=int, default=4, help="number of themes installed")
|
ap2.add_argument("--themes", metavar="NUM", type=int, default=6, help="number of themes installed")
|
||||||
ap2.add_argument("--js-browser", metavar="L", type=u, help="URL to additional JS to include")
|
ap2.add_argument("--js-browser", metavar="L", type=u, help="URL to additional JS to include")
|
||||||
ap2.add_argument("--css-browser", metavar="L", type=u, help="URL to additional CSS to include")
|
ap2.add_argument("--css-browser", metavar="L", type=u, help="URL to additional CSS to include")
|
||||||
ap2.add_argument("--html-head", metavar="TXT", type=u, default="", help="text to append to the <head> of all HTML pages")
|
ap2.add_argument("--html-head", metavar="TXT", type=u, default="", help="text to append to the <head> of all HTML pages")
|
||||||
@@ -559,9 +571,9 @@ def run_argparse(argv, formatter):
|
|||||||
ap2.add_argument("--doctitle", metavar="TXT", type=u, default="copyparty", help="title / service-name to show in html documents")
|
ap2.add_argument("--doctitle", metavar="TXT", type=u, default="copyparty", help="title / service-name to show in html documents")
|
||||||
|
|
||||||
ap2 = ap.add_argument_group('debug options')
|
ap2 = ap.add_argument_group('debug options')
|
||||||
ap2.add_argument("--no-sendfile", action="store_true", help="disable sendfile")
|
ap2.add_argument("--no-sendfile", action="store_true", help="disable sendfile; instead using a traditional file read loop")
|
||||||
ap2.add_argument("--no-scandir", action="store_true", help="disable scandir")
|
ap2.add_argument("--no-scandir", action="store_true", help="disable scandir; instead using listdir + stat on each file")
|
||||||
ap2.add_argument("--no-fastboot", action="store_true", help="wait for up2k indexing")
|
ap2.add_argument("--no-fastboot", action="store_true", help="wait for up2k indexing before starting the httpd")
|
||||||
ap2.add_argument("--no-htp", action="store_true", help="disable httpserver threadpool, create threads as-needed instead")
|
ap2.add_argument("--no-htp", action="store_true", help="disable httpserver threadpool, create threads as-needed instead")
|
||||||
ap2.add_argument("--stackmon", metavar="P,S", type=u, help="write stacktrace to Path every S second")
|
ap2.add_argument("--stackmon", metavar="P,S", type=u, help="write stacktrace to Path every S second")
|
||||||
ap2.add_argument("--log-thrs", metavar="SEC", type=float, help="list active threads every SEC")
|
ap2.add_argument("--log-thrs", metavar="SEC", type=float, help="list active threads every SEC")
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
# coding: utf-8
|
# coding: utf-8
|
||||||
|
|
||||||
VERSION = (1, 2, 6)
|
VERSION = (1, 2, 8)
|
||||||
CODENAME = "ftp btw"
|
CODENAME = "ftp btw"
|
||||||
BUILD_DT = (2022, 4, 15)
|
BUILD_DT = (2022, 4, 30)
|
||||||
|
|
||||||
S_VERSION = ".".join(map(str, VERSION))
|
S_VERSION = ".".join(map(str, VERSION))
|
||||||
S_BUILD_DT = "{0:04d}-{1:02d}-{2:02d}".format(*BUILD_DT)
|
S_BUILD_DT = "{0:04d}-{1:02d}-{2:02d}".format(*BUILD_DT)
|
||||||
|
|||||||
@@ -11,12 +11,13 @@ import hashlib
|
|||||||
import threading
|
import threading
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
from .__init__ import WINDOWS
|
from .__init__ import ANYWIN, WINDOWS
|
||||||
from .util import (
|
from .util import (
|
||||||
IMPLICATIONS,
|
IMPLICATIONS,
|
||||||
META_NOBOTS,
|
META_NOBOTS,
|
||||||
uncyg,
|
uncyg,
|
||||||
undot,
|
undot,
|
||||||
|
relchk,
|
||||||
unhumanize,
|
unhumanize,
|
||||||
absreal,
|
absreal,
|
||||||
Pebkac,
|
Pebkac,
|
||||||
@@ -335,6 +336,12 @@ class VFS(object):
|
|||||||
):
|
):
|
||||||
# type: (str, str, bool, bool, bool, bool, bool) -> tuple[VFS, str]
|
# type: (str, str, bool, bool, bool, bool, bool) -> tuple[VFS, str]
|
||||||
"""returns [vfsnode,fs_remainder] if user has the requested permissions"""
|
"""returns [vfsnode,fs_remainder] if user has the requested permissions"""
|
||||||
|
if ANYWIN:
|
||||||
|
mod = relchk(vpath)
|
||||||
|
if mod:
|
||||||
|
self.log("vfs", "invalid relpath [{}]".format(vpath))
|
||||||
|
raise Pebkac(404)
|
||||||
|
|
||||||
vn, rem = self._find(vpath)
|
vn, rem = self._find(vpath)
|
||||||
c = vn.axs
|
c = vn.axs
|
||||||
|
|
||||||
|
|||||||
@@ -121,6 +121,12 @@ class HttpCli(object):
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
self.mode, self.req, self.http_ver = headerlines[0].split(" ")
|
self.mode, self.req, self.http_ver = headerlines[0].split(" ")
|
||||||
|
|
||||||
|
# normalize incoming headers to lowercase;
|
||||||
|
# outgoing headers however are Correct-Case
|
||||||
|
for header_line in headerlines[1:]:
|
||||||
|
k, v = header_line.split(":", 1)
|
||||||
|
self.headers[k.lower()] = v.strip()
|
||||||
except:
|
except:
|
||||||
msg = " ]\n#[ ".join(headerlines)
|
msg = " ]\n#[ ".join(headerlines)
|
||||||
raise Pebkac(400, "bad headers:\n#[ " + msg + " ]")
|
raise Pebkac(400, "bad headers:\n#[ " + msg + " ]")
|
||||||
@@ -137,12 +143,6 @@ class HttpCli(object):
|
|||||||
if self.args.rsp_slp:
|
if self.args.rsp_slp:
|
||||||
time.sleep(self.args.rsp_slp)
|
time.sleep(self.args.rsp_slp)
|
||||||
|
|
||||||
# normalize incoming headers to lowercase;
|
|
||||||
# outgoing headers however are Correct-Case
|
|
||||||
for header_line in headerlines[1:]:
|
|
||||||
k, v = header_line.split(":", 1)
|
|
||||||
self.headers[k.lower()] = v.strip()
|
|
||||||
|
|
||||||
v = self.headers.get("connection", "").lower()
|
v = self.headers.get("connection", "").lower()
|
||||||
self.keepalive = not v.startswith("close") and self.http_ver != "HTTP/1.0"
|
self.keepalive = not v.startswith("close") and self.http_ver != "HTTP/1.0"
|
||||||
self.is_https = (self.headers.get("x-forwarded-proto", "").lower() == "https" or self.tls)
|
self.is_https = (self.headers.get("x-forwarded-proto", "").lower() == "https" or self.tls)
|
||||||
@@ -211,6 +211,14 @@ class HttpCli(object):
|
|||||||
self.cookies = cookies
|
self.cookies = cookies
|
||||||
self.vpath = unquotep(vpath) # not query, so + means +
|
self.vpath = unquotep(vpath) # not query, so + means +
|
||||||
|
|
||||||
|
ok = "\x00" not in self.vpath
|
||||||
|
if ANYWIN:
|
||||||
|
ok = ok and not relchk(self.vpath)
|
||||||
|
|
||||||
|
if not ok:
|
||||||
|
self.log("invalid relpath [{}]".format(self.vpath))
|
||||||
|
return self.tx_404() and self.keepalive
|
||||||
|
|
||||||
pwd = None
|
pwd = None
|
||||||
ba = self.headers.get("authorization")
|
ba = self.headers.get("authorization")
|
||||||
if ba:
|
if ba:
|
||||||
@@ -344,8 +352,11 @@ class HttpCli(object):
|
|||||||
return body
|
return body
|
||||||
|
|
||||||
def loud_reply(self, body, *args, **kwargs):
|
def loud_reply(self, body, *args, **kwargs):
|
||||||
|
if not kwargs.get("mime"):
|
||||||
|
kwargs["mime"] = "text/plain; charset=utf-8"
|
||||||
|
|
||||||
self.log(body.rstrip())
|
self.log(body.rstrip())
|
||||||
self.reply(b"<pre>" + body.encode("utf-8") + b"\r\n", *list(args), **kwargs)
|
self.reply(body.encode("utf-8") + b"\r\n", *list(args), **kwargs)
|
||||||
|
|
||||||
def urlq(self, add, rm):
|
def urlq(self, add, rm):
|
||||||
"""
|
"""
|
||||||
@@ -864,8 +875,9 @@ class HttpCli(object):
|
|||||||
else:
|
else:
|
||||||
# search by query params
|
# search by query params
|
||||||
q = body["q"]
|
q = body["q"]
|
||||||
self.log("qj: " + q)
|
n = body.get("n", self.args.srch_hits)
|
||||||
hits, taglist = idx.search(vols, q)
|
self.log("qj: {} |{}|".format(q, n))
|
||||||
|
hits, taglist = idx.search(vols, q, n)
|
||||||
msg = len(hits)
|
msg = len(hits)
|
||||||
|
|
||||||
idx.p_end = time.time()
|
idx.p_end = time.time()
|
||||||
@@ -1046,6 +1058,7 @@ class HttpCli(object):
|
|||||||
raise Pebkac(500, min_ex())
|
raise Pebkac(500, min_ex())
|
||||||
|
|
||||||
vpath = "{}/{}".format(self.vpath, sanitized).lstrip("/")
|
vpath = "{}/{}".format(self.vpath, sanitized).lstrip("/")
|
||||||
|
self.out_headers["X-New-Dir"] = quotep(sanitized)
|
||||||
self.redirect(vpath)
|
self.redirect(vpath)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@@ -2226,6 +2239,7 @@ class HttpCli(object):
|
|||||||
"srv_info": srv_info,
|
"srv_info": srv_info,
|
||||||
"dtheme": self.args.theme,
|
"dtheme": self.args.theme,
|
||||||
"themes": self.args.themes,
|
"themes": self.args.themes,
|
||||||
|
"turbolvl": self.args.turbo,
|
||||||
}
|
}
|
||||||
if not self.can_read:
|
if not self.can_read:
|
||||||
if is_ls:
|
if is_ls:
|
||||||
|
|||||||
@@ -21,6 +21,12 @@ except:
|
|||||||
HAVE_SQLITE3 = False
|
HAVE_SQLITE3 = False
|
||||||
|
|
||||||
|
|
||||||
|
try:
|
||||||
|
from pathlib import Path
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
class U2idx(object):
|
class U2idx(object):
|
||||||
def __init__(self, conn):
|
def __init__(self, conn):
|
||||||
self.log_func = conn.log_func
|
self.log_func = conn.log_func
|
||||||
@@ -55,7 +61,7 @@ class U2idx(object):
|
|||||||
uv = [wark[:16], wark]
|
uv = [wark[:16], wark]
|
||||||
|
|
||||||
try:
|
try:
|
||||||
return self.run_query(vols, uq, uv, True, False)[0]
|
return self.run_query(vols, uq, uv, True, False, 99999)[0]
|
||||||
except:
|
except:
|
||||||
raise Pebkac(500, min_ex())
|
raise Pebkac(500, min_ex())
|
||||||
|
|
||||||
@@ -76,11 +82,26 @@ class U2idx(object):
|
|||||||
if not bos.path.exists(db_path):
|
if not bos.path.exists(db_path):
|
||||||
return None
|
return None
|
||||||
|
|
||||||
cur = sqlite3.connect(db_path, 2).cursor()
|
cur = None
|
||||||
|
if ANYWIN:
|
||||||
|
uri = ""
|
||||||
|
try:
|
||||||
|
uri = "{}?mode=ro&nolock=1".format(Path(db_path).as_uri())
|
||||||
|
cur = sqlite3.connect(uri, 2, uri=True).cursor()
|
||||||
|
self.log("ro: {}".format(db_path))
|
||||||
|
except:
|
||||||
|
self.log("could not open read-only: {}\n{}".format(uri, min_ex()))
|
||||||
|
|
||||||
|
if not cur:
|
||||||
|
# on windows, this steals the write-lock from up2k.deferred_init --
|
||||||
|
# seen on win 10.0.17763.2686, py 3.10.4, sqlite 3.37.2
|
||||||
|
cur = sqlite3.connect(db_path, 2).cursor()
|
||||||
|
self.log("opened {}".format(db_path))
|
||||||
|
|
||||||
self.cur[ptop] = cur
|
self.cur[ptop] = cur
|
||||||
return cur
|
return cur
|
||||||
|
|
||||||
def search(self, vols, uq):
|
def search(self, vols, uq, lim):
|
||||||
"""search by query params"""
|
"""search by query params"""
|
||||||
if not HAVE_SQLITE3:
|
if not HAVE_SQLITE3:
|
||||||
return []
|
return []
|
||||||
@@ -222,11 +243,11 @@ class U2idx(object):
|
|||||||
q += " lower({}) {} ? ) ".format(field, oper)
|
q += " lower({}) {} ? ) ".format(field, oper)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
return self.run_query(vols, q, va, have_up, have_mt)
|
return self.run_query(vols, q, va, have_up, have_mt, lim)
|
||||||
except Exception as ex:
|
except Exception as ex:
|
||||||
raise Pebkac(500, repr(ex))
|
raise Pebkac(500, repr(ex))
|
||||||
|
|
||||||
def run_query(self, vols, uq, uv, have_up, have_mt):
|
def run_query(self, vols, uq, uv, have_up, have_mt, lim):
|
||||||
done_flag = []
|
done_flag = []
|
||||||
self.active_id = "{:.6f}_{}".format(
|
self.active_id = "{:.6f}_{}".format(
|
||||||
time.time(), threading.current_thread().ident
|
time.time(), threading.current_thread().ident
|
||||||
@@ -255,7 +276,7 @@ class U2idx(object):
|
|||||||
self.log("qs: {!r} {!r}".format(uq, uv))
|
self.log("qs: {!r} {!r}".format(uq, uv))
|
||||||
|
|
||||||
ret = []
|
ret = []
|
||||||
lim = int(self.args.srch_hits)
|
lim = min(lim, int(self.args.srch_hits))
|
||||||
taglist = {}
|
taglist = {}
|
||||||
for (vtop, ptop, flags) in vols:
|
for (vtop, ptop, flags) in vols:
|
||||||
cur = self.get_cur(ptop)
|
cur = self.get_cur(ptop)
|
||||||
@@ -278,7 +299,7 @@ class U2idx(object):
|
|||||||
for hit in c:
|
for hit in c:
|
||||||
w, ts, sz, rd, fn, ip, at = hit[:7]
|
w, ts, sz, rd, fn, ip, at = hit[:7]
|
||||||
lim -= 1
|
lim -= 1
|
||||||
if lim <= 0:
|
if lim < 0:
|
||||||
break
|
break
|
||||||
|
|
||||||
if rd.startswith("//") or fn.startswith("//"):
|
if rd.startswith("//") or fn.startswith("//"):
|
||||||
|
|||||||
@@ -95,7 +95,7 @@ class Up2k(object):
|
|||||||
|
|
||||||
if ANYWIN:
|
if ANYWIN:
|
||||||
# usually fails to set lastmod too quickly
|
# usually fails to set lastmod too quickly
|
||||||
self.lastmod_q = Queue()
|
self.lastmod_q = []
|
||||||
thr = threading.Thread(target=self._lastmodder, name="up2k-lastmod")
|
thr = threading.Thread(target=self._lastmodder, name="up2k-lastmod")
|
||||||
thr.daemon = True
|
thr.daemon = True
|
||||||
thr.start()
|
thr.start()
|
||||||
@@ -583,9 +583,11 @@ class Up2k(object):
|
|||||||
self.pp.msg = "a{} {}".format(self.pp.n, cdir)
|
self.pp.msg = "a{} {}".format(self.pp.n, cdir)
|
||||||
histpath = self.asrv.vfs.histtab[top]
|
histpath = self.asrv.vfs.histtab[top]
|
||||||
ret = 0
|
ret = 0
|
||||||
seen_files = {}
|
seen_files = {} # != inames; files-only for dropcheck
|
||||||
g = statdir(self.log_func, not self.args.no_scandir, False, cdir)
|
g = statdir(self.log_func, not self.args.no_scandir, False, cdir)
|
||||||
for iname, inf in sorted(g):
|
g = sorted(g)
|
||||||
|
inames = {x[0]: 1 for x in g}
|
||||||
|
for iname, inf in g:
|
||||||
abspath = os.path.join(cdir, iname)
|
abspath = os.path.join(cdir, iname)
|
||||||
if rei and rei.search(abspath):
|
if rei and rei.search(abspath):
|
||||||
continue
|
continue
|
||||||
@@ -612,6 +614,17 @@ class Up2k(object):
|
|||||||
if WINDOWS:
|
if WINDOWS:
|
||||||
rp = rp.replace("\\", "/").strip("/")
|
rp = rp.replace("\\", "/").strip("/")
|
||||||
|
|
||||||
|
if rp.endswith(".PARTIAL") and time.time() - lmod < 60:
|
||||||
|
# rescan during upload
|
||||||
|
continue
|
||||||
|
|
||||||
|
if not sz and (
|
||||||
|
"{}.PARTIAL".format(iname) in inames
|
||||||
|
or ".{}.PARTIAL".format(iname) in inames
|
||||||
|
):
|
||||||
|
# placeholder for unfinished upload
|
||||||
|
continue
|
||||||
|
|
||||||
rd, fn = rp.rsplit("/", 1) if "/" in rp else ["", rp]
|
rd, fn = rp.rsplit("/", 1) if "/" in rp else ["", rp]
|
||||||
sql = "select w, mt, sz from up where rd = ? and fn = ?"
|
sql = "select w, mt, sz from up where rd = ? and fn = ?"
|
||||||
try:
|
try:
|
||||||
@@ -781,6 +794,7 @@ class Up2k(object):
|
|||||||
if self.mtag.prefer_mt and self.args.mtag_mt > 1:
|
if self.mtag.prefer_mt and self.args.mtag_mt > 1:
|
||||||
mpool = self._start_mpool()
|
mpool = self._start_mpool()
|
||||||
|
|
||||||
|
# TODO blocks writes to registry cursor; do chunks instead
|
||||||
conn = sqlite3.connect(db_path, timeout=15)
|
conn = sqlite3.connect(db_path, timeout=15)
|
||||||
cur = conn.cursor()
|
cur = conn.cursor()
|
||||||
c2 = conn.cursor()
|
c2 = conn.cursor()
|
||||||
@@ -806,8 +820,8 @@ class Up2k(object):
|
|||||||
n_tags = self._tag_file(c3, *args)
|
n_tags = self._tag_file(c3, *args)
|
||||||
else:
|
else:
|
||||||
mpool.put(["mtag"] + args)
|
mpool.put(["mtag"] + args)
|
||||||
with self.mutex:
|
# not registry cursor; do not self.mutex:
|
||||||
n_tags = len(self._flush_mpool(c3))
|
n_tags = len(self._flush_mpool(c3))
|
||||||
|
|
||||||
n_add += n_tags
|
n_add += n_tags
|
||||||
n_buf += n_tags
|
n_buf += n_tags
|
||||||
@@ -830,9 +844,6 @@ class Up2k(object):
|
|||||||
cur.close()
|
cur.close()
|
||||||
conn.close()
|
conn.close()
|
||||||
|
|
||||||
with self.mutex:
|
|
||||||
gcur.connection.commit()
|
|
||||||
|
|
||||||
return n_add, n_rm, True
|
return n_add, n_rm, True
|
||||||
|
|
||||||
def _flush_mpool(self, wcur):
|
def _flush_mpool(self, wcur):
|
||||||
@@ -1111,7 +1122,8 @@ class Up2k(object):
|
|||||||
return ret
|
return ret
|
||||||
|
|
||||||
def _orz(self, db_path):
|
def _orz(self, db_path):
|
||||||
return sqlite3.connect(db_path, check_same_thread=False).cursor()
|
timeout = int(max(self.args.srch_time, 5) * 1.2)
|
||||||
|
return sqlite3.connect(db_path, timeout, check_same_thread=False).cursor()
|
||||||
# x.set_trace_callback(trace)
|
# x.set_trace_callback(trace)
|
||||||
|
|
||||||
def _open_db(self, db_path):
|
def _open_db(self, db_path):
|
||||||
@@ -1480,7 +1492,7 @@ class Up2k(object):
|
|||||||
if lmod and (not linked or SYMTIME):
|
if lmod and (not linked or SYMTIME):
|
||||||
times = (int(time.time()), int(lmod))
|
times = (int(time.time()), int(lmod))
|
||||||
if ANYWIN:
|
if ANYWIN:
|
||||||
self.lastmod_q.put([dst, 0, times])
|
self.lastmod_q.append([dst, 0, times])
|
||||||
else:
|
else:
|
||||||
bos.utime(dst, times, False)
|
bos.utime(dst, times, False)
|
||||||
|
|
||||||
@@ -1574,9 +1586,15 @@ class Up2k(object):
|
|||||||
# self.log("--- " + wark + " " + dst + " finish_upload atomic " + dst, 4)
|
# self.log("--- " + wark + " " + dst + " finish_upload atomic " + dst, 4)
|
||||||
atomic_move(src, dst)
|
atomic_move(src, dst)
|
||||||
|
|
||||||
|
times = (int(time.time()), int(job["lmod"]))
|
||||||
if ANYWIN:
|
if ANYWIN:
|
||||||
a = [dst, job["size"], (int(time.time()), int(job["lmod"]))]
|
a = [dst, job["size"], times]
|
||||||
self.lastmod_q.put(a)
|
self.lastmod_q.append(a)
|
||||||
|
elif not job["hash"]:
|
||||||
|
try:
|
||||||
|
bos.utime(dst, times)
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
a = [job[x] for x in "ptop wark prel name lmod size addr".split()]
|
a = [job[x] for x in "ptop wark prel name lmod size addr".split()]
|
||||||
a += [job.get("at") or time.time()]
|
a += [job.get("at") or time.time()]
|
||||||
@@ -1675,12 +1693,12 @@ class Up2k(object):
|
|||||||
vn, rem = vn.get_dbv(rem)
|
vn, rem = vn.get_dbv(rem)
|
||||||
unpost = False
|
unpost = False
|
||||||
except:
|
except:
|
||||||
# unpost with missing permissions? try read+write and verify with db
|
# unpost with missing permissions? verify with db
|
||||||
if not self.args.unpost:
|
if not self.args.unpost:
|
||||||
raise Pebkac(400, "the unpost feature is disabled in server config")
|
raise Pebkac(400, "the unpost feature is disabled in server config")
|
||||||
|
|
||||||
unpost = True
|
unpost = True
|
||||||
permsets = [[True, True]]
|
permsets = [[False, True]]
|
||||||
vn, rem = self.asrv.vfs.get(vpath, uname, *permsets[0])
|
vn, rem = self.asrv.vfs.get(vpath, uname, *permsets[0])
|
||||||
vn, rem = vn.get_dbv(rem)
|
vn, rem = vn.get_dbv(rem)
|
||||||
_, _, _, _, dip, dat = self._find_from_vpath(vn.realpath, rem)
|
_, _, _, _, dip, dat = self._find_from_vpath(vn.realpath, rem)
|
||||||
@@ -2064,9 +2082,8 @@ class Up2k(object):
|
|||||||
|
|
||||||
def _lastmodder(self):
|
def _lastmodder(self):
|
||||||
while True:
|
while True:
|
||||||
ready = []
|
ready = self.lastmod_q
|
||||||
while not self.lastmod_q.empty():
|
self.lastmod_q = []
|
||||||
ready.append(self.lastmod_q.get())
|
|
||||||
|
|
||||||
# self.log("lmod: got {}".format(len(ready)))
|
# self.log("lmod: got {}".format(len(ready)))
|
||||||
time.sleep(5)
|
time.sleep(5)
|
||||||
|
|||||||
@@ -912,6 +912,9 @@ def sanitize_fn(fn, ok, bad):
|
|||||||
if "/" not in ok:
|
if "/" not in ok:
|
||||||
fn = fn.replace("\\", "/").split("/")[-1]
|
fn = fn.replace("\\", "/").split("/")[-1]
|
||||||
|
|
||||||
|
if fn.lower() in bad:
|
||||||
|
fn = "_" + fn
|
||||||
|
|
||||||
if ANYWIN:
|
if ANYWIN:
|
||||||
remap = [
|
remap = [
|
||||||
["<", "<"],
|
["<", "<"],
|
||||||
@@ -927,16 +930,26 @@ def sanitize_fn(fn, ok, bad):
|
|||||||
for a, b in [x for x in remap if x[0] not in ok]:
|
for a, b in [x for x in remap if x[0] not in ok]:
|
||||||
fn = fn.replace(a, b)
|
fn = fn.replace(a, b)
|
||||||
|
|
||||||
bad.extend(["con", "prn", "aux", "nul"])
|
bad = ["con", "prn", "aux", "nul"]
|
||||||
for n in range(1, 10):
|
for n in range(1, 10):
|
||||||
bad += "com{0} lpt{0}".format(n).split(" ")
|
bad += "com{0} lpt{0}".format(n).split(" ")
|
||||||
|
|
||||||
if fn.lower() in bad:
|
if fn.lower().split(".")[0] in bad:
|
||||||
fn = "_" + fn
|
fn = "_" + fn
|
||||||
|
|
||||||
return fn.strip()
|
return fn.strip()
|
||||||
|
|
||||||
|
|
||||||
|
def relchk(rp):
|
||||||
|
if ANYWIN:
|
||||||
|
if "\n" in rp or "\r" in rp:
|
||||||
|
return "x\nx"
|
||||||
|
|
||||||
|
p = re.sub(r'[\\:*?"<>|]', "", rp)
|
||||||
|
if p != rp:
|
||||||
|
return "[{}]".format(p)
|
||||||
|
|
||||||
|
|
||||||
def absreal(fpath):
|
def absreal(fpath):
|
||||||
try:
|
try:
|
||||||
return fsdec(os.path.abspath(os.path.realpath(fsenc(fpath))))
|
return fsdec(os.path.abspath(os.path.realpath(fsenc(fpath))))
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -146,12 +146,13 @@
|
|||||||
have_del = {{ have_del|tojson }},
|
have_del = {{ have_del|tojson }},
|
||||||
have_unpost = {{ have_unpost|tojson }},
|
have_unpost = {{ have_unpost|tojson }},
|
||||||
have_zip = {{ have_zip|tojson }},
|
have_zip = {{ have_zip|tojson }},
|
||||||
|
turbolvl = {{ turbolvl|tojson }},
|
||||||
txt_ext = "{{ txt_ext }}",
|
txt_ext = "{{ txt_ext }}",
|
||||||
{% if no_prism %}no_prism = 1,{% endif %}
|
{% if no_prism %}no_prism = 1,{% endif %}
|
||||||
readme = {{ readme|tojson }},
|
readme = {{ readme|tojson }},
|
||||||
ls0 = {{ ls0|tojson }};
|
ls0 = {{ ls0|tojson }};
|
||||||
|
|
||||||
document.documentElement.setAttribute("class", localStorage.theme || dtheme);
|
document.documentElement.className = localStorage.theme || dtheme;
|
||||||
</script>
|
</script>
|
||||||
<script src="/.cpr/util.js?_={{ ts }}"></script>
|
<script src="/.cpr/util.js?_={{ ts }}"></script>
|
||||||
<script src="/.cpr/baguettebox.js?_={{ ts }}"></script>
|
<script src="/.cpr/baguettebox.js?_={{ ts }}"></script>
|
||||||
|
|||||||
@@ -452,7 +452,7 @@ var mpl = (function () {
|
|||||||
cover = null;
|
cover = null;
|
||||||
|
|
||||||
for (var a = 0, aa = files.length; a < aa; a++) {
|
for (var a = 0, aa = files.length; a < aa; a++) {
|
||||||
if (/^(cover|folder)\.(jpe?g|png|gif)$/.test(files[a].textContent)) {
|
if (/^(cover|folder)\.(jpe?g|png|gif)$/i.test(files[a].textContent)) {
|
||||||
cover = noq_href(files[a]);
|
cover = noq_href(files[a]);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -666,7 +666,7 @@ function ft2dict(tr) {
|
|||||||
|
|
||||||
for (var a = 1, aa = th.length; a < aa; a++) {
|
for (var a = 1, aa = th.length; a < aa; a++) {
|
||||||
var tv = tr.cells[a].textContent,
|
var tv = tr.cells[a].textContent,
|
||||||
tk = a == 1 ? 'file' : th[a].getAttribute('name').split('/').pop(),
|
tk = a == 1 ? 'file' : th[a].getAttribute('name').split('/').pop().toLowerCase(),
|
||||||
vis = th[a].className.indexOf('min') === -1;
|
vis = th[a].className.indexOf('min') === -1;
|
||||||
|
|
||||||
if (!tv)
|
if (!tv)
|
||||||
@@ -1414,7 +1414,7 @@ var audio_eq = (function () {
|
|||||||
|
|
||||||
|
|
||||||
// plays the tid'th audio file on the page
|
// plays the tid'th audio file on the page
|
||||||
function play(tid, is_ev, seek, call_depth) {
|
function play(tid, is_ev, seek) {
|
||||||
if (mp.order.length == 0)
|
if (mp.order.length == 0)
|
||||||
return console.log('no audio found wait what');
|
return console.log('no audio found wait what');
|
||||||
|
|
||||||
@@ -1913,9 +1913,6 @@ var fileman = (function () {
|
|||||||
if (!md.hasOwnProperty(k))
|
if (!md.hasOwnProperty(k))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
md[k.toLowerCase()] = md[k];
|
|
||||||
k = k.toLowerCase();
|
|
||||||
|
|
||||||
if (k.startsWith('.'))
|
if (k.startsWith('.'))
|
||||||
md[k.slice(1)] = md[k];
|
md[k.slice(1)] = md[k];
|
||||||
}
|
}
|
||||||
@@ -2508,7 +2505,7 @@ var showfile = (function () {
|
|||||||
|
|
||||||
el = el || QS('#doc>code');
|
el = el || QS('#doc>code');
|
||||||
Prism.highlightElement(el);
|
Prism.highlightElement(el);
|
||||||
if (el.getAttribute('class') == 'language-ans')
|
if (el.className == 'language-ans')
|
||||||
r.ansify(el);
|
r.ansify(el);
|
||||||
}
|
}
|
||||||
catch (ex) { }
|
catch (ex) { }
|
||||||
@@ -2529,7 +2526,7 @@ var showfile = (function () {
|
|||||||
el.textContent = txt;
|
el.textContent = txt;
|
||||||
el.innerHTML = '<code>' + el.innerHTML + '</code>';
|
el.innerHTML = '<code>' + el.innerHTML + '</code>';
|
||||||
if (!window['no_prism']) {
|
if (!window['no_prism']) {
|
||||||
el.setAttribute('class', 'prism linkable-line-numbers line-numbers language-' + lang);
|
el.className = 'prism linkable-line-numbers line-numbers language-' + lang;
|
||||||
if (!defer)
|
if (!defer)
|
||||||
fun(el.firstChild);
|
fun(el.firstChild);
|
||||||
else
|
else
|
||||||
@@ -2659,7 +2656,7 @@ var showfile = (function () {
|
|||||||
};
|
};
|
||||||
|
|
||||||
var bdoc = ebi('bdoc');
|
var bdoc = ebi('bdoc');
|
||||||
bdoc.setAttribute('class', 'line-numbers');
|
bdoc.className = 'line-numbers';
|
||||||
bdoc.innerHTML = (
|
bdoc.innerHTML = (
|
||||||
'<div id="hdoc" class="ghead">\n' +
|
'<div id="hdoc" class="ghead">\n' +
|
||||||
'<a href="#" class="btn" id="xdoc" tt="return to folder view$NHotkey: M">❌ close</a>\n' +
|
'<a href="#" class="btn" id="xdoc" tt="return to folder view$NHotkey: M">❌ close</a>\n' +
|
||||||
@@ -2854,19 +2851,24 @@ var thegrid = (function () {
|
|||||||
|
|
||||||
for (var a = 0, aa = ths.length; a < aa; a++) {
|
for (var a = 0, aa = ths.length; a < aa; a++) {
|
||||||
var tr = ebi(ths[a].getAttribute('ref')).closest('tr'),
|
var tr = ebi(ths[a].getAttribute('ref')).closest('tr'),
|
||||||
cl = tr.getAttribute('class') || '';
|
cl = tr.className || '';
|
||||||
|
|
||||||
if (noq_href(ths[a]).endsWith('/'))
|
if (noq_href(ths[a]).endsWith('/'))
|
||||||
cl += ' dir';
|
cl += ' dir';
|
||||||
|
|
||||||
ths[a].setAttribute('class', cl);
|
ths[a].className = cl;
|
||||||
}
|
}
|
||||||
var uns = QS('#ggrid a[ref="unsearch"]');
|
|
||||||
if (uns)
|
var sp = ['unsearch', 'moar'];
|
||||||
uns.onclick = function (e) {
|
for (var a = 0; a < sp.length; a++)
|
||||||
ev(e);
|
(function (a) {
|
||||||
ebi('unsearch').click();
|
var o = QS('#ggrid a[ref="' + sp[a] + '"]');
|
||||||
};
|
if (o)
|
||||||
|
o.onclick = function (e) {
|
||||||
|
ev(e);
|
||||||
|
ebi(sp[a]).click();
|
||||||
|
};
|
||||||
|
})(a);
|
||||||
};
|
};
|
||||||
|
|
||||||
r.tippen = function () {
|
r.tippen = function () {
|
||||||
@@ -3284,7 +3286,8 @@ document.onkeydown = function (e) {
|
|||||||
|
|
||||||
var trs = [],
|
var trs = [],
|
||||||
orig_url = null,
|
orig_url = null,
|
||||||
orig_html = null;
|
orig_html = null,
|
||||||
|
cap = 125;
|
||||||
|
|
||||||
for (var a = 0; a < sconf.length; a++) {
|
for (var a = 0; a < sconf.length; a++) {
|
||||||
var html = ['<tr><td><br />' + sconf[a][0] + '</td>'];
|
var html = ['<tr><td><br />' + sconf[a][0] + '</td>'];
|
||||||
@@ -3313,12 +3316,13 @@ document.onkeydown = function (e) {
|
|||||||
var o = QSA('#op_search input');
|
var o = QSA('#op_search input');
|
||||||
for (var a = 0; a < o.length; a++) {
|
for (var a = 0; a < o.length; a++) {
|
||||||
o[a].oninput = ev_search_input;
|
o[a].oninput = ev_search_input;
|
||||||
|
o[a].onkeydown = ev_search_keydown;
|
||||||
}
|
}
|
||||||
|
|
||||||
function srch_msg(err, txt) {
|
function srch_msg(err, txt) {
|
||||||
var o = ebi('srch_q');
|
var o = ebi('srch_q');
|
||||||
o.textContent = txt;
|
o.textContent = txt;
|
||||||
o.style.color = err ? '#f09' : '#c90';
|
clmod(o, 'err', err);
|
||||||
}
|
}
|
||||||
|
|
||||||
var search_timeout,
|
var search_timeout,
|
||||||
@@ -3338,12 +3342,18 @@ document.onkeydown = function (e) {
|
|||||||
encode_query();
|
encode_query();
|
||||||
|
|
||||||
set_vq();
|
set_vq();
|
||||||
|
cap = 125;
|
||||||
|
|
||||||
clearTimeout(defer_timeout);
|
clearTimeout(defer_timeout);
|
||||||
defer_timeout = setTimeout(try_search, 2000);
|
defer_timeout = setTimeout(try_search, 2000);
|
||||||
try_search(v);
|
try_search(v);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function ev_search_keydown(e) {
|
||||||
|
if (e.key == 'Enter')
|
||||||
|
do_search();
|
||||||
|
}
|
||||||
|
|
||||||
function try_search(v) {
|
function try_search(v) {
|
||||||
if (Date.now() - search_in_progress > 30 * 1000) {
|
if (Date.now() - search_in_progress > 30 * 1000) {
|
||||||
clearTimeout(defer_timeout);
|
clearTimeout(defer_timeout);
|
||||||
@@ -3372,8 +3382,6 @@ document.onkeydown = function (e) {
|
|||||||
vs = ebi('srch_' + k + 'v').value,
|
vs = ebi('srch_' + k + 'v').value,
|
||||||
tvs = [];
|
tvs = [];
|
||||||
|
|
||||||
if (k == 'name')
|
|
||||||
console.log('a');
|
|
||||||
while (vs) {
|
while (vs) {
|
||||||
vs = vs.trim();
|
vs = vs.trim();
|
||||||
if (!vs)
|
if (!vs)
|
||||||
@@ -3462,7 +3470,7 @@ document.onkeydown = function (e) {
|
|||||||
xhr.onreadystatechange = xhr_search_results;
|
xhr.onreadystatechange = xhr_search_results;
|
||||||
xhr.ts = Date.now();
|
xhr.ts = Date.now();
|
||||||
xhr.q_raw = ebi('q_raw').value;
|
xhr.q_raw = ebi('q_raw').value;
|
||||||
xhr.send(JSON.stringify({ "q": xhr.q_raw }));
|
xhr.send(JSON.stringify({ "q": xhr.q_raw, "n": cap }));
|
||||||
}
|
}
|
||||||
|
|
||||||
function xhr_search_results() {
|
function xhr_search_results() {
|
||||||
@@ -3495,7 +3503,9 @@ document.onkeydown = function (e) {
|
|||||||
|
|
||||||
var html = mk_files_header(tagord), seen = {};
|
var html = mk_files_header(tagord), seen = {};
|
||||||
html.push('<tbody>');
|
html.push('<tbody>');
|
||||||
html.push('<tr><td>-</td><td colspan="42"><a href="#" id="unsearch"><big style="font-weight:bold">[❌] close search results</big></a></td></tr>');
|
html.push('<tr class="srch_hdr"><td>-</td><td><a href="#" id="unsearch"><big style="font-weight:bold">[❌] close search results</big></a> -- showing ' +
|
||||||
|
res.hits.length + ' hits' + (res.hits.length == cap ? ' -- <a href="#" id="moar">load more</a>' : '') + '</td></tr>');
|
||||||
|
|
||||||
for (var a = 0; a < res.hits.length; a++) {
|
for (var a = 0; a < res.hits.length; a++) {
|
||||||
var r = res.hits[a],
|
var r = res.hits[a],
|
||||||
ts = parseInt(r.ts),
|
ts = parseInt(r.ts),
|
||||||
@@ -3547,6 +3557,9 @@ document.onkeydown = function (e) {
|
|||||||
|
|
||||||
sethash('q=' + uricom_enc(this.q_raw));
|
sethash('q=' + uricom_enc(this.q_raw));
|
||||||
ebi('unsearch').onclick = unsearch;
|
ebi('unsearch').onclick = unsearch;
|
||||||
|
var m = ebi('moar');
|
||||||
|
if (m)
|
||||||
|
m.onclick = moar;
|
||||||
}
|
}
|
||||||
|
|
||||||
function unsearch(e) {
|
function unsearch(e) {
|
||||||
@@ -3558,6 +3571,12 @@ document.onkeydown = function (e) {
|
|||||||
sethash('');
|
sethash('');
|
||||||
reload_browser();
|
reload_browser();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function moar(e) {
|
||||||
|
ev(e);
|
||||||
|
cap *= 2;
|
||||||
|
do_search();
|
||||||
|
}
|
||||||
})();
|
})();
|
||||||
|
|
||||||
|
|
||||||
@@ -3844,7 +3863,6 @@ var treectl = (function () {
|
|||||||
QS('#treeul>li>a+a').textContent = '[root]';
|
QS('#treeul>li>a+a').textContent = '[root]';
|
||||||
despin('#tree');
|
despin('#tree');
|
||||||
reload_tree();
|
reload_tree();
|
||||||
onresize();
|
|
||||||
|
|
||||||
var fun = r.dir_cb;
|
var fun = r.dir_cb;
|
||||||
if (fun) {
|
if (fun) {
|
||||||
@@ -3876,7 +3894,7 @@ var treectl = (function () {
|
|||||||
cl = 'par';
|
cl = 'par';
|
||||||
}
|
}
|
||||||
|
|
||||||
links[a].setAttribute('class', cl);
|
links[a].className = cl;
|
||||||
links[a].onclick = treego;
|
links[a].onclick = treego;
|
||||||
links[a].onmouseenter = nowrap ? menter : null;
|
links[a].onmouseenter = nowrap ? menter : null;
|
||||||
links[a].onmouseleave = nowrap ? mleave : null;
|
links[a].onmouseleave = nowrap ? mleave : null;
|
||||||
@@ -3897,6 +3915,7 @@ var treectl = (function () {
|
|||||||
catch (ex) { }
|
catch (ex) { }
|
||||||
r.pdir.shift();
|
r.pdir.shift();
|
||||||
r.pdirw = -1;
|
r.pdirw = -1;
|
||||||
|
onresize();
|
||||||
}
|
}
|
||||||
|
|
||||||
function compy() {
|
function compy() {
|
||||||
@@ -3943,7 +3962,7 @@ var treectl = (function () {
|
|||||||
return true;
|
return true;
|
||||||
|
|
||||||
ev(e);
|
ev(e);
|
||||||
if (this.getAttribute('class') == 'hl' &&
|
if (this.className == 'hl' &&
|
||||||
this.previousSibling.textContent == '-') {
|
this.previousSibling.textContent == '-') {
|
||||||
treegrow.call(this.previousSibling, e);
|
treegrow.call(this.previousSibling, e);
|
||||||
return;
|
return;
|
||||||
@@ -4029,7 +4048,6 @@ var treectl = (function () {
|
|||||||
r.ls_cb = null;
|
r.ls_cb = null;
|
||||||
fun();
|
fun();
|
||||||
}
|
}
|
||||||
eval_hash();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
r.gentab = function (top, res) {
|
r.gentab = function (top, res) {
|
||||||
@@ -4094,6 +4112,7 @@ var treectl = (function () {
|
|||||||
apply_perms(res.perms);
|
apply_perms(res.perms);
|
||||||
fileman.render();
|
fileman.render();
|
||||||
}
|
}
|
||||||
|
setTimeout(eval_hash, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
var m = scan_hash(hash0),
|
var m = scan_hash(hash0),
|
||||||
@@ -4209,7 +4228,7 @@ var treectl = (function () {
|
|||||||
function enspin(sel) {
|
function enspin(sel) {
|
||||||
despin(sel);
|
despin(sel);
|
||||||
var d = mknod('div');
|
var d = mknod('div');
|
||||||
d.setAttribute('class', 'dumb_loader_thing');
|
d.className = 'dumb_loader_thing';
|
||||||
d.innerHTML = '🌲';
|
d.innerHTML = '🌲';
|
||||||
var tgt = QS(sel);
|
var tgt = QS(sel);
|
||||||
tgt.insertBefore(d, tgt.childNodes[0]);
|
tgt.insertBefore(d, tgt.childNodes[0]);
|
||||||
@@ -4315,7 +4334,7 @@ function find_file_col(txt) {
|
|||||||
for (var a = 0; a < tds.length; a++) {
|
for (var a = 0; a < tds.length; a++) {
|
||||||
var spans = tds[a].getElementsByTagName('span');
|
var spans = tds[a].getElementsByTagName('span');
|
||||||
if (spans.length && spans[0].textContent == txt) {
|
if (spans.length && spans[0].textContent == txt) {
|
||||||
min = (tds[a].getAttribute('class') || '').indexOf('min') !== -1;
|
min = (tds[a].className || '').indexOf('min') !== -1;
|
||||||
i = a;
|
i = a;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -4461,7 +4480,7 @@ var filecols = (function () {
|
|||||||
tds = QSA('#files>tbody>tr>td:nth-child(' + (a + 1) + ')');
|
tds = QSA('#files>tbody>tr>td:nth-child(' + (a + 1) + ')');
|
||||||
|
|
||||||
for (var b = 0, bb = tds.length; b < bb; b++)
|
for (var b = 0, bb = tds.length; b < bb; b++)
|
||||||
tds[b].setAttribute('class', cls);
|
tds[b].className = cls;
|
||||||
}
|
}
|
||||||
if (window['tt']) {
|
if (window['tt']) {
|
||||||
tt.att(ebi('hcols'));
|
tt.att(ebi('hcols'));
|
||||||
@@ -4623,18 +4642,21 @@ var settheme = (function () {
|
|||||||
light = !!(theme.indexOf('y') + 1);
|
light = !!(theme.indexOf('y') + 1);
|
||||||
|
|
||||||
function freshen() {
|
function freshen() {
|
||||||
var cl = document.documentElement.getAttribute('class');
|
var cl = document.documentElement.className;
|
||||||
cl = cl.replace(/\b(light|dark|[a-z]{1,2})\b/g, '').replace(/ +/g, ' ');
|
cl = cl.replace(/\b(light|dark|[a-z]{1,2})\b/g, '').replace(/ +/g, ' ');
|
||||||
document.documentElement.setAttribute('class', cl + ' ' + theme + ' ');
|
document.documentElement.className = cl + ' ' + theme + ' ';
|
||||||
|
|
||||||
pbar.drawbuf();
|
pbar.drawbuf();
|
||||||
pbar.drawpos();
|
pbar.drawpos();
|
||||||
vbar.draw();
|
vbar.draw();
|
||||||
showfile.setstyle();
|
showfile.setstyle();
|
||||||
|
|
||||||
var html = [], itheme = ax.indexOf(theme.charAt(0)) * 2 + (light ? 1 : 0);
|
var html = [], itheme = ax.indexOf(theme.charAt(0)) * 2 + (light ? 1 : 0),
|
||||||
|
names = ['classic dark', 'classic light', 'flat dark', 'flat light', 'vice', 'hotdog stand'];
|
||||||
|
|
||||||
for (var a = 0; a < themes; a++)
|
for (var a = 0; a < themes; a++)
|
||||||
html.push('<a href="#" class="btn tgl' + (a == itheme ? ' on' : '') + '">' + a + '</a>');
|
html.push('<a href="#" class="btn tgl' + (a == itheme ? ' on' : '') +
|
||||||
|
'" tt="' + (names[a] || 'custom') + '">' + a + '</a>');
|
||||||
|
|
||||||
ebi('themes').innerHTML = html.join('');
|
ebi('themes').innerHTML = html.join('');
|
||||||
var btns = QSA('#themes a');
|
var btns = QSA('#themes a');
|
||||||
@@ -4642,6 +4664,7 @@ var settheme = (function () {
|
|||||||
btns[a].onclick = settheme;
|
btns[a].onclick = settheme;
|
||||||
|
|
||||||
bcfg_set('light', light);
|
bcfg_set('light', light);
|
||||||
|
tt.att(ebi('themes'));
|
||||||
}
|
}
|
||||||
|
|
||||||
function settheme(e) {
|
function settheme(e) {
|
||||||
@@ -4903,7 +4926,10 @@ var msel = (function () {
|
|||||||
tb.value = '';
|
tb.value = '';
|
||||||
clmod(sf, 'vis');
|
clmod(sf, 'vis');
|
||||||
sf.textContent = '';
|
sf.textContent = '';
|
||||||
treectl.goto(this.vp + uricom_enc(this.dn) + '/', true);
|
|
||||||
|
var dn = this.getResponseHeader('X-New-Dir');
|
||||||
|
dn = dn || uricom_enc(this.dn);
|
||||||
|
treectl.goto(this.vp + dn + '/', true);
|
||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
|
|
||||||
@@ -5075,7 +5101,7 @@ var unpost = (function () {
|
|||||||
html.push("<table><thead><tr><td></td><td>time</td><td>size</td><td>file</td></tr></thead><tbody>");
|
html.push("<table><thead><tr><td></td><td>time</td><td>size</td><td>file</td></tr></thead><tbody>");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
html.push("<p>sike! no uploads " + (filt.value ? 'matching that filter' : '') + " are sufficiently recent</p>");
|
html.push("sike! no uploads " + (filt.value ? 'matching that filter' : '') + " are sufficiently recent");
|
||||||
|
|
||||||
var mods = [1000, 100, 10];
|
var mods = [1000, 100, 10];
|
||||||
for (var a = 0; a < res.length; a++) {
|
for (var a = 0; a < res.length; a++) {
|
||||||
|
|||||||
@@ -161,7 +161,7 @@ blink {
|
|||||||
height: 1.05em;
|
height: 1.05em;
|
||||||
margin: -.2em .3em -.2em -.4em;
|
margin: -.2em .3em -.2em -.4em;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
border: 1px solid rgba(0,0,0,0.2);
|
border: 1px solid rgba(154,154,154,0.6);
|
||||||
border-width: .2em .2em 0 0;
|
border-width: .2em .2em 0 0;
|
||||||
transform: rotate(45deg);
|
transform: rotate(45deg);
|
||||||
}
|
}
|
||||||
@@ -236,9 +236,6 @@ blink {
|
|||||||
html.z #toc li {
|
html.z #toc li {
|
||||||
border-width: 0;
|
border-width: 0;
|
||||||
}
|
}
|
||||||
html.z #mn a:not(:last-child)::after {
|
|
||||||
border-color: rgba(255,255,255,0.3);
|
|
||||||
}
|
|
||||||
html.z #mn a {
|
html.z #mn a {
|
||||||
color: #ccc;
|
color: #ccc;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -140,7 +140,7 @@ var md_opt = {
|
|||||||
btn = document.getElementById("lightswitch"),
|
btn = document.getElementById("lightswitch"),
|
||||||
f = function (e) {
|
f = function (e) {
|
||||||
if (e) { e.preventDefault(); drk = !drk; }
|
if (e) { e.preventDefault(); drk = !drk; }
|
||||||
document.documentElement.setAttribute("class", drk? "z":"y");
|
document.documentElement.className = drk? "z":"y";
|
||||||
btn.innerHTML = "go " + (drk ? "light":"dark");
|
btn.innerHTML = "go " + (drk ? "light":"dark");
|
||||||
l.light = drk? 0:1;
|
l.light = drk? 0:1;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -278,7 +278,7 @@ function convert_markdown(md_text, dest_dom) {
|
|||||||
if (!txt)
|
if (!txt)
|
||||||
nodes[a].textContent = href;
|
nodes[a].textContent = href;
|
||||||
else if (href !== txt)
|
else if (href !== txt)
|
||||||
nodes[a].setAttribute('class', 'vis');
|
nodes[a].className = 'vis';
|
||||||
}
|
}
|
||||||
|
|
||||||
// todo-lists (should probably be a marked extension)
|
// todo-lists (should probably be a marked extension)
|
||||||
@@ -294,7 +294,7 @@ function convert_markdown(md_text, dest_dom) {
|
|||||||
var clas = done ? 'done' : 'pend';
|
var clas = done ? 'done' : 'pend';
|
||||||
var char = done ? 'Y' : 'N';
|
var char = done ? 'Y' : 'N';
|
||||||
|
|
||||||
dom_li.setAttribute('class', 'task-list-item');
|
dom_li.className = 'task-list-item';
|
||||||
dom_li.style.listStyleType = 'none';
|
dom_li.style.listStyleType = 'none';
|
||||||
var html = dom_li.innerHTML;
|
var html = dom_li.innerHTML;
|
||||||
dom_li.innerHTML =
|
dom_li.innerHTML =
|
||||||
@@ -468,11 +468,11 @@ function init_toc() {
|
|||||||
for (var a = 0; a < anchors.length; a++) {
|
for (var a = 0; a < anchors.length; a++) {
|
||||||
if (anchors[a].active) {
|
if (anchors[a].active) {
|
||||||
anchors[a].active = false;
|
anchors[a].active = false;
|
||||||
links[a].setAttribute('class', '');
|
links[a].className = '';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
anchors[hit].active = true;
|
anchors[hit].active = true;
|
||||||
links[hit].setAttribute('class', 'act');
|
links[hit].className = 'act';
|
||||||
}
|
}
|
||||||
|
|
||||||
var pane_height = parseInt(getComputedStyle(dom_toc).height);
|
var pane_height = parseInt(getComputedStyle(dom_toc).height);
|
||||||
|
|||||||
@@ -144,16 +144,16 @@ redraw = (function () {
|
|||||||
map_pre = genmap(dom_pre, map_pre);
|
map_pre = genmap(dom_pre, map_pre);
|
||||||
}
|
}
|
||||||
function setsbs() {
|
function setsbs() {
|
||||||
dom_wrap.setAttribute('class', '');
|
dom_wrap.className = '';
|
||||||
dom_swrap.setAttribute('class', '');
|
dom_swrap.className = '';
|
||||||
onresize();
|
onresize();
|
||||||
}
|
}
|
||||||
function modetoggle() {
|
function modetoggle() {
|
||||||
var mode = dom_nsbs.innerHTML;
|
var mode = dom_nsbs.innerHTML;
|
||||||
dom_nsbs.innerHTML = mode == 'editor' ? 'preview' : 'editor';
|
dom_nsbs.innerHTML = mode == 'editor' ? 'preview' : 'editor';
|
||||||
mode += ' single';
|
mode += ' single';
|
||||||
dom_wrap.setAttribute('class', mode);
|
dom_wrap.className = mode;
|
||||||
dom_swrap.setAttribute('class', mode);
|
dom_swrap.className = mode;
|
||||||
onresize();
|
onresize();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -309,7 +309,7 @@ var modpoll = new Modpoll();
|
|||||||
|
|
||||||
|
|
||||||
window.onbeforeunload = function (e) {
|
window.onbeforeunload = function (e) {
|
||||||
if ((ebi("save").getAttribute('class') + '').indexOf('disabled') >= 0)
|
if ((ebi("save").className + '').indexOf('disabled') >= 0)
|
||||||
return; //nice (todo)
|
return; //nice (todo)
|
||||||
|
|
||||||
e.preventDefault(); //ff
|
e.preventDefault(); //ff
|
||||||
@@ -321,7 +321,7 @@ window.onbeforeunload = function (e) {
|
|||||||
function save(e) {
|
function save(e) {
|
||||||
if (e) e.preventDefault();
|
if (e) e.preventDefault();
|
||||||
var save_btn = ebi("save"),
|
var save_btn = ebi("save"),
|
||||||
save_cls = save_btn.getAttribute('class') + '';
|
save_cls = save_btn.className + '';
|
||||||
|
|
||||||
if (save_cls.indexOf('disabled') >= 0)
|
if (save_cls.indexOf('disabled') >= 0)
|
||||||
return toast.inf(2, "no changes");
|
return toast.inf(2, "no changes");
|
||||||
@@ -678,7 +678,7 @@ function reLastIndexOf(txt, ptn, end) {
|
|||||||
// table formatter
|
// table formatter
|
||||||
function fmt_table(e) {
|
function fmt_table(e) {
|
||||||
if (e) e.preventDefault();
|
if (e) e.preventDefault();
|
||||||
//dom_tbox.setAttribute('class', '');
|
//dom_tbox.className = '';
|
||||||
|
|
||||||
var txt = dom_src.value,
|
var txt = dom_src.value,
|
||||||
ofs = dom_src.selectionStart,
|
ofs = dom_src.selectionStart,
|
||||||
@@ -829,7 +829,7 @@ function fmt_table(e) {
|
|||||||
// show unicode
|
// show unicode
|
||||||
function mark_uni(e) {
|
function mark_uni(e) {
|
||||||
if (e) e.preventDefault();
|
if (e) e.preventDefault();
|
||||||
dom_tbox.setAttribute('class', '');
|
dom_tbox.className = '';
|
||||||
|
|
||||||
var txt = dom_src.value,
|
var txt = dom_src.value,
|
||||||
ptn = new RegExp('([^' + js_uni_whitelist + ']+)', 'g'),
|
ptn = new RegExp('([^' + js_uni_whitelist + ']+)', 'g'),
|
||||||
@@ -989,14 +989,14 @@ var set_lno = (function () {
|
|||||||
|
|
||||||
ebi('tools').onclick = function (e) {
|
ebi('tools').onclick = function (e) {
|
||||||
if (e) e.preventDefault();
|
if (e) e.preventDefault();
|
||||||
var is_open = dom_tbox.getAttribute('class') != 'open';
|
var is_open = dom_tbox.className != 'open';
|
||||||
dom_tbox.setAttribute('class', is_open ? 'open' : '');
|
dom_tbox.className = is_open ? 'open' : '';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
ebi('help').onclick = function (e) {
|
ebi('help').onclick = function (e) {
|
||||||
if (e) e.preventDefault();
|
if (e) e.preventDefault();
|
||||||
dom_tbox.setAttribute('class', '');
|
dom_tbox.className = '';
|
||||||
|
|
||||||
var dom = ebi('helpbox');
|
var dom = ebi('helpbox');
|
||||||
var dtxt = dom.getElementsByTagName('textarea');
|
var dtxt = dom.getElementsByTagName('textarea');
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ var lightswitch = (function () {
|
|||||||
drk = l.light != 1,
|
drk = l.light != 1,
|
||||||
f = function (e) {
|
f = function (e) {
|
||||||
if (e) drk = !drk;
|
if (e) drk = !drk;
|
||||||
document.documentElement.setAttribute("class", drk? "z":"y");
|
document.documentElement.className = drk? "z":"y";
|
||||||
l.light = drk? 0:1;
|
l.light = drk? 0:1;
|
||||||
};
|
};
|
||||||
f();
|
f();
|
||||||
|
|||||||
@@ -97,7 +97,7 @@
|
|||||||
<a href="#" id="repl">π</a>
|
<a href="#" id="repl">π</a>
|
||||||
<script>
|
<script>
|
||||||
|
|
||||||
document.documentElement.setAttribute("class", localStorage.light == 1 ? "y" : "z");
|
document.documentElement.className = localStorage.light == 1 ? "y" : "z";
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
<script src="/.cpr/util.js?_={{ ts }}"></script>
|
<script src="/.cpr/util.js?_={{ ts }}"></script>
|
||||||
|
|||||||
@@ -158,6 +158,7 @@ html {
|
|||||||
color: #f6a;
|
color: #f6a;
|
||||||
}
|
}
|
||||||
html.y #tt {
|
html.y #tt {
|
||||||
|
color: #333;
|
||||||
background: #fff;
|
background: #fff;
|
||||||
border-color: #888 #000 #777 #000;
|
border-color: #888 #000 #777 #000;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -135,7 +135,7 @@ function up2k_flagbus() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function U2pvis(act, btns) {
|
function U2pvis(act, btns, uc) {
|
||||||
var r = this;
|
var r = this;
|
||||||
r.act = act;
|
r.act = act;
|
||||||
r.ctr = { "ok": 0, "ng": 0, "bz": 0, "q": 0 };
|
r.ctr = { "ok": 0, "ng": 0, "bz": 0, "q": 0 };
|
||||||
@@ -425,7 +425,9 @@ function U2pvis(act, btns) {
|
|||||||
html.push(r.genrow(a, true).replace(/><td>/, "><td>b "));
|
html.push(r.genrow(a, true).replace(/><td>/, "><td>b "));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ebi('u2tab').tBodies[0].innerHTML = html.join('\n');
|
var el = ebi('u2tab');
|
||||||
|
el.tBodies[0].innerHTML = html.join('\n');
|
||||||
|
el.className = (uc.fsearch ? 'srch ' : 'up ') + r.act;
|
||||||
};
|
};
|
||||||
|
|
||||||
r.genrow = function (nfile, as_html) {
|
r.genrow = function (nfile, as_html) {
|
||||||
@@ -624,11 +626,11 @@ function up2k_init(subtle) {
|
|||||||
|
|
||||||
function setmsg(msg, type) {
|
function setmsg(msg, type) {
|
||||||
if (msg !== undefined) {
|
if (msg !== undefined) {
|
||||||
ebi('u2err').setAttribute('class', type);
|
ebi('u2err').className = type;
|
||||||
ebi('u2err').innerHTML = msg;
|
ebi('u2err').innerHTML = msg;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
ebi('u2err').setAttribute('class', '');
|
ebi('u2err').className = '';
|
||||||
ebi('u2err').innerHTML = '';
|
ebi('u2err').innerHTML = '';
|
||||||
}
|
}
|
||||||
if (msg == suggest_up2k) {
|
if (msg == suggest_up2k) {
|
||||||
@@ -665,8 +667,8 @@ function up2k_init(subtle) {
|
|||||||
bcfg_bind(uc, 'ask_up', 'ask_up', true, null, false);
|
bcfg_bind(uc, 'ask_up', 'ask_up', true, null, false);
|
||||||
bcfg_bind(uc, 'flag_en', 'flag_en', false, apply_flag_cfg);
|
bcfg_bind(uc, 'flag_en', 'flag_en', false, apply_flag_cfg);
|
||||||
bcfg_bind(uc, 'fsearch', 'fsearch', false, set_fsearch, false);
|
bcfg_bind(uc, 'fsearch', 'fsearch', false, set_fsearch, false);
|
||||||
bcfg_bind(uc, 'turbo', 'u2turbo', false, draw_turbo, false);
|
bcfg_bind(uc, 'turbo', 'u2turbo', turbolvl > 1, draw_turbo, false);
|
||||||
bcfg_bind(uc, 'datechk', 'u2tdate', true, null, false);
|
bcfg_bind(uc, 'datechk', 'u2tdate', turbolvl < 3, null, false);
|
||||||
|
|
||||||
var st = {
|
var st = {
|
||||||
"files": [],
|
"files": [],
|
||||||
@@ -705,7 +707,7 @@ function up2k_init(subtle) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
var pvis = new U2pvis("bz", '#u2cards'),
|
var pvis = new U2pvis("bz", '#u2cards', uc),
|
||||||
donut = new Donut(uc, st);
|
donut = new Donut(uc, st);
|
||||||
|
|
||||||
var bobslice = null;
|
var bobslice = null;
|
||||||
@@ -1938,19 +1940,17 @@ function up2k_init(subtle) {
|
|||||||
|
|
||||||
if (btn.parentNode !== parent) {
|
if (btn.parentNode !== parent) {
|
||||||
parent.appendChild(btn);
|
parent.appendChild(btn);
|
||||||
ebi('u2conf').setAttribute('class', wide);
|
ebi('u2conf').className = ebi('u2cards').className = ebi('u2etaw').className = wide;
|
||||||
ebi('u2cards').setAttribute('class', wide);
|
|
||||||
ebi('u2etaw').setAttribute('class', wide);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
wide = write && wem > 78 ? 'ww' : wide;
|
wide = write && wem > 78 ? 'ww' : wide;
|
||||||
parent = ebi(wide == 'ww' && write ? 'u2c3w' : 'u2c3t');
|
parent = ebi(wide == 'ww' && write ? 'u2c3w' : 'u2c3t');
|
||||||
var its = [ebi('u2etaw'), ebi('u2cards')];
|
var its = [ebi('u2etaw'), ebi('u2cards')];
|
||||||
if (its[0].parentNode !== parent) {
|
if (its[0].parentNode !== parent) {
|
||||||
ebi('u2conf').setAttribute('class', wide);
|
ebi('u2conf').className = wide;
|
||||||
for (var a = 0; a < 2; a++) {
|
for (var a = 0; a < 2; a++) {
|
||||||
parent.appendChild(its[a]);
|
parent.appendChild(its[a]);
|
||||||
its[a].setAttribute('class', wide);
|
its[a].className = wide;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2024,9 +2024,12 @@ function up2k_init(subtle) {
|
|||||||
html = ebi('u2foot').innerHTML,
|
html = ebi('u2foot').innerHTML,
|
||||||
ohtml = html;
|
ohtml = html;
|
||||||
|
|
||||||
if (uc.turbo && html.indexOf(msg) === -1)
|
if (turbolvl || !uc.turbo)
|
||||||
|
msg = null;
|
||||||
|
|
||||||
|
if (msg && html.indexOf(msg) === -1)
|
||||||
html = html.replace(omsg, '') + msg;
|
html = html.replace(omsg, '') + msg;
|
||||||
else if (!uc.turbo)
|
else if (!msg)
|
||||||
html = html.replace(msgu, '').replace(msgs, '');
|
html = html.replace(msgu, '').replace(msgs, '');
|
||||||
|
|
||||||
if (html !== ohtml)
|
if (html !== ohtml)
|
||||||
@@ -2070,6 +2073,8 @@ function up2k_init(subtle) {
|
|||||||
}
|
}
|
||||||
catch (ex) { }
|
catch (ex) { }
|
||||||
|
|
||||||
|
ebi('u2tab').className = (uc.fsearch ? 'srch ' : 'up ') + pvis.act;
|
||||||
|
|
||||||
draw_turbo();
|
draw_turbo();
|
||||||
onresize();
|
onresize();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -89,6 +89,9 @@ function vis_exh(msg, url, lineNo, columnNo, error) {
|
|||||||
if ((msg + '').indexOf('l2d.js') !== -1)
|
if ((msg + '').indexOf('l2d.js') !== -1)
|
||||||
return; // `t` undefined in tapEvent -> hitTestSimpleCustom
|
return; // `t` undefined in tapEvent -> hitTestSimpleCustom
|
||||||
|
|
||||||
|
if (!/\.js($|\?)/.exec('' + url))
|
||||||
|
return; // chrome debugger
|
||||||
|
|
||||||
var ekey = url + '\n' + lineNo + '\n' + msg;
|
var ekey = url + '\n' + lineNo + '\n' + msg;
|
||||||
if (ignexd[ekey] || crashed)
|
if (ignexd[ekey] || crashed)
|
||||||
return;
|
return;
|
||||||
@@ -327,7 +330,7 @@ function clgot(el, cls) {
|
|||||||
if (el.classList)
|
if (el.classList)
|
||||||
return el.classList.contains(cls);
|
return el.classList.contains(cls);
|
||||||
|
|
||||||
var lst = (el.getAttribute('class') + '').split(/ /g);
|
var lst = (el.className + '').split(/ /g);
|
||||||
return has(lst, cls);
|
return has(lst, cls);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ ENV ver_asmcrypto=5b994303a9d3e27e0915f72a10b6c2c51535a4dc \
|
|||||||
ver_hashwasm=4.9.0 \
|
ver_hashwasm=4.9.0 \
|
||||||
ver_marked=4.0.12 \
|
ver_marked=4.0.12 \
|
||||||
ver_mde=2.16.1 \
|
ver_mde=2.16.1 \
|
||||||
ver_codemirror=5.65.2 \
|
ver_codemirror=5.65.3 \
|
||||||
ver_fontawesome=5.13.0 \
|
ver_fontawesome=5.13.0 \
|
||||||
ver_zopfli=1.0.3
|
ver_zopfli=1.0.3
|
||||||
|
|
||||||
|
|||||||
@@ -38,6 +38,9 @@ class Cfg(Namespace):
|
|||||||
no_mv=False,
|
no_mv=False,
|
||||||
no_del=False,
|
no_del=False,
|
||||||
no_zip=False,
|
no_zip=False,
|
||||||
|
no_thumb=False,
|
||||||
|
no_athumb=False,
|
||||||
|
no_vthumb=False,
|
||||||
no_voldump=True,
|
no_voldump=True,
|
||||||
no_scandir=False,
|
no_scandir=False,
|
||||||
no_sendfile=True,
|
no_sendfile=True,
|
||||||
@@ -53,6 +56,9 @@ class Cfg(Namespace):
|
|||||||
textfiles="",
|
textfiles="",
|
||||||
doctitle="",
|
doctitle="",
|
||||||
html_head="",
|
html_head="",
|
||||||
|
theme=0,
|
||||||
|
themes=0,
|
||||||
|
turbo=0,
|
||||||
hist=None,
|
hist=None,
|
||||||
no_idx=None,
|
no_idx=None,
|
||||||
no_hash=None,
|
no_hash=None,
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ from copyparty import util
|
|||||||
|
|
||||||
class Cfg(Namespace):
|
class Cfg(Namespace):
|
||||||
def __init__(self, a=None, v=None, c=None):
|
def __init__(self, a=None, v=None, c=None):
|
||||||
ex = "nw e2d e2ds e2dsa e2t e2ts e2tsr no_logues no_readme no_acode force_js no_robots"
|
ex = "nw e2d e2ds e2dsa e2t e2ts e2tsr no_logues no_readme no_acode force_js no_robots no_thumb no_athumb no_vthumb"
|
||||||
ex = {k: False for k in ex.split()}
|
ex = {k: False for k in ex.split()}
|
||||||
ex2 = {
|
ex2 = {
|
||||||
"mtp": [],
|
"mtp": [],
|
||||||
@@ -36,6 +36,9 @@ class Cfg(Namespace):
|
|||||||
"rsp_slp": 0,
|
"rsp_slp": 0,
|
||||||
"s_wr_slp": 0,
|
"s_wr_slp": 0,
|
||||||
"s_wr_sz": 512 * 1024,
|
"s_wr_sz": 512 * 1024,
|
||||||
|
"theme": 0,
|
||||||
|
"themes": 0,
|
||||||
|
"turbo": 0,
|
||||||
}
|
}
|
||||||
ex.update(ex2)
|
ex.update(ex2)
|
||||||
super(Cfg, self).__init__(a=a or [], v=v or [], c=c, **ex)
|
super(Cfg, self).__init__(a=a or [], v=v or [], c=c, **ex)
|
||||||
|
|||||||
Reference in New Issue
Block a user