feat: cookies desde navegador, fallback watch-page y optimizacion integral del nucleo

Extraccion autenticada:
- import_from_browser (Brave) con fallback CDP headless para cookies app-bound v20
- extract_via_watch_page: GET plano + ytInitialPlayerResponse cuando yt-dlp falla
  con sesion logueada (members-only); regex y opener cacheados
- js_runtimes (node/deno/bun/quickjs) propagado a todos los ydl_opts
- rutas de Brave multiplataforma (Windows/macOS/Linux)

Webapp UX: chips de filtros removibles, skeleton loaders, estado de vista en URL,
memoria de scroll, copyMd/openMd, import de cookies desde navegador, no-cache de statics

Rendimiento:
- entorno Jinja2 cacheado por directorio de plantilla (antes 1 por nota)
- _rank_unranked con guarda (antes full-scan en cada arranque/import)
- upsert_videos con executemany; dashboard sin N+1 (GROUP BY + conteo de tags en SQL)
- thumbnails en paralelo (6 hilos, CDN ytimg); handlers bloqueantes -> def (threadpool)
- reconcile de arranque en hilo daemon: uvicorn arriba al instante (0.95s con 1503 md),
  healthz expone reconcile_done
- Store.transaction(): escrituras por video agrupadas (~6 commits -> 3)

Refactor: helpers unicos (extract_handle->discover, safe_dirname/filename->render,
order_pending->store, keep_ref->config, seconds_to_ts solo en segments);
re-render del CLI delega en pipeline.re_render_videos (retira huerfanos y marca done);
fuera wrappers muertos de segments.py
This commit is contained in:
urieljareth
2026-09-10 00:32:19 -06:00
parent 1190228a81
commit b3b27ce883
21 changed files with 1494 additions and 326 deletions
+1 -9
View File
@@ -8,7 +8,7 @@ from pathlib import Path
from typing import Callable
from .parse import Segment
from .store import SearchHit, Store
from .store import Store
log = logging.getLogger(__name__)
@@ -91,14 +91,6 @@ def _strip_quotes(value: str) -> str:
return v
def store_segments(store: Store, video_id: str, segments: list[Segment]) -> None:
store.store_segments(video_id, segments)
def search(store: Store, query: str, channel_id: str | None = None, limit: int = 50) -> list[SearchHit]:
return store.search_segments(query, channel_id=channel_id, limit=limit)
def backfill_from_markdown(
store: Store,
md_root: Path,