362 lines
15 KiB
Python
Executable File
362 lines
15 KiB
Python
Executable File
import cherrypy
|
|
import requests
|
|
import os
|
|
import math
|
|
import pycountry
|
|
import json
|
|
import re
|
|
import threading
|
|
import time
|
|
import urllib.request
|
|
import xml.etree.ElementTree as ET
|
|
from PyMovieDb import IMDB
|
|
import subprocess
|
|
import yt_dlp
|
|
from downloadScript import download
|
|
imdb = IMDB()
|
|
top_movies = []
|
|
recent_movies = []
|
|
|
|
priorities = [
|
|
{"type": "web", "video_codec": "x264"},
|
|
{"type": "web", "video_codec": "x265"},
|
|
{"type": "bluray", "video_codec": "x264"},
|
|
{"type": "bluray", "video_codec": "x265"},
|
|
]
|
|
|
|
def createMoviePoster(json_format):
|
|
movie_data = json_format
|
|
movie_readout = ""
|
|
plex_movies = json.load(open('/mnt/moviehub/plex_movies.json', 'r'))
|
|
for movie in movie_data:
|
|
res720 = {}
|
|
res1080 = {}
|
|
resOther = {}
|
|
availableRes = []
|
|
downButton = ""
|
|
plex_server_resolution = []
|
|
title = movie["title_english"]
|
|
rating = movie["rating"]
|
|
year = movie["year"]
|
|
imdb = movie["imdb_code"]
|
|
language = pycountry.languages.get(alpha_2=movie['language'])
|
|
language = language.name if language is not None else language
|
|
poster_url = movie["large_cover_image"]
|
|
for movie_match in plex_movies["movies"]:
|
|
if str(movie_match["video"]["title"].lower()) == str(movie["title"].lower()) and abs(int(movie_match["video"]["year"]) - year) <= 1:
|
|
plex_server_resolution.append(movie_match["media"]["videoResolution"]+"p")
|
|
for priority in priorities:
|
|
for resolution in movie["torrents"]:
|
|
if (resolution["quality"] == "720p" and
|
|
resolution["type"] == priority["type"] and
|
|
resolution["video_codec"] == priority["video_codec"]):
|
|
res720["res"] = resolution["quality"]
|
|
res720["url"] = resolution["url"]
|
|
break
|
|
for priority in priorities:
|
|
for resolution in movie["torrents"]:
|
|
if (resolution["quality"] == "1080p" and
|
|
resolution["type"] == priority["type"] and
|
|
resolution["video_codec"] == priority["video_codec"]):
|
|
res1080["res"] = resolution["quality"]
|
|
res1080["url"] = resolution["url"]
|
|
break
|
|
if len(res720) == 0 and len(res1080) == 0:
|
|
for resolution in movie["torrents"]:
|
|
resOther["res"] = resolution["quality"]
|
|
resOther["url"] = resolution["url"]
|
|
break
|
|
availableRes.append(res720)
|
|
availableRes.append(res1080)
|
|
availableRes.append(resOther)
|
|
if availableRes:
|
|
for resolution in availableRes:
|
|
res = resolution.get('res')
|
|
url = resolution.get('url')
|
|
if res is not None and url is not None:
|
|
extra_class = "resolution_exists" if res in plex_server_resolution else "download-link resolution_available"
|
|
downButton += f"""<a class="resolution {extra_class}" data-filename="{title} ({year}) ({res}).torrent" data-url="{url}">{res}</a>\n"""
|
|
# Create the movie readout HTML for each movie
|
|
movie_readout += f"""
|
|
<div class="movie">
|
|
<div class="details">
|
|
<b class="chip rating">{rating}<i class='bx bxs-star'></i></b>
|
|
<b class="chip">{year}</b>
|
|
<b class="chip">{language}</b>
|
|
</div>
|
|
<a href="https://www.imdb.com/title/{imdb}" target="_blank">
|
|
<img src="{poster_url}" onerror="this.src='images/large-cover.jpg'" alt="{title} Poster">
|
|
</a>
|
|
<div class="description">
|
|
<h4>{title}</h4>
|
|
</div>
|
|
<div class="resDownload">
|
|
{downButton}
|
|
</div>
|
|
</div>"""
|
|
return movie_readout
|
|
|
|
def navBar(json=None):
|
|
movieCount =''
|
|
movieCount = '<b id="matches">'+str(json['data']['movie_count'])+' Matches</b>' if json != None else ""
|
|
return open('html/navbar.html').read().format(movieCount=movieCount)
|
|
|
|
def pageButton(json, query_term):
|
|
pages = ''
|
|
movieCount = json['data']['movie_count']
|
|
resultLimit = json['data']['limit']
|
|
activePage = json['data']['page_number']
|
|
pageLimit = math.ceil(movieCount / resultLimit)
|
|
|
|
# Calculate the range of pages to display (5 pages)
|
|
start_page = max(1, activePage - 2)
|
|
end_page = min(pageLimit, start_page + 4)
|
|
if end_page - start_page < 4: # Ensure we always show 5 pages if possible
|
|
start_page = max(1, end_page - 4)
|
|
|
|
for number in range(start_page, end_page + 1):
|
|
if number == activePage:
|
|
pages += "<input type='hidden' id='activePage' data-page='" + str(number) + "'>"
|
|
pages += "<a class='pageNumber activePage' data-page='" + str(number) + "'>" + str(number) + "</a>"
|
|
else:
|
|
pages += "<a class='pageNumber' data-page='" + str(number) + "'>" + str(number) + "</a>"
|
|
|
|
if pageLimit == 1:
|
|
return ''
|
|
else:
|
|
# Load your HTML template from the file 'pagebutton.html' and format it with the generated pages.
|
|
return open('html/pagebutton.html').read().format(pages=pages, query_term=query_term, pageLimit=pageLimit)
|
|
|
|
def eztv_json():
|
|
return 'pass'
|
|
|
|
class fetch():
|
|
|
|
def imdb_data(query_term=None):
|
|
json_data = imdb.search(query_term, tv=True) if query_term is not None else imdb.popular_movies(genre=None, sort_by=None)
|
|
data = json.loads(json_data)
|
|
results_data = data.get('results', [])
|
|
return results_data
|
|
|
|
def yts_movies(parameter=None, movie=None):
|
|
movie_id = movie['id'] if movie is not None else ""
|
|
url = f'https://yts.mx/api/v2/list_movies.json{parameter}{movie_id}'
|
|
data = requests.get(url).json()
|
|
if 'data' in data and data['data'].get('movie_count', 0) > 0:
|
|
return data['data']['movies'][0] if movie is not None else data["data"]["movies"]
|
|
else:
|
|
return None
|
|
|
|
def plex_data():
|
|
url = f'http://10.0.0.105:32400/library/sections/1/all'
|
|
response = requests.get(url)
|
|
if response.status_code == 200:
|
|
plex_xml_data = response.text
|
|
root = ET.fromstring(plex_xml_data)
|
|
movies = []
|
|
for video_elem in root.findall('.//Video'):
|
|
video_keys = {}
|
|
media_keys = {}
|
|
for key, value in video_elem.items():
|
|
video_keys[key] = value
|
|
media_element = video_elem.find(".//Media")
|
|
if media_element is not None:
|
|
media_attributes = media_element.attrib
|
|
for key, value in media_attributes.items():
|
|
media_keys[key] = value
|
|
movie_info = {
|
|
'video': video_keys,
|
|
'media': media_keys
|
|
}
|
|
movies.append(movie_info)
|
|
movie_dict = {'movies': movies}
|
|
json_data = json.dumps(movie_dict, indent=2)
|
|
with open('/mnt/moviehub/plex_movies.json', 'w') as json_file:
|
|
json_file.write(json_data)
|
|
else:
|
|
print(f'Error: {response.status_code}')
|
|
|
|
|
|
class background():
|
|
|
|
def update_top_movies():
|
|
while True:
|
|
global top_movies # Access the global variable
|
|
# Your code to fetch and update the top 18 movies
|
|
new_top_movies = []
|
|
for movie in fetch.imdb_data():
|
|
if len(new_top_movies) >= 12:
|
|
break
|
|
movie_data = fetch.yts_movies('?query_term=', movie)
|
|
if movie_data is not None:
|
|
new_top_movies.append(movie_data)
|
|
top_movies = new_top_movies # Update the global variable
|
|
|
|
global recent_movies
|
|
recent_movies = fetch.yts_movies('?sort_by=date_added&limit=18')
|
|
|
|
fetch.plex_data()
|
|
time.sleep(3600)
|
|
|
|
background_thread = threading.Thread(target=background.update_top_movies)
|
|
background_thread.daemon = True # Set the thread as a daemon to exit when the main program exits
|
|
background_thread.start()
|
|
|
|
class MovieApp:
|
|
@cherrypy.expose
|
|
def index(self):
|
|
latestMoviesTiles = createMoviePoster(top_movies)
|
|
recentMoviesTiles = createMoviePoster(recent_movies)
|
|
return open('html/index.html').read().format(latestMoviesTiles=latestMoviesTiles,recentMoviesTiles=recentMoviesTiles,navBar=navBar())
|
|
|
|
@cherrypy.expose
|
|
def movie(self, query_term='', page='1', genre=None, minimum_rating=None):
|
|
api_url = f"https://yts.mx/api/v2/list_movies.json?limit=30&query_term={query_term}&page={page}"
|
|
api_url += f"&genre={genre}" if genre is not None else ""
|
|
api_url += f"&minimum_rating={minimum_rating}" if minimum_rating is not None else ""
|
|
response = requests.get(api_url)
|
|
if response.status_code == 200 and response.json()['status_message'] == "Query was successful" and response.json()['data']['movie_count'] > 0:
|
|
movie_tiles = createMoviePoster(response.json()["data"]["movies"])
|
|
pageBar = pageButton(response.json(), query_term)
|
|
else:
|
|
movie_tiles = "<h1>No matches found<h1>"
|
|
pageBar = ""
|
|
return open('html/movie.html').read().format(movie_tiles=movie_tiles,navBar=navBar(response.json()),pageButton=pageBar)
|
|
|
|
@cherrypy.expose
|
|
def tv(self, query_term=''):
|
|
poster_x, poster_y = '500', '750'
|
|
results_data = fetch.imdb_data(query_term)
|
|
html = ''
|
|
regex_pattern = r'_V1_QL\d+_([A-Z]+)(\d+)_CR(\d+),(\d+),(\d+),(\d+)_'
|
|
for result in results_data:
|
|
extract = re.search(regex_pattern, result['poster'])
|
|
item_id = result.get('id', '').replace('tt', '')
|
|
zoom = int(extract.group(4))
|
|
move_x = int(extract.group(3)) if extract else 0
|
|
move_x = move_x * 9 if move_x != 0 else move_x + 6.5
|
|
upscale_num = '510' if extract.group(2) == extract.group(5) else poster_y
|
|
resolution = f"""_V1_QL100_{extract.group(1)}{upscale_num}_CR{str(move_x)},{str(zoom)},{poster_x},{poster_y}_"""
|
|
poster_url = re.sub(r'_V1[\w_,]+', resolution, result['poster'])
|
|
title = result['name']
|
|
html += f"""<img style="width: 200px;" src="{poster_url}" id='{item_id}' onerror="this.src='images/large-cover.jpg'" alt="{title} Poster">"""
|
|
return html
|
|
|
|
@cherrypy.expose
|
|
def download(self, url, filename):
|
|
save_path = os.path.join('/mnt/data/deluge/watch/movies', filename)
|
|
title = filename.replace('.torrent','')
|
|
response = requests.get(url)
|
|
if response.status_code == 200:
|
|
with open(save_path, 'wb') as file:
|
|
file.write(response.content)
|
|
return(f"Download has been set for '{title}'.")
|
|
else:
|
|
print(f"Failed to download the file from '{url}'.")
|
|
return(f"Failed to send download request. Contact your system administrator.")
|
|
|
|
@cherrypy.expose
|
|
def anime(self):
|
|
rss = open('/mnt/deluge/config/yarss2.conf').read()
|
|
config = {} # Create an empty dictionary to store configuration
|
|
|
|
with open('/mnt/deluge/config/yarss2.conf', 'r') as file:
|
|
lines = file.readlines()
|
|
|
|
current_section = None
|
|
for line in lines:
|
|
line = line.strip()
|
|
if line.startswith("{"):
|
|
current_section = {}
|
|
elif line.startswith("}"):
|
|
if current_section:
|
|
config.update(current_section)
|
|
current_section = None
|
|
elif current_section:
|
|
key, value = line.split(":")
|
|
current_section[key.strip()] = value.strip()
|
|
|
|
# The 'config' dictionary now contains your configuration data
|
|
print(config)
|
|
return config
|
|
|
|
@cherrypy.expose
|
|
def downloader(self):
|
|
return open('html/ydl.html').read().format(navBar=navBar())
|
|
|
|
@cherrypy.expose
|
|
def dropout(self):
|
|
return open('html/dropout.html').read().format(navBar=navBar())
|
|
|
|
@cherrypy.expose
|
|
def dropoutDownloader(self,show,season,episode=None):
|
|
download.dropout(show,season,episode)
|
|
|
|
@cherrypy.expose
|
|
def ydl(self, url, location):
|
|
dl_ops = {'paths': {'temp': '/mnt/data/downloader/temp', 'home': location}, 'outtmpl': '%(uploader)s/%(title)s.%(ext)s'}
|
|
if location == "/mnt/plex/music/podcast":
|
|
dl_ops['format'] = 'bestaudio/best[ext=mp3]'
|
|
dl_ops['postprocessors'] = [{
|
|
'key': 'FFmpegExtractAudio',
|
|
'preferredcodec': 'mp3',
|
|
'preferredquality': '192',
|
|
}, {
|
|
'key': 'FFmpegMetadata',
|
|
'add_metadata': True,
|
|
}]
|
|
elif location == "/mnt/plex/music/asmr":
|
|
dl_ops['format'] = 'bestaudio/best[ext=mp3]'
|
|
dl_ops['postprocessors'] = [{
|
|
'key': 'FFmpegExtractAudio',
|
|
'preferredcodec': 'mp3',
|
|
'preferredquality': '192',
|
|
}, {
|
|
'key': 'FFmpegMetadata',
|
|
'add_metadata': True,
|
|
}]
|
|
else:
|
|
dl_ops['format'] = 'bestvideo[ext=mp4]+bestaudio[ext=m4a]/best'
|
|
|
|
with yt_dlp.YoutubeDL(dl_ops) as ydl:
|
|
ydl.download(url)
|
|
|
|
# Extracting video information
|
|
video_info = ydl.extract_info(url, download=False)
|
|
thumbnail_url = video_info.get('thumbnail')
|
|
|
|
# Download the thumbnail image
|
|
if thumbnail_url:
|
|
try:
|
|
thumbnail_filename = os.path.join(location, f"{video_info['id']}.jpg")
|
|
with open(thumbnail_filename, 'wb') as thumbnail_file:
|
|
thumbnail_file.write(requests.get(thumbnail_url).content)
|
|
print("Downloaded MP4 and downloaded thumbnail successfully!")
|
|
except Exception as e:
|
|
print(f"Error downloading thumbnail: {str(e)}")
|
|
else:
|
|
print("Downloaded MP4 but no thumbnail found.")
|
|
|
|
@cherrypy.expose
|
|
def plex(self):
|
|
fetch.plex_data()
|
|
|
|
@cherrypy.expose
|
|
def yts(self):
|
|
background.update_top_movies()
|
|
|
|
@cherrypy.expose
|
|
def imdb(self):
|
|
return fetch.imdb_data()
|
|
|
|
|
|
cherrypy.config.update({
|
|
'tools.staticdir.on': True,
|
|
'tools.staticdir.dir': os.getcwd(),
|
|
'server.socket_host': '0.0.0.0',
|
|
'server.socket_port': 8008,
|
|
'favicon.ico': 'favicon.ico',
|
|
})
|
|
if __name__ == '__main__':
|
|
cherrypy.quickstart(MovieApp())
|