merging json config file changes to master

This commit is contained in:
simon 2021-04-24 12:15:51 +07:00
commit 0a8d5ceba3
11 changed files with 85 additions and 99 deletions

2
.gitignore vendored
View File

@ -2,7 +2,7 @@
__pycache__
# local configuration
config
config.json
# local testing
deploy.sh

View File

@ -52,21 +52,22 @@ These are the none standard Python libraries in use in this project:
Or use `pip` to install all the requirements:
`pip install -r requirements.txt`
### config file:
Duplicate the config.sample file to a file named *config* and set the following variables:
### config json file:
Duplicate the config.sample.json file to a file named *config.json* and set the following variables:
#### media
* `tv_downpath`: Folder path where the tv episodes get downloaded to.
* `movie_downpath`: Folder path where the movie files get downloaded to.
* `sortpath`: Empty folder the media_organizer can use as a temporary sort path.
* `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.
* `ext`: A list of valid media file extensions to easily filter out none media related files.
* `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 integration
* `emby_url`: url where your emby API is reachable
* `emby_user_id`: user id of your emby user
* `emby_api_key`: api key for your user on emby
*Trailer download:*
Arguments under the [ydl_opts] section will get passed in to youtube-dl. Check out the documentation for details.
#### ydl_opts *Trailer download:*
Arguments under the [ydl_opts] section will get passed in to youtube-dl for *trailers*. Check out the documentation for details.

View File

@ -1,20 +0,0 @@
[media]
tv_downpath = /media/movie/download/tv
movie_downpath = /media/movie/download/movie
sortpath = /media/movie/movie/temp
moviepath = /media/movie/movie-archive
tvpath = /media/movie/tv
ext = mp4 mkv avi m4v
log_folder = /home/user/logs/media_organize
movie_db_api = aaaabbbbccccdddd1111222233333444
[emby]
emby_url = http://media.local:8096/emby
emby_user_id = aaaa1111bbbb2222cccc3333dddd4444
emby_api_key = eeee5555ffff6666gggg7777hhhh8888
[ydl_opts]
format = bestvideo[height<=1080]+bestaudio/best[height<=1080]
merge_output_format = mkv
external_downloader = aria2c
geo_bypass = True

23
config.sample.json Normal file
View File

@ -0,0 +1,23 @@
{
"media": {
"tv_downpath": "/media/movie/download/tv",
"movie_downpath": "/media/movie/download/movie",
"sortpath": "/media/movie/movie/temp",
"moviepath": "/media/movie/movie/movie-archive",
"tvpath": "/media/movie/movie/tv",
"ext": ["mp4", "mkv", "avi", "m4v"],
"log_folder": "/home/user/logs/media_organize",
"movie_db_api": "aaaabbbbccccdddd1111222233333444"
},
"emby": {
"emby_url": "http://media.local:8096/emby",
"emby_user_id": "aaaa1111bbbb2222cccc3333dddd4444",
"emby_api_key": "eeee5555ffff6666gggg7777hhhh8888"
},
"ydl_opts": {
"format": "bestvideo[height<=1080]+bestaudio/best[height<=1080]",
"merge_output_format": "mkv",
"external_downloader": "aria2c",
"geo_bypass": true
}
}

View File

@ -2,7 +2,7 @@
""" curses interface to lunch moviesort and tvsort """
import curses
import configparser
import json
import logging
import sys
from os import path
@ -22,45 +22,23 @@ def get_config():
root_folder = path.dirname(sys.argv[0])
if root_folder == '/sbin':
# running interactive
config_path = 'config'
config_path = 'config.json'
else:
config_path = path.join(root_folder, 'config')
config_path = path.join(root_folder, 'config.json')
# parse
config_parser = configparser.ConfigParser()
config_parser.read(config_path)
# build dict
config = {}
config["tv_downpath"] = config_parser.get('media', 'tv_downpath')
config["movie_downpath"] = config_parser.get('media', 'movie_downpath')
config["sortpath"] = config_parser.get('media', 'sortpath')
config["moviepath"] = config_parser.get('media', 'moviepath')
config["tvpath"] = config_parser.get('media', 'tvpath')
config["log_folder"] = config_parser.get('media', 'log_folder')
config["movie_db_api"] = config_parser.get('media', 'movie_db_api')
# ext
ext_str = config_parser.get('media', 'ext')
config["ext"] = ['.' + i for i in ext_str.split()]
# emby
config["emby_url"] = config_parser.get('emby', 'emby_url')
config["emby_user_id"] = config_parser.get('emby', 'emby_user_id')
config["emby_api_key"] = config_parser.get('emby', 'emby_api_key')
# youtubedl_ops
ydl_opts = dict(config_parser.items('ydl_opts'))
# dedect string literals, is there a better way to do that?
for key, value in ydl_opts.items():
if value.isdigit():
ydl_opts[key] = int(value)
elif value.lower() in ['true', 'false']:
ydl_opts[key] = bool(value)
config['ydl_opts'] = ydl_opts
with open(config_path, 'r') as config_file:
data = config_file.read()
config = json.loads(data)
return config
def get_pending_all(config):
""" figure out what needs to be done """
movie_downpath = config['media']['movie_downpath']
tv_downpath = config['media']['tv_downpath']
# call subfunction to collect pending
pending_movie = moviesort.get_pending(config['movie_downpath'])
pending_tv = tvsort.get_pending(config['tv_downpath'])
pending_movie = moviesort.get_pending(movie_downpath)
pending_tv = tvsort.get_pending(tv_downpath)
pending_trailer = len(trailers.get_pending(config))
pending_movie_fix = len(id_fix.get_pending(config))
pending_total = pending_movie + pending_tv + pending_trailer + pending_movie_fix
@ -74,9 +52,8 @@ def get_pending_all(config):
return pending
def print_menu(stdscr, current_row_idx, menu, config, pending):
def print_menu(stdscr, current_row_idx, menu, pending):
""" print menu with populated pending count """
# build stdscr
h, w = stdscr.getmaxyx()
longest = len(max(menu))
@ -137,7 +114,7 @@ def curses_main(stdscr, menu, config):
curses.init_pair(1, curses.COLOR_BLUE, curses.COLOR_WHITE)
current_row_idx = 0
pending = get_pending_all(config)
print_menu(stdscr, current_row_idx, menu, config, pending)
print_menu(stdscr, current_row_idx, menu, pending)
# endless loop
while True:
# wait for exit signal
@ -157,7 +134,7 @@ def curses_main(stdscr, menu, config):
# exit curses and do something
return menu_item
# print
print_menu(stdscr, current_row_idx, menu, config, pending)
print_menu(stdscr, current_row_idx, menu, pending)
stdscr.refresh()
except KeyboardInterrupt:
# clean exit on ctrl + c
@ -169,7 +146,8 @@ def main():
# setup
menu = ['All', 'Movies', 'TV shows', 'DB export', 'Trailer download', 'Fix Movie Names', 'Exit']
config = get_config()
log_file = path.join(config["log_folder"], 'rename.log')
log_folder = config['media']['log_folder']
log_file = path.join(log_folder, 'rename.log')
logging.basicConfig(filename=log_file,level=logging.INFO,format='%(asctime)s:%(message)s')
# endless loop
while True:

View File

@ -7,9 +7,9 @@ import requests
def get_items(config):
""" get json from emby """
emby_url = config['emby_url']
emby_user_id = config['emby_user_id']
emby_api_key = config['emby_api_key']
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' +
@ -89,7 +89,7 @@ def parse_movies(all_movies):
def write_movie_files(movie_info_csv, movie_tech_csv, movie_seen, config):
""" writes the csv files to disk """
log_folder = config['log_folder']
log_folder = config['media']['log_folder']
# movie info
movie_info_sorted = sorted(movie_info_csv, key=lambda k: k['movie_name'])
@ -196,7 +196,7 @@ def parse_episodes(all_episodes):
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 = 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:

View File

@ -9,9 +9,9 @@ from time import sleep
def get_emby_list(config):
""" get current emby movie list """
emby_url = config['emby_url']
emby_user_id = config['emby_user_id']
emby_api_key = config['emby_api_key']
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')
@ -57,7 +57,7 @@ def compare_list(movie_list):
def rename(config, errors_list):
""" rename files with correct names """
print(f'renaming {len(errors_list)} movies.')
moviepath = config['moviepath']
moviepath = config['media']['moviepath']
skipped = []
for movie in errors_list:
old_year = movie[0]['year']

View File

@ -22,9 +22,11 @@ def move_to_sort(movie_downpath, sortpath, ext):
for dirpath, _, filenames in os.walk(movie_downpath):
for filename in filenames:
path = os.path.join(dirpath, filename)
_, extenstion = os.path.splitext(path)
_, extension = os.path.splitext(path)
extension = extension.lstrip('.').lower()
f_size = os.stat(path).st_size
if extenstion.lower() in ext and 'sample' not in filename and f_size > 50000000:
# TODO: set f_size in config.json
if extension in ext and 'sample' not in filename and f_size > 50000000:
move_to = os.path.join(sortpath, filename)
os.rename(path, move_to)
pending = os.listdir(sortpath)
@ -197,7 +199,6 @@ def move_to_archive(sortpath, moviepath):
print(f'{movie_name}\nalready exists in archive')
double = input('[o]: overwrite, [s]: skip and ignore\n')
if double == 'o':
import subprocess
subprocess.call(["trash", new_folder])
os.makedirs(new_folder)
elif double == 's':
@ -236,11 +237,11 @@ def cleanup(movie_downpath, sortpath, renamed):
def main(config):
""" main to sort movies """
# read config
movie_downpath = config['movie_downpath']
sortpath = config['sortpath']
moviepath = config['moviepath']
ext = config['ext']
movie_db_api = config['movie_db_api']
movie_downpath = config['media']['movie_downpath']
sortpath = config['media']['sortpath']
moviepath = config['media']['moviepath']
ext = config['media']['ext']
movie_db_api = config['media']['movie_db_api']
# check if pending
pending = get_pending(movie_downpath)
if not pending:

View File

@ -11,7 +11,7 @@ from time import sleep
def incomplete(config):
""" search for incomplete downloads and trash them """
sortpath = config['sortpath']
sortpath = config['media']['sortpath']
file_list = os.listdir(sortpath)
trashed = False
for file_name in file_list:
@ -28,8 +28,8 @@ def incomplete(config):
def get_local_trailers(config):
""" gets a list of existing trailers on filesystem """
emby_url = config['emby_url']
emby_api_key = config['emby_api_key']
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()
@ -51,8 +51,8 @@ def get_local_trailers(config):
def get_remote_trailers(config):
""" get a list of available trailers on emby """
emby_url = config['emby_url']
emby_api_key = config['emby_api_key']
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&' +
@ -73,8 +73,9 @@ def get_remote_trailers(config):
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(config['log_folder'], 'trailers')
log_file = os.path.join(log_folder, 'trailers')
# check if log file exists
if not os.path.isfile(log_file):
# create empty file
@ -100,7 +101,8 @@ def compare_download(local_trailer_list, remote_trailers_list, config):
def dl_pending(pending, config):
""" download pending trailers """
sortpath = config['sortpath']
sortpath = config['media']['sortpath']
log_folder = config['media']['loc_folder']
ydl_opts = config['ydl_opts']
# loop thrugh list
downloaded = []
@ -119,7 +121,7 @@ def dl_pending(pending, config):
except:
if i == 4:
# giving up
log_file = os.path.join(config['log_folder'], 'trailers')
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
@ -134,8 +136,8 @@ def dl_pending(pending, config):
def archive(config):
""" move downloaded trailers to movie archive """
sortpath = config['sortpath']
moviepath = config['moviepath']
sortpath = config['media']['sortpath']
moviepath = config['media']['moviepath']
new_trailers = os.listdir(sortpath)
# loop through new trailers
@ -176,6 +178,7 @@ def main(config):
else:
downloaded = False
print('no missing trailers found')
return
# move to archive
if downloaded:
new_trailers = archive(config)

View File

@ -16,9 +16,10 @@ def move_to_sort(tv_downpath, sortpath, ext):
for dirpath, _, filenames in os.walk(tv_downpath):
for filename in filenames:
path = os.path.join(dirpath, filename)
_, extenstion = os.path.splitext(path)
_, extension = os.path.splitext(path)
extension = extension.lstrip('.').lower()
f_size = os.stat(path).st_size
if extenstion.lower() in ext and 'sample' not in filename and f_size > 50000000:
if extension in ext and 'sample' not in filename and f_size > 50000000:
move_to = os.path.join(sortpath, filename)
os.rename(path, move_to)
if os.listdir(sortpath):
@ -61,17 +62,16 @@ def clean_up(sortpath, tv_downpath):
def main(config, tvsort_id):
""" main function to sort tv shows """
# parse config
tv_downpath = config['tv_downpath']
tvpath = config['tvpath']
sortpath = config['sortpath']
ext = config['ext']
tv_downpath = config['media']['tv_downpath']
tvpath = config['media']['tvpath']
sortpath = config['media']['sortpath']
ext = config['media']['ext']
# stop here if nothing to do
pending = get_pending(tv_downpath)
if not pending:
print('no tv shows to sort')
sleep(2)
return
# move files
to_sort = move_to_sort(tv_downpath, sortpath, ext)
if to_sort:

View File

@ -222,7 +222,7 @@ def get_episode_name(file_details, show_id):
def episode_rename(config):
""" loops through all files in sortpath """
sortpath = config['sortpath']
sortpath = config['media']['sortpath']
# poor man's cache
cache = {}
cache['last_show_name'] = None