Skip to content

Commit

Permalink
Fixed #938: added hack for mirrobrain magnet URIs
Browse files Browse the repository at this point in the history
Missing parameters are added to the magnet link for it to work properly
given it is mainly based on webseeds.
Each mirror serving the file is added as a webseed.
Params that requires URIEncoding are now encoded.

Introduces `makeURLSearchString()` to turn SearchParams into a string but only
URIEncoding some specific params.
  • Loading branch information
rgaudin committed Oct 6, 2023
1 parent 9166b67 commit e5fad92
Showing 1 changed file with 55 additions and 0 deletions.
55 changes: 55 additions & 0 deletions static/skin/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -197,13 +197,68 @@
}
}

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();
setTimeout(() => controller.abort(), 5000);
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;
}

Expand Down

0 comments on commit e5fad92

Please sign in to comment.