"""Serve org-scoped lesson MP3s for device DOWNLOAD / UPDATE_MANIFEST."""
|
|
from __future__ import annotations
|
|
import re
|
|
from aiohttp import web
|
|
from core.api.base_handler import BaseHandler
|
from core.app_runtime.lesson_audio import (
|
DEFAULT_ORG_ID,
|
ensure_org_dirs,
|
lesson_audio_public_root,
|
list_lesson_clips,
|
org_audio_dir,
|
resolve_disk_file,
|
)
|
|
_LEVEL_RE = re.compile(r"^l[1-6]$", re.I)
|
_STORY_RE = re.compile(r"^\d{3}$")
|
|
TAG = __name__
|
|
|
class LessonAudioHandler(BaseHandler):
|
def __init__(self, config: dict):
|
super().__init__(config)
|
ensure_org_dirs(config)
|
|
async def handle_index(self, request: web.Request) -> web.StreamResponse:
|
org_id = request.match_info.get("org_id") or DEFAULT_ORG_ID
|
kind = str(request.match_info.get("kind") or "").strip().lower()
|
level = str(request.match_info.get("level") or "").strip().lower()
|
story = str(request.match_info.get("story") or "").strip()
|
lang = str(request.match_info.get("lang") or "").strip().lower()
|
if (
|
kind not in ("repeater", "shadowing")
|
or lang not in ("cn", "en")
|
or not _LEVEL_RE.fullmatch(level)
|
or not _STORY_RE.fullmatch(story)
|
):
|
return web.json_response({"present": False, "files": []}, status=400)
|
files = list_lesson_clips(self.config, org_id, kind, f"{level}.{story}", lang)
|
return web.json_response({"present": files is not None, "files": files or []})
|
|
async def handle_get(self, request: web.Request) -> web.StreamResponse:
|
org_id = request.match_info.get("org_id") or DEFAULT_ORG_ID
|
rel = request.match_info.get("path") or ""
|
disk = resolve_disk_file(self.config, org_id, rel)
|
if not disk:
|
return web.Response(status=404, text="lesson audio not found")
|
return web.FileResponse(path=disk, headers={"Content-Type": "audio/mpeg"})
|
|
def public_root(self) -> str:
|
return lesson_audio_public_root(self.config)
|
|
def log_ready(self) -> None:
|
ensure_org_dirs(self.config)
|
self.logger.bind(tag=TAG).info(
|
f"课文音频 HTTP: {self.public_root()}/{{org_id}}/english_learning/..."
|
)
|
self.logger.bind(tag=TAG).info(
|
f"课文音频目录: {org_audio_dir(self.config, DEFAULT_ORG_ID)} (空 org 用 {DEFAULT_ORG_ID})"
|
)
|