merged new trailer class and small tvsort fixes
This commit is contained in:
commit
f1307729ce
|
@ -20,7 +20,7 @@ def get_pending_all(config):
|
|||
# call subfunction to collect pending
|
||||
pending_movie = moviesort.MovieHandler().pending
|
||||
pending_tv = tvsort.TvHandler().pending
|
||||
pending_trailer = len(trailers.get_pending(config))
|
||||
pending_trailer = len(trailers.TrailerHandler().pending)
|
||||
pending_movie_fix = len(id_fix.get_pending(config))
|
||||
pending_total = pending_movie + pending_tv + pending_trailer + pending_movie_fix
|
||||
# build dict
|
||||
|
@ -75,7 +75,7 @@ def sel_handler(menu_item, config):
|
|||
moviesort.main()
|
||||
tvsort.main()
|
||||
db_export.main(config)
|
||||
trailers.main(config)
|
||||
trailers.main()
|
||||
id_fix.main(config)
|
||||
elif menu_item == 'Movies':
|
||||
moviesort.main()
|
||||
|
@ -84,7 +84,7 @@ def sel_handler(menu_item, config):
|
|||
elif menu_item == 'DB export':
|
||||
db_export.main(config)
|
||||
elif menu_item == 'Trailer download':
|
||||
trailers.main(config)
|
||||
trailers.main()
|
||||
elif menu_item == 'Fix Movie Names':
|
||||
id_fix.main(config)
|
||||
|
||||
|
|
298
src/trailers.py
298
src/trailers.py
|
@ -2,185 +2,165 @@
|
|||
|
||||
import os
|
||||
import re
|
||||
import requests
|
||||
import subprocess
|
||||
import youtube_dl
|
||||
|
||||
from time import sleep
|
||||
|
||||
import requests
|
||||
import youtube_dl
|
||||
|
||||
def incomplete(config):
|
||||
""" search for incomplete downloads and trash them """
|
||||
sortpath = config['media']['sortpath']
|
||||
file_list = os.listdir(sortpath)
|
||||
trashed = False
|
||||
for file_name in file_list:
|
||||
if file_name.endswith('.part') or file_name.endswith('.ytdl'):
|
||||
trashed = True
|
||||
file_path = os.path.join(sortpath, file_name)
|
||||
if os.path.isfile(file_path):
|
||||
subprocess.call(['trash', file_path])
|
||||
if file_list and not trashed:
|
||||
new_trailers = archive(config)
|
||||
print(f'moved {len(new_trailers)} into archive')
|
||||
return trashed
|
||||
from src.config import get_config
|
||||
|
||||
|
||||
def get_local_trailers(config):
|
||||
""" gets a list of existing trailers on filesystem """
|
||||
emby_url = config['emby']['emby_url']
|
||||
emby_api_key = config['emby']['emby_api_key']
|
||||
url = (emby_url + '/Trailers?api_key=' + emby_api_key +
|
||||
'&Recursive=True&Fields=Path,MediaStreams')
|
||||
r = requests.get(url).json()
|
||||
local_trailer_list = []
|
||||
for movie in r['Items']:
|
||||
trailer_name = movie['Name']
|
||||
trailing_pattern = re.compile(r'(.*_)([0-9a-zA-Z-_]{11})(-trailer)$')
|
||||
youtube_id = trailing_pattern.findall(trailer_name)[0][1]
|
||||
movie_name = movie['Path'].split('/')[-2]
|
||||
media_streams = movie['MediaSources'][0]['MediaStreams']
|
||||
video_stream = list(filter(lambda stream: stream['Type'] == 'Video', media_streams))
|
||||
width = video_stream[0]['Width']
|
||||
height = video_stream[0]['Height']
|
||||
trailer_details = {'movie_name': movie_name, 'youtube_id': youtube_id,
|
||||
'width': width, 'height': height}
|
||||
local_trailer_list.append(trailer_details)
|
||||
return local_trailer_list
|
||||
class TrailerHandler():
|
||||
""" holds the trailers """
|
||||
|
||||
CONFIG = get_config()
|
||||
|
||||
def get_remote_trailers(config):
|
||||
""" get a list of available trailers on emby """
|
||||
emby_url = config['emby']['emby_url']
|
||||
emby_api_key = config['emby']['emby_api_key']
|
||||
# remote trailer urls
|
||||
url = (emby_url + '/Items?api_key=' + emby_api_key +
|
||||
'&Recursive=True&Fields=LocalTrailerCount,RemoteTrailers,Path&' +
|
||||
'IncludeItemTypes=Movie')
|
||||
r = requests.get(url).json()
|
||||
remote_trailers_list = []
|
||||
for movie in r['Items']:
|
||||
movie_name = movie['Path'].split('/')[-2]
|
||||
movie_path = '/'.join(movie['Path'].split('/')[-3:-1])
|
||||
local_trailer_count = movie['LocalTrailerCount']
|
||||
remote_trailers = movie['RemoteTrailers']
|
||||
trailer_details = {'movie_name': movie_name, 'movie_path': movie_path,
|
||||
'local_trailer_count': local_trailer_count,
|
||||
'remote_trailers': remote_trailers}
|
||||
remote_trailers_list.append(trailer_details)
|
||||
return remote_trailers_list
|
||||
def __init__(self):
|
||||
self.pending = self.get_pending()
|
||||
|
||||
def get_local_trailers(self):
|
||||
""" gets a list of existing trailers on filesystem """
|
||||
emby_url = self.CONFIG['emby']['emby_url']
|
||||
emby_api_key = self.CONFIG['emby']['emby_api_key']
|
||||
url = (emby_url + '/Trailers?api_key=' + emby_api_key
|
||||
+ '&Recursive=True&Fields=Path')
|
||||
request = requests.get(url).json()
|
||||
local_trailer_list = []
|
||||
for movie in request['Items']:
|
||||
trailer_name = movie['Name']
|
||||
trailing_reg = r'(.*_)([0-9a-zA-Z-_]{11})(-trailer)$'
|
||||
trailing_pattern = re.compile(trailing_reg)
|
||||
youtube_id = trailing_pattern.findall(trailer_name)[0][1]
|
||||
movie_name = movie['Path'].split('/')[-2]
|
||||
trailer_details = {'movie_name': movie_name,
|
||||
'youtube_id': youtube_id}
|
||||
local_trailer_list.append(trailer_details)
|
||||
return local_trailer_list
|
||||
|
||||
def compare_download(local_trailer_list, remote_trailers_list, config):
|
||||
""" figure out which trailers need downloading """
|
||||
log_folder = config['media']['log_folder']
|
||||
# failed before
|
||||
log_file = os.path.join(log_folder, 'trailers')
|
||||
# check if log file exists
|
||||
if not os.path.isfile(log_file):
|
||||
# create empty file
|
||||
open(log_file, 'a').close()
|
||||
failed_ids = []
|
||||
else:
|
||||
with open(log_file, 'r') as f:
|
||||
lines = f.readlines()
|
||||
failed_ids = [i.split()[0] for i in lines]
|
||||
# ids already downloaded
|
||||
local_ids = [i['youtube_id'] for i in local_trailer_list]
|
||||
# find pending
|
||||
pending = []
|
||||
for movie in remote_trailers_list:
|
||||
movie_name = movie['movie_name']
|
||||
for trailer in movie['remote_trailers']:
|
||||
vid_id = trailer['Url'].split('?v=')[1]
|
||||
def get_remote_trailers(self):
|
||||
""" get a list of available trailers on emby """
|
||||
emby_url = self.CONFIG['emby']['emby_url']
|
||||
emby_api_key = self.CONFIG['emby']['emby_api_key']
|
||||
# remote trailer urls
|
||||
url = (emby_url + '/Items?api_key=' + emby_api_key +
|
||||
'&Recursive=True&Fields=RemoteTrailers,Path' +
|
||||
'&IncludeItemTypes=Movie')
|
||||
request = requests.get(url).json()
|
||||
remote_trailers_list = []
|
||||
for movie in request['Items']:
|
||||
movie_name = movie['Path'].split('/')[-2]
|
||||
remote_trailers = movie['RemoteTrailers']
|
||||
for remote_trailer in remote_trailers:
|
||||
url = remote_trailer['Url']
|
||||
youtube_id = url.split('?v=')[1]
|
||||
trailer_details = {'movie_name': movie_name,
|
||||
'youtube_id': youtube_id}
|
||||
remote_trailers_list.append(trailer_details)
|
||||
return remote_trailers_list
|
||||
|
||||
if vid_id not in failed_ids and vid_id not in local_ids:
|
||||
pending.append((vid_id, movie_name))
|
||||
return pending
|
||||
def get_ignore_trailers(self):
|
||||
""" read log file to get list of trailers to ignore """
|
||||
log_folder = self.CONFIG['media']['log_folder']
|
||||
log_file = os.path.join(log_folder, 'trailers')
|
||||
with open(log_file, 'r') as log_file:
|
||||
trailer_lines = log_file.readlines()
|
||||
ignore_trailer_list = []
|
||||
for trailer_line in trailer_lines:
|
||||
youtube_id = trailer_line.split()[0]
|
||||
movie_name = trailer_line.lstrip(youtube_id).strip()
|
||||
trailer_details = {'movie_name': movie_name,
|
||||
'youtube_id': youtube_id}
|
||||
ignore_trailer_list.append(trailer_details)
|
||||
return ignore_trailer_list
|
||||
|
||||
def get_pending(self):
|
||||
""" compare have and pending """
|
||||
remote_trailers_list = self.get_remote_trailers()
|
||||
local_trailer_list = self.get_local_trailers()
|
||||
ignore_trailer_list = self.get_ignore_trailers()
|
||||
# add local and ignore list together
|
||||
have_trailers = ([i['youtube_id'] for i in local_trailer_list]
|
||||
+ [i['youtube_id'] for i in ignore_trailer_list])
|
||||
# add to pending if missing
|
||||
pending = []
|
||||
for remote_trailer in remote_trailers_list:
|
||||
youtube_id = remote_trailer['youtube_id']
|
||||
if youtube_id not in have_trailers:
|
||||
pending.append(remote_trailer)
|
||||
return pending
|
||||
|
||||
def dl_pending(pending, config):
|
||||
""" download pending trailers """
|
||||
sortpath = config['media']['sortpath']
|
||||
log_folder = config['media']['log_folder']
|
||||
ydl_opts = config['ydl_opts']
|
||||
# loop thrugh list
|
||||
downloaded = []
|
||||
for trailer in pending:
|
||||
to_down_id = trailer[0]
|
||||
movie_name = trailer[1]
|
||||
filename = os.path.join(sortpath, movie_name + '_' + to_down_id + '-trailer.mkv')
|
||||
ydl_opts['outtmpl'] = filename
|
||||
# try up to 5 times
|
||||
for i in range(5):
|
||||
try:
|
||||
print(f'[{i}] {to_down_id} {movie_name}')
|
||||
youtube_dl.YoutubeDL(ydl_opts).download(['https://www.youtube.com/watch?v=' + to_down_id])
|
||||
except KeyboardInterrupt:
|
||||
return False
|
||||
except:
|
||||
if i == 4:
|
||||
# giving up
|
||||
log_file = os.path.join(log_folder, 'trailers')
|
||||
with open(log_file, 'a') as f:
|
||||
f.write(f'{to_down_id} {movie_name}\n')
|
||||
break
|
||||
else:
|
||||
def dl_pending(self):
|
||||
""" download pending trailers """
|
||||
sortpath = self.CONFIG['media']['sortpath']
|
||||
log_folder = self.CONFIG['media']['log_folder']
|
||||
ydl_opts = self.CONFIG['ydl_opts']
|
||||
# loop thrugh list
|
||||
trailers_downloaded = []
|
||||
for trailer in self.pending:
|
||||
youtube_id = trailer['youtube_id']
|
||||
movie_name = trailer['movie_name']
|
||||
filename = f'{movie_name}_{youtube_id}-trailer.mkv'
|
||||
filepath = os.path.join(sortpath, filename)
|
||||
ydl_opts['outtmpl'] = filepath
|
||||
# try up to 5 times
|
||||
for i in range(5):
|
||||
try:
|
||||
print(f'[{i}] {youtube_id} {movie_name}')
|
||||
url = 'https://www.youtube.com/watch?v=' + youtube_id
|
||||
youtube_dl.YoutubeDL(ydl_opts).download([url])
|
||||
except KeyboardInterrupt:
|
||||
return False
|
||||
except Exception:
|
||||
if i == 4:
|
||||
# giving up
|
||||
log_file = os.path.join(log_folder, 'trailers')
|
||||
with open(log_file, 'a') as f:
|
||||
f.write(f'{youtube_id} {movie_name}\n')
|
||||
break
|
||||
sleep((i + 1) ** 2)
|
||||
continue
|
||||
else:
|
||||
downloaded.append(to_down_id)
|
||||
break
|
||||
return downloaded
|
||||
else:
|
||||
trailers_downloaded.append(trailer)
|
||||
break
|
||||
return trailers_downloaded
|
||||
|
||||
def archive(self):
|
||||
""" move downloaded trailers to movie archive """
|
||||
sortpath = self.CONFIG['media']['sortpath']
|
||||
moviepath = self.CONFIG['media']['moviepath']
|
||||
|
||||
new_trailers = os.listdir(sortpath)
|
||||
# loop through new trailers
|
||||
for trailer in new_trailers:
|
||||
# build path
|
||||
year_pattern = re.compile(r'(\()([0-9]{4})(\))')
|
||||
trailing_reg = r'(.*)(_[0-9a-zA-Z-_]{11}-trailer.mkv)$'
|
||||
trailing_pattern = re.compile(trailing_reg)
|
||||
movie_name = trailing_pattern.findall(trailer)[0][0]
|
||||
year = year_pattern.findall(trailer)[0][1]
|
||||
movie_folder = os.path.join(moviepath, year, movie_name)
|
||||
# move if all good
|
||||
if os.path.isdir(movie_folder):
|
||||
old_file = os.path.join(sortpath, trailer)
|
||||
new_file = os.path.join(movie_folder, trailer)
|
||||
os.rename(old_file, new_file)
|
||||
return new_trailers
|
||||
|
||||
|
||||
def archive(config):
|
||||
""" move downloaded trailers to movie archive """
|
||||
sortpath = config['media']['sortpath']
|
||||
moviepath = config['media']['moviepath']
|
||||
|
||||
new_trailers = os.listdir(sortpath)
|
||||
# loop through new trailers
|
||||
for trailer in new_trailers:
|
||||
# build path
|
||||
year_pattern = re.compile(r'(\()([0-9]{4})(\))')
|
||||
trailing_pattern = re.compile(r'(.*)(_[0-9a-zA-Z-_]{11}-trailer\.mkv)$')
|
||||
movie_name = trailing_pattern.findall(trailer)[0][0]
|
||||
year = year_pattern.findall(trailer)[0][1]
|
||||
movie_folder = os.path.join(moviepath, year, movie_name)
|
||||
# move if all good
|
||||
if os.path.isdir(movie_folder):
|
||||
old_file = os.path.join(sortpath, trailer)
|
||||
new_file = os.path.join(movie_folder, trailer)
|
||||
os.rename(old_file, new_file)
|
||||
return new_trailers
|
||||
|
||||
|
||||
def get_pending(config):
|
||||
""" get a list of pending trailers """
|
||||
local_trailer_list = get_local_trailers(config)
|
||||
remote_trailers_list = get_remote_trailers(config)
|
||||
pending = compare_download(local_trailer_list, remote_trailers_list, config)
|
||||
return pending
|
||||
|
||||
|
||||
def main(config):
|
||||
""" main function to download pending trailers """
|
||||
# check for clean folder
|
||||
trashed = incomplete(config)
|
||||
# look for trailer
|
||||
if not trashed:
|
||||
pending = get_pending(config)
|
||||
# download if needed
|
||||
if pending:
|
||||
print(f'downloading {len(pending)} trailers')
|
||||
downloaded = dl_pending(pending, config)
|
||||
def main():
|
||||
""" check and download missing trailers """
|
||||
handler = TrailerHandler()
|
||||
if handler.pending:
|
||||
print(f'downloading {len(handler.pending)} trailers')
|
||||
sleep(2)
|
||||
downloaded = handler.dl_pending()
|
||||
else:
|
||||
downloaded = False
|
||||
print('no missing trailers found')
|
||||
sleep(2)
|
||||
return
|
||||
# move to archive
|
||||
if downloaded:
|
||||
new_trailers = archive(config)
|
||||
new_trailers = handler.archive()
|
||||
print(f'downloaded {len(new_trailers)} new trailers')
|
||||
sleep(2)
|
||||
sleep(2)
|
||||
|
|
|
@ -72,13 +72,13 @@ class Static():
|
|||
def showname_encoder(showname):
|
||||
""" encodes showname for best possible match """
|
||||
# tvmaze doesn't like years in showname
|
||||
showname = showname.strip().rstrip('.')
|
||||
showname = showname.strip().rstrip('-').rstrip('.').strip()
|
||||
year_pattern = re.compile(r'\(?[0-9]{4}\)?')
|
||||
year = year_pattern.findall(showname)
|
||||
if year:
|
||||
showname = showname.rstrip(str(year))
|
||||
showname = showname.rstrip(str(year)).strip()
|
||||
encoded = showname.replace(" ", "%20")
|
||||
encoded = encoded.replace(".", "%20").replace("'", "%20")
|
||||
encoded = encoded.replace(".", "%20").replace("'", "%27")
|
||||
return encoded
|
||||
|
||||
@staticmethod
|
||||
|
@ -274,7 +274,7 @@ class Episode():
|
|||
# build and return tuple
|
||||
season = str(show_response['season']).zfill(2)
|
||||
episode = str(show_response['number']).zfill(2)
|
||||
episode_name = show_response['name']
|
||||
episode_name = show_response['name'].replace('/', '-')
|
||||
return season, episode, episode_name
|
||||
|
||||
|
||||
|
@ -311,7 +311,7 @@ class TvHandler():
|
|||
f_size > min_file_size):
|
||||
move_to = os.path.join(sortpath, filename)
|
||||
os.rename(path, move_to)
|
||||
pending = os.listdir(sortpath)
|
||||
pending = sorted(os.listdir(sortpath))
|
||||
return pending
|
||||
|
||||
def episode_identify(self, to_rename):
|
||||
|
@ -329,6 +329,7 @@ class TvHandler():
|
|||
discovered_item['show_id'] = show_id
|
||||
self.discovered.append(discovered_item)
|
||||
identified.append(episode)
|
||||
print(filename)
|
||||
return identified
|
||||
|
||||
def episode_rename(self, identified):
|
||||
|
@ -354,7 +355,6 @@ class TvHandler():
|
|||
os.makedirs(new_folder, exist_ok=True)
|
||||
os.rename(old_file, new_file)
|
||||
# finish up
|
||||
print(episode.filename)
|
||||
renamed.append(new_file)
|
||||
logging.info('tv:from [%s] to [%s]', episode.filename, new_file)
|
||||
return renamed
|
||||
|
@ -406,5 +406,5 @@ def main():
|
|||
renamed = handler.episode_rename(identified)
|
||||
if renamed:
|
||||
handler.move_to_archive()
|
||||
print(f'renamed {len(renamed)} movies')
|
||||
print(f'renamed {len(renamed)} tv episodes')
|
||||
handler.clean_up()
|
||||
|
|
Loading…
Reference in New Issue