perf(api): historia ogladania wsadowo, wyszukiwanie w obrebie strony po rownosci originu
- /watch/recent: _build_scenes_out_batch(light) zamiast 5-7 zapytan na scene (N+1 w Sentry)
- /scenes?origin=tube❌ rownosc zamiast ILIKE '%...%', 4,6 s -> 1,1 s na hdporngg z q
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
8462ab50df
commit
747d0cec3b
2 changed files with 22 additions and 5 deletions
|
|
@ -383,13 +383,21 @@ def list_scenes(
|
||||||
base = base.where(~live_playback_exists())
|
base = base.where(~live_playback_exists())
|
||||||
|
|
||||||
if origin:
|
if origin:
|
||||||
# Substring match na origin, 'hqporner' złapie 'tube:hqpornercom'.
|
# Pelny origin z ekranu strony ('tube:hdporngg') porownujemy wprost: rownosc idzie
|
||||||
|
# po ix_playback_sources_origin_live, a ILIKE '%...%' przeszukiwal kazde zrodlo
|
||||||
|
# kazdej kandydujacej sceny (wyszukiwanie w obrebie strony 4,6 s, Sentry Slow DB
|
||||||
|
# Query 2026-09-15). Krotka forma z filtra ('hqporner') zostaje substringiem.
|
||||||
|
origin_clause = (
|
||||||
|
PlaybackSource.origin == origin
|
||||||
|
if ":" in origin
|
||||||
|
else PlaybackSource.origin.ilike(f"%{origin}%")
|
||||||
|
)
|
||||||
base = base.where(
|
base = base.where(
|
||||||
exists(
|
exists(
|
||||||
select(1).where(
|
select(1).where(
|
||||||
PlaybackSource.scene_id == Scene.id,
|
PlaybackSource.scene_id == Scene.id,
|
||||||
PlaybackSource.dead_at.is_(None),
|
PlaybackSource.dead_at.is_(None),
|
||||||
PlaybackSource.origin.ilike(f"%{origin}%"),
|
origin_clause,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@ from sqlalchemy import desc, select
|
||||||
from sqlalchemy.orm import Session
|
from sqlalchemy.orm import Session
|
||||||
|
|
||||||
from app.api.device import get_device_id
|
from app.api.device import get_device_id
|
||||||
from app.api.scenes import _build_scene_out
|
from app.api.scenes import _build_scenes_out_batch
|
||||||
from app.api.schemas import SceneOut
|
from app.api.schemas import SceneOut
|
||||||
from app.auth import require_api_key
|
from app.auth import require_api_key
|
||||||
from app.db import get_session
|
from app.db import get_session
|
||||||
|
|
@ -237,11 +237,20 @@ def list_recent(
|
||||||
if not include_finished:
|
if not include_finished:
|
||||||
stmt = stmt.where(ScenePlayProgress.finished.is_(False))
|
stmt = stmt.where(ScenePlayProgress.finished.is_(False))
|
||||||
|
|
||||||
|
# Serializacja wsadowa jak w liscie scen: rail „Continue watching" leci przy kazdym
|
||||||
|
# starcie apki, a pelny `_build_scene_out` robil 5-7 zapytan na scene (N+1 w Sentry,
|
||||||
|
# 2026-09-14). Rail uzywa tytulu, miniatury i dlugosci, wiec wersja lekka wystarcza.
|
||||||
|
rows = session.execute(stmt).all()
|
||||||
|
outs = _build_scenes_out_batch(session, [scene for _, scene in rows], light=True, device_id=device_id)
|
||||||
|
out_by_id = {o.id: o for o in outs}
|
||||||
items: list[WatchEntry] = []
|
items: list[WatchEntry] = []
|
||||||
for prog, scene in session.execute(stmt).all():
|
for prog, scene in rows:
|
||||||
|
scene_out = out_by_id.get(scene.id)
|
||||||
|
if scene_out is None:
|
||||||
|
continue
|
||||||
items.append(
|
items.append(
|
||||||
WatchEntry(
|
WatchEntry(
|
||||||
scene=_build_scene_out(session, scene, device_id=device_id),
|
scene=scene_out,
|
||||||
position_sec=prog.position_sec,
|
position_sec=prog.position_sec,
|
||||||
duration_sec=prog.duration_sec,
|
duration_sec=prog.duration_sec,
|
||||||
finished=prog.finished,
|
finished=prog.finished,
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue