diff --git a/misc/config.py b/misc/config.py index 0e7800d..fe0e659 100644 --- a/misc/config.py +++ b/misc/config.py @@ -61,16 +61,16 @@ base_config = { 'anticipated': 100, 'trending': 2, 'popular': 1 + } + }, + 'notifications': { + 'plex slack': { + 'type': 'slack', + 'webhook': 'http://' }, - 'notifications': { - 'plex slack': { - 'type': 'slack', - 'webhook': 'http://' - }, - 'my pushover': { - 'client_id': '....', - 'client_secret': '....' - } + 'my pushover': { + 'client_id': '....', + 'client_secret': '....' } } } diff --git a/traktarr.py b/traktarr.py index 1b1dd10..1fad9f6 100644 --- a/traktarr.py +++ b/traktarr.py @@ -36,14 +36,17 @@ def app(): @click.option('--add-limit', '-l', default=0, help='Limit number of shows added to Sonarr.', show_default=True) @click.option('--add-delay', '-d', default=2.5, help='Seconds between each add request to Sonarr.', show_default=True) @click.option('--no-search', is_flag=True, help='Disable search when adding shows to Sonarr.') -def shows(list_type, add_limit=0, add_delay=2.5, no_search=False): +@click.option('--notifications', is_flag=True, help='Send notifications.') +def shows(list_type, add_limit=0, add_delay=2.5, no_search=False, notifications=False): added_shows = 0 # validate trakt api_key trakt = Trakt(cfg.trakt.api_key) if not trakt.validate_api_key(): log.error("Aborting due to failure to validate Trakt API Key") - return + if notifications: + callback_notify({'event': 'abort', 'type': 'shows', 'reason': 'Failure to validate Trakt API Key'}) + return None else: log.info("Validated Trakt API Key") @@ -51,7 +54,9 @@ def shows(list_type, add_limit=0, add_delay=2.5, no_search=False): sonarr = Sonarr(cfg.sonarr.url, cfg.sonarr.api_key) if not sonarr.validate_api_key(): log.error("Aborting due to failure to validate Sonarr URL / API Key") - return + if notifications: + callback_notify({'event': 'abort', 'type': 'shows', 'reason': 'Failure to validate Sonarr URL / API Key'}) + return None else: log.info("Validated Sonarr URL & API Key") @@ -59,7 +64,10 @@ def shows(list_type, add_limit=0, add_delay=2.5, no_search=False): profile_id = sonarr.get_profile_id(cfg.sonarr.profile) if not profile_id or not profile_id > 0: log.error("Aborting due to failure to retrieve Profile ID for: %s", cfg.sonarr.profile) - return + if notifications: + callback_notify({'event': 'abort', 'type': 'shows', + 'reason': 'Failure to retrieve Sonarr Profile ID of %s' % cfg.sonarr.profile}) + return None else: log.info("Retrieved Profile ID for %s: %d", cfg.sonarr.profile, profile_id) @@ -67,7 +75,9 @@ def shows(list_type, add_limit=0, add_delay=2.5, no_search=False): profile_tags = sonarr.get_tags() if profile_tags is None: log.error("Aborting due to failure to retrieve Tag ID's") - return + if notifications: + callback_notify({'event': 'abort', 'type': 'shows', 'reason': "Failure to retrieve Sonarr Tag ID's"}) + return None else: log.info("Retrieved %d Tag ID's", len(profile_tags)) @@ -75,7 +85,9 @@ def shows(list_type, add_limit=0, add_delay=2.5, no_search=False): sonarr_series_list = sonarr.get_series() if not sonarr_series_list: log.error("Aborting due to failure to retrieve Sonarr shows list") - return + if notifications: + callback_notify({'event': 'abort', 'type': 'shows', 'reason': 'Failure to retrieve Sonarr shows list'}) + return None else: log.info("Retrieved Sonarr shows list, shows found: %d", len(sonarr_series_list)) @@ -89,10 +101,15 @@ def shows(list_type, add_limit=0, add_delay=2.5, no_search=False): trakt_series_list = trakt.get_popular_shows() else: log.error("Aborting due to unknown Trakt list type") - return + if notifications: + callback_notify({'event': 'abort', 'type': 'shows', 'reason': 'Failure to determine Trakt list type'}) + return None if not trakt_series_list: log.error("Aborting due to failure to retrieve Trakt %s shows list", list_type) - return + if notifications: + callback_notify( + {'event': 'abort', 'type': 'shows', 'reason': 'Failure to retrieve Trakt %s shows list' % list_type}) + return None else: log.info("Retrieved Trakt %s shows list, shows found: %d", list_type, len(trakt_series_list)) @@ -100,7 +117,11 @@ def shows(list_type, add_limit=0, add_delay=2.5, no_search=False): processed_series_list = helpers.sonarr_remove_existing_series(sonarr_series_list, trakt_series_list) if processed_series_list is None: log.error("Aborting due to failure to remove existing Sonarr shows from retrieved Trakt shows list") - return + if notifications: + callback_notify({'event': 'abort', 'type': 'shows', + 'reason': 'Failure to remove existing Sonarr shows from retrieved Trakt %s shows list' % list_type + }) + return None else: log.info("Removed existing Sonarr shows from Trakt shows list, shows left to process: %d", len(processed_series_list)) @@ -127,6 +148,8 @@ def shows(list_type, add_limit=0, add_delay=2.5, no_search=False): series['show']['ids']['slug'], profile_id, cfg.sonarr.root_folder, use_tags, not no_search): log.info("ADDED %s (%d) with tags: %s", series['show']['title'], series['show']['year'], use_tags) + if notifications: + callback_notify({'event': 'add_show', 'show': series['show']}) added_shows += 1 else: log.error("FAILED adding %s (%d) with tags: %s", series['show']['title'], series['show']['year'], @@ -143,6 +166,7 @@ def shows(list_type, add_limit=0, add_delay=2.5, no_search=False): log.exception("Exception while processing show %s: ", series['show']['title']) log.info("Added %d new show(s) to Sonarr", added_shows) + return added_shows ############################################################ @@ -155,14 +179,17 @@ def shows(list_type, add_limit=0, add_delay=2.5, no_search=False): @click.option('--add-limit', '-l', default=0, help='Limit number of movies added to Radarr.', show_default=True) @click.option('--add-delay', '-d', default=2.5, help='Seconds between each add request to Radarr.', show_default=True) @click.option('--no-search', is_flag=True, help='Disable search when adding movies to Radarr.') -def movies(list_type, add_limit=0, add_delay=2.5, no_search=False): +@click.option('--notifications', is_flag=True, help='Send notifications.') +def movies(list_type, add_limit=0, add_delay=2.5, no_search=False, notifications=False): added_movies = 0 # validate trakt api_key trakt = Trakt(cfg.trakt.api_key) if not trakt.validate_api_key(): log.error("Aborting due to failure to validate Trakt API Key") - return + if notifications: + callback_notify({'event': 'abort', 'type': 'movies', 'reason': 'Failure to validate Trakt API Key'}) + return None else: log.info("Validated Trakt API Key") @@ -170,7 +197,10 @@ def movies(list_type, add_limit=0, add_delay=2.5, no_search=False): radarr = Radarr(cfg.radarr.url, cfg.radarr.api_key) if not radarr.validate_api_key(): log.error("Aborting due to failure to validate Radarr URL / API Key") - return + if notifications: + callback_notify( + {'event': 'abort', 'type': 'movies', 'reason': 'Failure to validate Radarr URL / API Key'}) + return None else: log.info("Validated Radarr URL & API Key") @@ -178,7 +208,10 @@ def movies(list_type, add_limit=0, add_delay=2.5, no_search=False): profile_id = radarr.get_profile_id(cfg.radarr.profile) if not profile_id or not profile_id > 0: log.error("Aborting due to failure to retrieve Profile ID for: %s", cfg.radarr.profile) - return + if notifications: + callback_notify({'event': 'abort', 'type': 'movies', + 'reason': 'Failure to retrieve Radarr Profile ID of %s' % cfg.radarr.profile}) + return None else: log.info("Retrieved Profile ID for %s: %d", cfg.radarr.profile, profile_id) @@ -186,7 +219,9 @@ def movies(list_type, add_limit=0, add_delay=2.5, no_search=False): radarr_movie_list = radarr.get_movies() if not radarr_movie_list: log.error("Aborting due to failure to retrieve Radarr movies list") - return + if notifications: + callback_notify({'event': 'abort', 'type': 'movies', 'reason': 'Failure to retrieve Radarr movies list'}) + return None else: log.info("Retrieved Radarr movies list, movies found: %d", len(radarr_movie_list)) @@ -202,10 +237,15 @@ def movies(list_type, add_limit=0, add_delay=2.5, no_search=False): trakt_movies_list = trakt.get_boxoffice_movies() else: log.error("Aborting due to unknown Trakt list type") - return + if notifications: + callback_notify({'event': 'abort', 'type': 'movies', 'reason': 'Failure to determine Trakt list type'}) + return None if not trakt_movies_list: log.error("Aborting due to failure to retrieve Trakt %s movies list", list_type) - return + if notifications: + callback_notify( + {'event': 'abort', 'type': 'movies', 'reason': 'Failure to retrieve Trakt %s movies list' % list_type}) + return None else: log.info("Retrieved Trakt %s movies list, movies found: %d", list_type, len(trakt_movies_list)) @@ -213,7 +253,11 @@ def movies(list_type, add_limit=0, add_delay=2.5, no_search=False): processed_movies_list = helpers.radarr_remove_existing_movies(radarr_movie_list, trakt_movies_list) if processed_movies_list is None: log.error("Aborting due to failure to remove existing Radarr movies from retrieved Trakt movies list") - return + if notifications: + callback_notify({'event': 'abort', 'type': 'movies', + 'reason': 'Failure to remove existing Radarr movies from retrieved ' + 'Trakt %s movies list' % list_type}) + return None else: log.info("Removed existing Radarr movies from Trakt movies list, movies left to process: %d", len(processed_movies_list)) @@ -234,6 +278,8 @@ def movies(list_type, add_limit=0, add_delay=2.5, no_search=False): if radarr.add_movie(movie['movie']['ids']['tmdb'], movie['movie']['title'], movie['movie']['year'], movie['movie']['ids']['slug'], profile_id, cfg.radarr.root_folder, not no_search): log.info("ADDED %s (%d)", movie['movie']['title'], movie['movie']['year']) + if notifications: + callback_notify({'event': 'add_movie', 'movie': movie['movie']}) added_movies += 1 else: log.error("FAILED adding %s (%d)", movie['movie']['title'], movie['movie']['year']) @@ -249,28 +295,36 @@ def movies(list_type, add_limit=0, add_delay=2.5, no_search=False): log.exception("Exception while processing movie %s: ", movie['movie']['title']) log.info("Added %d new movie(s) to Radarr", added_movies) + return added_movies ############################################################ # AUTOMATIC ############################################################ -def callback_automatic(data): - log.debug("Received callback data:\n%s", data) +def callback_notify(data): + log.debug("Received callback data: %s", data) # handle event if data['event'] == 'add_movie': log.info("Added movie: %s (%d)", data['movie']['title'], data['movie']['year']) + return elif data['event'] == 'add_show': log.info("Added show: %s (%d)", data['show']['title'], data['show']['year']) + return + elif data['event'] == 'abort': + log.error("Error while adding %s due to: %s", data['type'], data['reason']) + return else: - log.error("Unexpected callback:\n%s", data) - + log.error("Unexpected callback: %s", data) return -def automatic_shows(add_delay=2.5, no_search=False): +def automatic_shows(add_delay=2.5, no_search=False, notifications=False): + total_shows_added = 0 try: + log.info("Started") + for list_type, type_amount in cfg.automatic.shows.items(): if list_type.lower() == 'interval': continue @@ -280,14 +334,34 @@ def automatic_shows(add_delay=2.5, no_search=False): else: log.info("Adding %d shows from Trakt's %s list", type_amount, list_type) + # run shows + added_shows = shows.callback(list_type=list_type, add_limit=type_amount, + add_delay=add_delay, no_search=no_search, + notifications=notifications) + if added_shows is None: + log.error("Failed adding shows from Trakt's %s list", list_type) + time.sleep(15) + continue + total_shows_added += added_shows + + # send notification + log.info("Added %d shows from Trakt's %s list", added_shows, list_type) + + # sleep + time.sleep(15) + + log.info("Finished, added %d shows in total to Sonarr", total_shows_added) except Exception: log.exception("Exception while automatically adding shows: ") return -def automatic_movies(add_delay=2.5, no_search=False): +def automatic_movies(add_delay=2.5, no_search=False, notifications=False): + total_movies_added = 0 try: + log.info("Started") + for list_type, type_amount in cfg.automatic.movies.items(): if list_type.lower() == 'interval': continue @@ -297,6 +371,23 @@ def automatic_movies(add_delay=2.5, no_search=False): else: log.info("Adding %d movies from Trakt's %s list", type_amount, list_type) + # run movies + added_movies = movies.callback(list_type=list_type, add_limit=type_amount, + add_delay=add_delay, no_search=no_search, + notifications=notifications) + if added_movies is None: + log.error("Failed adding movies from Trakt's %s list", list_type) + time.sleep(15) + continue + total_movies_added += added_movies + + # send notification + log.info("Added %d movies from Trakt's %s list", added_movies, list_type) + + # sleep + time.sleep(15) + + log.info("Finished, added %d movies in total to Radarr", total_movies_added) except Exception: log.exception("Exception while automatically adding movies: ") @@ -307,10 +398,12 @@ def automatic_movies(add_delay=2.5, no_search=False): @click.option('--add-delay', '-d', default=2.5, help='Seconds between each add request to Sonarr / Radarr.', show_default=True) @click.option('--no-search', is_flag=True, help='Disable search when adding to Sonarr / Radarr.') -def run(add_delay=2.5, no_search=False): +@click.option('--no-notifications', is_flag=True, help="Disable notifications.") +def run(add_delay=2.5, no_search=False, no_notifications=False): # add tasks to repeat - schedule.every(cfg.automatic.movies.interval).minutes.do(automatic_movies, add_delay, no_search) - schedule.every(cfg.automatic.shows.interval).minutes.do(automatic_shows, add_delay, no_search) + schedule.every(cfg.automatic.movies.interval).minutes.do(automatic_movies, add_delay, no_search, + not no_notifications) + schedule.every(cfg.automatic.shows.interval).minutes.do(automatic_shows, add_delay, no_search, not no_notifications) # run schedule log.info("Automatic mode is now running...")