feat: rebuild iOS app from API audit + new podcast/media service
iOS App (complete rebuild): - Audited all fitness API endpoints against live responses - Models match exact API field names (snapshot_ prefixes, UUID strings) - FoodEntry uses computed properties (foodName, calories, etc.) wrapping snapshot fields - Flexible Int/Double decoding for all numeric fields - AI assistant with raw JSON state management (JSONSerialization, not Codable) - Home dashboard with custom background, frosted glass calorie widget - Fitness: Today/Templates/Goals/Foods tabs - Food search with recent + all sections - Meal sections with colored accent bars, swipe to delete - 120fps ProMotion, iOS 17+ @Observable Podcast/Media Service: - FastAPI backend for podcast RSS + local audiobook folders - Shows, episodes, playback progress, queue management - RSS feed fetching with feedparser + ETag support - Local folder scanning with mutagen for audio metadata - HTTP Range streaming for local audio files - Playback events logging (play/pause/seek/complete) - Reuses brain's PostgreSQL + Redis - media_ prefixed tables Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
87
services/media/app/main.py
Normal file
87
services/media/app/main.py
Normal file
@@ -0,0 +1,87 @@
|
||||
"""Media service — FastAPI entrypoint."""
|
||||
|
||||
import logging
|
||||
|
||||
from fastapi import FastAPI, Depends
|
||||
from sqlalchemy import select, func
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from app.config import DEBUG, PORT
|
||||
|
||||
logging.basicConfig(
|
||||
level=logging.DEBUG if DEBUG else logging.INFO,
|
||||
format="%(asctime)s %(levelname)s %(name)s: %(message)s",
|
||||
)
|
||||
|
||||
app = FastAPI(
|
||||
title="Media Service",
|
||||
description="Podcast and local audio management with playback tracking.",
|
||||
version="1.0.0",
|
||||
docs_url="/api/docs" if DEBUG else None,
|
||||
redoc_url=None,
|
||||
)
|
||||
|
||||
|
||||
@app.on_event("startup")
|
||||
async def startup():
|
||||
from app.database import engine, Base
|
||||
from app.models import Show, Episode, Progress, QueueItem, PlaybackEvent # noqa
|
||||
|
||||
async with engine.begin() as conn:
|
||||
await conn.run_sync(Base.metadata.create_all)
|
||||
|
||||
logging.getLogger(__name__).info("Media service started on port %s", PORT)
|
||||
|
||||
|
||||
# Register routers
|
||||
from app.api.shows import router as shows_router
|
||||
from app.api.episodes import router as episodes_router
|
||||
from app.api.playback import router as playback_router
|
||||
from app.api.queue import router as queue_router
|
||||
|
||||
app.include_router(shows_router)
|
||||
app.include_router(episodes_router)
|
||||
app.include_router(playback_router)
|
||||
app.include_router(queue_router)
|
||||
|
||||
|
||||
@app.get("/api/health")
|
||||
async def health():
|
||||
return {"status": "ok", "service": "media"}
|
||||
|
||||
|
||||
from app.api.deps import get_user_id, get_db_session
|
||||
|
||||
|
||||
@app.get("/api/stats")
|
||||
async def get_stats(
|
||||
user_id: str = Depends(get_user_id),
|
||||
db: AsyncSession = Depends(get_db_session),
|
||||
):
|
||||
from app.models import Show, Episode, Progress
|
||||
|
||||
total_shows = await db.scalar(
|
||||
select(func.count(Show.id)).where(Show.user_id == user_id)
|
||||
)
|
||||
total_episodes = await db.scalar(
|
||||
select(func.count(Episode.id)).where(Episode.user_id == user_id)
|
||||
)
|
||||
total_seconds = await db.scalar(
|
||||
select(func.coalesce(func.sum(Progress.position_seconds), 0)).where(
|
||||
Progress.user_id == user_id,
|
||||
)
|
||||
)
|
||||
in_progress = await db.scalar(
|
||||
select(func.count(Progress.id)).where(
|
||||
Progress.user_id == user_id,
|
||||
Progress.position_seconds > 0,
|
||||
Progress.is_completed == False, # noqa: E712
|
||||
)
|
||||
)
|
||||
|
||||
return {
|
||||
"total_shows": total_shows or 0,
|
||||
"total_episodes": total_episodes or 0,
|
||||
"total_listened_hours": round((total_seconds or 0) / 3600, 1),
|
||||
"in_progress": in_progress or 0,
|
||||
}
|
||||
Reference in New Issue
Block a user