aboutsummaryrefslogtreecommitdiff
path: root/code/cogs/status.py
diff options
context:
space:
mode:
Diffstat (limited to 'code/cogs/status.py')
-rw-r--r--code/cogs/status.py293
1 files changed, 214 insertions, 79 deletions
diff --git a/code/cogs/status.py b/code/cogs/status.py
index 7b6b463..abda84a 100644
--- a/code/cogs/status.py
+++ b/code/cogs/status.py
@@ -3,10 +3,13 @@ from discord import app_commands
from discord.ext import commands
import requests
import sqlite3
-import datetime
-import humanize
-from global_variables import RADARR_HOST_URL, RADARR_HEADERS
+from utils.config import (
+ RADARR_HOST_URL,
+ RADARR_HEADERS,
+ SONARR_HOST_URL,
+ SONARR_HEADERS,
+)
class Status(commands.Cog):
@@ -14,97 +17,229 @@ class Status(commands.Cog):
self.bot = bot
@app_commands.command()
- async def status(self, interaction: discord.Interaction):
- "Get the status of the movies you have requested"
- # Get all the movie_ids that were requested by the user
+ async def status(self, interaction: discord.Interaction) -> None:
+ """Get the status of the movies you have requested"""
db = sqlite3.connect("cordarr.db")
cursor = db.cursor()
cursor.execute(
- "SELECT movie_id, movie_title FROM movies WHERE user_id = ?",
+ "SELECT title, release_year, local_id, tmdbid, tvdbid FROM"
+ " requests WHERE user_id = ?",
(interaction.user.id,),
)
- requested_movies = cursor.fetchall()
+ requested_content = cursor.fetchall()
+ db.close()
- users_movies = {} # Dictionary to store the movies that the user has requested
- for movie_id, movie_title in requested_movies:
- users_movies[movie_id] = movie_title
- # If theres no movies, return a message saying so
- if not users_movies:
+ # No content requested
+ if len(requested_content) == 0:
embed = discord.Embed(
- title="No Movies Requested",
- description="You have no movies being downloaded at the moment. If you previously added a movie, it is likely that it has finished downloading. If you believe this is an error, please contact an administrator.",
- color=0xD01B86
+ title="No Content Requested",
+ description=(
+ "If you believe this is in error, the content you have"
+ " requested is likely already downloaded."
+ ),
+ color=0xD01B86,
+ )
+ return await interaction.response.send_message(
+ embed=embed, ephemeral=True
)
- return await interaction.response.send_message(embed=embed, ephemeral=True)
- # Otherwise, create the default embed to display the movies being downloaded
+
+ # Create template embed
embed = discord.Embed(
- title="Movies Requested",
- description="Here are the movies you have requested that are currently being downloaded:\n",
- color=0xD01B86
+ title="Requested Content",
+ description=(
+ "Below are the movies/shows you have requested that are"
+ " currently being downloaded:\n"
+ ),
+ color=0xD01B86,
+ )
+
+ # Unpack the content
+ radarr_content_info, sonarr_content_info = self.unpack_content(
+ requested_content
)
+ # Get the descriptions and local IDs found in queue
+ radarr_desc, radarr_added_ids = self.process_queue(
+ radarr_content_info, "radarr"
+ )
+ sonarr_desc, sonarr_added_ids = self.process_queue(
+ sonarr_content_info, "sonarr"
+ )
+
+ added_ids = radarr_added_ids + sonarr_added_ids
+ # Get the description of content not in the queue
+ non_queue_desc = self.get_non_queue_content(
+ requested_content, added_ids, interaction.user.id
+ )
+
+ embed.description += radarr_desc + sonarr_desc + non_queue_desc
+
+ await interaction.response.send_message(embed=embed, ephemeral=True)
+
+ def unpack_content(self, requested_content: list) -> tuple:
+ """
+ Given a list of requested content, unpack it into two dictionaries
+
+ Args:
+ requested_content (list): A list of requested content
+
+ Returns:
+ tuple: A tuple of two dictionaries
+ """
+
+ radarr_content_info = {}
+ sonarr_content_info = {}
+
+ for content in requested_content:
+ title, release_year, local_id, tmdbid, tvdbid = content
+ if tmdbid is not None:
+ radarr_content_info[local_id] = {
+ "title": title,
+ "release_year": release_year,
+ "tmdbid": tmdbid,
+ }
+ else:
+ sonarr_content_info[local_id] = {
+ "title": title,
+ "release_year": release_year,
+ "tvdbid": tvdbid,
+ }
+
+ return radarr_content_info, sonarr_content_info
+
+ def process_queue(self, content_info: dict, service: str) -> str:
+ """
+ Given a dictionary of requested content and "sonarr"/"radarr", process the queue
+
+ Args:
+ content_info (dict): A dictionary of content information
+ service (str): The service to check the queue of
- # Now, we get the download status of all movies from the Radarr queue
- response = requests.get(
- f"{RADARR_HOST_URL}/api/v3/queue/", headers=RADARR_HEADERS
+ Returns:
+ str: The description of the embed
+ """
+
+ description = ""
+ added_ids = []
+
+ queue = requests.get(
+ f"{RADARR_HOST_URL if service == 'radarr' else SONARR_HOST_URL}/api/v3/queue",
+ headers=RADARR_HEADERS if service == "radarr" else SONARR_HEADERS,
).json()
- count = 0
- added_movie_ids = []
- for movie in response["records"]:
- movie_id = movie["movieId"]
- # If the movie is user requested and is being downloaded
- if movie_id in users_movies.keys():
- count += 1
- added_movie_ids.append(movie_id)
- if movie["status"] == "downloading":
- # Humanize the download time left, or result to 'Unknown
- try:
- time_left = humanize.precisedelta(
- datetime.datetime.strptime(movie["timeleft"], "%H:%M:%S")
- - datetime.datetime.strptime("00:00:00", "%H:%M:%S"),
- minimum_unit="seconds",
- )
- except ValueError:
- # Sometimes movies will download extremely show and therefore might
- # show 'days' in the time left, so strptime appropriately
- time_left = humanize.precisedelta(
- datetime.datetime.strptime(movie["timeleft"], "%d.%H:%M:%S")
- - datetime.datetime.strptime("00:00:00", "%H:%M:%S"),
- minimum_unit="seconds",
- )
- except KeyError or ValueError:
- time_left = "Unknown"
-
- # Add all the information
- embed.description += f"\n{count}. **{users_movies[movie_id]}** - Time Left: ` {time_left} `"
- else:
- embed.description += f"\n{count}. **{users_movies[movie_id]}** - Status: `{str(movie['status']).upper()}`"
-
- # If a movie wasn't found in the Radarr queue, then it has either finished downloading
- # or the movie was never found for download
- if len(added_movie_ids) != len(users_movies.keys()):
- # Grab all of the "missing" movies to see if a movie is missing or finished downloading
- response = requests.get(
- f"{RADARR_HOST_URL}/api/v3/wanted/missing", headers=RADARR_HEADERS
- ).json()
- for movie in response["records"]:
- movie_id = movie["id"]
- if movie_id in users_movies.keys() and movie_id not in added_movie_ids:
- count += 1
- added_movie_ids.append(movie_id)
- embed.description += f"\n{count}. **{users_movies[movie_id]}** - Status: ` NOT FOUND `"
- # If there are still movies that haven't been added to the embed, then they
- # have finished downloading and can be removed from the database
- for movie_id in users_movies.keys():
- if movie_id not in added_movie_ids:
- cursor.execute(
- "DELETE FROM movies WHERE user_id = ? AND movie_id = ?",
- (interaction.user.id, movie_id),
+ for download in queue["records"]:
+ id_str = "movieId" if service == "radarr" else "seriesId"
+ # If the content was requested by the user
+ if (
+ download[id_str] in content_info.keys()
+ and download[id_str] not in added_ids
+ ):
+ # Append local ID
+ added_ids.append(download[id_str])
+ # Add the download to the embed
+ try:
+ time_left = self.process_time(download["timeleft"])
+ except KeyError:
+ time_left = "Unknown"
+ description += (
+ f"\n**{content_info[download[id_str]]['title']} ({content_info[download[id_str]]['release_year']})**"
+ f" - Time Left: `{time_left}`"
)
- db.commit()
- db.close()
- await interaction.response.send_message(embed=embed, ephemeral=True)
+ return description, added_ids
+
+ def get_non_queue_content(
+ self, requested_content: list, added_ids: list, user_id: int
+ ) -> str:
+ """
+ Given a list of requested content and a list of added IDs, return a description of content not in the queue
+
+ Args:
+ requested_content (list): A list of requested content
+ added_ids (list): A list of IDs that are in the queue
+ user_id (int): The ID of the user
+
+ Returns:
+ str: A description of content not in the queue
+ """
+
+ description = ""
+ # For evry piece of content not in the queue, check if it has a file
+ for content in requested_content:
+ title, release_year, local_id, tmdbid, _ = content
+ # If not in queue
+ if local_id not in added_ids:
+ # Pull the movie data from the service
+ if tmdbid is not None:
+ data = requests.get(
+ f"{RADARR_HOST_URL}/api/v3/movie/{local_id}",
+ headers=RADARR_HEADERS,
+ ).json()
+ else:
+ data = requests.get(
+ f"{SONARR_HOST_URL}/api/v3/series/{local_id}",
+ headers=SONARR_HEADERS,
+ ).json()
+
+ # If the movie has a file, then it has finished downloading
+ if data.get("hasFile", True):
+ # Remove from database
+ db = sqlite3.connect("cordarr.db")
+ cursor = db.cursor()
+ cursor.execute(
+ "DELETE FROM requests WHERE user_id = ? AND"
+ " local_id = ?",
+ (user_id, local_id),
+ )
+ db.commit()
+ db.close()
+ # If series and only a portion of episodes have been downloaded
+ if data.get("statistics").get("percentOfEpisodes"):
+ description += (
+ f"\n**{title} ({release_year})** - Status: `NOT"
+ " FOUND"
+ f" ({int(data['statistics']['percentOfEpisodes'])}%"
+ " of eps.)`"
+ )
+ # All other scenarios, download not found
+ else:
+ description += (
+ f"\n**{title} ({release_year})** - Status: `NOT FOUND`"
+ )
+
+ return description
+
+ def process_time(self, time) -> str:
+ """
+ Given a time string, process it into a human readable format
+
+ Args:
+ time (str): A string representing time
+
+ Returns:
+ str: A human readable time
+ """
+ # Split the input by either ':' or spaces
+ parts = time.replace(" ", ":").replace(".", ":").split(":")
+
+ # Handle different input lengths
+ if len(parts) == 2: # Format: MM:SS
+ minutes, seconds = map(int, parts)
+ return f"{minutes} min. {seconds} sec."
+
+ elif len(parts) == 3: # Format: HH:MM:SS
+ hours, minutes, seconds = map(int, parts)
+ if hours == 0:
+ return f"{minutes} min. {seconds} sec."
+ return f"{hours} hr. {minutes} min."
+
+ elif len(parts) == 4: # Format: D:HH:MM:SS
+ days, hours, minutes, seconds = map(int, parts)
+ if days == 0:
+ return f"{hours} hr. {minutes} min."
+ return f"{days} days {hours} hr."
+
+ else:
+ return "Unknown"
async def setup(bot):