reworked version by chatgpt

main
oscar 3 months ago
parent 7580320366
commit ea7d8a4635

107
app.py

@ -1,16 +1,12 @@
# app.py DB-powered version # app.py — Streamaster, DB-Only, Clean
import os, time, json, zlib, math, hashlib, subprocess import os, time, json, math, hashlib, subprocess
from concurrent.futures import ThreadPoolExecutor from concurrent.futures import ThreadPoolExecutor
import psycopg2.extras import psycopg2.extras
from flask import Flask, render_template, request, jsonify, send_file
from flask import (
Flask, render_template, request, jsonify, send_file
)
from config import get_local_db_connection from config import get_local_db_connection
# ───────── CONFIG ───────── # # ───────── CONFIG ───────── #
app = Flask(__name__) app = Flask(__name__)
THUMB_DIR = "static/thumbnails" THUMB_DIR = "static/thumbnails"
VIDEOS_PER_PAGE = 40 VIDEOS_PER_PAGE = 40
@ -21,21 +17,21 @@ FF_QUALITY = "80"
os.makedirs(THUMB_DIR, exist_ok=True) os.makedirs(THUMB_DIR, exist_ok=True)
# ───────── DB HELPER ───────── # # ───────── DB HELPER ───────── #
def db_get_videos(): def db_get_videos():
conn, cur = get_local_db_connection() conn, cur = get_local_db_connection()
cur = conn.cursor(cursor_factory=psycopg2.extras.DictCursor) cur = conn.cursor(cursor_factory=psycopg2.extras.DictCursor)
cur.execute(""" cur.execute("""
SELECT SELECT
video_id, username, site AS platform, video_id, username, site AS platform,
filepath, size, duration, gender, created_at, updated_at filepath, size, duration, gender,
created_at, updated_at
FROM videos FROM videos
""") """)
rows = cur.fetchall() rows = cur.fetchall()
cur.close(); conn.close() cur.close(); conn.close()
return [dict(r) for r in rows] return [dict(r) for r in rows]
# ───────── THUMB UTILS ───────── # # ───────── THUMBNAIL HELPERS ───────── #
def _hashed_thumb_path(video_id: str): def _hashed_thumb_path(video_id: str):
h = hashlib.md5(video_id.encode()).hexdigest() h = hashlib.md5(video_id.encode()).hexdigest()
sub1, sub2 = h[:2], h[2:4] sub1, sub2 = h[:2], h[2:4]
@ -53,62 +49,60 @@ def _gen_thumb_cmd(src: str, dest: str):
dest dest
] ]
def generate_thumbnail(task): def generate_thumbnails_for_videos(videos):
src, dest = task tasks = []
if not os.path.exists(dest): for v in videos:
subprocess.run(_gen_thumb_cmd(src, dest), stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) video_id = v["video_id"]
thumb_path = _hashed_thumb_path(video_id)
if not os.path.exists(thumb_path):
tasks.append((v["filepath"], thumb_path))
v["thumbnail"] = thumb_path
if tasks:
with ThreadPoolExecutor(max_workers=os.cpu_count() * 2) as exe:
list(exe.map(lambda t: subprocess.run(_gen_thumb_cmd(*t), stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL), tasks))
# ───────── CACHE BUILDER ───────── #
def build_cache(): def build_cache():
videos = db_get_videos() videos = db_get_videos()
# group by (username, platform)
grouped = {} grouped = {}
for v in videos: for v in videos:
key = (v["username"], v["platform"]) key = (v["username"], v["platform"])
grouped.setdefault(key, []).append(v) grouped.setdefault(key, []).append(v)
storage_usage, avg_sizes, video_map = {}, {}, {} storage_usage, avg_sizes, video_map = {}, {}, {}
thumb_tasks = []
for (username, platform), vids in grouped.items(): for (username, platform), vids in grouped.items():
key = f"{username}::{platform}" key = f"{username}::{platform}"
total_gb = 0 total_gb = 0
for v in vids: for v in vids:
try: try:
size_mb = float(v.get("size", 0) or 0) total_gb += float(v.get("size", 0) or 0) / 1024
except ValueError: except ValueError:
print(f"⚠️ Invalid size for video {v.get('video_id')}: {v.get('size')}") print(f"⚠️ Invalid size for video {v.get('video_id')}: {v.get('size')}")
size_mb = 0
total_gb += size_mb / 1024
storage_usage[key] = { storage_usage[key] = {
"total_size": total_gb, "total_size": total_gb,
"video_count": len(vids) "video_count": len(vids)
} }
avg_sizes[key] = total_gb / len(vids) if vids else 0 avg_sizes[key] = total_gb / len(vids) if vids else 0
for v in vids:
video_id = v["video_id"]
thumb_path = _hashed_thumb_path(video_id)
if not os.path.exists(thumb_path):
thumb_tasks.append((v["filepath"], thumb_path))
v["thumbnail"] = thumb_path
video_map[key] = vids video_map[key] = vids
if thumb_tasks: generate_thumbnails_for_videos(videos)
with ThreadPoolExecutor(max_workers=os.cpu_count()*2) as exe:
list(exe.map(generate_thumbnail, thumb_tasks))
return { return {
"timestamp" : time.time(), "timestamp": time.time(),
"videos" : video_map, "videos": video_map,
"storage_usage" : storage_usage, "storage_usage": storage_usage,
"avg_sizes" : avg_sizes "avg_sizes": avg_sizes
} }
# ───────── ROUTES (unchanged logic) ───────── # # ───────── ROUTES ───────── #
@app.route("/") @app.route("/")
def dashboard(): def dashboard():
cache = build_cache() cache = build_cache()
@ -122,33 +116,32 @@ def dashboard():
if query: if query:
sorted_usage = [e for e in sorted_usage if query in e[0].lower()] sorted_usage = [e for e in sorted_usage if query in e[0].lower()]
page = max(1, int(request.args.get("page", 1))) page = max(1, int(request.args.get("page", 1)))
total_pages = max(1, math.ceil(len(sorted_usage) / DASHBOARD_PER_PAGE)) total_pages = max(1, math.ceil(len(sorted_usage) / DASHBOARD_PER_PAGE))
start = (page - 1) * DASHBOARD_PER_PAGE start = (page - 1) * DASHBOARD_PER_PAGE
paginated = sorted_usage[start:start + DASHBOARD_PER_PAGE] paginated = sorted_usage[start:start + DASHBOARD_PER_PAGE]
return render_template( return render_template(
"analytics.html", "analytics.html",
storage_usage = paginated, storage_usage=paginated,
avg_sizes = cache["avg_sizes"], avg_sizes=cache["avg_sizes"],
page = page, page=page,
total_pages = total_pages, total_pages=total_pages,
query = query query=query
) )
@app.route("/refresh") @app.route("/refresh")
def refresh(): def refresh():
cache = build_cache() cache = build_cache()
return jsonify({ return jsonify({
"status" : "ok", "status": "ok",
"videos" : sum(x["video_count"] for x in cache["storage_usage"].values()), "videos": sum(x["video_count"] for x in cache["storage_usage"].values()),
"updated" : time.ctime(cache["timestamp"]) "updated": time.ctime(cache["timestamp"])
}) })
@app.route("/user/<username>") @app.route("/user/<username>")
def user_page(username): def user_page(username):
cache = build_cache() cache = build_cache()
videos = [ videos = [
v | {"platform": key.split("::")[1]} v | {"platform": key.split("::")[1]}
for key, vids in cache["videos"].items() for key, vids in cache["videos"].items()
@ -156,17 +149,17 @@ def user_page(username):
for v in vids for v in vids
] ]
page = max(1, int(request.args.get("page", 1))) page = max(1, int(request.args.get("page", 1)))
total_pages = max(1, math.ceil(len(videos) / VIDEOS_PER_PAGE)) total_pages = max(1, math.ceil(len(videos) / VIDEOS_PER_PAGE))
start = (page - 1) * VIDEOS_PER_PAGE start = (page - 1) * VIDEOS_PER_PAGE
paginated = videos[start:start + VIDEOS_PER_PAGE] paginated = videos[start:start + VIDEOS_PER_PAGE]
return render_template( return render_template(
"user_page.html", "user_page.html",
username = username, username=username,
videos = paginated, videos=paginated,
page = page, page=page,
total_pages = total_pages total_pages=total_pages
) )
@app.route("/video/stream/<video_id>") @app.route("/video/stream/<video_id>")

Loading…
Cancel
Save