fix(ui): add ic-btn-reset CSS + auto-convert auth.json to cookies.txt
All checks were successful
Build & Push Docker Image / test-and-build (push) Successful in 1m3s

- layout.css: add button.ic-btn-reset rule so all icon buttons
  (bell, back, close, retry, etc.) get proper background:none reset
  instead of browser-default white/grey appearance in dark mode
- instagram-extractor.ts: auto-convert secrets/auth.json
  (Playwright storage format) to Netscape cookies.txt at runtime
  whenever auth.json is newer; ensures sessionid and all Instagram
  session cookies are passed to yt-dlp, fixing empty media response

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
Giancarmine Salucci
2026-05-12 22:29:12 +02:00
parent 91aca8d35a
commit 040ae17c12
2 changed files with 54 additions and 2 deletions

View File

@@ -9,7 +9,7 @@
import { execFile } from 'node:child_process';
import { promisify } from 'node:util';
import { existsSync } from 'node:fs';
import { existsSync, readFileSync, writeFileSync, statSync } from 'node:fs';
import { logError } from './utils/logger';
import type { ExtractedContent, ProgressCallback } from './extraction';
@@ -20,9 +20,60 @@ const IMAGE_FETCH_TIMEOUT_MS = 10_000;
const USER_AGENT =
'Mozilla/5.0 (iPhone; CPU iPhone OS 17_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.0 Mobile/15E148 Safari/604.1';
const AUTH_PATHS = ['/app/secrets/auth.json', './secrets/auth.json'];
const COOKIE_PATHS = ['/app/secrets/cookies.txt', './secrets/cookies.txt'];
interface PlaywrightCookie {
name: string;
value: string;
domain: string;
path: string;
expires: number;
httpOnly?: boolean;
secure?: boolean;
}
/** Convert Playwright auth.json → Netscape cookies.txt next to it. */
function maybeConvertAuthJson(): void {
for (let i = 0; i < AUTH_PATHS.length; i++) {
const authPath = AUTH_PATHS[i];
const cookiePath = COOKIE_PATHS[i];
if (!existsSync(authPath)) continue;
// Re-generate whenever auth.json is newer than the existing cookies.txt
const authMtime = statSync(authPath).mtimeMs;
const cookieMtime = existsSync(cookiePath) ? statSync(cookiePath).mtimeMs : 0;
if (cookieMtime >= authMtime) continue;
try {
const auth = JSON.parse(readFileSync(authPath, 'utf8')) as {
cookies?: PlaywrightCookie[];
};
const cookies: PlaywrightCookie[] = auth.cookies ?? [];
const lines = [
'# Netscape HTTP Cookie File',
'# Auto-generated from auth.json by InstaChef.',
''
];
for (const c of cookies) {
const domain = c.domain.startsWith('.') ? c.domain : `.${c.domain}`;
const includeSubdomains = 'TRUE';
const secure = c.secure ? 'TRUE' : 'FALSE';
const expiry = Math.floor(c.expires > 0 ? c.expires : 0);
lines.push(
[domain, includeSubdomains, c.path ?? '/', secure, expiry, c.name, c.value].join('\t')
);
}
writeFileSync(cookiePath, lines.join('\n') + '\n', 'utf8');
} catch {
// Non-fatal: yt-dlp will just run without cookies
}
break;
}
}
function resolveCookiePath(): string | null {
maybeConvertAuthJson();
for (const p of COOKIE_PATHS) {
if (existsSync(p)) return p;
}

View File

@@ -99,7 +99,8 @@ html, body {
}
.ic-scroll::-webkit-scrollbar { display: none; }
button.ic-btn {
button.ic-btn,
button.ic-btn-reset {
background: none;
border: 0;
padding: 0;