server-persisted watch history with position + resume
- POST/GET /api/history: JSON file in data/history.json (50 entries kept, top 5 shown on home), upsert per (anime, episode) - watch position saved every ~10s of playback + on pause/unload - resume: opening an anime seeks to the saved position unless >=95% through (then starts fresh); position shown on home items - drops localStorage; server is the source of truth (verified: fresh watch -> new session resumes at saved pos -> home lists it)
This commit is contained in:
1 parent
32f93c0f65
commit
a8426b5922
3 files changed
+80
-15
No files matched your search
@@ -5,9 +5,12 @@ pointed at /stream/{token}; the proxy fetches upstream (with the same
|
||||
Cloudflare workaround) and rewrites playlist URIs to go through itself.
|
||||
"""
|
||||
import base64
|
||||
import json
|
||||
import time
|
||||
from pathlib import Path
|
||||
from urllib.parse import urljoin
|
||||
|
||||
from fastapi import FastAPI, HTTPException, Query
|
||||
from fastapi import Body, FastAPI, HTTPException, Query
|
||||
from fastapi.responses import Response
|
||||
from fastapi.staticfiles import StaticFiles
|
||||
|
||||
@@ -15,6 +18,42 @@ import upstream
|
||||
|
||||
app = FastAPI(title="ani-web")
|
||||
|
||||
HIST_FILE = Path(__file__).parent / "data" / "history.json"
|
||||
|
||||
|
||||
def _load_hist():
|
||||
try:
|
||||
return json.loads(HIST_FILE.read_text())
|
||||
except (FileNotFoundError, json.JSONDecodeError):
|
||||
return []
|
||||
|
||||
|
||||
def _save_hist(entries):
|
||||
HIST_FILE.parent.mkdir(exist_ok=True)
|
||||
# ponytail: unguarded read-modify-write, fine for a single-user tool
|
||||
HIST_FILE.write_text(json.dumps(entries, indent=1))
|
||||
|
||||
|
||||
@app.get("/api/history")
|
||||
def api_history():
|
||||
return sorted(_load_hist(), key=lambda e: e.get("t", 0), reverse=True)[:5]
|
||||
|
||||
|
||||
@app.post("/api/history")
|
||||
def api_history_post(entry: dict = Body(...)):
|
||||
if "ep_id" not in entry:
|
||||
raise HTTPException(422, "ep_id required")
|
||||
entry["t"] = time.time()
|
||||
entries = _load_hist()
|
||||
for i, e in enumerate(entries):
|
||||
if e.get("slug_id") == entry.get("slug_id") and e.get("ep_id") == entry.get("ep_id"):
|
||||
entries[i] = {**e, **entry}
|
||||
break
|
||||
else:
|
||||
entries.append(entry)
|
||||
_save_hist(sorted(entries, key=lambda e: e.get("t", 0), reverse=True)[:50])
|
||||
return entry
|
||||
|
||||
|
||||
def _b64(url: str) -> str:
|
||||
return base64.urlsafe_b64encode(url.encode()).decode()
|
||||
|
||||
Reference in new issue
Block a user