mirror of
https://github.com/PR0M3TH3AN/bitvid.git
synced 2026-03-12 05:52:33 +00:00
171 lines
4.1 KiB
JavaScript
171 lines
4.1 KiB
JavaScript
import { WSS_TRACKERS } from "./constants.js";
|
|
|
|
const HEX_INFO_HASH = /^[0-9a-f]{40}$/i;
|
|
const ENCODED_BTih_PATTERN = /xt=urn%3Abtih%3A([0-9a-z]+)/gi;
|
|
|
|
function getOriginProtocol() {
|
|
if (typeof window !== "undefined" && window.location?.protocol) {
|
|
return window.location.protocol.toLowerCase();
|
|
}
|
|
return "https:";
|
|
}
|
|
|
|
function decodeLoose(value) {
|
|
if (typeof value !== "string") {
|
|
return "";
|
|
}
|
|
const trimmed = value.trim();
|
|
if (!trimmed) {
|
|
return "";
|
|
}
|
|
try {
|
|
return decodeURIComponent(trimmed);
|
|
} catch (err) {
|
|
return trimmed;
|
|
}
|
|
}
|
|
|
|
function normalizeForComparison(value) {
|
|
if (typeof value !== "string") {
|
|
return "";
|
|
}
|
|
return value.trim().replace(/\/+$/, "").toLowerCase();
|
|
}
|
|
|
|
function tryFormatAbsoluteUrl(candidate) {
|
|
if (typeof candidate !== "string") {
|
|
return "";
|
|
}
|
|
const trimmed = candidate.trim();
|
|
if (!trimmed) {
|
|
return "";
|
|
}
|
|
try {
|
|
const parsed = new URL(trimmed);
|
|
return parsed.toString();
|
|
} catch (err) {
|
|
return trimmed;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Normalize and enrich a magnet string entered in the upload form so that it
|
|
* always produces a WebTorrent-friendly URI. The logic mirrors the behaviour of
|
|
* the legacy magnetUtils helper but returns a plain string for ease of use.
|
|
*/
|
|
export function normalizeAndAugmentMagnet(rawValue, { ws = "", xs = "" } = {}) {
|
|
const initial = typeof rawValue === "string" ? rawValue.trim() : "";
|
|
if (!initial) {
|
|
return "";
|
|
}
|
|
|
|
let working = initial;
|
|
|
|
if (HEX_INFO_HASH.test(working)) {
|
|
working = `magnet:?xt=urn:btih:${working.toLowerCase()}`;
|
|
}
|
|
|
|
working = working.replace(ENCODED_BTih_PATTERN, (_, hash) => `xt=urn:btih:${hash}`);
|
|
|
|
if (!/^magnet:/i.test(working)) {
|
|
return working;
|
|
}
|
|
|
|
let fragment = "";
|
|
const hashIndex = working.indexOf("#");
|
|
if (hashIndex !== -1) {
|
|
fragment = working.slice(hashIndex);
|
|
working = working.slice(0, hashIndex);
|
|
}
|
|
|
|
const [, queryPart = ""] = working.split("?", 2);
|
|
const normalizedScheme = "magnet:";
|
|
const rawParams = queryPart
|
|
.split("&")
|
|
.map((part) => part.trim())
|
|
.filter(Boolean);
|
|
|
|
const params = [];
|
|
|
|
for (const rawParam of rawParams) {
|
|
const [rawKey, rawVal = ""] = rawParam.split("=", 2);
|
|
const key = rawKey.trim();
|
|
if (!key) {
|
|
continue;
|
|
}
|
|
let value = rawVal.trim();
|
|
if (key.toLowerCase() === "xt" && value) {
|
|
const decoded = decodeLoose(value);
|
|
if (decoded) {
|
|
value = decoded;
|
|
}
|
|
}
|
|
params.push({
|
|
key,
|
|
value,
|
|
lowerKey: key.toLowerCase(),
|
|
compareValue: normalizeForComparison(value),
|
|
});
|
|
}
|
|
|
|
const appendUniqueParam = (key, value) => {
|
|
if (!value) {
|
|
return;
|
|
}
|
|
const trimmedValue = value.trim();
|
|
if (!trimmedValue) {
|
|
return;
|
|
}
|
|
const lowerKey = key.toLowerCase();
|
|
const compareValue = normalizeForComparison(trimmedValue);
|
|
if (compareValue) {
|
|
const exists = params.some(
|
|
(param) => param.lowerKey === lowerKey && param.compareValue === compareValue
|
|
);
|
|
if (exists) {
|
|
return;
|
|
}
|
|
}
|
|
params.push({
|
|
key,
|
|
value: trimmedValue,
|
|
lowerKey,
|
|
compareValue,
|
|
});
|
|
};
|
|
|
|
for (const tracker of WSS_TRACKERS) {
|
|
if (typeof tracker !== "string") {
|
|
continue;
|
|
}
|
|
const trimmedTracker = tracker.trim();
|
|
if (!/^wss:\/\//i.test(trimmedTracker)) {
|
|
continue;
|
|
}
|
|
appendUniqueParam("tr", trimmedTracker);
|
|
}
|
|
|
|
const normalizedXs = typeof xs === "string" ? xs.trim() : "";
|
|
if (normalizedXs) {
|
|
appendUniqueParam("xs", normalizedXs);
|
|
}
|
|
|
|
const originProtocol = getOriginProtocol();
|
|
const rawWs = typeof ws === "string" ? ws.trim() : "";
|
|
if (rawWs) {
|
|
const formattedWs = tryFormatAbsoluteUrl(rawWs);
|
|
const lowerWs = formattedWs.toLowerCase();
|
|
const isHttpSeed = lowerWs.startsWith("http://");
|
|
const allowHttpSeed = originProtocol === "http:";
|
|
if (!isHttpSeed || allowHttpSeed) {
|
|
appendUniqueParam("ws", formattedWs);
|
|
}
|
|
}
|
|
|
|
const queryString = params
|
|
.map(({ key, value }) => (value ? `${key}=${value}` : key))
|
|
.join("&");
|
|
|
|
return `${normalizedScheme}${queryString ? `?${queryString}` : ""}${fragment}`;
|
|
}
|