copyparty 1.6.9__tar.gz → 1.6.11__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 (121) hide show
  1. {copyparty-1.6.9/copyparty.egg-info → copyparty-1.6.11}/PKG-INFO +61 -30
  2. {copyparty-1.6.9 → copyparty-1.6.11}/README.md +60 -29
  3. {copyparty-1.6.9 → copyparty-1.6.11}/bin/up2k.py +5 -7
  4. {copyparty-1.6.9 → copyparty-1.6.11}/copyparty/__main__.py +1 -1
  5. {copyparty-1.6.9 → copyparty-1.6.11}/copyparty/__version__.py +2 -2
  6. {copyparty-1.6.9 → copyparty-1.6.11}/copyparty/httpcli.py +48 -17
  7. {copyparty-1.6.9 → copyparty-1.6.11}/copyparty/th_srv.py +33 -1
  8. {copyparty-1.6.9 → copyparty-1.6.11}/copyparty/up2k.py +13 -6
  9. {copyparty-1.6.9 → copyparty-1.6.11}/copyparty/web/a/up2k.py +5 -7
  10. copyparty-1.6.11/copyparty/web/baguettebox.js.gz +0 -0
  11. copyparty-1.6.11/copyparty/web/browser.css.gz +0 -0
  12. copyparty-1.6.11/copyparty/web/browser.js.gz +0 -0
  13. {copyparty-1.6.9 → copyparty-1.6.11}/copyparty/web/deps/easymde.css.gz +0 -0
  14. {copyparty-1.6.9 → copyparty-1.6.11}/copyparty/web/deps/easymde.js.gz +0 -0
  15. copyparty-1.6.11/copyparty/web/deps/marked.js.gz +0 -0
  16. copyparty-1.6.11/copyparty/web/deps/mini-fa.css.gz +0 -0
  17. {copyparty-1.6.9 → copyparty-1.6.11}/copyparty/web/deps/sha512.ac.js.gz +0 -0
  18. copyparty-1.6.11/copyparty/web/md.js.gz +0 -0
  19. {copyparty-1.6.9 → copyparty-1.6.11}/copyparty/web/svcs.html +3 -4
  20. copyparty-1.6.11/copyparty/web/ui.css.gz +0 -0
  21. copyparty-1.6.11/copyparty/web/util.js.gz +0 -0
  22. {copyparty-1.6.9 → copyparty-1.6.11/copyparty.egg-info}/PKG-INFO +61 -30
  23. copyparty-1.6.9/copyparty/web/baguettebox.js.gz +0 -0
  24. copyparty-1.6.9/copyparty/web/browser.css.gz +0 -0
  25. copyparty-1.6.9/copyparty/web/browser.js.gz +0 -0
  26. copyparty-1.6.9/copyparty/web/deps/marked.js.gz +0 -0
  27. copyparty-1.6.9/copyparty/web/deps/mini-fa.css.gz +0 -0
  28. copyparty-1.6.9/copyparty/web/md.js.gz +0 -0
  29. copyparty-1.6.9/copyparty/web/ui.css.gz +0 -0
  30. copyparty-1.6.9/copyparty/web/util.js.gz +0 -0
  31. {copyparty-1.6.9 → copyparty-1.6.11}/LICENSE +0 -0
  32. {copyparty-1.6.9 → copyparty-1.6.11}/MANIFEST.in +0 -0
  33. {copyparty-1.6.9 → copyparty-1.6.11}/bin/partyfuse.py +0 -0
  34. {copyparty-1.6.9 → copyparty-1.6.11}/copyparty/__init__.py +0 -0
  35. {copyparty-1.6.9 → copyparty-1.6.11}/copyparty/authsrv.py +0 -0
  36. {copyparty-1.6.9 → copyparty-1.6.11}/copyparty/bos/__init__.py +0 -0
  37. {copyparty-1.6.9 → copyparty-1.6.11}/copyparty/bos/bos.py +0 -0
  38. {copyparty-1.6.9 → copyparty-1.6.11}/copyparty/bos/path.py +0 -0
  39. {copyparty-1.6.9 → copyparty-1.6.11}/copyparty/broker_mp.py +0 -0
  40. {copyparty-1.6.9 → copyparty-1.6.11}/copyparty/broker_mpw.py +0 -0
  41. {copyparty-1.6.9 → copyparty-1.6.11}/copyparty/broker_thr.py +0 -0
  42. {copyparty-1.6.9 → copyparty-1.6.11}/copyparty/broker_util.py +0 -0
  43. {copyparty-1.6.9 → copyparty-1.6.11}/copyparty/cfg.py +0 -0
  44. {copyparty-1.6.9 → copyparty-1.6.11}/copyparty/dxml.py +0 -0
  45. {copyparty-1.6.9 → copyparty-1.6.11}/copyparty/fsutil.py +0 -0
  46. {copyparty-1.6.9 → copyparty-1.6.11}/copyparty/ftpd.py +0 -0
  47. {copyparty-1.6.9 → copyparty-1.6.11}/copyparty/httpconn.py +0 -0
  48. {copyparty-1.6.9 → copyparty-1.6.11}/copyparty/httpsrv.py +0 -0
  49. {copyparty-1.6.9 → copyparty-1.6.11}/copyparty/ico.py +0 -0
  50. {copyparty-1.6.9 → copyparty-1.6.11}/copyparty/mdns.py +0 -0
  51. {copyparty-1.6.9 → copyparty-1.6.11}/copyparty/mtag.py +0 -0
  52. {copyparty-1.6.9 → copyparty-1.6.11}/copyparty/multicast.py +0 -0
  53. {copyparty-1.6.9 → copyparty-1.6.11}/copyparty/res/COPYING.txt +0 -0
  54. {copyparty-1.6.9 → copyparty-1.6.11}/copyparty/res/insecure.pem +0 -0
  55. {copyparty-1.6.9 → copyparty-1.6.11}/copyparty/smbd.py +0 -0
  56. {copyparty-1.6.9 → copyparty-1.6.11}/copyparty/ssdp.py +0 -0
  57. {copyparty-1.6.9 → copyparty-1.6.11}/copyparty/star.py +0 -0
  58. {copyparty-1.6.9 → copyparty-1.6.11}/copyparty/stolen/__init__.py +0 -0
  59. {copyparty-1.6.9 → copyparty-1.6.11}/copyparty/stolen/dnslib/__init__.py +0 -0
  60. {copyparty-1.6.9 → copyparty-1.6.11}/copyparty/stolen/dnslib/bimap.py +0 -0
  61. {copyparty-1.6.9 → copyparty-1.6.11}/copyparty/stolen/dnslib/bit.py +0 -0
  62. {copyparty-1.6.9 → copyparty-1.6.11}/copyparty/stolen/dnslib/buffer.py +0 -0
  63. {copyparty-1.6.9 → copyparty-1.6.11}/copyparty/stolen/dnslib/dns.py +0 -0
  64. {copyparty-1.6.9 → copyparty-1.6.11}/copyparty/stolen/dnslib/label.py +0 -0
  65. {copyparty-1.6.9 → copyparty-1.6.11}/copyparty/stolen/dnslib/lex.py +0 -0
  66. {copyparty-1.6.9 → copyparty-1.6.11}/copyparty/stolen/dnslib/ranges.py +0 -0
  67. {copyparty-1.6.9 → copyparty-1.6.11}/copyparty/stolen/ifaddr/__init__.py +0 -0
  68. {copyparty-1.6.9 → copyparty-1.6.11}/copyparty/stolen/ifaddr/_posix.py +0 -0
  69. {copyparty-1.6.9 → copyparty-1.6.11}/copyparty/stolen/ifaddr/_shared.py +0 -0
  70. {copyparty-1.6.9 → copyparty-1.6.11}/copyparty/stolen/ifaddr/_win32.py +0 -0
  71. {copyparty-1.6.9 → copyparty-1.6.11}/copyparty/stolen/qrcodegen.py +0 -0
  72. {copyparty-1.6.9 → copyparty-1.6.11}/copyparty/stolen/surrogateescape.py +0 -0
  73. {copyparty-1.6.9 → copyparty-1.6.11}/copyparty/sutil.py +0 -0
  74. {copyparty-1.6.9 → copyparty-1.6.11}/copyparty/svchub.py +0 -0
  75. {copyparty-1.6.9 → copyparty-1.6.11}/copyparty/szip.py +0 -0
  76. {copyparty-1.6.9 → copyparty-1.6.11}/copyparty/tcpsrv.py +0 -0
  77. {copyparty-1.6.9 → copyparty-1.6.11}/copyparty/th_cli.py +0 -0
  78. {copyparty-1.6.9 → copyparty-1.6.11}/copyparty/u2idx.py +0 -0
  79. {copyparty-1.6.9 → copyparty-1.6.11}/copyparty/util.py +0 -0
  80. {copyparty-1.6.9 → copyparty-1.6.11}/copyparty/web/a/__init__.py +0 -0
  81. {copyparty-1.6.9 → copyparty-1.6.11}/copyparty/web/a/partyfuse.py +0 -0
  82. {copyparty-1.6.9 → copyparty-1.6.11}/copyparty/web/a/webdav-cfg.bat +0 -0
  83. {copyparty-1.6.9 → copyparty-1.6.11}/copyparty/web/browser.html +0 -0
  84. {copyparty-1.6.9 → copyparty-1.6.11}/copyparty/web/browser2.html +0 -0
  85. {copyparty-1.6.9 → copyparty-1.6.11}/copyparty/web/cf.html +0 -0
  86. {copyparty-1.6.9 → copyparty-1.6.11}/copyparty/web/copyparty.gif +0 -0
  87. {copyparty-1.6.9 → copyparty-1.6.11}/copyparty/web/dbg-audio.js.gz +0 -0
  88. {copyparty-1.6.9 → copyparty-1.6.11}/copyparty/web/dd/2.png +0 -0
  89. {copyparty-1.6.9 → copyparty-1.6.11}/copyparty/web/dd/3.png +0 -0
  90. {copyparty-1.6.9 → copyparty-1.6.11}/copyparty/web/dd/4.png +0 -0
  91. {copyparty-1.6.9 → copyparty-1.6.11}/copyparty/web/dd/5.png +0 -0
  92. {copyparty-1.6.9 → copyparty-1.6.11}/copyparty/web/dd/__init__.py +0 -0
  93. {copyparty-1.6.9 → copyparty-1.6.11}/copyparty/web/deps/__init__.py +0 -0
  94. {copyparty-1.6.9 → copyparty-1.6.11}/copyparty/web/deps/mini-fa.woff +0 -0
  95. {copyparty-1.6.9 → copyparty-1.6.11}/copyparty/web/deps/prism.css.gz +0 -0
  96. {copyparty-1.6.9 → copyparty-1.6.11}/copyparty/web/deps/prism.js.gz +0 -0
  97. {copyparty-1.6.9 → copyparty-1.6.11}/copyparty/web/deps/prismd.css.gz +0 -0
  98. {copyparty-1.6.9 → copyparty-1.6.11}/copyparty/web/deps/scp.woff2 +0 -0
  99. {copyparty-1.6.9 → copyparty-1.6.11}/copyparty/web/deps/sha512.hw.js.gz +0 -0
  100. {copyparty-1.6.9 → copyparty-1.6.11}/copyparty/web/md.css.gz +0 -0
  101. {copyparty-1.6.9 → copyparty-1.6.11}/copyparty/web/md.html +0 -0
  102. {copyparty-1.6.9 → copyparty-1.6.11}/copyparty/web/md2.css.gz +0 -0
  103. {copyparty-1.6.9 → copyparty-1.6.11}/copyparty/web/md2.js.gz +0 -0
  104. {copyparty-1.6.9 → copyparty-1.6.11}/copyparty/web/mde.css.gz +0 -0
  105. {copyparty-1.6.9 → copyparty-1.6.11}/copyparty/web/mde.html +0 -0
  106. {copyparty-1.6.9 → copyparty-1.6.11}/copyparty/web/mde.js.gz +0 -0
  107. {copyparty-1.6.9 → copyparty-1.6.11}/copyparty/web/msg.css.gz +0 -0
  108. {copyparty-1.6.9 → copyparty-1.6.11}/copyparty/web/msg.html +0 -0
  109. {copyparty-1.6.9 → copyparty-1.6.11}/copyparty/web/splash.css.gz +0 -0
  110. {copyparty-1.6.9 → copyparty-1.6.11}/copyparty/web/splash.html +0 -0
  111. {copyparty-1.6.9 → copyparty-1.6.11}/copyparty/web/splash.js.gz +0 -0
  112. {copyparty-1.6.9 → copyparty-1.6.11}/copyparty/web/svcs.js.gz +0 -0
  113. {copyparty-1.6.9 → copyparty-1.6.11}/copyparty/web/up2k.js.gz +0 -0
  114. {copyparty-1.6.9 → copyparty-1.6.11}/copyparty/web/w.hash.js.gz +0 -0
  115. {copyparty-1.6.9 → copyparty-1.6.11}/copyparty.egg-info/SOURCES.txt +0 -0
  116. {copyparty-1.6.9 → copyparty-1.6.11}/copyparty.egg-info/dependency_links.txt +0 -0
  117. {copyparty-1.6.9 → copyparty-1.6.11}/copyparty.egg-info/entry_points.txt +0 -0
  118. {copyparty-1.6.9 → copyparty-1.6.11}/copyparty.egg-info/requires.txt +0 -0
  119. {copyparty-1.6.9 → copyparty-1.6.11}/copyparty.egg-info/top_level.txt +0 -0
  120. {copyparty-1.6.9 → copyparty-1.6.11}/setup.cfg +0 -0
  121. {copyparty-1.6.9 → copyparty-1.6.11}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: copyparty
3
- Version: 1.6.9
3
+ Version: 1.6.11
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
  Home-page: https://github.com/9001/copyparty
6
6
  Author: ed
@@ -40,30 +40,17 @@ License-File: LICENSE
40
40
 
41
41
  # 💾🎉 copyparty
42
42
 
43
- * portable file sharing hub (py2/py3) [(on PyPI)](https://pypi.org/project/copyparty/)
44
- * MIT-Licensed, 2019-05-26, ed @ irc.rizon.net
43
+ turn almost any device into a file server with resumable uploads/downloads using [*any*](#browser-support) web browser
45
44
 
45
+ * server only needs Python (2 or 3), all dependencies optional
46
+ * 🔌 protocols: [http](#the-browser) // [ftp](#ftp-server) // [webdav](#webdav-server) // [smb/cifs](#smb-server)
47
+ * 📱 [android app](#android-app) // [iPhone shortcuts](#ios-shortcuts)
46
48
 
47
- ## summary
48
-
49
- turn your phone or raspi into a portable file server with resumable uploads/downloads using *any* web browser
50
-
51
- * server only needs Python (`2.7` or `3.3+`), all dependencies optional
52
- * browse/upload with [IE4](#browser-support) / netscape4.0 on win3.11 (heh)
53
- * protocols: [http](#the-browser) // [ftp](#ftp-server) // [webdav](#webdav-server) // [smb/cifs](#smb-server)
54
-
55
- **[Get started](#quickstart)!** or visit the **[read-only demo server](https://a.ocv.me/pub/demo/)** 👀 running from a basement in finland
49
+ 👉 **[Get started](#quickstart)!** or visit the **[read-only demo server](https://a.ocv.me/pub/demo/)** 👀 running from a basement in finland
56
50
 
57
51
  📷 **screenshots:** [browser](#the-browser) // [upload](#uploading) // [unpost](#unpost) // [thumbnails](#thumbnails) // [search](#searching) // [fsearch](#file-search) // [zip-DL](#zip-downloads) // [md-viewer](#markdown-viewer)
58
52
 
59
53
 
60
- ## get the app
61
-
62
- <a href="https://f-droid.org/packages/me.ocv.partyup/"><img src="https://ocv.me/fdroid.png" alt="Get it on F-Droid" height="50" /> '' <img src="https://img.shields.io/f-droid/v/me.ocv.partyup.svg" alt="f-droid version info" /></a> '' <a href="https://github.com/9001/party-up"><img src="https://img.shields.io/github/release/9001/party-up.svg?logo=github" alt="github version info" /></a>
63
-
64
- (the app is **NOT** the full copyparty server! just a basic upload client, nothing fancy yet)
65
-
66
-
67
54
  ## readme toc
68
55
 
69
56
  * top
@@ -122,12 +109,16 @@ turn your phone or raspi into a portable file server with resumable uploads/down
122
109
  * [reverse-proxy](#reverse-proxy) - running copyparty next to other websites
123
110
  * [browser support](#browser-support) - TLDR: yes
124
111
  * [client examples](#client-examples) - interact with copyparty using non-browser clients
112
+ * [folder sync](#folder-sync) - sync folders to/from copyparty
125
113
  * [mount as drive](#mount-as-drive) - a remote copyparty server as a local filesystem
114
+ * [android app](#android-app) - upload to copyparty with one tap
115
+ * [iOS shortcuts](#iOS-shortcuts) - there is no iPhone app, but
126
116
  * [performance](#performance) - defaults are usually fine - expect `8 GiB/s` download, `1 GiB/s` upload
127
117
  * [client-side](#client-side) - when uploading files
128
118
  * [security](#security) - some notes on hardening
129
119
  * [gotchas](#gotchas) - behavior that might be unexpected
130
120
  * [cors](#cors) - cross-site request config
121
+ * [https](#https) - both HTTP and HTTPS are accepted
131
122
  * [recovering from crashes](#recovering-from-crashes)
132
123
  * [client crashes](#client-crashes)
133
124
  * [frefox wsod](#frefox-wsod) - firefox 87 can crash during uploads
@@ -179,7 +170,7 @@ some recommended options:
179
170
 
180
171
  you may also want these, especially on servers:
181
172
 
182
- * [contrib/systemd/copyparty.service](contrib/systemd/copyparty.service) to run copyparty as a systemd service
173
+ * [contrib/systemd/copyparty.service](contrib/systemd/copyparty.service) to run copyparty as a systemd service (see guide inside)
183
174
  * [contrib/systemd/prisonparty.service](contrib/systemd/prisonparty.service) to run it in a chroot (for extra security)
184
175
  * [contrib/rc/copyparty](contrib/rc/copyparty) to run copyparty on FreeBSD
185
176
  * [contrib/nginx/copyparty.conf](contrib/nginx/copyparty.conf) to [reverse-proxy](#reverse-proxy) behind nginx (for better https)
@@ -217,7 +208,7 @@ firewall-cmd --reload
217
208
  * ☑ write-only folders
218
209
  * ☑ [unpost](#unpost): undo/delete accidental uploads
219
210
  * ☑ [self-destruct](#self-destruct) (specified server-side or client-side)
220
- * ☑ symlink/discard existing files (content-matching)
211
+ * ☑ symlink/discard duplicates (content-matching)
221
212
  * download
222
213
  * ☑ single files in browser
223
214
  * ☑ [folders as zip / tar files](#zip-downloads)
@@ -239,7 +230,7 @@ firewall-cmd --reload
239
230
  * ☑ search by name/path/date/size
240
231
  * ☑ [search by ID3-tags etc.](#searching)
241
232
  * client support
242
- * ☑ [sync folder to server](https://github.com/9001/copyparty/tree/hovudstraum/bin#up2kpy)
233
+ * ☑ [folder sync](#folder-sync)
243
234
  * ☑ [curl-friendly](https://user-images.githubusercontent.com/241032/215322619-ea5fd606-3654-40ad-94ee-2bc058647bb2.png)
244
235
  * markdown
245
236
  * ☑ [viewer](#markdown-viewer)
@@ -318,7 +309,7 @@ server notes:
318
309
 
319
310
  * 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)
320
311
  * *future workaround:* enable the equalizer, make it all-zero, and set a negative boost to reduce the volume
321
- * "future" because `AudioContext` is broken in the current iOS version (15.1), maybe one day...
312
+ * "future" because `AudioContext` can't maintain a stable playback speed in the current iOS version (15.7), maybe one day...
322
313
 
323
314
  * Windows: folders cannot be accessed if the name ends with `.`
324
315
  * python or windows bug
@@ -1142,13 +1133,15 @@ see the top of [./copyparty/web/browser.css](./copyparty/web/browser.css) where
1142
1133
 
1143
1134
  ## reverse-proxy
1144
1135
 
1145
- running copyparty next to other websites hosted on an existing webserver such as nginx or apache
1136
+ running copyparty next to other websites hosted on an existing webserver such as nginx, caddy, or apache
1146
1137
 
1147
1138
  you can either:
1148
1139
  * give copyparty its own domain or subdomain (recommended)
1149
1140
  * or do location-based proxying, using `--rp-loc=/stuff` to tell copyparty where it is mounted -- has a slight performance cost and higher chance of bugs
1150
1141
  * if copyparty says `incorrect --rp-loc or webserver config; expected vpath starting with [...]` it's likely because the webserver is stripping away the proxy location from the request URLs -- see the `ProxyPass` in the apache example below
1151
1142
 
1143
+ some reverse proxies (such as [Caddy](https://caddyserver.com/)) can automatically obtain a valid https/tls certificate for you, and some support HTTP/2 and QUIC which could be a nice speed boost
1144
+
1152
1145
  example webserver configs:
1153
1146
 
1154
1147
  * [nginx config](contrib/nginx/copyparty.conf) -- entire domain/subdomain
@@ -1226,7 +1219,7 @@ interact with copyparty using non-browser clients
1226
1219
  * `(printf 'PUT / HTTP/1.1\r\n\r\n'; cat movie.mkv) >/dev/tcp/127.0.0.1/3923`
1227
1220
 
1228
1221
  * python: [up2k.py](https://github.com/9001/copyparty/blob/hovudstraum/bin/up2k.py) is a command-line up2k client [(webm)](https://ocv.me/stuff/u2cli.webm)
1229
- * file uploads, file-search, folder sync, autoresume of aborted/broken uploads
1222
+ * file uploads, file-search, [folder sync](#folder-sync), autoresume of aborted/broken uploads
1230
1223
  * can be downloaded from copyparty: controlpanel -> connect -> [up2k.py](http://127.0.0.1:3923/.cpr/a/up2k.py)
1231
1224
  * see [./bin/README.md#up2kpy](bin/README.md#up2kpy)
1232
1225
 
@@ -1247,17 +1240,27 @@ you can provide passwords using header `PW: hunter2`, cookie `cppwd=hunter2`, ur
1247
1240
  NOTE: curl will not send the original filename if you use `-T` combined with url-params! Also, make sure to always leave a trailing slash in URLs unless you want to override the filename
1248
1241
 
1249
1242
 
1243
+ ## folder sync
1244
+
1245
+ sync folders to/from copyparty
1246
+
1247
+ the commandline uploader [up2k.py](https://github.com/9001/copyparty/tree/hovudstraum/bin#up2kpy) with `--dr` is the best way to sync a folder to copyparty; verifies checksums and does files in parallel, and deletes unexpected files on the server after upload has finished which makes file-renames really cheap (it'll rename serverside and skip uploading)
1248
+
1249
+ alternatively there is [rclone](./docs/rclone.md) which allows for bidirectional sync and is *way* more flexible (stream files straight from sftp/s3/gcs to copyparty, ...), although there is no integrity check and it won't work with files over 100 MiB if copyparty is behind cloudflare
1250
+
1251
+ * starting from rclone v1.63 (currently [in beta](https://beta.rclone.org/?filter=latest)), rclone will also be faster than up2k.py
1252
+
1253
+
1250
1254
  ## mount as drive
1251
1255
 
1252
1256
  a remote copyparty server as a local filesystem; go to the control-panel and click `connect` to see a list of commands to do that
1253
1257
 
1254
1258
  alternatively, some alternatives roughly sorted by speed (unreproducible benchmark), best first:
1255
1259
 
1256
- * [rclone-http](./docs/rclone.md) (25s), read-only
1260
+ * [rclone-webdav](./docs/rclone.md) (25s), read/WRITE ([v1.63-beta](https://beta.rclone.org/?filter=latest))
1261
+ * [rclone-http](./docs/rclone.md) (26s), read-only
1262
+ * [partyfuse.py](./bin/#partyfusepy) (35s), read-only
1257
1263
  * [rclone-ftp](./docs/rclone.md) (47s), read/WRITE
1258
- * [rclone-webdav](./docs/rclone.md) (51s), read/WRITE
1259
- * copyparty-1.5.0's webdav server is faster than rclone-1.60.0 (69s)
1260
- * [partyfuse.py](./bin/#partyfusepy) (71s), read-only
1261
1264
  * davfs2 (103s), read/WRITE, *very fast* on small files
1262
1265
  * [win10-webdav](#webdav-server) (138s), read/WRITE
1263
1266
  * [win10-smb2](#smb-server) (387s), read/WRITE
@@ -1265,6 +1268,27 @@ alternatively, some alternatives roughly sorted by speed (unreproducible benchma
1265
1268
  most clients will fail to mount the root of a copyparty server unless there is a root volume (so you get the admin-panel instead of a browser when accessing it) -- in that case, mount a specific volume instead
1266
1269
 
1267
1270
 
1271
+ # android app
1272
+
1273
+ upload to copyparty with one tap
1274
+
1275
+ <a href="https://f-droid.org/packages/me.ocv.partyup/"><img src="https://ocv.me/fdroid.png" alt="Get it on F-Droid" height="50" /> '' <img src="https://img.shields.io/f-droid/v/me.ocv.partyup.svg" alt="f-droid version info" /></a> '' <a href="https://github.com/9001/party-up"><img src="https://img.shields.io/github/release/9001/party-up.svg?logo=github" alt="github version info" /></a>
1276
+
1277
+ the app is **NOT** the full copyparty server! just a basic upload client, nothing fancy yet
1278
+
1279
+ if you want to run the copyparty server on your android device, see [install on android](#install-on-android)
1280
+
1281
+
1282
+ # iOS shortcuts
1283
+
1284
+ there is no iPhone app, but the following shortcuts are almost as good:
1285
+
1286
+ * [upload to copyparty](https://www.icloud.com/shortcuts/41e98dd985cb4d3bb433222bc1e9e770) ([offline](https://github.com/9001/copyparty/raw/hovudstraum/contrib/ios/upload-to-copyparty.shortcut)) ([png](https://user-images.githubusercontent.com/241032/226118053-78623554-b0ed-482e-98e4-6d57ada58ea4.png)) based on the [original](https://www.icloud.com/shortcuts/ab415d5b4de3467b9ce6f151b439a5d7) by [Daedren](https://github.com/Daedren) (thx!)
1287
+ * can strip exif, upload files, pics, vids, links, clipboard
1288
+ * can download links and rehost the target file on copyparty (see first comment inside the shortcut)
1289
+ * pics become lowres if you share from gallery to shortcut, so better to launch the shortcut and pick stuff from there
1290
+
1291
+
1268
1292
  # performance
1269
1293
 
1270
1294
  defaults are usually fine - expect `8 GiB/s` download, `1 GiB/s` upload
@@ -1360,6 +1384,13 @@ by default, except for `GET` and `HEAD` operations, all requests must either:
1360
1384
  cors can be configured with `--acao` and `--acam`, or the protections entirely disabled with `--allow-csrf`
1361
1385
 
1362
1386
 
1387
+ ## https
1388
+
1389
+ both HTTP and HTTPS are accepted by default, but letting a [reverse proxy](#reverse-proxy) handle the https/tls/ssl would be better (probably more secure by default)
1390
+
1391
+ copyparty doesn't speak HTTP/2 or QUIC, so using a reverse proxy would solve that as well
1392
+
1393
+
1363
1394
  # recovering from crashes
1364
1395
 
1365
1396
  ## client crashes
@@ -1427,7 +1458,7 @@ these are standalone programs and will never be imported / evaluated by copypart
1427
1458
 
1428
1459
  the self-contained "binary" [copyparty-sfx.py](https://github.com/9001/copyparty/releases/latest/download/copyparty-sfx.py) will unpack itself and run copyparty, assuming you have python installed of course
1429
1460
 
1430
- you can reduce the sfx size by repacking it; see [./docs/devnotes.md#sfx-repack](#./docs/devnotes.md#sfx-repack)
1461
+ you can reduce the sfx size by repacking it; see [./docs/devnotes.md#sfx-repack](./docs/devnotes.md#sfx-repack)
1431
1462
 
1432
1463
 
1433
1464
  ## copyparty.exe
@@ -1,29 +1,16 @@
1
1
  # 💾🎉 copyparty
2
2
 
3
- * portable file sharing hub (py2/py3) [(on PyPI)](https://pypi.org/project/copyparty/)
4
- * MIT-Licensed, 2019-05-26, ed @ irc.rizon.net
3
+ turn almost any device into a file server with resumable uploads/downloads using [*any*](#browser-support) web browser
5
4
 
5
+ * server only needs Python (2 or 3), all dependencies optional
6
+ * 🔌 protocols: [http](#the-browser) // [ftp](#ftp-server) // [webdav](#webdav-server) // [smb/cifs](#smb-server)
7
+ * 📱 [android app](#android-app) // [iPhone shortcuts](#ios-shortcuts)
6
8
 
7
- ## summary
8
-
9
- turn your phone or raspi into a portable file server with resumable uploads/downloads using *any* web browser
10
-
11
- * server only needs Python (`2.7` or `3.3+`), all dependencies optional
12
- * browse/upload with [IE4](#browser-support) / netscape4.0 on win3.11 (heh)
13
- * protocols: [http](#the-browser) // [ftp](#ftp-server) // [webdav](#webdav-server) // [smb/cifs](#smb-server)
14
-
15
- **[Get started](#quickstart)!** or visit the **[read-only demo server](https://a.ocv.me/pub/demo/)** 👀 running from a basement in finland
9
+ 👉 **[Get started](#quickstart)!** or visit the **[read-only demo server](https://a.ocv.me/pub/demo/)** 👀 running from a basement in finland
16
10
 
17
11
  📷 **screenshots:** [browser](#the-browser) // [upload](#uploading) // [unpost](#unpost) // [thumbnails](#thumbnails) // [search](#searching) // [fsearch](#file-search) // [zip-DL](#zip-downloads) // [md-viewer](#markdown-viewer)
18
12
 
19
13
 
20
- ## get the app
21
-
22
- <a href="https://f-droid.org/packages/me.ocv.partyup/"><img src="https://ocv.me/fdroid.png" alt="Get it on F-Droid" height="50" /> '' <img src="https://img.shields.io/f-droid/v/me.ocv.partyup.svg" alt="f-droid version info" /></a> '' <a href="https://github.com/9001/party-up"><img src="https://img.shields.io/github/release/9001/party-up.svg?logo=github" alt="github version info" /></a>
23
-
24
- (the app is **NOT** the full copyparty server! just a basic upload client, nothing fancy yet)
25
-
26
-
27
14
  ## readme toc
28
15
 
29
16
  * top
@@ -82,12 +69,16 @@ turn your phone or raspi into a portable file server with resumable uploads/down
82
69
  * [reverse-proxy](#reverse-proxy) - running copyparty next to other websites
83
70
  * [browser support](#browser-support) - TLDR: yes
84
71
  * [client examples](#client-examples) - interact with copyparty using non-browser clients
72
+ * [folder sync](#folder-sync) - sync folders to/from copyparty
85
73
  * [mount as drive](#mount-as-drive) - a remote copyparty server as a local filesystem
74
+ * [android app](#android-app) - upload to copyparty with one tap
75
+ * [iOS shortcuts](#iOS-shortcuts) - there is no iPhone app, but
86
76
  * [performance](#performance) - defaults are usually fine - expect `8 GiB/s` download, `1 GiB/s` upload
87
77
  * [client-side](#client-side) - when uploading files
88
78
  * [security](#security) - some notes on hardening
89
79
  * [gotchas](#gotchas) - behavior that might be unexpected
90
80
  * [cors](#cors) - cross-site request config
81
+ * [https](#https) - both HTTP and HTTPS are accepted
91
82
  * [recovering from crashes](#recovering-from-crashes)
92
83
  * [client crashes](#client-crashes)
93
84
  * [frefox wsod](#frefox-wsod) - firefox 87 can crash during uploads
@@ -139,7 +130,7 @@ some recommended options:
139
130
 
140
131
  you may also want these, especially on servers:
141
132
 
142
- * [contrib/systemd/copyparty.service](contrib/systemd/copyparty.service) to run copyparty as a systemd service
133
+ * [contrib/systemd/copyparty.service](contrib/systemd/copyparty.service) to run copyparty as a systemd service (see guide inside)
143
134
  * [contrib/systemd/prisonparty.service](contrib/systemd/prisonparty.service) to run it in a chroot (for extra security)
144
135
  * [contrib/rc/copyparty](contrib/rc/copyparty) to run copyparty on FreeBSD
145
136
  * [contrib/nginx/copyparty.conf](contrib/nginx/copyparty.conf) to [reverse-proxy](#reverse-proxy) behind nginx (for better https)
@@ -177,7 +168,7 @@ firewall-cmd --reload
177
168
  * ☑ write-only folders
178
169
  * ☑ [unpost](#unpost): undo/delete accidental uploads
179
170
  * ☑ [self-destruct](#self-destruct) (specified server-side or client-side)
180
- * ☑ symlink/discard existing files (content-matching)
171
+ * ☑ symlink/discard duplicates (content-matching)
181
172
  * download
182
173
  * ☑ single files in browser
183
174
  * ☑ [folders as zip / tar files](#zip-downloads)
@@ -199,7 +190,7 @@ firewall-cmd --reload
199
190
  * ☑ search by name/path/date/size
200
191
  * ☑ [search by ID3-tags etc.](#searching)
201
192
  * client support
202
- * ☑ [sync folder to server](https://github.com/9001/copyparty/tree/hovudstraum/bin#up2kpy)
193
+ * ☑ [folder sync](#folder-sync)
203
194
  * ☑ [curl-friendly](https://user-images.githubusercontent.com/241032/215322619-ea5fd606-3654-40ad-94ee-2bc058647bb2.png)
204
195
  * markdown
205
196
  * ☑ [viewer](#markdown-viewer)
@@ -278,7 +269,7 @@ server notes:
278
269
 
279
270
  * 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)
280
271
  * *future workaround:* enable the equalizer, make it all-zero, and set a negative boost to reduce the volume
281
- * "future" because `AudioContext` is broken in the current iOS version (15.1), maybe one day...
272
+ * "future" because `AudioContext` can't maintain a stable playback speed in the current iOS version (15.7), maybe one day...
282
273
 
283
274
  * Windows: folders cannot be accessed if the name ends with `.`
284
275
  * python or windows bug
@@ -1102,13 +1093,15 @@ see the top of [./copyparty/web/browser.css](./copyparty/web/browser.css) where
1102
1093
 
1103
1094
  ## reverse-proxy
1104
1095
 
1105
- running copyparty next to other websites hosted on an existing webserver such as nginx or apache
1096
+ running copyparty next to other websites hosted on an existing webserver such as nginx, caddy, or apache
1106
1097
 
1107
1098
  you can either:
1108
1099
  * give copyparty its own domain or subdomain (recommended)
1109
1100
  * or do location-based proxying, using `--rp-loc=/stuff` to tell copyparty where it is mounted -- has a slight performance cost and higher chance of bugs
1110
1101
  * if copyparty says `incorrect --rp-loc or webserver config; expected vpath starting with [...]` it's likely because the webserver is stripping away the proxy location from the request URLs -- see the `ProxyPass` in the apache example below
1111
1102
 
1103
+ some reverse proxies (such as [Caddy](https://caddyserver.com/)) can automatically obtain a valid https/tls certificate for you, and some support HTTP/2 and QUIC which could be a nice speed boost
1104
+
1112
1105
  example webserver configs:
1113
1106
 
1114
1107
  * [nginx config](contrib/nginx/copyparty.conf) -- entire domain/subdomain
@@ -1186,7 +1179,7 @@ interact with copyparty using non-browser clients
1186
1179
  * `(printf 'PUT / HTTP/1.1\r\n\r\n'; cat movie.mkv) >/dev/tcp/127.0.0.1/3923`
1187
1180
 
1188
1181
  * python: [up2k.py](https://github.com/9001/copyparty/blob/hovudstraum/bin/up2k.py) is a command-line up2k client [(webm)](https://ocv.me/stuff/u2cli.webm)
1189
- * file uploads, file-search, folder sync, autoresume of aborted/broken uploads
1182
+ * file uploads, file-search, [folder sync](#folder-sync), autoresume of aborted/broken uploads
1190
1183
  * can be downloaded from copyparty: controlpanel -> connect -> [up2k.py](http://127.0.0.1:3923/.cpr/a/up2k.py)
1191
1184
  * see [./bin/README.md#up2kpy](bin/README.md#up2kpy)
1192
1185
 
@@ -1207,17 +1200,27 @@ you can provide passwords using header `PW: hunter2`, cookie `cppwd=hunter2`, ur
1207
1200
  NOTE: curl will not send the original filename if you use `-T` combined with url-params! Also, make sure to always leave a trailing slash in URLs unless you want to override the filename
1208
1201
 
1209
1202
 
1203
+ ## folder sync
1204
+
1205
+ sync folders to/from copyparty
1206
+
1207
+ the commandline uploader [up2k.py](https://github.com/9001/copyparty/tree/hovudstraum/bin#up2kpy) with `--dr` is the best way to sync a folder to copyparty; verifies checksums and does files in parallel, and deletes unexpected files on the server after upload has finished which makes file-renames really cheap (it'll rename serverside and skip uploading)
1208
+
1209
+ alternatively there is [rclone](./docs/rclone.md) which allows for bidirectional sync and is *way* more flexible (stream files straight from sftp/s3/gcs to copyparty, ...), although there is no integrity check and it won't work with files over 100 MiB if copyparty is behind cloudflare
1210
+
1211
+ * starting from rclone v1.63 (currently [in beta](https://beta.rclone.org/?filter=latest)), rclone will also be faster than up2k.py
1212
+
1213
+
1210
1214
  ## mount as drive
1211
1215
 
1212
1216
  a remote copyparty server as a local filesystem; go to the control-panel and click `connect` to see a list of commands to do that
1213
1217
 
1214
1218
  alternatively, some alternatives roughly sorted by speed (unreproducible benchmark), best first:
1215
1219
 
1216
- * [rclone-http](./docs/rclone.md) (25s), read-only
1220
+ * [rclone-webdav](./docs/rclone.md) (25s), read/WRITE ([v1.63-beta](https://beta.rclone.org/?filter=latest))
1221
+ * [rclone-http](./docs/rclone.md) (26s), read-only
1222
+ * [partyfuse.py](./bin/#partyfusepy) (35s), read-only
1217
1223
  * [rclone-ftp](./docs/rclone.md) (47s), read/WRITE
1218
- * [rclone-webdav](./docs/rclone.md) (51s), read/WRITE
1219
- * copyparty-1.5.0's webdav server is faster than rclone-1.60.0 (69s)
1220
- * [partyfuse.py](./bin/#partyfusepy) (71s), read-only
1221
1224
  * davfs2 (103s), read/WRITE, *very fast* on small files
1222
1225
  * [win10-webdav](#webdav-server) (138s), read/WRITE
1223
1226
  * [win10-smb2](#smb-server) (387s), read/WRITE
@@ -1225,6 +1228,27 @@ alternatively, some alternatives roughly sorted by speed (unreproducible benchma
1225
1228
  most clients will fail to mount the root of a copyparty server unless there is a root volume (so you get the admin-panel instead of a browser when accessing it) -- in that case, mount a specific volume instead
1226
1229
 
1227
1230
 
1231
+ # android app
1232
+
1233
+ upload to copyparty with one tap
1234
+
1235
+ <a href="https://f-droid.org/packages/me.ocv.partyup/"><img src="https://ocv.me/fdroid.png" alt="Get it on F-Droid" height="50" /> '' <img src="https://img.shields.io/f-droid/v/me.ocv.partyup.svg" alt="f-droid version info" /></a> '' <a href="https://github.com/9001/party-up"><img src="https://img.shields.io/github/release/9001/party-up.svg?logo=github" alt="github version info" /></a>
1236
+
1237
+ the app is **NOT** the full copyparty server! just a basic upload client, nothing fancy yet
1238
+
1239
+ if you want to run the copyparty server on your android device, see [install on android](#install-on-android)
1240
+
1241
+
1242
+ # iOS shortcuts
1243
+
1244
+ there is no iPhone app, but the following shortcuts are almost as good:
1245
+
1246
+ * [upload to copyparty](https://www.icloud.com/shortcuts/41e98dd985cb4d3bb433222bc1e9e770) ([offline](https://github.com/9001/copyparty/raw/hovudstraum/contrib/ios/upload-to-copyparty.shortcut)) ([png](https://user-images.githubusercontent.com/241032/226118053-78623554-b0ed-482e-98e4-6d57ada58ea4.png)) based on the [original](https://www.icloud.com/shortcuts/ab415d5b4de3467b9ce6f151b439a5d7) by [Daedren](https://github.com/Daedren) (thx!)
1247
+ * can strip exif, upload files, pics, vids, links, clipboard
1248
+ * can download links and rehost the target file on copyparty (see first comment inside the shortcut)
1249
+ * pics become lowres if you share from gallery to shortcut, so better to launch the shortcut and pick stuff from there
1250
+
1251
+
1228
1252
  # performance
1229
1253
 
1230
1254
  defaults are usually fine - expect `8 GiB/s` download, `1 GiB/s` upload
@@ -1320,6 +1344,13 @@ by default, except for `GET` and `HEAD` operations, all requests must either:
1320
1344
  cors can be configured with `--acao` and `--acam`, or the protections entirely disabled with `--allow-csrf`
1321
1345
 
1322
1346
 
1347
+ ## https
1348
+
1349
+ both HTTP and HTTPS are accepted by default, but letting a [reverse proxy](#reverse-proxy) handle the https/tls/ssl would be better (probably more secure by default)
1350
+
1351
+ copyparty doesn't speak HTTP/2 or QUIC, so using a reverse proxy would solve that as well
1352
+
1353
+
1323
1354
  # recovering from crashes
1324
1355
 
1325
1356
  ## client crashes
@@ -1387,7 +1418,7 @@ these are standalone programs and will never be imported / evaluated by copypart
1387
1418
 
1388
1419
  the self-contained "binary" [copyparty-sfx.py](https://github.com/9001/copyparty/releases/latest/download/copyparty-sfx.py) will unpack itself and run copyparty, assuming you have python installed of course
1389
1420
 
1390
- you can reduce the sfx size by repacking it; see [./docs/devnotes.md#sfx-repack](#./docs/devnotes.md#sfx-repack)
1421
+ you can reduce the sfx size by repacking it; see [./docs/devnotes.md#sfx-repack](./docs/devnotes.md#sfx-repack)
1391
1422
 
1392
1423
 
1393
1424
  ## copyparty.exe
@@ -1009,8 +1009,9 @@ class Ctl(object):
1009
1009
  file, cid = task
1010
1010
  try:
1011
1011
  upload(file, cid, self.ar.a, stats)
1012
- except:
1013
- eprint("upload failed, retrying: {0} #{1}\n".format(file.name, cid[:8]))
1012
+ except Exception as ex:
1013
+ t = "upload failed, retrying: {0} #{1} ({2})\n"
1014
+ eprint(t.format(file.name, cid[:8], ex))
1014
1015
  # handshake will fix it
1015
1016
 
1016
1017
  with self.mutex:
@@ -1049,6 +1050,8 @@ def main():
1049
1050
  print(ver)
1050
1051
  return
1051
1052
 
1053
+ sys.argv = [x for x in sys.argv if x != "--ws"]
1054
+
1052
1055
  # fmt: off
1053
1056
  ap = app = argparse.ArgumentParser(formatter_class=APF, description="copyparty up2k uploader / filesearch tool, " + ver, epilog="""
1054
1057
  NOTE:
@@ -1067,7 +1070,6 @@ source file/folder selection uses rsync syntax, meaning that:
1067
1070
 
1068
1071
  ap = app.add_argument_group("compatibility")
1069
1072
  ap.add_argument("--cls", action="store_true", help="clear screen before start")
1070
- ap.add_argument("--ws", action="store_true", help="copyparty is running on windows; wait before deleting files after uploading")
1071
1073
 
1072
1074
  ap = app.add_argument_group("folder sync")
1073
1075
  ap.add_argument("--dl", action="store_true", help="delete local files after uploading")
@@ -1131,10 +1133,6 @@ source file/folder selection uses rsync syntax, meaning that:
1131
1133
 
1132
1134
  if ar.dr and not ar.drd:
1133
1135
  print("\npass 2/2: delete")
1134
- if getattr(ctl, "up_br") and ar.ws:
1135
- # wait for up2k to mtime if there was uploads
1136
- time.sleep(4)
1137
-
1138
1136
  ar.drd = True
1139
1137
  ar.z = True
1140
1138
  Ctl(ar, ctl.stats)
@@ -752,7 +752,7 @@ def add_ftp(ap):
752
752
 
753
753
  def add_webdav(ap):
754
754
  ap2 = ap.add_argument_group('WebDAV options')
755
- ap2.add_argument("--daw", action="store_true", help="enable full write support. \033[1;31mWARNING:\033[0m This has side-effects -- PUT-operations will now \033[1;31mOVERWRITE\033[0m existing files, rather than inventing new filenames to avoid loss of data. You might want to instead set this as a volflag where needed. By not setting this flag, uploaded files can get written to a filename which the client does not expect (which might be okay, depending on client)")
755
+ ap2.add_argument("--daw", action="store_true", help="enable full write support, even if client may not be webdav. \033[1;31mWARNING:\033[0m This has side-effects -- PUT-operations will now \033[1;31mOVERWRITE\033[0m existing files, rather than inventing new filenames to avoid loss of data. You might want to instead set this as a volflag where needed. By not setting this flag, uploaded files can get written to a filename which the client does not expect (which might be okay, depending on client)")
756
756
  ap2.add_argument("--dav-inf", action="store_true", help="allow depth:infinite requests (recursive file listing); extremely server-heavy but required for spec compliance -- luckily few clients rely on this")
757
757
  ap2.add_argument("--dav-mac", action="store_true", help="disable apple-garbage filter -- allow macos to create junk files (._* and .DS_Store, .Spotlight-*, .fseventsd, .Trashes, .AppleDouble, __MACOS)")
758
758
 
@@ -1,8 +1,8 @@
1
1
  # coding: utf-8
2
2
 
3
- VERSION = (1, 6, 9)
3
+ VERSION = (1, 6, 11)
4
4
  CODENAME = "cors k"
5
- BUILD_DT = (2023, 3, 16)
5
+ BUILD_DT = (2023, 4, 1)
6
6
 
7
7
  S_VERSION = ".".join(map(str, VERSION))
8
8
  S_BUILD_DT = "{0:04d}-{1:02d}-{2:02d}".format(*BUILD_DT)
@@ -861,7 +861,17 @@ class HttpCli(object):
861
861
  vn, rem = self.asrv.vfs.get(self.vpath, self.uname, True, False, err=401)
862
862
  depth = self.headers.get("depth", "infinity").lower()
863
863
 
864
- if depth == "infinity":
864
+ try:
865
+ topdir = {"vp": "", "st": bos.stat(vn.canonical(rem))}
866
+ except OSError as ex:
867
+ if ex.errno != errno.ENOENT:
868
+ raise
869
+ raise Pebkac(404)
870
+
871
+ if not stat.S_ISDIR(topdir["st"].st_mode):
872
+ fgen = []
873
+
874
+ elif depth == "infinity":
865
875
  if not self.args.dav_inf:
866
876
  self.log("client wants --dav-inf", 3)
867
877
  zb = b'<?xml version="1.0" encoding="utf-8"?>\n<D:error xmlns:D="DAV:"><D:propfind-finite-depth/></D:error>'
@@ -900,13 +910,6 @@ class HttpCli(object):
900
910
  t2 = " or 'infinity'" if self.args.dav_inf else ""
901
911
  raise Pebkac(412, t.format(depth, t2))
902
912
 
903
- try:
904
- topdir = {"vp": "", "st": os.stat(vn.canonical(rem))}
905
- except OSError as ex:
906
- if ex.errno != errno.ENOENT:
907
- raise
908
- raise Pebkac(404)
909
-
910
913
  fgen = itertools.chain([topdir], fgen) # type: ignore
911
914
  vtop = vjoin(self.args.R, vjoin(vn.vpath, rem))
912
915
 
@@ -1108,7 +1111,14 @@ class HttpCli(object):
1108
1111
  if self.do_log:
1109
1112
  self.log("MKCOL " + self.req)
1110
1113
 
1111
- return self._mkdir(self.vpath)
1114
+ try:
1115
+ return self._mkdir(self.vpath, True)
1116
+ except Pebkac as ex:
1117
+ if ex.code >= 500:
1118
+ raise
1119
+
1120
+ self.reply(b"", ex.code)
1121
+ return True
1112
1122
 
1113
1123
  def handle_move(self) :
1114
1124
  dst = self.headers["destination"]
@@ -1422,9 +1432,9 @@ class HttpCli(object):
1422
1432
  self.log(t, 1)
1423
1433
  raise Pebkac(403, t)
1424
1434
 
1425
- if is_put and not (self.args.no_dav or self.args.nw):
1435
+ if is_put and not (self.args.no_dav or self.args.nw) and bos.path.exists(path):
1426
1436
  # allow overwrite if...
1427
- # * volflag 'daw' is set
1437
+ # * volflag 'daw' is set, or client is definitely webdav
1428
1438
  # * and account has delete-access
1429
1439
  # or...
1430
1440
  # * file exists, is empty, sufficiently new
@@ -1434,9 +1444,11 @@ class HttpCli(object):
1434
1444
  if self.args.dotpart:
1435
1445
  tnam = "." + tnam
1436
1446
 
1437
- if (vfs.flags.get("daw") and self.can_delete) or (
1447
+ if (
1448
+ self.can_delete
1449
+ and (vfs.flags.get("daw") or "x-oc-mtime" in self.headers)
1450
+ ) or (
1438
1451
  not bos.path.exists(os.path.join(fdir, tnam))
1439
- and bos.path.exists(path)
1440
1452
  and not bos.path.getsize(path)
1441
1453
  and bos.path.getmtime(path) >= time.time() - self.args.blank_wt
1442
1454
  ):
@@ -1460,6 +1472,16 @@ class HttpCli(object):
1460
1472
  if self.args.nw:
1461
1473
  return post_sz, sha_hex, sha_b64, remains, path, ""
1462
1474
 
1475
+ at = mt = time.time() - lifetime
1476
+ cli_mt = self.headers.get("x-oc-mtime")
1477
+ if cli_mt:
1478
+ try:
1479
+ mt = int(cli_mt)
1480
+ times = (int(time.time()), mt)
1481
+ bos.utime(path, times, False)
1482
+ except:
1483
+ pass
1484
+
1463
1485
  if nameless and "magic" in vfs.flags:
1464
1486
  try:
1465
1487
  ext = self.conn.hsrv.magician.ext(path)
@@ -1482,7 +1504,6 @@ class HttpCli(object):
1482
1504
  fn = fn2
1483
1505
  path = path2
1484
1506
 
1485
- at = time.time() - lifetime
1486
1507
  if xau and not runhook(
1487
1508
  self.log,
1488
1509
  xau,
@@ -1490,7 +1511,7 @@ class HttpCli(object):
1490
1511
  self.vpath,
1491
1512
  self.host,
1492
1513
  self.uname,
1493
- at,
1514
+ mt,
1494
1515
  post_sz,
1495
1516
  self.ip,
1496
1517
  at,
@@ -1550,6 +1571,11 @@ class HttpCli(object):
1550
1571
  t = "{}\n{}\n{}\n{}\n".format(post_sz, sha_b64, sha_hex[:56], url)
1551
1572
 
1552
1573
  h = {"Location": url} if is_put and url else {}
1574
+
1575
+ if "x-oc-mtime" in self.headers:
1576
+ h["X-OC-MTime"] = "accepted"
1577
+ t = "" # some webdav clients expect/prefer this
1578
+
1553
1579
  self.reply(t.encode("utf-8"), 201, headers=h)
1554
1580
  return True
1555
1581
 
@@ -1950,7 +1976,7 @@ class HttpCli(object):
1950
1976
  sanitized = sanitize_fn(new_dir, "", [])
1951
1977
  return self._mkdir(vjoin(self.vpath, sanitized))
1952
1978
 
1953
- def _mkdir(self, vpath ) :
1979
+ def _mkdir(self, vpath , dav = False) :
1954
1980
  nullwrite = self.args.nw
1955
1981
  vfs, rem = self.asrv.vfs.get(vpath, self.uname, False, True)
1956
1982
  self._assert_safe_rem(rem)
@@ -1976,7 +2002,12 @@ class HttpCli(object):
1976
2002
  raise Pebkac(500, min_ex())
1977
2003
 
1978
2004
  self.out_headers["X-New-Dir"] = quotep(vpath.split("/")[-1])
1979
- self.redirect(vpath, status=201)
2005
+
2006
+ if dav:
2007
+ self.reply(b"", 201)
2008
+ else:
2009
+ self.redirect(vpath, status=201)
2010
+
1980
2011
  return True
1981
2012
 
1982
2013
  def handle_new_md(self) :
@@ -558,12 +558,19 @@ class ThumbSrv(object):
558
558
  if "ac" not in ret:
559
559
  raise Exception("not audio")
560
560
 
561
+ try:
562
+ dur = ret[".dur"][1]
563
+ except:
564
+ dur = 0
565
+
561
566
  src_opus = abspath.lower().endswith(".opus") or ret["ac"][1] == "opus"
562
567
  want_caf = tpath.endswith(".caf")
563
568
  tmp_opus = tpath
564
569
  if want_caf:
565
570
  tmp_opus = tpath.rsplit(".", 1)[0] + ".opus"
566
571
 
572
+ caf_src = abspath if src_opus else tmp_opus
573
+
567
574
  if not want_caf or (not src_opus and not bos.path.isfile(tmp_opus)):
568
575
  # fmt: off
569
576
  cmd = [
@@ -581,7 +588,32 @@ class ThumbSrv(object):
581
588
  # fmt: on
582
589
  self._run_ff(cmd)
583
590
 
584
- if want_caf:
591
+ # iOS fails to play some "insufficiently complex" files
592
+ # (average file shorter than 8 seconds), so of course we
593
+ # fix that by mixing in some inaudible pink noise :^)
594
+ # 6.3 sec seems like the cutoff so lets do 7, and
595
+ # 7 sec of psyqui-musou.opus @ 3:50 is 174 KiB
596
+ if want_caf and (dur < 20 or bos.path.getsize(caf_src) < 256 * 1024):
597
+ # fmt: off
598
+ cmd = [
599
+ b"ffmpeg",
600
+ b"-nostdin",
601
+ b"-v", b"error",
602
+ b"-hide_banner",
603
+ b"-i", fsenc(abspath),
604
+ b"-filter_complex", b"anoisesrc=a=0.001:d=7:c=pink,asplit[l][r]; [l][r]amerge[s]; [0:a:0][s]amix",
605
+ b"-map_metadata", b"-1",
606
+ b"-ac", b"2",
607
+ b"-c:a", b"libopus",
608
+ b"-b:a", b"128k",
609
+ b"-f", b"caf",
610
+ fsenc(tpath)
611
+ ]
612
+ # fmt: on
613
+ self._run_ff(cmd)
614
+
615
+ elif want_caf:
616
+ # simple remux should be safe
585
617
  # fmt: off
586
618
  cmd = [
587
619
  b"ffmpeg",