414 lines
18 KiB
Python
414 lines
18 KiB
Python
import requests, imdb, discord, json, math, time, asyncio
|
||
|
||
class BedreNetflix():
|
||
def __init__(self,bot):
|
||
self.bot = bot
|
||
ip = ["localhost", "192.168.0.40"][self.bot.options["testing"]]
|
||
|
||
self.radarrURL = "http://"+ip+":7878/api/v3/"
|
||
self.sonarrURL = "http://"+ip+":8989/api/"
|
||
self.qbittorrentURL = "http://"+ip+":8080/api/v2/"
|
||
self.moviePath = "/media/plex/Server/movies/"
|
||
self.showPath = "/media/plex/Server/Shows/"
|
||
|
||
#Returns a list of no more than 5 options when user requests a movie
|
||
async def requestMovie(self, ctx, movieName):
|
||
await self.bot.defer(ctx)
|
||
|
||
self.bot.log("Searching for "+movieName)
|
||
movieList = imdb.IMDb().search_movie(movieName)
|
||
movies = []
|
||
for movie in movieList:
|
||
if movie["kind"] == "movie":
|
||
movies.append(movie)
|
||
if len(movies) > 5:
|
||
movies = movies[:5]
|
||
|
||
if len(movies) == 1:
|
||
messageTitle = "**Is it this movie?**"
|
||
else:
|
||
messageTitle = "**Is it any of these movies?**"
|
||
|
||
messageText = ""
|
||
imdb_ids = []
|
||
|
||
for x, movie in enumerate(movies):
|
||
try:
|
||
messageText += "\n"+str(x+1)+") "+movie["title"]+" ("+str(movie["year"])+")"
|
||
except:
|
||
try:
|
||
messageText += "\n"+str(x+1)+") "+movie["title"]
|
||
except:
|
||
messageText += "Error"
|
||
imdb_ids.append(movie.movieID)
|
||
|
||
self.bot.log("Returning a list of "+str(len(movies))+" possible movies: "+str(imdb_ids))
|
||
|
||
em = discord.Embed(title=messageTitle,description=messageText,colour=0x00FF00)
|
||
|
||
message = await ctx.send(embed=em)
|
||
|
||
messageData = {"message_id":message.id,"imdb_ids":imdb_ids}
|
||
|
||
with open("gwendolyn/resources/bedre_netflix/old_message"+str(ctx.channel.id),"w") as f:
|
||
json.dump(messageData,f)
|
||
|
||
if len(movies) == 1:
|
||
await message.add_reaction("✔️")
|
||
else:
|
||
for x in range(len(movies)):
|
||
await message.add_reaction(["1️⃣","2️⃣","3️⃣","4️⃣","5️⃣"][x])
|
||
|
||
await message.add_reaction("❌")
|
||
|
||
message = await ctx.channel.fetch_message(message.id)
|
||
if message.content != "" and not isinstance(ctx.channel, discord.DMChannel):
|
||
await message.clear_reactions()
|
||
|
||
#Adds the requested movie to Bedre Netflix
|
||
async def add_movie(self, message, imdbId, editMessage = True):
|
||
if imdbId == None:
|
||
self.bot.log("Did not find what the user was searching for")
|
||
if editMessage:
|
||
await message.edit(embed = None, content = "Try searching for the IMDB id")
|
||
else:
|
||
await message.channel.send("Try searching for the IMDB id")
|
||
else:
|
||
self.bot.log("Trying to add movie "+str(imdbId))
|
||
apiKey = self.bot.credentials["radarr_key"]
|
||
response = requests.get(self.radarrURL+"movie/lookup/imdb?imdbId=tt"+imdbId+"&apiKey="+apiKey)
|
||
lookupData = response.json()
|
||
postData = {"qualityProfileId": 1,
|
||
"rootFolderPath" : self.moviePath,
|
||
"monitored" : True,
|
||
"addOptions": {"searchForMovie": True}}
|
||
for key in ["tmdbId","title","titleSlug","images","year"]:
|
||
postData.update({key : lookupData[key]})
|
||
|
||
r = requests.post(url= self.radarrURL+"movie?apikey="+apiKey,json = postData)
|
||
|
||
if r.status_code == 201:
|
||
if editMessage:
|
||
await message.edit(embed = None, content = postData["title"]+" successfully added to Bedre Netflix")
|
||
else:
|
||
await message.channel.send(postData["title"]+" successfully added to Bedre Netflix")
|
||
|
||
self.bot.log("Added "+postData["title"]+" to Bedre Netflix")
|
||
elif r.status_code == 400:
|
||
text = f"{postData['title']} is either already on Bedre Netflix, downloading, or not available"
|
||
if editMessage:
|
||
await message.edit(embed = None, content = text)
|
||
else:
|
||
await message.channel.send(text)
|
||
else:
|
||
if editMessage:
|
||
await message.edit(embed = None, content = "Something went wrong")
|
||
else:
|
||
await message.channel.send("Something went wrong")
|
||
self.bot.log(str(r.status_code)+" "+r.reason)
|
||
|
||
#Returns a list of no more than 5 options when user requests a show
|
||
async def requestShow(self, ctx, showName):
|
||
await self.bot.defer(ctx)
|
||
|
||
self.bot.log("Searching for "+showName)
|
||
movies = imdb.IMDb().search_movie(showName) #Replace with tvdb
|
||
shows = []
|
||
for movie in movies:
|
||
if movie["kind"] in ["tv series","tv miniseries"]:
|
||
shows.append(movie)
|
||
if len(shows) > 5:
|
||
shows = shows[:5]
|
||
|
||
if len(shows) == 1:
|
||
messageTitle = "**Is it this show?**"
|
||
else:
|
||
messageTitle = "**Is it any of these shows?**"
|
||
|
||
messageText = ""
|
||
imdb_names = []
|
||
|
||
for x, show in enumerate(shows):
|
||
try:
|
||
messageText += "\n"+str(x+1)+") "+show["title"]+" ("+str(show["year"])+")"
|
||
except:
|
||
try:
|
||
messageText += "\n"+str(x+1)+") "+show["title"]
|
||
except:
|
||
messageText += "Error"
|
||
imdb_names.append(show["title"])
|
||
|
||
self.bot.log("Returning a list of "+str(len(shows))+" possible shows: "+str(imdb_names))
|
||
|
||
em = discord.Embed(title=messageTitle,description=messageText,colour=0x00FF00)
|
||
|
||
message = await ctx.send(embed=em)
|
||
|
||
messageData = {"message_id":message.id,"imdb_names":imdb_names}
|
||
|
||
with open("gwendolyn/resources/bedre_netflix/old_message"+str(ctx.channel.id),"w") as f:
|
||
json.dump(messageData,f)
|
||
|
||
if len(shows) == 1:
|
||
await message.add_reaction("✔️")
|
||
else:
|
||
for x in range(len(shows)):
|
||
await message.add_reaction(["1️⃣","2️⃣","3️⃣","4️⃣","5️⃣"][x])
|
||
|
||
await message.add_reaction("❌")
|
||
|
||
message = await ctx.channel.fetch_message(message.id)
|
||
if message.content != "" and not isinstance(ctx.channel, discord.DMChannel):
|
||
await message.clear_reactions()
|
||
|
||
#Adds the requested show to Bedre Netflix
|
||
async def add_show(self, message, imdb_name):
|
||
if imdb_name == None:
|
||
self.bot.log("Did not find what the user was searching for")
|
||
await message.edit(embed = None, content = "Try searching for the IMDB id")
|
||
else:
|
||
self.bot.log("Trying to add show "+str(imdb_name))
|
||
apiKey = self.bot.credentials["sonarr_key"]
|
||
response = requests.get(self.sonarrURL+"series/lookup?term="+imdb_name.replace(" ","%20")+"&apiKey="+apiKey)
|
||
lookupData = response.json()[0]
|
||
postData = {"ProfileId" : 1,
|
||
"rootFolderPath" : self.showPath,
|
||
"monitored" : True,
|
||
"addOptions" : {"searchForMissingEpisodes" : True}}
|
||
for key in ["tvdbId","title","titleSlug","images","seasons"]:
|
||
postData.update({key : lookupData[key]})
|
||
|
||
r = requests.post(url= self.sonarrURL+"series?apikey="+apiKey,json = postData)
|
||
|
||
if r.status_code == 201:
|
||
await message.edit(embed = None, content = postData["title"]+" successfully added to Bedre Netflix")
|
||
self.bot.log("Added a "+postData["title"]+" to Bedre Netflix")
|
||
elif r.status_code == 400:
|
||
text = f"{postData['title']} is either already on Bedre Netflix, downloading, or not available"
|
||
await message.edit(embed = None, content = text)
|
||
else:
|
||
await message.edit(embed = None, content = "Something went wrong")
|
||
self.bot.log(str(r.status_code)+" "+r.reason)
|
||
|
||
#Generates a list of all torrents and returns formatted list and whether all torrents are downloaded
|
||
async def genDownloadList(self, showDM, showMovies, showShows, episodes):
|
||
self.bot.log("Generating torrent list")
|
||
titleWidth = 100
|
||
message = []
|
||
allDownloaded = True
|
||
|
||
if showDM:
|
||
message.append("")
|
||
DMSectionTitle = "*Torrent Downloads*"
|
||
DMSectionTitleLine = "-"*((titleWidth-len(DMSectionTitle))//2)
|
||
message.append(DMSectionTitleLine+DMSectionTitle+DMSectionTitleLine)
|
||
response = requests.get(self.qbittorrentURL+"torrents/info")
|
||
torrentList = response.json()
|
||
|
||
if len(torrentList) > 0:
|
||
for torrent in torrentList:
|
||
torrentName = torrent["name"]
|
||
if len(torrentName) > 30:
|
||
if torrentName[26] == " ":
|
||
torrentName = torrentName[:26]+"...."
|
||
else:
|
||
torrentName = torrentName[:27]+"..."
|
||
while len(torrentName) < 30:
|
||
torrentName += " "
|
||
|
||
if torrent["size"] == 0:
|
||
downloadedRatio = 0
|
||
elif torrent["amount_left"] == 0:
|
||
downloadedRatio = 1
|
||
else:
|
||
downloadedRatio = min(torrent["downloaded"]/torrent["size"],1)
|
||
progressBar = "|"+("█"*math.floor(downloadedRatio*20))
|
||
while len(progressBar) < 21:
|
||
progressBar += " "
|
||
|
||
progressBar += "| "+str(math.floor(downloadedRatio*100))+"%"
|
||
|
||
while len(progressBar) < 27:
|
||
progressBar += " "
|
||
|
||
etaInSeconds = torrent["eta"]
|
||
|
||
if etaInSeconds >= 8640000:
|
||
eta = "∞"
|
||
else:
|
||
eta = ""
|
||
if etaInSeconds >= 86400:
|
||
eta += str(math.floor(etaInSeconds/86400))+"d "
|
||
if etaInSeconds >= 3600:
|
||
eta += str(math.floor((etaInSeconds%86400)/3600))+"h "
|
||
if etaInSeconds >= 60:
|
||
eta += str(math.floor((etaInSeconds%3600)/60))+"m "
|
||
|
||
eta += str(etaInSeconds%60)+"s"
|
||
|
||
torrentInfo = torrentName+" "+progressBar+" (Eta: "+eta+")"
|
||
|
||
if torrent["state"] == "stalledDL":
|
||
torrentInfo += " (Stalled)"
|
||
|
||
if not (downloadedRatio == 1 and torrent["last_activity"] < time.time()-7200):
|
||
message.append(torrentInfo)
|
||
|
||
if downloadedRatio < 1 and torrent["state"] != "stalledDL":
|
||
allDownloaded = False
|
||
else:
|
||
message.append("No torrents currently downloading")
|
||
|
||
if showMovies:
|
||
message.append("")
|
||
movieSectionTitle = "*Missing movies not downloading*"
|
||
movieSectionTitleLine = "-"*((titleWidth-len(movieSectionTitle))//2)
|
||
message.append(movieSectionTitleLine+movieSectionTitle+movieSectionTitleLine)
|
||
movieList = requests.get(self.radarrURL+"movie?apiKey="+self.bot.credentials["radarr_key"]).json()
|
||
movieQueue = requests.get(self.radarrURL+"queue?apiKey="+self.bot.credentials["radarr_key"]).json()
|
||
movieQueueIDs = []
|
||
|
||
for queueItem in movieQueue["records"]:
|
||
movieQueueIDs.append(queueItem["movieId"])
|
||
|
||
for movie in movieList:
|
||
if not movie["hasFile"]:
|
||
if movie["id"] not in movieQueueIDs:
|
||
movieName = movie["title"]
|
||
if len(movieName) > 40:
|
||
if movieName[36] == " ":
|
||
movieName = movieName[:36]+"...."
|
||
else:
|
||
movieName = movieName[:37]+"..."
|
||
|
||
while len(movieName) < 41:
|
||
movieName += " "
|
||
|
||
if movie["monitored"]:
|
||
movieInfo = movieName+"Could not find a torrent"
|
||
else:
|
||
movieInfo = movieName+"No torrent exists. Likely because the movie is not yet released on DVD"
|
||
|
||
message.append(movieInfo)
|
||
|
||
if showShows:
|
||
message.append("")
|
||
showSectionTitle = "*Missing shows not downloading*"
|
||
showSectionTitleLine = "-"*((titleWidth-len(showSectionTitle))//2)
|
||
message.append(showSectionTitleLine+showSectionTitle+showSectionTitleLine)
|
||
|
||
showList = requests.get(self.sonarrURL+"series?apiKey="+self.bot.credentials["sonarr_key"]).json()
|
||
|
||
for show in showList:
|
||
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:
|
||
missingEpisodes = sum(i["statistics"]["totalEpisodeCount"] - i["statistics"]["episodeCount"] for i in seasons)
|
||
message.append(show["title"] + f" ({missingEpisodes} episodes)")
|
||
|
||
message.append("-"*titleWidth)
|
||
|
||
messageText = "```"+"\n".join(message[1:])+"```"
|
||
if messageText == "``````":
|
||
messageText = "There are no torrents downloading right. If the torrent you're looking for was added more than 24 hours ago, it might already be on Bedre Netflix."
|
||
return messageText, allDownloaded
|
||
|
||
async def downloading(self, ctx, content):
|
||
async def SendLongMessage(ctx,messageText):
|
||
if len(messageText) <= 1994:
|
||
await ctx.send("```"+messageText+"```")
|
||
else:
|
||
cutOffIndex = messageText[:1994].rfind("\n")
|
||
await ctx.send("```"+messageText[:cutOffIndex]+"```")
|
||
await SendLongMessage(ctx,messageText[cutOffIndex+1:])
|
||
|
||
await self.bot.defer(ctx)
|
||
|
||
# showDM, showMovies, showShows, episodes
|
||
params = [False, False, False, False]
|
||
showDMArgs = ["d", "dm", "downloading", "downloadmanager"]
|
||
showMoviesArgs = ["m", "movies"]
|
||
showShowsArgs = ["s", "shows", "series"]
|
||
episodesArgs = ["e", "episodes"]
|
||
argList = [showDMArgs, showMoviesArgs, showShowsArgs, episodesArgs]
|
||
inputArgs = []
|
||
validArguments = True
|
||
|
||
while content != "" and validArguments:
|
||
if content[0] == " ":
|
||
content = content[1:]
|
||
elif content[0] == "-":
|
||
if content[1] == "-":
|
||
argStart = 2
|
||
if " " in content:
|
||
argStop = content.find(" ")
|
||
else:
|
||
argStop = None
|
||
else:
|
||
argStart = 1
|
||
argStop = 2
|
||
|
||
inputArgs.append(content[argStart:argStop])
|
||
if argStop is None:
|
||
content = ""
|
||
else:
|
||
content = content[argStop:]
|
||
else:
|
||
validArguments = False
|
||
|
||
if validArguments:
|
||
for x, argAliases in enumerate(argList):
|
||
argInInput = [i in inputArgs for i in argAliases]
|
||
if any(argInInput):
|
||
inputArgs.remove(argAliases[argInInput.index(True)])
|
||
params[x] = True
|
||
|
||
if len(inputArgs) != 0 or (params[2] == False and params[3] == True):
|
||
validArguments = False
|
||
|
||
showAnything = any(i for i in params)
|
||
if validArguments and showAnything:
|
||
messageText, allDownloaded = await self.genDownloadList(*params)
|
||
if messageText.startswith("```"):
|
||
|
||
if len(messageText) <= 2000:
|
||
if not allDownloaded:
|
||
updatesLeft = 60
|
||
messageText = messageText[:-3]+"\nThis message will update every 10 seconds for "+str(math.ceil(updatesLeft/6))+" more minutes\n```"
|
||
old_message = await ctx.send(messageText)
|
||
|
||
while ((not allDownloaded) and updatesLeft > 0):
|
||
await asyncio.sleep(10)
|
||
updatesLeft -= 1
|
||
messageText, allDownloaded = await self.genDownloadList(*params)
|
||
messageText = messageText[:-3]+"\nThis message will update every 10 seconds for "+str(math.ceil(updatesLeft/6))+" more minutes\n```"
|
||
await old_message.edit(content = messageText)
|
||
|
||
messageText, allDownloaded = await self.genDownloadList(*params)
|
||
|
||
if messageText.startswith("```"):
|
||
if allDownloaded:
|
||
self.bot.log("All torrents are downloaded")
|
||
else:
|
||
messageText = messageText[:-3]+"\nThis message will not update anymore\n```"
|
||
self.bot.log("The message updated 20 times")
|
||
|
||
await old_message.edit(content = messageText)
|
||
|
||
else:
|
||
await ctx.send(messageText)
|
||
else:
|
||
messageText = messageText[3:-3]
|
||
await SendLongMessage(ctx,messageText)
|
||
else:
|
||
await ctx.send(messageText)
|
||
else:
|
||
await ctx.send("Invalid or repeated parameters. Use '/help downloading' to see valid parameters.")
|
||
|