Fix backend imports and clip pinning flow

This commit is contained in:
Codex
2026-04-22 07:48:12 -05:00
parent 45c2b46304
commit ec73156966
12 changed files with 736 additions and 156 deletions

View File

@@ -6,7 +6,7 @@ from pathlib import Path
from fastapi import APIRouter, Depends, File, Form, HTTPException, UploadFile
from fastapi.responses import FileResponse
from sqlalchemy import delete, select, update
from sqlalchemy import delete, func, select, update
from sqlalchemy.orm import Session
from ..auth import require_session
@@ -19,6 +19,7 @@ from ..schemas import (
AudioClipCreate,
AudioClipResponse,
AudioClipUpdate,
AudioClipReorder,
)
from ..storage import storage
@@ -39,6 +40,7 @@ def clip_to_response(clip: AudioClip) -> AudioClipResponse:
label=clip.label,
start_ms=clip.start_ms,
end_ms=clip.end_ms,
sort_order=clip.sort_order,
normalization_status=clip.normalization_status,
normalized_url=normalized_url,
waveform_duration_ms=waveform["duration_ms"] if waveform else None,
@@ -53,6 +55,18 @@ def can_manage_asset(session: UserSession, asset: AudioAsset, owner_external_pla
return owner_external_player_id is not None and asset.owner_external_player_id == owner_external_player_id
def next_clip_sort_order(db: Session, *, external_team_id: str, owner_external_player_id: str) -> int:
highest_sort_order = db.scalar(
select(func.max(AudioClip.sort_order))
.join(AudioClip.asset)
.where(
AudioAsset.external_team_id == external_team_id,
AudioAsset.owner_external_player_id == owner_external_player_id,
)
)
return highest_sort_order + 1 if highest_sort_order is not None else 0
def create_asset_with_default_clip(
*,
db: Session,
@@ -83,6 +97,11 @@ def create_asset_with_default_clip(
label=asset.title,
start_ms=0,
end_ms=DEFAULT_CLIP_LENGTH_MS,
sort_order=next_clip_sort_order(
db,
external_team_id=external_team_id,
owner_external_player_id=owner_external_player_id,
),
normalization_status="processing",
)
db.add(clip)
@@ -282,6 +301,11 @@ def create_clip(
label=payload.label,
start_ms=payload.start_ms,
end_ms=payload.end_ms,
sort_order=next_clip_sort_order(
db,
external_team_id=payload.external_team_id,
owner_external_player_id=payload.owner_external_player_id,
),
normalization_status="processing",
)
db.add(clip)
@@ -314,6 +338,8 @@ def update_clip(
clip.label = payload.label or clip.label
clip.start_ms = payload.start_ms
clip.end_ms = payload.end_ms
if payload.sort_order is not None:
clip.sort_order = payload.sort_order
db.commit()
db.refresh(clip)
return clip_to_response(clip)
@@ -357,7 +383,7 @@ def list_clips(
select(AudioClip)
.join(AudioClip.asset)
.where(AudioAsset.external_team_id == external_team_id)
.order_by(AudioClip.created_at.desc())
.order_by(AudioClip.sort_order.asc(), AudioClip.created_at.desc())
)
if owner_external_player_id:
query = query.where(AudioAsset.owner_external_player_id == owner_external_player_id)
@@ -365,6 +391,33 @@ def list_clips(
return [clip_to_response(clip) for clip in clips]
@router.post("/clips/reorder", status_code=204)
def reorder_clips(
payload: AudioClipReorder,
session: UserSession = Depends(require_session),
db: Session = Depends(get_db),
) -> None:
if not session.is_admin and session.external_team_id != payload.external_team_id:
raise HTTPException(status_code=403, detail="You can only reorder clips for your selected team")
clips = db.scalars(
select(AudioClip)
.join(AudioClip.asset)
.where(
AudioAsset.external_team_id == payload.external_team_id,
AudioAsset.owner_external_player_id == payload.owner_external_player_id,
)
).all()
clips_by_id = {clip.id: clip for clip in clips}
if len(clips_by_id) != len(clips) or set(clips_by_id) != set(payload.clip_ids):
raise HTTPException(status_code=422, detail="Clip order must include every clip for that player")
for sort_order, clip_id in enumerate(payload.clip_ids):
clips_by_id[clip_id].sort_order = sort_order
db.commit()
@router.get("/files/{relative_path:path}")
def media_file(relative_path: str) -> FileResponse:
path = storage.absolute_path(relative_path)