Push Basic Servarr Support
This commit is contained in:
@@ -26,6 +26,15 @@ JFA_PASSWORD=yourpassword
|
|||||||
JFA_API_KEY=your_api_key_here
|
JFA_API_KEY=your_api_key_here
|
||||||
JFA_TOKEN=
|
JFA_TOKEN=
|
||||||
|
|
||||||
|
# |Servarr|
|
||||||
|
RADARR_URL=http://your-radarr-ip:7878
|
||||||
|
RADARR_API_KEY=yourradarrapikey
|
||||||
|
ENABLE_RADARR=false
|
||||||
|
|
||||||
|
SONARR_URL=http://your-sonarr-ip:8989
|
||||||
|
SONARR_API_KEY=yoursonarrapikey
|
||||||
|
ENABLE_SONARR=false
|
||||||
|
|
||||||
# |QBittorrent|
|
# |QBittorrent|
|
||||||
ENABLE_QBITTORRENT=false
|
ENABLE_QBITTORRENT=false
|
||||||
QBIT_HOST=http://localhost:8080
|
QBIT_HOST=http://localhost:8080
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
# 1.1.0
|
# 1.1.0
|
||||||
|
|
||||||
- The bot now sends limited info stats to a public frontend for development insight. (Reports enabled features and bot instance only | Jellyseerr, Proxmox, JFA, qBittorrent)
|
- The bot now sends limited info stats to a public frontend for development insight. (Reports enabled features and bot instance only | Jellyseerr, Proxmox, JFA, qBittorrent)
|
||||||
|
- Radarr & Sonarr Support
|
||||||
|
|
||||||
# 1.0.9
|
# 1.0.9
|
||||||
|
|
||||||
|
|||||||
189
app.py
189
app.py
@@ -56,6 +56,14 @@ JFA_USERNAME = os.getenv("JFA_USERNAME")
|
|||||||
JFA_PASSWORD = os.getenv("JFA_PASSWORD")
|
JFA_PASSWORD = os.getenv("JFA_PASSWORD")
|
||||||
JFA_API_KEY = os.getenv("JFA_API_KEY")
|
JFA_API_KEY = os.getenv("JFA_API_KEY")
|
||||||
|
|
||||||
|
ENABLE_RADARR = os.getenv("ENABLE_RADARR", "false").lower() == "true"
|
||||||
|
RADARR_URL = os.getenv("RADARR_URL", "").rstrip("/")
|
||||||
|
RADARR_API_KEY = os.getenv("RADARR_API_KEY", "")
|
||||||
|
|
||||||
|
ENABLE_SONARR = os.getenv("ENABLE_SONARR", "false").lower() == "true"
|
||||||
|
SONARR_URL = os.getenv("SONARR_URL", "").rstrip("/")
|
||||||
|
SONARR_API_KEY = os.getenv("SONARR_API_KEY", "")
|
||||||
|
|
||||||
ENABLE_QBITTORRENT = os.getenv("ENABLE_QBITTORRENT", "False").lower() == "true"
|
ENABLE_QBITTORRENT = os.getenv("ENABLE_QBITTORRENT", "False").lower() == "true"
|
||||||
QBIT_HOST = os.getenv("QBIT_HOST")
|
QBIT_HOST = os.getenv("QBIT_HOST")
|
||||||
QBIT_USERNAME = os.getenv("QBIT_USERNAME")
|
QBIT_USERNAME = os.getenv("QBIT_USERNAME")
|
||||||
@@ -91,7 +99,9 @@ POST_ENDPOINTS = {
|
|||||||
"jellyseerr": "https://jellycordstats.pengucc.com/api/jellyseerr",
|
"jellyseerr": "https://jellycordstats.pengucc.com/api/jellyseerr",
|
||||||
"proxmox": "https://jellycordstats.pengucc.com/api/proxmox",
|
"proxmox": "https://jellycordstats.pengucc.com/api/proxmox",
|
||||||
"jfa": "https://jellycordstats.pengucc.com/api/jfa",
|
"jfa": "https://jellycordstats.pengucc.com/api/jfa",
|
||||||
"qbittorrent": "https://jellycordstats.pengucc.com/api/qbittorrent"
|
"qbittorrent": "https://jellycordstats.pengucc.com/api/qbittorrent",
|
||||||
|
"radarr": "https://jellycordstats.pengucc.com/api/radarr",
|
||||||
|
"sonarr": "https://jellycordstats.pengucc.com/api/sonarr"
|
||||||
}
|
}
|
||||||
|
|
||||||
# =====================
|
# =====================
|
||||||
@@ -409,6 +419,81 @@ def delete_jellyseerr_user(js_id: str) -> bool:
|
|||||||
print(f"[Jellyseerr] Failed to delete user {js_id}: {e}")
|
print(f"[Jellyseerr] Failed to delete user {js_id}: {e}")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
# =====================
|
||||||
|
# SERVARR HELPERS
|
||||||
|
# =====================
|
||||||
|
|
||||||
|
def radarr_get_movies():
|
||||||
|
"""Return a list of all movies Radarr is managing."""
|
||||||
|
if not ENABLE_RADARR:
|
||||||
|
return None
|
||||||
|
|
||||||
|
try:
|
||||||
|
response = requests.get(
|
||||||
|
f"{RADARR_URL}/api/v3/movie",
|
||||||
|
headers={"X-Api-Key": RADARR_API_KEY},
|
||||||
|
timeout=10
|
||||||
|
)
|
||||||
|
if response.status_code != 200:
|
||||||
|
print(f"[Radarr] Error fetching movies: {response.status_code} {response.text}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
return response.json()
|
||||||
|
except Exception as e:
|
||||||
|
print(f"[Radarr] Exception: {e}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
def radarr_get_latest_movies(count=5):
|
||||||
|
"""Return the latest added movies from Radarr."""
|
||||||
|
movies = radarr_get_movies()
|
||||||
|
if not movies:
|
||||||
|
return None
|
||||||
|
|
||||||
|
# Sort by 'added' field if available
|
||||||
|
sorted_movies = sorted(
|
||||||
|
movies,
|
||||||
|
key=lambda m: m.get("added", ""),
|
||||||
|
reverse=True
|
||||||
|
)
|
||||||
|
|
||||||
|
return sorted_movies[:count]
|
||||||
|
|
||||||
|
def sonarr_get_series():
|
||||||
|
"""Return a list of all series Sonarr is managing."""
|
||||||
|
if not ENABLE_SONARR:
|
||||||
|
return None
|
||||||
|
|
||||||
|
try:
|
||||||
|
response = requests.get(
|
||||||
|
f"{SONARR_URL}/api/v3/series",
|
||||||
|
headers={"X-Api-Key": SONARR_API_KEY},
|
||||||
|
timeout=10
|
||||||
|
)
|
||||||
|
if response.status_code != 200:
|
||||||
|
print(f"[Sonarr] Error fetching series: {response.status_code} {response.text}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
return response.json()
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"[Sonarr] Exception: {e}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
def sonarr_get_latest_series(count=5):
|
||||||
|
"""Return the latest added series from Sonarr."""
|
||||||
|
series = sonarr_get_series()
|
||||||
|
if not series:
|
||||||
|
return None
|
||||||
|
|
||||||
|
# Sonarr tracks `added` timestamps too
|
||||||
|
sorted_series = sorted(
|
||||||
|
series,
|
||||||
|
key=lambda s: s.get("added", ""),
|
||||||
|
reverse=True
|
||||||
|
)
|
||||||
|
|
||||||
|
return sorted_series[:count]
|
||||||
|
|
||||||
# =====================
|
# =====================
|
||||||
# QBITTORRENT HELPERS
|
# QBITTORRENT HELPERS
|
||||||
# =====================
|
# =====================
|
||||||
@@ -1526,6 +1611,100 @@ async def activestreams(ctx):
|
|||||||
await ctx.send(f"❌ Error fetching active streams: {e}")
|
await ctx.send(f"❌ Error fetching active streams: {e}")
|
||||||
print(f"[activestreams] Error: {e}")
|
print(f"[activestreams] Error: {e}")
|
||||||
|
|
||||||
|
@bot.command()
|
||||||
|
async def moviestats(ctx):
|
||||||
|
"""Show Radarr's latest 5 added movies with total count."""
|
||||||
|
if not ENABLE_RADARR:
|
||||||
|
await ctx.send("⚠️ Radarr support is not enabled.")
|
||||||
|
return
|
||||||
|
|
||||||
|
movies = radarr_get_movies()
|
||||||
|
if movies is None:
|
||||||
|
await ctx.send("❌ Failed to connect to Radarr.")
|
||||||
|
return
|
||||||
|
|
||||||
|
total_count = len(movies)
|
||||||
|
|
||||||
|
# Sort by newest "added"
|
||||||
|
latest = sorted(
|
||||||
|
movies,
|
||||||
|
key=lambda m: m.get("added", ""),
|
||||||
|
reverse=True
|
||||||
|
)[:5]
|
||||||
|
|
||||||
|
embed = discord.Embed(
|
||||||
|
title="🎞️ Latest Radarr Additions",
|
||||||
|
color=discord.Color.orange()
|
||||||
|
)
|
||||||
|
|
||||||
|
for movie in latest:
|
||||||
|
title = movie.get("title", "Unknown")
|
||||||
|
year = movie.get("year", "Unknown")
|
||||||
|
added = movie.get("added", "Unknown")
|
||||||
|
tmdb_id = movie.get("tmdbId")
|
||||||
|
|
||||||
|
tmdb_link = (
|
||||||
|
f"https://www.themoviedb.org/movie/{tmdb_id}"
|
||||||
|
if tmdb_id else "No TMDB ID"
|
||||||
|
)
|
||||||
|
|
||||||
|
embed.add_field(
|
||||||
|
name=f"{title} ({year})",
|
||||||
|
value=f"📅 Added: `{added}`\n🔗 {tmdb_link}",
|
||||||
|
inline=False
|
||||||
|
)
|
||||||
|
|
||||||
|
embed.set_footer(text=f"Total movies managed by Radarr: {total_count}")
|
||||||
|
|
||||||
|
await ctx.send(embed=embed)
|
||||||
|
|
||||||
|
@bot.command()
|
||||||
|
async def showstats(ctx):
|
||||||
|
"""Show Sonarr's latest 5 added series with total count."""
|
||||||
|
if not ENABLE_SONARR:
|
||||||
|
await ctx.send("⚠️ Sonarr support is not enabled.")
|
||||||
|
return
|
||||||
|
|
||||||
|
series = sonarr_get_series()
|
||||||
|
if series is None:
|
||||||
|
await ctx.send("❌ Failed to connect to Sonarr.")
|
||||||
|
return
|
||||||
|
|
||||||
|
total_count = len(series)
|
||||||
|
|
||||||
|
# Newest first
|
||||||
|
latest = sorted(
|
||||||
|
series,
|
||||||
|
key=lambda s: s.get("added", ""),
|
||||||
|
reverse=True
|
||||||
|
)[:5]
|
||||||
|
|
||||||
|
embed = discord.Embed(
|
||||||
|
title="📺 Latest Sonarr Additions",
|
||||||
|
color=discord.Color.blue()
|
||||||
|
)
|
||||||
|
|
||||||
|
for show in latest:
|
||||||
|
title = show.get("title", "Unknown")
|
||||||
|
year = show.get("year", "Unknown")
|
||||||
|
added = show.get("added", "Unknown")
|
||||||
|
tvdb_id = show.get("tvdbId")
|
||||||
|
|
||||||
|
tvdb_link = (
|
||||||
|
f"https://thetvdb.com/?id={tvdb_id}&tab=series"
|
||||||
|
if tvdb_id else "No TVDB ID"
|
||||||
|
)
|
||||||
|
|
||||||
|
embed.add_field(
|
||||||
|
name=f"{title} ({year})",
|
||||||
|
value=f"📅 Added: `{added}`\n🔗 {tvdb_link}",
|
||||||
|
inline=False
|
||||||
|
)
|
||||||
|
|
||||||
|
embed.set_footer(text=f"Total series managed by Sonarr: {total_count}")
|
||||||
|
|
||||||
|
await ctx.send(embed=embed)
|
||||||
|
|
||||||
@bot.command()
|
@bot.command()
|
||||||
async def qbview(ctx):
|
async def qbview(ctx):
|
||||||
"""Admin-only: View current qBittorrent downloads."""
|
"""Admin-only: View current qBittorrent downloads."""
|
||||||
@@ -2139,7 +2318,9 @@ async def help_command(ctx):
|
|||||||
f"`{PREFIX}recoveraccount <newpassword>` - Reset your password",
|
f"`{PREFIX}recoveraccount <newpassword>` - Reset your password",
|
||||||
f"`{PREFIX}deleteaccount <username>` - Delete your Jellyfin account",
|
f"`{PREFIX}deleteaccount <username>` - Delete your Jellyfin account",
|
||||||
f"`{PREFIX}movies2watch` - Lists 5 random movie suggestions from the Jellyfin Library",
|
f"`{PREFIX}movies2watch` - Lists 5 random movie suggestions from the Jellyfin Library",
|
||||||
f"`{PREFIX}shows2watch` - Lists 5 random show suggestions from the Jellyfin Library"
|
f"`{PREFIX}shows2watch` - Lists 5 random show suggestions from the Jellyfin Library",
|
||||||
|
f"`{PREFIX}moviestats` - Lists latest 5 movies added, also shows total movie library size",
|
||||||
|
f"`{PREFIX}showstats` - Lists latest 5 movies added, also shows total series library size"
|
||||||
]
|
]
|
||||||
if ENABLE_TRIAL_ACCOUNTS:
|
if ENABLE_TRIAL_ACCOUNTS:
|
||||||
user_cmds.append(f"`{PREFIX}trialaccount <username> <password>` - Create a {TRIAL_TIME}-hour trial Jellyfin account")
|
user_cmds.append(f"`{PREFIX}trialaccount <username> <password>` - Create a {TRIAL_TIME}-hour trial Jellyfin account")
|
||||||
@@ -2360,7 +2541,9 @@ async def periodic_post_task():
|
|||||||
"jellyseerr": JELLYSEERR_ENABLED,
|
"jellyseerr": JELLYSEERR_ENABLED,
|
||||||
"proxmox": ENABLE_PROXMOX,
|
"proxmox": ENABLE_PROXMOX,
|
||||||
"jfa": ENABLE_JFA,
|
"jfa": ENABLE_JFA,
|
||||||
"qbittorrent": ENABLE_QBITTORRENT
|
"qbittorrent": ENABLE_QBITTORRENT,
|
||||||
|
"radarr": ENABLE_RADARR,
|
||||||
|
"sonarr": ENABLE_SONARR
|
||||||
}
|
}
|
||||||
|
|
||||||
for feature, enabled in features.items():
|
for feature, enabled in features.items():
|
||||||
|
|||||||
Reference in New Issue
Block a user