"use client"; // The X account session — where the X fetchers' login comes from, whether a // login is visible there, and the operator-facing half of the session broker. // // Two sources (release 16 slice XL, common/social/xCookieSource.ts): // - the operator's everyday browser (`cookiesFromBrowser`): gallery-dl reads // its X login on every fetch, so nothing expires while the operator stays // logged in to x.com there — no window at all; // - a profile connected here once: a persistent, logged-in browser profile // the fetcher re-exports fresh cookies from, because X cookies otherwise // expire within days. import { useEffect, useState, useTransition } from "react"; import { checkXLoginAction, clearXSessionAction, connectXAccountAction, refreshXCookiesAction, setXCookieSourceAction, type XSessionActionResult, } from "../xSessionActions"; import type { XSessionStatus } from "yt-dlp-transcript-common/social/xSessionBroker"; import type { XLoginStatus } from "yt-dlp-transcript-common/social/xBrowserLogin"; import { resolveXCookieSource, xCookieSourceLabel, type XCookieSourceView, type XPostsVisibility, } from "yt-dlp-transcript-common/social/xCookieSource"; import { XPostsVisibilityControl } from "./XPostsVisibilityControl"; export function XSessionSection({ initial, initialSource, initialVisibility, }: { initial: XSessionStatus; initialSource: XCookieSourceView; // `social.x.visibility`, resolved (absent = "public"); release 17 slice XP. initialVisibility: XPostsVisibility; }) { const [status, setStatus] = useState(initial); const [source, setSource] = useState(initialSource); const [login, setLogin] = useState(null); const [error, setError] = useState(null); const [note, setNote] = useState(null); // Two transitions: the session actions (Connect waits for the window to // close) and the source controls (Check, the source select). const [pending, startTransition] = useTransition(); const [checking, startChecking] = useTransition(); // A save of cookiesFromBrowser above (or another tab's choice) re-renders the // page with a new resolved source; take it. useEffect(() => { setSource(initialSource); // Keyed on the values, not the object: every server render makes a new one. // eslint-disable-next-line react-hooks/exhaustive-deps }, [initialSource.source, initialSource.chosen, initialSource.browserSpec]); const run = (fn: () => Promise, okNote: (r: { browser?: string }) => string) => startTransition(async () => { setError(null); setNote(null); const res = await fn(); if (res.ok) { setStatus(res.status); setSource(res.source); setLogin(null); setNote(okNote(res)); } else { setError(res.error); } }); const check = () => startChecking(async () => { setError(null); setNote(null); const res = await checkXLoginAction(); if (res.ok) { setLogin(res.login); setSource(res.login); } else { setError(res.error); } }); const choose = (choice: string) => startChecking(async () => { setError(null); setNote(null); const res = await setXCookieSourceAction(choice); if (res.ok) { setSource(res.source); setLogin(null); setNote("Login source saved."); } else { setError(res.error); } }); // What "Automatic" resolves to right now, for its option's text. const automatic = xCookieSourceLabel( resolveXCookieSource({ browserSpec: source.browserSpec, profileConnected: status.looksAuthenticated, }), ); return (

X account session

{status.looksAuthenticated ? "connected" : status.hasProfile ? "profile present, not logged in" : "not connected"}

X post archiving needs a logged-in session, from one of two sources.{" "} Browser login: your everyday browser, named by{" "} cookiesFromBrowser above — gallery-dl reads its X login on every fetch, so nothing expires while you stay logged in to x.com there, and no window is needed. Connected profile: a browser profile connected here once; the fetcher re-exports fresh cookies from it, because X cookies otherwise expire within days.

In use: {source.label} {source.chosen ? "" : " (automatic)"}

{login && (

{login.summary}{" "} (checked {new Date(login.checkedAt).toLocaleTimeString()})

)} {status.cookiesUpdatedAt && (

Cookies last exported:{" "} {new Date(status.cookiesUpdatedAt).toLocaleString()}

)}

Connecting opens a browser window on the machine running the editor: your own Chromium or Chrome when one is installed (else Playwright's bundled Chromium), without the automation signals. Complete the login (2FA and captcha included), then close the window. Google's sign-in may still refuse an embedded browser (“this browser or app may not be secure”) — X's own password login is the reliable path. The login is never automated — that is what gets accounts flagged.

{note && (

{note}

)} {error && (

{error}

)}
); }