mirror of
https://github.com/PR0M3TH3AN/bitvid.git
synced 2026-11-05 04:12:25 +00:00
Decode magnet URIs before torrent playback
This commit is contained in:
1 parent
c88bc404b6
commit
db43e3634b
5 files changed
+103
-15
No files matched your search
@@ -5,7 +5,7 @@ import { nostrClient } from "./nostr.js";
|
||||
import { torrentClient } from "./webtorrent.js";
|
||||
import { isDevMode } from "./config.js";
|
||||
import { isWhitelistEnabled } from "./config.js";
|
||||
import { normalizeAndAugmentMagnet } from "./magnetUtils.js";
|
||||
import { normalizeAndAugmentMagnet, safeDecodeMagnet } from "./magnetUtils.js";
|
||||
import { deriveTorrentPlaybackConfig } from "./playbackUtils.js";
|
||||
import {
|
||||
initialWhitelist,
|
||||
@@ -38,12 +38,15 @@ function isValidMagnetUri(magnet) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (/^[0-9a-f]{40}$/i.test(trimmed)) {
|
||||
const decoded = safeDecodeMagnet(trimmed);
|
||||
const candidate = decoded || trimmed;
|
||||
|
||||
if (/^[0-9a-f]{40}$/i.test(candidate)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
try {
|
||||
const parsed = new URL(trimmed);
|
||||
const parsed = new URL(candidate);
|
||||
if (parsed.protocol.toLowerCase() !== "magnet:") {
|
||||
return false;
|
||||
}
|
||||
@@ -2227,8 +2230,10 @@ class bitvidApp {
|
||||
const legacyInfoHash =
|
||||
typeof video.infoHash === "string" ? video.infoHash.trim().toLowerCase() : "";
|
||||
const magnetCandidate = rawMagnet || legacyInfoHash;
|
||||
const magnetSupported = isValidMagnetUri(magnetCandidate);
|
||||
const sanitizedMagnet = magnetSupported ? magnetCandidate : "";
|
||||
const decodedMagnetCandidate = safeDecodeMagnet(magnetCandidate);
|
||||
const usableMagnetCandidate = decodedMagnetCandidate || magnetCandidate;
|
||||
const magnetSupported = isValidMagnetUri(usableMagnetCandidate);
|
||||
const sanitizedMagnet = magnetSupported ? usableMagnetCandidate : "";
|
||||
|
||||
this.currentVideo = {
|
||||
...video,
|
||||
@@ -2297,7 +2302,7 @@ class bitvidApp {
|
||||
|
||||
await this.playVideoWithFallback({
|
||||
url: trimmedUrl,
|
||||
magnet: magnetCandidate,
|
||||
magnet: usableMagnetCandidate,
|
||||
infoHash: legacyInfoHash,
|
||||
title: video.title,
|
||||
description: video.description,
|
||||
@@ -2312,8 +2317,10 @@ class bitvidApp {
|
||||
} = {}) {
|
||||
const sanitizedUrl = typeof url === "string" ? url.trim() : "";
|
||||
const trimmedMagnet = typeof magnet === "string" ? magnet.trim() : "";
|
||||
const magnetSupported = isValidMagnetUri(trimmedMagnet);
|
||||
const sanitizedMagnet = magnetSupported ? trimmedMagnet : "";
|
||||
const decodedMagnet = safeDecodeMagnet(trimmedMagnet);
|
||||
const usableMagnet = decodedMagnet || trimmedMagnet;
|
||||
const magnetSupported = isValidMagnetUri(usableMagnet);
|
||||
const sanitizedMagnet = magnetSupported ? usableMagnet : "";
|
||||
|
||||
if (!sanitizedUrl && !sanitizedMagnet) {
|
||||
const message = trimmedMagnet && !magnetSupported
|
||||
@@ -2368,7 +2375,7 @@ class bitvidApp {
|
||||
|
||||
await this.playVideoWithFallback({
|
||||
url: sanitizedUrl,
|
||||
magnet: trimmedMagnet,
|
||||
magnet: usableMagnet,
|
||||
title,
|
||||
description,
|
||||
});
|
||||
|
||||
@@ -51,6 +51,38 @@ function sanitizeHttpUrl(candidate) {
|
||||
return "";
|
||||
}
|
||||
|
||||
export function safeDecodeMagnet(value) {
|
||||
if (typeof value !== "string") {
|
||||
return "";
|
||||
}
|
||||
|
||||
let decoded = value.trim();
|
||||
if (!decoded) {
|
||||
return "";
|
||||
}
|
||||
|
||||
for (let i = 0; i < 2; i += 1) {
|
||||
if (!decoded.includes("%")) {
|
||||
break;
|
||||
}
|
||||
|
||||
try {
|
||||
const candidate = decodeURIComponent(decoded);
|
||||
if (!candidate) {
|
||||
break;
|
||||
}
|
||||
if (candidate === decoded) {
|
||||
break;
|
||||
}
|
||||
decoded = candidate.trim();
|
||||
} catch (err) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return decoded;
|
||||
}
|
||||
|
||||
export function normalizeAndAugmentMagnet(
|
||||
rawValue,
|
||||
{
|
||||
|
||||
+12
-6
@@ -1,6 +1,9 @@
|
||||
// js/playbackUtils.js
|
||||
|
||||
import { normalizeAndAugmentMagnet } from "./magnetUtils.js";
|
||||
import {
|
||||
normalizeAndAugmentMagnet,
|
||||
safeDecodeMagnet,
|
||||
} from "./magnetUtils.js";
|
||||
|
||||
const HEX_INFO_HASH = /^[0-9a-f]{40}$/i;
|
||||
const MAGNET_URI = /^magnet:\?/i;
|
||||
@@ -13,18 +16,21 @@ export function deriveTorrentPlaybackConfig({
|
||||
appProtocol,
|
||||
} = {}) {
|
||||
const trimmedMagnet = typeof magnet === "string" ? magnet.trim() : "";
|
||||
const decodedMagnet = safeDecodeMagnet(trimmedMagnet);
|
||||
const magnetCandidate = decodedMagnet || trimmedMagnet;
|
||||
const trimmedInfoHash =
|
||||
typeof infoHash === "string" ? infoHash.trim().toLowerCase() : "";
|
||||
const sanitizedUrl = typeof url === "string" ? url.trim() : "";
|
||||
|
||||
const magnetIsUri = MAGNET_URI.test(trimmedMagnet);
|
||||
const magnetLooksLikeInfoHash = HEX_INFO_HASH.test(trimmedMagnet);
|
||||
const magnetIsUri = MAGNET_URI.test(magnetCandidate);
|
||||
const magnetLooksLikeInfoHash = HEX_INFO_HASH.test(magnetCandidate);
|
||||
const resolvedInfoHash = trimmedInfoHash || (magnetLooksLikeInfoHash
|
||||
? trimmedMagnet.toLowerCase()
|
||||
? magnetCandidate.toLowerCase()
|
||||
: "");
|
||||
|
||||
const normalizationInput = magnetIsUri ? trimmedMagnet : resolvedInfoHash;
|
||||
const normalizationInput = magnetIsUri ? magnetCandidate : resolvedInfoHash;
|
||||
const provided = Boolean(trimmedMagnet || trimmedInfoHash);
|
||||
const decodeChanged = magnetCandidate !== trimmedMagnet;
|
||||
|
||||
if (!normalizationInput) {
|
||||
return {
|
||||
@@ -66,7 +72,7 @@ export function deriveTorrentPlaybackConfig({
|
||||
provided,
|
||||
usedInfoHash,
|
||||
originalInput: normalizationInput,
|
||||
didMutate: normalization.didChange || usedInfoHash,
|
||||
didMutate: normalization.didChange || usedInfoHash || decodeChanged,
|
||||
infoHash: resolvedInfoHash,
|
||||
};
|
||||
}
|
||||
@@ -96,4 +96,25 @@ const LEGACY_INFO_HASH = "0123456789abcdef0123456789abcdef01234567";
|
||||
assert.equal(result.provided, true);
|
||||
})();
|
||||
|
||||
(function testPlaybackConfigDecodesEncodedMagnet() {
|
||||
const rawMagnet =
|
||||
`magnet:?xt=urn:btih:${LEGACY_INFO_HASH}&dn=Legacy+Example`;
|
||||
const encodedMagnet = encodeURIComponent(rawMagnet);
|
||||
const result = deriveTorrentPlaybackConfig({
|
||||
magnet: encodedMagnet,
|
||||
infoHash: "",
|
||||
url: "",
|
||||
});
|
||||
|
||||
assert.ok(result.magnet.startsWith("magnet:?"));
|
||||
const parsed = new URL(result.magnet);
|
||||
assert.equal(
|
||||
parsed.searchParams.get("xt"),
|
||||
`urn:btih:${LEGACY_INFO_HASH}`,
|
||||
"Expected encoded magnet to be normalized"
|
||||
);
|
||||
assert.equal(result.provided, true);
|
||||
assert.equal(result.usedInfoHash, false);
|
||||
})();
|
||||
|
||||
console.log("legacy infohash tests passed");
|
||||
@@ -2,6 +2,7 @@ import assert from "node:assert/strict";
|
||||
import {
|
||||
DEFAULT_WSS_TRACKERS,
|
||||
normalizeAndAugmentMagnet,
|
||||
safeDecodeMagnet,
|
||||
} from "../js/magnetUtils.js";
|
||||
|
||||
function getParamValues(magnet, key) {
|
||||
@@ -92,4 +93,25 @@ function getParamValues(magnet, key) {
|
||||
assert.deepEqual(seeds, ["http://cdn.example.com/video.mp4"]);
|
||||
})();
|
||||
|
||||
(function testSafeDecodeMagnetHandlesEncodedValues() {
|
||||
const rawMagnet =
|
||||
"magnet:?xt=urn:btih:0123456789abcdef0123456789abcdef01234567&dn=Example";
|
||||
const encodedMagnet = encodeURIComponent(rawMagnet);
|
||||
assert.equal(
|
||||
safeDecodeMagnet(encodedMagnet),
|
||||
rawMagnet,
|
||||
"Expected percent-encoded magnet to be decoded"
|
||||
);
|
||||
})();
|
||||
|
||||
(function testSafeDecodeMagnetLeavesPlainStringsUntouched() {
|
||||
const rawMagnet =
|
||||
"magnet:?xt=urn:btih:abcdefabcdefabcdefabcdefabcdefabcdefabcd";
|
||||
assert.equal(
|
||||
safeDecodeMagnet(rawMagnet),
|
||||
rawMagnet,
|
||||
"Plain magnet strings should pass through unchanged"
|
||||
);
|
||||
})();
|
||||
|
||||
console.log("magnet-utils tests passed");
|
||||
Reference in new issue
Block a user