From 538a0dc635fa5bee371192181f8c3f810556a364 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=B5=85=E5=BF=B5?= Date: Tue, 11 Aug 2026 12:06:26 +0800 Subject: [PATCH] =?UTF-8?q?feat(sync):=20=E9=95=BF=E8=AF=AD=E9=9F=B3?= =?UTF-8?q?=E9=9F=B3=E9=A2=91=E6=9C=AC=E5=9C=B0=E5=8C=96(voice=5Fclip=20me?= =?UTF-8?q?dia=5Fhash+download+local-first=20playback)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Mirror download_call_recordings pattern for voice_clips: - VoiceClip.media_hash column + Alembic migration (c4d5e6f7a8b9) - VoiceClipOut.media_hash + frontend VoiceClip.media_hash type - download_voice_clip_audio in app/scraper/media.py (resilient, sha256-dedup, same download_url/upsert_media helpers as call recordings) - sync_voice_clip_audio orchestrator wrapper (commits own SyncRun, mirrors sync_call_recordings semantics) - scheduler: _run_voice_clip_audio + ENTITY_FUNCS['voice_clip_audio'] + appended to daily _run_all job (after voice_clips) - VoiceClipModal playClip local-first: /api/media/{media_hash} with OSS oss_file_name fallback (mirrors CallList.playRecord) Tested: 376 pytest pass; vue-tsc clean; migration applied; 1523/1533 clips localized (10 pending are genuine upstream HTTP 404s — resilient skip). Co-Authored-By: Claude --- yunke-server/app/api/schemas.py | 3 + yunke-server/app/db/models.py | 6 ++ yunke-server/app/scraper/media.py | 83 ++++++++++++++++++- yunke-server/app/sync/orchestrator.py | 63 +++++++++++++- yunke-server/app/sync/scheduler.py | 15 ++++ .../c4d5e6f7a8b9_voice_clip_media_hash.py | 34 ++++++++ yunkes/src/api/types.ts | 2 + yunkes/src/components/VoiceClipModal.vue | 8 +- 8 files changed, 210 insertions(+), 4 deletions(-) create mode 100644 yunke-server/migrations/versions/c4d5e6f7a8b9_voice_clip_media_hash.py diff --git a/yunke-server/app/api/schemas.py b/yunke-server/app/api/schemas.py index f735217..9ee5f71 100644 --- a/yunke-server/app/api/schemas.py +++ b/yunke-server/app/api/schemas.py @@ -275,6 +275,9 @@ class VoiceClipOut(BaseModel): # OSS recording URL — surfaced so the modal can render an inline # ``