fix(route): repair u3c3 search and drop sticky promo rows (#22268)

* fix(route): repair u3c3 search and drop sticky promo rows

The handler destructured `keywoard` (typo for `keyword`), so the search
branch never ran and every query fell through to the homepage listing.
u3c3 has also added an anti-scrape token (`search2=<token>`, rotated and
embedded in the homepage `search21()` JS) — `?search=<kw>` alone silently
returns the homepage, so scrape the token live with a graceful fallback.

Also skip the sticky promo rows (category-nav and external-ad rows, e.g.
the "國產原创" entries) shown on every page including search; real torrent
rows always link to /view.

* refactor(route): use filter+map instead of flatMap in u3c3

Address review feedback: the flatMap returning []/[obj] was just a
filter+map in disguise. Skip non-/view rows up front, then map.
This commit is contained in:
Jagger.H 2026-06-16 12:07:12 +08:00 committed by GitHub
parent ec3f93384f
commit b10a33b191
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
1 changed files with 25 additions and 16 deletions

View File

@ -23,7 +23,7 @@ export const route: Route = {
};
async function handler(ctx) {
const { type, keywoard, preview } = ctx.req.param();
const { type, keyword, preview } = ctx.req.param();
// should be:
// undefined
// U3C3
@ -36,9 +36,18 @@ async function handler(ctx) {
const rootURL = 'https://www.u3c3.com';
let currentURL;
let title;
if (keywoard) {
currentURL = `${rootURL}/?search=${keywoard}`;
title = `search ${keywoard} - u3c3`;
if (keyword) {
// u3c3 search needs an anti-scrape token: its search box (search21() JS on the
// homepage) redirects to /?search2=<token>&search=<kw>. The token rotates and is
// embedded in the page, so scrape it live; hitting /?search=<kw> alone silently
// returns the homepage listing (the long-standing "always 國產原创" bug).
const { data: home } = await got(rootURL);
const varName = home.match(/search2="\s*\+\s*(\w+)/)?.[1];
const token = varName ? home.match(new RegExp(String.raw`(?<!/)\bvar\s+${varName}\s*=\s*"([^"]+)"`))?.[1] : undefined;
currentURL = token
? `${rootURL}/?search2=${token}&search=${encodeURIComponent(keyword)}`
: `${rootURL}/?search=${encodeURIComponent(keyword)}`;
title = `search ${keyword} - u3c3`;
} else if (type === undefined) {
currentURL = rootURL;
title = 'home - u3c3';
@ -52,20 +61,20 @@ async function handler(ctx) {
const list = $('body > div.container > div.table-responsive > table > tbody > tr')
.toArray()
.map((item) => {
item = $(item);
const title = item.find('td:nth-of-type(2) > a ').attr('title');
const guid = rootURL + item.find('td:nth-of-type(2) > a').attr('href');
const link = guid;
const pubDate = item.find('td:nth-of-type(5)').text();
const enclosure_url = item.find('td:nth-of-type(3) > a:nth-of-type(2)').attr('href');
// Skip u3c3's sticky promo rows that appear on every page (home AND search):
// category-nav rows (href="/?type=…") and external ad rows (href="http://…",
// e.g. the "國產原创" entries). Real torrent rows always link to /view.
.filter((el) => $(el).find('td:nth-of-type(2) > a').attr('href')?.startsWith('/view'))
.map((el) => {
const item = $(el);
const a = item.find('td:nth-of-type(2) > a');
const guid = rootURL + a.attr('href');
return {
title,
title: a.attr('title'),
guid,
link,
pubDate,
enclosure_url,
link: guid,
pubDate: item.find('td:nth-of-type(5)').text(),
enclosure_url: item.find('td:nth-of-type(3) > a:nth-of-type(2)').attr('href'),
enclosure_type: 'application/x-bittorrent',
};
});