Optional Plex module groundwork (design locked 2026-07-05): plays the LOCAL physical file; Plex is metadata + optional watch-sync only. - models + migration 0044: plex_libraries/plex_shows/plex_seasons/plex_items (playable leaf, ffprobe playability class, markers, weighted FTS search_vector) + plex_states (per-user watch state, mirrors video_states) - sysconfig 'plex' group + config.py env defaults (server url/token[secret]/ path-map/libraries/sync-interval/max-transcodes) - app/plex/client.py (PlexClient httpx: server info, sections, items, metadata +markers, image) + app/plex/paths.py (Plex→local path map + file resolve) - routes/plex.py admin POST /api/plex/test (verify connection + list sections)
47 lines
1.8 KiB
Python
47 lines
1.8 KiB
Python
"""Plex integration API.
|
|
|
|
Read endpoints (browse/search/stream/image/state) are available to ANY authenticated user — the
|
|
whole point of the module is to be an access layer to the Plex library WITHOUT requiring a plex.tv
|
|
account, and playback is a local file (no quota/credit cost), so demo + pure-Siftlode users are
|
|
allowed. Admin endpoints configure + test the connection and trigger a sync.
|
|
|
|
P0 ships only the admin connectivity endpoints; browse/search/player land in P1/P2.
|
|
"""
|
|
from fastapi import APIRouter, Depends, HTTPException
|
|
from sqlalchemy.orm import Session
|
|
|
|
from app.db import get_db
|
|
from app.models import User
|
|
from app.plex.client import PlexClient, PlexError, PlexNotConfigured
|
|
from app.routes.admin import admin_user
|
|
|
|
router = APIRouter(prefix="/api/plex", tags=["plex"])
|
|
|
|
|
|
@router.post("/test")
|
|
def test_connection(_: User = Depends(admin_user), db: Session = Depends(get_db)) -> dict:
|
|
"""Admin: verify the configured Plex server URL + token, returning the server name and the
|
|
available library sections (movie/show) for the config library-picker."""
|
|
try:
|
|
with PlexClient(db) as plex:
|
|
info = plex.server_info()
|
|
sections = plex.sections()
|
|
except PlexNotConfigured as e:
|
|
raise HTTPException(status_code=400, detail=str(e))
|
|
except PlexError as e:
|
|
raise HTTPException(status_code=422, detail=f"Plex connection failed: {e}")
|
|
return {
|
|
"ok": True,
|
|
"server_name": info.get("friendlyName") or info.get("title1") or "Plex",
|
|
"version": info.get("version"),
|
|
"sections": [
|
|
{
|
|
"key": str(s.get("key")),
|
|
"title": s.get("title"),
|
|
"type": s.get("type"),
|
|
"count": s.get("count"),
|
|
}
|
|
for s in sections
|
|
if s.get("type") in ("movie", "show")
|
|
],
|
|
}
|