new media player functionality

This commit is contained in:
2026-06-25 06:09:28 +00:00
parent 69f848c260
commit d9b0964f07
20 changed files with 404 additions and 5 deletions
Binary file not shown.
Binary file not shown.
Binary file not shown.
+41 -1
View File
@@ -11,6 +11,45 @@ from app.user_models import User
from app.api import events, auth, station_config, history, team, community, shows, storage, underwriters
def _migrate_add_missing_columns(sync_conn):
"""Add missing columns to existing tables (startup migration — no Alembic).
Handles both SQLite and PostgreSQL. Idempotent: skips columns that already exist.
Receives a sync SQLAlchemy Connection from engine.run_sync().
"""
import sqlalchemy as sa
# Column definitions: (table, name, type, default)
pending = [
("station_config", "stream_url", sa.String(500), ""),
("station_config", "stream_metadata_url", sa.String(500), ""),
]
# Determine dialect
dialect = sync_conn.dialect.name # "sqlite" | "postgresql"
inspector = sa.inspect(sync_conn)
for table_name, col_name, col_type, default in pending:
# Check if column already exists
existing = inspector.get_columns(table_name)
if any(c["name"] == col_name for c in existing):
continue
print(f" → Migrating: adding column {table_name}.{col_name}")
if dialect == "sqlite":
sync_conn.execute(
sa.text(f'ALTER TABLE {table_name} ADD COLUMN "{col_name}" TEXT NOT NULL DEFAULT "{default}"')
)
else:
sync_conn.execute(
sa.text(f'ALTER TABLE {table_name} ADD COLUMN "{col_name}" VARCHAR(500) NOT NULL DEFAULT "{default}"')
)
print(f" ✓ Added {table_name}.{col_name}")
async def _ensure_station_config(session):
"""Ensure the singleton station config row exists (idempotent)."""
result = await session.execute(
@@ -28,9 +67,10 @@ async def _ensure_station_config(session):
@asynccontextmanager
async def lifespan(app: FastAPI):
# Create tables on startup
# Create tables on startup, then patch any missing columns
async with engine.begin() as conn:
await conn.run_sync(Base.metadata.create_all)
await conn.run_sync(_migrate_add_missing_columns)
# Auto-seed if database is empty
async with async_session() as session:
+4
View File
@@ -94,6 +94,10 @@ class StationConfig(Base):
# Donation
donation_url = Column(String(500), nullable=False, default="")
# Stream
stream_url = Column(String(500), nullable=False, default="")
stream_metadata_url = Column(String(500), nullable=False, default="")
class HistoryEntry(Base):
__tablename__ = "history_entries"
+4
View File
@@ -73,6 +73,8 @@ class StationConfigResponse(BaseModel):
email: str
website: str
donation_url: str
stream_url: str
stream_metadata_url: str
model_config = {"from_attributes": True}
@@ -95,6 +97,8 @@ class StationConfigUpdate(BaseModel):
email: Optional[str] = None
website: Optional[str] = None
donation_url: Optional[str] = None
stream_url: Optional[str] = None
stream_metadata_url: Optional[str] = None
# ── HistoryEntry ──────────────────────────────────────────
+2
View File
@@ -212,6 +212,8 @@ STATION_CONFIG: dict = {
"email": "hello@kmountainflower.org",
"website": "kmountainflower.org",
"donation_url": "",
"stream_url": "",
"stream_metadata_url": "",
}
HISTORY_ENTRIES: list[dict] = [