feat: genre field in DetailPanel and ID3 TCON tag

- Added genre column to clips table (auto-migration)
- Genre field in DetailPanel metadata section (between Album and Comment)
- Genre saved via PATCH /api/clips/:id/metadata
- Genre embedded in ID3 TCON tag on download
- Placeholder text: 'e.g. Hardcore Techno, Phonk, Dark Ambient...'
This commit is contained in:
rin.kagamine committed 2026-05-31 11:18:37 -04:00
1 parent 1903d8bd2a
commit bef707f9a4
5 files changed
+29 -12

No files matched your search

+8 -3
View File
@@ -98,6 +98,11 @@ async def init_db():
await db.execute("ALTER TABLE clips ADD COLUMN comment TEXT")
except Exception:
pass # Column already exists
# Migration: add genre column if it doesn't exist
try:
await db.execute("ALTER TABLE clips ADD COLUMN genre TEXT")
except Exception:
pass # Column already exists
await db.commit()
async def create_clip(prompt: str, model: str, duration: int, prompt_structure: str = None) -> str:
@@ -176,11 +181,11 @@ async def rename_clip(clip_id: str, title: str):
)
await db.commit()
async def update_metadata(clip_id: str, artist: str = None, album: str = None, comment: str = None):
async def update_metadata(clip_id: str, artist: str = None, album: str = None, comment: str = None, genre: str = None):
async with aiosqlite.connect(DB_PATH) as db:
await db.execute(
"UPDATE clips SET artist = ?, album = ?, comment = ? WHERE id = ?",
(artist, album, comment, clip_id)
"UPDATE clips SET artist = ?, album = ?, comment = ?, genre = ? WHERE id = ?",
(artist, album, comment, genre, clip_id)
)
await db.commit()
+6 -2
View File
@@ -135,7 +135,7 @@ async def stream_audio(clip_id: str):
return FileResponse(file_path, media_type="audio/mpeg", filename=clip["filename"])
from mutagen.mp3 import MP3
from mutagen.id3 import ID3, TIT1, TIT2, TPE1, TALB, COMM, TBPM, TKEY, APIC, PictureType
from mutagen.id3 import ID3, TIT1, TIT2, TPE1, TALB, COMM, TBPM, TKEY, APIC, PictureType, TCON
@app.get("/api/clips/{clip_id}/download")
async def download_clip(clip_id: str):
@@ -197,6 +197,10 @@ async def download_clip(clip_id: str):
if clip.get("key"):
audio.tags["TKEY"] = TKEY(encoding=3, text=clip["key"])
# Genre
if clip.get("genre"):
audio.tags["TCON"] = TCON(encoding=3, text=clip["genre"])
# Album art
if clip.get("artwork_filename"):
artwork_path = ARTWORK_DIR / clip["artwork_filename"]
@@ -496,7 +500,7 @@ async def update_clip_metadata(clip_id: str, request: MetadataRequest):
clip = await get_clip(clip_id)
if not clip:
raise HTTPException(status_code=404, detail="Clip not found")
await update_metadata(clip_id, request.artist, request.album, request.comment)
await update_metadata(clip_id, request.artist, request.album, request.comment, request.genre)
updated = await get_clip(clip_id)
return updated
+1
View File
@@ -92,6 +92,7 @@ class MetadataRequest(BaseModel):
artist: Optional[str] = None
album: Optional[str] = None
comment: Optional[str] = None
genre: Optional[str] = None
class GenerateArtworkRequest(BaseModel):
prompt: str