diff --git a/static/skin/index.js b/static/skin/index.js
index b93253aaa..c8c346003 100644
--- a/static/skin/index.js
+++ b/static/skin/index.js
@@ -197,6 +197,58 @@
}
}
+ function makeURLSearchString(params, keysToURIEncode) {
+ let output = '';
+ for (const [key, value] of params.entries()) {
+ let finalValue = (keysToURIEncode.indexOf(key) >= 0) ? encodeURIComponent(value) : value;
+ output += `&${key}=${finalValue}`;
+ }
+ return output;
+ }
+
+ /* hack for library.kiwix.org magnet links (created by MirrorBrain)
+ See https://github.com/kiwix/container-images/issues/242 */
+ async function getFixedMirrorbrainMagnet(magnetLink) {
+ // parse as query parameters
+ const params = new URLSearchParams(
+ magnetLink.replaceAll('&', '&').replace(/^magnet:/, ''));
+
+ const zimUrl = params.get('as'); // as= is fallback URL
+ // download metalink to build list of mirrored URLs
+ let mirrorUrls = [];
+
+ const metalink = await fetch(`${zimUrl}.meta4`).then(response => {
+ return response.ok ? response.text() : '';
+ }).catch((_error) => '');
+ if (metalink) {
+ try {
+ const parser = new DOMParser();
+ const doc = parser.parseFromString(metalink, "application/xml");
+ doc.querySelectorAll("url").forEach((node) => {
+ if (node.hasAttribute("priority")) { // ensures its a mirror link
+ mirrorUrls.push(node.innerHTML);
+ }
+ });
+ } catch (err) {
+ // not a big deal, magnet will only contain primary URL
+ console.debug(`Failed to parse mirror links for ${zimUrl}`);
+ }
+ }
+
+ // set webseed (ws=) URL to primary download URL (redirects to mirror)
+ params.set('ws', zimUrl);
+ // if we got metalink mirror URLs, append them all
+ if (mirrorUrls) {
+ mirrorUrls.forEach((url) => {
+ params.append('ws', url);
+ });
+ }
+
+ params.set('xs', `${zimUrl}.torrent`); // adding xs= to point to torrent URL
+
+ return 'magnet:?' + makeURLSearchString(params, ['ws', 'as', 'dn', 'xs', 'tr']);
+ }
+
async function getMagnetLink(downloadLink) {
const magnetUrl = downloadLink + '.magnet';
const controller = new AbortController();
@@ -204,6 +256,9 @@
const magnetLink = await fetch(magnetUrl, { signal: controller.signal }).then(response => {
return response.ok ? response.text() : '';
}).catch((_error) => '');
+ if (magnetLink) {
+ return await getFixedMirrorbrainMagnet(magnetLink);
+ }
return magnetLink;
}
diff --git a/test/server.cpp b/test/server.cpp
index a565a3536..684e7e6b3 100644
--- a/test/server.cpp
+++ b/test/server.cpp
@@ -63,7 +63,7 @@ const ResourceCollection resources200Compressible{
{ DYNAMIC_CONTENT, "/ROOT%23%3F/skin/index.css" },
{ STATIC_CONTENT, "/ROOT%23%3F/skin/index.css?cacheid=e4d76d16" },
{ DYNAMIC_CONTENT, "/ROOT%23%3F/skin/index.js" },
- { STATIC_CONTENT, "/ROOT%23%3F/skin/index.js?cacheid=e1b1ae55" },
+ { STATIC_CONTENT, "/ROOT%23%3F/skin/index.js?cacheid=ce19da2a" },
{ DYNAMIC_CONTENT, "/ROOT%23%3F/skin/iso6391To3.js" },
{ STATIC_CONTENT, "/ROOT%23%3F/skin/iso6391To3.js?cacheid=ecde2bb3" },
{ DYNAMIC_CONTENT, "/ROOT%23%3F/skin/isotope.pkgd.min.js" },
@@ -288,7 +288,7 @@ R"EXPECTEDRESULT( href="/ROOT%23%3F/skin/index.css?cacheid=e4d76d16"
-
+