diff --git a/backend/lib/squid.js b/backend/lib/squid.js index 7c39231..2e94685 100644 --- a/backend/lib/squid.js +++ b/backend/lib/squid.js @@ -106,8 +106,8 @@ async function getAltchaCookie() { // 2. Solve PoW const solution = await solveAltcha(challenge); - // 3. Verify — payload contains the unwrapped challenge params + our solution - const payload = Buffer.from(JSON.stringify({ challenge, solution })).toString('base64'); + // 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'); 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 fd3a0af..57eb964 100644 --- a/backend/lib/ytdlp.js +++ b/backend/lib/ytdlp.js @@ -28,18 +28,19 @@ 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. const RULES = [ - { key: 'sped', expr: 'title !*= sped' }, - { key: 'speedup', expr: 'title !*= speedup' }, - { key: 'slowed', expr: 'title !*= slowed' }, - { key: 'reverb', expr: 'title !*= reverb' }, - { key: 'nightcore', expr: 'title !*= nightcore' }, - { key: 'karaoke', expr: 'title !*= karaoke' }, - { key: 'parody', expr: 'title !*= parody' }, - { key: 'remix', expr: 'title !*= remix' }, - { key: 'cover', expr: 'title !*= cover' }, - { key: 'acoustic', expr: 'title !*= acoustic' }, - { key: 'instrumental', expr: 'title !*= instrumental' }, + { 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' }, ]; const parts = RULES.filter(r => !t.includes(r.key)).map(r => r.expr); return parts.length ? parts.join(' & ') : '';