From 8f8c38fcf0aabf6b47329d4c74d1ed17cc7eedff Mon Sep 17 00:00:00 2001 From: Honey Date: Sun, 3 May 2026 01:48:29 +0200 Subject: [PATCH] fix: word-boundary variant filter; reattempt ALTCHA verify payload structure - ytdlp.js: use \b word boundaries in all variant filter regexes so that partial matches like "nightcoreals" containing "nightcore" no longer falsely reject official videos; also fixes sped regression (!~= \bsped\b instead of the too-narrow "sped up" pattern) - squid.js: restructure verify payload to mirror challenge response layout { parameters, solution: { number, derivedKey }, signature } Co-Authored-By: Claude Sonnet 4.6 --- backend/lib/squid.js | 9 +++++++-- backend/lib/ytdlp.js | 25 +++++++++++++------------ 2 files changed, 20 insertions(+), 14 deletions(-) diff --git a/backend/lib/squid.js b/backend/lib/squid.js index 2e94685..ebc00c0 100644 --- a/backend/lib/squid.js +++ b/backend/lib/squid.js @@ -106,8 +106,13 @@ async function getAltchaCookie() { // 2. Solve PoW const solution = await solveAltcha(challenge); - // 3. Verify — include the top-level signature so the server can authenticate the challenge - const payload = Buffer.from(JSON.stringify({ challenge, solution, signature: chBody.signature })).toString('base64'); + // 3. Verify — mirror the challenge response structure: { parameters, signature } + solution fields + const verifyBody = { + parameters: challenge, + solution: { number: solution.counter, derivedKey: solution.derivedKey }, + signature: chBody.signature, + }; + const payload = Buffer.from(JSON.stringify(verifyBody)).toString('base64'); const vRes = await httpsPost(`${SQUID_BASE}/api/altcha/verify`, { payload }); if (vRes.status !== 200) throw new Error(`ALTCHA verify HTTP ${vRes.status}`); diff --git a/backend/lib/ytdlp.js b/backend/lib/ytdlp.js index 57eb964..98f99c4 100644 --- a/backend/lib/ytdlp.js +++ b/backend/lib/ytdlp.js @@ -28,19 +28,20 @@ function buildTitleKeywords(title) { function buildVariantFilter(title) { const t = (title || '').toLowerCase(); - // !~= (?i) is yt-dlp's case-insensitive "does not match regex" — catches Remix/REMIX/remix etc. + // !~= (?i) = case-insensitive regex "does not match". Use \b word boundaries so that + // partial matches like "nightcoreals" containing "nightcore" don't falsely reject results. const RULES = [ - { key: 'sped', expr: 'title !~= (?i)sped up' }, - { key: 'speedup', expr: 'title !~= (?i)speed\\s*up' }, - { key: 'slowed', expr: 'title !~= (?i)slowed' }, - { key: 'reverb', expr: 'title !~= (?i)reverb' }, - { key: 'nightcore', expr: 'title !~= (?i)nightcore' }, - { key: 'karaoke', expr: 'title !~= (?i)karaoke' }, - { key: 'parody', expr: 'title !~= (?i)parody' }, - { key: 'remix', expr: 'title !~= (?i)remix' }, - { key: 'cover', expr: 'title !~= (?i)cover' }, - { key: 'acoustic', expr: 'title !~= (?i)acoustic' }, - { key: 'instrumental', expr: 'title !~= (?i)instrumental' }, + { key: 'sped', expr: 'title !~= (?i)\\bsped\\b' }, + { key: 'speedup', expr: 'title !~= (?i)\\bspeed.{0,2}up\\b' }, + { key: 'slowed', expr: 'title !~= (?i)\\bslowed\\b' }, + { key: 'reverb', expr: 'title !~= (?i)\\breverb\\b' }, + { key: 'nightcore', expr: 'title !~= (?i)\\bnightcore\\b' }, + { key: 'karaoke', expr: 'title !~= (?i)\\bkaraoke\\b' }, + { key: 'parody', expr: 'title !~= (?i)\\bparody\\b' }, + { key: 'remix', expr: 'title !~= (?i)\\bremix\\b' }, + { key: 'cover', expr: 'title !~= (?i)\\bcover\\b' }, + { key: 'acoustic', expr: 'title !~= (?i)\\bacoustic\\b' }, + { key: 'instrumental', expr: 'title !~= (?i)\\binstrumental\\b' }, ]; const parts = RULES.filter(r => !t.includes(r.key)).map(r => r.expr); return parts.length ? parts.join(' & ') : '';