copyparty 1.13.0__tar.gz → 1.13.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (120) hide show
  1. {copyparty-1.13.0 → copyparty-1.13.1}/PKG-INFO +50 -6
  2. {copyparty-1.13.0 → copyparty-1.13.1}/README.md +49 -5
  3. {copyparty-1.13.0 → copyparty-1.13.1}/copyparty/__main__.py +27 -2
  4. {copyparty-1.13.0 → copyparty-1.13.1}/copyparty/__version__.py +2 -2
  5. {copyparty-1.13.0 → copyparty-1.13.1}/copyparty/authsrv.py +14 -2
  6. {copyparty-1.13.0 → copyparty-1.13.1}/copyparty/cfg.py +15 -2
  7. {copyparty-1.13.0 → copyparty-1.13.1}/copyparty/fsutil.py +25 -5
  8. {copyparty-1.13.0 → copyparty-1.13.1}/copyparty/httpcli.py +263 -19
  9. {copyparty-1.13.0 → copyparty-1.13.1}/copyparty/metrics.py +1 -1
  10. {copyparty-1.13.0 → copyparty-1.13.1}/copyparty/svchub.py +5 -1
  11. {copyparty-1.13.0 → copyparty-1.13.1}/copyparty/tcpsrv.py +6 -0
  12. {copyparty-1.13.0 → copyparty-1.13.1}/copyparty/u2idx.py +18 -3
  13. {copyparty-1.13.0 → copyparty-1.13.1}/copyparty/up2k.py +13 -1
  14. {copyparty-1.13.0 → copyparty-1.13.1}/copyparty/util.py +12 -0
  15. copyparty-1.13.1/copyparty/web/baguettebox.js.gz +0 -0
  16. copyparty-1.13.1/copyparty/web/browser.css.gz +0 -0
  17. {copyparty-1.13.0 → copyparty-1.13.1}/copyparty/web/browser.html +1 -1
  18. copyparty-1.13.1/copyparty/web/browser.js.gz +0 -0
  19. {copyparty-1.13.0 → copyparty-1.13.1}/copyparty/web/md.html +1 -1
  20. {copyparty-1.13.0 → copyparty-1.13.1}/copyparty/web/mde.html +1 -1
  21. {copyparty-1.13.0 → copyparty-1.13.1}/copyparty/web/msg.html +1 -1
  22. {copyparty-1.13.0 → copyparty-1.13.1}/copyparty/web/splash.html +2 -1
  23. copyparty-1.13.1/copyparty/web/splash.js.gz +0 -0
  24. {copyparty-1.13.0 → copyparty-1.13.1}/copyparty/web/svcs.html +1 -1
  25. copyparty-1.13.1/copyparty/web/util.js.gz +0 -0
  26. {copyparty-1.13.0 → copyparty-1.13.1}/copyparty.egg-info/PKG-INFO +50 -6
  27. copyparty-1.13.0/copyparty/web/baguettebox.js.gz +0 -0
  28. copyparty-1.13.0/copyparty/web/browser.css.gz +0 -0
  29. copyparty-1.13.0/copyparty/web/browser.js.gz +0 -0
  30. copyparty-1.13.0/copyparty/web/splash.js.gz +0 -0
  31. copyparty-1.13.0/copyparty/web/util.js.gz +0 -0
  32. {copyparty-1.13.0 → copyparty-1.13.1}/LICENSE +0 -0
  33. {copyparty-1.13.0 → copyparty-1.13.1}/copyparty/__init__.py +0 -0
  34. {copyparty-1.13.0 → copyparty-1.13.1}/copyparty/bos/__init__.py +0 -0
  35. {copyparty-1.13.0 → copyparty-1.13.1}/copyparty/bos/bos.py +0 -0
  36. {copyparty-1.13.0 → copyparty-1.13.1}/copyparty/bos/path.py +0 -0
  37. {copyparty-1.13.0 → copyparty-1.13.1}/copyparty/broker_mp.py +0 -0
  38. {copyparty-1.13.0 → copyparty-1.13.1}/copyparty/broker_mpw.py +0 -0
  39. {copyparty-1.13.0 → copyparty-1.13.1}/copyparty/broker_thr.py +0 -0
  40. {copyparty-1.13.0 → copyparty-1.13.1}/copyparty/broker_util.py +0 -0
  41. {copyparty-1.13.0 → copyparty-1.13.1}/copyparty/cert.py +0 -0
  42. {copyparty-1.13.0 → copyparty-1.13.1}/copyparty/dxml.py +0 -0
  43. {copyparty-1.13.0 → copyparty-1.13.1}/copyparty/ftpd.py +0 -0
  44. {copyparty-1.13.0 → copyparty-1.13.1}/copyparty/httpconn.py +0 -0
  45. {copyparty-1.13.0 → copyparty-1.13.1}/copyparty/httpsrv.py +0 -0
  46. {copyparty-1.13.0 → copyparty-1.13.1}/copyparty/ico.py +0 -0
  47. {copyparty-1.13.0 → copyparty-1.13.1}/copyparty/mdns.py +0 -0
  48. {copyparty-1.13.0 → copyparty-1.13.1}/copyparty/mtag.py +0 -0
  49. {copyparty-1.13.0 → copyparty-1.13.1}/copyparty/multicast.py +0 -0
  50. {copyparty-1.13.0 → copyparty-1.13.1}/copyparty/pwhash.py +0 -0
  51. {copyparty-1.13.0 → copyparty-1.13.1}/copyparty/res/COPYING.txt +0 -0
  52. {copyparty-1.13.0 → copyparty-1.13.1}/copyparty/res/__init__.py +0 -0
  53. {copyparty-1.13.0 → copyparty-1.13.1}/copyparty/res/insecure.pem +0 -0
  54. {copyparty-1.13.0 → copyparty-1.13.1}/copyparty/smbd.py +0 -0
  55. {copyparty-1.13.0 → copyparty-1.13.1}/copyparty/ssdp.py +0 -0
  56. {copyparty-1.13.0 → copyparty-1.13.1}/copyparty/star.py +0 -0
  57. {copyparty-1.13.0 → copyparty-1.13.1}/copyparty/stolen/__init__.py +0 -0
  58. {copyparty-1.13.0 → copyparty-1.13.1}/copyparty/stolen/dnslib/__init__.py +0 -0
  59. {copyparty-1.13.0 → copyparty-1.13.1}/copyparty/stolen/dnslib/bimap.py +0 -0
  60. {copyparty-1.13.0 → copyparty-1.13.1}/copyparty/stolen/dnslib/bit.py +0 -0
  61. {copyparty-1.13.0 → copyparty-1.13.1}/copyparty/stolen/dnslib/buffer.py +0 -0
  62. {copyparty-1.13.0 → copyparty-1.13.1}/copyparty/stolen/dnslib/dns.py +0 -0
  63. {copyparty-1.13.0 → copyparty-1.13.1}/copyparty/stolen/dnslib/label.py +0 -0
  64. {copyparty-1.13.0 → copyparty-1.13.1}/copyparty/stolen/dnslib/lex.py +0 -0
  65. {copyparty-1.13.0 → copyparty-1.13.1}/copyparty/stolen/dnslib/ranges.py +0 -0
  66. {copyparty-1.13.0 → copyparty-1.13.1}/copyparty/stolen/ifaddr/__init__.py +0 -0
  67. {copyparty-1.13.0 → copyparty-1.13.1}/copyparty/stolen/ifaddr/_posix.py +0 -0
  68. {copyparty-1.13.0 → copyparty-1.13.1}/copyparty/stolen/ifaddr/_shared.py +0 -0
  69. {copyparty-1.13.0 → copyparty-1.13.1}/copyparty/stolen/ifaddr/_win32.py +0 -0
  70. {copyparty-1.13.0 → copyparty-1.13.1}/copyparty/stolen/qrcodegen.py +0 -0
  71. {copyparty-1.13.0 → copyparty-1.13.1}/copyparty/stolen/surrogateescape.py +0 -0
  72. {copyparty-1.13.0 → copyparty-1.13.1}/copyparty/sutil.py +0 -0
  73. {copyparty-1.13.0 → copyparty-1.13.1}/copyparty/szip.py +0 -0
  74. {copyparty-1.13.0 → copyparty-1.13.1}/copyparty/tftpd.py +0 -0
  75. {copyparty-1.13.0 → copyparty-1.13.1}/copyparty/th_cli.py +0 -0
  76. {copyparty-1.13.0 → copyparty-1.13.1}/copyparty/th_srv.py +0 -0
  77. {copyparty-1.13.0 → copyparty-1.13.1}/copyparty/web/a/__init__.py +0 -0
  78. {copyparty-1.13.0 → copyparty-1.13.1}/copyparty/web/a/partyfuse.py +0 -0
  79. {copyparty-1.13.0 → copyparty-1.13.1}/copyparty/web/a/u2c.py +0 -0
  80. {copyparty-1.13.0 → copyparty-1.13.1}/copyparty/web/a/webdav-cfg.bat +0 -0
  81. {copyparty-1.13.0 → copyparty-1.13.1}/copyparty/web/browser2.html +0 -0
  82. {copyparty-1.13.0 → copyparty-1.13.1}/copyparty/web/cf.html +0 -0
  83. {copyparty-1.13.0 → copyparty-1.13.1}/copyparty/web/dbg-audio.js.gz +0 -0
  84. {copyparty-1.13.0 → copyparty-1.13.1}/copyparty/web/dd/2.png +0 -0
  85. {copyparty-1.13.0 → copyparty-1.13.1}/copyparty/web/dd/3.png +0 -0
  86. {copyparty-1.13.0 → copyparty-1.13.1}/copyparty/web/dd/4.png +0 -0
  87. {copyparty-1.13.0 → copyparty-1.13.1}/copyparty/web/dd/5.png +0 -0
  88. {copyparty-1.13.0 → copyparty-1.13.1}/copyparty/web/dd/__init__.py +0 -0
  89. {copyparty-1.13.0 → copyparty-1.13.1}/copyparty/web/deps/__init__.py +0 -0
  90. {copyparty-1.13.0 → copyparty-1.13.1}/copyparty/web/deps/busy.mp3.gz +0 -0
  91. {copyparty-1.13.0 → copyparty-1.13.1}/copyparty/web/deps/easymde.css.gz +0 -0
  92. {copyparty-1.13.0 → copyparty-1.13.1}/copyparty/web/deps/easymde.js.gz +0 -0
  93. {copyparty-1.13.0 → copyparty-1.13.1}/copyparty/web/deps/marked.js.gz +0 -0
  94. {copyparty-1.13.0 → copyparty-1.13.1}/copyparty/web/deps/mini-fa.css.gz +0 -0
  95. {copyparty-1.13.0 → copyparty-1.13.1}/copyparty/web/deps/mini-fa.woff +0 -0
  96. {copyparty-1.13.0 → copyparty-1.13.1}/copyparty/web/deps/prism.css.gz +0 -0
  97. {copyparty-1.13.0 → copyparty-1.13.1}/copyparty/web/deps/prism.js.gz +0 -0
  98. {copyparty-1.13.0 → copyparty-1.13.1}/copyparty/web/deps/prismd.css.gz +0 -0
  99. {copyparty-1.13.0 → copyparty-1.13.1}/copyparty/web/deps/scp.woff2 +0 -0
  100. {copyparty-1.13.0 → copyparty-1.13.1}/copyparty/web/deps/sha512.ac.js.gz +0 -0
  101. {copyparty-1.13.0 → copyparty-1.13.1}/copyparty/web/deps/sha512.hw.js.gz +0 -0
  102. {copyparty-1.13.0 → copyparty-1.13.1}/copyparty/web/md.css.gz +0 -0
  103. {copyparty-1.13.0 → copyparty-1.13.1}/copyparty/web/md.js.gz +0 -0
  104. {copyparty-1.13.0 → copyparty-1.13.1}/copyparty/web/md2.css.gz +0 -0
  105. {copyparty-1.13.0 → copyparty-1.13.1}/copyparty/web/md2.js.gz +0 -0
  106. {copyparty-1.13.0 → copyparty-1.13.1}/copyparty/web/mde.css.gz +0 -0
  107. {copyparty-1.13.0 → copyparty-1.13.1}/copyparty/web/mde.js.gz +0 -0
  108. {copyparty-1.13.0 → copyparty-1.13.1}/copyparty/web/msg.css.gz +0 -0
  109. {copyparty-1.13.0 → copyparty-1.13.1}/copyparty/web/splash.css.gz +0 -0
  110. {copyparty-1.13.0 → copyparty-1.13.1}/copyparty/web/svcs.js.gz +0 -0
  111. {copyparty-1.13.0 → copyparty-1.13.1}/copyparty/web/ui.css.gz +0 -0
  112. {copyparty-1.13.0 → copyparty-1.13.1}/copyparty/web/up2k.js.gz +0 -0
  113. {copyparty-1.13.0 → copyparty-1.13.1}/copyparty/web/w.hash.js.gz +0 -0
  114. {copyparty-1.13.0 → copyparty-1.13.1}/copyparty.egg-info/SOURCES.txt +0 -0
  115. {copyparty-1.13.0 → copyparty-1.13.1}/copyparty.egg-info/dependency_links.txt +0 -0
  116. {copyparty-1.13.0 → copyparty-1.13.1}/copyparty.egg-info/entry_points.txt +0 -0
  117. {copyparty-1.13.0 → copyparty-1.13.1}/copyparty.egg-info/requires.txt +0 -0
  118. {copyparty-1.13.0 → copyparty-1.13.1}/copyparty.egg-info/top_level.txt +0 -0
  119. {copyparty-1.13.0 → copyparty-1.13.1}/pyproject.toml +0 -0
  120. {copyparty-1.13.0 → copyparty-1.13.1}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: copyparty
3
- Version: 1.13.0
3
+ Version: 1.13.1
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
@@ -101,6 +101,7 @@ turn almost any device into a file server with resumable uploads/downloads using
101
101
  * [audio equalizer](#audio-equalizer) - and [dynamic range compressor](https://en.wikipedia.org/wiki/Dynamic_range_compression)
102
102
  * [fix unreliable playback on android](#fix-unreliable-playback-on-android) - due to phone / app settings
103
103
  * [markdown viewer](#markdown-viewer) - and there are *two* editors
104
+ * [markdown vars](#markdown-vars) - dynamic docs with serverside variable expansion
104
105
  * [other tricks](#other-tricks)
105
106
  * [searching](#searching) - search by size, date, path/name, mp3-tags, ...
106
107
  * [server config](#server-config) - using arguments or config files, or a mix of both
@@ -114,6 +115,7 @@ turn almost any device into a file server with resumable uploads/downloads using
114
115
  * [tftp server](#tftp-server) - a TFTP server (read/write) can be started using `--tftp 3969`
115
116
  * [smb server](#smb-server) - unsafe, slow, not recommended for wan
116
117
  * [browser ux](#browser-ux) - tweaking the ui
118
+ * [opengraph](#opengraph) - discord and social-media embeds
117
119
  * [file indexing](#file-indexing) - enables dedup and music search ++
118
120
  * [exclude-patterns](#exclude-patterns) - to save some time
119
121
  * [filesystem guards](#filesystem-guards) - avoid traversing into other filesystems
@@ -162,8 +164,9 @@ turn almost any device into a file server with resumable uploads/downloads using
162
164
  * [dependencies](#dependencies) - mandatory deps
163
165
  * [optional dependencies](#optional-dependencies) - install these to enable bonus features
164
166
  * [optional gpl stuff](#optional-gpl-stuff)
165
- * [sfx](#sfx) - the self-contained "binary"
167
+ * [sfx](#sfx) - the self-contained "binary" (recommended!)
166
168
  * [copyparty.exe](#copypartyexe) - download [copyparty.exe](https://github.com/9001/copyparty/releases/latest/download/copyparty.exe) (win8+) or [copyparty32.exe](https://github.com/9001/copyparty/releases/latest/download/copyparty32.exe) (win7+)
169
+ * [zipapp](#zipapp) - another emergency alternative, [copyparty.pyz](https://github.com/9001/copyparty/releases/latest/download/copyparty.pyz)
167
170
  * [install on android](#install-on-android)
168
171
  * [reporting bugs](#reporting-bugs) - ideas for context to include, and where to submit them
169
172
  * [devnotes](#devnotes) - for build instructions etc, see [./docs/devnotes.md](./docs/devnotes.md)
@@ -177,6 +180,7 @@ just run **[copyparty-sfx.py](https://github.com/9001/copyparty/releases/latest/
177
180
  * or if you cannot install python, you can use [copyparty.exe](#copypartyexe) instead
178
181
  * or install [on arch](#arch-package) ╱ [on NixOS](#nixos-module) ╱ [through nix](#nix-package)
179
182
  * or if you are on android, [install copyparty in termux](#install-on-android)
183
+ * or if your computer is messed up and nothing else works, [try the pyz](#zipapp)
180
184
  * or if you prefer to [use docker](./scripts/docker/) 🐋 you can do that too
181
185
  * docker has all deps built-in, so skip this step:
182
186
 
@@ -288,9 +292,11 @@ also see [comparison to similar software](./docs/versus.md)
288
292
  * client support
289
293
  * ☑ [folder sync](#folder-sync)
290
294
  * ☑ [curl-friendly](https://user-images.githubusercontent.com/241032/215322619-ea5fd606-3654-40ad-94ee-2bc058647bb2.png)
295
+ * ☑ [opengraph](#opengraph) (discord embeds)
291
296
  * markdown
292
297
  * ☑ [viewer](#markdown-viewer)
293
298
  * ☑ editor (sure why not)
299
+ * ☑ [variables](#markdown-vars)
294
300
 
295
301
  PS: something missing? post any crazy ideas you've got as a [feature request](https://github.com/9001/copyparty/issues/new?assignees=9001&labels=enhancement&template=feature_request.md) or [discussion](https://github.com/9001/copyparty/discussions/new?category=ideas) 🤙
296
302
 
@@ -668,9 +674,15 @@ cool trick: download a folder by appending url-params `?tar&opus` or `?tar&mp3`
668
674
 
669
675
  ## uploading
670
676
 
671
- drag files/folders into the web-browser to upload (or use the [command-line uploader](https://github.com/9001/copyparty/tree/hovudstraum/bin#u2cpy))
677
+ drag files/folders into the web-browser to upload
672
678
 
673
- this initiates an upload using `up2k`; there are two uploaders available:
679
+ dragdrop is the recommended way, but you may also:
680
+
681
+ * select some files (not folders) in your file explorer and press CTRL-V inside the browser window
682
+ * use the [command-line uploader](https://github.com/9001/copyparty/tree/hovudstraum/bin#u2cpy)
683
+ * upload using [curl or sharex](#client-examples)
684
+
685
+ when uploading files through dragdrop or CTRL-V, this initiates an upload using `up2k`; there are two browser-based uploaders available:
674
686
  * `[🎈] bup`, the basic uploader, supports almost every browser since netscape 4.0
675
687
  * `[🚀] up2k`, the good / fancy one
676
688
 
@@ -897,6 +909,13 @@ other notes,
897
909
  * the document preview has a max-width which is the same as an A4 paper when printed
898
910
 
899
911
 
912
+ ### markdown vars
913
+
914
+ dynamic docs with serverside variable expansion to replace stuff like `{{self.ip}}` with the client's IP, or `{{srv.htime}}` with the current time on the server
915
+
916
+ see [./srv/expand/](./srv/expand/) for usage and examples
917
+
918
+
900
919
  ## other tricks
901
920
 
902
921
  * you can link a particular timestamp in an audio file by adding it to the URL, such as `&20` / `&20s` / `&1m20` / `&t=1:20` after the `.../#af-c8960dab`
@@ -1121,7 +1140,22 @@ tweaking the ui
1121
1140
  * to sort in music order (album, track, artist, title) with filename as fallback, you could `--sort tags/Cirle,tags/.tn,tags/Artist,tags/Title,href`
1122
1141
  * to sort by upload date, first enable showing the upload date in the listing with `-e2d -mte +.up_at` and then `--sort tags/.up_at`
1123
1142
 
1124
- see [./docs/rice](./docs/rice) for more
1143
+ see [./docs/rice](./docs/rice) for more, including how to add stuff (css/`<meta>`/...) to the html `<head>` tag
1144
+
1145
+
1146
+ ## opengraph
1147
+
1148
+ discord and social-media embeds
1149
+
1150
+ can be enabled globally with `--og` or per-volume with volflag `og`
1151
+
1152
+ note that this disables hotlinking because the opengraph spec demands it; to sneak past this intentional limitation, you can enable opengraph selectively by user-agent, for example `--og-ua '(Discord|Twitter|Slack)bot'` (or volflag `og_ua`)
1153
+
1154
+ you can also hotlink files regardless by appending `?raw` to the url
1155
+
1156
+ NOTE: because discord (and maybe others) strip query args such as `?raw` in opengraph tags, any links which require a filekey or dirkey will not work
1157
+
1158
+ 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)
1125
1159
 
1126
1160
 
1127
1161
  ## file indexing
@@ -2035,7 +2069,7 @@ these are standalone programs and will never be imported / evaluated by copypart
2035
2069
 
2036
2070
  # sfx
2037
2071
 
2038
- 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
2072
+ the self-contained "binary" (recommended!) [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
2039
2073
 
2040
2074
  you can reduce the sfx size by repacking it; see [./docs/devnotes.md#sfx-repack](./docs/devnotes.md#sfx-repack)
2041
2075
 
@@ -2062,6 +2096,16 @@ meanwhile [copyparty-sfx.py](https://github.com/9001/copyparty/releases/latest/d
2062
2096
  then again, if you are already into downloading shady binaries from the internet, you may also want my [minimal builds](./scripts/pyinstaller#ffmpeg) of [ffmpeg](https://ocv.me/stuff/bin/ffmpeg.exe) and [ffprobe](https://ocv.me/stuff/bin/ffprobe.exe) which enables copyparty to extract multimedia-info, do audio-transcoding, and thumbnails/spectrograms/waveforms, however it's much better to instead grab a [recent official build](https://www.gyan.dev/ffmpeg/builds/ffmpeg-git-full.7z) every once ina while if you can afford the size
2063
2097
 
2064
2098
 
2099
+ ## zipapp
2100
+
2101
+ another emergency alternative, [copyparty.pyz](https://github.com/9001/copyparty/releases/latest/download/copyparty.pyz) has less features, requires python 3.7 or newer, worse compression, and more importantly is unable to benefit from more recent versions of jinja2 and such (which makes it less secure)... lots of drawbacks with this one really -- but it *may* just work if the regular sfx fails to start because the computer is messed up in certain funky ways, so it's worth a shot if all else fails
2102
+
2103
+ run it by doubleclicking it, or try typing `python copyparty.pyz` in your terminal/console/commandline/telex if that fails
2104
+
2105
+ it is a python [zipapp](https://docs.python.org/3/library/zipapp.html) meaning it doesn't have to unpack its own python code anywhere to run, so if the filesystem is busted it has a better chance of getting somewhere
2106
+ * but note that it currently still needs to extract the web-resources somewhere (they'll land in the default TEMP-folder of your OS)
2107
+
2108
+
2065
2109
  # install on android
2066
2110
 
2067
2111
  install [Termux](https://termux.com/) + its companion app `Termux:API` (see [ocv.me/termux](https://ocv.me/termux/)) and then copy-paste this into Termux (long-tap) all at once:
@@ -47,6 +47,7 @@ turn almost any device into a file server with resumable uploads/downloads using
47
47
  * [audio equalizer](#audio-equalizer) - and [dynamic range compressor](https://en.wikipedia.org/wiki/Dynamic_range_compression)
48
48
  * [fix unreliable playback on android](#fix-unreliable-playback-on-android) - due to phone / app settings
49
49
  * [markdown viewer](#markdown-viewer) - and there are *two* editors
50
+ * [markdown vars](#markdown-vars) - dynamic docs with serverside variable expansion
50
51
  * [other tricks](#other-tricks)
51
52
  * [searching](#searching) - search by size, date, path/name, mp3-tags, ...
52
53
  * [server config](#server-config) - using arguments or config files, or a mix of both
@@ -60,6 +61,7 @@ turn almost any device into a file server with resumable uploads/downloads using
60
61
  * [tftp server](#tftp-server) - a TFTP server (read/write) can be started using `--tftp 3969`
61
62
  * [smb server](#smb-server) - unsafe, slow, not recommended for wan
62
63
  * [browser ux](#browser-ux) - tweaking the ui
64
+ * [opengraph](#opengraph) - discord and social-media embeds
63
65
  * [file indexing](#file-indexing) - enables dedup and music search ++
64
66
  * [exclude-patterns](#exclude-patterns) - to save some time
65
67
  * [filesystem guards](#filesystem-guards) - avoid traversing into other filesystems
@@ -108,8 +110,9 @@ turn almost any device into a file server with resumable uploads/downloads using
108
110
  * [dependencies](#dependencies) - mandatory deps
109
111
  * [optional dependencies](#optional-dependencies) - install these to enable bonus features
110
112
  * [optional gpl stuff](#optional-gpl-stuff)
111
- * [sfx](#sfx) - the self-contained "binary"
113
+ * [sfx](#sfx) - the self-contained "binary" (recommended!)
112
114
  * [copyparty.exe](#copypartyexe) - download [copyparty.exe](https://github.com/9001/copyparty/releases/latest/download/copyparty.exe) (win8+) or [copyparty32.exe](https://github.com/9001/copyparty/releases/latest/download/copyparty32.exe) (win7+)
115
+ * [zipapp](#zipapp) - another emergency alternative, [copyparty.pyz](https://github.com/9001/copyparty/releases/latest/download/copyparty.pyz)
113
116
  * [install on android](#install-on-android)
114
117
  * [reporting bugs](#reporting-bugs) - ideas for context to include, and where to submit them
115
118
  * [devnotes](#devnotes) - for build instructions etc, see [./docs/devnotes.md](./docs/devnotes.md)
@@ -123,6 +126,7 @@ just run **[copyparty-sfx.py](https://github.com/9001/copyparty/releases/latest/
123
126
  * or if you cannot install python, you can use [copyparty.exe](#copypartyexe) instead
124
127
  * or install [on arch](#arch-package) ╱ [on NixOS](#nixos-module) ╱ [through nix](#nix-package)
125
128
  * or if you are on android, [install copyparty in termux](#install-on-android)
129
+ * or if your computer is messed up and nothing else works, [try the pyz](#zipapp)
126
130
  * or if you prefer to [use docker](./scripts/docker/) 🐋 you can do that too
127
131
  * docker has all deps built-in, so skip this step:
128
132
 
@@ -234,9 +238,11 @@ also see [comparison to similar software](./docs/versus.md)
234
238
  * client support
235
239
  * ☑ [folder sync](#folder-sync)
236
240
  * ☑ [curl-friendly](https://user-images.githubusercontent.com/241032/215322619-ea5fd606-3654-40ad-94ee-2bc058647bb2.png)
241
+ * ☑ [opengraph](#opengraph) (discord embeds)
237
242
  * markdown
238
243
  * ☑ [viewer](#markdown-viewer)
239
244
  * ☑ editor (sure why not)
245
+ * ☑ [variables](#markdown-vars)
240
246
 
241
247
  PS: something missing? post any crazy ideas you've got as a [feature request](https://github.com/9001/copyparty/issues/new?assignees=9001&labels=enhancement&template=feature_request.md) or [discussion](https://github.com/9001/copyparty/discussions/new?category=ideas) 🤙
242
248
 
@@ -614,9 +620,15 @@ cool trick: download a folder by appending url-params `?tar&opus` or `?tar&mp3`
614
620
 
615
621
  ## uploading
616
622
 
617
- drag files/folders into the web-browser to upload (or use the [command-line uploader](https://github.com/9001/copyparty/tree/hovudstraum/bin#u2cpy))
623
+ drag files/folders into the web-browser to upload
618
624
 
619
- this initiates an upload using `up2k`; there are two uploaders available:
625
+ dragdrop is the recommended way, but you may also:
626
+
627
+ * select some files (not folders) in your file explorer and press CTRL-V inside the browser window
628
+ * use the [command-line uploader](https://github.com/9001/copyparty/tree/hovudstraum/bin#u2cpy)
629
+ * upload using [curl or sharex](#client-examples)
630
+
631
+ when uploading files through dragdrop or CTRL-V, this initiates an upload using `up2k`; there are two browser-based uploaders available:
620
632
  * `[🎈] bup`, the basic uploader, supports almost every browser since netscape 4.0
621
633
  * `[🚀] up2k`, the good / fancy one
622
634
 
@@ -843,6 +855,13 @@ other notes,
843
855
  * the document preview has a max-width which is the same as an A4 paper when printed
844
856
 
845
857
 
858
+ ### markdown vars
859
+
860
+ dynamic docs with serverside variable expansion to replace stuff like `{{self.ip}}` with the client's IP, or `{{srv.htime}}` with the current time on the server
861
+
862
+ see [./srv/expand/](./srv/expand/) for usage and examples
863
+
864
+
846
865
  ## other tricks
847
866
 
848
867
  * you can link a particular timestamp in an audio file by adding it to the URL, such as `&20` / `&20s` / `&1m20` / `&t=1:20` after the `.../#af-c8960dab`
@@ -1067,7 +1086,22 @@ tweaking the ui
1067
1086
  * to sort in music order (album, track, artist, title) with filename as fallback, you could `--sort tags/Cirle,tags/.tn,tags/Artist,tags/Title,href`
1068
1087
  * to sort by upload date, first enable showing the upload date in the listing with `-e2d -mte +.up_at` and then `--sort tags/.up_at`
1069
1088
 
1070
- see [./docs/rice](./docs/rice) for more
1089
+ see [./docs/rice](./docs/rice) for more, including how to add stuff (css/`<meta>`/...) to the html `<head>` tag
1090
+
1091
+
1092
+ ## opengraph
1093
+
1094
+ discord and social-media embeds
1095
+
1096
+ can be enabled globally with `--og` or per-volume with volflag `og`
1097
+
1098
+ note that this disables hotlinking because the opengraph spec demands it; to sneak past this intentional limitation, you can enable opengraph selectively by user-agent, for example `--og-ua '(Discord|Twitter|Slack)bot'` (or volflag `og_ua`)
1099
+
1100
+ you can also hotlink files regardless by appending `?raw` to the url
1101
+
1102
+ NOTE: because discord (and maybe others) strip query args such as `?raw` in opengraph tags, any links which require a filekey or dirkey will not work
1103
+
1104
+ 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)
1071
1105
 
1072
1106
 
1073
1107
  ## file indexing
@@ -1981,7 +2015,7 @@ these are standalone programs and will never be imported / evaluated by copypart
1981
2015
 
1982
2016
  # sfx
1983
2017
 
1984
- 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
2018
+ the self-contained "binary" (recommended!) [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
1985
2019
 
1986
2020
  you can reduce the sfx size by repacking it; see [./docs/devnotes.md#sfx-repack](./docs/devnotes.md#sfx-repack)
1987
2021
 
@@ -2008,6 +2042,16 @@ meanwhile [copyparty-sfx.py](https://github.com/9001/copyparty/releases/latest/d
2008
2042
  then again, if you are already into downloading shady binaries from the internet, you may also want my [minimal builds](./scripts/pyinstaller#ffmpeg) of [ffmpeg](https://ocv.me/stuff/bin/ffmpeg.exe) and [ffprobe](https://ocv.me/stuff/bin/ffprobe.exe) which enables copyparty to extract multimedia-info, do audio-transcoding, and thumbnails/spectrograms/waveforms, however it's much better to instead grab a [recent official build](https://www.gyan.dev/ffmpeg/builds/ffmpeg-git-full.7z) every once ina while if you can afford the size
2009
2043
 
2010
2044
 
2045
+ ## zipapp
2046
+
2047
+ another emergency alternative, [copyparty.pyz](https://github.com/9001/copyparty/releases/latest/download/copyparty.pyz) has less features, requires python 3.7 or newer, worse compression, and more importantly is unable to benefit from more recent versions of jinja2 and such (which makes it less secure)... lots of drawbacks with this one really -- but it *may* just work if the regular sfx fails to start because the computer is messed up in certain funky ways, so it's worth a shot if all else fails
2048
+
2049
+ run it by doubleclicking it, or try typing `python copyparty.pyz` in your terminal/console/commandline/telex if that fails
2050
+
2051
+ it is a python [zipapp](https://docs.python.org/3/library/zipapp.html) meaning it doesn't have to unpack its own python code anywhere to run, so if the filesystem is busted it has a better chance of getting somewhere
2052
+ * but note that it currently still needs to extract the web-resources somewhere (they'll land in the default TEMP-folder of your OS)
2053
+
2054
+
2011
2055
  # install on android
2012
2056
 
2013
2057
  install [Termux](https://termux.com/) + its companion app `Termux:API` (see [ocv.me/termux](https://ocv.me/termux/)) and then copy-paste this into Termux (long-tap) all at once:
@@ -854,6 +854,7 @@ def add_fs(ap):
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)")
857
+ ap2.add_argument("--mtab-age", metavar="SEC", type=int, default=60, help="rebuild mountpoint cache every \033[33mSEC\033[0m to keep track of sparse-files support; keep low on servers with removable media")
857
858
 
858
859
 
859
860
  def add_upload(ap):
@@ -1250,19 +1251,38 @@ def add_txt(ap):
1250
1251
  ap2.add_argument("--exp-lg", metavar="V,V,V", type=u, default=DEF_EXP, help="comma/space-separated list of placeholders to expand in prologue/epilogue files (volflag=exp_lg)")
1251
1252
 
1252
1253
 
1254
+ def add_og(ap):
1255
+ ap2 = ap.add_argument_group('og / open graph / discord-embed options')
1256
+ ap2.add_argument("--og", action="store_true", help="disable hotlinking and return an html document instead; this is required by open-graph, but can also be useful on its own (volflag=og)")
1257
+ ap2.add_argument("--og-ua", metavar="RE", type=u, default="", help="only disable hotlinking / engage OG behavior if the useragent matches regex \033[33mRE\033[0m (volflag=og_ua)")
1258
+ ap2.add_argument("--og-tpl", metavar="PATH", type=u, default="", help="do not return the regular copyparty html, but instead load the jinja2 template at \033[33mPATH\033[0m (if path contains 'EXT' then EXT will be replaced with the requested file's extension) (volflag=og_tpl)")
1259
+ ap2.add_argument("--og-no-head", action="store_true", help="do not automatically add OG entries into <head> (useful if you're doing this yourself in a template or such) (volflag=og_no_head)")
1260
+ ap2.add_argument("--og-th", metavar="FMT", type=u, default="jf3", help="thumbnail format; j=jpeg, jf=jpeg-uncropped, jf3=jpeg-uncropped-large, w=webm, ... (volflag=og_th)")
1261
+ ap2.add_argument("--og-title", metavar="TXT", type=u, default="", help="fallback title if there is nothing in the \033[33m-e2t\033[0m database (volflag=og_title)")
1262
+ ap2.add_argument("--og-title-a", metavar="T", type=u, default="🎵 {{ artist }} - {{ title }}", help="audio title format; takes any metadata key (volflag=og_title_a)")
1263
+ ap2.add_argument("--og-title-v", metavar="T", type=u, default="{{ title }}", help="video title format; takes any metadata key (volflag=og_title_v)")
1264
+ ap2.add_argument("--og-title-i", metavar="T", type=u, default="{{ title }}", help="image title format; takes any metadata key (volflag=og_title_i)")
1265
+ ap2.add_argument("--og-s-title", action="store_true", help="force default title; do not read from tags (volflag=og_s_title)")
1266
+ ap2.add_argument("--og-desc", metavar="TXT", type=u, default="", help="description text; same for all files, disable with [\033[32m-\033[0m] (volflag=og_desc)")
1267
+ ap2.add_argument("--og-site", metavar="TXT", type=u, default="", help="sitename; defaults to \033[33m--name\033[0m, disable with [\033[32m-\033[0m] (volflag=og_site)")
1268
+ ap2.add_argument("--tcolor", metavar="RGB", type=u, default="333", help="accent color (3 or 6 hex digits); may also affect safari and/or android-chrome (volflag=tcolor)")
1269
+ ap2.add_argument("--uqe", action="store_true", help="query-string parceling; translate a request for \033[33m/foo/.uqe/BASE64\033[0m into \033[33m/foo?TEXT\033[0m, or \033[33m/foo/?TEXT\033[0m if the first character in \033[33mTEXT\033[0m is a slash. Automatically enabled for \033[33m--og\033[0m")
1270
+
1271
+
1253
1272
  def add_ui(ap, retry):
1254
1273
  ap2 = ap.add_argument_group('ui options')
1255
1274
  ap2.add_argument("--grid", action="store_true", help="show grid/thumbnails by default (volflag=grid)")
1256
1275
  ap2.add_argument("--lang", metavar="LANG", type=u, default="eng", help="language; one of the following: \033[32meng nor\033[0m")
1257
1276
  ap2.add_argument("--theme", metavar="NUM", type=int, default=0, help="default theme to use (0..7)")
1258
1277
  ap2.add_argument("--themes", metavar="NUM", type=int, default=8, help="number of themes installed")
1278
+ ap2.add_argument("--au-vol", metavar="0-100", type=int, default=50, choices=range(0, 101), help="default audio/video volume percent")
1259
1279
  ap2.add_argument("--sort", metavar="C,C,C", type=u, default="href", help="default sort order, comma-separated column IDs (see header tooltips), prefix with '-' for descending. Examples: \033[32mhref -href ext sz ts tags/Album tags/.tn\033[0m (volflag=sort)")
1260
1280
  ap2.add_argument("--unlist", metavar="REGEX", type=u, default="", help="don't show files matching \033[33mREGEX\033[0m in file list. Purely cosmetic! Does not affect API calls, just the browser. Example: [\033[32m\\.(js|css)$\033[0m] (volflag=unlist)")
1261
1281
  ap2.add_argument("--favico", metavar="TXT", type=u, default="c 000 none" if retry else "🎉 000 none", help="\033[33mfavicon-text\033[0m [ \033[33mforeground\033[0m [ \033[33mbackground\033[0m ] ], set blank to disable")
1262
1282
  ap2.add_argument("--mpmc", metavar="URL", type=u, default="", help="change the mediaplayer-toggle mouse cursor; URL to a folder with {2..5}.png inside (or disable with [\033[32m.\033[0m])")
1263
1283
  ap2.add_argument("--js-browser", metavar="L", type=u, help="URL to additional JS to include")
1264
1284
  ap2.add_argument("--css-browser", metavar="L", type=u, help="URL to additional CSS to include")
1265
- ap2.add_argument("--html-head", metavar="TXT", type=u, default="", help="text to append to the <head> of all HTML pages")
1285
+ ap2.add_argument("--html-head", metavar="TXT", type=u, default="", help="text to append to the <head> of all HTML pages; can be @PATH to send the contents of a file at PATH, and/or begin with %% to render as jinja2 template (volflag=html_head)")
1266
1286
  ap2.add_argument("--ih", action="store_true", help="if a folder contains index.html, show that instead of the directory listing by default (can be changed in the client settings UI, or add ?v to URL for override)")
1267
1287
  ap2.add_argument("--textfiles", metavar="CSV", type=u, default="txt,nfo,diz,cue,readme", help="file extensions to present as plaintext")
1268
1288
  ap2.add_argument("--txt-max", metavar="KiB", type=int, default=64, help="max size of embedded textfiles on ?doc= (anything bigger will be lazy-loaded by JS)")
@@ -1350,6 +1370,7 @@ def run_argparse(
1350
1370
  add_hooks(ap)
1351
1371
  add_stats(ap)
1352
1372
  add_txt(ap)
1373
+ add_og(ap)
1353
1374
  add_ui(ap, retry)
1354
1375
  add_admin(ap)
1355
1376
  add_logging(ap)
@@ -1384,12 +1405,16 @@ def run_argparse(
1384
1405
  return ret
1385
1406
 
1386
1407
 
1387
- def main(argv = None) :
1408
+ def main(argv = None, rsrc = None) :
1388
1409
  time.strptime("19970815", "%Y%m%d") # python#7980
1389
1410
  if WINDOWS:
1390
1411
  os.system("rem") # enables colors
1391
1412
 
1392
1413
  init_E(E)
1414
+
1415
+ if rsrc: # pyz
1416
+ E.mod = rsrc
1417
+
1393
1418
  if argv is None:
1394
1419
  argv = sys.argv
1395
1420
 
@@ -1,8 +1,8 @@
1
1
  # coding: utf-8
2
2
 
3
- VERSION = (1, 13, 0)
3
+ VERSION = (1, 13, 1)
4
4
  CODENAME = "race the beam"
5
- BUILD_DT = (2024, 4, 20)
5
+ BUILD_DT = (2024, 5, 6)
6
6
 
7
7
  S_VERSION = ".".join(map(str, VERSION))
8
8
  S_BUILD_DT = "{0:04d}-{1:02d}-{2:02d}".format(*BUILD_DT)
@@ -1435,6 +1435,7 @@ class AuthSrv(object):
1435
1435
  elif "" not in mount:
1436
1436
  # there's volumes but no root; make root inaccessible
1437
1437
  vfs = VFS(self.log_func, "", "", AXS(), {})
1438
+ vfs.flags["tcolor"] = self.args.tcolor
1438
1439
  vfs.flags["d2d"] = True
1439
1440
 
1440
1441
  maxdepth = 0
@@ -1720,7 +1721,11 @@ class AuthSrv(object):
1720
1721
  if self.args.e2d or "e2ds" in vol.flags:
1721
1722
  vol.flags["e2d"] = True
1722
1723
 
1723
- for ga, vf in [["no_hash", "nohash"], ["no_idx", "noidx"]]:
1724
+ for ga, vf in [
1725
+ ["no_hash", "nohash"],
1726
+ ["no_idx", "noidx"],
1727
+ ["og_ua", "og_ua"],
1728
+ ]:
1724
1729
  if vf in vol.flags:
1725
1730
  ptn = re.compile(vol.flags.pop(vf))
1726
1731
  else:
@@ -1766,6 +1771,13 @@ class AuthSrv(object):
1766
1771
  t = 'volume "/%s" has invalid %stry [%s]'
1767
1772
  raise Exception(t % (vol.vpath, k, vol.flags.get(k + "try")))
1768
1773
 
1774
+ if vol.flags.get("og"):
1775
+ self.args.uqe = True
1776
+
1777
+ zs = str(vol.flags.get("tcolor", "")).lstrip("#")
1778
+ if len(zs) == 3: # fc5 => ffcc55
1779
+ vol.flags["tcolor"] = "".join([x * 2 for x in zs])
1780
+
1769
1781
  for k1, k2 in IMPLICATIONS:
1770
1782
  if k1 in vol.flags:
1771
1783
  vol.flags[k2] = True
@@ -2403,7 +2415,7 @@ def expand_config_file(
2403
2415
  if not cnames:
2404
2416
  t = "warning: tried to read config-files from folder '%s' but it does not contain any "
2405
2417
  if names:
2406
- t += ".conf files; the following files were ignored: %s"
2418
+ t += ".conf files; the following files/subfolders were ignored: %s"
2407
2419
  t = t % (fp, ", ".join(names[:8]))
2408
2420
  else:
2409
2421
  t += "files at all"
@@ -39,6 +39,9 @@ def vf_bmap() :
39
39
  "magic",
40
40
  "no_sb_md",
41
41
  "no_sb_lg",
42
+ "og",
43
+ "og_no_head",
44
+ "og_s_title",
42
45
  "rand",
43
46
  "xdev",
44
47
  "xlink",
@@ -61,12 +64,23 @@ def vf_vmap() :
61
64
  }
62
65
  for k in (
63
66
  "dbd",
67
+ "html_head",
64
68
  "lg_sbf",
65
69
  "md_sbf",
66
70
  "nrand",
71
+ "og_desc",
72
+ "og_site",
73
+ "og_th",
74
+ "og_title",
75
+ "og_title_a",
76
+ "og_title_v",
77
+ "og_title_i",
78
+ "og_tpl",
79
+ "og_ua",
67
80
  "mv_retry",
68
81
  "rm_retry",
69
82
  "sort",
83
+ "tcolor",
70
84
  "unlist",
71
85
  "u2abort",
72
86
  "u2ts",
@@ -81,7 +95,6 @@ def vf_cmap() :
81
95
  for k in (
82
96
  "exp_lg",
83
97
  "exp_md",
84
- "html_head",
85
98
  "mte",
86
99
  "mth",
87
100
  "mtp",
@@ -201,7 +214,7 @@ flagcats = {
201
214
  "grid": "show grid/thumbnails by default",
202
215
  "sort": "default sort order",
203
216
  "unlist": "dont list files matching REGEX",
204
- "html_head=TXT": "includes TXT in the <head>",
217
+ "html_head=TXT": "includes TXT in the <head>, or @PATH for file at PATH",
205
218
  "robots": "allows indexing by search engines (default)",
206
219
  "norobots": "kindly asks search engines to leave",
207
220
  "no_sb_md": "disable js sandbox for markdown files",
@@ -1,6 +1,7 @@
1
1
  # coding: utf-8
2
2
  from __future__ import print_function, unicode_literals
3
3
 
4
+ import argparse
4
5
  import os
5
6
  import re
6
7
  import time
@@ -11,20 +12,26 @@ from .bos import bos
11
12
  from .util import chkcmd, min_ex
12
13
 
13
14
  class Fstab(object):
14
- def __init__(self, log ):
15
+ def __init__(self, log , args ):
15
16
  self.log_func = log
16
17
 
18
+ self.warned = False
17
19
  self.trusted = False
18
20
  self.tab = None
21
+ self.oldtab = None
22
+ self.srctab = "a"
19
23
  self.cache = {}
20
24
  self.age = 0.0
25
+ self.maxage = args.mtab_age
21
26
 
22
27
  def log(self, msg , c = 0) :
23
28
  self.log_func("fstab", msg, c)
24
29
 
25
30
  def get(self, path ) :
26
- if len(self.cache) > 9000:
27
- self.age = time.time()
31
+ now = time.time()
32
+ if now - self.age > self.maxage or len(self.cache) > 9000:
33
+ self.age = now
34
+ self.oldtab = self.tab or self.oldtab
28
35
  self.tab = None
29
36
  self.cache = {}
30
37
 
@@ -69,7 +76,7 @@ class Fstab(object):
69
76
  self.trusted = False
70
77
 
71
78
  def build_tab(self) :
72
- self.log("building tab")
79
+ self.log("inspecting mtab for changes")
73
80
 
74
81
  sptn = r"^.*? on (.*) type ([^ ]+) \(.*"
75
82
  if MACOS:
@@ -78,6 +85,7 @@ class Fstab(object):
78
85
  ptn = re.compile(sptn)
79
86
  so, _ = chkcmd(["mount"])
80
87
  tab1 = []
88
+ atab = []
81
89
  for ln in so.split("\n"):
82
90
  m = ptn.match(ln)
83
91
  if not m:
@@ -85,6 +93,15 @@ class Fstab(object):
85
93
 
86
94
  zs1, zs2 = m.groups()
87
95
  tab1.append((str(zs1), str(zs2)))
96
+ atab.append(ln)
97
+
98
+ # keep empirically-correct values if mounttab unchanged
99
+ srctab = "\n".join(sorted(atab))
100
+ if srctab == self.srctab:
101
+ self.tab = self.oldtab
102
+ return
103
+
104
+ self.log("mtab has changed; reevaluating support for sparse files")
88
105
 
89
106
  tab1.sort(key=lambda x: (len(x[0]), x[0]))
90
107
  path1, fs1 = tab1[0]
@@ -93,6 +110,7 @@ class Fstab(object):
93
110
  tab.add(fs, path.lstrip("/"))
94
111
 
95
112
  self.tab = tab
113
+ self.srctab = srctab
96
114
 
97
115
  def relabel(self, path , nval ) :
98
116
  assert self.tab
@@ -127,7 +145,9 @@ class Fstab(object):
127
145
  self.trusted = True
128
146
  except:
129
147
  # prisonparty or other restrictive environment
130
- self.log("failed to build tab:\n{}".format(min_ex()), 3)
148
+ if not self.warned:
149
+ self.warned = True
150
+ self.log("failed to build tab:\n{}".format(min_ex()), 3)
131
151
  self.build_fallback()
132
152
 
133
153
  assert self.tab