Redid what2watch

This commit is contained in:
2025-09-21 18:13:47 -05:00
parent 547ccbbb0a
commit c5d619d450
5 changed files with 125 additions and 40 deletions

View File

@@ -2,6 +2,8 @@
- Fixed JFA-GO API keys expiring. The bot now schedules a key refresh - Fixed JFA-GO API keys expiring. The bot now schedules a key refresh
- Added Proxmox support for checking storage pool size - Added Proxmox support for checking storage pool size
- !what2watch is now !movies2watch
- Added !shows2watch, now pulls 5 random shows
# 1.0.6 # 1.0.6

View File

@@ -49,7 +49,8 @@ Fill out values in the .env and you're good to go!
- `!recoveraccount` <username> <newpassword> - Reset your password - `!recoveraccount` <username> <newpassword> - Reset your password
- `!deleteaccount` <username> - Delete your Jellyfin account - `!deleteaccount` <username> - Delete your Jellyfin account
- `!trialaccount` <username> <password> - Create a 24-hour trial Jellyfin account. Only if ENABLE_TRIAL_ACCOUNTS=True - `!trialaccount` <username> <password> - Create a 24-hour trial Jellyfin account. Only if ENABLE_TRIAL_ACCOUNTS=True
- `!what2watch` - Lists 5 random movie suggestions from the Jellyfin Library - `!movies2watch` - Lists 5 random movie suggestions from the Jellyfin Library
- `!shows2watch` - Lists 5 random show suggestions from the Jellyfin Library
- `!help` - Displays help command - `!help` - Displays help command
***🛠️ Admin Commands*** ***🛠️ Admin Commands***

156
app.py
View File

@@ -63,7 +63,7 @@ DB_NAME = get_env_var("DB_NAME")
LOCAL_TZ = pytz.timezone(get_env_var("LOCAL_TZ", str, required=False) or "America/Chicago") LOCAL_TZ = pytz.timezone(get_env_var("LOCAL_TZ", str, required=False) or "America/Chicago")
BOT_VERSION = "1.0.6" BOT_VERSION = "1.0.7"
VERSION_URL = "https://raw.githubusercontent.com/PenguCCN/Jellycord/main/version.txt" VERSION_URL = "https://raw.githubusercontent.com/PenguCCN/Jellycord/main/version.txt"
RELEASES_URL = "https://github.com/PenguCCN/Jellycord/releases" RELEASES_URL = "https://github.com/PenguCCN/Jellycord/releases"
@@ -299,6 +299,32 @@ def reset_jellyfin_password(username: str, new_password: str) -> bool:
response = requests.post(f"{JELLYFIN_URL}/Users/{user_id}/Password", headers=headers, json=data) response = requests.post(f"{JELLYFIN_URL}/Users/{user_id}/Password", headers=headers, json=data)
return response.status_code in (200, 204) return response.status_code in (200, 204)
def create_trial_jellyfin_user(username, password):
payload = {
"Name": username,
"Password": password,
"Policy": {
"EnableDownloads": False,
"EnableSyncTranscoding": False,
"EnableRemoteControlOfOtherUsers": False,
"EnableLiveTvAccess": False,
"IsAdministrator": False,
"IsHidden": False,
"IsDisabled": False
}
}
headers = {
"X-Emby-Token": JELLYFIN_API_KEY,
"Content-Type": "application/json"
}
response = requests.post(f"{JELLYFIN_URL}/Users/New", json=payload, headers=headers)
if response.status_code == 200:
return response.json().get("Id")
else:
print(f"[Jellyfin] Trial user creation failed. Status: {response.status_code}, Response: {response.text}")
return None
# ===================== # =====================
# JELLYSEERR HELPERS # JELLYSEERR HELPERS
# ===================== # =====================
@@ -522,32 +548,6 @@ def get_metadata(key):
cur.close() cur.close()
conn.close() conn.close()
return row[0] if row else None return row[0] if row else None
def create_trial_jellyfin_user(username, password):
payload = {
"Name": username,
"Password": password,
"Policy": {
"EnableDownloads": False,
"EnableSyncTranscoding": False,
"EnableRemoteControlOfOtherUsers": False,
"EnableLiveTvAccess": False,
"IsAdministrator": False,
"IsHidden": False,
"IsDisabled": False
}
}
headers = {
"X-Emby-Token": JELLYFIN_API_KEY,
"Content-Type": "application/json"
}
response = requests.post(f"{JELLYFIN_URL}/Users/New", json=payload, headers=headers)
if response.status_code == 200:
return response.json().get("Id")
else:
print(f"[Jellyfin] Trial user creation failed. Status: {response.status_code}, Response: {response.text}")
return None
def _update_env_key(key: str, value: str, env_path: str = ".env"): def _update_env_key(key: str, value: str, env_path: str = ".env"):
"""Update or append key=value in .env (keeps file order).""" """Update or append key=value in .env (keeps file order)."""
@@ -1049,8 +1049,9 @@ async def deleteaccount(ctx, username: str = None):
await ctx.send(f"❌ Failed to delete Jellyfin account **{username}**.") await ctx.send(f"❌ Failed to delete Jellyfin account **{username}**.")
@bot.command() @bot.command()
async def what2watch(ctx): async def movies2watch(ctx):
"""Pick 5 random movies from the Jellyfin library with embeds and posters.""" log_event(f"movies2watch invoked by {ctx.author}")
"""Pick 5 random movies from the Jellyfin library with embeds, and IMDb links."""
member = ctx.guild.get_member(ctx.author.id) if ctx.guild else None member = ctx.guild.get_member(ctx.author.id) if ctx.guild else None
if not member or not has_required_role(member): if not member or not has_required_role(member):
await ctx.send(f"{ctx.author.mention}, you dont have the required role to use this command.") await ctx.send(f"{ctx.author.mention}, you dont have the required role to use this command.")
@@ -1058,8 +1059,17 @@ async def what2watch(ctx):
headers = {"X-Emby-Token": JELLYFIN_API_KEY} headers = {"X-Emby-Token": JELLYFIN_API_KEY}
try: try:
# Fetch all movies # Fetch all movies (include ProviderIds explicitly!)
r = requests.get(f"{JELLYFIN_URL}/Items?IncludeItemTypes=Movie&Recursive=true", headers=headers, timeout=10) r = requests.get(
f"{JELLYFIN_URL}/Items",
headers=headers,
params={
"IncludeItemTypes": "Movie",
"Recursive": "true",
"Fields": "ProviderIds"
},
timeout=10
)
if r.status_code != 200: if r.status_code != 200:
await ctx.send(f"❌ Failed to fetch movies. Status code: {r.status_code}") await ctx.send(f"❌ Failed to fetch movies. Status code: {r.status_code}")
return return
@@ -1079,21 +1089,26 @@ async def what2watch(ctx):
) )
for movie in selection: for movie in selection:
name = movie.get("Name") name = movie.get("Name", "Unknown Title")
year = movie.get("ProductionYear", "N/A") year = movie.get("ProductionYear", "N/A")
runtime = movie.get("RunTimeTicks", None) runtime = movie.get("RunTimeTicks", None)
runtime_min = int(runtime / 10_000_000 / 60) if runtime else "N/A" runtime_min = int(runtime / 10_000_000 / 60) if runtime else "N/A"
# Poster URL if available # Poster URL if available
poster_url = None poster_url = None
if "PrimaryImageTag" in movie and movie["PrimaryImageTag"]: if "PrimaryImageTag" in movie and movie["PrimaryImageTag"]:
poster_url = f"{JELLYFIN_URL}/Items/{movie['Id']}/Images/Primary?tag={movie['PrimaryImageTag']}&quality=90" poster_url = f"{JELLYFIN_URL}/Items/{movie['Id']}/Images/Primary?tag={movie['PrimaryImageTag']}&quality=90"
field_value = f"Year: {year}\nRuntime: {runtime_min} min" # IMDb link if available
imdb_id = movie.get("ProviderIds", {}).get("Imdb")
imdb_link = f"[IMDb Link](https://www.imdb.com/title/{imdb_id})" if imdb_id else "No IMDb ID available"
# Field content
field_value = f"Year: {year}\nRuntime: {runtime_min} min\n{imdb_link}"
embed.add_field(name=name, value=field_value, inline=False) embed.add_field(name=name, value=field_value, inline=False)
if poster_url: if poster_url:
embed.set_image(url=poster_url) # Only last movie's poster will appear as main embed image embed.set_image(url=poster_url) # Only the last poster appears as main embed image
await ctx.send(embed=embed) await ctx.send(embed=embed)
@@ -1101,6 +1116,72 @@ async def what2watch(ctx):
await ctx.send(f"❌ Error fetching movies: {e}") await ctx.send(f"❌ Error fetching movies: {e}")
print(f"[what2watch] Error: {e}") print(f"[what2watch] Error: {e}")
@bot.command()
async def shows2watch(ctx):
log_event(f"shows2watch invoked by {ctx.author}")
"""Pick 5 random TV shows from the Jellyfin library with embeds, and IMDb links."""
member = ctx.guild.get_member(ctx.author.id) if ctx.guild else None
if not member or not has_required_role(member):
await ctx.send(f"{ctx.author.mention}, you dont have the required role to use this command.")
return
headers = {"X-Emby-Token": JELLYFIN_API_KEY}
try:
# Fetch all shows (include ProviderIds explicitly!)
r = requests.get(
f"{JELLYFIN_URL}/Items",
headers=headers,
params={
"IncludeItemTypes": "Series",
"Recursive": "true",
"Fields": "ProviderIds"
},
timeout=10
)
if r.status_code != 200:
await ctx.send(f"❌ Failed to fetch shows. Status code: {r.status_code}")
return
shows = r.json().get("Items", [])
if not shows:
await ctx.send("⚠️ No shows found in the library.")
return
# Pick 5 random shows
selection = random.sample(shows, min(5, len(shows)))
embed = discord.Embed(
title="📺 Shows to Watch",
description="Here are 5 random TV show suggestions from the library:",
color=discord.Color.green()
)
for show in selection:
name = show.get("Name", "Unknown Title")
year = show.get("ProductionYear", "N/A")
# Poster URL if available
poster_url = None
if "PrimaryImageTag" in show and show["PrimaryImageTag"]:
poster_url = f"{JELLYFIN_URL}/Items/{show['Id']}/Images/Primary?tag={show['PrimaryImageTag']}&quality=90"
# IMDb link if available
imdb_id = show.get("ProviderIds", {}).get("Imdb")
imdb_link = f"[IMDb Link](https://www.imdb.com/title/{imdb_id})" if imdb_id else "No IMDb ID available"
# Field content
field_value = f"Year: {year}\n{imdb_link}"
embed.add_field(name=name, value=field_value, inline=False)
if poster_url:
embed.set_image(url=poster_url) # Only the last show's poster will appear as the embed image
await ctx.send(embed=embed)
except Exception as e:
await ctx.send(f"❌ Error fetching shows: {e}")
print(f"[shows2watch] Error: {e}")
@bot.command() @bot.command()
async def cleanup(ctx): async def cleanup(ctx):
@@ -1591,7 +1672,8 @@ async def help_command(ctx):
f"`{PREFIX}createaccount <username> <password>` - Create your Jellyfin account", f"`{PREFIX}createaccount <username> <password>` - Create your Jellyfin account",
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}what2watch` - 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"
] ]
if ENABLE_TRIAL_ACCOUNTS: if ENABLE_TRIAL_ACCOUNTS:
user_cmds.append(f"`{PREFIX}trialaccount <username> <password>` - Create a 24-hour trial Jellyfin account") user_cmds.append(f"`{PREFIX}trialaccount <username> <password>` - Create a 24-hour trial Jellyfin account")

View File

@@ -1 +1 @@
{ "version": "1.0.6" } { "version": "1.0.7" }

View File

@@ -1 +1 @@
1.0.6 1.0.7