first commit
This commit is contained in:
88
app/routes/api.py
Normal file
88
app/routes/api.py
Normal file
@@ -0,0 +1,88 @@
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
from flask import Blueprint, abort, jsonify, request, url_for
|
||||
|
||||
from ..extensions import db
|
||||
from ..models import Display, DisplaySession
|
||||
|
||||
bp = Blueprint("api", __name__, url_prefix="/api")
|
||||
|
||||
|
||||
MAX_ACTIVE_SESSIONS_PER_DISPLAY = 2
|
||||
SESSION_TTL_SECONDS = 90
|
||||
|
||||
|
||||
@bp.get("/display/<token>/playlist")
|
||||
def display_playlist(token: str):
|
||||
display = Display.query.filter_by(token=token).first()
|
||||
if not display:
|
||||
abort(404)
|
||||
|
||||
# Enforce: a display URL/token can be opened by max 2 concurrently active sessions.
|
||||
# Player sends a stable `sid` via querystring.
|
||||
sid = (request.args.get("sid") or "").strip()
|
||||
if sid:
|
||||
cutoff = datetime.utcnow() - timedelta(seconds=SESSION_TTL_SECONDS)
|
||||
DisplaySession.query.filter(
|
||||
DisplaySession.display_id == display.id,
|
||||
DisplaySession.last_seen_at < cutoff,
|
||||
).delete(synchronize_session=False)
|
||||
db.session.commit()
|
||||
|
||||
existing = DisplaySession.query.filter_by(display_id=display.id, sid=sid).first()
|
||||
if existing:
|
||||
existing.last_seen_at = datetime.utcnow()
|
||||
db.session.commit()
|
||||
else:
|
||||
active_count = (
|
||||
DisplaySession.query.filter(
|
||||
DisplaySession.display_id == display.id,
|
||||
DisplaySession.last_seen_at >= cutoff,
|
||||
).count()
|
||||
)
|
||||
if active_count >= MAX_ACTIVE_SESSIONS_PER_DISPLAY:
|
||||
return (
|
||||
jsonify(
|
||||
{
|
||||
"error": "display_limit_reached",
|
||||
"message": f"This display URL is already open on {MAX_ACTIVE_SESSIONS_PER_DISPLAY} displays.",
|
||||
}
|
||||
),
|
||||
429,
|
||||
)
|
||||
|
||||
s = DisplaySession(
|
||||
display_id=display.id,
|
||||
sid=sid,
|
||||
last_seen_at=datetime.utcnow(),
|
||||
ip=request.headers.get("X-Forwarded-For", request.remote_addr),
|
||||
user_agent=(request.headers.get("User-Agent") or "")[:300],
|
||||
)
|
||||
db.session.add(s)
|
||||
db.session.commit()
|
||||
|
||||
playlist = display.assigned_playlist
|
||||
if not playlist:
|
||||
return jsonify({"display": display.name, "playlist": None, "items": []})
|
||||
|
||||
items = []
|
||||
for item in playlist.items:
|
||||
payload = {
|
||||
"id": item.id,
|
||||
"type": item.item_type,
|
||||
"title": item.title,
|
||||
"duration": item.duration_seconds,
|
||||
}
|
||||
if item.item_type in ("image", "video") and item.file_path:
|
||||
payload["src"] = url_for("static", filename=item.file_path)
|
||||
if item.item_type == "webpage":
|
||||
payload["url"] = item.url
|
||||
items.append(payload)
|
||||
|
||||
return jsonify(
|
||||
{
|
||||
"display": display.name,
|
||||
"playlist": {"id": playlist.id, "name": playlist.name},
|
||||
"items": items,
|
||||
}
|
||||
)
|
||||
Reference in New Issue
Block a user