admin_routes.py
Class: AdminRoutesModule
Purpose: Defines all admin-related HTTP routes, API endpoints, and WebSocket handlers for managing downloaded videos, extracted frames, and AI-generated questions.
Fields:
- templates: Jinja2Templates — Template renderer configured with shared
TEMPLATES_DIR(module-level, invariant: must match app-wide template path) - router_admin_pages: APIRouter — Router for admin HTML pages (mounted under
/admin) - router_admin_api: APIRouter — Router for admin REST API endpoints (mounted under
/api) - router_admin_ws: APIRouter — Router for admin WebSocket endpoints (mounted with no prefix)
- DOWNLOADS_DIR: Path — Shared directory containing downloaded video assets (imported from settings; invariant: must remain consistent across modules)
Methods:
- format_hhmmss(total_seconds: int): str — Formats seconds into
HH:MM:SSorMM:SS. Preconditions:total_secondsmust be a non-negative integer. Postconditions:- Returns formatted time string. Throws:
- No explicit exceptions (invalid input may raise TypeError). Example:
- format_hhmmss(125) →
"02:05"
- _collect_downloaded_videos(include_without_frames: bool): List[Dict[str, Any]] — Enumerates downloaded videos and collects metadata for admin UI.
Preconditions:
DOWNLOADS_DIRmust exist and contain<video_id>subdirectories. Postconditions:- Returns a list of video metadata dictionaries including:
- video_id
- title
- duration
- frame availability
- question file availability Throws:
- Silently ignores malformed metadata files. Example:
- _collect_downloaded_videos(False) →
[ { "video_id": "...", "has_frames": True } ]
- _segment_frame_debug(video_id: str, start: int, end: int): Dict[str, Any] — Inspects frame coverage for a specific segment.
Preconditions:
video_idmust correspond to a folder inDOWNLOADS_DIR.startandenddefine a valid time interval. Postconditions:- Returns debug dictionary including:
- frame counts
- timestamp range
- missing frame files
- failure reason if applicable Throws:
- Returns structured error payload instead of raising. Example:
- _segment_frame_debug("abc123", 0, 60)
- _wrap_segment_result(video_id: str, start: int, end: int, result_text: Optional[str], result_obj: Any): Any — Normalizes question generation results.
Preconditions:
result_textis raw generation output.result_objis parsed JSON or original object. Postconditions:- Returns valid question object OR structured error payload including frame debug info. Throws:
- No direct exceptions (wraps failures). Example:
- _wrap_segment_result("abc123", 0, 60, raw, parsed)
- admin_page(request: Request): HTMLResponse — Renders the admin dashboard page.
Preconditions:
admin.htmltemplate must exist. Postconditions:- Returns rendered template. Throws:
- Template rendering exceptions if missing. Example:
- GET
/admin/
- api_download(url: str): Dict[str, Any] — Downloads a YouTube video.
Preconditions:
urlmust be a valid YouTube URL. Postconditions:- Returns download outcome from service layer. Throws:
- Service-layer exceptions may propagate. Example:
- POST
/api/download
- api_extract_frames(video_id: str): Dict[str, Any] — Extracts frames per second for a video.
Preconditions:
video_idmust exist inDOWNLOADS_DIR. Postconditions:- Returns extraction result from frame service. Throws:
- Service-layer exceptions may propagate. Example:
- POST
/api/frames/{video_id}
- admin_list_downloaded_videos(include_without_frames: bool): Dict[str, Any] — Returns lightweight manifest of downloaded videos.
Preconditions:
DOWNLOADS_DIRaccessible. Postconditions:- Returns JSON:
- success flag
- count
- list of videos Throws:
- No explicit exceptions. Example:
- GET
/api/admin/videos
- submit_questions(payload: Dict[str, Any]): Dict[str, Any] — Saves finalized questions for a video.
Preconditions:
payloadmust containvideo_idandquestions. Postconditions:- Writes JSON to:
downloads/<video_id>/questions/<video_id>.jsonThrows: - HTTPException(400) if missing required fields.
- HTTPException(500) if file write fails. Example:
- POST
/api/submit-questions
- ws_questions(websocket: WebSocket, video_id: str): None — WebSocket endpoint for streaming AI question generation.
Preconditions:
- Frames must exist for the video.
- Client must send JSON including:
- start_seconds
- interval_seconds
- full_duration Postconditions:
- Streams:
- status updates
- per-segment results
- final aggregated result Throws:
- Sends structured error messages on failure.
- Handles WebSocketDisconnect silently. Example:
- WS
/ws/questions/{video_id}
- asyncio_to_thread(func, *args, **kwargs): Future — Runs synchronous function in thread executor.
Preconditions:
funcmust be callable. Postconditions:- Returns Future resolving to function result. Throws:
- Exceptions from
funcpropagate to caller. Example: - await asyncio_to_thread(my_function, arg1)
Invariants:
- All file operations are relative to
DOWNLOADS_DIR. - Routers are mounted externally in main application.
- Business logic is delegated to service-layer modules.
- WebSocket question generation depends on extracted frame data.