Add btih extraction helper

This commit is contained in:
thePR0M3TH3AN committed 2026-01-26 21:49:18 -05:00
1 parent 0897106c2e
commit 50793430a8
5 files changed
+107 -63

No files matched your search

+4 -4
View File
@@ -13,7 +13,7 @@ import {
ALLOW_NSFW_CONTENT,
} from "./config.js";
import { accessControl } from "./accessControl.js";
import { safeDecodeMagnet } from "./magnetUtils.js";
import { extractBtihFromMagnet, safeDecodeMagnet } from "./magnetUtils.js";
import { deriveTorrentPlaybackConfig } from "./playbackUtils.js";
import {
URL_FIRST_ENABLED,
@@ -8571,9 +8571,9 @@ class Application {
typeof video.infoHash === "string" ? video.infoHash.trim().toLowerCase() : "";
const fallbackMagnetForCandidate = fallbackMagnetCandidate || "";
if (!legacyInfoHash && fallbackMagnetForCandidate) {
const match = fallbackMagnetForCandidate.match(/xt=urn:btih:([0-9a-z]+)/i);
if (match && match[1]) {
legacyInfoHash = match[1].toLowerCase();
const extracted = extractBtihFromMagnet(fallbackMagnetForCandidate);
if (extracted) {
legacyInfoHash = extracted;
}
}
+92 -1
View File
@@ -5,9 +5,11 @@ import {
} from "./utils/safeDecode.js";
const HEX_INFO_HASH = /^[0-9a-f]{40}$/i;
const BASE32_INFO_HASH = /^[a-z2-7]{32}$/i;
const BTIH_PREFIX = "urn:btih:";
const MAGNET_SCHEME = "magnet:";
const ENCODED_BTih_PATTERN = /xt=urn%3Abtih%3A([0-9a-z]+)/gi;
const BASE32_ALPHABET = "abcdefghijklmnopqrstuvwxyz234567";
export function safeDecodeMagnet(value) {
if (typeof value !== "string") {
@@ -56,6 +58,96 @@ export function normalizeForComparison(value) {
}
}
function decodeBase32ToHex(value) {
if (typeof value !== "string" || !value) {
return "";
}
const normalized = value.toLowerCase();
let bits = 0;
let buffer = 0;
const bytes = [];
for (const char of normalized) {
const index = BASE32_ALPHABET.indexOf(char);
if (index === -1) {
return "";
}
buffer = (buffer << 5) | index;
bits += 5;
if (bits >= 8) {
bits -= 8;
const byte = (buffer >> bits) & 0xff;
bytes.push(byte);
}
}
if (!bytes.length) {
return "";
}
return bytes.map((byte) => byte.toString(16).padStart(2, "0")).join("");
}
export function normalizeInfoHash(value) {
const trimmed = typeof value === "string" ? value.trim() : "";
if (!trimmed) {
return "";
}
if (HEX_INFO_HASH.test(trimmed)) {
return trimmed.toLowerCase();
}
if (BASE32_INFO_HASH.test(trimmed)) {
const hex = decodeBase32ToHex(trimmed);
return hex ? hex.toLowerCase() : "";
}
return "";
}
function extractInfoHashFromXt(value) {
if (typeof value !== "string") {
return "";
}
const trimmed = value.trim();
if (!trimmed) {
return "";
}
const lower = trimmed.toLowerCase();
if (!lower.startsWith(BTIH_PREFIX)) {
return "";
}
return normalizeInfoHash(trimmed.slice(BTIH_PREFIX.length));
}
export function extractBtihFromMagnet(rawValue) {
const decoded = safeDecodeMagnet(rawValue);
const { initial, isMagnet, params } = normalizeMagnetInput(decoded);
if (!initial) {
return "";
}
if (!isMagnet) {
if (initial.toLowerCase().startsWith(BTIH_PREFIX)) {
return normalizeInfoHash(initial.slice(BTIH_PREFIX.length));
}
return normalizeInfoHash(initial);
}
for (const param of params) {
if (param.lowerKey !== "xt") {
continue;
}
const candidate = param.decoded || param.value;
const normalized = extractInfoHashFromXt(candidate);
if (normalized) {
return normalized;
}
}
return "";
}
function createParam(key, value) {
const trimmedValue = typeof value === "string" ? value.trim() : "";
const decoded = safeDecodeURIComponentLoose(trimmedValue);
@@ -342,4 +434,3 @@ export function extractMagnetHints(rawValue) {
}
return hints;
}
+4
View File
@@ -6,13 +6,17 @@ import {
ensureTrackers,
ensureTorrentHint,
ensureWebSeeds,
extractBtihFromMagnet as sharedExtractBtihFromMagnet,
normalizeMagnetInput,
normalizeInfoHash as sharedNormalizeInfoHash,
resolveAppProtocol,
safeDecodeMagnet as sharedSafeDecodeMagnet,
} from "./magnetShared.js";
export { WSS_TRACKERS };
export const safeDecodeMagnet = sharedSafeDecodeMagnet;
export const extractBtihFromMagnet = sharedExtractBtihFromMagnet;
export const normalizeInfoHash = sharedNormalizeInfoHash;
export function normalizeAndAugmentMagnet(
rawValue,
+3 -54
View File
@@ -1,35 +1,4 @@
const BASE32_ALPHABET = "abcdefghijklmnopqrstuvwxyz234567";
function decodeBase32ToHex(value) {
if (typeof value !== "string" || !value) {
return "";
}
const normalized = value.toLowerCase();
let bits = 0;
let buffer = 0;
const bytes = [];
for (const char of normalized) {
const index = BASE32_ALPHABET.indexOf(char);
if (index === -1) {
return "";
}
buffer = (buffer << 5) | index;
bits += 5;
if (bits >= 8) {
bits -= 8;
const byte = (buffer >> bits) & 0xff;
bytes.push(byte);
}
}
if (bytes.length === 0) {
return "";
}
return bytes.map((byte) => byte.toString(16).padStart(2, "0")).join("");
}
import { extractBtihFromMagnet, normalizeInfoHash } from "./magnetShared.js";
function extractInfoHashFromXt(value) {
if (typeof value !== "string") {
@@ -50,32 +19,12 @@ function extractInfoHashFromXt(value) {
return normalizeInfoHash(payload);
}
function normalizeInfoHash(candidate) {
const trimmed = typeof candidate === "string" ? candidate.trim() : "";
if (!trimmed) {
return null;
}
if (/^[0-9a-f]{40}$/i.test(trimmed)) {
return trimmed.toLowerCase();
}
if (/^[a-z2-7]{32}$/i.test(trimmed)) {
const hex = decodeBase32ToHex(trimmed);
return hex ? hex.toLowerCase() : null;
}
return null;
}
export function infoHashFromMagnet(magnet) {
if (typeof magnet !== "string") {
return null;
}
const direct = normalizeInfoHash(magnet);
if (direct) {
return direct;
}
const parsed = parseMagnetLite(magnet);
const hash = typeof parsed.infoHash === "string" ? parsed.infoHash : "";
return hash ? hash.toLowerCase() : null;
const extracted = extractBtihFromMagnet(magnet);
return extracted || null;
}
export function trackersFromMagnet(magnet) {
+4 -4
View File
@@ -1,5 +1,5 @@
import { deriveTitleFromEvent } from "../videoEventUtils.js";
import { extractMagnetHints } from "../magnetShared.js";
import { extractBtihFromMagnet, extractMagnetHints } from "../magnetShared.js";
import { devLogger } from "../utils/logger.js";
import { getCachedNostrTools } from "./toolkit.js";
@@ -1432,9 +1432,9 @@ export function convertEventToVideo(event = {}) {
const originalFileSha256 = normalizeSha256(parsedContent.originalFileSha256);
if (!infoHash && magnet) {
const match = magnet.match(/xt=urn:btih:([0-9a-z]+)/i);
if (match && match[1]) {
pushInfoHash(match[1]);
const extracted = extractBtihFromMagnet(magnet);
if (extracted) {
infoHash = extracted;
}
}