Merge branch 'testing' with new id_fix and db_export
This commit is contained in:
commit
118ba8f664
24
interface.py
24
interface.py
|
@ -15,13 +15,13 @@ import src.trailers as trailers
|
||||||
import src.id_fix as id_fix
|
import src.id_fix as id_fix
|
||||||
|
|
||||||
|
|
||||||
def get_pending_all(config):
|
def get_pending_all():
|
||||||
""" figure out what needs to be done """
|
""" figure out what needs to be done """
|
||||||
# call subfunction to collect pending
|
# call subfunction to collect pending
|
||||||
pending_movie = moviesort.MovieHandler().pending
|
pending_movie = moviesort.MovieHandler().pending
|
||||||
pending_tv = tvsort.TvHandler().pending
|
pending_tv = tvsort.TvHandler().pending
|
||||||
pending_trailer = len(trailers.TrailerHandler().pending)
|
pending_trailer = len(trailers.TrailerHandler().pending)
|
||||||
pending_movie_fix = len(id_fix.get_pending(config))
|
pending_movie_fix = len(id_fix.MovieNameFix().pending)
|
||||||
pending_total = pending_movie + pending_tv + pending_trailer + pending_movie_fix
|
pending_total = pending_movie + pending_tv + pending_trailer + pending_movie_fix
|
||||||
# build dict
|
# build dict
|
||||||
pending = {}
|
pending = {}
|
||||||
|
@ -69,32 +69,32 @@ def print_menu(stdscr, current_row_idx, menu, pending):
|
||||||
stdscr.refresh()
|
stdscr.refresh()
|
||||||
|
|
||||||
|
|
||||||
def sel_handler(menu_item, config):
|
def sel_handler(menu_item):
|
||||||
""" lunch scripts from here based on selection """
|
""" lunch scripts from here based on selection """
|
||||||
if menu_item == 'All':
|
if menu_item == 'All':
|
||||||
moviesort.main()
|
moviesort.main()
|
||||||
tvsort.main()
|
tvsort.main()
|
||||||
db_export.main(config)
|
db_export.main()
|
||||||
trailers.main()
|
trailers.main()
|
||||||
id_fix.main(config)
|
id_fix.main()
|
||||||
elif menu_item == 'Movies':
|
elif menu_item == 'Movies':
|
||||||
moviesort.main()
|
moviesort.main()
|
||||||
elif menu_item == 'TV shows':
|
elif menu_item == 'TV shows':
|
||||||
tvsort.main()
|
tvsort.main()
|
||||||
elif menu_item == 'DB export':
|
elif menu_item == 'DB export':
|
||||||
db_export.main(config)
|
db_export.main()
|
||||||
elif menu_item == 'Trailer download':
|
elif menu_item == 'Trailer download':
|
||||||
trailers.main()
|
trailers.main()
|
||||||
elif menu_item == 'Fix Movie Names':
|
elif menu_item == 'Fix Movie Names':
|
||||||
id_fix.main(config)
|
id_fix.main()
|
||||||
|
|
||||||
|
|
||||||
def curses_main(stdscr, menu, config):
|
def curses_main(stdscr, menu):
|
||||||
""" curses main to desplay and restart the menu """
|
""" curses main to desplay and restart the menu """
|
||||||
curses.curs_set(0)
|
curses.curs_set(0)
|
||||||
curses.init_pair(1, curses.COLOR_BLUE, curses.COLOR_WHITE)
|
curses.init_pair(1, curses.COLOR_BLUE, curses.COLOR_WHITE)
|
||||||
current_row_idx = 0
|
current_row_idx = 0
|
||||||
pending = get_pending_all(config)
|
pending = get_pending_all()
|
||||||
print_menu(stdscr, current_row_idx, menu, pending)
|
print_menu(stdscr, current_row_idx, menu, pending)
|
||||||
# endless loop
|
# endless loop
|
||||||
while True:
|
while True:
|
||||||
|
@ -132,14 +132,14 @@ def main():
|
||||||
logging.basicConfig(filename=log_file,level=logging.INFO,format='%(asctime)s:%(message)s')
|
logging.basicConfig(filename=log_file,level=logging.INFO,format='%(asctime)s:%(message)s')
|
||||||
# endless loop
|
# endless loop
|
||||||
while True:
|
while True:
|
||||||
pending = get_pending_all(config)
|
pending = get_pending_all()
|
||||||
if not pending:
|
if not pending:
|
||||||
return
|
return
|
||||||
menu_item = curses.wrapper(curses_main, menu, config)
|
menu_item = curses.wrapper(curses_main, menu)
|
||||||
if menu_item == 'Exit':
|
if menu_item == 'Exit':
|
||||||
return
|
return
|
||||||
else:
|
else:
|
||||||
sel_handler(menu_item, config)
|
sel_handler(menu_item)
|
||||||
sleep(3)
|
sleep(3)
|
||||||
|
|
||||||
|
|
||||||
|
|
447
src/db_export.py
447
src/db_export.py
|
@ -2,238 +2,237 @@
|
||||||
|
|
||||||
import csv
|
import csv
|
||||||
from os import path
|
from os import path
|
||||||
|
|
||||||
import requests
|
import requests
|
||||||
|
|
||||||
|
from src.config import get_config
|
||||||
def get_items(config):
|
|
||||||
""" get json from emby """
|
|
||||||
emby_url = config['emby']['emby_url']
|
|
||||||
emby_user_id = config['emby']['emby_user_id']
|
|
||||||
emby_api_key = config['emby']['emby_api_key']
|
|
||||||
# movies
|
|
||||||
url = (f'{emby_url}/Users/{emby_user_id}/Items?api_key={emby_api_key}' +
|
|
||||||
'&Recursive=true&IncludeItemTypes=Movie' +
|
|
||||||
'&fields=Genres,MediaStreams,Overview,ProviderIds' +
|
|
||||||
'&SortBy=DateCreated&SortOrder=Descending')
|
|
||||||
r = requests.get(url)
|
|
||||||
all_movies = r.json()['Items']
|
|
||||||
# episodes
|
|
||||||
url = (f'{emby_url}/Users/{emby_user_id}/Items?api_key={emby_api_key}' +
|
|
||||||
'&IncludeItemTypes=Episode&Recursive=true&StartIndex=0' +
|
|
||||||
'&Fields=DateCreated,Genres,MediaStreams,MediaSources,Overview,ProviderIds'
|
|
||||||
'&SortBy=DateCreated&SortOrder=Descending&IsMissing=false')
|
|
||||||
|
|
||||||
r = requests.get(url)
|
|
||||||
all_episodes = r.json()['Items']
|
|
||||||
return all_movies, all_episodes
|
|
||||||
|
|
||||||
|
|
||||||
def parse_movies(all_movies):
|
class DatabaseExport():
|
||||||
""" loop through the movies """
|
""" saves database to CSV """
|
||||||
movie_info_csv = []
|
|
||||||
movie_tech_csv = []
|
CONFIG = get_config()
|
||||||
movie_seen = []
|
|
||||||
for movie in all_movies:
|
def __init__(self):
|
||||||
# general
|
self.all_movies, self.all_episodes = self.get_items()
|
||||||
movie_name = movie['Name']
|
|
||||||
overview = movie['Overview']
|
def get_items(self):
|
||||||
imdb = movie['ProviderIds']['Imdb']
|
""" get json from emby """
|
||||||
played = movie['UserData']['Played']
|
emby_url = self.CONFIG['emby']['emby_url']
|
||||||
genres = ', '.join(movie['Genres'])
|
emby_user_id = self.CONFIG['emby']['emby_user_id']
|
||||||
# media
|
emby_api_key = self.CONFIG['emby']['emby_api_key']
|
||||||
for i in movie['MediaSources']:
|
# movies
|
||||||
if i['Protocol'] == 'File':
|
url = (f'{emby_url}/Users/{emby_user_id}/Items?api_key={emby_api_key}'
|
||||||
file_name = path.basename(i['Path'])
|
'&Recursive=true&IncludeItemTypes=Movie'
|
||||||
year = path.splitext(file_name)[0][-5:-1]
|
'&fields=Genres,MediaStreams,Overview,'
|
||||||
duration_min = round(i['RunTimeTicks'] / 600000000)
|
'ProviderIds,Path,RunTimeTicks'
|
||||||
filesize_MB = round(i['Size'] / 1024 / 1024)
|
'&SortBy=DateCreated&SortOrder=Descending')
|
||||||
for j in i['MediaStreams']:
|
|
||||||
if j['Type'] == 'Video':
|
response = requests.get(url)
|
||||||
image_width = j['Width']
|
all_movies = response.json()['Items']
|
||||||
image_height = j['Height']
|
# episodes
|
||||||
avg_bitrate_MB = round(j['BitRate'] / 1024 / 1024, 2)
|
url = (f'{emby_url}/Users/{emby_user_id}/Items?api_key={emby_api_key}'
|
||||||
codec = j['Codec']
|
'&IncludeItemTypes=Episode&Recursive=true&StartIndex=0'
|
||||||
# found it
|
'&Fields=DateCreated,Genres,MediaStreams,'
|
||||||
break
|
'MediaSources,Overview,ProviderIds,Path,RunTimeTicks'
|
||||||
# found it
|
'&SortBy=DateCreated&SortOrder=Descending&IsMissing=false')
|
||||||
break
|
|
||||||
# info csv
|
response = requests.get(url)
|
||||||
info_dict = {}
|
all_episodes = response.json()['Items']
|
||||||
info_dict['movie_name'] = movie_name
|
return all_movies, all_episodes
|
||||||
info_dict['year'] = year
|
|
||||||
info_dict['imdb'] = imdb
|
def parse_movies(self):
|
||||||
info_dict['genres'] = genres
|
""" handle the movies """
|
||||||
info_dict['overview'] = overview
|
all_movies = self.all_movies
|
||||||
info_dict['duration_min'] = duration_min
|
# seen
|
||||||
movie_info_csv.append(info_dict)
|
movie_seen = ListParser.build_seen(all_movies)
|
||||||
# technical csv
|
self.write_seen(movie_seen, 'movienew')
|
||||||
tech_dict = {}
|
# tech
|
||||||
tech_dict['file_name'] = file_name
|
movie_tech = ListParser.build_tech(all_movies)
|
||||||
tech_dict['duration_min'] = duration_min
|
self.write_csv(movie_tech, 'movie-tech.csv')
|
||||||
tech_dict['filesize_MB'] = filesize_MB
|
# info
|
||||||
tech_dict['image_width'] = image_width
|
movie_info = ListParser.build_movie_info(all_movies)
|
||||||
tech_dict['image_height'] = image_height
|
self.write_csv(movie_info, 'movie-info.csv')
|
||||||
tech_dict['avg_bitrate_MB'] = avg_bitrate_MB
|
|
||||||
tech_dict['codec'] = codec
|
def parse_episodes(self):
|
||||||
movie_tech_csv.append(tech_dict)
|
""" handle the episodes """
|
||||||
# seen or unseen
|
all_episodes = self.all_episodes
|
||||||
if played == True:
|
# seen
|
||||||
icon = '[X]'
|
episode_seen = ListParser.build_seen(all_episodes)
|
||||||
elif played == False:
|
self.write_seen(episode_seen, 'episodenew')
|
||||||
icon = '[ ]'
|
# tech
|
||||||
seen_line = f'{icon} {movie_name} ({year})'
|
episode_tech = ListParser.build_tech(all_episodes)
|
||||||
movie_seen.append(seen_line)
|
self.write_csv(episode_tech, 'episode-tech.csv')
|
||||||
|
# info
|
||||||
return movie_info_csv, movie_tech_csv, movie_seen
|
episode_info = ListParser.build_episode_info(all_episodes)
|
||||||
|
self.write_csv(episode_info, 'episode-info.csv')
|
||||||
|
|
||||||
|
def write_csv(self, to_write, filename):
|
||||||
|
""" write list of dicts to CSV """
|
||||||
|
|
||||||
|
log_folder = self.CONFIG['media']['log_folder']
|
||||||
|
file_path = path.join(log_folder, filename)
|
||||||
|
|
||||||
|
# open and write
|
||||||
|
with open(file_path, 'w') as f:
|
||||||
|
# take fieldnames from first line
|
||||||
|
fieldnames = to_write[0].keys()
|
||||||
|
csv_writer = csv.DictWriter(f, fieldnames)
|
||||||
|
csv_writer.writeheader()
|
||||||
|
csv_writer.writerows(to_write)
|
||||||
|
|
||||||
|
def write_seen(self, to_write, filename):
|
||||||
|
""" write list of seen """
|
||||||
|
log_folder = self.CONFIG['media']['log_folder']
|
||||||
|
file_path = path.join(log_folder, filename)
|
||||||
|
# movie by new
|
||||||
|
with open(file_path, 'w') as f:
|
||||||
|
for line in to_write:
|
||||||
|
f.write(line + '\n')
|
||||||
|
|
||||||
|
|
||||||
def write_movie_files(movie_info_csv, movie_tech_csv, movie_seen, config):
|
class ListParser():
|
||||||
""" writes the csv files to disk """
|
""" static parse the lists from DatabaseExport """
|
||||||
log_folder = config['media']['log_folder']
|
|
||||||
|
|
||||||
# movie info
|
@staticmethod
|
||||||
movie_info_sorted = sorted(movie_info_csv, key=lambda k: k['movie_name'])
|
def build_seen(filelist):
|
||||||
file_path = path.join(log_folder, 'movie-info.csv')
|
""" build the seen list """
|
||||||
# open and write
|
|
||||||
with open(file_path, 'w') as f:
|
|
||||||
# take fieldnames from first line
|
|
||||||
fieldnames = movie_info_sorted[0].keys()
|
|
||||||
csv_writer = csv.DictWriter(f, fieldnames)
|
|
||||||
csv_writer.writeheader()
|
|
||||||
csv_writer.writerows(movie_info_sorted)
|
|
||||||
|
|
||||||
# movie tech
|
file_item_seen = []
|
||||||
movie_tech_csv_sorted = sorted(movie_tech_csv, key=lambda k: k['file_name'])
|
|
||||||
file_path = path.join(log_folder, 'movie-tech.csv')
|
for file_item in filelist:
|
||||||
# open and write
|
played = file_item['UserData']['Played']
|
||||||
with open(file_path, 'w') as f:
|
file_name = path.basename(file_item['Path'])
|
||||||
# take fieldnames from first line
|
file_item_name = path.splitext(file_name)[0]
|
||||||
fieldnames = movie_tech_csv_sorted[0].keys()
|
# seen or unseen
|
||||||
csv_writer = csv.DictWriter(f, fieldnames)
|
if played:
|
||||||
csv_writer.writeheader()
|
icon = '[X]'
|
||||||
csv_writer.writerows(movie_tech_csv_sorted)
|
else:
|
||||||
|
icon = '[ ]'
|
||||||
# movie by new
|
seen_line = f'{icon} {file_item_name}'
|
||||||
file_path = path.join(log_folder, 'movienew')
|
file_item_seen.append(seen_line)
|
||||||
with open(file_path, 'w') as f:
|
|
||||||
for line in movie_seen:
|
return file_item_seen
|
||||||
f.write(line + '\n')
|
|
||||||
|
@staticmethod
|
||||||
|
def build_tech(filelist):
|
||||||
|
""" build tech csv """
|
||||||
|
|
||||||
|
file_item_tech = []
|
||||||
|
|
||||||
|
for file_item in filelist:
|
||||||
|
file_name = path.basename(file_item['Path'])
|
||||||
|
duration_min = round(file_item['RunTimeTicks'] / 600000000)
|
||||||
|
# loop through media sources
|
||||||
|
for i in file_item['MediaSources']:
|
||||||
|
if i['Protocol'] == 'File':
|
||||||
|
filesize = round(i['Size'] / 1024 / 1024)
|
||||||
|
for j in i['MediaStreams']:
|
||||||
|
if j['Type'] == 'Video':
|
||||||
|
image_width = j['Width']
|
||||||
|
image_height = j['Height']
|
||||||
|
avg_bitrate = round(j['BitRate'] / 1024 / 1024, 2)
|
||||||
|
codec = j['Codec']
|
||||||
|
# found it
|
||||||
|
break
|
||||||
|
# found it
|
||||||
|
break
|
||||||
|
# technical csv
|
||||||
|
tech_dict = {}
|
||||||
|
tech_dict['file_name'] = file_name
|
||||||
|
tech_dict['duration_min'] = duration_min
|
||||||
|
tech_dict['filesize_MB'] = filesize
|
||||||
|
tech_dict['image_width'] = image_width
|
||||||
|
tech_dict['image_height'] = image_height
|
||||||
|
tech_dict['avg_bitrate_MB'] = avg_bitrate
|
||||||
|
tech_dict['codec'] = codec
|
||||||
|
file_item_tech.append(tech_dict)
|
||||||
|
|
||||||
|
# sort and return
|
||||||
|
file_item_tech_sorted = sorted(
|
||||||
|
file_item_tech, key=lambda k: k['file_name']
|
||||||
|
)
|
||||||
|
return file_item_tech_sorted
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def build_movie_info(all_movies):
|
||||||
|
""" build movie info csv """
|
||||||
|
|
||||||
|
movie_info = []
|
||||||
|
|
||||||
|
for movie in all_movies:
|
||||||
|
|
||||||
|
movie_name = movie['Name']
|
||||||
|
year = movie['Path'].split('/')[3]
|
||||||
|
overview = movie['Overview']
|
||||||
|
imdb = movie['ProviderIds']['Imdb']
|
||||||
|
genres = ', '.join(movie['Genres'])
|
||||||
|
duration_min = round(movie['RunTimeTicks'] / 600000000)
|
||||||
|
|
||||||
|
info_dict = {}
|
||||||
|
info_dict['movie_name'] = movie_name
|
||||||
|
info_dict['year'] = year
|
||||||
|
info_dict['imdb'] = imdb
|
||||||
|
info_dict['genres'] = genres
|
||||||
|
info_dict['overview'] = overview
|
||||||
|
info_dict['duration_min'] = duration_min
|
||||||
|
movie_info.append(info_dict)
|
||||||
|
|
||||||
|
# sort and return
|
||||||
|
movie_info_sorted = sorted(movie_info, key=lambda k: k['movie_name'])
|
||||||
|
return movie_info_sorted
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def build_episode_info(all_episodes):
|
||||||
|
""" build episode info csv """
|
||||||
|
|
||||||
|
episode_info = []
|
||||||
|
|
||||||
|
for episode in all_episodes:
|
||||||
|
episode_name = episode['Name']
|
||||||
|
file_name = path.basename(episode['Path'])
|
||||||
|
try:
|
||||||
|
episode_id = episode['IndexNumber']
|
||||||
|
except KeyError:
|
||||||
|
# not a real episode
|
||||||
|
continue
|
||||||
|
try:
|
||||||
|
overview = episode['Overview'].replace('\n\n', ' ')
|
||||||
|
overview = overview.replace('\n', ' ')
|
||||||
|
except KeyError:
|
||||||
|
overview = 'NA'
|
||||||
|
try:
|
||||||
|
imdb = episode['ProviderIds']['Imdb']
|
||||||
|
except KeyError:
|
||||||
|
imdb = 'NA'
|
||||||
|
genres = ', '.join(episode['Genres'])
|
||||||
|
season_name = episode['SeasonName']
|
||||||
|
series_name = episode['SeriesName']
|
||||||
|
duration_min = round(episode['RunTimeTicks'] / 600000000)
|
||||||
|
|
||||||
|
# info csv
|
||||||
|
info_dict = {}
|
||||||
|
info_dict['series_name'] = series_name
|
||||||
|
info_dict['season_name'] = season_name
|
||||||
|
info_dict['episode_id'] = episode_id
|
||||||
|
info_dict['episode_name'] = episode_name
|
||||||
|
info_dict['file_name'] = file_name
|
||||||
|
info_dict['imdb'] = imdb
|
||||||
|
info_dict['genres'] = genres
|
||||||
|
info_dict['overview'] = overview
|
||||||
|
info_dict['duration_min'] = duration_min
|
||||||
|
episode_info.append(info_dict)
|
||||||
|
|
||||||
|
# sort and return
|
||||||
|
episode_info_sorted = sorted(
|
||||||
|
episode_info, key=lambda k: k['file_name']
|
||||||
|
)
|
||||||
|
return episode_info_sorted
|
||||||
|
|
||||||
|
|
||||||
def parse_episodes(all_episodes):
|
def main():
|
||||||
""" loop through all episodes """
|
""" main to regenerate csv files """
|
||||||
episode_info_csv = []
|
|
||||||
episode_tech_csv = []
|
|
||||||
episode_seen = []
|
|
||||||
|
|
||||||
for episode in all_episodes:
|
|
||||||
if episode['ParentIndexNumber'] == 0:
|
|
||||||
# not a real episode
|
|
||||||
continue
|
|
||||||
# general
|
|
||||||
episode_name = episode['Name']
|
|
||||||
episode_id = episode['IndexNumber']
|
|
||||||
try:
|
|
||||||
overview = episode['Overview'].replace('\n\n', ' ').replace('\n', ' ')
|
|
||||||
except KeyError:
|
|
||||||
overview = 'NA'
|
|
||||||
try:
|
|
||||||
imdb = episode['ProviderIds']['Imdb']
|
|
||||||
except KeyError:
|
|
||||||
imdb = 'NA'
|
|
||||||
played = episode['UserData']['Played']
|
|
||||||
genres = ', '.join(episode['Genres'])
|
|
||||||
season_name = episode['SeasonName']
|
|
||||||
series_name = episode['SeriesName']
|
|
||||||
# media
|
|
||||||
for i in episode['MediaSources']:
|
|
||||||
if i['Protocol'] == 'File':
|
|
||||||
file_name = path.basename(i['Path'])
|
|
||||||
file_id = i['Name']
|
|
||||||
duration_min = round(i['RunTimeTicks'] / 600000000)
|
|
||||||
filesize_MB = round(i['Size'] / 1024 / 1024)
|
|
||||||
for j in i['MediaStreams']:
|
|
||||||
if j['Type'] == 'Video':
|
|
||||||
image_width = j['Width']
|
|
||||||
image_height = j['Height']
|
|
||||||
avg_bitrate_MB = round(j['BitRate'] / 1024 / 1024, 2)
|
|
||||||
codec = j['Codec']
|
|
||||||
# found it
|
|
||||||
break
|
|
||||||
# found it
|
|
||||||
break
|
|
||||||
# info csv
|
|
||||||
info_dict = {}
|
|
||||||
info_dict['series_name'] = series_name
|
|
||||||
info_dict['file_id'] = file_id
|
|
||||||
info_dict['season_name'] = season_name
|
|
||||||
info_dict['episode_id'] = episode_id
|
|
||||||
info_dict['episode_name'] = episode_name
|
|
||||||
info_dict['imdb'] = imdb
|
|
||||||
info_dict['genres'] = genres
|
|
||||||
info_dict['overview'] = overview
|
|
||||||
info_dict['duration_min'] = duration_min
|
|
||||||
episode_info_csv.append(info_dict)
|
|
||||||
# technical csv
|
|
||||||
tech_dict = {}
|
|
||||||
tech_dict['file_name'] = file_name
|
|
||||||
tech_dict['duration_min'] = duration_min
|
|
||||||
tech_dict['filesize_MB'] = filesize_MB
|
|
||||||
tech_dict['image_width'] = image_width
|
|
||||||
tech_dict['image_height'] = image_height
|
|
||||||
tech_dict['avg_bitrate_MB'] = avg_bitrate_MB
|
|
||||||
tech_dict['codec'] = codec
|
|
||||||
episode_tech_csv.append(tech_dict)
|
|
||||||
# seen or unseen
|
|
||||||
if played == True:
|
|
||||||
icon = '[X]'
|
|
||||||
elif played == False:
|
|
||||||
icon = '[ ]'
|
|
||||||
seen_line = f'{icon} {file_id}'
|
|
||||||
episode_seen.append(seen_line)
|
|
||||||
return episode_info_csv, episode_tech_csv, episode_seen
|
|
||||||
|
|
||||||
|
|
||||||
def write_episode_files(episode_info_csv, episode_tech_csv, episode_seen, config):
|
|
||||||
""" writes the csv files to disk """
|
|
||||||
log_folder = config['media']['log_folder']
|
|
||||||
# episode info
|
|
||||||
episode_info_sorted = sorted(episode_info_csv, key=lambda k: k['file_id'])
|
|
||||||
for i in episode_info_sorted:
|
|
||||||
i.pop('file_id', None)
|
|
||||||
file_path = path.join(log_folder, 'episode-info.csv')
|
|
||||||
# open and write
|
|
||||||
with open(file_path, 'w') as f:
|
|
||||||
# take fieldnames from first line
|
|
||||||
fieldnames = episode_info_sorted[0].keys()
|
|
||||||
csv_writer = csv.DictWriter(f, fieldnames)
|
|
||||||
csv_writer.writeheader()
|
|
||||||
csv_writer.writerows(episode_info_sorted)
|
|
||||||
# episode tech
|
|
||||||
episode_tech_csv_sorted = sorted(episode_tech_csv, key=lambda k: k['file_name'])
|
|
||||||
file_path = path.join(log_folder, 'episode-tech.csv')
|
|
||||||
# open and write
|
|
||||||
with open(file_path, 'w') as f:
|
|
||||||
# take fieldnames from first line
|
|
||||||
fieldnames = episode_tech_csv_sorted[0].keys()
|
|
||||||
csv_writer = csv.DictWriter(f, fieldnames)
|
|
||||||
csv_writer.writeheader()
|
|
||||||
csv_writer.writerows(episode_tech_csv_sorted)
|
|
||||||
# episode by new
|
|
||||||
file_path = path.join(log_folder, 'episodenew')
|
|
||||||
with open(file_path, 'w') as f:
|
|
||||||
for line in episode_seen:
|
|
||||||
f.write(line + '\n')
|
|
||||||
|
|
||||||
|
|
||||||
def main(config):
|
|
||||||
""" write collection to csv """
|
|
||||||
print('recreating db files')
|
print('recreating db files')
|
||||||
# get data
|
export = DatabaseExport()
|
||||||
all_movies, all_episodes = get_items(config)
|
export.parse_movies()
|
||||||
# write movies
|
export.parse_episodes()
|
||||||
movie_info_csv, movie_tech_csv, movie_seen = parse_movies(all_movies)
|
|
||||||
write_movie_files(movie_info_csv, movie_tech_csv, movie_seen, config)
|
|
||||||
# write episodes
|
|
||||||
episode_info_csv, episode_tech_csv, episode_seen = parse_episodes(all_episodes)
|
|
||||||
write_episode_files(episode_info_csv, episode_tech_csv, episode_seen, config)
|
|
||||||
|
|
241
src/id_fix.py
241
src/id_fix.py
|
@ -2,119 +2,158 @@
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
import requests
|
import subprocess
|
||||||
|
|
||||||
from time import sleep
|
from time import sleep
|
||||||
|
|
||||||
|
import requests
|
||||||
|
|
||||||
def get_emby_list(config):
|
from src.config import get_config
|
||||||
""" get current emby movie list """
|
|
||||||
emby_url = config['emby']['emby_url']
|
|
||||||
emby_user_id = config['emby']['emby_user_id']
|
|
||||||
emby_api_key = config['emby']['emby_api_key']
|
|
||||||
|
|
||||||
url = (emby_url + '/Users/' + emby_user_id + '/Items?api_key=' + emby_api_key +
|
|
||||||
'&Recursive=True&IncludeItemTypes=Movie&Fields=Path,PremiereDate')
|
|
||||||
r_emby = requests.get(url).json()
|
|
||||||
movie_list = r_emby['Items']
|
|
||||||
return movie_list
|
|
||||||
|
|
||||||
|
|
||||||
def compare_list(movie_list):
|
class MovieNameFix():
|
||||||
""" compare the movie_list and look for wong ids """
|
""" check movie names in library and
|
||||||
errors_list = []
|
rename if premiere date doesn't match with filename """
|
||||||
for movie in movie_list:
|
|
||||||
# from file name
|
CONFIG = get_config()
|
||||||
file_name = os.path.basename(os.path.splitext(movie['Path'])[0])
|
|
||||||
year_id_pattern = re.compile(r'\(\d{4}\)')
|
def __init__(self):
|
||||||
year_str = year_id_pattern.findall(file_name)[-1]
|
self.movie_list = self.get_emby_list()
|
||||||
file_year = year_str.replace('(', '').replace(')', '')
|
self.pending = self.find_errors()
|
||||||
file_base_name = file_name.replace(year_str, '').strip()
|
|
||||||
# dedected in emby
|
def get_emby_list(self):
|
||||||
movie_name = movie['Name']
|
""" get current emby movie list """
|
||||||
try:
|
emby_url = self.CONFIG['emby']['emby_url']
|
||||||
premier_year = movie['PremiereDate'].split('-')[0]
|
emby_user_id = self.CONFIG['emby']['emby_user_id']
|
||||||
except KeyError:
|
emby_api_key = self.CONFIG['emby']['emby_api_key']
|
||||||
premier_year = file_year
|
|
||||||
# check for error
|
url = (f'{emby_url}/Users/{emby_user_id}/Items?api_key={emby_api_key}'
|
||||||
error = False
|
'&Recursive=True&IncludeItemTypes=Movie'
|
||||||
if file_base_name != movie_name:
|
'&Fields=Path,PremiereDate')
|
||||||
for i, j in enumerate(file_base_name):
|
request = requests.get(url).json()
|
||||||
if j != movie_name[i] and j != '-':
|
movie_list = request['Items']
|
||||||
|
return movie_list
|
||||||
|
|
||||||
|
def find_errors(self):
|
||||||
|
""" find missmatch in movie_list """
|
||||||
|
|
||||||
|
errors = []
|
||||||
|
for movie in self.movie_list:
|
||||||
|
# parse filename
|
||||||
|
file_name = os.path.basename(movie['Path'])
|
||||||
|
ext = os.path.splitext(file_name)[1]
|
||||||
|
movie_name = os.path.splitext(file_name)[0]
|
||||||
|
year_id_pattern = re.compile(r'\((\d{4})\)$')
|
||||||
|
file_year = year_id_pattern.findall(movie_name)[-1]
|
||||||
|
movie_name_file = movie_name.split(f'({file_year})')[0].strip()
|
||||||
|
# premier date
|
||||||
|
try:
|
||||||
|
premier_year = movie['PremiereDate'].split('-')[0]
|
||||||
|
except KeyError:
|
||||||
|
premier_year = file_year
|
||||||
|
# emby
|
||||||
|
emby_name = movie['Name']
|
||||||
|
error = False
|
||||||
|
if emby_name != movie_name_file:
|
||||||
|
diff = self.str_diff(emby_name, movie_name_file)
|
||||||
|
if diff:
|
||||||
error = True
|
error = True
|
||||||
break
|
if premier_year != file_year:
|
||||||
if file_year != premier_year:
|
error = True
|
||||||
error = True
|
if error:
|
||||||
# add to list on error
|
error_dict = {}
|
||||||
if error:
|
error_dict['old_year'] = file_year
|
||||||
new_name = f'{movie_name} ({premier_year})'.replace('/', '-')
|
error_dict['old_name'] = file_name
|
||||||
old = {'filename': file_name, 'year': file_year}
|
error_dict['new_year'] = premier_year
|
||||||
new = {'filename': new_name, 'year': premier_year}
|
error_dict['new_name'] = f'{emby_name} ({premier_year}){ext}'
|
||||||
errors_list.append([old, new])
|
errors.append(error_dict)
|
||||||
return errors_list
|
return errors
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def str_diff(str1, str2):
|
||||||
|
""" simple diff calculator between two strings
|
||||||
|
ignoreing - and / """
|
||||||
|
diff = []
|
||||||
|
for num, value in enumerate(str1):
|
||||||
|
try:
|
||||||
|
if value not in (str2[num], '/'):
|
||||||
|
diff.append(value)
|
||||||
|
except IndexError:
|
||||||
|
diff.append(value)
|
||||||
|
for num, value in enumerate(str2):
|
||||||
|
try:
|
||||||
|
if value not in (str1[num], '-'):
|
||||||
|
diff.append(value)
|
||||||
|
except IndexError:
|
||||||
|
diff.append(value)
|
||||||
|
return list(diff)
|
||||||
|
|
||||||
def rename(config, errors_list):
|
def fix_errors(self):
|
||||||
""" rename files with correct names """
|
""" select what to do """
|
||||||
print(f'renaming {len(errors_list)} movies.')
|
skipped = []
|
||||||
moviepath = config['media']['moviepath']
|
fixed = []
|
||||||
skipped = []
|
print(f'found {len(self.pending)} problems')
|
||||||
for movie in errors_list:
|
for error in self.pending:
|
||||||
old_year = movie[0]['year']
|
old_name = error['old_name']
|
||||||
old_name = movie[0]['filename']
|
new_name = error['new_name']
|
||||||
old_folder = os.path.join(moviepath, old_year, old_name)
|
# prompt
|
||||||
|
print(f'\nrenaming from-to:\n{old_name}\n{new_name}')
|
||||||
|
print('[0]: skip')
|
||||||
|
print('[1]: rename')
|
||||||
|
print('[c]: cancel')
|
||||||
|
select = input()
|
||||||
|
|
||||||
rename_files = os.listdir(old_folder)
|
if select == '1':
|
||||||
new_year = movie[1]['year']
|
self.rename_files(error)
|
||||||
new_name = movie[1]['filename']
|
fixed.append(new_name)
|
||||||
# prompt
|
elif select == '0':
|
||||||
print(f'\nrenaming from-to:\n{old_name}\n{new_name}')
|
skipped.append(old_name)
|
||||||
print('[0]: skip')
|
continue
|
||||||
print('[1]: rename')
|
elif select == 'c':
|
||||||
print('[c]: cancel')
|
print('cancel')
|
||||||
select = input()
|
return
|
||||||
if select == 0:
|
else:
|
||||||
skipped.append(old_name)
|
print(f'{select} is invalid input')
|
||||||
break
|
return
|
||||||
elif select == 'c':
|
# pritty output
|
||||||
return
|
if skipped:
|
||||||
# continue
|
print('skipped files:')
|
||||||
for item in rename_files:
|
for i in skipped:
|
||||||
old_file_name = os.path.join(old_folder, item)
|
print(i)
|
||||||
new_file_name = os.path.join(old_folder, item.replace(old_name, new_name))
|
if fixed:
|
||||||
os.rename(old_file_name, new_file_name)
|
print(f'fixed {len(fixed)} movies')
|
||||||
# movie folder
|
|
||||||
os.rename(old_folder, old_folder.replace(old_name, new_name))
|
def rename_files(self, error):
|
||||||
# year folder
|
""" actually rename the files """
|
||||||
|
moviepath = self.CONFIG['media']['moviepath']
|
||||||
|
old_year = error['old_year']
|
||||||
|
new_year = error['new_year']
|
||||||
|
old_movie = os.path.splitext(error['old_name'])[0]
|
||||||
|
old_folder = os.path.join(moviepath, old_year, old_movie)
|
||||||
|
new_movie = os.path.splitext(error['new_name'])[0]
|
||||||
|
# handle folder
|
||||||
if old_year != new_year:
|
if old_year != new_year:
|
||||||
old_folder_name = old_folder.replace(f'({old_year})', f'({new_year})')
|
old_year_folder = os.path.split(old_folder)[0]
|
||||||
new_folder_name = old_folder_name.replace(old_year, new_year)
|
new_year_folder = old_year_folder.replace(old_year, new_year)
|
||||||
os.rename(old_folder_name, new_folder_name)
|
new_folder = os.path.join(new_year_folder, new_movie)
|
||||||
return skipped
|
else:
|
||||||
|
new_folder = old_folder.replace(old_movie, new_movie)
|
||||||
|
os.makedirs(new_folder)
|
||||||
|
# handle files
|
||||||
|
for file_name in os.listdir(old_folder):
|
||||||
|
old_file = os.path.join(old_folder, file_name)
|
||||||
|
new_file_name = file_name.replace(old_movie, new_movie)
|
||||||
|
new_file = os.path.join(new_folder, new_file_name)
|
||||||
|
os.rename(old_file, new_file)
|
||||||
|
# trash now empty folder
|
||||||
|
subprocess.call(['trash', old_folder])
|
||||||
|
|
||||||
|
|
||||||
def get_pending(config):
|
def main():
|
||||||
""" returns a list of movies with errors """
|
""" main for fixing movie filenames """
|
||||||
movie_list = get_emby_list(config)
|
handler = MovieNameFix()
|
||||||
errors_list = compare_list(movie_list)
|
|
||||||
return errors_list
|
|
||||||
|
|
||||||
|
if not handler.pending:
|
||||||
def main(config):
|
|
||||||
""" main to lunch the id_fix """
|
|
||||||
errors_list = get_pending(config)
|
|
||||||
if not errors_list:
|
|
||||||
print('no errors found')
|
print('no errors found')
|
||||||
sleep(2)
|
|
||||||
return
|
return
|
||||||
else:
|
handler.fix_errors()
|
||||||
skipped = rename(config, errors_list)
|
sleep(2)
|
||||||
|
|
||||||
if skipped:
|
|
||||||
print('skipped following movies:')
|
|
||||||
for movie in skipped:
|
|
||||||
print(movie)
|
|
||||||
input('continue?')
|
|
||||||
else:
|
|
||||||
print(f'fixed {len(errors_list)} movie names.')
|
|
||||||
sleep(2)
|
|
||||||
|
|
Loading…
Reference in New Issue