mirror of
https://github.com/baairon/torlink.git
synced 2026-07-08 18:28:22 +02:00
feat: extract upload date for 1337x results
Parse the "Date uploaded" field from 1337x detail pages into TorrentResult.added, matching the other sources. Reuses the detail-page fetch already done for the magnet, so no extra requests are made.
This commit is contained in:
@@ -0,0 +1,23 @@
|
||||
import { describe, it, expect } from "vitest";
|
||||
import { parseUploadDate } from "./x1337";
|
||||
|
||||
const detail = (span: string) =>
|
||||
`<ul class="list"><li><strong>Date uploaded</strong><span>${span}</span> </li></ul>`;
|
||||
|
||||
describe("parseUploadDate", () => {
|
||||
it("parses the 'Mon. Dayth \\'YY' format to a UTC unix timestamp", () => {
|
||||
const ts = parseUploadDate(detail("Jun. 26th '26"));
|
||||
expect(ts).toBe(Math.floor(Date.UTC(2026, 5, 26) / 1000));
|
||||
});
|
||||
|
||||
it("handles single-digit days and other ordinals", () => {
|
||||
expect(parseUploadDate(detail("Jan. 1st '24"))).toBe(Math.floor(Date.UTC(2024, 0, 1) / 1000));
|
||||
expect(parseUploadDate(detail("Mar. 3rd '25"))).toBe(Math.floor(Date.UTC(2025, 2, 3) / 1000));
|
||||
expect(parseUploadDate(detail("Dec. 22nd '23"))).toBe(Math.floor(Date.UTC(2023, 11, 22) / 1000));
|
||||
});
|
||||
|
||||
it("returns undefined when the field is missing or unparseable", () => {
|
||||
expect(parseUploadDate("<div>no date here</div>")).toBeUndefined();
|
||||
expect(parseUploadDate(detail("sometime"))).toBeUndefined();
|
||||
});
|
||||
});
|
||||
+29
-6
@@ -46,11 +46,33 @@ async function fetchText(url: string, opts: SearchOptions, retries: number): Pro
|
||||
return res.text();
|
||||
}
|
||||
|
||||
async function detailMagnet(base: string, path: string, opts: SearchOptions): Promise<string | null> {
|
||||
const MONTHS: Record<string, number> = {
|
||||
jan: 0, feb: 1, mar: 2, apr: 3, may: 4, jun: 5,
|
||||
jul: 6, aug: 7, sep: 8, oct: 9, nov: 10, dec: 11,
|
||||
};
|
||||
|
||||
// 1337x detail pages render "Date uploaded" as e.g. "Jun. 26th '26".
|
||||
export function parseUploadDate(html: string): number | undefined {
|
||||
const m = html.match(/Date uploaded<\/strong>\s*<span>\s*([A-Za-z]{3})\.?\s+(\d{1,2})[a-z]{2}\s*'(\d{2})/i);
|
||||
if (!m) return undefined;
|
||||
const month = MONTHS[m[1]!.toLowerCase()];
|
||||
if (month === undefined) return undefined;
|
||||
const day = Number(m[2]);
|
||||
const year = 2000 + Number(m[3]);
|
||||
const secs = Math.floor(Date.UTC(year, month, day) / 1000);
|
||||
return Number.isNaN(secs) ? undefined : secs;
|
||||
}
|
||||
|
||||
async function detailInfo(
|
||||
base: string,
|
||||
path: string,
|
||||
opts: SearchOptions,
|
||||
): Promise<{ magnet: string; added?: number } | null> {
|
||||
try {
|
||||
const html = await fetchText(`${base}${path}`, opts, 1);
|
||||
const raw = html.match(/magnet:\?xt=urn:btih:[^"'<>\s]+/i)?.[0];
|
||||
return raw ? unescapeEntities(raw) : null;
|
||||
if (!raw) return null;
|
||||
return { magnet: unescapeEntities(raw), added: parseUploadDate(html) };
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
@@ -97,9 +119,9 @@ async function search(
|
||||
const rows = matched.slice(0, MAX_DETAILS);
|
||||
const settled = await Promise.all(
|
||||
rows.map(async (row): Promise<TorrentResult | null> => {
|
||||
const magnet = await detailMagnet(base, row.path, opts);
|
||||
const infoHash = magnet?.match(/urn:btih:([a-zA-Z0-9]+)/i)?.[1]?.toLowerCase();
|
||||
if (!magnet || !infoHash) return null;
|
||||
const detail = await detailInfo(base, row.path, opts);
|
||||
const infoHash = detail?.magnet?.match(/urn:btih:([a-zA-Z0-9]+)/i)?.[1]?.toLowerCase();
|
||||
if (!detail || !infoHash) return null;
|
||||
return {
|
||||
infoHash,
|
||||
name: row.name,
|
||||
@@ -107,7 +129,8 @@ async function search(
|
||||
seeders: row.seeders,
|
||||
leechers: row.leechers,
|
||||
source,
|
||||
magnet,
|
||||
magnet: detail.magnet,
|
||||
added: detail.added,
|
||||
};
|
||||
}),
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user