Spaces:
Paused
Paused
Update app.py
Browse files
app.py
CHANGED
|
@@ -162,7 +162,7 @@ class MetaData(BaseModel):
|
|
| 162 |
rating: Optional[int] = None
|
| 163 |
genre: Optional[str] = None
|
| 164 |
last_played: Optional[str] = None # ISO timestamp
|
| 165 |
-
|
| 166 |
# --- GCS I/O HELPERS ---
|
| 167 |
|
| 168 |
|
|
@@ -866,7 +866,58 @@ async def update_music_metadata(music_id: str, payload: SampleMetaUpdatePayload)
|
|
| 866 |
|
| 867 |
# --- 5. SMART SYNC (The "Magic" Button) ---
|
| 868 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 869 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 870 |
@app.post("/api/admin/sync")
|
| 871 |
async def sync_gcs_storage():
|
| 872 |
"""
|
|
|
|
| 162 |
rating: Optional[int] = None
|
| 163 |
genre: Optional[str] = None
|
| 164 |
last_played: Optional[str] = None # ISO timestamp
|
| 165 |
+
tags: List[str] = Field(default_factory=list) # ← ADD THIS
|
| 166 |
# --- GCS I/O HELPERS ---
|
| 167 |
|
| 168 |
|
|
|
|
| 866 |
|
| 867 |
# --- 5. SMART SYNC (The "Magic" Button) ---
|
| 868 |
|
| 869 |
+
from pydantic import Field
|
| 870 |
+
from typing import Optional, List
|
| 871 |
+
|
| 872 |
+
class MetaPatch(BaseModel):
|
| 873 |
+
name: Optional[str] = None
|
| 874 |
+
rating: Optional[int] = Field(None, ge=0, le=10)
|
| 875 |
+
genre: Optional[str] = None
|
| 876 |
+
tags: Optional[List[str]] = None # full list (replace)
|
| 877 |
+
last_played: Optional[str] = None # ISO string
|
| 878 |
+
|
| 879 |
+
@app.patch("/api/songs/{item_id}")
|
| 880 |
+
async def patch_song(item_id: str, patch: MetaPatch):
|
| 881 |
+
config = STORAGE_MAP["songs"] # ← JSON index only
|
| 882 |
+
index_path = config["index"]
|
| 883 |
+
|
| 884 |
+
async with INDEX_LOCK:
|
| 885 |
+
try:
|
| 886 |
+
index: list = await run_io(_read_json_sync, index_path)
|
| 887 |
+
if not isinstance(index, list):
|
| 888 |
+
index = []
|
| 889 |
+
|
| 890 |
+
# Find by id
|
| 891 |
+
entry = next((e for e in index if e.get("id") == item_id), None)
|
| 892 |
+
if not entry:
|
| 893 |
+
raise HTTPException(status_code=404, detail="Song not found")
|
| 894 |
+
|
| 895 |
+
changes = patch.model_dump(exclude_unset=True)
|
| 896 |
+
if not changes:
|
| 897 |
+
return {"status": "no-op", "message": "Nothing to update"}
|
| 898 |
+
|
| 899 |
+
updated = {}
|
| 900 |
+
for field, value in changes.items():
|
| 901 |
+
if field == "tags":
|
| 902 |
+
entry["tags"] = value if value is not None else []
|
| 903 |
+
else:
|
| 904 |
+
entry[field] = value
|
| 905 |
+
updated[field] = entry[field]
|
| 906 |
+
|
| 907 |
+
await run_io(_write_json_sync, index_path, index)
|
| 908 |
+
await cache.clear() # or cache.delete("library:songs") for precision
|
| 909 |
|
| 910 |
+
return {
|
| 911 |
+
"status": "success",
|
| 912 |
+
"item_id": item_id,
|
| 913 |
+
"updated": updated
|
| 914 |
+
}
|
| 915 |
+
|
| 916 |
+
except Exception as e:
|
| 917 |
+
logging.error(f"PATCH /songs/{item_id} failed: {e}")
|
| 918 |
+
raise HTTPException(status_code=500, detail=str(e))
|
| 919 |
+
|
| 920 |
+
|
| 921 |
@app.post("/api/admin/sync")
|
| 922 |
async def sync_gcs_storage():
|
| 923 |
"""
|