feat(media): resolve thumbnail_url using product thumbnail priority (latest still → cad thumbnail)
This commit is contained in:
@@ -5,16 +5,60 @@ import zipfile
|
|||||||
|
|
||||||
from fastapi import APIRouter, Depends, HTTPException, Query
|
from fastapi import APIRouter, Depends, HTTPException, Query
|
||||||
from fastapi.responses import StreamingResponse
|
from fastapi.responses import StreamingResponse
|
||||||
|
from sqlalchemy import select
|
||||||
from sqlalchemy.ext.asyncio import AsyncSession
|
from sqlalchemy.ext.asyncio import AsyncSession
|
||||||
|
|
||||||
from app.database import get_db
|
from app.database import get_db
|
||||||
from app.domains.media.models import MediaAssetType
|
from app.domains.media.models import MediaAsset, MediaAssetType
|
||||||
from app.domains.media.schemas import MediaAssetOut
|
from app.domains.media.schemas import MediaAssetOut
|
||||||
from app.domains.media import service
|
from app.domains.media import service
|
||||||
|
|
||||||
router = APIRouter(prefix="/api/media", tags=["media"], redirect_slashes=False)
|
router = APIRouter(prefix="/api/media", tags=["media"], redirect_slashes=False)
|
||||||
|
|
||||||
|
|
||||||
|
async def _resolve_thumbnails_bulk(db: AsyncSession, assets: list) -> None:
|
||||||
|
"""Resolve thumbnail_url for assets using the same priority as product pages.
|
||||||
|
|
||||||
|
Priority per asset (applied only when thumbnail_url is not yet set):
|
||||||
|
1. Latest 'still' MediaAsset for the same product (rendered preview)
|
||||||
|
2. Product's linked CadFile thumbnail (/api/cad/{id}/thumbnail)
|
||||||
|
"""
|
||||||
|
needs = [a for a in assets if not a.thumbnail_url and a.product_id]
|
||||||
|
if not needs:
|
||||||
|
return
|
||||||
|
|
||||||
|
product_ids = list({a.product_id for a in needs})
|
||||||
|
|
||||||
|
# 1. Latest 'still' asset per product (DISTINCT ON product_id ORDER BY created_at DESC)
|
||||||
|
still_rows = await db.execute(
|
||||||
|
select(MediaAsset.product_id, MediaAsset.id)
|
||||||
|
.where(
|
||||||
|
MediaAsset.product_id.in_(product_ids),
|
||||||
|
MediaAsset.asset_type == MediaAssetType.still,
|
||||||
|
MediaAsset.is_archived == False, # noqa: E712
|
||||||
|
)
|
||||||
|
.order_by(MediaAsset.product_id, MediaAsset.created_at.desc())
|
||||||
|
.distinct(MediaAsset.product_id)
|
||||||
|
)
|
||||||
|
best_still: dict[str, str] = {str(pid): str(sid) for pid, sid in still_rows.all()}
|
||||||
|
|
||||||
|
# 2. Fallback: product's cad_file_id → CAD thumbnail endpoint
|
||||||
|
from app.domains.products.models import Product
|
||||||
|
prod_rows = await db.execute(
|
||||||
|
select(Product.id, Product.cad_file_id).where(Product.id.in_(product_ids))
|
||||||
|
)
|
||||||
|
product_cad: dict[str, str] = {
|
||||||
|
str(pid): str(cid) for pid, cid in prod_rows.all() if cid
|
||||||
|
}
|
||||||
|
|
||||||
|
for a in needs:
|
||||||
|
pid = str(a.product_id)
|
||||||
|
if pid in best_still:
|
||||||
|
a.thumbnail_url = f"/api/media/{best_still[pid]}/download"
|
||||||
|
elif pid in product_cad:
|
||||||
|
a.thumbnail_url = f"/api/cad/{product_cad[pid]}/thumbnail"
|
||||||
|
|
||||||
|
|
||||||
@router.get("", response_model=list[MediaAssetOut])
|
@router.get("", response_model=list[MediaAssetOut])
|
||||||
@router.get("/", response_model=list[MediaAssetOut], include_in_schema=False)
|
@router.get("/", response_model=list[MediaAssetOut], include_in_schema=False)
|
||||||
async def list_assets(
|
async def list_assets(
|
||||||
@@ -40,6 +84,7 @@ async def list_assets(
|
|||||||
for a in assets:
|
for a in assets:
|
||||||
a.download_url = service.get_download_url(a)
|
a.download_url = service.get_download_url(a)
|
||||||
a.thumbnail_url = service.get_thumbnail_url(a)
|
a.thumbnail_url = service.get_thumbnail_url(a)
|
||||||
|
await _resolve_thumbnails_bulk(db, assets)
|
||||||
return assets
|
return assets
|
||||||
|
|
||||||
|
|
||||||
@@ -50,6 +95,7 @@ async def get_asset(asset_id: uuid.UUID, db: AsyncSession = Depends(get_db)):
|
|||||||
raise HTTPException(404, "Asset not found")
|
raise HTTPException(404, "Asset not found")
|
||||||
asset.download_url = service.get_download_url(asset)
|
asset.download_url = service.get_download_url(asset)
|
||||||
asset.thumbnail_url = service.get_thumbnail_url(asset)
|
asset.thumbnail_url = service.get_thumbnail_url(asset)
|
||||||
|
await _resolve_thumbnails_bulk(db, [asset])
|
||||||
return asset
|
return asset
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user