exporting episodes and movies from emby to csv

This commit is contained in:
simon 2021-04-04 15:06:49 +07:00
parent 23d48f1d52
commit bac85f6bd1
3 changed files with 127 additions and 5 deletions

View File

@ -20,6 +20,8 @@ Detect tv show filenames by querying the publicly available [tvmaze.com](https:/
Episodes are named in this style, a more flexible solution is in pending:
**{show-name}/Season {nr}/show-name - S{nr}E{nr} - {episode-name}.{ext}**
## db_export
Export the library to csv files. Calles the Emby API to get a list of movies and episodes and exports this to a convenient set ov CSV files.
## setup
### install
@ -39,5 +41,9 @@ Duplicate the config.sample file to a file named *config* and set the following
* `moviepath`: Root folder where the organized movie files will go.
* `tvpath`: Root folder where the organized tv episodes will go.
* `ext`: A space separated list of valid media file extensions to easily filter out none media related files.
* `log_file`: Path to a file to output all renaming done to keep track and check for any errors.
* `movie_db_api`: Register and get your themoviedb.com **API Key (v3 auth)** acces from [here](https://www.themoviedb.org/settings/api).
* `log_path`: Path to a folder to output all renaming done to keep track and check for any errors and safe csv files.
* `movie_db_api`: Register and get your themoviedb.com **API Key (v3 auth)** acces from [here](https://www.themoviedb.org/settings/api).
Emby integration:
* `emby_url`: url where your emby instance is reachable
* `emby_user_id`: user id of your emby user
* `emby_api_key`: api key for your user on emby

View File

@ -97,6 +97,7 @@ def sel_handler(menu_item, config):
if menu_item == 'All':
moviesort.main(config)
tvsort.main(config, tvsort_id)
db_export.main(config)
elif menu_item == 'Movies':
moviesort.main(config)
elif menu_item == 'TV shows':

View File

@ -20,7 +20,7 @@ def get_items(config):
# episodes
url = (f'{emby_url}/Users/{emby_user_id}/Items?api_key={emby_api_key}' +
'&IncludeItemTypes=Episode&Recursive=true&StartIndex=0' +
'&Fields=DateCreated,MediaStreams,MediaSources'
'&Fields=DateCreated,Genres,MediaStreams,MediaSources,Overview,ProviderIds'
'&SortBy=DateCreated&SortOrder=Descending&IsMissing=false')
r = requests.get(url)
@ -116,13 +116,128 @@ def write_movie_files(movie_info_csv, movie_tech_csv, movie_seen, config):
# movie by new
file_path = path.join(log_folder, 'movienew')
with open(file_path, 'w') as f:
f.writelines(movie_seen)
for line in movie_seen:
f.write(line + '\n')
def parse_episodes(all_episodes):
""" loop through all episodes """
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['log_folder']
log_folder = '/tmp'
# 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')
# get data
all_movies, _ = get_items(config)
all_movies, all_episodes = get_items(config)
# write movies
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)