6ea64bc19e
- Add backend API for Transmission RPC integration - Create frontend page with torrent and tracker status - Add health monitoring script - Display daemon status, speeds, and tracker errors - Support reannounce, start/stop actions
212 lines
6.6 KiB
Python
212 lines
6.6 KiB
Python
import subprocess
|
|
from typing import Any, Dict, List
|
|
|
|
import httpx
|
|
from fastapi import APIRouter, HTTPException
|
|
|
|
router = APIRouter(prefix="/api/transmission", tags=["transmission"])
|
|
|
|
|
|
def get_session_id() -> str:
|
|
"""Get Transmission RPC session ID"""
|
|
try:
|
|
result = subprocess.run(
|
|
["ssh", "nas", "curl -si -u 'admin:admin' 'http://127.0.0.1:9091/transmission/rpc' 2>&1 | grep -i 'X-Transmission-Session-Id:' | cut -d' ' -f2 | tr -d '\\r'"],
|
|
capture_output=True,
|
|
text=True,
|
|
shell=True,
|
|
timeout=5
|
|
)
|
|
session_id = result.stdout.strip()
|
|
if not session_id:
|
|
raise Exception("Failed to get session ID")
|
|
return session_id
|
|
except Exception as e:
|
|
raise HTTPException(status_code=500, detail=f"Failed to get Transmission session ID: {str(e)}")
|
|
|
|
|
|
def transmission_rpc(method: str, arguments: Dict[str, Any] = None) -> Dict[str, Any]:
|
|
"""Make Transmission RPC call"""
|
|
try:
|
|
session_id = get_session_id()
|
|
|
|
payload = {"method": method}
|
|
if arguments:
|
|
payload["arguments"] = arguments
|
|
|
|
# Use httpx to make the request
|
|
with httpx.Client(timeout=10.0) as client:
|
|
response = client.post(
|
|
"http://100.78.131.124:9091/transmission/rpc",
|
|
json=payload,
|
|
auth=("admin", "admin"),
|
|
headers={"X-Transmission-Session-Id": session_id}
|
|
)
|
|
|
|
if response.status_code == 200:
|
|
data = response.json()
|
|
if data.get("result") == "success":
|
|
return data.get("arguments", {})
|
|
else:
|
|
raise Exception(f"RPC error: {data.get('result')}")
|
|
else:
|
|
raise Exception(f"HTTP {response.status_code}: {response.text}")
|
|
|
|
except Exception as e:
|
|
raise HTTPException(status_code=500, detail=f"Transmission RPC failed: {str(e)}")
|
|
|
|
|
|
@router.get("/status")
|
|
async def get_status():
|
|
"""Get Transmission daemon status"""
|
|
try:
|
|
# Check if process is running
|
|
result = subprocess.run(
|
|
["ssh", "nas", "ps aux | grep transmission-daemon | grep sc-transmission | grep -v grep"],
|
|
capture_output=True,
|
|
text=True,
|
|
shell=True,
|
|
timeout=5
|
|
)
|
|
|
|
is_running = bool(result.stdout.strip())
|
|
|
|
if not is_running:
|
|
return {
|
|
"running": False,
|
|
"message": "Transmission daemon is not running"
|
|
}
|
|
|
|
# Get session stats
|
|
try:
|
|
stats = transmission_rpc("session-stats")
|
|
return {
|
|
"running": True,
|
|
"stats": stats
|
|
}
|
|
except:
|
|
return {
|
|
"running": True,
|
|
"message": "Daemon running but RPC not accessible"
|
|
}
|
|
|
|
except Exception as e:
|
|
raise HTTPException(status_code=500, detail=str(e))
|
|
|
|
|
|
@router.get("/torrents")
|
|
async def get_torrents():
|
|
"""Get list of torrents with tracker status"""
|
|
try:
|
|
data = transmission_rpc("torrent-get", {
|
|
"fields": [
|
|
"id", "name", "status", "percentDone",
|
|
"rateDownload", "rateUpload",
|
|
"uploadRatio", "trackerStats",
|
|
"error", "errorString"
|
|
]
|
|
})
|
|
|
|
torrents = data.get("torrents", [])
|
|
|
|
# Process tracker stats
|
|
for torrent in torrents:
|
|
tracker_errors = []
|
|
for tracker in torrent.get("trackerStats", []):
|
|
if tracker.get("lastAnnounceResult") and tracker.get("lastAnnounceResult") != "Success":
|
|
tracker_errors.append({
|
|
"host": tracker.get("host"),
|
|
"error": tracker.get("lastAnnounceResult")
|
|
})
|
|
torrent["trackerErrors"] = tracker_errors
|
|
torrent["hasTrackerErrors"] = len(tracker_errors) > 0
|
|
|
|
return {"torrents": torrents}
|
|
|
|
except Exception as e:
|
|
raise HTTPException(status_code=500, detail=str(e))
|
|
|
|
|
|
@router.get("/trackers")
|
|
async def get_tracker_summary():
|
|
"""Get summary of tracker connectivity"""
|
|
try:
|
|
data = transmission_rpc("torrent-get", {
|
|
"fields": ["trackerStats"]
|
|
})
|
|
|
|
torrents = data.get("torrents", [])
|
|
tracker_summary = {}
|
|
|
|
for torrent in torrents:
|
|
for tracker in torrent.get("trackerStats", []):
|
|
host = tracker.get("host")
|
|
if host not in tracker_summary:
|
|
tracker_summary[host] = {
|
|
"host": host,
|
|
"total": 0,
|
|
"errors": 0,
|
|
"lastError": None
|
|
}
|
|
|
|
tracker_summary[host]["total"] += 1
|
|
|
|
if tracker.get("lastAnnounceResult") and tracker.get("lastAnnounceResult") != "Success":
|
|
tracker_summary[host]["errors"] += 1
|
|
tracker_summary[host]["lastError"] = tracker.get("lastAnnounceResult")
|
|
|
|
return {"trackers": list(tracker_summary.values())}
|
|
|
|
except Exception as e:
|
|
raise HTTPException(status_code=500, detail=str(e))
|
|
|
|
|
|
@router.get("/stats")
|
|
async def get_stats():
|
|
"""Get Transmission statistics"""
|
|
try:
|
|
stats = transmission_rpc("session-stats")
|
|
return stats
|
|
except Exception as e:
|
|
raise HTTPException(status_code=500, detail=str(e))
|
|
|
|
|
|
@router.post("/start/{torrent_id}")
|
|
async def start_torrent(torrent_id: int):
|
|
"""Start a torrent"""
|
|
try:
|
|
transmission_rpc("torrent-start", {"ids": [torrent_id]})
|
|
return {"success": True}
|
|
except Exception as e:
|
|
raise HTTPException(status_code=500, detail=str(e))
|
|
|
|
|
|
@router.post("/stop/{torrent_id}")
|
|
async def stop_torrent(torrent_id: int):
|
|
"""Stop a torrent"""
|
|
try:
|
|
transmission_rpc("torrent-stop", {"ids": [torrent_id]})
|
|
return {"success": True}
|
|
except Exception as e:
|
|
raise HTTPException(status_code=500, detail=str(e))
|
|
|
|
|
|
@router.post("/verify/{torrent_id}")
|
|
async def verify_torrent(torrent_id: int):
|
|
"""Verify a torrent"""
|
|
try:
|
|
transmission_rpc("torrent-verify", {"ids": [torrent_id]})
|
|
return {"success": True}
|
|
except Exception as e:
|
|
raise HTTPException(status_code=500, detail=str(e))
|
|
|
|
|
|
@router.post("/reannounce/{torrent_id}")
|
|
async def reannounce_torrent(torrent_id: int):
|
|
"""Force reannounce to tracker"""
|
|
try:
|
|
transmission_rpc("torrent-reannounce", {"ids": [torrent_id]})
|
|
return {"success": True}
|
|
except Exception as e:
|
|
raise HTTPException(status_code=500, detail=str(e))
|