Merge pull request #386 from g0ldyy/development

merge dev into main
This commit is contained in:
Goldy
2025-12-01 09:58:10 +01:00
committed by GitHub
9 changed files with 53 additions and 25 deletions
+2 -1
View File
@@ -40,7 +40,8 @@ DATABASE_BATCH_SIZE=20000 # The batch size for the database import and export op
# Cache Settings (Seconds) #
# ============================== #
METADATA_CACHE_TTL=2592000 # 30 days
TORRENT_CACHE_TTL=1296000 # 15 days
TORRENT_CACHE_TTL=1296000 # 15 days - How long before torrents in DB start to be removed. -1 to disable.
LIVE_TORRENT_CACHE_TTL=1296000 # 15 days - How long before Live is re-queried for a search.
DEBRID_CACHE_TTL=86400 # 1 day
METRICS_CACHE_TTL=60 # 1 minute
SCRAPE_LOCK_TTL=300 # 5 minutes - Duration for distributed scraping locks
+3 -8
View File
@@ -223,25 +223,20 @@ async def admin_api_metrics(admin_session: str = Cookie(None)):
# Torrents by tracker
top_trackers = await database.fetch_all("""
SELECT tracker, COUNT(*) as count
SELECT tracker, COUNT(*) as count, AVG(seeders) as avg_seeders, AVG(size) as avg_size
FROM torrents
GROUP BY tracker
ORDER BY count DESC
LIMIT 5
""")
tracker_stats = []
for row in top_trackers:
stats = await database.fetch_one(
"SELECT AVG(seeders) as avg_seeders, AVG(size) as avg_size FROM torrents WHERE tracker = :tracker",
{"tracker": row["tracker"]},
)
tracker_stats.append(
{
"tracker": row["tracker"],
"count": row["count"],
"avg_seeders": stats["avg_seeders"],
"avg_size": stats["avg_size"],
"avg_seeders": row["avg_seeders"],
"avg_size": row["avg_size"],
}
)
+3 -1
View File
@@ -109,6 +109,8 @@ async def stream(
if "tmdb:" in media_id:
return {"streams": []}
media_id = media_id.replace("imdb_id:", "")
config = config_check(b64config)
if not config:
return {
@@ -145,7 +147,7 @@ async def stream(
"media_id": id,
"season": season,
"episode": episode,
"cache_ttl": settings.TORRENT_CACHE_TTL,
"cache_ttl": settings.LIVE_TORRENT_CACHE_TTL,
"current_time": time.time(),
},
)
+10 -7
View File
@@ -610,13 +610,14 @@ async def setup_database():
{"cache_ttl": settings.METADATA_CACHE_TTL, "current_time": time.time()},
)
await database.execute(
"""
DELETE FROM torrents
WHERE timestamp + :cache_ttl < :current_time;
""",
{"cache_ttl": settings.TORRENT_CACHE_TTL, "current_time": time.time()},
)
if settings.TORRENT_CACHE_TTL >= 0:
await database.execute(
"""
DELETE FROM torrents
WHERE timestamp + :cache_ttl < :current_time;
""",
{"cache_ttl": settings.TORRENT_CACHE_TTL, "current_time": time.time()},
)
await database.execute(
"""
@@ -630,6 +631,8 @@ async def setup_database():
await database.execute("DELETE FROM active_connections")
await database.execute("DELETE FROM metrics_cache")
except Exception as e:
logger.error(f"Error setting up the database: {e}")
logger.exception(traceback.format_exc())
+1 -1
View File
@@ -180,7 +180,7 @@ def log_startup_info(settings):
)
logger.log(
"COMET",
f"Database ({settings.DATABASE_TYPE}): {settings.DATABASE_PATH if settings.DATABASE_TYPE == 'sqlite' else settings.DATABASE_URL} - TTL: metadata={settings.METADATA_CACHE_TTL}s, torrents={settings.TORRENT_CACHE_TTL}s, debrid={settings.DEBRID_CACHE_TTL}s, metrics={settings.METRICS_CACHE_TTL}s",
f"Database ({settings.DATABASE_TYPE}): {settings.DATABASE_PATH if settings.DATABASE_TYPE == 'sqlite' else settings.DATABASE_URL} - TTL: metadata={settings.METADATA_CACHE_TTL}s, torrents={settings.TORRENT_CACHE_TTL}s, live_torrents={settings.LIVE_TORRENT_CACHE_TTL}s, debrid={settings.DEBRID_CACHE_TTL}s, metrics={settings.METRICS_CACHE_TTL}s",
)
logger.log("COMET", f"Bypass Proxy: {settings.BYPASS_PROXY_URL}")
+1
View File
@@ -34,6 +34,7 @@ class AppSettings(BaseSettings):
DATABASE_BATCH_SIZE: Optional[int] = 20000
METADATA_CACHE_TTL: Optional[int] = 2592000 # 30 days
TORRENT_CACHE_TTL: Optional[int] = 1296000 # 15 days
LIVE_TORRENT_CACHE_TTL: Optional[int] = 1296000 # 15 days
DEBRID_CACHE_TTL: Optional[int] = 86400 # 1 day
METRICS_CACHE_TTL: Optional[int] = 60 # 1 minute
SCRAPE_LOCK_TTL: Optional[int] = 300 # 5 minutes
+2 -2
View File
@@ -110,7 +110,7 @@ class TorrentManager:
"media_id": self.media_only_id,
"season": self.season,
"episode": self.episode,
"cache_ttl": settings.TORRENT_CACHE_TTL,
"cache_ttl": settings.LIVE_TORRENT_CACHE_TTL,
"current_time": time.time(),
},
)
@@ -156,7 +156,7 @@ class TorrentManager:
]
query = f"""
INSERT {"OR IGNORE " if settings.DATABASE_TYPE == "sqlite" else ""}
INSERT {"OR REPLACE " if settings.DATABASE_TYPE == "sqlite" else ""}
INTO torrents
VALUES (:media_id, :info_hash, :file_index, :season, :episode, :title, :seeders, :size, :tracker, :sources, :parsed, :timestamp)
{" ON CONFLICT DO NOTHING" if settings.DATABASE_TYPE == "postgresql" else ""}
+2 -2
View File
@@ -148,7 +148,7 @@ async def add_torrent(
await database.execute(
f"""
INSERT {"OR IGNORE " if settings.DATABASE_TYPE == "sqlite" else ""}
INSERT {"OR REPLACE " if settings.DATABASE_TYPE == "sqlite" else ""}
INTO torrents
VALUES (:media_id, :info_hash, :file_index, :season, :episode, :title, :seeders, :size, :tracker, :sources, :parsed, :timestamp)
{" ON CONFLICT DO NOTHING" if settings.DATABASE_TYPE == "postgresql" else ""}
@@ -402,7 +402,7 @@ class TorrentUpdateQueue:
sub_batch = self.batches["inserts"][i : i + sub_batch_size]
async with database.transaction():
insert_query = f"""
INSERT {"OR IGNORE " if settings.DATABASE_TYPE == "sqlite" else ""}
INSERT {"OR REPLACE " if settings.DATABASE_TYPE == "sqlite" else ""}
INTO torrents
VALUES (
:media_id,
+29 -3
View File
@@ -956,7 +956,16 @@
<h3><sl-icon name="download"></sl-icon> Torrents Analysis</h3>
<div class="metric-content">
<div class="metric-subsection">
<h4>Top Trackers</h4>
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 12px;">
<h4 style="margin: 0;">Top Trackers</h4>
<sl-select id="tracker-limit" size="small" value="5" style="width: 110px;">
<sl-option value="5">Top 5</sl-option>
<sl-option value="10">Top 10</sl-option>
<sl-option value="20">Top 20</sl-option>
<sl-option value="50">Top 50</sl-option>
<sl-option value="all">All</sl-option>
</sl-select>
</div>
<div id="tracker-stats" class="data-list">
<div class="loading-state">
<sl-spinner></sl-spinner>
@@ -1075,6 +1084,7 @@
initializeDashboard();
});
let currentMetricsData = null;
let connectionsInterval;
let logsInterval;
let lastLogTimestamp = 0;
@@ -1103,6 +1113,13 @@
document
.getElementById("hide-api-logs")
.addEventListener("sl-change", toggleApiLogsVisibility);
document
.getElementById("tracker-limit")
.addEventListener("sl-change", () => {
if (currentMetricsData) {
updateTorrentsSection(currentMetricsData.torrents);
}
});
// Tab change handler
document
@@ -1390,6 +1407,7 @@
try {
const response = await fetch("/admin/api/metrics");
const data = await response.json();
currentMetricsData = data;
updateMetricsOverview(data);
updateTorrentsSection(data.torrents);
@@ -1414,11 +1432,19 @@
function updateTorrentsSection(torrentsData) {
// Update tracker statistics
const trackerContainer = document.getElementById("tracker-stats");
if (torrentsData.by_tracker.length === 0) {
const limitSelect = document.getElementById("tracker-limit");
const limit = limitSelect ? limitSelect.value : "5";
let displayTrackers = torrentsData.by_tracker;
if (limit !== "all") {
displayTrackers = displayTrackers.slice(0, parseInt(limit));
}
if (displayTrackers.length === 0) {
trackerContainer.innerHTML =
'<div class="empty-state"><sl-icon name="database-slash"></sl-icon><p>No tracker data available</p></div>';
} else {
trackerContainer.innerHTML = torrentsData.by_tracker
trackerContainer.innerHTML = displayTrackers
.map(
(tracker) => `
<div class="tracker-item">