copyparty 1.14.4__tar.gz → 1.15.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 (121) hide show
  1. {copyparty-1.14.4 → copyparty-1.15.0}/PKG-INFO +39 -6
  2. {copyparty-1.14.4 → copyparty-1.15.0}/README.md +38 -5
  3. {copyparty-1.14.4 → copyparty-1.15.0}/copyparty/__main__.py +9 -6
  4. {copyparty-1.14.4 → copyparty-1.15.0}/copyparty/__version__.py +3 -3
  5. {copyparty-1.14.4 → copyparty-1.15.0}/copyparty/authsrv.py +21 -5
  6. {copyparty-1.14.4 → copyparty-1.15.0}/copyparty/cfg.py +9 -7
  7. {copyparty-1.14.4 → copyparty-1.15.0}/copyparty/svchub.py +23 -38
  8. {copyparty-1.14.4 → copyparty-1.15.0}/copyparty/up2k.py +140 -43
  9. {copyparty-1.14.4 → copyparty-1.15.0}/copyparty/util.py +2 -0
  10. {copyparty-1.14.4 → copyparty-1.15.0}/copyparty/web/a/u2c.py +109 -42
  11. copyparty-1.15.0/copyparty/web/browser.css.gz +0 -0
  12. copyparty-1.15.0/copyparty/web/browser.js.gz +0 -0
  13. copyparty-1.15.0/copyparty/web/up2k.js.gz +0 -0
  14. {copyparty-1.14.4 → copyparty-1.15.0}/copyparty/web/util.js.gz +0 -0
  15. {copyparty-1.14.4 → copyparty-1.15.0}/copyparty.egg-info/PKG-INFO +39 -6
  16. copyparty-1.14.4/copyparty/web/browser.css.gz +0 -0
  17. copyparty-1.14.4/copyparty/web/browser.js.gz +0 -0
  18. copyparty-1.14.4/copyparty/web/up2k.js.gz +0 -0
  19. {copyparty-1.14.4 → copyparty-1.15.0}/LICENSE +0 -0
  20. {copyparty-1.14.4 → copyparty-1.15.0}/copyparty/__init__.py +0 -0
  21. {copyparty-1.14.4 → copyparty-1.15.0}/copyparty/bos/__init__.py +0 -0
  22. {copyparty-1.14.4 → copyparty-1.15.0}/copyparty/bos/bos.py +0 -0
  23. {copyparty-1.14.4 → copyparty-1.15.0}/copyparty/bos/path.py +0 -0
  24. {copyparty-1.14.4 → copyparty-1.15.0}/copyparty/broker_mp.py +0 -0
  25. {copyparty-1.14.4 → copyparty-1.15.0}/copyparty/broker_mpw.py +0 -0
  26. {copyparty-1.14.4 → copyparty-1.15.0}/copyparty/broker_thr.py +0 -0
  27. {copyparty-1.14.4 → copyparty-1.15.0}/copyparty/broker_util.py +0 -0
  28. {copyparty-1.14.4 → copyparty-1.15.0}/copyparty/cert.py +0 -0
  29. {copyparty-1.14.4 → copyparty-1.15.0}/copyparty/dxml.py +0 -0
  30. {copyparty-1.14.4 → copyparty-1.15.0}/copyparty/fsutil.py +0 -0
  31. {copyparty-1.14.4 → copyparty-1.15.0}/copyparty/ftpd.py +0 -0
  32. {copyparty-1.14.4 → copyparty-1.15.0}/copyparty/httpcli.py +0 -0
  33. {copyparty-1.14.4 → copyparty-1.15.0}/copyparty/httpconn.py +0 -0
  34. {copyparty-1.14.4 → copyparty-1.15.0}/copyparty/httpsrv.py +0 -0
  35. {copyparty-1.14.4 → copyparty-1.15.0}/copyparty/ico.py +0 -0
  36. {copyparty-1.14.4 → copyparty-1.15.0}/copyparty/mdns.py +0 -0
  37. {copyparty-1.14.4 → copyparty-1.15.0}/copyparty/metrics.py +0 -0
  38. {copyparty-1.14.4 → copyparty-1.15.0}/copyparty/mtag.py +0 -0
  39. {copyparty-1.14.4 → copyparty-1.15.0}/copyparty/multicast.py +0 -0
  40. {copyparty-1.14.4 → copyparty-1.15.0}/copyparty/pwhash.py +0 -0
  41. {copyparty-1.14.4 → copyparty-1.15.0}/copyparty/res/COPYING.txt +0 -0
  42. {copyparty-1.14.4 → copyparty-1.15.0}/copyparty/res/__init__.py +0 -0
  43. {copyparty-1.14.4 → copyparty-1.15.0}/copyparty/res/insecure.pem +0 -0
  44. {copyparty-1.14.4 → copyparty-1.15.0}/copyparty/smbd.py +0 -0
  45. {copyparty-1.14.4 → copyparty-1.15.0}/copyparty/ssdp.py +0 -0
  46. {copyparty-1.14.4 → copyparty-1.15.0}/copyparty/star.py +0 -0
  47. {copyparty-1.14.4 → copyparty-1.15.0}/copyparty/stolen/__init__.py +0 -0
  48. {copyparty-1.14.4 → copyparty-1.15.0}/copyparty/stolen/dnslib/__init__.py +0 -0
  49. {copyparty-1.14.4 → copyparty-1.15.0}/copyparty/stolen/dnslib/bimap.py +0 -0
  50. {copyparty-1.14.4 → copyparty-1.15.0}/copyparty/stolen/dnslib/bit.py +0 -0
  51. {copyparty-1.14.4 → copyparty-1.15.0}/copyparty/stolen/dnslib/buffer.py +0 -0
  52. {copyparty-1.14.4 → copyparty-1.15.0}/copyparty/stolen/dnslib/dns.py +0 -0
  53. {copyparty-1.14.4 → copyparty-1.15.0}/copyparty/stolen/dnslib/label.py +0 -0
  54. {copyparty-1.14.4 → copyparty-1.15.0}/copyparty/stolen/dnslib/lex.py +0 -0
  55. {copyparty-1.14.4 → copyparty-1.15.0}/copyparty/stolen/dnslib/ranges.py +0 -0
  56. {copyparty-1.14.4 → copyparty-1.15.0}/copyparty/stolen/ifaddr/__init__.py +0 -0
  57. {copyparty-1.14.4 → copyparty-1.15.0}/copyparty/stolen/ifaddr/_posix.py +0 -0
  58. {copyparty-1.14.4 → copyparty-1.15.0}/copyparty/stolen/ifaddr/_shared.py +0 -0
  59. {copyparty-1.14.4 → copyparty-1.15.0}/copyparty/stolen/ifaddr/_win32.py +0 -0
  60. {copyparty-1.14.4 → copyparty-1.15.0}/copyparty/stolen/qrcodegen.py +0 -0
  61. {copyparty-1.14.4 → copyparty-1.15.0}/copyparty/stolen/surrogateescape.py +0 -0
  62. {copyparty-1.14.4 → copyparty-1.15.0}/copyparty/sutil.py +0 -0
  63. {copyparty-1.14.4 → copyparty-1.15.0}/copyparty/szip.py +0 -0
  64. {copyparty-1.14.4 → copyparty-1.15.0}/copyparty/tcpsrv.py +0 -0
  65. {copyparty-1.14.4 → copyparty-1.15.0}/copyparty/tftpd.py +0 -0
  66. {copyparty-1.14.4 → copyparty-1.15.0}/copyparty/th_cli.py +0 -0
  67. {copyparty-1.14.4 → copyparty-1.15.0}/copyparty/th_srv.py +0 -0
  68. {copyparty-1.14.4 → copyparty-1.15.0}/copyparty/u2idx.py +0 -0
  69. {copyparty-1.14.4 → copyparty-1.15.0}/copyparty/web/a/__init__.py +0 -0
  70. {copyparty-1.14.4 → copyparty-1.15.0}/copyparty/web/a/partyfuse.py +0 -0
  71. {copyparty-1.14.4 → copyparty-1.15.0}/copyparty/web/a/webdav-cfg.bat +0 -0
  72. {copyparty-1.14.4 → copyparty-1.15.0}/copyparty/web/baguettebox.js.gz +0 -0
  73. {copyparty-1.14.4 → copyparty-1.15.0}/copyparty/web/browser.html +0 -0
  74. {copyparty-1.14.4 → copyparty-1.15.0}/copyparty/web/browser2.html +0 -0
  75. {copyparty-1.14.4 → copyparty-1.15.0}/copyparty/web/cf.html +0 -0
  76. {copyparty-1.14.4 → copyparty-1.15.0}/copyparty/web/dbg-audio.js.gz +0 -0
  77. {copyparty-1.14.4 → copyparty-1.15.0}/copyparty/web/dd/2.png +0 -0
  78. {copyparty-1.14.4 → copyparty-1.15.0}/copyparty/web/dd/3.png +0 -0
  79. {copyparty-1.14.4 → copyparty-1.15.0}/copyparty/web/dd/4.png +0 -0
  80. {copyparty-1.14.4 → copyparty-1.15.0}/copyparty/web/dd/5.png +0 -0
  81. {copyparty-1.14.4 → copyparty-1.15.0}/copyparty/web/dd/__init__.py +0 -0
  82. {copyparty-1.14.4 → copyparty-1.15.0}/copyparty/web/deps/__init__.py +0 -0
  83. {copyparty-1.14.4 → copyparty-1.15.0}/copyparty/web/deps/busy.mp3.gz +0 -0
  84. {copyparty-1.14.4 → copyparty-1.15.0}/copyparty/web/deps/easymde.css.gz +0 -0
  85. {copyparty-1.14.4 → copyparty-1.15.0}/copyparty/web/deps/easymde.js.gz +0 -0
  86. {copyparty-1.14.4 → copyparty-1.15.0}/copyparty/web/deps/marked.js.gz +0 -0
  87. {copyparty-1.14.4 → copyparty-1.15.0}/copyparty/web/deps/mini-fa.css.gz +0 -0
  88. {copyparty-1.14.4 → copyparty-1.15.0}/copyparty/web/deps/mini-fa.woff +0 -0
  89. {copyparty-1.14.4 → copyparty-1.15.0}/copyparty/web/deps/prism.css.gz +0 -0
  90. {copyparty-1.14.4 → copyparty-1.15.0}/copyparty/web/deps/prism.js.gz +0 -0
  91. {copyparty-1.14.4 → copyparty-1.15.0}/copyparty/web/deps/prismd.css.gz +0 -0
  92. {copyparty-1.14.4 → copyparty-1.15.0}/copyparty/web/deps/scp.woff2 +0 -0
  93. {copyparty-1.14.4 → copyparty-1.15.0}/copyparty/web/deps/sha512.ac.js.gz +0 -0
  94. {copyparty-1.14.4 → copyparty-1.15.0}/copyparty/web/deps/sha512.hw.js.gz +0 -0
  95. {copyparty-1.14.4 → copyparty-1.15.0}/copyparty/web/md.css.gz +0 -0
  96. {copyparty-1.14.4 → copyparty-1.15.0}/copyparty/web/md.html +0 -0
  97. {copyparty-1.14.4 → copyparty-1.15.0}/copyparty/web/md.js.gz +0 -0
  98. {copyparty-1.14.4 → copyparty-1.15.0}/copyparty/web/md2.css.gz +0 -0
  99. {copyparty-1.14.4 → copyparty-1.15.0}/copyparty/web/md2.js.gz +0 -0
  100. {copyparty-1.14.4 → copyparty-1.15.0}/copyparty/web/mde.css.gz +0 -0
  101. {copyparty-1.14.4 → copyparty-1.15.0}/copyparty/web/mde.html +0 -0
  102. {copyparty-1.14.4 → copyparty-1.15.0}/copyparty/web/mde.js.gz +0 -0
  103. {copyparty-1.14.4 → copyparty-1.15.0}/copyparty/web/msg.css.gz +0 -0
  104. {copyparty-1.14.4 → copyparty-1.15.0}/copyparty/web/msg.html +0 -0
  105. {copyparty-1.14.4 → copyparty-1.15.0}/copyparty/web/shares.css.gz +0 -0
  106. {copyparty-1.14.4 → copyparty-1.15.0}/copyparty/web/shares.html +0 -0
  107. {copyparty-1.14.4 → copyparty-1.15.0}/copyparty/web/shares.js.gz +0 -0
  108. {copyparty-1.14.4 → copyparty-1.15.0}/copyparty/web/splash.css.gz +0 -0
  109. {copyparty-1.14.4 → copyparty-1.15.0}/copyparty/web/splash.html +0 -0
  110. {copyparty-1.14.4 → copyparty-1.15.0}/copyparty/web/splash.js.gz +0 -0
  111. {copyparty-1.14.4 → copyparty-1.15.0}/copyparty/web/svcs.html +0 -0
  112. {copyparty-1.14.4 → copyparty-1.15.0}/copyparty/web/svcs.js.gz +0 -0
  113. {copyparty-1.14.4 → copyparty-1.15.0}/copyparty/web/ui.css.gz +0 -0
  114. {copyparty-1.14.4 → copyparty-1.15.0}/copyparty/web/w.hash.js.gz +0 -0
  115. {copyparty-1.14.4 → copyparty-1.15.0}/copyparty.egg-info/SOURCES.txt +0 -0
  116. {copyparty-1.14.4 → copyparty-1.15.0}/copyparty.egg-info/dependency_links.txt +0 -0
  117. {copyparty-1.14.4 → copyparty-1.15.0}/copyparty.egg-info/entry_points.txt +0 -0
  118. {copyparty-1.14.4 → copyparty-1.15.0}/copyparty.egg-info/requires.txt +0 -0
  119. {copyparty-1.14.4 → copyparty-1.15.0}/copyparty.egg-info/top_level.txt +0 -0
  120. {copyparty-1.14.4 → copyparty-1.15.0}/pyproject.toml +0 -0
  121. {copyparty-1.14.4 → copyparty-1.15.0}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: copyparty
3
- Version: 1.14.4
3
+ Version: 1.15.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
@@ -119,7 +119,8 @@ turn almost any device into a file server with resumable uploads/downloads using
119
119
  * [smb server](#smb-server) - unsafe, slow, not recommended for wan
120
120
  * [browser ux](#browser-ux) - tweaking the ui
121
121
  * [opengraph](#opengraph) - discord and social-media embeds
122
- * [file indexing](#file-indexing) - enables dedup and music search ++
122
+ * [file deduplication](#file-deduplication) - enable symlink-based upload deduplication
123
+ * [file indexing](#file-indexing) - enable music search, upload-undo, and better dedup
123
124
  * [exclude-patterns](#exclude-patterns) - to save some time
124
125
  * [filesystem guards](#filesystem-guards) - avoid traversing into other filesystems
125
126
  * [periodic rescan](#periodic-rescan) - filesystem monitoring
@@ -1209,9 +1210,41 @@ NOTE: because discord (and maybe others) strip query args such as `?raw` in open
1209
1210
  if you want to entirely replace the copyparty response with your own jinja2 template, give the template filepath to `--og-tpl` or volflag `og_tpl` (all members of `HttpCli` are available through the `this` object)
1210
1211
 
1211
1212
 
1213
+ ## file deduplication
1214
+
1215
+ enable symlink-based upload deduplication globally with `--dedup` or per-volume with volflag `dedup`
1216
+
1217
+ when someone tries to upload a file that already exists on the server, the upload will be politely declined and a symlink is created instead, pointing to the nearest copy on disk, thus reducinc disk space usage
1218
+
1219
+ **warning:** when enabling dedup, you should also:
1220
+ * enable indexing with `-e2dsa` or volflag `e2dsa` (see [file indexing](#file-indexing) section below); strongly recommended
1221
+ * ...and/or `--hardlink-only` to use hardlink-based deduplication instead of symlinks; see explanation below
1222
+
1223
+ it will not be safe to rename/delete files if you only enable dedup and none of the above; if you enable indexing then it is not *necessary* to also do hardlinks (but you may still want to)
1224
+
1225
+ by default, deduplication is done based on symlinks (symbolic links); these are tiny files which are pointers to the nearest full copy of the file
1226
+
1227
+ you can choose to use hardlinks instead of softlinks, globally with `--hardlink-only` or volflag `hardlinkonly`;
1228
+
1229
+ advantages of using hardlinks:
1230
+ * hardlinks are more compatible with other software; they behave entirely like regular files
1231
+ * you can safely move and rename files using other file managers
1232
+ * symlinks need to be managed by copyparty to ensure the destinations remain correct
1233
+
1234
+ advantages of using symlinks (default):
1235
+ * each symlink can have its own last-modified timestamp, but a single timestamp is shared by all hardlinks
1236
+ * symlinks make it more obvious to other software that the file is not a regular file, so this can be less dangerous
1237
+ * hardlinks look like regular files, so other software may assume they are safe to edit without affecting the other copies
1238
+
1239
+ **warning:** if you edit the contents of a deduplicated file, then you will also edit all other copies of that file! This is especially surprising with hardlinks, because they look like regular files, but that same file exists in multiple locations
1240
+
1241
+ global-option `--xlink` / volflag `xlink` additionally enables deduplication across volumes, but this is probably buggy and not recommended
1242
+
1243
+
1244
+
1212
1245
  ## file indexing
1213
1246
 
1214
- enables dedup and music search ++
1247
+ enable music search, upload-undo, and better dedup
1215
1248
 
1216
1249
  file indexing relies on two database tables, the up2k filetree (`-e2d`) and the metadata tags (`-e2t`), stored in `.hist/up2k.db`. Configuration can be done through arguments, volflags, or a mix of both.
1217
1250
 
@@ -1225,7 +1258,6 @@ through arguments:
1225
1258
  * `-e2v` verfies file integrity at startup, comparing hashes from the db
1226
1259
  * `-e2vu` patches the database with the new hashes from the filesystem
1227
1260
  * `-e2vp` panics and kills copyparty instead
1228
- * `--xlink` enables deduplication across volumes
1229
1261
 
1230
1262
  the same arguments can be set as volflags, in addition to `d2d`, `d2ds`, `d2t`, `d2ts`, `d2v` for disabling:
1231
1263
  * `-v ~/music::r:c,e2ds,e2tsr` does a full reindex of everything on startup
@@ -1238,7 +1270,6 @@ note:
1238
1270
  * upload-times can be displayed in the file listing by enabling the `.up_at` metadata key, either globally with `-e2d -mte +.up_at` or per-volume with volflags `e2d,mte=+.up_at` (will have a ~17% performance impact on directory listings)
1239
1271
  * `e2tsr` is probably always overkill, since `e2ds`/`e2dsa` would pick up any file modifications and `e2ts` would then reindex those, unless there is a new copyparty version with new parsers and the release note says otherwise
1240
1272
  * the rescan button in the admin panel has no effect unless the volume has `-e2ds` or higher
1241
- * deduplication is possible on windows if you run copyparty as administrator (not saying you should!)
1242
1273
 
1243
1274
  ### exclude-patterns
1244
1275
 
@@ -1978,6 +2009,8 @@ below are some tweaks roughly ordered by usefulness:
1978
2009
  * `-q` disables logging and can help a bunch, even when combined with `-lo` to redirect logs to file
1979
2010
  * `--hist` pointing to a fast location (ssd) will make directory listings and searches faster when `-e2d` or `-e2t` is set
1980
2011
  * and also makes thumbnails load faster, regardless of e2d/e2t
2012
+ * `--dedup` enables deduplication and thus avoids writing to the HDD if someone uploads a dupe
2013
+ * `--safe-dedup 1` makes deduplication much faster during upload by skipping verification of file contents; safe if there is no other software editing/moving the files in the volumes
1981
2014
  * `--no-hash .` when indexing a network-disk if you don't care about the actual filehashes and only want the names/tags searchable
1982
2015
  * 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`
1983
2016
  * `--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)
@@ -2032,7 +2065,7 @@ safety profiles:
2032
2065
  * `--hardlink` creates hardlinks instead of symlinks when deduplicating uploads, which is less maintenance
2033
2066
  * however note if you edit one file it will also affect the other copies
2034
2067
  * `--vague-403` returns a "404 not found" instead of "401 unauthorized" which is a common enterprise meme
2035
- * `--nih` removes the server hostname from directory listings
2068
+ * `-nih` removes the server hostname from directory listings
2036
2069
 
2037
2070
  * option `-sss` is a shortcut for the above plus:
2038
2071
  * `--no-dav` disables webdav support
@@ -65,7 +65,8 @@ turn almost any device into a file server with resumable uploads/downloads using
65
65
  * [smb server](#smb-server) - unsafe, slow, not recommended for wan
66
66
  * [browser ux](#browser-ux) - tweaking the ui
67
67
  * [opengraph](#opengraph) - discord and social-media embeds
68
- * [file indexing](#file-indexing) - enables dedup and music search ++
68
+ * [file deduplication](#file-deduplication) - enable symlink-based upload deduplication
69
+ * [file indexing](#file-indexing) - enable music search, upload-undo, and better dedup
69
70
  * [exclude-patterns](#exclude-patterns) - to save some time
70
71
  * [filesystem guards](#filesystem-guards) - avoid traversing into other filesystems
71
72
  * [periodic rescan](#periodic-rescan) - filesystem monitoring
@@ -1155,9 +1156,41 @@ NOTE: because discord (and maybe others) strip query args such as `?raw` in open
1155
1156
  if you want to entirely replace the copyparty response with your own jinja2 template, give the template filepath to `--og-tpl` or volflag `og_tpl` (all members of `HttpCli` are available through the `this` object)
1156
1157
 
1157
1158
 
1159
+ ## file deduplication
1160
+
1161
+ enable symlink-based upload deduplication globally with `--dedup` or per-volume with volflag `dedup`
1162
+
1163
+ when someone tries to upload a file that already exists on the server, the upload will be politely declined and a symlink is created instead, pointing to the nearest copy on disk, thus reducinc disk space usage
1164
+
1165
+ **warning:** when enabling dedup, you should also:
1166
+ * enable indexing with `-e2dsa` or volflag `e2dsa` (see [file indexing](#file-indexing) section below); strongly recommended
1167
+ * ...and/or `--hardlink-only` to use hardlink-based deduplication instead of symlinks; see explanation below
1168
+
1169
+ it will not be safe to rename/delete files if you only enable dedup and none of the above; if you enable indexing then it is not *necessary* to also do hardlinks (but you may still want to)
1170
+
1171
+ by default, deduplication is done based on symlinks (symbolic links); these are tiny files which are pointers to the nearest full copy of the file
1172
+
1173
+ you can choose to use hardlinks instead of softlinks, globally with `--hardlink-only` or volflag `hardlinkonly`;
1174
+
1175
+ advantages of using hardlinks:
1176
+ * hardlinks are more compatible with other software; they behave entirely like regular files
1177
+ * you can safely move and rename files using other file managers
1178
+ * symlinks need to be managed by copyparty to ensure the destinations remain correct
1179
+
1180
+ advantages of using symlinks (default):
1181
+ * each symlink can have its own last-modified timestamp, but a single timestamp is shared by all hardlinks
1182
+ * symlinks make it more obvious to other software that the file is not a regular file, so this can be less dangerous
1183
+ * hardlinks look like regular files, so other software may assume they are safe to edit without affecting the other copies
1184
+
1185
+ **warning:** if you edit the contents of a deduplicated file, then you will also edit all other copies of that file! This is especially surprising with hardlinks, because they look like regular files, but that same file exists in multiple locations
1186
+
1187
+ global-option `--xlink` / volflag `xlink` additionally enables deduplication across volumes, but this is probably buggy and not recommended
1188
+
1189
+
1190
+
1158
1191
  ## file indexing
1159
1192
 
1160
- enables dedup and music search ++
1193
+ enable music search, upload-undo, and better dedup
1161
1194
 
1162
1195
  file indexing relies on two database tables, the up2k filetree (`-e2d`) and the metadata tags (`-e2t`), stored in `.hist/up2k.db`. Configuration can be done through arguments, volflags, or a mix of both.
1163
1196
 
@@ -1171,7 +1204,6 @@ through arguments:
1171
1204
  * `-e2v` verfies file integrity at startup, comparing hashes from the db
1172
1205
  * `-e2vu` patches the database with the new hashes from the filesystem
1173
1206
  * `-e2vp` panics and kills copyparty instead
1174
- * `--xlink` enables deduplication across volumes
1175
1207
 
1176
1208
  the same arguments can be set as volflags, in addition to `d2d`, `d2ds`, `d2t`, `d2ts`, `d2v` for disabling:
1177
1209
  * `-v ~/music::r:c,e2ds,e2tsr` does a full reindex of everything on startup
@@ -1184,7 +1216,6 @@ note:
1184
1216
  * upload-times can be displayed in the file listing by enabling the `.up_at` metadata key, either globally with `-e2d -mte +.up_at` or per-volume with volflags `e2d,mte=+.up_at` (will have a ~17% performance impact on directory listings)
1185
1217
  * `e2tsr` is probably always overkill, since `e2ds`/`e2dsa` would pick up any file modifications and `e2ts` would then reindex those, unless there is a new copyparty version with new parsers and the release note says otherwise
1186
1218
  * the rescan button in the admin panel has no effect unless the volume has `-e2ds` or higher
1187
- * deduplication is possible on windows if you run copyparty as administrator (not saying you should!)
1188
1219
 
1189
1220
  ### exclude-patterns
1190
1221
 
@@ -1924,6 +1955,8 @@ below are some tweaks roughly ordered by usefulness:
1924
1955
  * `-q` disables logging and can help a bunch, even when combined with `-lo` to redirect logs to file
1925
1956
  * `--hist` pointing to a fast location (ssd) will make directory listings and searches faster when `-e2d` or `-e2t` is set
1926
1957
  * and also makes thumbnails load faster, regardless of e2d/e2t
1958
+ * `--dedup` enables deduplication and thus avoids writing to the HDD if someone uploads a dupe
1959
+ * `--safe-dedup 1` makes deduplication much faster during upload by skipping verification of file contents; safe if there is no other software editing/moving the files in the volumes
1927
1960
  * `--no-hash .` when indexing a network-disk if you don't care about the actual filehashes and only want the names/tags searchable
1928
1961
  * 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`
1929
1962
  * `--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)
@@ -1978,7 +2011,7 @@ safety profiles:
1978
2011
  * `--hardlink` creates hardlinks instead of symlinks when deduplicating uploads, which is less maintenance
1979
2012
  * however note if you edit one file it will also affect the other copies
1980
2013
  * `--vague-403` returns a "404 not found" instead of "401 unauthorized" which is a common enterprise meme
1981
- * `--nih` removes the server hostname from directory listings
2014
+ * `-nih` removes the server hostname from directory listings
1982
2015
 
1983
2016
  * option `-sss` is a shortcut for the above plus:
1984
2017
  * `--no-dav` disables webdav support
@@ -986,9 +986,10 @@ def add_upload(ap):
986
986
  ap2.add_argument("--reg-cap", metavar="N", type=int, default=38400, help="max number of uploads to keep in memory when running without \033[33m-e2d\033[0m; roughly 1 MiB RAM per 600")
987
987
  ap2.add_argument("--no-fpool", action="store_true", help="disable file-handle pooling -- instead, repeatedly close and reopen files during upload (bad idea to enable this on windows and/or cow filesystems)")
988
988
  ap2.add_argument("--use-fpool", action="store_true", help="force file-handle pooling, even when it might be dangerous (multiprocessing, filesystems lacking sparse-files support, ...)")
989
- ap2.add_argument("--hardlink", action="store_true", help="prefer hardlinks instead of symlinks when possible (within same filesystem) (volflag=hardlink)")
990
- ap2.add_argument("--never-symlink", action="store_true", help="do not fallback to symlinks when a hardlink cannot be made (volflag=neversymlink)")
991
- ap2.add_argument("--no-dedup", action="store_true", help="disable symlink/hardlink creation; copy file contents instead (volflag=copydupes)")
989
+ ap2.add_argument("--dedup", action="store_true", help="enable symlink-based upload deduplication (volflag=dedup)")
990
+ ap2.add_argument("--safe-dedup", metavar="N", type=int, default=50, help="how careful to be when deduplicating files; [\033[32m1\033[0m] = just verify the filesize, [\033[32m50\033[0m] = verify file contents have not been altered (volflag=safededup)")
991
+ ap2.add_argument("--hardlink", action="store_true", help="enable hardlink-based dedup; will fallback on symlinks when that is impossible (across filesystems) (volflag=hardlink)")
992
+ ap2.add_argument("--hardlink-only", action="store_true", help="do not fallback to symlinks when a hardlink cannot be made (volflag=hardlinkonly)")
992
993
  ap2.add_argument("--no-dupe", action="store_true", help="reject duplicate files during upload; only matches within the same volume (volflag=nodupe)")
993
994
  ap2.add_argument("--no-snap", action="store_true", help="disable snapshots -- forget unfinished uploads on shutdown; don't create .hist/up2k.snap files -- abandoned/interrupted uploads must be cleaned up manually")
994
995
  ap2.add_argument("--snap-wri", metavar="SEC", type=int, default=300, help="write upload state to ./hist/up2k.snap every \033[33mSEC\033[0m seconds; allows resuming incomplete uploads after a server crash")
@@ -1279,6 +1280,7 @@ def add_logging(ap):
1279
1280
  ap2.add_argument("--ansi", action="store_true", help="force colors; overrides environment-variable NO_COLOR")
1280
1281
  ap2.add_argument("--no-logflush", action="store_true", help="don't flush the logfile after each write; tiny bit faster")
1281
1282
  ap2.add_argument("--no-voldump", action="store_true", help="do not list volumes and permissions on startup")
1283
+ ap2.add_argument("--log-utc", action="store_true", help="do not use local timezone; assume the TZ env-var is UTC (tiny bit faster)")
1282
1284
  ap2.add_argument("--log-tdec", metavar="N", type=int, default=3, help="timestamp resolution / number of timestamp decimals")
1283
1285
  ap2.add_argument("--log-badpwd", metavar="N", type=int, default=1, help="log failed login attempt passwords: 0=terse, 1=plaintext, 2=hashed")
1284
1286
  ap2.add_argument("--log-conn", action="store_true", help="debug: print tcp-server msgs")
@@ -1337,7 +1339,7 @@ def add_transcoding(ap):
1337
1339
  def add_db_general(ap, hcores):
1338
1340
  noidx = APPLESAN_TXT if MACOS else ""
1339
1341
  ap2 = ap.add_argument_group('general db options')
1340
- ap2.add_argument("-e2d", action="store_true", help="enable up2k database, making files searchable + enables upload deduplication")
1342
+ ap2.add_argument("-e2d", action="store_true", help="enable up2k database; this enables file search, upload-undo, improves deduplication")
1341
1343
  ap2.add_argument("-e2ds", action="store_true", help="scan writable folders for new files on startup; sets \033[33m-e2d\033[0m")
1342
1344
  ap2.add_argument("-e2dsa", action="store_true", help="scans all folders on startup; sets \033[33m-e2ds\033[0m")
1343
1345
  ap2.add_argument("-e2v", action="store_true", help="verify file integrity; rehash all files and compare with db")
@@ -1350,7 +1352,7 @@ def add_db_general(ap, hcores):
1350
1352
  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)")
1351
1353
  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)")
1352
1354
  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)")
1353
- ap2.add_argument("--xlink", action="store_true", help="on upload: check all volumes for dupes, not just the target volume (volflag=xlink)")
1355
+ ap2.add_argument("--xlink", action="store_true", help="on upload: check all volumes for dupes, not just the target volume (probably buggy, not recommended) (volflag=xlink)")
1354
1356
  ap2.add_argument("--hash-mt", metavar="CORES", type=int, default=hcores, help="num cpu cores to use for file hashing; set 0 or 1 for single-core hashing")
1355
1357
  ap2.add_argument("--re-maxage", metavar="SEC", type=int, default=0, help="rescan filesystem for changes every \033[33mSEC\033[0m seconds; 0=off (volflag=scan)")
1356
1358
  ap2.add_argument("--db-act", metavar="SEC", type=float, default=10.0, help="defer any scheduled volume reindexing until \033[33mSEC\033[0m seconds after last db write (uploads, renames, ...)")
@@ -1613,6 +1615,7 @@ def main(argv = None, rsrc = None) :
1613
1615
  ("--hdr-au-usr", "--idp-h-usr"),
1614
1616
  ("--idp-h-sep", "--idp-gsep"),
1615
1617
  ("--th-no-crop", "--th-crop=n"),
1618
+ ("--never-symlink", "--hardlink-only"),
1616
1619
  ]
1617
1620
  for dk, nk in deprecated:
1618
1621
  idx = -1
@@ -1637,7 +1640,7 @@ def main(argv = None, rsrc = None) :
1637
1640
  argv.extend(["--qr"])
1638
1641
  if ANYWIN or not os.geteuid():
1639
1642
  # win10 allows symlinks if admin; can be unexpected
1640
- argv.extend(["-p80,443,3923", "--ign-ebind", "--no-dedup"])
1643
+ argv.extend(["-p80,443,3923", "--ign-ebind"])
1641
1644
  except:
1642
1645
  pass
1643
1646
 
@@ -1,8 +1,8 @@
1
1
  # coding: utf-8
2
2
 
3
- VERSION = (1, 14, 4)
4
- CODENAME = "one step forward"
5
- BUILD_DT = (2024, 9, 2)
3
+ VERSION = (1, 15, 0)
4
+ CODENAME = "fill the drives"
5
+ BUILD_DT = (2024, 9, 8)
6
6
 
7
7
  S_VERSION = ".".join(map(str, VERSION))
8
8
  S_BUILD_DT = "{0:04d}-{1:02d}-{2:02d}".format(*BUILD_DT)
@@ -1884,6 +1884,11 @@ class AuthSrv(object):
1884
1884
  if len(zs) == 3: # fc5 => ffcc55
1885
1885
  vol.flags["tcolor"] = "".join([x * 2 for x in zs])
1886
1886
 
1887
+ if vol.flags.get("neversymlink"):
1888
+ vol.flags["hardlinkonly"] = True # was renamed
1889
+ if vol.flags.get("hardlinkonly"):
1890
+ vol.flags["hardlink"] = True
1891
+
1887
1892
  for k1, k2 in IMPLICATIONS:
1888
1893
  if k1 in vol.flags:
1889
1894
  vol.flags[k2] = True
@@ -1988,9 +1993,6 @@ class AuthSrv(object):
1988
1993
  for x in drop:
1989
1994
  vol.flags.pop(x)
1990
1995
 
1991
- if vol.flags.get("neversymlink") and not vol.flags.get("hardlink"):
1992
- vol.flags["copydupes"] = True
1993
-
1994
1996
  # verify tags mentioned by -mt[mp] are used by -mte
1995
1997
  local_mtp = {}
1996
1998
  local_only_mtp = {}
@@ -2069,6 +2071,8 @@ class AuthSrv(object):
2069
2071
 
2070
2072
  have_e2d = False
2071
2073
  have_e2t = False
2074
+ have_dedup = False
2075
+ unsafe_dedup = []
2072
2076
  t = "volumes and permissions:\n"
2073
2077
  for zv in vfs.all_vols.values():
2074
2078
  if not self.warn_anonwrite or verbosity < 5:
@@ -2101,6 +2105,11 @@ class AuthSrv(object):
2101
2105
  if "e2t" in zv.flags:
2102
2106
  have_e2t = True
2103
2107
 
2108
+ if "dedup" in zv.flags:
2109
+ have_dedup = True
2110
+ if "e2d" not in zv.flags and "hardlink" not in zv.flags:
2111
+ unsafe_dedup.append("/" + zv.vpath)
2112
+
2104
2113
  t += "\n"
2105
2114
 
2106
2115
  if self.warn_anonwrite and verbosity > 4:
@@ -2113,10 +2122,17 @@ class AuthSrv(object):
2113
2122
  self.log("\n\033[{}\033[0m\n".format(t))
2114
2123
 
2115
2124
  if not have_e2t:
2116
- t = "hint: argument -e2ts enables multimedia indexing (artist/title/...)"
2125
+ t = "hint: enable multimedia indexing (artist/title/...) with argument -e2ts"
2117
2126
  self.log(t, 6)
2118
2127
  else:
2119
- t = "hint: argument -e2dsa enables searching, upload-undo, and better deduplication"
2128
+ t = "hint: enable searching and upload-undo with argument -e2dsa"
2129
+ self.log(t, 6)
2130
+
2131
+ if unsafe_dedup:
2132
+ t = "WARNING: symlink-based deduplication is enabled for some volumes, but without indexing. Please enable -e2dsa and/or --hardlink to avoid problems when moving/renaming files. Affected volumes: %s"
2133
+ self.log(t % (", ".join(unsafe_dedup)), 3)
2134
+ elif not have_dedup:
2135
+ t = "hint: enable upload deduplication with --dedup (but see readme for consequences)"
2120
2136
  self.log(t, 6)
2121
2137
 
2122
2138
  zv, _ = vfs.get("/", "*", False, False)
@@ -12,8 +12,7 @@ def vf_bmap() :
12
12
  "dav_auth": "davauth",
13
13
  "dav_rt": "davrt",
14
14
  "ed": "dots",
15
- "never_symlink": "neversymlink",
16
- "no_dedup": "copydupes",
15
+ "hardlink_only": "hardlinkonly",
17
16
  "no_dupe": "nodupe",
18
17
  "no_forget": "noforget",
19
18
  "no_pipe": "nopipe",
@@ -23,6 +22,7 @@ def vf_bmap() :
23
22
  "no_athumb": "dathumb",
24
23
  }
25
24
  for k in (
25
+ "dedup",
26
26
  "dotsrch",
27
27
  "e2d",
28
28
  "e2ds",
@@ -58,6 +58,7 @@ def vf_vmap() :
58
58
  "no_hash": "nohash",
59
59
  "no_idx": "noidx",
60
60
  "re_maxage": "scan",
61
+ "safe_dedup": "safededup",
61
62
  "th_convt": "convt",
62
63
  "th_size": "thsize",
63
64
  "th_crop": "crop",
@@ -129,10 +130,11 @@ permdescs = {
129
130
 
130
131
  flagcats = {
131
132
  "uploads, general": {
133
+ "dedup": "enable symlink-based file deduplication",
134
+ "hardlink": "enable hardlink-based file deduplication,\nwith fallback on symlinks when that is impossible",
135
+ "hardlinkonly": "dedup with hardlink only, never symlink;\nmake a full copy if hardlink is impossible",
136
+ "safededup": "verify on-disk data before using it for dedup",
132
137
  "nodupe": "rejects existing files (instead of symlinking them)",
133
- "hardlink": "does dedup with hardlinks instead of symlinks",
134
- "neversymlink": "disables symlink fallback; full copy instead",
135
- "copydupes": "disables dedup, always saves full copies of dupes",
136
138
  "sparse": "force use of sparse files, mainly for s3-backed storage",
137
139
  "daw": "enable full WebDAV write support (dangerous);\nPUT-operations will now \033[1;31mOVERWRITE\033[0;35m existing files",
138
140
  "nosub": "forces all uploads into the top folder of the vfs",
@@ -159,7 +161,7 @@ flagcats = {
159
161
  "lifetime=3600": "uploads are deleted after 1 hour",
160
162
  },
161
163
  "database, general": {
162
- "e2d": "enable database; makes files searchable + enables upload dedup",
164
+ "e2d": "enable database; makes files searchable + enables upload-undo",
163
165
  "e2ds": "scan writable folders for new files on startup; also sets -e2d",
164
166
  "e2dsa": "scans all folders for new files on startup; also sets -e2d",
165
167
  "e2t": "enable multimedia indexing; makes it possible to search for tags",
@@ -177,7 +179,7 @@ flagcats = {
177
179
  "noforget": "don't forget files when deleted from disk",
178
180
  "fat32": "avoid excessive reindexing on android sdcardfs",
179
181
  "dbd=[acid|swal|wal|yolo]": "database speed-durability tradeoff",
180
- "xlink": "cross-volume dupe detection / linking",
182
+ "xlink": "cross-volume dupe detection / linking (dangerous)",
181
183
  "xdev": "do not descend into other filesystems",
182
184
  "xvol": "do not follow symlinks leaving the volume root",
183
185
  "dotsrch": "show dotfiles in search results",
@@ -3,7 +3,6 @@ from __future__ import print_function, unicode_literals
3
3
 
4
4
  import argparse
5
5
  import base64
6
- import calendar
7
6
  import errno
8
7
  import gzip
9
8
  import logging
@@ -16,7 +15,7 @@ import string
16
15
  import sys
17
16
  import threading
18
17
  import time
19
- from datetime import datetime, timedelta
18
+ from datetime import datetime
20
19
 
21
20
  # from inspect import currentframe
22
21
  # print(currentframe().f_lineno)
@@ -98,6 +97,7 @@ class SvcHub(object):
98
97
  self.argv = argv
99
98
  self.E = args.E
100
99
  self.no_ansi = args.no_ansi
100
+ self.tz = UTC if args.log_utc else None
101
101
  self.logf = None
102
102
  self.logf_base_fn = ""
103
103
  self.is_dut = False # running in unittest; always False
@@ -112,7 +112,8 @@ class SvcHub(object):
112
112
  self.httpsrv_up = 0
113
113
 
114
114
  self.log_mutex = threading.Lock()
115
- self.next_day = 0
115
+ self.cday = 0
116
+ self.cmon = 0
116
117
  self.tstack = 0.0
117
118
 
118
119
  self.iphash = HMaccas(os.path.join(self.E.cfg, "iphash"), 8)
@@ -785,7 +786,7 @@ class SvcHub(object):
785
786
  self.args.nc = min(self.args.nc, soft // 2)
786
787
 
787
788
  def _logname(self) :
788
- dt = datetime.now(UTC)
789
+ dt = datetime.now(self.tz)
789
790
  fn = str(self.args.lo)
790
791
  for fs in "YmdHMS":
791
792
  fs = "%" + fs
@@ -1058,12 +1059,12 @@ class SvcHub(object):
1058
1059
  return
1059
1060
 
1060
1061
  with self.log_mutex:
1061
- zd = datetime.now(UTC)
1062
+ dt = datetime.now(self.tz)
1062
1063
  ts = self.log_dfmt % (
1063
- zd.year,
1064
- zd.month * 100 + zd.day,
1065
- (zd.hour * 100 + zd.minute) * 100 + zd.second,
1066
- zd.microsecond // self.log_div,
1064
+ dt.year,
1065
+ dt.month * 100 + dt.day,
1066
+ (dt.hour * 100 + dt.minute) * 100 + dt.second,
1067
+ dt.microsecond // self.log_div,
1067
1068
  )
1068
1069
 
1069
1070
  if c and not self.args.no_ansi:
@@ -1084,41 +1085,26 @@ class SvcHub(object):
1084
1085
  if not self.args.no_logflush:
1085
1086
  self.logf.flush()
1086
1087
 
1087
- now = time.time()
1088
- if int(now) >= self.next_day:
1089
- self._set_next_day()
1088
+ if dt.day != self.cday or dt.month != self.cmon:
1089
+ self._set_next_day(dt)
1090
1090
 
1091
- def _set_next_day(self) :
1092
- if self.next_day and self.logf and self.logf_base_fn != self._logname():
1091
+ def _set_next_day(self, dt ) :
1092
+ if self.cday and self.logf and self.logf_base_fn != self._logname():
1093
1093
  self.logf.close()
1094
1094
  self._setup_logfile("")
1095
1095
 
1096
- dt = datetime.now(UTC)
1097
-
1098
- # unix timestamp of next 00:00:00 (leap-seconds safe)
1099
- day_now = dt.day
1100
- while dt.day == day_now:
1101
- dt += timedelta(hours=12)
1102
-
1103
- dt = dt.replace(hour=0, minute=0, second=0)
1104
- try:
1105
- tt = dt.utctimetuple()
1106
- except:
1107
- # still makes me hella uncomfortable
1108
- tt = dt.timetuple()
1109
-
1110
- self.next_day = calendar.timegm(tt)
1096
+ self.cday = dt.day
1097
+ self.cmon = dt.month
1111
1098
 
1112
1099
  def _log_enabled(self, src , msg , c = 0) :
1113
1100
  """handles logging from all components"""
1114
1101
  with self.log_mutex:
1115
- now = time.time()
1116
- if int(now) >= self.next_day:
1117
- dt = datetime.fromtimestamp(now, UTC)
1102
+ dt = datetime.now(self.tz)
1103
+ if dt.day != self.cday or dt.month != self.cmon:
1118
1104
  zs = "{}\n" if self.no_ansi else "\033[36m{}\033[0m\n"
1119
1105
  zs = zs.format(dt.strftime("%Y-%m-%d"))
1120
1106
  print(zs, end="")
1121
- self._set_next_day()
1107
+ self._set_next_day(dt)
1122
1108
  if self.logf:
1123
1109
  self.logf.write(zs)
1124
1110
 
@@ -1137,12 +1123,11 @@ class SvcHub(object):
1137
1123
  else:
1138
1124
  msg = "%s%s\033[0m" % (c, msg)
1139
1125
 
1140
- zd = datetime.fromtimestamp(now, UTC)
1141
1126
  ts = self.log_efmt % (
1142
- zd.hour,
1143
- zd.minute,
1144
- zd.second,
1145
- zd.microsecond // self.log_div,
1127
+ dt.hour,
1128
+ dt.minute,
1129
+ dt.second,
1130
+ dt.microsecond // self.log_div,
1146
1131
  )
1147
1132
  msg = fmt % (ts, src, msg)
1148
1133
  try: