Finished API integration

This commit is contained in:
2025-10-30 21:24:32 +03:30
parent 305170f185
commit 03d13d432a
11 changed files with 636 additions and 339 deletions
+32
View File
@@ -0,0 +1,32 @@
import { getMagnetUrl, parseMagnet } from "~~/server/utils/magnetParser";
import { addTrackers, getTrackers } from "~~/server/utils/trackerHelper";
export default defineEventHandler(async (event) => {
const body = await readBody<{ magnet?: string }>(event);
const magnetUrl = body?.magnet;
if (!magnetUrl) {
throw createError({
statusCode: 400,
statusMessage: "Missing magnet URL",
});
}
try {
const maxedMagnet = addTrackers(
parseMagnet(magnetUrl),
await getTrackers()
);
return {
totalTrackers: maxedMagnet.tr?.length,
name: maxedMagnet.dn,
id: maxedMagnet.xt.slice(9),
maxedMagnet: getMagnetUrl(maxedMagnet),
};
} catch (error: any) {
throw createError({
statusCode: 400,
statusMessage: error.message || "Invalid magnet URL",
});
}
});
+68
View File
@@ -0,0 +1,68 @@
import type { MagnetUrl } from "../../shared/types/magnet";
export function parseMagnet(magnetUrl: string): MagnetUrl {
if (!magnetUrl.startsWith("magnet:?")) {
throw new Error('Invalid magnet URL: Must start with "magnet:?"');
}
const params = new URLSearchParams(magnetUrl.substring(8)); // Remove 'magnet:?' prefix
const xt = params.get("xt");
if (!xt) {
throw new Error("Invalid magnet URL: Missing required xt parameter");
}
// Validate xt format (btih hash)
const btihRegex = /^urn:btih:([a-fA-F0-9]{40}|[A-Z2-7]{32})$/;
if (!btihRegex.test(xt)) {
throw new Error("Invalid magnet URL: Invalid xt parameter format");
}
const magnet: MagnetUrl = {
xt,
};
const dn = params.get("dn");
if (dn) {
magnet.dn = decodeURIComponent(dn);
}
const tr = params.getAll("tr");
if (tr.length > 0) {
magnet.tr = tr.map(decodeURIComponent);
}
const as = params.getAll("as");
if (as.length > 0) {
magnet.as = as.map(decodeURIComponent);
}
const xs = params.getAll("xs");
if (xs.length > 0) {
magnet.xs = xs.map(decodeURIComponent);
}
const kt = params.get("kt");
if (kt) {
magnet.kt = decodeURIComponent(kt);
}
const mt = params.get("mt");
if (mt) {
magnet.mt = decodeURIComponent(mt);
}
return magnet;
}
export function getMagnetUrl(magnet: MagnetUrl): string {
const params = new URLSearchParams();
Object.entries(magnet).forEach(([key, value]) => {
if (Array.isArray(value)) {
value.forEach((v) => params.append(`${key}`, v));
} else {
params.append(`${key}`, value);
}
});
return `magnet:?${params.toString()}`;
}
+54
View File
@@ -0,0 +1,54 @@
import type { MagnetUrl } from "../../shared/types/magnet";
let cachedTrackers: string[] | null = null;
let lastFetched = 0;
const CACHE_TTL = 1000 * 60 * 10; // 10 minutes
export async function getTrackers(): Promise<string[]> {
const now = Date.now();
if (cachedTrackers && now - lastFetched < CACHE_TTL) {
return cachedTrackers;
}
const TRACKER_URLS = [
"https://raw.githubusercontent.com/XIU2/TrackersListCollection/refs/heads/master/all.txt",
"https://raw.githubusercontent.com/ngosang/trackerslist/refs/heads/master/trackers_all.txt",
];
for (const url of TRACKER_URLS) {
try {
const response = await fetch(url);
if (response.ok) {
const text = await response.text();
const trackers = text
.split("\n")
.map((line) => line.trim())
.filter((line) => line !== "");
// update cache
cachedTrackers = trackers;
lastFetched = now;
return trackers;
}
} catch (err) {
console.error(err);
}
}
// fallback: return cached even if stale, or empty
return cachedTrackers ?? [];
}
export function addTrackers(magnet: MagnetUrl, trackers: string[]): MagnetUrl {
if (trackers.length > 0) {
// avoid duplicates
const existing = new Set(magnet.tr ?? []);
for (const tracker of trackers) {
if (!existing.has(tracker)) {
magnet.tr?.push(tracker);
}
}
}
return magnet;
}