copyparty 1.19.1__tar.gz → 1.19.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {copyparty-1.19.1 → copyparty-1.19.2}/PKG-INFO +39 -3
- {copyparty-1.19.1 → copyparty-1.19.2}/README.md +38 -2
- {copyparty-1.19.1 → copyparty-1.19.2}/copyparty/__main__.py +127 -28
- {copyparty-1.19.1 → copyparty-1.19.2}/copyparty/__version__.py +2 -2
- {copyparty-1.19.1 → copyparty-1.19.2}/copyparty/authsrv.py +33 -7
- {copyparty-1.19.1 → copyparty-1.19.2}/copyparty/cfg.py +7 -1
- {copyparty-1.19.1 → copyparty-1.19.2}/copyparty/dxml.py +3 -0
- {copyparty-1.19.1 → copyparty-1.19.2}/copyparty/ftpd.py +21 -6
- {copyparty-1.19.1 → copyparty-1.19.2}/copyparty/httpcli.py +81 -16
- {copyparty-1.19.1 → copyparty-1.19.2}/copyparty/httpsrv.py +6 -0
- {copyparty-1.19.1 → copyparty-1.19.2}/copyparty/mtag.py +88 -6
- {copyparty-1.19.1 → copyparty-1.19.2}/copyparty/svchub.py +76 -5
- {copyparty-1.19.1 → copyparty-1.19.2}/copyparty/tcpsrv.py +6 -0
- {copyparty-1.19.1 → copyparty-1.19.2}/copyparty/th_cli.py +5 -1
- {copyparty-1.19.1 → copyparty-1.19.2}/copyparty/th_srv.py +160 -51
- {copyparty-1.19.1 → copyparty-1.19.2}/copyparty/u2idx.py +1 -1
- {copyparty-1.19.1 → copyparty-1.19.2}/copyparty/up2k.py +80 -39
- {copyparty-1.19.1 → copyparty-1.19.2}/copyparty/util.py +25 -1
- copyparty-1.19.2/copyparty/web/baguettebox.js.gz +0 -0
- copyparty-1.19.2/copyparty/web/browser.css.gz +0 -0
- copyparty-1.19.2/copyparty/web/browser.js.gz +0 -0
- copyparty-1.19.2/copyparty/web/rups.js.gz +0 -0
- copyparty-1.19.2/copyparty/web/splash.css.gz +0 -0
- {copyparty-1.19.1 → copyparty-1.19.2}/copyparty/web/splash.html +8 -1
- copyparty-1.19.2/copyparty/web/splash.js.gz +0 -0
- {copyparty-1.19.1 → copyparty-1.19.2}/copyparty/web/svcs.html +1 -1
- copyparty-1.19.2/copyparty/web/up2k.js.gz +0 -0
- copyparty-1.19.2/copyparty/web/util.js.gz +0 -0
- {copyparty-1.19.1 → copyparty-1.19.2}/copyparty.egg-info/PKG-INFO +39 -3
- copyparty-1.19.1/copyparty/web/baguettebox.js.gz +0 -0
- copyparty-1.19.1/copyparty/web/browser.css.gz +0 -0
- copyparty-1.19.1/copyparty/web/browser.js.gz +0 -0
- copyparty-1.19.1/copyparty/web/rups.js.gz +0 -0
- copyparty-1.19.1/copyparty/web/splash.css.gz +0 -0
- copyparty-1.19.1/copyparty/web/splash.js.gz +0 -0
- copyparty-1.19.1/copyparty/web/up2k.js.gz +0 -0
- copyparty-1.19.1/copyparty/web/util.js.gz +0 -0
- {copyparty-1.19.1 → copyparty-1.19.2}/LICENSE +0 -0
- {copyparty-1.19.1 → copyparty-1.19.2}/copyparty/__init__.py +0 -0
- {copyparty-1.19.1 → copyparty-1.19.2}/copyparty/bos/__init__.py +0 -0
- {copyparty-1.19.1 → copyparty-1.19.2}/copyparty/bos/bos.py +0 -0
- {copyparty-1.19.1 → copyparty-1.19.2}/copyparty/bos/path.py +0 -0
- {copyparty-1.19.1 → copyparty-1.19.2}/copyparty/broker_mp.py +0 -0
- {copyparty-1.19.1 → copyparty-1.19.2}/copyparty/broker_mpw.py +0 -0
- {copyparty-1.19.1 → copyparty-1.19.2}/copyparty/broker_thr.py +0 -0
- {copyparty-1.19.1 → copyparty-1.19.2}/copyparty/broker_util.py +0 -0
- {copyparty-1.19.1 → copyparty-1.19.2}/copyparty/cert.py +0 -0
- {copyparty-1.19.1 → copyparty-1.19.2}/copyparty/fsutil.py +0 -0
- {copyparty-1.19.1 → copyparty-1.19.2}/copyparty/httpconn.py +0 -0
- {copyparty-1.19.1 → copyparty-1.19.2}/copyparty/ico.py +0 -0
- {copyparty-1.19.1 → copyparty-1.19.2}/copyparty/mdns.py +0 -0
- {copyparty-1.19.1 → copyparty-1.19.2}/copyparty/metrics.py +0 -0
- {copyparty-1.19.1 → copyparty-1.19.2}/copyparty/multicast.py +0 -0
- {copyparty-1.19.1 → copyparty-1.19.2}/copyparty/pwhash.py +0 -0
- {copyparty-1.19.1 → copyparty-1.19.2}/copyparty/res/COPYING.txt +0 -0
- {copyparty-1.19.1 → copyparty-1.19.2}/copyparty/res/__init__.py +0 -0
- {copyparty-1.19.1 → copyparty-1.19.2}/copyparty/res/insecure.pem +0 -0
- {copyparty-1.19.1 → copyparty-1.19.2}/copyparty/smbd.py +0 -0
- {copyparty-1.19.1 → copyparty-1.19.2}/copyparty/ssdp.py +0 -0
- {copyparty-1.19.1 → copyparty-1.19.2}/copyparty/star.py +0 -0
- {copyparty-1.19.1 → copyparty-1.19.2}/copyparty/stolen/__init__.py +0 -0
- {copyparty-1.19.1 → copyparty-1.19.2}/copyparty/stolen/dnslib/__init__.py +0 -0
- {copyparty-1.19.1 → copyparty-1.19.2}/copyparty/stolen/dnslib/bimap.py +0 -0
- {copyparty-1.19.1 → copyparty-1.19.2}/copyparty/stolen/dnslib/bit.py +0 -0
- {copyparty-1.19.1 → copyparty-1.19.2}/copyparty/stolen/dnslib/buffer.py +0 -0
- {copyparty-1.19.1 → copyparty-1.19.2}/copyparty/stolen/dnslib/dns.py +0 -0
- {copyparty-1.19.1 → copyparty-1.19.2}/copyparty/stolen/dnslib/label.py +0 -0
- {copyparty-1.19.1 → copyparty-1.19.2}/copyparty/stolen/dnslib/lex.py +0 -0
- {copyparty-1.19.1 → copyparty-1.19.2}/copyparty/stolen/dnslib/ranges.py +0 -0
- {copyparty-1.19.1 → copyparty-1.19.2}/copyparty/stolen/ifaddr/__init__.py +0 -0
- {copyparty-1.19.1 → copyparty-1.19.2}/copyparty/stolen/ifaddr/_posix.py +0 -0
- {copyparty-1.19.1 → copyparty-1.19.2}/copyparty/stolen/ifaddr/_shared.py +0 -0
- {copyparty-1.19.1 → copyparty-1.19.2}/copyparty/stolen/ifaddr/_win32.py +0 -0
- {copyparty-1.19.1 → copyparty-1.19.2}/copyparty/stolen/qrcodegen.py +0 -0
- {copyparty-1.19.1 → copyparty-1.19.2}/copyparty/stolen/surrogateescape.py +0 -0
- {copyparty-1.19.1 → copyparty-1.19.2}/copyparty/sutil.py +0 -0
- {copyparty-1.19.1 → copyparty-1.19.2}/copyparty/szip.py +0 -0
- {copyparty-1.19.1 → copyparty-1.19.2}/copyparty/tftpd.py +0 -0
- {copyparty-1.19.1 → copyparty-1.19.2}/copyparty/web/a/__init__.py +0 -0
- {copyparty-1.19.1 → copyparty-1.19.2}/copyparty/web/a/partyfuse.py +0 -0
- {copyparty-1.19.1 → copyparty-1.19.2}/copyparty/web/a/u2c.py +0 -0
- {copyparty-1.19.1 → copyparty-1.19.2}/copyparty/web/a/webdav-cfg.bat +0 -0
- {copyparty-1.19.1 → copyparty-1.19.2}/copyparty/web/browser.html +0 -0
- {copyparty-1.19.1 → copyparty-1.19.2}/copyparty/web/browser2.html +0 -0
- {copyparty-1.19.1 → copyparty-1.19.2}/copyparty/web/cf.html +0 -0
- {copyparty-1.19.1 → copyparty-1.19.2}/copyparty/web/dbg-audio.js.gz +0 -0
- {copyparty-1.19.1 → copyparty-1.19.2}/copyparty/web/deps/__init__.py +0 -0
- {copyparty-1.19.1 → copyparty-1.19.2}/copyparty/web/deps/busy.mp3.gz +0 -0
- {copyparty-1.19.1 → copyparty-1.19.2}/copyparty/web/deps/easymde.css.gz +0 -0
- {copyparty-1.19.1 → copyparty-1.19.2}/copyparty/web/deps/easymde.js.gz +0 -0
- {copyparty-1.19.1 → copyparty-1.19.2}/copyparty/web/deps/fuse.py +0 -0
- {copyparty-1.19.1 → copyparty-1.19.2}/copyparty/web/deps/marked.js.gz +0 -0
- {copyparty-1.19.1 → copyparty-1.19.2}/copyparty/web/deps/mini-fa.css.gz +0 -0
- {copyparty-1.19.1 → copyparty-1.19.2}/copyparty/web/deps/mini-fa.woff +0 -0
- {copyparty-1.19.1 → copyparty-1.19.2}/copyparty/web/deps/prism.css.gz +0 -0
- {copyparty-1.19.1 → copyparty-1.19.2}/copyparty/web/deps/prism.js.gz +0 -0
- {copyparty-1.19.1 → copyparty-1.19.2}/copyparty/web/deps/prismd.css.gz +0 -0
- {copyparty-1.19.1 → copyparty-1.19.2}/copyparty/web/deps/scp.woff2 +0 -0
- {copyparty-1.19.1 → copyparty-1.19.2}/copyparty/web/deps/sha512.ac.js.gz +0 -0
- {copyparty-1.19.1 → copyparty-1.19.2}/copyparty/web/deps/sha512.hw.js.gz +0 -0
- {copyparty-1.19.1 → copyparty-1.19.2}/copyparty/web/idp.html +0 -0
- {copyparty-1.19.1 → copyparty-1.19.2}/copyparty/web/md.css.gz +0 -0
- {copyparty-1.19.1 → copyparty-1.19.2}/copyparty/web/md.html +0 -0
- {copyparty-1.19.1 → copyparty-1.19.2}/copyparty/web/md.js.gz +0 -0
- {copyparty-1.19.1 → copyparty-1.19.2}/copyparty/web/md2.css.gz +0 -0
- {copyparty-1.19.1 → copyparty-1.19.2}/copyparty/web/md2.js.gz +0 -0
- {copyparty-1.19.1 → copyparty-1.19.2}/copyparty/web/mde.css.gz +0 -0
- {copyparty-1.19.1 → copyparty-1.19.2}/copyparty/web/mde.html +0 -0
- {copyparty-1.19.1 → copyparty-1.19.2}/copyparty/web/mde.js.gz +0 -0
- {copyparty-1.19.1 → copyparty-1.19.2}/copyparty/web/msg.css.gz +0 -0
- {copyparty-1.19.1 → copyparty-1.19.2}/copyparty/web/msg.html +0 -0
- {copyparty-1.19.1 → copyparty-1.19.2}/copyparty/web/rups.css.gz +0 -0
- {copyparty-1.19.1 → copyparty-1.19.2}/copyparty/web/rups.html +0 -0
- {copyparty-1.19.1 → copyparty-1.19.2}/copyparty/web/shares.css.gz +0 -0
- {copyparty-1.19.1 → copyparty-1.19.2}/copyparty/web/shares.html +0 -0
- {copyparty-1.19.1 → copyparty-1.19.2}/copyparty/web/shares.js.gz +0 -0
- {copyparty-1.19.1 → copyparty-1.19.2}/copyparty/web/svcs.js.gz +0 -0
- {copyparty-1.19.1 → copyparty-1.19.2}/copyparty/web/ui.css.gz +0 -0
- {copyparty-1.19.1 → copyparty-1.19.2}/copyparty/web/w.hash.js.gz +0 -0
- {copyparty-1.19.1 → copyparty-1.19.2}/copyparty.egg-info/SOURCES.txt +0 -0
- {copyparty-1.19.1 → copyparty-1.19.2}/copyparty.egg-info/dependency_links.txt +0 -0
- {copyparty-1.19.1 → copyparty-1.19.2}/copyparty.egg-info/entry_points.txt +0 -0
- {copyparty-1.19.1 → copyparty-1.19.2}/copyparty.egg-info/requires.txt +0 -0
- {copyparty-1.19.1 → copyparty-1.19.2}/copyparty.egg-info/top_level.txt +0 -0
- {copyparty-1.19.1 → copyparty-1.19.2}/pyproject.toml +0 -0
- {copyparty-1.19.1 → copyparty-1.19.2}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: copyparty
|
3
|
-
Version: 1.19.
|
3
|
+
Version: 1.19.2
|
4
4
|
Summary: Portable file server with accelerated resumable uploads, deduplication, WebDAV, FTP, zeroconf, media indexer, video thumbnails, audio transcoding, and write-only folders
|
5
5
|
Author-email: ed <copyparty@ocv.me>
|
6
6
|
License: MIT
|
@@ -155,7 +155,9 @@ made in Norway 🇳🇴
|
|
155
155
|
* [upload events](#upload-events) - the older, more powerful approach ([examples](./bin/mtag/))
|
156
156
|
* [handlers](#handlers) - redefine behavior with plugins ([examples](./bin/handlers/))
|
157
157
|
* [ip auth](#ip-auth) - autologin based on IP range (CIDR)
|
158
|
+
* [restrict to ip](#restrict-to-ip) - limit a user to certain IP ranges (CIDR)
|
158
159
|
* [identity providers](#identity-providers) - replace copyparty passwords with oauth and such
|
160
|
+
* [generic header auth](#generic-header-auth) - other ways to auth by header
|
159
161
|
* [user-changeable passwords](#user-changeable-passwords) - if permitted, users can change their own passwords
|
160
162
|
* [using the cloud as storage](#using-the-cloud-as-storage) - connecting to an aws s3 bucket and similar
|
161
163
|
* [hiding from google](#hiding-from-google) - tell search engines you don't wanna be indexed
|
@@ -331,6 +333,7 @@ also see [comparison to similar software](./docs/versus.md)
|
|
331
333
|
* ☑ realtime streaming of growing files (logfiles and such)
|
332
334
|
* ☑ [thumbnails](#thumbnails)
|
333
335
|
* ☑ ...of images using Pillow, pyvips, or FFmpeg
|
336
|
+
* ☑ ...of RAW images using rawpy
|
334
337
|
* ☑ ...of videos using FFmpeg
|
335
338
|
* ☑ ...of audio (spectrograms) using FFmpeg
|
336
339
|
* ☑ cache eviction (max-age; maybe max-size eventually)
|
@@ -577,6 +580,8 @@ examples:
|
|
577
580
|
* replacing the `g` permission with `wg` would let anonymous users upload files, but not see the required filekey to access it
|
578
581
|
* replacing the `g` permission with `wG` would let anonymous users upload files, receiving a working direct link in return
|
579
582
|
|
583
|
+
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`
|
584
|
+
|
580
585
|
anyone trying to bruteforce a password gets banned according to `--ban-pw`; default is 24h ban for 9 failed attempts in 1 hour
|
581
586
|
|
582
587
|
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`
|
@@ -602,6 +607,7 @@ and if you want to use config files instead of commandline args (good!) then her
|
|
602
607
|
accs:
|
603
608
|
r: u1, u2 # only these accounts can read,
|
604
609
|
r: @g1 # (exactly the same, just with a group instead)
|
610
|
+
r: @acct # (alternatively, ALL users who are logged in)
|
605
611
|
rw: u3 # and only u3 can read-write
|
606
612
|
|
607
613
|
[/inc]
|
@@ -1957,6 +1963,20 @@ repeat the option to map additional subnets
|
|
1957
1963
|
**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=`
|
1958
1964
|
|
1959
1965
|
|
1966
|
+
### restrict to ip
|
1967
|
+
|
1968
|
+
limit a user to certain IP ranges (CIDR) , using the global-option `--ipr`
|
1969
|
+
|
1970
|
+
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:
|
1971
|
+
|
1972
|
+
```yaml
|
1973
|
+
[global]
|
1974
|
+
ipr: 192.168.123.0/24,172.16.0.0/16=spartacus
|
1975
|
+
```
|
1976
|
+
|
1977
|
+
repeat the option to map additional users
|
1978
|
+
|
1979
|
+
|
1960
1980
|
## identity providers
|
1961
1981
|
|
1962
1982
|
replace copyparty passwords with oauth and such
|
@@ -1976,6 +1996,20 @@ a more complete example of the copyparty configuration options [look like this](
|
|
1976
1996
|
but if you just want to let users change their own passwords, then you probably want [user-changeable passwords](#user-changeable-passwords) instead
|
1977
1997
|
|
1978
1998
|
|
1999
|
+
### generic header auth
|
2000
|
+
|
2001
|
+
other ways to auth by header
|
2002
|
+
|
2003
|
+
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:
|
2004
|
+
|
2005
|
+
```yaml
|
2006
|
+
[global]
|
2007
|
+
idp-hm-usr: ^Tailscale-User-Login^alice.m@forest.net^alice
|
2008
|
+
```
|
2009
|
+
|
2010
|
+
repeat the whole `idp-hm-usr` option to add more mappings
|
2011
|
+
|
2012
|
+
|
1979
2013
|
## user-changeable passwords
|
1980
2014
|
|
1981
2015
|
if permitted, users can change their own passwords in the control-panel
|
@@ -2857,9 +2891,10 @@ enable [music tags](#metadata-from-audio-files):
|
|
2857
2891
|
enable [thumbnails](#thumbnails) of...
|
2858
2892
|
* **images:** `Pillow` and/or `pyvips` and/or `ffmpeg` (requires py2.7 or py3.5+)
|
2859
2893
|
* **videos/audio:** `ffmpeg` and `ffprobe` somewhere in `$PATH`
|
2860
|
-
* **HEIF pictures:** `pyvips` or `ffmpeg` or `
|
2894
|
+
* **HEIF pictures:** `pyvips` or `ffmpeg` or `pillow-heif`
|
2861
2895
|
* **AVIF pictures:** `pyvips` or `ffmpeg` or `pillow-avif-plugin` or pillow v11.3+
|
2862
2896
|
* **JPEG XL pictures:** `pyvips` or `ffmpeg`
|
2897
|
+
* **RAW images:** `rawpy`, plus one of `pyvips` or `Pillow` (for some formats)
|
2863
2898
|
|
2864
2899
|
enable sending [zeromq messages](#zeromq) from event-hooks: `pyzmq`
|
2865
2900
|
|
@@ -2890,9 +2925,10 @@ set any of the following environment variables to disable its associated optiona
|
|
2890
2925
|
| `PRTY_NO_PIL` | disable all [Pillow](https://pypi.org/project/pillow/)-based thumbnail support; will fallback to libvips or ffmpeg |
|
2891
2926
|
| `PRTY_NO_PILF` | disable Pillow `ImageFont` text rendering, used for folder thumbnails |
|
2892
2927
|
| `PRTY_NO_PIL_AVIF` | disable Pillow avif support (internal and/or [plugin](https://pypi.org/project/pillow-avif-plugin/)) |
|
2893
|
-
| `PRTY_NO_PIL_HEIF` | disable 3rd-party Pillow plugin for [HEIF support](https://pypi.org/project/
|
2928
|
+
| `PRTY_NO_PIL_HEIF` | disable 3rd-party Pillow plugin for [HEIF support](https://pypi.org/project/pillow-heif/) |
|
2894
2929
|
| `PRTY_NO_PIL_WEBP` | disable use of native webp support in Pillow |
|
2895
2930
|
| `PRTY_NO_PSUTIL` | do not use [psutil](https://pypi.org/project/psutil/) for reaping stuck hooks and plugins on Windows |
|
2931
|
+
| `PRTY_NO_RAW` | disable all [rawpy](https://pypi.org/project/rawpy/)-based thumbnail support for RAW images |
|
2896
2932
|
| `PRTY_NO_VIPS` | disable all [libvips](https://pypi.org/project/pyvips/)-based thumbnail support; will fallback to Pillow or ffmpeg |
|
2897
2933
|
|
2898
2934
|
example: `PRTY_NO_PIL=1 python3 copyparty-sfx.py`
|
@@ -90,7 +90,9 @@ made in Norway 🇳🇴
|
|
90
90
|
* [upload events](#upload-events) - the older, more powerful approach ([examples](./bin/mtag/))
|
91
91
|
* [handlers](#handlers) - redefine behavior with plugins ([examples](./bin/handlers/))
|
92
92
|
* [ip auth](#ip-auth) - autologin based on IP range (CIDR)
|
93
|
+
* [restrict to ip](#restrict-to-ip) - limit a user to certain IP ranges (CIDR)
|
93
94
|
* [identity providers](#identity-providers) - replace copyparty passwords with oauth and such
|
95
|
+
* [generic header auth](#generic-header-auth) - other ways to auth by header
|
94
96
|
* [user-changeable passwords](#user-changeable-passwords) - if permitted, users can change their own passwords
|
95
97
|
* [using the cloud as storage](#using-the-cloud-as-storage) - connecting to an aws s3 bucket and similar
|
96
98
|
* [hiding from google](#hiding-from-google) - tell search engines you don't wanna be indexed
|
@@ -266,6 +268,7 @@ also see [comparison to similar software](./docs/versus.md)
|
|
266
268
|
* ☑ realtime streaming of growing files (logfiles and such)
|
267
269
|
* ☑ [thumbnails](#thumbnails)
|
268
270
|
* ☑ ...of images using Pillow, pyvips, or FFmpeg
|
271
|
+
* ☑ ...of RAW images using rawpy
|
269
272
|
* ☑ ...of videos using FFmpeg
|
270
273
|
* ☑ ...of audio (spectrograms) using FFmpeg
|
271
274
|
* ☑ cache eviction (max-age; maybe max-size eventually)
|
@@ -512,6 +515,8 @@ examples:
|
|
512
515
|
* replacing the `g` permission with `wg` would let anonymous users upload files, but not see the required filekey to access it
|
513
516
|
* replacing the `g` permission with `wG` would let anonymous users upload files, receiving a working direct link in return
|
514
517
|
|
518
|
+
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`
|
519
|
+
|
515
520
|
anyone trying to bruteforce a password gets banned according to `--ban-pw`; default is 24h ban for 9 failed attempts in 1 hour
|
516
521
|
|
517
522
|
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 +542,7 @@ and if you want to use config files instead of commandline args (good!) then her
|
|
537
542
|
accs:
|
538
543
|
r: u1, u2 # only these accounts can read,
|
539
544
|
r: @g1 # (exactly the same, just with a group instead)
|
545
|
+
r: @acct # (alternatively, ALL users who are logged in)
|
540
546
|
rw: u3 # and only u3 can read-write
|
541
547
|
|
542
548
|
[/inc]
|
@@ -1892,6 +1898,20 @@ repeat the option to map additional subnets
|
|
1892
1898
|
**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=`
|
1893
1899
|
|
1894
1900
|
|
1901
|
+
### restrict to ip
|
1902
|
+
|
1903
|
+
limit a user to certain IP ranges (CIDR) , using the global-option `--ipr`
|
1904
|
+
|
1905
|
+
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:
|
1906
|
+
|
1907
|
+
```yaml
|
1908
|
+
[global]
|
1909
|
+
ipr: 192.168.123.0/24,172.16.0.0/16=spartacus
|
1910
|
+
```
|
1911
|
+
|
1912
|
+
repeat the option to map additional users
|
1913
|
+
|
1914
|
+
|
1895
1915
|
## identity providers
|
1896
1916
|
|
1897
1917
|
replace copyparty passwords with oauth and such
|
@@ -1911,6 +1931,20 @@ a more complete example of the copyparty configuration options [look like this](
|
|
1911
1931
|
but if you just want to let users change their own passwords, then you probably want [user-changeable passwords](#user-changeable-passwords) instead
|
1912
1932
|
|
1913
1933
|
|
1934
|
+
### generic header auth
|
1935
|
+
|
1936
|
+
other ways to auth by header
|
1937
|
+
|
1938
|
+
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:
|
1939
|
+
|
1940
|
+
```yaml
|
1941
|
+
[global]
|
1942
|
+
idp-hm-usr: ^Tailscale-User-Login^alice.m@forest.net^alice
|
1943
|
+
```
|
1944
|
+
|
1945
|
+
repeat the whole `idp-hm-usr` option to add more mappings
|
1946
|
+
|
1947
|
+
|
1914
1948
|
## user-changeable passwords
|
1915
1949
|
|
1916
1950
|
if permitted, users can change their own passwords in the control-panel
|
@@ -2792,9 +2826,10 @@ enable [music tags](#metadata-from-audio-files):
|
|
2792
2826
|
enable [thumbnails](#thumbnails) of...
|
2793
2827
|
* **images:** `Pillow` and/or `pyvips` and/or `ffmpeg` (requires py2.7 or py3.5+)
|
2794
2828
|
* **videos/audio:** `ffmpeg` and `ffprobe` somewhere in `$PATH`
|
2795
|
-
* **HEIF pictures:** `pyvips` or `ffmpeg` or `
|
2829
|
+
* **HEIF pictures:** `pyvips` or `ffmpeg` or `pillow-heif`
|
2796
2830
|
* **AVIF pictures:** `pyvips` or `ffmpeg` or `pillow-avif-plugin` or pillow v11.3+
|
2797
2831
|
* **JPEG XL pictures:** `pyvips` or `ffmpeg`
|
2832
|
+
* **RAW images:** `rawpy`, plus one of `pyvips` or `Pillow` (for some formats)
|
2798
2833
|
|
2799
2834
|
enable sending [zeromq messages](#zeromq) from event-hooks: `pyzmq`
|
2800
2835
|
|
@@ -2825,9 +2860,10 @@ set any of the following environment variables to disable its associated optiona
|
|
2825
2860
|
| `PRTY_NO_PIL` | disable all [Pillow](https://pypi.org/project/pillow/)-based thumbnail support; will fallback to libvips or ffmpeg |
|
2826
2861
|
| `PRTY_NO_PILF` | disable Pillow `ImageFont` text rendering, used for folder thumbnails |
|
2827
2862
|
| `PRTY_NO_PIL_AVIF` | disable Pillow avif support (internal and/or [plugin](https://pypi.org/project/pillow-avif-plugin/)) |
|
2828
|
-
| `PRTY_NO_PIL_HEIF` | disable 3rd-party Pillow plugin for [HEIF support](https://pypi.org/project/
|
2863
|
+
| `PRTY_NO_PIL_HEIF` | disable 3rd-party Pillow plugin for [HEIF support](https://pypi.org/project/pillow-heif/) |
|
2829
2864
|
| `PRTY_NO_PIL_WEBP` | disable use of native webp support in Pillow |
|
2830
2865
|
| `PRTY_NO_PSUTIL` | do not use [psutil](https://pypi.org/project/psutil/) for reaping stuck hooks and plugins on Windows |
|
2866
|
+
| `PRTY_NO_RAW` | disable all [rawpy](https://pypi.org/project/rawpy/)-based thumbnail support for RAW images |
|
2831
2867
|
| `PRTY_NO_VIPS` | disable all [libvips](https://pypi.org/project/pyvips/)-based thumbnail support; will fallback to Pillow or ffmpeg |
|
2832
2868
|
|
2833
2869
|
example: `PRTY_NO_PIL=1 python3 copyparty-sfx.py`
|
@@ -428,6 +428,40 @@ def args_from_cfg(cfg_path ) :
|
|
428
428
|
return ret
|
429
429
|
|
430
430
|
|
431
|
+
def expand_cfg(argv) :
|
432
|
+
if CFG_DEF:
|
433
|
+
supp = args_from_cfg(CFG_DEF[0])
|
434
|
+
argv = supp + argv
|
435
|
+
|
436
|
+
n = spins = 0
|
437
|
+
while n < len(argv):
|
438
|
+
if not n:
|
439
|
+
if spins % 1000 == 999:
|
440
|
+
t = "still expanding config files... giving up after %d more"
|
441
|
+
print(t % (9999 - spins))
|
442
|
+
if spins > 9999:
|
443
|
+
t = "got stuck expanding config files; do you have a config-file which imports itself? this is where I gave up:\n%r"
|
444
|
+
raise Exception(t % (argv[:1000]))
|
445
|
+
v1 = argv[n]
|
446
|
+
v1v = v1[2:].lstrip("=")
|
447
|
+
try:
|
448
|
+
v2 = argv[n + 1]
|
449
|
+
except:
|
450
|
+
v2 = ""
|
451
|
+
|
452
|
+
if v1 == "-c" and v2 and os.path.isfile(v2):
|
453
|
+
argv = argv[:n] + args_from_cfg(v2) + argv[n + 2 :]
|
454
|
+
spins += 1
|
455
|
+
n = 0
|
456
|
+
elif v1.startswith("-c") and v1v and os.path.isfile(v1v):
|
457
|
+
argv = argv[:n] + args_from_cfg(v1v) + argv[n + 1 :]
|
458
|
+
spins += 1
|
459
|
+
n = 0
|
460
|
+
else:
|
461
|
+
n += 1
|
462
|
+
return argv
|
463
|
+
|
464
|
+
|
431
465
|
def sighandler(sig = None, frame = None) :
|
432
466
|
msg = [""] * 5
|
433
467
|
for th in threading.enumerate():
|
@@ -601,8 +635,41 @@ def get_sects():
|
|
601
635
|
if no accounts or volumes are configured,
|
602
636
|
current folder will be read/write for everyone
|
603
637
|
|
638
|
+
the group @acct will always have every user with an account
|
639
|
+
(the name of that group can be changed with --grp-all)
|
640
|
+
|
604
641
|
consider the config file for more flexible account/volume management,
|
605
642
|
including dynamic reload at runtime (and being more readable w)
|
643
|
+
|
644
|
+
see \033[32m--help-auth\033[0m for ways to provide the password in requests;
|
645
|
+
see \033[32m--help-idp\033[0m for replacing it with SSO and auth-middlewares
|
646
|
+
"""
|
647
|
+
),
|
648
|
+
],
|
649
|
+
[
|
650
|
+
"auth",
|
651
|
+
"how to login from a client",
|
652
|
+
dedent(
|
653
|
+
"""
|
654
|
+
different ways to provide the password so you become authenticated:
|
655
|
+
|
656
|
+
login with the ui:
|
657
|
+
go to \033[36mhttp://127.0.0.1:3923/?h\033[0m and login there
|
658
|
+
|
659
|
+
send the password in the '\033[36mPW\033[0m' http-header:
|
660
|
+
\033[36mPW: \033[35mhunter2\033[0m
|
661
|
+
or if you have \033[33m--accounts\033[0m enabled,
|
662
|
+
\033[36mPW: \033[35med:hunter2\033[0m
|
663
|
+
|
664
|
+
send the password in the URL itself:
|
665
|
+
\033[36mhttp://127.0.0.1:3923/\033[35m?pw=hunter2\033[0m
|
666
|
+
or if you have \033[33m--accounts\033[0m enabled,
|
667
|
+
\033[36mhttp://127.0.0.1:3923/\033[35m?pw=ed:hunter2\033[0m
|
668
|
+
|
669
|
+
use basic-authentication:
|
670
|
+
\033[36mhttp://\033[35med:hunter2\033[36m@127.0.0.1:3923/\033[0m
|
671
|
+
which should be the same as this header:
|
672
|
+
\033[36mAuthorization: Basic \033[35mZWQ6aHVudGVyMg==\033[0m
|
606
673
|
"""
|
607
674
|
),
|
608
675
|
],
|
@@ -754,6 +821,36 @@ def get_sects():
|
|
754
821
|
the upload speed can easily drop to 10% for small files)"""
|
755
822
|
),
|
756
823
|
],
|
824
|
+
[
|
825
|
+
"idp",
|
826
|
+
"replacing the login system with fancy middleware",
|
827
|
+
dedent(
|
828
|
+
"""
|
829
|
+
if you already have a centralized service which handles
|
830
|
+
user-authentication for other services already, you can
|
831
|
+
integrate copyparty with that for automatic login
|
832
|
+
|
833
|
+
if the middleware is providing the username in an http-header
|
834
|
+
named '\033[35mtheUsername\033[0m' then do this: \033[36m--idp-h-usr theUsername\033[0m
|
835
|
+
|
836
|
+
if the middleware is providing a list of groups in the header
|
837
|
+
named '\033[35mtheGroups\033[0m' then do this: \033[36m--idp-h-grp theGroup\033[0m
|
838
|
+
|
839
|
+
if the list of groups is separated by '\033[35m%\033[0m' then \033[36m--idp-gsep %\033[0m
|
840
|
+
|
841
|
+
if the middleware is providing a header named '\033[35mAccount\033[0m'
|
842
|
+
and the value is '\033[35malice@forest.net\033[0m' but the username is
|
843
|
+
actually '\033[35mmarisa\033[0m' then do this for each user:
|
844
|
+
\033[36m--idp-hm-usr ^Account^alice@forest.net^marisa\033[0m
|
845
|
+
(the separator '\033[35m^\033[0m' can be any character)
|
846
|
+
|
847
|
+
make ABSOLUTELY SURE that the header can only be set by your
|
848
|
+
middleware and not by clients! and, as an extra precaution,
|
849
|
+
send a header named '\033[36mfinalmasterspark\033[0m' (a secret keyword)
|
850
|
+
and then \033[36m--idp-h-key finalmasterspark\033[0m to require that
|
851
|
+
"""
|
852
|
+
),
|
853
|
+
],
|
757
854
|
[
|
758
855
|
"urlform",
|
759
856
|
"how to handle url-form POSTs",
|
@@ -1011,14 +1108,15 @@ def add_general(ap, nc, srvname):
|
|
1011
1108
|
|
1012
1109
|
def add_qr(ap, tty):
|
1013
1110
|
ap2 = ap.add_argument_group("qr options")
|
1014
|
-
ap2.add_argument("--qr", action="store_true", help="show
|
1015
|
-
ap2.add_argument("--qrs", action="store_true", help="
|
1111
|
+
ap2.add_argument("--qr", action="store_true", help="show QR-code on startup")
|
1112
|
+
ap2.add_argument("--qrs", action="store_true", help="change the QR-code URL to https://")
|
1016
1113
|
ap2.add_argument("--qrl", metavar="PATH", type=u, default="", help="location to include in the url, for example [\033[32mpriv/?pw=hunter2\033[0m]")
|
1017
1114
|
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")
|
1018
|
-
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")
|
1115
|
+
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")
|
1019
1116
|
ap2.add_argument("--qr-bg", metavar="COLOR", type=int, default=229, help="background (white=255)")
|
1020
1117
|
ap2.add_argument("--qrp", metavar="CELLS", type=int, default=4, help="padding (spec says 4 or more, but 1 is usually fine)")
|
1021
1118
|
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)")
|
1119
|
+
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")
|
1022
1120
|
|
1023
1121
|
|
1024
1122
|
def add_fs(ap):
|
@@ -1048,6 +1146,7 @@ def add_upload(ap):
|
|
1048
1146
|
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)")
|
1049
1147
|
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)")
|
1050
1148
|
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")
|
1149
|
+
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)")
|
1051
1150
|
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)")
|
1052
1151
|
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)")
|
1053
1152
|
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")
|
@@ -1141,7 +1240,8 @@ def add_auth(ap):
|
|
1141
1240
|
idp_db = os.path.join(E.cfg, "idp.db")
|
1142
1241
|
ses_db = os.path.join(E.cfg, "sessions.db")
|
1143
1242
|
ap2 = ap.add_argument_group("IdP / identity provider / user authentication options")
|
1144
|
-
ap2.add_argument("--idp-h-usr", metavar="HN", type=u,
|
1243
|
+
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")
|
1244
|
+
ap2.add_argument("--idp-hm-usr", metavar="TXT", type=u, action="append", help="\033[34mREPEATABLE:\033[0m bypass the copyparty authentication checks if the request-header \033[33mHN\033[0m is provided, and its value exists in a mapping defined by this option; see --help-idp")
|
1145
1245
|
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")
|
1146
1246
|
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")
|
1147
1247
|
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")
|
@@ -1154,7 +1254,11 @@ def add_auth(ap):
|
|
1154
1254
|
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)")
|
1155
1255
|
ap2.add_argument("--ses-len", metavar="CHARS", type=int, default=20, help="session key length; default is 120 bits ((20//4)*4*6)")
|
1156
1256
|
ap2.add_argument("--no-ses", action="store_true", help="disable sessions; use plaintext passwords in cookies")
|
1257
|
+
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")
|
1157
1258
|
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]")
|
1259
|
+
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]")
|
1260
|
+
ap2.add_argument("--have-idp-hdrs", type=u, default="", help=argparse.SUPPRESS)
|
1261
|
+
ap2.add_argument("--have-ipu-or-ipr", type=u, default="", help=argparse.SUPPRESS)
|
1158
1262
|
|
1159
1263
|
|
1160
1264
|
def add_chpw(ap):
|
@@ -1310,6 +1414,7 @@ def add_optouts(ap):
|
|
1310
1414
|
ap2.add_argument("--no-del", action="store_true", help="disable delete operations")
|
1311
1415
|
ap2.add_argument("--no-mv", action="store_true", help="disable move/rename operations")
|
1312
1416
|
ap2.add_argument("--no-cp", action="store_true", help="disable copy operations")
|
1417
|
+
ap2.add_argument("--no-fs-abrt", action="store_true", help="disable ability to abort ongoing copy/move")
|
1313
1418
|
ap2.add_argument("-nth", action="store_true", help="no title hostname; don't show \033[33m--name\033[0m in <title>")
|
1314
1419
|
ap2.add_argument("-nih", action="store_true", help="no info hostname -- don't show in UI")
|
1315
1420
|
ap2.add_argument("-nid", action="store_true", help="no info disk-usage -- don't show in UI")
|
@@ -1331,7 +1436,7 @@ def add_optouts(ap):
|
|
1331
1436
|
def add_safety(ap):
|
1332
1437
|
ap2 = ap.add_argument_group("safety options")
|
1333
1438
|
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")
|
1334
|
-
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")
|
1439
|
+
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")
|
1335
1440
|
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")
|
1336
1441
|
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]")
|
1337
1442
|
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)")
|
@@ -1353,6 +1458,8 @@ def add_safety(ap):
|
|
1353
1458
|
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]")
|
1354
1459
|
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]")
|
1355
1460
|
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")
|
1461
|
+
ap2.add_argument("--cookie-nmax", metavar="N", type=int, default=50, help="reject HTTP-request from client if they send more than N cookies")
|
1462
|
+
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")
|
1356
1463
|
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")
|
1357
1464
|
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]")
|
1358
1465
|
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")
|
@@ -1419,11 +1526,12 @@ def add_thumbnail(ap):
|
|
1419
1526
|
ap2.add_argument("--no-athumb", action="store_true", help="disable audio thumbnails (spectrograms) (volflag=dathumb)")
|
1420
1527
|
ap2.add_argument("--th-size", metavar="WxH", default="320x256", help="thumbnail res (volflag=thsize)")
|
1421
1528
|
ap2.add_argument("--th-mt", metavar="CORES", type=int, default=CORES, help="num cpu cores to use for generating thumbnails")
|
1422
|
-
ap2.add_argument("--th-convt", metavar="SEC", type=float, default=60.0, help="
|
1529
|
+
ap2.add_argument("--th-convt", metavar="SEC", type=float, default=60.0, help="convert-to-image timeout in seconds (volflag=convt)")
|
1530
|
+
ap2.add_argument("--ac-convt", metavar="SEC", type=float, default=150.0, help="convert-to-audio timeout in seconds (volflag=aconvt)")
|
1423
1531
|
ap2.add_argument("--th-ram-max", metavar="GB", type=float, default=th_ram, help="max memory usage (GiB) permitted by thumbnailer; not very accurate")
|
1424
1532
|
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)")
|
1425
1533
|
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)")
|
1426
|
-
ap2.add_argument("--th-dec", metavar="LIBS", default="vips,pil,ff", help="image decoders, in order of preference")
|
1534
|
+
ap2.add_argument("--th-dec", metavar="LIBS", default="vips,pil,raw,ff", help="image decoders, in order of preference")
|
1427
1535
|
ap2.add_argument("--th-no-jpg", action="store_true", help="disable jpg output")
|
1428
1536
|
ap2.add_argument("--th-no-webp", action="store_true", help="disable webp output")
|
1429
1537
|
ap2.add_argument("--th-ff-jpg", action="store_true", help="force jpg output for video thumbs (avoids issues on some FFmpeg builds)")
|
@@ -1432,16 +1540,19 @@ def add_thumbnail(ap):
|
|
1432
1540
|
ap2.add_argument("--th-clean", metavar="SEC", type=int, default=43200, help="cleanup interval; 0=disabled")
|
1433
1541
|
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")
|
1434
1542
|
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")
|
1543
|
+
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")
|
1435
1544
|
# https://pillow.readthedocs.io/en/stable/handbook/image-file-formats.html
|
1436
1545
|
# https://github.com/libvips/libvips
|
1546
|
+
# https://stackoverflow.com/a/47612661
|
1437
1547
|
# 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:'
|
1438
|
-
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")
|
1439
|
-
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")
|
1440
|
-
ap2.add_argument("--th-r-
|
1548
|
+
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")
|
1549
|
+
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")
|
1550
|
+
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")
|
1551
|
+
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")
|
1441
1552
|
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")
|
1442
1553
|
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")
|
1443
1554
|
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)")
|
1444
|
-
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")
|
1555
|
+
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")
|
1445
1556
|
|
1446
1557
|
|
1447
1558
|
def add_transcoding(ap):
|
@@ -1553,13 +1664,14 @@ def add_og(ap):
|
|
1553
1664
|
|
1554
1665
|
|
1555
1666
|
def add_ui(ap, retry):
|
1667
|
+
THEMES = 10
|
1556
1668
|
ap2 = ap.add_argument_group("ui options")
|
1557
1669
|
ap2.add_argument("--grid", action="store_true", help="show grid/thumbnails by default (volflag=grid)")
|
1558
1670
|
ap2.add_argument("--gsel", action="store_true", help="select files in grid by ctrl-click (volflag=gsel)")
|
1559
1671
|
ap2.add_argument("--localtime", action="store_true", help="default to local timezone instead of UTC")
|
1560
|
-
ap2.add_argument("--lang", metavar="LANG", type=u, default="eng", help="language
|
1561
|
-
ap2.add_argument("--theme", metavar="NUM", type=int, default=0, help="default theme to use (0
|
1562
|
-
ap2.add_argument("--themes", metavar="NUM", type=int, default=
|
1672
|
+
ap2.add_argument("--lang", metavar="LANG", type=u, default="eng", help="language, for example \033[32meng\033[0m / \033[32mnor\033[0m / ...")
|
1673
|
+
ap2.add_argument("--theme", metavar="NUM", type=int, default=0, help="default theme to use (0..%d)" % (THEMES - 1,))
|
1674
|
+
ap2.add_argument("--themes", metavar="NUM", type=int, default=THEMES, help="number of themes installed")
|
1563
1675
|
ap2.add_argument("--au-vol", metavar="0-100", type=int, default=50, choices=range(0, 101), help="default audio/video volume percent")
|
1564
1676
|
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)")
|
1565
1677
|
ap2.add_argument("--nsort", action="store_true", help="default-enable natural sort of filenames with leading numbers (volflag=nsort)")
|
@@ -1760,20 +1872,7 @@ def main(argv = None) :
|
|
1760
1872
|
|
1761
1873
|
ensure_webdeps()
|
1762
1874
|
|
1763
|
-
|
1764
|
-
supp = args_from_cfg(CFG_DEF[0])
|
1765
|
-
argv.extend(supp)
|
1766
|
-
|
1767
|
-
for k, v in zip(argv[1:], argv[2:]):
|
1768
|
-
if k == "-c" and os.path.isfile(v):
|
1769
|
-
supp = args_from_cfg(v)
|
1770
|
-
argv.extend(supp)
|
1771
|
-
|
1772
|
-
for k in argv[1:]:
|
1773
|
-
v = k[2:]
|
1774
|
-
if k.startswith("-c") and v and os.path.isfile(v):
|
1775
|
-
supp = args_from_cfg(v)
|
1776
|
-
argv.extend(supp)
|
1875
|
+
argv = expand_cfg(argv)
|
1777
1876
|
|
1778
1877
|
deprecated = [
|
1779
1878
|
("--salt", "--warksalt"),
|
@@ -874,6 +874,15 @@ class VFS(object):
|
|
874
874
|
return None
|
875
875
|
|
876
876
|
if "xvol" in self.flags:
|
877
|
+
self_ap = self.realpath + os.sep
|
878
|
+
if aps.startswith(self_ap):
|
879
|
+
vp = aps[len(self_ap) :]
|
880
|
+
if ANYWIN:
|
881
|
+
vp = vp.replace(os.sep, "/")
|
882
|
+
vn2, _ = self._find(vp)
|
883
|
+
if self == vn2:
|
884
|
+
return self
|
885
|
+
|
877
886
|
all_aps = self.shr_all_aps or self.root.all_aps
|
878
887
|
|
879
888
|
for vap, vns in all_aps:
|
@@ -1091,6 +1100,9 @@ class AuthSrv(object):
|
|
1091
1100
|
if rejected:
|
1092
1101
|
continue
|
1093
1102
|
|
1103
|
+
if gn == self.args.grp_all:
|
1104
|
+
gn = ""
|
1105
|
+
|
1094
1106
|
# if ap/vp has a user/group placeholder, make sure to keep
|
1095
1107
|
# track so the same user/group is mapped when setting perms;
|
1096
1108
|
# otherwise clear un/gn to indicate it's a regular volume
|
@@ -1200,6 +1212,7 @@ class AuthSrv(object):
|
|
1200
1212
|
self.load_idp_db(bool(self.idp_accs))
|
1201
1213
|
ret = {un: gns[:] for un, gns in self.idp_accs.items()}
|
1202
1214
|
ret.update({zs: [""] for zs in acct if zs not in ret})
|
1215
|
+
grps[self.args.grp_all] = list(ret.keys())
|
1203
1216
|
for gn, uns in grps.items():
|
1204
1217
|
for un in uns:
|
1205
1218
|
try:
|
@@ -1677,6 +1690,9 @@ class AuthSrv(object):
|
|
1677
1690
|
self.log("\n{0}\n{1}{0}".format(t, "\n".join(slns)))
|
1678
1691
|
raise
|
1679
1692
|
|
1693
|
+
self.args.have_idp_hdrs = bool(self.args.idp_h_usr or self.args.idp_hm_usr)
|
1694
|
+
self.args.have_ipu_or_ipr = bool(self.args.ipu or self.args.ipr)
|
1695
|
+
|
1680
1696
|
self.setup_pwhash(acct)
|
1681
1697
|
defpw = acct.copy()
|
1682
1698
|
self.setup_chpw(acct)
|
@@ -1689,7 +1705,7 @@ class AuthSrv(object):
|
|
1689
1705
|
|
1690
1706
|
mount = cased
|
1691
1707
|
|
1692
|
-
if not mount and not self.args.
|
1708
|
+
if not mount and not self.args.have_idp_hdrs:
|
1693
1709
|
# -h says our defaults are CWD at root and read/write for everyone
|
1694
1710
|
axs = AXS(["*"], ["*"], None, None)
|
1695
1711
|
ehint = ""
|
@@ -1861,7 +1877,7 @@ class AuthSrv(object):
|
|
1861
1877
|
|
1862
1878
|
if missing_users:
|
1863
1879
|
zs = ", ".join(k for k in sorted(missing_users))
|
1864
|
-
if self.args.
|
1880
|
+
if self.args.have_idp_hdrs:
|
1865
1881
|
t = "the following users are unknown, and assumed to come from IdP: "
|
1866
1882
|
self.log(t + zs, c=6)
|
1867
1883
|
else:
|
@@ -1872,6 +1888,16 @@ class AuthSrv(object):
|
|
1872
1888
|
if LEELOO_DALLAS in all_users:
|
1873
1889
|
raise Exception("sorry, reserved username: " + LEELOO_DALLAS)
|
1874
1890
|
|
1891
|
+
zsl = []
|
1892
|
+
for usr in list(acct)[:]:
|
1893
|
+
zs = acct[usr].strip()
|
1894
|
+
if not zs:
|
1895
|
+
zs = ub64enc(os.urandom(48)).decode("ascii")
|
1896
|
+
zsl.append(usr)
|
1897
|
+
acct[usr] = zs
|
1898
|
+
if zsl:
|
1899
|
+
self.log("generated random passwords for users %r" % (zsl,), 6)
|
1900
|
+
|
1875
1901
|
seenpwds = {}
|
1876
1902
|
for usr, pwd in acct.items():
|
1877
1903
|
if pwd in seenpwds:
|
@@ -2192,12 +2218,12 @@ class AuthSrv(object):
|
|
2192
2218
|
if vf not in vol.flags:
|
2193
2219
|
vol.flags[vf] = getattr(self.args, ga)
|
2194
2220
|
|
2195
|
-
zs = "forget_ip gid nrand tail_who u2abort u2ow uid ups_who zip_who"
|
2221
|
+
zs = "forget_ip gid nrand tail_who th_spec_p u2abort u2ow uid unp_who ups_who zip_who"
|
2196
2222
|
for k in zs.split():
|
2197
2223
|
if k in vol.flags:
|
2198
2224
|
vol.flags[k] = int(vol.flags[k])
|
2199
2225
|
|
2200
|
-
zs = "convt tail_fd tail_rate tail_tmax"
|
2226
|
+
zs = "aconvt convt tail_fd tail_rate tail_tmax"
|
2201
2227
|
for k in zs.split():
|
2202
2228
|
if k in vol.flags:
|
2203
2229
|
vol.flags[k] = float(vol.flags[k])
|
@@ -2528,7 +2554,7 @@ class AuthSrv(object):
|
|
2528
2554
|
if not self.args.no_voldump:
|
2529
2555
|
self.log(t)
|
2530
2556
|
|
2531
|
-
if have_e2d or self.args.
|
2557
|
+
if have_e2d or self.args.have_idp_hdrs:
|
2532
2558
|
t = self.chk_sqlite_threadsafe()
|
2533
2559
|
if t:
|
2534
2560
|
self.log("\n\033[{}\033[0m\n".format(t))
|
@@ -2817,7 +2843,7 @@ class AuthSrv(object):
|
|
2817
2843
|
def load_idp_db(self, quiet=False) :
|
2818
2844
|
# mutex me
|
2819
2845
|
level = self.args.idp_store
|
2820
|
-
if level < 2 or not self.args.
|
2846
|
+
if level < 2 or not self.args.have_idp_hdrs:
|
2821
2847
|
return
|
2822
2848
|
|
2823
2849
|
|
@@ -2872,7 +2898,7 @@ class AuthSrv(object):
|
|
2872
2898
|
n = []
|
2873
2899
|
q = "insert into us values (?,?,?)"
|
2874
2900
|
accs = list(self.acct)
|
2875
|
-
if self.args.
|
2901
|
+
if self.args.have_idp_hdrs and self.args.idp_cookie:
|
2876
2902
|
accs.extend(self.idp_accs.keys())
|
2877
2903
|
for uname in accs:
|
2878
2904
|
if uname not in ases:
|