Twitch stream event platform with OBS browser source overlay. Features: - Twitch OAuth login (streamer + mod access) - EventSub WebSocket for subs, gift subs, bits, channel points, follows, raids - Chat commands via tmi.js in the overlay - Gap audio — tone plays on new chat message after silence, volume/pitch scale with gap length - Self-hosted media library (upload sounds and videos) - Action configuration — map any event to sound, video, or alert - Per-action cooldowns, test button, enable/disable toggle - Multiple actions per event (all matching actions fire) - Activity log dashboard with EventSub connection status - Layout editor — iframe-based WYSIWYG with drag handles, live style preview - Custom CSS and custom JS injection into overlay - Custom DOM events (bashyoverlay:sub, bashyoverlay:raid, etc.) for custom JS hooks - deploy.sh — one-shot setup and launch script Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
64 lines
2.2 KiB
Python
64 lines
2.2 KiB
Python
from fastapi import APIRouter, Depends, Form, Request
|
|
from fastapi.responses import HTMLResponse, RedirectResponse
|
|
from sqlmodel import Session, select
|
|
|
|
from app.auth import require_user
|
|
from app.database import get_session
|
|
from app.models import Settings
|
|
from app.templates import templates
|
|
|
|
router = APIRouter(prefix="/settings")
|
|
|
|
|
|
@router.get("", response_class=HTMLResponse)
|
|
async def settings_page(request: Request, session: Session = Depends(get_session), user=Depends(require_user)):
|
|
settings = session.exec(select(Settings)).first() or Settings()
|
|
return templates.TemplateResponse("settings.html", {
|
|
"request": request,
|
|
"user": user,
|
|
"settings": settings,
|
|
"overlay_url": str(request.base_url) + "overlay",
|
|
})
|
|
|
|
|
|
@router.post("", response_class=HTMLResponse)
|
|
async def save_settings(
|
|
request: Request,
|
|
twitch_channel: str = Form(...),
|
|
gap_silence_threshold: int = Form(default=30),
|
|
gap_scale_end: int = Form(default=120),
|
|
gap_pitch_scale_end: int = Form(default=300),
|
|
custom_css: str = Form(default=""),
|
|
custom_js: str = Form(default=""),
|
|
session: Session = Depends(get_session),
|
|
user=Depends(require_user),
|
|
):
|
|
settings = session.exec(select(Settings)).first()
|
|
if not settings:
|
|
settings = Settings()
|
|
|
|
settings.twitch_channel = twitch_channel.strip().lstrip("#")
|
|
settings.gap_silence_threshold = max(0, gap_silence_threshold)
|
|
settings.gap_scale_end = max(settings.gap_silence_threshold + 1, gap_scale_end)
|
|
settings.gap_pitch_scale_end = max(settings.gap_scale_end + 1, gap_pitch_scale_end)
|
|
settings.custom_css = custom_css
|
|
settings.custom_js = custom_js
|
|
|
|
session.add(settings)
|
|
session.commit()
|
|
|
|
return templates.TemplateResponse("settings.html", {
|
|
"request": request,
|
|
"user": user,
|
|
"settings": settings,
|
|
"overlay_url": str(request.base_url) + "overlay",
|
|
"saved": True,
|
|
})
|
|
|
|
|
|
@router.post("/reconnect")
|
|
async def reconnect_eventsub(user=Depends(require_user)):
|
|
from app.main import start_eventsub
|
|
import asyncio
|
|
asyncio.create_task(start_eventsub())
|
|
return RedirectResponse(url="/settings", status_code=303)
|