68 lines
2.2 KiB
Python
68 lines
2.2 KiB
Python
"""One-off: store inline (cid:) images that the old shallow attachment extractor
|
|||
|
|
missed for messages nested as mixed>related>alternative, so their cid: references
|
||
|
|
resolve instead of rendering broken. Idempotent — skips content_ids already stored.
|
||
|
|
|
||
|
|
Run: python -m scripts.backfill_inline_attachments
|
||
|
|
"""
|
||
|
|
|
||
|
|
from __future__ import annotations
|
||
|
|
|
||
|
|
import asyncio
|
||
|
|
|
||
|
|
from sqlalchemy import select
|
||
|
|
|
||
|
|
from app.db import get_sessionmaker
|
||
|
|
from app.indexer import blobstore
|
||
|
|
from app.indexer.mime import parse_message
|
||
|
|
from app.models import Attachment, Message
|
||
|
|
|
||
|
|
|
||
|
|
async def main() -> None:
|
||
|
|
sm = get_sessionmaker()
|
||
|
|
added = 0
|
||
|
|
fixed_msgs = 0
|
||
|
|
async with sm() as session:
|
||
|
|
msgs = list(await session.scalars(select(Message)))
|
||
|
|
for msg in msgs:
|
||
|
|
try:
|
||
|
|
raw = blobstore.load(msg.user_id, msg.blob_sha256)
|
||
|
|
except FileNotFoundError:
|
||
|
|
continue
|
||
|
|
parsed = parse_message(raw)
|
||
|
|
inline = [a for a in parsed.attachments if a.content_id]
|
||
|
|
if not inline:
|
||
|
|
continue
|
||
|
|
have = set(
|
||
|
|
await session.scalars(
|
||
|
|
select(Attachment.content_id).where(
|
||
|
|
Attachment.message_id == msg.id, Attachment.content_id.is_not(None)
|
||
|
|
)
|
||
|
|
)
|
||
|
|
)
|
||
|
|
new = 0
|
||
|
|
for att in inline:
|
||
|
|
if att.content_id in have:
|
||
|
|
continue
|
||
|
|
att_sha = blobstore.store(msg.user_id, att.payload)
|
||
|
|
session.add(
|
||
|
|
Attachment(
|
||
|
|
message_id=msg.id,
|
||
|
|
user_id=msg.user_id,
|
||
|
|
filename=att.filename,
|
||
|
|
mime_type=att.mime_type,
|
||
|
|
size_bytes=len(att.payload),
|
||
|
|
content_id=att.content_id,
|
||
|
|
blob_sha256=att_sha,
|
||
|
|
)
|
||
|
|
)
|
||
|
|
new += 1
|
||
|
|
if new:
|
||
|
|
added += new
|
||
|
|
fixed_msgs += 1
|
||
|
|
await session.commit()
|
||
|
|
print(f"stored {added} missing inline images across {fixed_msgs} messages")
|
||
|
|
|
||
|
|
|
||
|
|
if __name__ == "__main__":
|
||
|
|
asyncio.run(main())
|