5ce33232c1
- Message 模型新增 media_hash 列 (sha256 FK -> Media.media_hash)
- Alembic 迁移 e5f6a7b8c9d0_message_media_hash (batch_alter_table, SQLite/MySQL 通用)
- app/scraper/media.py:download_message_images 批量下载器,镜像 download_call_recordings
- 候选: media_hash IS NULL AND channel IN ('wechat','whatsapp') AND type IN ('image','video','file') AND 任一 file_*_url LIKE 'http%'
- image 类型优先 file_th_url (缩略图,小快省),video/file 优先 file_url
- per-row try/except + per-run tried set,失败不挂循环
- sync_message_images orchestrator 入口 + ENTITY_MESSAGE_IMAGES 常量 (提交自身 SyncRun,镜像 call_recordings)
- scheduler: _run_message_images + ENTITY_FUNCS['message_images'] + 加入 daily all job (尾部)
- MessageOut schema 暴露 media_hash 字段
- MessageBubble.vue local-first: media_hash 优先 /api/media/{hash},否则回退 OSS file_url/file_th_url/file_hd_url
- types.ts: Message 接口添加 media_hash 字段
- 新增 test_message_images_downloader.py (6 tests, 镜像 test_recording_downloader)
Co-Authored-By: Claude <noreply@anthropic.com>
36 lines
1.1 KiB
Python
36 lines
1.1 KiB
Python
"""message media_hash
|
|
|
|
Revision ID: e5f6a7b8c9d0
|
|
Revises: c4d5e6f7a8b9
|
|
Create Date: 2026-08-11 00:00:00.000000
|
|
|
|
"""
|
|
from collections.abc import Sequence
|
|
|
|
import sqlalchemy as sa
|
|
from alembic import op
|
|
|
|
|
|
# revision identifiers, used by Alembic.
|
|
revision: str = 'e5f6a7b8c9d0'
|
|
down_revision: str | Sequence[str] | None = 'c4d5e6f7a8b9'
|
|
branch_labels: str | Sequence[str] | None = None
|
|
depends_on: str | Sequence[str] | None = None
|
|
|
|
|
|
def upgrade() -> None:
|
|
"""Upgrade schema."""
|
|
# sha256 media_hash of the localized media blob (FK -> Media.media_hash).
|
|
# NULL until sync_message_images downloads it. batch_alter_table keeps
|
|
# this portable across SQLite (dev/test, table-recreate) and MySQL (prod,
|
|
# direct ALTER). Mirrors the voice_clip.media_hash + phone_call.record_media_hash
|
|
# migrations.
|
|
with op.batch_alter_table('message', schema=None) as batch_op:
|
|
batch_op.add_column(sa.Column('media_hash', sa.String(length=64), nullable=True))
|
|
|
|
|
|
def downgrade() -> None:
|
|
"""Downgrade schema."""
|
|
with op.batch_alter_table('message', schema=None) as batch_op:
|
|
batch_op.drop_column('media_hash')
|