copyparty 1.12.2__tar.gz → 1.13.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.
- {copyparty-1.12.2 → copyparty-1.13.0}/PKG-INFO +17 -4
- {copyparty-1.12.2 → copyparty-1.13.0}/README.md +16 -3
- {copyparty-1.12.2 → copyparty-1.13.0}/copyparty/__main__.py +4 -2
- {copyparty-1.12.2 → copyparty-1.13.0}/copyparty/__version__.py +3 -3
- {copyparty-1.12.2 → copyparty-1.13.0}/copyparty/cfg.py +1 -0
- {copyparty-1.12.2 → copyparty-1.13.0}/copyparty/httpcli.py +174 -3
- {copyparty-1.12.2 → copyparty-1.13.0}/copyparty/httpconn.py +1 -0
- {copyparty-1.12.2 → copyparty-1.13.0}/copyparty/httpsrv.py +2 -0
- {copyparty-1.12.2 → copyparty-1.13.0}/copyparty/up2k.py +115 -52
- {copyparty-1.12.2 → copyparty-1.13.0}/copyparty/util.py +37 -3
- {copyparty-1.12.2 → copyparty-1.13.0}/copyparty/web/a/u2c.py +8 -4
- copyparty-1.13.0/copyparty/web/browser.css.gz +0 -0
- copyparty-1.13.0/copyparty/web/browser.js.gz +0 -0
- {copyparty-1.12.2 → copyparty-1.13.0}/copyparty/web/up2k.js.gz +0 -0
- {copyparty-1.12.2 → copyparty-1.13.0}/copyparty.egg-info/PKG-INFO +17 -4
- copyparty-1.12.2/copyparty/web/browser.css.gz +0 -0
- copyparty-1.12.2/copyparty/web/browser.js.gz +0 -0
- {copyparty-1.12.2 → copyparty-1.13.0}/LICENSE +0 -0
- {copyparty-1.12.2 → copyparty-1.13.0}/copyparty/__init__.py +0 -0
- {copyparty-1.12.2 → copyparty-1.13.0}/copyparty/authsrv.py +0 -0
- {copyparty-1.12.2 → copyparty-1.13.0}/copyparty/bos/__init__.py +0 -0
- {copyparty-1.12.2 → copyparty-1.13.0}/copyparty/bos/bos.py +0 -0
- {copyparty-1.12.2 → copyparty-1.13.0}/copyparty/bos/path.py +0 -0
- {copyparty-1.12.2 → copyparty-1.13.0}/copyparty/broker_mp.py +0 -0
- {copyparty-1.12.2 → copyparty-1.13.0}/copyparty/broker_mpw.py +0 -0
- {copyparty-1.12.2 → copyparty-1.13.0}/copyparty/broker_thr.py +0 -0
- {copyparty-1.12.2 → copyparty-1.13.0}/copyparty/broker_util.py +0 -0
- {copyparty-1.12.2 → copyparty-1.13.0}/copyparty/cert.py +0 -0
- {copyparty-1.12.2 → copyparty-1.13.0}/copyparty/dxml.py +0 -0
- {copyparty-1.12.2 → copyparty-1.13.0}/copyparty/fsutil.py +0 -0
- {copyparty-1.12.2 → copyparty-1.13.0}/copyparty/ftpd.py +0 -0
- {copyparty-1.12.2 → copyparty-1.13.0}/copyparty/ico.py +0 -0
- {copyparty-1.12.2 → copyparty-1.13.0}/copyparty/mdns.py +0 -0
- {copyparty-1.12.2 → copyparty-1.13.0}/copyparty/metrics.py +0 -0
- {copyparty-1.12.2 → copyparty-1.13.0}/copyparty/mtag.py +0 -0
- {copyparty-1.12.2 → copyparty-1.13.0}/copyparty/multicast.py +0 -0
- {copyparty-1.12.2 → copyparty-1.13.0}/copyparty/pwhash.py +0 -0
- {copyparty-1.12.2 → copyparty-1.13.0}/copyparty/res/COPYING.txt +0 -0
- {copyparty-1.12.2 → copyparty-1.13.0}/copyparty/res/__init__.py +0 -0
- {copyparty-1.12.2 → copyparty-1.13.0}/copyparty/res/insecure.pem +0 -0
- {copyparty-1.12.2 → copyparty-1.13.0}/copyparty/smbd.py +0 -0
- {copyparty-1.12.2 → copyparty-1.13.0}/copyparty/ssdp.py +0 -0
- {copyparty-1.12.2 → copyparty-1.13.0}/copyparty/star.py +0 -0
- {copyparty-1.12.2 → copyparty-1.13.0}/copyparty/stolen/__init__.py +0 -0
- {copyparty-1.12.2 → copyparty-1.13.0}/copyparty/stolen/dnslib/__init__.py +0 -0
- {copyparty-1.12.2 → copyparty-1.13.0}/copyparty/stolen/dnslib/bimap.py +0 -0
- {copyparty-1.12.2 → copyparty-1.13.0}/copyparty/stolen/dnslib/bit.py +0 -0
- {copyparty-1.12.2 → copyparty-1.13.0}/copyparty/stolen/dnslib/buffer.py +0 -0
- {copyparty-1.12.2 → copyparty-1.13.0}/copyparty/stolen/dnslib/dns.py +0 -0
- {copyparty-1.12.2 → copyparty-1.13.0}/copyparty/stolen/dnslib/label.py +0 -0
- {copyparty-1.12.2 → copyparty-1.13.0}/copyparty/stolen/dnslib/lex.py +0 -0
- {copyparty-1.12.2 → copyparty-1.13.0}/copyparty/stolen/dnslib/ranges.py +0 -0
- {copyparty-1.12.2 → copyparty-1.13.0}/copyparty/stolen/ifaddr/__init__.py +0 -0
- {copyparty-1.12.2 → copyparty-1.13.0}/copyparty/stolen/ifaddr/_posix.py +0 -0
- {copyparty-1.12.2 → copyparty-1.13.0}/copyparty/stolen/ifaddr/_shared.py +0 -0
- {copyparty-1.12.2 → copyparty-1.13.0}/copyparty/stolen/ifaddr/_win32.py +0 -0
- {copyparty-1.12.2 → copyparty-1.13.0}/copyparty/stolen/qrcodegen.py +0 -0
- {copyparty-1.12.2 → copyparty-1.13.0}/copyparty/stolen/surrogateescape.py +0 -0
- {copyparty-1.12.2 → copyparty-1.13.0}/copyparty/sutil.py +0 -0
- {copyparty-1.12.2 → copyparty-1.13.0}/copyparty/svchub.py +0 -0
- {copyparty-1.12.2 → copyparty-1.13.0}/copyparty/szip.py +0 -0
- {copyparty-1.12.2 → copyparty-1.13.0}/copyparty/tcpsrv.py +0 -0
- {copyparty-1.12.2 → copyparty-1.13.0}/copyparty/tftpd.py +0 -0
- {copyparty-1.12.2 → copyparty-1.13.0}/copyparty/th_cli.py +0 -0
- {copyparty-1.12.2 → copyparty-1.13.0}/copyparty/th_srv.py +0 -0
- {copyparty-1.12.2 → copyparty-1.13.0}/copyparty/u2idx.py +0 -0
- {copyparty-1.12.2 → copyparty-1.13.0}/copyparty/web/a/__init__.py +0 -0
- {copyparty-1.12.2 → copyparty-1.13.0}/copyparty/web/a/partyfuse.py +0 -0
- {copyparty-1.12.2 → copyparty-1.13.0}/copyparty/web/a/webdav-cfg.bat +0 -0
- {copyparty-1.12.2 → copyparty-1.13.0}/copyparty/web/baguettebox.js.gz +0 -0
- {copyparty-1.12.2 → copyparty-1.13.0}/copyparty/web/browser.html +0 -0
- {copyparty-1.12.2 → copyparty-1.13.0}/copyparty/web/browser2.html +0 -0
- {copyparty-1.12.2 → copyparty-1.13.0}/copyparty/web/cf.html +0 -0
- {copyparty-1.12.2 → copyparty-1.13.0}/copyparty/web/dbg-audio.js.gz +0 -0
- {copyparty-1.12.2 → copyparty-1.13.0}/copyparty/web/dd/2.png +0 -0
- {copyparty-1.12.2 → copyparty-1.13.0}/copyparty/web/dd/3.png +0 -0
- {copyparty-1.12.2 → copyparty-1.13.0}/copyparty/web/dd/4.png +0 -0
- {copyparty-1.12.2 → copyparty-1.13.0}/copyparty/web/dd/5.png +0 -0
- {copyparty-1.12.2 → copyparty-1.13.0}/copyparty/web/dd/__init__.py +0 -0
- {copyparty-1.12.2 → copyparty-1.13.0}/copyparty/web/deps/__init__.py +0 -0
- {copyparty-1.12.2 → copyparty-1.13.0}/copyparty/web/deps/busy.mp3.gz +0 -0
- {copyparty-1.12.2 → copyparty-1.13.0}/copyparty/web/deps/easymde.css.gz +0 -0
- {copyparty-1.12.2 → copyparty-1.13.0}/copyparty/web/deps/easymde.js.gz +0 -0
- {copyparty-1.12.2 → copyparty-1.13.0}/copyparty/web/deps/marked.js.gz +0 -0
- {copyparty-1.12.2 → copyparty-1.13.0}/copyparty/web/deps/mini-fa.css.gz +0 -0
- {copyparty-1.12.2 → copyparty-1.13.0}/copyparty/web/deps/mini-fa.woff +0 -0
- {copyparty-1.12.2 → copyparty-1.13.0}/copyparty/web/deps/prism.css.gz +0 -0
- {copyparty-1.12.2 → copyparty-1.13.0}/copyparty/web/deps/prism.js.gz +0 -0
- {copyparty-1.12.2 → copyparty-1.13.0}/copyparty/web/deps/prismd.css.gz +0 -0
- {copyparty-1.12.2 → copyparty-1.13.0}/copyparty/web/deps/scp.woff2 +0 -0
- {copyparty-1.12.2 → copyparty-1.13.0}/copyparty/web/deps/sha512.ac.js.gz +0 -0
- {copyparty-1.12.2 → copyparty-1.13.0}/copyparty/web/deps/sha512.hw.js.gz +0 -0
- {copyparty-1.12.2 → copyparty-1.13.0}/copyparty/web/md.css.gz +0 -0
- {copyparty-1.12.2 → copyparty-1.13.0}/copyparty/web/md.html +0 -0
- {copyparty-1.12.2 → copyparty-1.13.0}/copyparty/web/md.js.gz +0 -0
- {copyparty-1.12.2 → copyparty-1.13.0}/copyparty/web/md2.css.gz +0 -0
- {copyparty-1.12.2 → copyparty-1.13.0}/copyparty/web/md2.js.gz +0 -0
- {copyparty-1.12.2 → copyparty-1.13.0}/copyparty/web/mde.css.gz +0 -0
- {copyparty-1.12.2 → copyparty-1.13.0}/copyparty/web/mde.html +0 -0
- {copyparty-1.12.2 → copyparty-1.13.0}/copyparty/web/mde.js.gz +0 -0
- {copyparty-1.12.2 → copyparty-1.13.0}/copyparty/web/msg.css.gz +0 -0
- {copyparty-1.12.2 → copyparty-1.13.0}/copyparty/web/msg.html +0 -0
- {copyparty-1.12.2 → copyparty-1.13.0}/copyparty/web/splash.css.gz +0 -0
- {copyparty-1.12.2 → copyparty-1.13.0}/copyparty/web/splash.html +0 -0
- {copyparty-1.12.2 → copyparty-1.13.0}/copyparty/web/splash.js.gz +0 -0
- {copyparty-1.12.2 → copyparty-1.13.0}/copyparty/web/svcs.html +0 -0
- {copyparty-1.12.2 → copyparty-1.13.0}/copyparty/web/svcs.js.gz +0 -0
- {copyparty-1.12.2 → copyparty-1.13.0}/copyparty/web/ui.css.gz +0 -0
- {copyparty-1.12.2 → copyparty-1.13.0}/copyparty/web/util.js.gz +0 -0
- {copyparty-1.12.2 → copyparty-1.13.0}/copyparty/web/w.hash.js.gz +0 -0
- {copyparty-1.12.2 → copyparty-1.13.0}/copyparty.egg-info/SOURCES.txt +0 -0
- {copyparty-1.12.2 → copyparty-1.13.0}/copyparty.egg-info/dependency_links.txt +0 -0
- {copyparty-1.12.2 → copyparty-1.13.0}/copyparty.egg-info/entry_points.txt +0 -0
- {copyparty-1.12.2 → copyparty-1.13.0}/copyparty.egg-info/requires.txt +0 -0
- {copyparty-1.12.2 → copyparty-1.13.0}/copyparty.egg-info/top_level.txt +0 -0
- {copyparty-1.12.2 → copyparty-1.13.0}/pyproject.toml +0 -0
- {copyparty-1.12.2 → copyparty-1.13.0}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: copyparty
|
3
|
-
Version: 1.
|
3
|
+
Version: 1.13.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
|
@@ -64,6 +64,8 @@ turn almost any device into a file server with resumable uploads/downloads using
|
|
64
64
|
|
65
65
|
📷 **screenshots:** [browser](#the-browser) // [upload](#uploading) // [unpost](#unpost) // [thumbnails](#thumbnails) // [search](#searching) // [fsearch](#file-search) // [zip-DL](#zip-downloads) // [md-viewer](#markdown-viewer)
|
66
66
|
|
67
|
+
🎬 **videos:** [upload](https://a.ocv.me/pub/demo/pics-vids/up2k.webm) // [cli-upload](https://a.ocv.me/pub/demo/pics-vids/u2cli.webm) // [race-the-beam](https://a.ocv.me/pub/g/nerd-stuff/cpp/2024-0418-race-the-beam.webm)
|
68
|
+
|
67
69
|
|
68
70
|
## readme toc
|
69
71
|
|
@@ -71,7 +73,7 @@ turn almost any device into a file server with resumable uploads/downloads using
|
|
71
73
|
* [quickstart](#quickstart) - just run **[copyparty-sfx.py](https://github.com/9001/copyparty/releases/latest/download/copyparty-sfx.py)** -- that's it! 🎉
|
72
74
|
* [at home](#at-home) - make it accessible over the internet
|
73
75
|
* [on servers](#on-servers) - you may also want these, especially on servers
|
74
|
-
* [features](#features)
|
76
|
+
* [features](#features) - also see [comparison to similar software](./docs/versus.md)
|
75
77
|
* [testimonials](#testimonials) - small collection of user feedback
|
76
78
|
* [motivations](#motivations) - project goals / philosophy
|
77
79
|
* [notes](#notes) - general notes
|
@@ -92,6 +94,7 @@ turn almost any device into a file server with resumable uploads/downloads using
|
|
92
94
|
* [file-search](#file-search) - dropping files into the browser also lets you see if they exist on the server
|
93
95
|
* [unpost](#unpost) - undo/delete accidental uploads
|
94
96
|
* [self-destruct](#self-destruct) - uploads can be given a lifetime
|
97
|
+
* [race the beam](#race-the-beam) - download files while they're still uploading ([demo video](http://a.ocv.me/pub/g/nerd-stuff/cpp/2024-0418-race-the-beam.webm))
|
95
98
|
* [file manager](#file-manager) - cut/paste, rename, and delete files/folders (if you have permission)
|
96
99
|
* [batch rename](#batch-rename) - select some files and press `F2` to bring up the rename UI
|
97
100
|
* [media player](#media-player) - plays almost every audio format there is
|
@@ -181,7 +184,7 @@ enable thumbnails (images/audio/video), media indexing, and audio transcoding by
|
|
181
184
|
|
182
185
|
* **Alpine:** `apk add py3-pillow ffmpeg`
|
183
186
|
* **Debian:** `apt install --no-install-recommends python3-pil ffmpeg`
|
184
|
-
* **Fedora:** rpmfusion + `dnf install python3-pillow ffmpeg`
|
187
|
+
* **Fedora:** rpmfusion + `dnf install python3-pillow ffmpeg --allowerasing`
|
185
188
|
* **FreeBSD:** `pkg install py39-sqlite3 py39-pillow ffmpeg`
|
186
189
|
* **MacOS:** `port install py-Pillow ffmpeg`
|
187
190
|
* **MacOS** (alternative): `brew install pillow ffmpeg`
|
@@ -236,6 +239,8 @@ firewall-cmd --reload
|
|
236
239
|
|
237
240
|
## features
|
238
241
|
|
242
|
+
also see [comparison to similar software](./docs/versus.md)
|
243
|
+
|
239
244
|
* backend stuff
|
240
245
|
* ☑ IPv6
|
241
246
|
* ☑ [multiprocessing](#performance) (actual multithreading)
|
@@ -258,6 +263,7 @@ firewall-cmd --reload
|
|
258
263
|
* ☑ write-only folders
|
259
264
|
* ☑ [unpost](#unpost): undo/delete accidental uploads
|
260
265
|
* ☑ [self-destruct](#self-destruct) (specified server-side or client-side)
|
266
|
+
* ☑ [race the beam](#race-the-beam) (almost like peer-to-peer)
|
261
267
|
* ☑ symlink/discard duplicates (content-matching)
|
262
268
|
* download
|
263
269
|
* ☑ single files in browser
|
@@ -683,7 +689,7 @@ up2k has several advantages:
|
|
683
689
|
> it is perfectly safe to restart / upgrade copyparty while someone is uploading to it!
|
684
690
|
> all known up2k clients will resume just fine 💪
|
685
691
|
|
686
|
-
see [up2k](#up2k) for details on how it works, or watch a [demo video](https://a.ocv.me/pub/demo/pics-vids/#gf-0f6f5c0d)
|
692
|
+
see [up2k](./docs/devnotes.md#up2k) for details on how it works, or watch a [demo video](https://a.ocv.me/pub/demo/pics-vids/#gf-0f6f5c0d)
|
687
693
|
|
688
694
|

|
689
695
|
|
@@ -749,6 +755,13 @@ clients can specify a shorter expiration time using the [up2k ui](#uploading) --
|
|
749
755
|
specifying a custom expiration time client-side will affect the timespan in which unposts are permitted, so keep an eye on the estimates in the up2k ui
|
750
756
|
|
751
757
|
|
758
|
+
### race the beam
|
759
|
+
|
760
|
+
download files while they're still uploading ([demo video](http://a.ocv.me/pub/g/nerd-stuff/cpp/2024-0418-race-the-beam.webm)) -- it's almost like peer-to-peer
|
761
|
+
|
762
|
+
requires the file to be uploaded using up2k (which is the default drag-and-drop uploader), alternatively the command-line program
|
763
|
+
|
764
|
+
|
752
765
|
## file manager
|
753
766
|
|
754
767
|
cut/paste, rename, and delete files/folders (if you have permission)
|
@@ -10,6 +10,8 @@ turn almost any device into a file server with resumable uploads/downloads using
|
|
10
10
|
|
11
11
|
📷 **screenshots:** [browser](#the-browser) // [upload](#uploading) // [unpost](#unpost) // [thumbnails](#thumbnails) // [search](#searching) // [fsearch](#file-search) // [zip-DL](#zip-downloads) // [md-viewer](#markdown-viewer)
|
12
12
|
|
13
|
+
🎬 **videos:** [upload](https://a.ocv.me/pub/demo/pics-vids/up2k.webm) // [cli-upload](https://a.ocv.me/pub/demo/pics-vids/u2cli.webm) // [race-the-beam](https://a.ocv.me/pub/g/nerd-stuff/cpp/2024-0418-race-the-beam.webm)
|
14
|
+
|
13
15
|
|
14
16
|
## readme toc
|
15
17
|
|
@@ -17,7 +19,7 @@ turn almost any device into a file server with resumable uploads/downloads using
|
|
17
19
|
* [quickstart](#quickstart) - just run **[copyparty-sfx.py](https://github.com/9001/copyparty/releases/latest/download/copyparty-sfx.py)** -- that's it! 🎉
|
18
20
|
* [at home](#at-home) - make it accessible over the internet
|
19
21
|
* [on servers](#on-servers) - you may also want these, especially on servers
|
20
|
-
* [features](#features)
|
22
|
+
* [features](#features) - also see [comparison to similar software](./docs/versus.md)
|
21
23
|
* [testimonials](#testimonials) - small collection of user feedback
|
22
24
|
* [motivations](#motivations) - project goals / philosophy
|
23
25
|
* [notes](#notes) - general notes
|
@@ -38,6 +40,7 @@ turn almost any device into a file server with resumable uploads/downloads using
|
|
38
40
|
* [file-search](#file-search) - dropping files into the browser also lets you see if they exist on the server
|
39
41
|
* [unpost](#unpost) - undo/delete accidental uploads
|
40
42
|
* [self-destruct](#self-destruct) - uploads can be given a lifetime
|
43
|
+
* [race the beam](#race-the-beam) - download files while they're still uploading ([demo video](http://a.ocv.me/pub/g/nerd-stuff/cpp/2024-0418-race-the-beam.webm))
|
41
44
|
* [file manager](#file-manager) - cut/paste, rename, and delete files/folders (if you have permission)
|
42
45
|
* [batch rename](#batch-rename) - select some files and press `F2` to bring up the rename UI
|
43
46
|
* [media player](#media-player) - plays almost every audio format there is
|
@@ -127,7 +130,7 @@ enable thumbnails (images/audio/video), media indexing, and audio transcoding by
|
|
127
130
|
|
128
131
|
* **Alpine:** `apk add py3-pillow ffmpeg`
|
129
132
|
* **Debian:** `apt install --no-install-recommends python3-pil ffmpeg`
|
130
|
-
* **Fedora:** rpmfusion + `dnf install python3-pillow ffmpeg`
|
133
|
+
* **Fedora:** rpmfusion + `dnf install python3-pillow ffmpeg --allowerasing`
|
131
134
|
* **FreeBSD:** `pkg install py39-sqlite3 py39-pillow ffmpeg`
|
132
135
|
* **MacOS:** `port install py-Pillow ffmpeg`
|
133
136
|
* **MacOS** (alternative): `brew install pillow ffmpeg`
|
@@ -182,6 +185,8 @@ firewall-cmd --reload
|
|
182
185
|
|
183
186
|
## features
|
184
187
|
|
188
|
+
also see [comparison to similar software](./docs/versus.md)
|
189
|
+
|
185
190
|
* backend stuff
|
186
191
|
* ☑ IPv6
|
187
192
|
* ☑ [multiprocessing](#performance) (actual multithreading)
|
@@ -204,6 +209,7 @@ firewall-cmd --reload
|
|
204
209
|
* ☑ write-only folders
|
205
210
|
* ☑ [unpost](#unpost): undo/delete accidental uploads
|
206
211
|
* ☑ [self-destruct](#self-destruct) (specified server-side or client-side)
|
212
|
+
* ☑ [race the beam](#race-the-beam) (almost like peer-to-peer)
|
207
213
|
* ☑ symlink/discard duplicates (content-matching)
|
208
214
|
* download
|
209
215
|
* ☑ single files in browser
|
@@ -629,7 +635,7 @@ up2k has several advantages:
|
|
629
635
|
> it is perfectly safe to restart / upgrade copyparty while someone is uploading to it!
|
630
636
|
> all known up2k clients will resume just fine 💪
|
631
637
|
|
632
|
-
see [up2k](#up2k) for details on how it works, or watch a [demo video](https://a.ocv.me/pub/demo/pics-vids/#gf-0f6f5c0d)
|
638
|
+
see [up2k](./docs/devnotes.md#up2k) for details on how it works, or watch a [demo video](https://a.ocv.me/pub/demo/pics-vids/#gf-0f6f5c0d)
|
633
639
|
|
634
640
|

|
635
641
|
|
@@ -695,6 +701,13 @@ clients can specify a shorter expiration time using the [up2k ui](#uploading) --
|
|
695
701
|
specifying a custom expiration time client-side will affect the timespan in which unposts are permitted, so keep an eye on the estimates in the up2k ui
|
696
702
|
|
697
703
|
|
704
|
+
### race the beam
|
705
|
+
|
706
|
+
download files while they're still uploading ([demo video](http://a.ocv.me/pub/g/nerd-stuff/cpp/2024-0418-race-the-beam.webm)) -- it's almost like peer-to-peer
|
707
|
+
|
708
|
+
requires the file to be uploaded using up2k (which is the default drag-and-drop uploader), alternatively the command-line program
|
709
|
+
|
710
|
+
|
698
711
|
## file manager
|
699
712
|
|
700
713
|
cut/paste, rename, and delete files/folders (if you have permission)
|
@@ -850,7 +850,7 @@ def add_qr(ap, tty):
|
|
850
850
|
|
851
851
|
def add_fs(ap):
|
852
852
|
ap2 = ap.add_argument_group("filesystem options")
|
853
|
-
rm_re_def = "
|
853
|
+
rm_re_def = "15/0.1" if ANYWIN else "0/0"
|
854
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
855
|
ap2.add_argument("--mv-retry", metavar="T/R", type=u, default=rm_re_def, help="if a file cannot be renamed because it is busy, continue trying for \033[33mT\033[0m seconds, retry every \033[33mR\033[0m seconds; disable with 0/0 (volflag=mv_retry)")
|
856
856
|
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)")
|
@@ -1085,6 +1085,8 @@ def add_optouts(ap):
|
|
1085
1085
|
ap2.add_argument("--no-zip", action="store_true", help="disable download as zip/tar")
|
1086
1086
|
ap2.add_argument("--no-tarcmp", action="store_true", help="disable download as compressed tar (?tar=gz, ?tar=bz2, ?tar=xz, ?tar=gz:9, ...)")
|
1087
1087
|
ap2.add_argument("--no-lifetime", action="store_true", help="do not allow clients (or server config) to schedule an upload to be deleted after a given time")
|
1088
|
+
ap2.add_argument("--no-pipe", action="store_true", help="disable race-the-beam (lockstep download of files which are currently being uploaded) (volflag=nopipe)")
|
1089
|
+
ap2.add_argument("--no-db-ip", action="store_true", help="do not write uploader IPs into the database")
|
1088
1090
|
|
1089
1091
|
|
1090
1092
|
def add_safety(ap):
|
@@ -1210,7 +1212,7 @@ def add_db_general(ap, hcores):
|
|
1210
1212
|
ap2.add_argument("--no-hash", metavar="PTN", type=u, help="regex: disable hashing of matching absolute-filesystem-paths during e2ds folder scans (volflag=nohash)")
|
1211
1213
|
ap2.add_argument("--no-idx", metavar="PTN", type=u, default=noidx, help="regex: disable indexing of matching absolute-filesystem-paths during e2ds folder scans (volflag=noidx)")
|
1212
1214
|
ap2.add_argument("--no-dhash", action="store_true", help="disable rescan acceleration; do full database integrity check -- makes the db ~5%% smaller and bootup/rescans 3~10x slower")
|
1213
|
-
ap2.add_argument("--re-dhash", action="store_true", help="
|
1215
|
+
ap2.add_argument("--re-dhash", action="store_true", help="force a cache rebuild on startup; enable this once if it gets out of sync (should never be necessary)")
|
1214
1216
|
ap2.add_argument("--no-forget", action="store_true", help="never forget indexed files, even when deleted from disk -- makes it impossible to ever upload the same file twice -- only useful for offloading uploads to a cloud service or something (volflag=noforget)")
|
1215
1217
|
ap2.add_argument("--dbd", metavar="PROFILE", default="wal", help="database durability profile; sets the tradeoff between robustness and speed, see \033[33m--help-dbd\033[0m (volflag=dbd)")
|
1216
1218
|
ap2.add_argument("--xlink", action="store_true", help="on upload: check all volumes for dupes, not just the target volume (volflag=xlink)")
|
@@ -1,8 +1,8 @@
|
|
1
1
|
# coding: utf-8
|
2
2
|
|
3
|
-
VERSION = (1,
|
4
|
-
CODENAME = "
|
5
|
-
BUILD_DT = (2024, 4,
|
3
|
+
VERSION = (1, 13, 0)
|
4
|
+
CODENAME = "race the beam"
|
5
|
+
BUILD_DT = (2024, 4, 20)
|
6
6
|
|
7
7
|
S_VERSION = ".".join(map(str, VERSION))
|
8
8
|
S_BUILD_DT = "{0:04d}-{1:02d}-{2:02d}".format(*BUILD_DT)
|
@@ -36,6 +36,7 @@ from .bos import bos
|
|
36
36
|
from .star import StreamTar
|
37
37
|
from .sutil import StreamArc, gfilter
|
38
38
|
from .szip import StreamZip
|
39
|
+
from .up2k import up2k_chunksize
|
39
40
|
from .util import unquote # type: ignore
|
40
41
|
from .util import (
|
41
42
|
APPLESAN_RE,
|
@@ -123,6 +124,7 @@ class HttpCli(object):
|
|
123
124
|
self.ico = conn.ico # mypy404
|
124
125
|
self.thumbcli = conn.thumbcli # mypy404
|
125
126
|
self.u2fh = conn.u2fh # mypy404
|
127
|
+
self.pipes = conn.pipes # mypy404
|
126
128
|
self.log_func = conn.log_func # mypy404
|
127
129
|
self.log_src = conn.log_src # mypy404
|
128
130
|
self.gen_fk = self._gen_fk if self.args.log_fk else gen_filekey
|
@@ -2925,17 +2927,42 @@ class HttpCli(object):
|
|
2925
2927
|
|
2926
2928
|
return txt
|
2927
2929
|
|
2928
|
-
def tx_file(self, req_path ) :
|
2930
|
+
def tx_file(self, req_path , ptop = None) :
|
2929
2931
|
status = 200
|
2930
2932
|
logmsg = "{:4} {} ".format("", self.req)
|
2931
2933
|
logtail = ""
|
2932
2934
|
|
2935
|
+
if ptop is not None:
|
2936
|
+
try:
|
2937
|
+
dp, fn = os.path.split(req_path)
|
2938
|
+
tnam = fn + ".PARTIAL"
|
2939
|
+
if self.args.dotpart:
|
2940
|
+
tnam = "." + tnam
|
2941
|
+
ap_data = os.path.join(dp, tnam)
|
2942
|
+
st_data = bos.stat(ap_data)
|
2943
|
+
if not st_data.st_size:
|
2944
|
+
raise Exception("partial is empty")
|
2945
|
+
x = self.conn.hsrv.broker.ask("up2k.find_job_by_ap", ptop, req_path)
|
2946
|
+
job = json.loads(x.get())
|
2947
|
+
if not job:
|
2948
|
+
raise Exception("not found in registry")
|
2949
|
+
self.pipes.set(req_path, job)
|
2950
|
+
except Exception as ex:
|
2951
|
+
self.log("will not pipe [%s]; %s" % (ap_data, ex), 6)
|
2952
|
+
ptop = None
|
2953
|
+
|
2933
2954
|
#
|
2934
2955
|
# if request is for foo.js, check if we have foo.js.gz
|
2935
2956
|
|
2936
2957
|
file_ts = 0.0
|
2937
2958
|
editions = {}
|
2938
2959
|
for ext in ("", ".gz"):
|
2960
|
+
if ptop is not None:
|
2961
|
+
sz = job["size"]
|
2962
|
+
file_ts = job["lmod"]
|
2963
|
+
editions["plain"] = (ap_data, sz)
|
2964
|
+
break
|
2965
|
+
|
2939
2966
|
try:
|
2940
2967
|
fs_path = req_path + ext
|
2941
2968
|
st = bos.stat(fs_path)
|
@@ -3092,6 +3119,11 @@ class HttpCli(object):
|
|
3092
3119
|
self.send_headers(length=upper - lower, status=status, mime=mime)
|
3093
3120
|
return True
|
3094
3121
|
|
3122
|
+
if ptop is not None:
|
3123
|
+
return self.tx_pipe(
|
3124
|
+
ptop, req_path, ap_data, job, lower, upper, status, mime, logmsg
|
3125
|
+
)
|
3126
|
+
|
3095
3127
|
ret = True
|
3096
3128
|
with open_func(*open_args) as f:
|
3097
3129
|
self.send_headers(length=upper - lower, status=status, mime=mime)
|
@@ -3111,6 +3143,143 @@ class HttpCli(object):
|
|
3111
3143
|
|
3112
3144
|
return ret
|
3113
3145
|
|
3146
|
+
def tx_pipe(
|
3147
|
+
self,
|
3148
|
+
ptop ,
|
3149
|
+
req_path ,
|
3150
|
+
ap_data ,
|
3151
|
+
job ,
|
3152
|
+
lower ,
|
3153
|
+
upper ,
|
3154
|
+
status ,
|
3155
|
+
mime ,
|
3156
|
+
logmsg ,
|
3157
|
+
) :
|
3158
|
+
M = 1048576
|
3159
|
+
self.send_headers(length=upper - lower, status=status, mime=mime)
|
3160
|
+
wr_slp = self.args.s_wr_slp
|
3161
|
+
wr_sz = self.args.s_wr_sz
|
3162
|
+
file_size = job["size"]
|
3163
|
+
chunk_size = up2k_chunksize(file_size)
|
3164
|
+
num_need = -1
|
3165
|
+
data_end = 0
|
3166
|
+
remains = upper - lower
|
3167
|
+
broken = False
|
3168
|
+
spins = 0
|
3169
|
+
tier = 0
|
3170
|
+
tiers = ["uncapped", "reduced speed", "one byte per sec"]
|
3171
|
+
|
3172
|
+
while lower < upper and not broken:
|
3173
|
+
with self.u2mutex:
|
3174
|
+
job = self.pipes.get(req_path)
|
3175
|
+
if not job:
|
3176
|
+
x = self.conn.hsrv.broker.ask("up2k.find_job_by_ap", ptop, req_path)
|
3177
|
+
job = json.loads(x.get())
|
3178
|
+
if job:
|
3179
|
+
self.pipes.set(req_path, job)
|
3180
|
+
|
3181
|
+
if not job:
|
3182
|
+
t = "pipe: OK, upload has finished; yeeting remainder"
|
3183
|
+
self.log(t, 2)
|
3184
|
+
data_end = file_size
|
3185
|
+
break
|
3186
|
+
|
3187
|
+
if num_need != len(job["need"]):
|
3188
|
+
num_need = len(job["need"])
|
3189
|
+
data_end = 0
|
3190
|
+
for cid in job["hash"]:
|
3191
|
+
if cid in job["need"]:
|
3192
|
+
break
|
3193
|
+
data_end += chunk_size
|
3194
|
+
t = "pipe: can stream %.2f MiB; requested range is %.2f to %.2f"
|
3195
|
+
self.log(t % (data_end / M, lower / M, upper / M), 6)
|
3196
|
+
with self.u2mutex:
|
3197
|
+
if data_end > self.u2fh.aps.get(ap_data, data_end):
|
3198
|
+
try:
|
3199
|
+
fhs = self.u2fh.cache[ap_data].all_fhs
|
3200
|
+
for fh in fhs:
|
3201
|
+
fh.flush()
|
3202
|
+
self.u2fh.aps[ap_data] = data_end
|
3203
|
+
self.log("pipe: flushed %d up2k-FDs" % (len(fhs),))
|
3204
|
+
except Exception as ex:
|
3205
|
+
self.log("pipe: u2fh flush failed: %r" % (ex,))
|
3206
|
+
|
3207
|
+
if lower >= data_end:
|
3208
|
+
if data_end:
|
3209
|
+
t = "pipe: uploader is too slow; aborting download at %.2f MiB"
|
3210
|
+
self.log(t % (data_end / M))
|
3211
|
+
raise Pebkac(416, "uploader is too slow")
|
3212
|
+
|
3213
|
+
raise Pebkac(416, "no data available yet; please retry in a bit")
|
3214
|
+
|
3215
|
+
slack = data_end - lower
|
3216
|
+
if slack >= 8 * M:
|
3217
|
+
ntier = 0
|
3218
|
+
winsz = M
|
3219
|
+
bufsz = wr_sz
|
3220
|
+
slp = wr_slp
|
3221
|
+
else:
|
3222
|
+
winsz = max(40, int(M * (slack / (12 * M))))
|
3223
|
+
base_rate = M if not wr_slp else wr_sz / wr_slp
|
3224
|
+
if winsz > base_rate:
|
3225
|
+
ntier = 0
|
3226
|
+
bufsz = wr_sz
|
3227
|
+
slp = wr_slp
|
3228
|
+
elif winsz > 300:
|
3229
|
+
ntier = 1
|
3230
|
+
bufsz = winsz // 5
|
3231
|
+
slp = 0.2
|
3232
|
+
else:
|
3233
|
+
ntier = 2
|
3234
|
+
bufsz = winsz = slp = 1
|
3235
|
+
|
3236
|
+
if tier != ntier:
|
3237
|
+
tier = ntier
|
3238
|
+
self.log("moved to tier %d (%s)" % (tier, tiers[tier]))
|
3239
|
+
|
3240
|
+
try:
|
3241
|
+
with open(ap_data, "rb", self.args.iobuf) as f:
|
3242
|
+
f.seek(lower)
|
3243
|
+
page = f.read(min(winsz, data_end - lower, upper - lower))
|
3244
|
+
if not page:
|
3245
|
+
raise Exception("got 0 bytes (EOF?)")
|
3246
|
+
except Exception as ex:
|
3247
|
+
self.log("pipe: read failed at %.2f MiB: %s" % (lower / M, ex), 3)
|
3248
|
+
with self.u2mutex:
|
3249
|
+
self.pipes.c.pop(req_path, None)
|
3250
|
+
spins += 1
|
3251
|
+
if spins > 3:
|
3252
|
+
raise Pebkac(500, "file became unreadable")
|
3253
|
+
time.sleep(2)
|
3254
|
+
continue
|
3255
|
+
|
3256
|
+
spins = 0
|
3257
|
+
pofs = 0
|
3258
|
+
while pofs < len(page):
|
3259
|
+
if slp:
|
3260
|
+
time.sleep(slp)
|
3261
|
+
|
3262
|
+
try:
|
3263
|
+
buf = page[pofs : pofs + bufsz]
|
3264
|
+
self.s.sendall(buf)
|
3265
|
+
zi = len(buf)
|
3266
|
+
remains -= zi
|
3267
|
+
lower += zi
|
3268
|
+
pofs += zi
|
3269
|
+
except:
|
3270
|
+
broken = True
|
3271
|
+
break
|
3272
|
+
|
3273
|
+
if lower < upper and not broken:
|
3274
|
+
with open(req_path, "rb") as f:
|
3275
|
+
remains = sendfile_py(self.log, lower, upper, f, self.s, wr_sz, wr_slp)
|
3276
|
+
|
3277
|
+
spd = self._spd((upper - lower) - remains)
|
3278
|
+
if self.do_log:
|
3279
|
+
self.log("{}, {}".format(logmsg, spd))
|
3280
|
+
|
3281
|
+
return not broken
|
3282
|
+
|
3114
3283
|
def tx_zip(
|
3115
3284
|
self,
|
3116
3285
|
fmt ,
|
@@ -3748,7 +3917,7 @@ class HttpCli(object):
|
|
3748
3917
|
if not allvols:
|
3749
3918
|
ret = [{"kinshi": 1}]
|
3750
3919
|
|
3751
|
-
jtxt = '{"u":%s,"c":%s}' % (uret, json.dumps(ret,
|
3920
|
+
jtxt = '{"u":%s,"c":%s}' % (uret, json.dumps(ret, separators=(",\n", ": ")))
|
3752
3921
|
zi = len(uret.split('\n"pd":')) - 1
|
3753
3922
|
self.log("%s #%d+%d %.2fsec" % (lm, zi, len(ret), time.time() - t0))
|
3754
3923
|
self.reply(jtxt.encode("utf-8", "replace"), mime="application/json")
|
@@ -4027,7 +4196,9 @@ class HttpCli(object):
|
|
4027
4196
|
):
|
4028
4197
|
return self.tx_md(vn, abspath)
|
4029
4198
|
|
4030
|
-
return self.tx_file(
|
4199
|
+
return self.tx_file(
|
4200
|
+
abspath, None if st.st_size or "nopipe" in vn.flags else vn.realpath
|
4201
|
+
)
|
4031
4202
|
|
4032
4203
|
elif is_dir and not self.can_read:
|
4033
4204
|
if self._use_dirkey(abspath):
|
@@ -61,6 +61,7 @@ from .u2idx import U2idx
|
|
61
61
|
from .util import (
|
62
62
|
E_SCK,
|
63
63
|
FHC,
|
64
|
+
CachedDict,
|
64
65
|
Daemon,
|
65
66
|
Garda,
|
66
67
|
Magician,
|
@@ -126,6 +127,7 @@ class HttpSrv(object):
|
|
126
127
|
self.t_periodic = None
|
127
128
|
|
128
129
|
self.u2fh = FHC()
|
130
|
+
self.pipes = CachedDict(0.2)
|
129
131
|
self.metrics = Metrics(self)
|
130
132
|
self.nreq = 0
|
131
133
|
self.nsus = 0
|