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:
@@ -0,0 +1,68 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from pathlib import Path
|
||||
from fastapi import FastAPI
|
||||
from fastapi.testclient import TestClient
|
||||
import pytest
|
||||
|
||||
from yt_scraper.config import Config
|
||||
from yt_scraper.store import Store, VideoRef
|
||||
from yt_scraper.webapp.api import build_router
|
||||
from yt_scraper.webapp.jobs import JobManager
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def api_client(tmp_path: Path, monkeypatch):
|
||||
db_path = tmp_path / "state.db"
|
||||
md_root = tmp_path / "markdown"
|
||||
md_root.mkdir()
|
||||
|
||||
store = Store(db_path)
|
||||
cfg = Config(database_path=str(db_path), output_dir=str(md_root))
|
||||
jobs = JobManager(store, cfg)
|
||||
router = build_router(store, cfg, jobs)
|
||||
|
||||
app = FastAPI()
|
||||
app.include_router(router)
|
||||
client = TestClient(app)
|
||||
return client, store, cfg, tmp_path
|
||||
|
||||
|
||||
def test_get_and_open_markdown(api_client, monkeypatch):
|
||||
client, store, cfg, tmp_path = api_client
|
||||
store.upsert_channel("UC1", "@test", "Test Channel", 1)
|
||||
store.upsert_videos([
|
||||
VideoRef("vid1", "UC1", "Test Video", "https://www.youtube.com/watch?v=vid1")
|
||||
])
|
||||
|
||||
# 1. Not generated yet -> 404
|
||||
r_get = client.get("/api/videos/vid1/markdown")
|
||||
assert r_get.status_code == 404
|
||||
|
||||
r_open = client.post("/api/videos/vid1/open-markdown")
|
||||
assert r_open.status_code == 404
|
||||
|
||||
# 2. Create markdown file and mark done
|
||||
md_dir = tmp_path / "markdown" / "Test Channel"
|
||||
md_dir.mkdir(parents=True)
|
||||
md_file = md_dir / "2026-08-23_test-video.md"
|
||||
md_content = "# Test Video\n\nContent here"
|
||||
md_file.write_text(md_content, encoding="utf-8", newline="\n")
|
||||
|
||||
rel_path = "markdown/Test Channel/2026-08-23_test-video.md"
|
||||
store.mark_done("vid1", rel_path, "es", "auto", False)
|
||||
|
||||
# 3. GET markdown returns text
|
||||
r_get = client.get("/api/videos/vid1/markdown")
|
||||
assert r_get.status_code == 200
|
||||
assert r_get.text.replace("\r\n", "\n") == md_content
|
||||
|
||||
# 4. POST open-markdown calls _open_in_os
|
||||
opened_paths = []
|
||||
from yt_scraper.webapp import api as api_mod
|
||||
monkeypatch.setattr(api_mod, "_open_in_os", lambda p: opened_paths.append(str(p)))
|
||||
|
||||
r_open = client.post("/api/videos/vid1/open-markdown")
|
||||
assert r_open.status_code == 200
|
||||
assert len(opened_paths) == 1
|
||||
assert str(md_file.resolve()) in [str(Path(p).resolve()) for p in opened_paths]
|
||||
Reference in New Issue
Block a user