copyparty 1.11.1__tar.gz → 1.12.0__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.
Files changed (120) hide show
  1. {copyparty-1.11.1 → copyparty-1.12.0}/PKG-INFO +28 -7
  2. {copyparty-1.11.1 → copyparty-1.12.0}/README.md +27 -6
  3. {copyparty-1.11.1 → copyparty-1.12.0}/copyparty/__init__.py +0 -1
  4. {copyparty-1.11.1 → copyparty-1.12.0}/copyparty/__main__.py +26 -31
  5. {copyparty-1.11.1 → copyparty-1.12.0}/copyparty/__version__.py +3 -3
  6. {copyparty-1.11.1 → copyparty-1.12.0}/copyparty/authsrv.py +41 -23
  7. {copyparty-1.11.1 → copyparty-1.12.0}/copyparty/ftpd.py +1 -1
  8. {copyparty-1.11.1 → copyparty-1.12.0}/copyparty/httpcli.py +112 -28
  9. {copyparty-1.11.1 → copyparty-1.12.0}/copyparty/mtag.py +1 -2
  10. {copyparty-1.11.1 → copyparty-1.12.0}/copyparty/star.py +4 -2
  11. {copyparty-1.11.1 → copyparty-1.12.0}/copyparty/sutil.py +6 -1
  12. {copyparty-1.11.1 → copyparty-1.12.0}/copyparty/svchub.py +26 -1
  13. {copyparty-1.11.1 → copyparty-1.12.0}/copyparty/szip.py +5 -3
  14. {copyparty-1.11.1 → copyparty-1.12.0}/copyparty/tftpd.py +3 -0
  15. {copyparty-1.11.1 → copyparty-1.12.0}/copyparty/th_cli.py +1 -1
  16. {copyparty-1.11.1 → copyparty-1.12.0}/copyparty/th_srv.py +48 -6
  17. {copyparty-1.11.1 → copyparty-1.12.0}/copyparty/up2k.py +1 -1
  18. {copyparty-1.11.1 → copyparty-1.12.0}/copyparty/util.py +18 -11
  19. copyparty-1.12.0/copyparty/web/baguettebox.js.gz +0 -0
  20. {copyparty-1.11.1 → copyparty-1.12.0}/copyparty/web/browser.css.gz +0 -0
  21. copyparty-1.12.0/copyparty/web/browser.js.gz +0 -0
  22. copyparty-1.12.0/copyparty/web/deps/busy.mp3.gz +0 -0
  23. copyparty-1.12.0/copyparty/web/deps/marked.js.gz +0 -0
  24. {copyparty-1.11.1 → copyparty-1.12.0}/copyparty/web/md.css.gz +0 -0
  25. {copyparty-1.11.1 → copyparty-1.12.0}/copyparty/web/md.js.gz +0 -0
  26. {copyparty-1.11.1 → copyparty-1.12.0}/copyparty/web/md2.css.gz +0 -0
  27. {copyparty-1.11.1 → copyparty-1.12.0}/copyparty/web/md2.js.gz +0 -0
  28. {copyparty-1.11.1 → copyparty-1.12.0}/copyparty/web/mde.css.gz +0 -0
  29. {copyparty-1.11.1 → copyparty-1.12.0}/copyparty/web/mde.js.gz +0 -0
  30. {copyparty-1.11.1 → copyparty-1.12.0}/copyparty/web/msg.css.gz +0 -0
  31. {copyparty-1.11.1 → copyparty-1.12.0}/copyparty/web/splash.css.gz +0 -0
  32. {copyparty-1.11.1 → copyparty-1.12.0}/copyparty/web/splash.js.gz +0 -0
  33. {copyparty-1.11.1 → copyparty-1.12.0}/copyparty/web/ui.css.gz +0 -0
  34. copyparty-1.12.0/copyparty/web/up2k.js.gz +0 -0
  35. copyparty-1.12.0/copyparty/web/util.js.gz +0 -0
  36. {copyparty-1.11.1 → copyparty-1.12.0}/copyparty.egg-info/PKG-INFO +28 -7
  37. {copyparty-1.11.1 → copyparty-1.12.0}/copyparty.egg-info/SOURCES.txt +1 -0
  38. copyparty-1.11.1/copyparty/web/baguettebox.js.gz +0 -0
  39. copyparty-1.11.1/copyparty/web/browser.js.gz +0 -0
  40. copyparty-1.11.1/copyparty/web/deps/marked.js.gz +0 -0
  41. copyparty-1.11.1/copyparty/web/up2k.js.gz +0 -0
  42. copyparty-1.11.1/copyparty/web/util.js.gz +0 -0
  43. {copyparty-1.11.1 → copyparty-1.12.0}/LICENSE +0 -0
  44. {copyparty-1.11.1 → copyparty-1.12.0}/copyparty/bos/__init__.py +0 -0
  45. {copyparty-1.11.1 → copyparty-1.12.0}/copyparty/bos/bos.py +0 -0
  46. {copyparty-1.11.1 → copyparty-1.12.0}/copyparty/bos/path.py +0 -0
  47. {copyparty-1.11.1 → copyparty-1.12.0}/copyparty/broker_mp.py +0 -0
  48. {copyparty-1.11.1 → copyparty-1.12.0}/copyparty/broker_mpw.py +0 -0
  49. {copyparty-1.11.1 → copyparty-1.12.0}/copyparty/broker_thr.py +0 -0
  50. {copyparty-1.11.1 → copyparty-1.12.0}/copyparty/broker_util.py +0 -0
  51. {copyparty-1.11.1 → copyparty-1.12.0}/copyparty/cert.py +0 -0
  52. {copyparty-1.11.1 → copyparty-1.12.0}/copyparty/cfg.py +0 -0
  53. {copyparty-1.11.1 → copyparty-1.12.0}/copyparty/dxml.py +0 -0
  54. {copyparty-1.11.1 → copyparty-1.12.0}/copyparty/fsutil.py +0 -0
  55. {copyparty-1.11.1 → copyparty-1.12.0}/copyparty/httpconn.py +0 -0
  56. {copyparty-1.11.1 → copyparty-1.12.0}/copyparty/httpsrv.py +0 -0
  57. {copyparty-1.11.1 → copyparty-1.12.0}/copyparty/ico.py +0 -0
  58. {copyparty-1.11.1 → copyparty-1.12.0}/copyparty/mdns.py +0 -0
  59. {copyparty-1.11.1 → copyparty-1.12.0}/copyparty/metrics.py +0 -0
  60. {copyparty-1.11.1 → copyparty-1.12.0}/copyparty/multicast.py +0 -0
  61. {copyparty-1.11.1 → copyparty-1.12.0}/copyparty/pwhash.py +0 -0
  62. {copyparty-1.11.1 → copyparty-1.12.0}/copyparty/res/COPYING.txt +0 -0
  63. {copyparty-1.11.1 → copyparty-1.12.0}/copyparty/res/__init__.py +0 -0
  64. {copyparty-1.11.1 → copyparty-1.12.0}/copyparty/res/insecure.pem +0 -0
  65. {copyparty-1.11.1 → copyparty-1.12.0}/copyparty/smbd.py +0 -0
  66. {copyparty-1.11.1 → copyparty-1.12.0}/copyparty/ssdp.py +0 -0
  67. {copyparty-1.11.1 → copyparty-1.12.0}/copyparty/stolen/__init__.py +0 -0
  68. {copyparty-1.11.1 → copyparty-1.12.0}/copyparty/stolen/dnslib/__init__.py +0 -0
  69. {copyparty-1.11.1 → copyparty-1.12.0}/copyparty/stolen/dnslib/bimap.py +0 -0
  70. {copyparty-1.11.1 → copyparty-1.12.0}/copyparty/stolen/dnslib/bit.py +0 -0
  71. {copyparty-1.11.1 → copyparty-1.12.0}/copyparty/stolen/dnslib/buffer.py +0 -0
  72. {copyparty-1.11.1 → copyparty-1.12.0}/copyparty/stolen/dnslib/dns.py +0 -0
  73. {copyparty-1.11.1 → copyparty-1.12.0}/copyparty/stolen/dnslib/label.py +0 -0
  74. {copyparty-1.11.1 → copyparty-1.12.0}/copyparty/stolen/dnslib/lex.py +0 -0
  75. {copyparty-1.11.1 → copyparty-1.12.0}/copyparty/stolen/dnslib/ranges.py +0 -0
  76. {copyparty-1.11.1 → copyparty-1.12.0}/copyparty/stolen/ifaddr/__init__.py +0 -0
  77. {copyparty-1.11.1 → copyparty-1.12.0}/copyparty/stolen/ifaddr/_posix.py +0 -0
  78. {copyparty-1.11.1 → copyparty-1.12.0}/copyparty/stolen/ifaddr/_shared.py +0 -0
  79. {copyparty-1.11.1 → copyparty-1.12.0}/copyparty/stolen/ifaddr/_win32.py +0 -0
  80. {copyparty-1.11.1 → copyparty-1.12.0}/copyparty/stolen/qrcodegen.py +0 -0
  81. {copyparty-1.11.1 → copyparty-1.12.0}/copyparty/stolen/surrogateescape.py +0 -0
  82. {copyparty-1.11.1 → copyparty-1.12.0}/copyparty/tcpsrv.py +0 -0
  83. {copyparty-1.11.1 → copyparty-1.12.0}/copyparty/u2idx.py +0 -0
  84. {copyparty-1.11.1 → copyparty-1.12.0}/copyparty/web/a/__init__.py +0 -0
  85. {copyparty-1.11.1 → copyparty-1.12.0}/copyparty/web/a/partyfuse.py +0 -0
  86. {copyparty-1.11.1 → copyparty-1.12.0}/copyparty/web/a/u2c.py +0 -0
  87. {copyparty-1.11.1 → copyparty-1.12.0}/copyparty/web/a/webdav-cfg.bat +0 -0
  88. {copyparty-1.11.1 → copyparty-1.12.0}/copyparty/web/browser.html +0 -0
  89. {copyparty-1.11.1 → copyparty-1.12.0}/copyparty/web/browser2.html +0 -0
  90. {copyparty-1.11.1 → copyparty-1.12.0}/copyparty/web/cf.html +0 -0
  91. {copyparty-1.11.1 → copyparty-1.12.0}/copyparty/web/dbg-audio.js.gz +0 -0
  92. {copyparty-1.11.1 → copyparty-1.12.0}/copyparty/web/dd/2.png +0 -0
  93. {copyparty-1.11.1 → copyparty-1.12.0}/copyparty/web/dd/3.png +0 -0
  94. {copyparty-1.11.1 → copyparty-1.12.0}/copyparty/web/dd/4.png +0 -0
  95. {copyparty-1.11.1 → copyparty-1.12.0}/copyparty/web/dd/5.png +0 -0
  96. {copyparty-1.11.1 → copyparty-1.12.0}/copyparty/web/dd/__init__.py +0 -0
  97. {copyparty-1.11.1 → copyparty-1.12.0}/copyparty/web/deps/__init__.py +0 -0
  98. {copyparty-1.11.1 → copyparty-1.12.0}/copyparty/web/deps/easymde.css.gz +0 -0
  99. {copyparty-1.11.1 → copyparty-1.12.0}/copyparty/web/deps/easymde.js.gz +0 -0
  100. {copyparty-1.11.1 → copyparty-1.12.0}/copyparty/web/deps/mini-fa.css.gz +0 -0
  101. {copyparty-1.11.1 → copyparty-1.12.0}/copyparty/web/deps/mini-fa.woff +0 -0
  102. {copyparty-1.11.1 → copyparty-1.12.0}/copyparty/web/deps/prism.css.gz +0 -0
  103. {copyparty-1.11.1 → copyparty-1.12.0}/copyparty/web/deps/prism.js.gz +0 -0
  104. {copyparty-1.11.1 → copyparty-1.12.0}/copyparty/web/deps/prismd.css.gz +0 -0
  105. {copyparty-1.11.1 → copyparty-1.12.0}/copyparty/web/deps/scp.woff2 +0 -0
  106. {copyparty-1.11.1 → copyparty-1.12.0}/copyparty/web/deps/sha512.ac.js.gz +0 -0
  107. {copyparty-1.11.1 → copyparty-1.12.0}/copyparty/web/deps/sha512.hw.js.gz +0 -0
  108. {copyparty-1.11.1 → copyparty-1.12.0}/copyparty/web/md.html +0 -0
  109. {copyparty-1.11.1 → copyparty-1.12.0}/copyparty/web/mde.html +0 -0
  110. {copyparty-1.11.1 → copyparty-1.12.0}/copyparty/web/msg.html +0 -0
  111. {copyparty-1.11.1 → copyparty-1.12.0}/copyparty/web/splash.html +0 -0
  112. {copyparty-1.11.1 → copyparty-1.12.0}/copyparty/web/svcs.html +0 -0
  113. {copyparty-1.11.1 → copyparty-1.12.0}/copyparty/web/svcs.js.gz +0 -0
  114. {copyparty-1.11.1 → copyparty-1.12.0}/copyparty/web/w.hash.js.gz +0 -0
  115. {copyparty-1.11.1 → copyparty-1.12.0}/copyparty.egg-info/dependency_links.txt +0 -0
  116. {copyparty-1.11.1 → copyparty-1.12.0}/copyparty.egg-info/entry_points.txt +0 -0
  117. {copyparty-1.11.1 → copyparty-1.12.0}/copyparty.egg-info/requires.txt +0 -0
  118. {copyparty-1.11.1 → copyparty-1.12.0}/copyparty.egg-info/top_level.txt +0 -0
  119. {copyparty-1.11.1 → copyparty-1.12.0}/pyproject.toml +0 -0
  120. {copyparty-1.11.1 → copyparty-1.12.0}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: copyparty
3
- Version: 1.11.1
3
+ Version: 1.12.0
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
@@ -148,6 +148,7 @@ turn almost any device into a file server with resumable uploads/downloads using
148
148
  * [gotchas](#gotchas) - behavior that might be unexpected
149
149
  * [cors](#cors) - cross-site request config
150
150
  * [filekeys](#filekeys) - prevent filename bruteforcing
151
+ * [dirkeys](#dirkeys) - share specific folders in a volume
151
152
  * [password hashing](#password-hashing) - you can hash passwords
152
153
  * [https](#https) - both HTTP and HTTPS are accepted
153
154
  * [recovering from crashes](#recovering-from-crashes)
@@ -253,7 +254,7 @@ firewall-cmd --reload
253
254
  * browser
254
255
  * ☑ [navpane](#navpane) (directory tree sidebar)
255
256
  * ☑ file manager (cut/paste, delete, [batch-rename](#batch-rename))
256
- * ☑ audio player (with [OS media controls](https://user-images.githubusercontent.com/241032/215347492-b4250797-6c90-4e09-9a4c-721edf2fb15c.png) and opus transcoding)
257
+ * ☑ audio player (with [OS media controls](https://user-images.githubusercontent.com/241032/215347492-b4250797-6c90-4e09-9a4c-721edf2fb15c.png) and opus/mp3 transcoding)
257
258
  * ☑ image gallery with webm player
258
259
  * ☑ textfile browser with syntax hilighting
259
260
  * ☑ [thumbnails](#thumbnails)
@@ -641,7 +642,7 @@ you can also zip a selection of files or folders by clicking them in the browser
641
642
 
642
643
  ![copyparty-zipsel-fs8](https://user-images.githubusercontent.com/241032/129635374-e5136e01-470a-49b1-a762-848e8a4c9cdc.png)
643
644
 
644
- cool trick: download a folder by appending url-params `?tar&opus` to transcode all audio files (except aac|m4a|mp3|ogg|opus|wma) to opus before they're added to the archive
645
+ cool trick: download a folder by appending url-params `?tar&opus` or `?tar&mp3` to transcode all audio files (except aac|m4a|mp3|ogg|opus|wma) to opus/mp3 before they're added to the archive
645
646
  * super useful if you're 5 minutes away from takeoff and realize you don't have any music on your phone but your server only has flac files and downloading those will burn through all your data + there wouldn't be enough time anyways
646
647
  * and url-params `&j` / `&w` produce jpeg/webm thumbnails/spectrograms instead of the original audio/video/images
647
648
  * can also be used to pregenerate thumbnails; combine with `--th-maxage=9999999` or `--th-clean=0`
@@ -832,9 +833,9 @@ open the `[🎺]` media-player-settings tab to configure it,
832
833
  * `[loop]` keeps looping the folder
833
834
  * `[next]` plays into the next folder
834
835
  * "transcode":
835
- * `[flac]` converts `flac` and `wav` files into opus
836
- * `[aac]` converts `aac` and `m4a` files into opus
837
- * `[oth]` converts all other known formats into opus
836
+ * `[flac]` converts `flac` and `wav` files into opus (if supported by browser) or mp3
837
+ * `[aac]` converts `aac` and `m4a` files into opus (if supported by browser) or mp3
838
+ * `[oth]` converts all other known formats into opus (if supported by browser) or mp3
838
839
  * `aac|ac3|aif|aiff|alac|alaw|amr|ape|au|dfpwm|dts|flac|gsm|it|m4a|mo3|mod|mp2|mp3|mpc|mptm|mt2|mulaw|ogg|okt|opus|ra|s3m|tak|tta|ulaw|wav|wma|wv|xm|xpk`
839
840
  * "tint" reduces the contrast of the playback bar
840
841
 
@@ -1345,6 +1346,8 @@ you may experience poor upload performance this way, but that can sometimes be f
1345
1346
 
1346
1347
  someone has also tested geesefs in combination with [gocryptfs](https://nuetzlich.net/gocryptfs/) with surprisingly good results, getting 60 MiB/s upload speeds on a gbit line, but JuiceFS won with 80 MiB/s using its built-in encryption
1347
1348
 
1349
+ you may improve performance by specifying larger values for `--iobuf` / `--s-rd-sz` / `--s-wr-sz`
1350
+
1348
1351
 
1349
1352
  ## hiding from google
1350
1353
 
@@ -1794,6 +1797,7 @@ below are some tweaks roughly ordered by usefulness:
1794
1797
  * `--hist` pointing to a fast location (ssd) will make directory listings and searches faster when `-e2d` or `-e2t` is set
1795
1798
  * and also makes thumbnails load faster, regardless of e2d/e2t
1796
1799
  * `--no-hash .` when indexing a network-disk if you don't care about the actual filehashes and only want the names/tags searchable
1800
+ * if your volumes are on a network-disk such as NFS / SMB / s3, specifying larger values for `--iobuf` and/or `--s-rd-sz` and/or `--s-wr-sz` may help; try setting all of them to `524288` or `1048576` or `4194304`
1797
1801
  * `--no-htp --hash-mt=0 --mtag-mt=1 --th-mt=1` minimizes the number of threads; can help in some eccentric environments (like the vscode debugger)
1798
1802
  * `-j0` enables multiprocessing (actual multithreading), can reduce latency to `20+80/numCores` percent and generally improve performance in cpu-intensive workloads, for example:
1799
1803
  * lots of connections (many users or heavy clients)
@@ -1890,12 +1894,29 @@ cors can be configured with `--acao` and `--acam`, or the protections entirely d
1890
1894
 
1891
1895
  prevent filename bruteforcing
1892
1896
 
1893
- volflag `c,fk` generates filekeys (per-file accesskeys) for all files; users which have full read-access (permission `r`) will then see URLs with the correct filekey `?k=...` appended to the end, and `g` users must provide that URL including the correct key to avoid a 404
1897
+ volflag `fk` generates filekeys (per-file accesskeys) for all files; users which have full read-access (permission `r`) will then see URLs with the correct filekey `?k=...` appended to the end, and `g` users must provide that URL including the correct key to avoid a 404
1894
1898
 
1895
1899
  by default, filekeys are generated based on salt (`--fk-salt`) + filesystem-path + file-size + inode (if not windows); add volflag `fka` to generate slightly weaker filekeys which will not be invalidated if the file is edited (only salt + path)
1896
1900
 
1897
1901
  permissions `wG` (write + upget) lets users upload files and receive their own filekeys, still without being able to see other uploads
1898
1902
 
1903
+ ### dirkeys
1904
+
1905
+ share specific folders in a volume without giving away full read-access to the rest -- the visitor only needs the `g` (get) permission to view the link
1906
+
1907
+ volflag `dk` generates dirkeys (per-directory accesskeys) for all folders, granting read-access to that folder; by default only that folder itself, no subfolders
1908
+
1909
+ volflag `dky` disables the actual key-check, meaning anyone can see the contents of a folder where they have `g` access, but not its subdirectories
1910
+
1911
+ * `dk` + `dky` gives the same behavior as if all users with `g` access have full read-access, but subfolders are hidden files (their names start with a dot), so `dky` is an alternative to renaming all the folders for that purpose, maybe just for some users
1912
+
1913
+ volflag `dks` lets people enter subfolders as well, and also enables download-as-zip/tar
1914
+
1915
+ dirkeys are generated based on another salt (`--dk-salt`) + filesystem-path and have a few limitations:
1916
+ * the key does not change if the contents of the folder is modified
1917
+ * if you need a new dirkey, either change the salt or rename the folder
1918
+ * linking to a textfile (so it opens in the textfile viewer) is not possible if recipient doesn't have read-access
1919
+
1899
1920
 
1900
1921
  ## password hashing
1901
1922
 
@@ -94,6 +94,7 @@ turn almost any device into a file server with resumable uploads/downloads using
94
94
  * [gotchas](#gotchas) - behavior that might be unexpected
95
95
  * [cors](#cors) - cross-site request config
96
96
  * [filekeys](#filekeys) - prevent filename bruteforcing
97
+ * [dirkeys](#dirkeys) - share specific folders in a volume
97
98
  * [password hashing](#password-hashing) - you can hash passwords
98
99
  * [https](#https) - both HTTP and HTTPS are accepted
99
100
  * [recovering from crashes](#recovering-from-crashes)
@@ -199,7 +200,7 @@ firewall-cmd --reload
199
200
  * browser
200
201
  * ☑ [navpane](#navpane) (directory tree sidebar)
201
202
  * ☑ file manager (cut/paste, delete, [batch-rename](#batch-rename))
202
- * ☑ audio player (with [OS media controls](https://user-images.githubusercontent.com/241032/215347492-b4250797-6c90-4e09-9a4c-721edf2fb15c.png) and opus transcoding)
203
+ * ☑ audio player (with [OS media controls](https://user-images.githubusercontent.com/241032/215347492-b4250797-6c90-4e09-9a4c-721edf2fb15c.png) and opus/mp3 transcoding)
203
204
  * ☑ image gallery with webm player
204
205
  * ☑ textfile browser with syntax hilighting
205
206
  * ☑ [thumbnails](#thumbnails)
@@ -587,7 +588,7 @@ you can also zip a selection of files or folders by clicking them in the browser
587
588
 
588
589
  ![copyparty-zipsel-fs8](https://user-images.githubusercontent.com/241032/129635374-e5136e01-470a-49b1-a762-848e8a4c9cdc.png)
589
590
 
590
- cool trick: download a folder by appending url-params `?tar&opus` to transcode all audio files (except aac|m4a|mp3|ogg|opus|wma) to opus before they're added to the archive
591
+ cool trick: download a folder by appending url-params `?tar&opus` or `?tar&mp3` to transcode all audio files (except aac|m4a|mp3|ogg|opus|wma) to opus/mp3 before they're added to the archive
591
592
  * super useful if you're 5 minutes away from takeoff and realize you don't have any music on your phone but your server only has flac files and downloading those will burn through all your data + there wouldn't be enough time anyways
592
593
  * and url-params `&j` / `&w` produce jpeg/webm thumbnails/spectrograms instead of the original audio/video/images
593
594
  * can also be used to pregenerate thumbnails; combine with `--th-maxage=9999999` or `--th-clean=0`
@@ -778,9 +779,9 @@ open the `[🎺]` media-player-settings tab to configure it,
778
779
  * `[loop]` keeps looping the folder
779
780
  * `[next]` plays into the next folder
780
781
  * "transcode":
781
- * `[flac]` converts `flac` and `wav` files into opus
782
- * `[aac]` converts `aac` and `m4a` files into opus
783
- * `[oth]` converts all other known formats into opus
782
+ * `[flac]` converts `flac` and `wav` files into opus (if supported by browser) or mp3
783
+ * `[aac]` converts `aac` and `m4a` files into opus (if supported by browser) or mp3
784
+ * `[oth]` converts all other known formats into opus (if supported by browser) or mp3
784
785
  * `aac|ac3|aif|aiff|alac|alaw|amr|ape|au|dfpwm|dts|flac|gsm|it|m4a|mo3|mod|mp2|mp3|mpc|mptm|mt2|mulaw|ogg|okt|opus|ra|s3m|tak|tta|ulaw|wav|wma|wv|xm|xpk`
785
786
  * "tint" reduces the contrast of the playback bar
786
787
 
@@ -1291,6 +1292,8 @@ you may experience poor upload performance this way, but that can sometimes be f
1291
1292
 
1292
1293
  someone has also tested geesefs in combination with [gocryptfs](https://nuetzlich.net/gocryptfs/) with surprisingly good results, getting 60 MiB/s upload speeds on a gbit line, but JuiceFS won with 80 MiB/s using its built-in encryption
1293
1294
 
1295
+ you may improve performance by specifying larger values for `--iobuf` / `--s-rd-sz` / `--s-wr-sz`
1296
+
1294
1297
 
1295
1298
  ## hiding from google
1296
1299
 
@@ -1740,6 +1743,7 @@ below are some tweaks roughly ordered by usefulness:
1740
1743
  * `--hist` pointing to a fast location (ssd) will make directory listings and searches faster when `-e2d` or `-e2t` is set
1741
1744
  * and also makes thumbnails load faster, regardless of e2d/e2t
1742
1745
  * `--no-hash .` when indexing a network-disk if you don't care about the actual filehashes and only want the names/tags searchable
1746
+ * if your volumes are on a network-disk such as NFS / SMB / s3, specifying larger values for `--iobuf` and/or `--s-rd-sz` and/or `--s-wr-sz` may help; try setting all of them to `524288` or `1048576` or `4194304`
1743
1747
  * `--no-htp --hash-mt=0 --mtag-mt=1 --th-mt=1` minimizes the number of threads; can help in some eccentric environments (like the vscode debugger)
1744
1748
  * `-j0` enables multiprocessing (actual multithreading), can reduce latency to `20+80/numCores` percent and generally improve performance in cpu-intensive workloads, for example:
1745
1749
  * lots of connections (many users or heavy clients)
@@ -1836,12 +1840,29 @@ cors can be configured with `--acao` and `--acam`, or the protections entirely d
1836
1840
 
1837
1841
  prevent filename bruteforcing
1838
1842
 
1839
- volflag `c,fk` generates filekeys (per-file accesskeys) for all files; users which have full read-access (permission `r`) will then see URLs with the correct filekey `?k=...` appended to the end, and `g` users must provide that URL including the correct key to avoid a 404
1843
+ volflag `fk` generates filekeys (per-file accesskeys) for all files; users which have full read-access (permission `r`) will then see URLs with the correct filekey `?k=...` appended to the end, and `g` users must provide that URL including the correct key to avoid a 404
1840
1844
 
1841
1845
  by default, filekeys are generated based on salt (`--fk-salt`) + filesystem-path + file-size + inode (if not windows); add volflag `fka` to generate slightly weaker filekeys which will not be invalidated if the file is edited (only salt + path)
1842
1846
 
1843
1847
  permissions `wG` (write + upget) lets users upload files and receive their own filekeys, still without being able to see other uploads
1844
1848
 
1849
+ ### dirkeys
1850
+
1851
+ share specific folders in a volume without giving away full read-access to the rest -- the visitor only needs the `g` (get) permission to view the link
1852
+
1853
+ volflag `dk` generates dirkeys (per-directory accesskeys) for all folders, granting read-access to that folder; by default only that folder itself, no subfolders
1854
+
1855
+ volflag `dky` disables the actual key-check, meaning anyone can see the contents of a folder where they have `g` access, but not its subdirectories
1856
+
1857
+ * `dk` + `dky` gives the same behavior as if all users with `g` access have full read-access, but subfolders are hidden files (their names start with a dot), so `dky` is an alternative to renaming all the folders for that purpose, maybe just for some users
1858
+
1859
+ volflag `dks` lets people enter subfolders as well, and also enables download-as-zip/tar
1860
+
1861
+ dirkeys are generated based on another salt (`--dk-salt`) + filesystem-path and have a few limitations:
1862
+ * the key does not change if the contents of the folder is modified
1863
+ * if you need a new dirkey, either change the salt or rename the folder
1864
+ * linking to a textfile (so it opens in the textfile viewer) is not possible if recipient doesn't have read-access
1865
+
1845
1866
 
1846
1867
  ## password hashing
1847
1868
 
@@ -53,7 +53,6 @@ class EnvParams(object):
53
53
  self.t0 = time.time()
54
54
  self.mod = ""
55
55
  self.cfg = ""
56
- self.ox = getattr(sys, "oxidized", None)
57
56
 
58
57
 
59
58
  E = EnvParams()
@@ -151,7 +151,8 @@ def warn(msg ) :
151
151
 
152
152
 
153
153
  def init_E(EE ) :
154
- # __init__ runs 18 times when oxidized; do expensive stuff here
154
+ # some cpython alternatives (such as pyoxidizer) can
155
+ # __init__ several times, so do expensive stuff here
155
156
 
156
157
  E = EE # pylint: disable=redefined-outer-name
157
158
 
@@ -184,34 +185,9 @@ def init_E(EE ) :
184
185
 
185
186
  raise Exception("could not find a writable path for config")
186
187
 
187
- def _unpack() :
188
- import atexit
189
- import tarfile
190
- import tempfile
191
- from importlib.resources import open_binary
192
-
193
- td = tempfile.TemporaryDirectory(prefix="")
194
- atexit.register(td.cleanup)
195
- tdn = td.name
196
-
197
- with open_binary("copyparty", "z.tar") as tgz:
198
- with tarfile.open(fileobj=tgz) as tf:
199
- try:
200
- tf.extractall(tdn, filter="tar")
201
- except TypeError:
202
- tf.extractall(tdn) # nosec (archive is safe)
203
-
204
- return tdn
205
-
206
- try:
207
- E.mod = os.path.dirname(os.path.realpath(__file__))
208
- if E.mod.endswith("__init__"):
209
- E.mod = os.path.dirname(E.mod)
210
- except:
211
- if not E.ox:
212
- raise
213
-
214
- E.mod = _unpack()
188
+ E.mod = os.path.dirname(os.path.realpath(__file__))
189
+ if E.mod.endswith("__init__"):
190
+ E.mod = os.path.dirname(E.mod)
215
191
 
216
192
  if sys.platform == "win32":
217
193
  bdir = os.environ.get("APPDATA") or os.environ.get("TEMP") or "."
@@ -268,6 +244,19 @@ def get_fk_salt() :
268
244
  return ret.decode("utf-8")
269
245
 
270
246
 
247
+ def get_dk_salt() :
248
+ fp = os.path.join(E.cfg, "dk-salt.txt")
249
+ try:
250
+ with open(fp, "rb") as f:
251
+ ret = f.read().strip()
252
+ except:
253
+ ret = base64.b64encode(os.urandom(30))
254
+ with open(fp, "wb") as f:
255
+ f.write(ret + b"\n")
256
+
257
+ return ret.decode("utf-8")
258
+
259
+
271
260
  def get_ah_salt() :
272
261
  fp = os.path.join(E.cfg, "ah-salt.txt")
273
262
  try:
@@ -863,6 +852,7 @@ def add_fs(ap):
863
852
  ap2 = ap.add_argument_group("filesystem options")
864
853
  rm_re_def = "5/0.1" if ANYWIN else "0/0"
865
854
  ap2.add_argument("--rm-retry", metavar="T/R", type=u, default=rm_re_def, help="if a file cannot be deleted because it is busy, continue trying for \033[33mT\033[0m seconds, retry every \033[33mR\033[0m seconds; disable with 0/0 (volflag=rm_retry)")
855
+ ap2.add_argument("--iobuf", metavar="BYTES", type=int, default=256*1024, help="file I/O buffer-size; if your volumes are on a network drive, try increasing to \033[32m524288\033[0m or even \033[32m4194304\033[0m (and let me know if that improves your performance)")
866
856
 
867
857
 
868
858
  def add_upload(ap):
@@ -910,6 +900,7 @@ def add_network(ap):
910
900
  ap2.add_argument("--freebind", action="store_true", help="allow listening on IPs which do not yet exist, for example if the network interfaces haven't finished going up. Only makes sense for IPs other than '0.0.0.0', '127.0.0.1', '::', and '::1'. May require running as root (unless net.ipv6.ip_nonlocal_bind)")
911
901
  ap2.add_argument("--s-thead", metavar="SEC", type=int, default=120, help="socket timeout (read request header)")
912
902
  ap2.add_argument("--s-tbody", metavar="SEC", type=float, default=186, help="socket timeout (read/write request/response bodies). Use 60 on fast servers (default is extremely safe). Disable with 0 if reverse-proxied for a 2%% speed boost")
903
+ ap2.add_argument("--s-rd-sz", metavar="B", type=int, default=256*1024, help="socket read size in bytes (indirectly affects filesystem writes; recommendation: keep equal-to or lower-than \033[33m--iobuf\033[0m)")
913
904
  ap2.add_argument("--s-wr-sz", metavar="B", type=int, default=256*1024, help="socket write size in bytes")
914
905
  ap2.add_argument("--s-wr-slp", metavar="SEC", type=float, default=0, help="debug: socket write delay in seconds")
915
906
  ap2.add_argument("--rsp-slp", metavar="SEC", type=float, default=0, help="debug: response delay in seconds")
@@ -1123,13 +1114,14 @@ def add_safety(ap):
1123
1114
  ap2.add_argument("--acam", metavar="V[,V]", type=u, default="GET,HEAD", help="Access-Control-Allow-Methods; list of methods to accept from offsite ('*' behaves like \033[33m--acao\033[0m's description)")
1124
1115
 
1125
1116
 
1126
- def add_salt(ap, fk_salt, ah_salt):
1117
+ def add_salt(ap, fk_salt, dk_salt, ah_salt):
1127
1118
  ap2 = ap.add_argument_group('salting options')
1128
1119
  ap2.add_argument("--ah-alg", metavar="ALG", type=u, default="none", help="account-pw hashing algorithm; one of these, best to worst: \033[32margon2 scrypt sha2 none\033[0m (each optionally followed by alg-specific comma-sep. config)")
1129
1120
  ap2.add_argument("--ah-salt", metavar="SALT", type=u, default=ah_salt, help="account-pw salt; ignored if \033[33m--ah-alg\033[0m is none (default)")
1130
1121
  ap2.add_argument("--ah-gen", metavar="PW", type=u, default="", help="generate hashed password for \033[33mPW\033[0m, or read passwords from STDIN if \033[33mPW\033[0m is [\033[32m-\033[0m]")
1131
1122
  ap2.add_argument("--ah-cli", action="store_true", help="launch an interactive shell which hashes passwords without ever storing or displaying the original passwords")
1132
1123
  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")
1124
+ ap2.add_argument("--dk-salt", metavar="SALT", type=u, default=dk_salt, help="per-directory accesskey salt; used to generate unpredictable URLs to share folders with users who only have the 'get' permission")
1133
1125
  ap2.add_argument("--warksalt", metavar="SALT", type=u, default="hunter2", help="up2k file-hash salt; serves no purpose, no reason to change this (but delete all databases if you do)")
1134
1126
 
1135
1127
 
@@ -1195,6 +1187,8 @@ def add_thumbnail(ap):
1195
1187
 
1196
1188
  def add_transcoding(ap):
1197
1189
  ap2 = ap.add_argument_group('transcoding options')
1190
+ ap2.add_argument("--q-opus", metavar="KBPS", type=int, default=128, help="target bitrate for transcoding to opus; set 0 to disable")
1191
+ ap2.add_argument("--q-mp3", metavar="QUALITY", type=u, default="q2", help="target quality for transcoding to mp3, for example [\033[32m192k\033[0m] (CBR) or [\033[32mq0\033[0m] (CQ/CRF, q0=maxquality, q9=smallest); set 0 to disable")
1198
1192
  ap2.add_argument("--no-acode", action="store_true", help="disable audio transcoding")
1199
1193
  ap2.add_argument("--no-bacode", action="store_true", help="disable batch audio transcoding by folder download (zip/tar)")
1200
1194
  ap2.add_argument("--ac-maxage", metavar="SEC", type=int, default=86400, help="delete cached transcode output after \033[33mSEC\033[0m seconds")
@@ -1311,6 +1305,7 @@ def run_argparse(
1311
1305
  cert_path = os.path.join(E.cfg, "cert.pem")
1312
1306
 
1313
1307
  fk_salt = get_fk_salt()
1308
+ dk_salt = get_dk_salt()
1314
1309
  ah_salt = get_ah_salt()
1315
1310
 
1316
1311
  # alpine peaks at 5 threads for some reason,
@@ -1342,7 +1337,7 @@ def run_argparse(
1342
1337
  add_tftp(ap)
1343
1338
  add_smb(ap)
1344
1339
  add_safety(ap)
1345
- add_salt(ap, fk_salt, ah_salt)
1340
+ add_salt(ap, fk_salt, dk_salt, ah_salt)
1346
1341
  add_optouts(ap)
1347
1342
  add_shutdown(ap)
1348
1343
  add_yolo(ap)
@@ -1,8 +1,8 @@
1
1
  # coding: utf-8
2
2
 
3
- VERSION = (1, 11, 1)
4
- CODENAME = "You Can (Not) Proceed"
5
- BUILD_DT = (2024, 3, 18)
3
+ VERSION = (1, 12, 0)
4
+ CODENAME = "locksmith"
5
+ BUILD_DT = (2024, 4, 6)
6
6
 
7
7
  S_VERSION = ".".join(map(str, VERSION))
8
8
  S_BUILD_DT = "{0:04d}-{1:02d}-{2:02d}".format(*BUILD_DT)
@@ -548,7 +548,12 @@ class VFS(object):
548
548
  # no vfs nodes in the list of real inodes
549
549
  real = [x for x in real if x[0] not in self.nodes]
550
550
 
551
+ dbv = self.dbv or self
551
552
  for name, vn2 in sorted(self.nodes.items()):
553
+ if vn2.dbv == dbv and self.flags.get("dk"):
554
+ virt_vis[name] = vn2
555
+ continue
556
+
552
557
  ok = False
553
558
  zx = vn2.axs
554
559
  axs = [zx.uread, zx.uwrite, zx.umove, zx.udel, zx.uget]
@@ -1217,7 +1222,9 @@ class AuthSrv(object):
1217
1222
  if un.startswith("@"):
1218
1223
  grp = un[1:]
1219
1224
  uns = [x[0] for x in un_gns.items() if grp in x[1]]
1220
- if not uns and grp != "${g}" and not self.args.idp_h_grp:
1225
+ if grp == "${g}":
1226
+ unames.append(un)
1227
+ elif not uns and not self.args.idp_h_grp:
1221
1228
  t = "group [%s] must be defined with --grp argument (or in a [groups] config section)"
1222
1229
  raise CfgEx(t % (grp,))
1223
1230
 
@@ -1227,31 +1234,28 @@ class AuthSrv(object):
1227
1234
 
1228
1235
  # unames may still contain ${u} and ${g} so now expand those;
1229
1236
  un_gn = [(un, gn) for un, gns in un_gns.items() for gn in gns]
1230
- if "*" not in un_gns:
1231
- # need ("*","") to match "*" in unames
1232
- un_gn.append(("*", ""))
1233
-
1234
- for _, dst, vu, vg in vols:
1235
- unames2 = set()
1236
- for un, gn in un_gn:
1237
- # if vu/vg (volume user/group) is non-null,
1238
- # then each non-null value corresponds to
1239
- # ${u}/${g}; consider this a filter to
1240
- # apply to unames, as well as un_gn
1241
- if (vu and vu != un) or (vg and vg != gn):
1242
- continue
1243
1237
 
1244
- for uname in unames + ([un] if vu or vg else []):
1245
- if uname == "${u}":
1246
- uname = vu or un
1247
- elif uname in ("${g}", "@${g}"):
1248
- uname = vg or gn
1238
+ for src, dst, vu, vg in vols:
1239
+ unames2 = set(unames)
1249
1240
 
1250
- if vu and vu != uname:
1251
- continue
1241
+ if "${u}" in unames:
1242
+ if not vu:
1243
+ t = "cannot use ${u} in accs of volume [%s] because the volume url does not contain ${u}"
1244
+ raise CfgEx(t % (src,))
1245
+ unames2.add(vu)
1246
+
1247
+ if "@${g}" in unames:
1248
+ if not vg:
1249
+ t = "cannot use @${g} in accs of volume [%s] because the volume url does not contain @${g}"
1250
+ raise CfgEx(t % (src,))
1251
+ unames2.update([un for un, gn in un_gn if gn == vg])
1252
1252
 
1253
- if uname:
1254
- unames2.add(uname)
1253
+ if "${g}" in unames:
1254
+ t = 'the accs of volume [%s] contains "${g}" but the only supported way of specifying that is "@${g}"'
1255
+ raise CfgEx(t % (src,))
1256
+
1257
+ unames2.discard("${u}")
1258
+ unames2.discard("@${g}")
1255
1259
 
1256
1260
  self._read_vol_str(lvl, list(unames2), axs[dst])
1257
1261
 
@@ -1675,6 +1679,20 @@ class AuthSrv(object):
1675
1679
  vol.flags["fk"] = int(fk) if fk is not True else 8
1676
1680
  have_fk = True
1677
1681
 
1682
+ dk = vol.flags.get("dk")
1683
+ dks = vol.flags.get("dks")
1684
+ dky = vol.flags.get("dky")
1685
+ if dks is not None and dky is not None:
1686
+ t = "WARNING: volume /%s has both dks and dky enabled; this is too yolo and not permitted"
1687
+ raise Exception(t % (vol.vpath,))
1688
+
1689
+ if dks and not dk:
1690
+ dk = dks
1691
+ if dky and not dk:
1692
+ dk = dky
1693
+ if dk:
1694
+ vol.flags["dk"] = int(dk) if dk is not True else 8
1695
+
1678
1696
  if have_fk and re.match(r"^[0-9\.]+$", self.args.fk_salt):
1679
1697
  self.log("filekey salt: {}".format(self.args.fk_salt))
1680
1698
 
@@ -213,7 +213,7 @@ class FtpFs(AbstractedFS):
213
213
  raise FSE("Cannot open existing file for writing")
214
214
 
215
215
  self.validpath(ap)
216
- return open(fsenc(ap), mode)
216
+ return open(fsenc(ap), mode, self.args.iobuf)
217
217
 
218
218
  def chdir(self, path ) :
219
219
  nwd = join(self.cwd, path)