183 lines
6.8 KiB
Python
183 lines
6.8 KiB
Python
from __future__ import annotations
|
|
|
|
from ._shared import *
|
|
from ..services.rtorrent.diagnostics import profile_diagnostics
|
|
|
|
@bp.get("/profiles")
|
|
def profiles_list():
|
|
return ok({"profiles": preferences.list_profiles(), "active": preferences.active_profile()})
|
|
|
|
|
|
|
|
@bp.post("/profiles")
|
|
def profiles_create():
|
|
try:
|
|
return ok({"profile": preferences.save_profile(request.json or {})})
|
|
except Exception as exc:
|
|
return jsonify({"ok": False, "error": str(exc)}), 400
|
|
|
|
|
|
|
|
@bp.put("/profiles/<int:profile_id>")
|
|
def profiles_update(profile_id: int):
|
|
try:
|
|
return ok({"profile": preferences.update_profile(profile_id, request.json or {})})
|
|
except Exception as exc:
|
|
return jsonify({"ok": False, "error": str(exc)}), 400
|
|
|
|
|
|
|
|
@bp.delete("/profiles/<int:profile_id>")
|
|
def profiles_delete(profile_id: int):
|
|
preferences.delete_profile(profile_id)
|
|
return ok({"profiles": preferences.list_profiles(), "active": preferences.active_profile()})
|
|
|
|
|
|
|
|
@bp.post("/profiles/<int:profile_id>/activate")
|
|
def profiles_activate(profile_id: int):
|
|
try:
|
|
return ok({"profile": preferences.activate_profile(profile_id)})
|
|
except Exception as exc:
|
|
return jsonify({"ok": False, "error": str(exc)}), 404
|
|
|
|
|
|
|
|
@bp.post("/profiles/test")
|
|
def profiles_test_unsaved():
|
|
data = request.get_json(silent=True) or {}
|
|
profile = {
|
|
"id": data.get("id"),
|
|
"name": data.get("name") or "test",
|
|
"scgi_url": data.get("scgi_url") or "",
|
|
"timeout_seconds": data.get("timeout_seconds") or 5,
|
|
}
|
|
return ok({"diagnostics": profile_diagnostics(profile)})
|
|
|
|
|
|
@bp.get("/profiles/<int:profile_id>/diagnostics")
|
|
def profiles_diagnostics(profile_id: int):
|
|
profile = preferences.get_profile(profile_id)
|
|
if not profile:
|
|
return jsonify({"ok": False, "error": "Profile not found"}), 404
|
|
return ok({"diagnostics": profile_diagnostics(profile)})
|
|
|
|
|
|
@bp.get("/profiles/diagnostics")
|
|
def profiles_diagnostics_all():
|
|
rows = preferences.list_profiles()
|
|
diagnostics = []
|
|
for profile in rows:
|
|
diagnostics.append(profile_diagnostics(profile))
|
|
return ok({"diagnostics": diagnostics})
|
|
|
|
|
|
@bp.get("/profiles/export")
|
|
def profiles_export():
|
|
return ok(preferences.export_profiles())
|
|
|
|
|
|
@bp.post("/profiles/import")
|
|
def profiles_import():
|
|
try:
|
|
rows = preferences.import_profiles(request.get_json(silent=True) or {})
|
|
return ok({"profiles": rows})
|
|
except Exception as exc:
|
|
return jsonify({"ok": False, "error": str(exc)}), 400
|
|
|
|
|
|
@bp.get("/preferences")
|
|
def prefs_get():
|
|
return ok({"preferences": preferences.get_preferences()})
|
|
|
|
|
|
|
|
@bp.post("/preferences")
|
|
def prefs_save():
|
|
return ok({"preferences": preferences.save_preferences(request.json or {})})
|
|
|
|
|
|
@bp.post("/preferences/table-columns/recommended")
|
|
def prefs_table_columns_recommended():
|
|
# Note: Applies the backend-owned recommended desktop and mobile column layout.
|
|
return ok({"preferences": preferences.apply_recommended_table_columns()})
|
|
|
|
|
|
|
|
@bp.get("/labels")
|
|
def labels_list():
|
|
profile = preferences.active_profile()
|
|
pid = profile["id"] if profile else None
|
|
with connect() as conn:
|
|
rows = conn.execute("SELECT * FROM labels WHERE user_id=? AND (profile_id=? OR profile_id IS NULL) ORDER BY name COLLATE NOCASE", (default_user_id(), pid)).fetchall()
|
|
return ok({"labels": rows})
|
|
|
|
|
|
|
|
@bp.post("/labels")
|
|
def labels_save():
|
|
profile = preferences.active_profile()
|
|
if not profile:
|
|
return jsonify({"ok": False, "error": "No profile"}), 400
|
|
data = request.get_json(silent=True) or {}
|
|
name = str(data.get("name") or "").strip()
|
|
if not name:
|
|
return jsonify({"ok": False, "error": "Missing label name"}), 400
|
|
now = utcnow()
|
|
with connect() as conn:
|
|
conn.execute("INSERT OR IGNORE INTO labels(user_id,profile_id,name,color,created_at,updated_at) VALUES(?,?,?,?,?,?)", (default_user_id(), profile["id"], name, data.get("color") or "#64748b", now, now))
|
|
return labels_list()
|
|
|
|
|
|
|
|
@bp.delete("/labels/<int:label_id>")
|
|
def labels_delete(label_id: int):
|
|
profile = preferences.active_profile()
|
|
pid = profile["id"] if profile else None
|
|
with connect() as conn:
|
|
conn.execute("DELETE FROM labels WHERE id=? AND user_id=? AND (profile_id=? OR profile_id IS NULL)", (label_id, default_user_id(), pid))
|
|
return labels_list()
|
|
|
|
|
|
|
|
@bp.get("/ratio-groups")
|
|
def ratio_groups_list():
|
|
profile = preferences.active_profile()
|
|
pid = profile["id"] if profile else None
|
|
with connect() as conn:
|
|
rows = conn.execute("SELECT * FROM ratio_groups WHERE user_id=? AND (profile_id=? OR profile_id IS NULL) ORDER BY name COLLATE NOCASE", (default_user_id(), pid)).fetchall()
|
|
history = conn.execute("SELECT * FROM ratio_history WHERE user_id=? AND profile_id=? ORDER BY id DESC LIMIT 50", (default_user_id(), pid or 0)).fetchall() if pid else []
|
|
return ok({"groups": rows, "history": history})
|
|
|
|
|
|
|
|
@bp.post("/ratio-groups")
|
|
def ratio_groups_save():
|
|
profile = preferences.active_profile()
|
|
if not profile:
|
|
return jsonify({"ok": False, "error": "No profile"}), 400
|
|
data = request.get_json(silent=True) or {}
|
|
name = str(data.get("name") or "").strip()
|
|
if not name:
|
|
return jsonify({"ok": False, "error": "Missing group name"}), 400
|
|
now = utcnow()
|
|
with connect() as conn:
|
|
conn.execute(
|
|
"""INSERT INTO ratio_groups(user_id,profile_id,name,min_ratio,max_ratio,seed_time_minutes,min_seed_time_minutes,ignore_private,ignore_active_upload,active_upload_min_bytes,move_path,set_label,action,enabled,created_at,updated_at)
|
|
VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)
|
|
ON CONFLICT(user_id,profile_id,name) DO UPDATE SET min_ratio=excluded.min_ratio,max_ratio=excluded.max_ratio,seed_time_minutes=excluded.seed_time_minutes,min_seed_time_minutes=excluded.min_seed_time_minutes,ignore_private=excluded.ignore_private,ignore_active_upload=excluded.ignore_active_upload,active_upload_min_bytes=excluded.active_upload_min_bytes,move_path=excluded.move_path,set_label=excluded.set_label,action=excluded.action,enabled=excluded.enabled,updated_at=excluded.updated_at""",
|
|
(default_user_id(), profile["id"], name, float(data.get("min_ratio") or 1), float(data.get("max_ratio") or 2), int(data.get("seed_time_minutes") or 0), int(data.get("min_seed_time_minutes") or 0), 1 if data.get("ignore_private", True) else 0, 1 if data.get("ignore_active_upload", True) else 0, int(data.get("active_upload_min_bytes") or 1024), data.get("move_path") or "", data.get("set_label") or "", data.get("action") or "stop", 1 if data.get("enabled", True) else 0, now, now),
|
|
)
|
|
return ratio_groups_list()
|
|
|
|
|
|
|
|
@bp.post("/ratio-groups/check")
|
|
def ratio_groups_check():
|
|
profile = preferences.active_profile()
|
|
if not profile:
|
|
return jsonify({"ok": False, "error": "No profile"}), 400
|
|
return ok({"result": ratio_rules.check(profile, default_user_id())})
|
|
|
|
|