pmtiles-swarm 0.43.0 → 0.44.0

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.
package/CHANGELOG.md CHANGED
@@ -7,6 +7,20 @@
7
7
  ### 🐞 Bug fixes
8
8
  - _...Add new stuff here..._
9
9
 
10
+ ## 0.44.0
11
+ ### ✨ Features and improvements
12
+ - **Web seeds are added where they are listed.** The **HTTP sources** tab now has a field and an Add
13
+ button under the list, so adding one shows what is already there and what the addition did. It
14
+ replaces the **Add web seed** button in Actions, which opened a `prompt()` a long way from the list
15
+ it changed — one way to do this rather than two.
16
+
17
+ The tab now also says why this is safe on a published archive: `url-list` sits outside the info
18
+ dictionary, so the infohash is unchanged and every magnet and peer already relying on it keeps
19
+ working. The result distinguishes "added" from "the engine has taken it", which are different
20
+ claims and the second is the one that matters.
21
+
22
+ ### 🐞 Bug fixes
23
+
10
24
  ## 0.43.0
11
25
  ### ✨ Features and improvements
12
26
  - **Six detail tabs are now five, and the two bars that matter lead the General tab.** How much of an
@@ -151,6 +151,22 @@ Adding from a **local path** never publishes a seed unless you ask: a web seed
151
151
  appears only when `webSeedBase` is set, and `publishDir` moves the file without
152
152
  implying one.
153
153
 
154
+ ### Adding one later
155
+
156
+ An archive already in circulation can be given a web seed at any time, from the
157
+ **HTTP sources** tab of its details or with
158
+ `POST /api/torrents/<infohash>/webseeds`. This is safe on a published torrent:
159
+ `url-list` sits outside the info dictionary, so the infohash does not change and
160
+ every magnet, tracker and peer already relying on it keeps working.
161
+
162
+ The URL has to serve the whole archive, byte for byte, under the name the torrent
163
+ gives it. A seed that answers with anything else is not a faster path to the
164
+ data — peers will fetch from it, fail the piece hash, and fall back to the swarm
165
+ having wasted the transfer.
166
+
167
+ Worth doing whenever an archive gets a public home after the fact: a swarm with
168
+ one web seed starts cold in seconds rather than waiting for a peer to appear.
169
+
154
170
  ## Building from a feed of source data
155
171
 
156
172
  Subscribing is not only for finished archives. The OpenStreetMap project
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pmtiles-swarm",
3
- "version": "0.43.0",
3
+ "version": "0.44.0",
4
4
  "description": "BitTorrent distribution for PMTiles map archives: create torrents, watch folders, publish and subscribe to RSS feeds, and seed through qBittorrent or an embedded client",
5
5
  "type": "module",
6
6
  "main": "src/index.js",
@@ -1728,12 +1728,63 @@
1728
1728
  if (name === 'sources') {
1729
1729
  const seeds = entry.webSeeds ?? [];
1730
1730
  pane.innerHTML =
1731
- table(['URL'], seeds.map((url) => [escapeHtml(url)])) +
1732
- `<div class="sub" style="margin-top:0.5rem">
1731
+ table(
1732
+ ['URL'],
1733
+ seeds.map((url) => [escapeHtml(url)]),
1734
+ ) +
1735
+ `<div style="display:flex; gap:0.4rem; margin-top:0.75rem">
1736
+ <input
1737
+ id="seed-url"
1738
+ class="grow"
1739
+ placeholder="https://example.org/${escapeHtml(entry.name)}"
1740
+ spellcheck="false"
1741
+ />
1742
+ <button id="seed-add">Add</button>
1743
+ </div>
1744
+ <div class="sub" style="margin-top:0.5rem">
1733
1745
  A web seed serves the archive over plain HTTP, which is what
1734
1746
  makes it usable before it has peers — and the difference
1735
- between a cold tile taking tens of seconds and under one.
1747
+ between a cold tile taking tens of seconds and under one. One
1748
+ can be added at any time: <code>url-list</code> sits outside
1749
+ the info dictionary, so the infohash is unchanged and every
1750
+ magnet and peer already relying on it keeps working. The URL
1751
+ must serve the whole archive, byte for byte, under the name
1752
+ the torrent gives it.
1736
1753
  </div>`;
1754
+
1755
+ // Attached here rather than in the panel, because this pane is
1756
+ // rebuilt whenever it is shown and the handler has to survive that.
1757
+ // Safe: sources is not one of the panes the refresh redraws.
1758
+ const add = async () => {
1759
+ const url = pane.querySelector('#seed-url').value.trim();
1760
+ if (!url) return;
1761
+ const button = pane.querySelector('#seed-add');
1762
+ button.disabled = true;
1763
+ try {
1764
+ const result = await api(
1765
+ `/api/torrents/${infoHash}/webseeds`,
1766
+ { method: 'POST', body: { webSeeds: [url] } },
1767
+ );
1768
+ // "Added" and "the engine is using it" are different claims,
1769
+ // and the second is the one somebody adding a seed wants.
1770
+ toast(
1771
+ result.live
1772
+ ? 'web seed added and live'
1773
+ : 'web seed added; the engine has not taken it yet',
1774
+ );
1775
+ renderDetail(infoHash);
1776
+ } catch (error) {
1777
+ toast(error.message);
1778
+ button.disabled = false;
1779
+ }
1780
+ };
1781
+
1782
+ pane.querySelector('#seed-add').onclick = add;
1783
+ pane.querySelector('#seed-url').onkeydown = (event) => {
1784
+ // Enter, because a single field with a button beside it is a
1785
+ // form in everything but name.
1786
+ if (event.key === 'Enter') add();
1787
+ };
1737
1788
  return;
1738
1789
  }
1739
1790
 
@@ -1976,7 +2027,6 @@
1976
2027
  <div class="actions">
1977
2028
  ${servable ? '<button id="warm">Warm region</button>' : ''}
1978
2029
  <button id="set-location">Set location…</button>
1979
- <button id="add-seed">Add web seed</button>
1980
2030
  <button id="clear-cache" ${mode === 'cache' ? '' : 'disabled title="only cache-mode archives have a cache to clear"'}>Clear cache</button>
1981
2031
  <button id="recheck" title="Hash the files on disk again and believe the result. For an archive that reads 0% next to a file that is plainly there, or one that claims to be complete and is not — every other figure comes from something written down earlier, and this is the only thing that goes and looks.">Recheck files</button>
1982
2032
  <button id="pause">${entry.paused ? 'Resume' : 'Pause'}</button>
@@ -2248,24 +2298,6 @@
2248
2298
  };
2249
2299
  };
2250
2300
 
2251
- $('add-seed').onclick = async () => {
2252
- const url = window.prompt(
2253
- 'Web seed URL (adding one does not change the infohash):',
2254
- `https://example.org/${entry.name}`,
2255
- );
2256
- if (!url) return;
2257
- try {
2258
- const result = await api(`/api/torrents/${infoHash}/webseeds`, {
2259
- method: 'POST',
2260
- body: { webSeeds: [url] },
2261
- });
2262
- toast(result.live ? 'web seed added and live' : 'web seed added');
2263
- renderDetail(infoHash);
2264
- } catch (error) {
2265
- toast(error.message);
2266
- }
2267
- };
2268
-
2269
2301
  $('clear-cache').onclick = async () => {
2270
2302
  if (!window.confirm('Discard this archive’s cached pieces?')) return;
2271
2303
  try {