This commit is contained in:
2024-11-06 16:54:56 +01:00
parent e71ba34371
commit 7be6103a91
5 changed files with 342 additions and 15 deletions

View File

@ -6,4 +6,9 @@ class NoToken(Exception):
class CannotConnectToService(Exception): class CannotConnectToService(Exception):
def __init__(self, service: str) -> None: def __init__(self, service: str) -> None:
self.message = f"Cannot connect to {service}" self.message = f"Cannot connect to {service}"
super().__init__(self.message)
class ServiceNotProvided(Exception):
def __init__(self, service: str) -> None:
self.message = f"A {service} instance was not provided"
super().__init__(self.message) super().__init__(self.message)

View File

@ -1,6 +1,6 @@
"""A collection of all Gwendolyn functions.""" """A collection of all Gwendolyn functions."""
__all__ = ["Other","BetterNetflix", "Sonarr", "Radarr", "TMDb"] __all__ = ["Other","BetterNetflix", "Sonarr", "Radarr", "TMDb", "QBittorrent"]
from .other import Other from .other import Other
from .better_netflix import Radarr, Sonarr, BetterNetflix, TMDb from .better_netflix import Radarr, Sonarr, BetterNetflix, TMDb, QBittorrent

View File

@ -1,5 +1,5 @@
"""Better Netflix functions for Gwendolyn.""" """Better Netflix functions for Gwendolyn."""
__all__ = ["BetterNetflix", "Sonarr", "Radarr", "TMDb"] __all__ = ["BetterNetflix", "Sonarr", "Radarr", "TMDb", "QBittorrent"]
from .better_netflix import BetterNetflix, Sonarr, Radarr, TMDb from .better_netflix import BetterNetflix, Sonarr, Radarr, TMDb, QBittorrent

View File

@ -1,11 +1,13 @@
from requests import get from requests import get
from random import choice from random import choice
import io import io
from math import floor
from time import time
from PIL import Image from PIL import Image
from interactions import SlashContext, Embed, EmbedFooter, EmbedAttachment, File from interactions import SlashContext, Embed, EmbedFooter, EmbedAttachment, File
from gwendolyn.exceptions import CannotConnectToService from gwendolyn.exceptions import CannotConnectToService, ServiceNotProvided
class Service(): class Service():
def __init__(self, ip: str, port: str, api_key: str) -> None: def __init__(self, ip: str, port: str, api_key: str) -> None:
@ -35,6 +37,24 @@ class Sonarr(Service):
return response.json() return response.json()
class QBittorrent(Service):
def __init__(self, ip: str, port: str, username: str, password: str) -> None:
self.ip = ip
self.port = port
response = get(f"http://{ip}:{port}/api/v2/auth/login?username={username}&password={password}")
self.logged_in = response.ok
self.cookie = {"SID": response.cookies.values()[0]}
def test(self):
return self.logged_in
def get_torrents(self):
response = get(
f"http://{self.ip}:{self.port}/api/v2/torrents/info",
cookies=self.cookie
)
return response.json()
class TMDb(): class TMDb():
def __init__(self, api_token: str) -> None: def __init__(self, api_token: str) -> None:
self.header = { self.header = {
@ -47,20 +67,23 @@ class TMDb():
class BetterNetflix(): class BetterNetflix():
def __init__(self, radarr: Radarr, sonarr: Sonarr, tmdb: TMDb, bot) -> None: def __init__(self, bot, **kwargs) -> None:
self.radarr = radarr
self.sonarr = sonarr
self.tmdb = tmdb
self.bot = bot self.bot = bot
services = ["radarr","sonarr","tmdb"] services = ["radarr","sonarr","tmdb","qbittorrent"]
for service in services: for service in services:
try:
setattr(self,service,kwargs[service])
except:
raise ServiceNotProvided(service)
if getattr(self,service).test(): if getattr(self,service).test():
self.bot.log(f"Connected to {service}") self.bot.log(f"Connected to {service}")
else: else:
raise CannotConnectToService(service) raise CannotConnectToService(service)
def _poster_color(self, poster_url: str): def _poster_color(self, poster_url: str):
response = get(poster_url, stream=True) response = get(poster_url, stream=True)
img = Image.open(io.BytesIO(response.content)) img = Image.open(io.BytesIO(response.content))
@ -157,3 +180,301 @@ class BetterNetflix():
footer=year footer=year
) )
await msg.edit(content="",embed=embed) await msg.edit(content="",embed=embed)
def _draw_torrent_list(self, title_width: int):
def ratio_to_bar(ratio):
progress_bar = "|"+(""*floor(download_ratio*20))
while len(progress_bar) < 21:
progress_bar += " "
progress_bar += "| "+str(floor(download_ratio*100))+"%"
while len(progress_bar) < 27:
progress_bar += " "
return progress_bar
message = [""]
all_downloaded = True
dm_section_title = "*Torrent Downloads*"
dm_section_title_line = "-"*((title_width-len(dm_section_title))//2)
message.append(
dm_section_title_line+dm_section_title+dm_section_title_line
)
torrent_list = self.qbittorrent.get_torrents()
if len(torrent_list) == 0:
message.append("No torrents currently downloading")
return message, all_downloaded
for torrent in torrent_list:
if torrent['category'] not in ["radarr", "tv-sonarr"]:
break
torrent_name = torrent["name"]
if len(torrent_name) > 30:
if torrent_name[26] == " ":
torrent_name = torrent_name[:26]+"...."
else:
torrent_name = torrent_name[:27]+"..."
while len(torrent_name) < 30:
torrent_name += " "
if torrent["size"] == 0:
download_ratio = 0
elif torrent["amount_left"] == 0:
download_ratio = 1
else:
download_ratio = min(
torrent["downloaded"]/torrent["size"],
1
)
progress_bar = ratio_to_bar(download_ratio)
eta_in_seconds = torrent["eta"]
if eta_in_seconds >= 8640000:
eta = ""
else:
eta = ""
if eta_in_seconds >= 86400:
eta += str(floor(eta_in_seconds/86400))+"d "
if eta_in_seconds >= 3600:
eta += str(floor((eta_in_seconds%86400)/3600))+"h "
if eta_in_seconds >= 60:
eta += str(floor((eta_in_seconds%3600)/60))+"m "
eta += str(eta_in_seconds%60)+"s"
torrent_info = f"{torrent_name} {progress_bar} "
torrent_info += f"(Eta: {eta})"
if torrent["state"] == "stalledDL":
torrent_info += " (Stalled)"
if not (download_ratio == 1 and
torrent["last_activity"] < time()-7200):
message.append(torrent_info)
if download_ratio < 1 and torrent["state"] != "stalledDL":
all_downloaded = False
return message, all_downloaded
async def _generate_download_list(self, show_dm, show_movies, show_shows,
episodes):
"""Generate a list of all torrents.
*Returns*
message_text: str
A formatted list of all torrents
all_downloaded: bool
Whether all torrents are downloaded
"""
self.bot.log("Generating torrent list")
title_width = 100
message = []
if show_dm:
m, all_downloaded = self._draw_torrent_list(title_width)
message += m
# if show_movies:
# message.append("")
# movies_section_title = "*Missing movies not downloading*"
# movies_section_line = (
# "-"*((title_width-len(movies_section_title))//2)
# )
# message.append(
# movies_section_line+movies_section_title+movies_section_line
# )
# movie_list = requests.get(
# self.radarr_url+"movie?apiKey="+self.credentials["radarr_key"]
# ).json()
# print(
# self.radarr_url+"movie?apiKey="+self.credentials["radarr_key"]
# )
# movie_queue = requests.get(
# self.radarr_url+"queue?apiKey="+self.credentials["radarr_key"]
# ).json()
# movie_queue_ids = []
# for queue_item in movie_queue["records"]:
# movie_queue_ids.append(queue_item["movieId"])
# for movie in movie_list:
# if (not movie["hasFile"] and
# movie["id"] not in movie_queue_ids):
# movie_name = movie["title"]
# if len(movie_name) > 40:
# if movie_name[36] == " ":
# movie_name = movie_name[:36]+"...."
# else:
# movie_name = movie_name[:37]+"..."
# while len(movie_name) < 41:
# movie_name += " "
# if movie["monitored"]:
# movie_info = movie_name+"Could not find a torrent"
# else:
# movie_info = self.long_strings["No torrent"].format(
# movie_name
# )
# message.append(movie_info)
# if show_shows:
# message.append("")
# show_section_title = "*Missing shows not downloading*"
# show_section_line = "-"*((title_width-len(show_section_title))//2)
# message.append(
# show_section_line+show_section_title+show_section_line
# )
# show_list = requests.get(
# self.sonarr_url+"series?apiKey="+self.credentials["sonarr_key"]
# ).json()
# for show in show_list:
# if show["seasons"][0]["seasonNumber"] == 0:
# seasons = show["seasons"][1:]
# else:
# seasons = show["seasons"]
# if any(
# (
# i["statistics"]["episodeCount"] !=
# i["statistics"]["totalEpisodeCount"]
# ) for i in seasons):
# if all(
# i["statistics"]["episodeCount"] == 0 for i in seasons
# ):
# message.append(show["title"] + " (all episodes)")
# else:
# if episodes:
# missing_episodes = sum(
# (i["statistics"]["totalEpisodeCount"] -
# i["statistics"]["episodeCount"])
# for i in seasons)
# message.append(
# f"{show['title']} ({missing_episodes} episodes)"
# )
message.append("-"*title_width)
message_text = "```"+"\n".join(message[1:])+"```"
if message_text == "``````":
message_text = self.long_strings["No torrents downloading"]
return message_text, all_downloaded
# async def downloading(self, ctx, content):
# """Send message with list of all downloading torrents."""
# async def send_long_message(ctx,message_text):
# if len(message_text) <= 1994:
# await ctx.send("```"+message_text+"```")
# else:
# cut_off_index = message_text[:1994].rfind("\n")
# await ctx.send("```"+message_text[:cut_off_index]+"```")
# await send_long_message(ctx,message_text[cut_off_index+1:])
# await self.bot.defer(ctx)
# # showDM, showMovies, showShows, episodes
# parameters = [False, False, False, False]
# show_dm_args = ["d", "dm", "downloading", "downloadmanager"]
# show_movies_args = ["m", "movies"]
# show_shows_args = ["s", "shows", "series"]
# show_episode_args = ["e", "episodes"]
# arg_list = [
# show_dm_args, show_movies_args, show_shows_args, show_episode_args
# ]
# input_args = []
# valid_arguments = True
# while content != "" and valid_arguments:
# if content[0] == " ":
# content = content[1:]
# elif content[0] == "-":
# if content[1] == "-":
# arg_start = 2
# if " " in content:
# arg_stop = content.find(" ")
# else:
# arg_stop = None
# else:
# arg_start = 1
# arg_stop = 2
# input_args.append(content[arg_start:arg_stop])
# if arg_stop is None:
# content = ""
# else:
# content = content[arg_stop:]
# else:
# valid_arguments = False
# if valid_arguments:
# for arg_index, arg_aliases in enumerate(arg_list):
# arg_in_input = [i in input_args for i in arg_aliases]
# if any(arg_in_input):
# input_args.remove(arg_aliases[arg_in_input.index(True)])
# parameters[arg_index] = True
# if len(input_args) != 0 or (not parameters[2] and parameters[3]):
# valid_arguments = False
# show_anything = any(i for i in parameters)
# if not (valid_arguments and show_anything):
# await ctx.send(self.long_strings["Invalid parameters"])
# else:
# message_text, all_downloaded = await self.__generate_download_list(
# *parameters
# )
# if not message_text.startswith("```"):
# await ctx.send(message_text)
# elif len(message_text) > 2000:
# message_text = message_text[3:-3]
# await send_long_message(ctx,message_text)
# elif all_downloaded:
# await ctx.send(message_text)
# else:
# updates_left = 60
# message_text = self.long_strings["Update"].format(
# message_text[:-3], ceil(updates_left/6)
# )
# old_message = await ctx.send(message_text)
# while ((not all_downloaded) and updates_left > 0):
# await asyncio.sleep(10)
# updates_left -= 1
# message_text, all_downloaded = await (
# self.__generate_download_list(*parameters)
# )
# message_text = self.long_strings["Update"].format(
# message_text[:-3],
# ceil(updates_left/6)
# )
# await old_message.edit(content = message_text)
# message_text, all_downloaded = await (
# self.__generate_download_list(*parameters)
# )
# if message_text.startswith("```"):
# if all_downloaded:
# self.bot.log("All torrents are downloaded")
# else:
# message_text = self.long_strings["No updates"].format(
# message_text[:-3]
# )
# self.bot.log("The message updated 20 times")
# await old_message.edit(content = message_text)

View File

@ -6,7 +6,7 @@ from pymongo import MongoClient # Used for database management
from gwendolyn.utils import log from gwendolyn.utils import log
from gwendolyn.exceptions import NoToken, CannotConnectToService from gwendolyn.exceptions import NoToken, CannotConnectToService
from gwendolyn.funcs import Other, BetterNetflix, Sonarr, Radarr, TMDb from gwendolyn.funcs import Other, BetterNetflix, Sonarr, Radarr, TMDb, QBittorrent
class Gwendolyn(Client): class Gwendolyn(Client):
def __init__(self, testing: bool = True): def __init__(self, testing: bool = True):
@ -55,10 +55,11 @@ class Gwendolyn(Client):
def _add_functions(self): def _add_functions(self):
self.other = Other(self) self.other = Other(self)
self.better_netflix = BetterNetflix( self.better_netflix = BetterNetflix(
Radarr(getenv("RADARR_IP"),getenv("RADARR_PORT"),getenv("RADARR_API_KEY")), self,
Sonarr(getenv("SONARR_IP"),getenv("SONARR_PORT"),getenv("SONARR_API_KEY")), radarr=Radarr(getenv("RADARR_IP"),getenv("RADARR_PORT"),getenv("RADARR_API_KEY")),
TMDb(getenv("TMDB_API_ACCESS_TOKEN")), sonarr=Sonarr(getenv("SONARR_IP"),getenv("SONARR_PORT"),getenv("SONARR_API_KEY")),
self tmdb=TMDb(getenv("TMDB_API_ACCESS_TOKEN")),
qbittorrent=QBittorrent(getenv("QBITTORRENT_IP"),getenv("QBITTORRENT_PORT"),getenv("QBITTORRENT_USERNAME"),getenv("QBITTORRENT_PASSWORD"))
) )
def _add_extensions(self): def _add_extensions(self):