Spaces:
Runtime error
Runtime error
perf: in-memory feed cache — load once at startup, append on upload, zero disk reads
Browse files
app.py
CHANGED
|
@@ -355,7 +355,9 @@ def create(
|
|
| 355 |
f.write(thumb_bytes)
|
| 356 |
has_thumb = True
|
| 357 |
|
| 358 |
-
# Save metadata
|
|
|
|
|
|
|
| 359 |
meta = {
|
| 360 |
"id": song_id,
|
| 361 |
"title": title,
|
|
@@ -363,15 +365,19 @@ def create(
|
|
| 363 |
"tags": tags,
|
| 364 |
"lyrics": lyrics,
|
| 365 |
"duration": audio_duration,
|
|
|
|
|
|
|
| 366 |
"has_thumb": has_thumb,
|
| 367 |
"created_at": datetime.now(timezone.utc).isoformat(),
|
| 368 |
}
|
| 369 |
with open(f"{song_dir}/meta.json", "w") as f:
|
| 370 |
json.dump(meta, f, indent=2)
|
| 371 |
|
| 372 |
-
|
| 373 |
-
|
| 374 |
-
|
|
|
|
|
|
|
| 375 |
except Exception as upload_err:
|
| 376 |
print(f"[create] Community upload failed: {upload_err}")
|
| 377 |
|
|
@@ -406,43 +412,37 @@ def generate(
|
|
| 406 |
raise
|
| 407 |
|
| 408 |
|
| 409 |
-
# ──
|
| 410 |
-
|
| 411 |
-
_feed_cache = {"data": "[]", "ts": 0}
|
| 412 |
-
_FEED_TTL = 10 # seconds
|
| 413 |
|
| 414 |
-
def
|
| 415 |
-
|
| 416 |
songs_dir = "/data/songs"
|
| 417 |
-
if os.path.isdir(songs_dir):
|
| 418 |
-
|
| 419 |
-
|
| 420 |
-
|
| 421 |
-
|
| 422 |
-
|
| 423 |
-
|
| 424 |
-
|
| 425 |
-
|
| 426 |
-
|
| 427 |
-
|
| 428 |
-
|
| 429 |
-
|
| 430 |
-
|
| 431 |
-
|
| 432 |
-
|
| 433 |
-
|
| 434 |
-
|
| 435 |
-
|
| 436 |
-
|
| 437 |
|
| 438 |
@app.api(name="community", concurrency_limit=4)
|
| 439 |
def community() -> str:
|
| 440 |
-
"""List community songs
|
| 441 |
-
|
| 442 |
-
if now - _feed_cache["ts"] > _FEED_TTL:
|
| 443 |
-
_feed_cache["data"] = _rebuild_feed()
|
| 444 |
-
_feed_cache["ts"] = now
|
| 445 |
-
return _feed_cache["data"]
|
| 446 |
|
| 447 |
|
| 448 |
# ── Serve custom HTML frontend ────────────────────────────────────────────────
|
|
|
|
| 355 |
f.write(thumb_bytes)
|
| 356 |
has_thumb = True
|
| 357 |
|
| 358 |
+
# Save metadata to bucket (durability) + memory (instant reads)
|
| 359 |
+
audio_url = f"{BUCKET_URL}/songs/{song_id}/{wav_name}"
|
| 360 |
+
thumb_url = f"{BUCKET_URL}/songs/{song_id}/thumb.png" if has_thumb else None
|
| 361 |
meta = {
|
| 362 |
"id": song_id,
|
| 363 |
"title": title,
|
|
|
|
| 365 |
"tags": tags,
|
| 366 |
"lyrics": lyrics,
|
| 367 |
"duration": audio_duration,
|
| 368 |
+
"audio_url": audio_url,
|
| 369 |
+
"thumb_url": thumb_url,
|
| 370 |
"has_thumb": has_thumb,
|
| 371 |
"created_at": datetime.now(timezone.utc).isoformat(),
|
| 372 |
}
|
| 373 |
with open(f"{song_dir}/meta.json", "w") as f:
|
| 374 |
json.dump(meta, f, indent=2)
|
| 375 |
|
| 376 |
+
# Prepend to in-memory feed (no re-scan needed)
|
| 377 |
+
_feed_songs.insert(0, meta)
|
| 378 |
+
|
| 379 |
+
result["community_url"] = audio_url
|
| 380 |
+
print(f"[create] Shared to community: {audio_url}")
|
| 381 |
except Exception as upload_err:
|
| 382 |
print(f"[create] Community upload failed: {upload_err}")
|
| 383 |
|
|
|
|
| 412 |
raise
|
| 413 |
|
| 414 |
|
| 415 |
+
# ── Community feed (in-memory, loaded once at startup) ───────────────────────
|
| 416 |
+
_feed_songs = []
|
|
|
|
|
|
|
| 417 |
|
| 418 |
+
def _load_feed_from_disk():
|
| 419 |
+
"""One-time scan at startup to populate memory from bucket."""
|
| 420 |
songs_dir = "/data/songs"
|
| 421 |
+
if not os.path.isdir(songs_dir):
|
| 422 |
+
print("[feed] /data/songs not found, starting with empty feed")
|
| 423 |
+
return
|
| 424 |
+
for song_id in os.listdir(songs_dir):
|
| 425 |
+
meta_path = os.path.join(songs_dir, song_id, "meta.json")
|
| 426 |
+
if os.path.isfile(meta_path):
|
| 427 |
+
try:
|
| 428 |
+
with open(meta_path) as f:
|
| 429 |
+
meta = json.load(f)
|
| 430 |
+
meta["audio_url"] = f"{BUCKET_URL}/songs/{song_id}/{song_id}.wav"
|
| 431 |
+
thumb_path = os.path.join(songs_dir, song_id, "thumb.png")
|
| 432 |
+
if os.path.isfile(thumb_path):
|
| 433 |
+
meta["thumb_url"] = f"{BUCKET_URL}/songs/{song_id}/thumb.png"
|
| 434 |
+
_feed_songs.append(meta)
|
| 435 |
+
except Exception:
|
| 436 |
+
pass
|
| 437 |
+
_feed_songs.sort(key=lambda s: s.get("created_at", ""), reverse=True)
|
| 438 |
+
print(f"[feed] Loaded {len(_feed_songs)} songs into memory")
|
| 439 |
+
|
| 440 |
+
_load_feed_from_disk()
|
| 441 |
|
| 442 |
@app.api(name="community", concurrency_limit=4)
|
| 443 |
def community() -> str:
|
| 444 |
+
"""List community songs — served from memory, zero disk I/O."""
|
| 445 |
+
return json.dumps(_feed_songs[:50])
|
|
|
|
|
|
|
|
|
|
|
|
|
| 446 |
|
| 447 |
|
| 448 |
# ── Serve custom HTML frontend ────────────────────────────────────────────────
|