Merge branch 'develop'

pull/8/head
l3uddz 7 years ago
commit 25c7a30996

1
.gitignore vendored

@ -31,5 +31,6 @@ __pycache__/
# PyInstaller
build/
dist/
*.manifest
*.spec

@ -1,5 +1,5 @@
# Traktarr
Script to add new TV series & movies to Sonarr/Radarr based on Trakt lists.
Script to add new shows & movies to Sonarr/Radarr based on Trakt lists.
# Requirements
1. Python 3.5 or higher (`sudo apt install python3 python3-pip`).
@ -127,21 +127,21 @@ Options:
```
## TV Shows
## Shows
```
Usage: python3 traktarr.py shows [OPTIONS]
Add new series to Sonarr.
Add new shows to Sonarr.
Options:
-t, --list-type [anticipated|trending|popular]
Trakt list to process. [required]
-l, --add-limit INTEGER Limit number of series added to Sonarr.
-l, --add-limit INTEGER Limit number of shows added to Sonarr.
[default: 0]
-d, --add-delay FLOAT Seconds between each add request to Sonarr.
[default: 2.5]
--no-search Disable search when adding series to Sonarr.
--no-search Disable search when adding shows to Sonarr.
--help Show this message and exit.
```

@ -46,12 +46,12 @@ class Sonarr:
if req.status_code == 200:
resp_json = req.json()
log.debug("Found %d series", len(resp_json))
log.debug("Found %d shows", len(resp_json))
return resp_json
else:
log.error("Failed to retrieve all series, request response: %d", req.status_code)
log.error("Failed to retrieve all shows, request response: %d", req.status_code)
except Exception:
log.exception("Exception retrieving series: ")
log.exception("Exception retrieving show: ")
return None
@backoff.on_predicate(backoff.expo, lambda x: x is None, max_tries=4, on_backoff=backoff_handler)
@ -64,6 +64,7 @@ class Sonarr:
if req.status_code == 200:
resp_json = req.json()
log.debug("Found %d quality profiles", len(resp_json))
for profile in resp_json:
if profile['name'].lower() == profile_name.lower():
log.debug("Found id of %s profile: %d", profile_name, profile['id'])
@ -76,12 +77,57 @@ class Sonarr:
return None
@backoff.on_predicate(backoff.expo, lambda x: x is None, max_tries=4, on_backoff=backoff_handler)
def add_series(self, series_tvdbid, series_title, series_title_slug, profile_id, root_folder, search_missing=False):
def get_tag_id(self, tag_name):
try:
# make request
req = requests.get(urljoin(self.server_url, 'api/tag'), headers=self.headers, timeout=30)
log.debug("Request URL: %s", req.url)
log.debug("Request Response: %d", req.status_code)
if req.status_code == 200:
resp_json = req.json()
log.debug("Found %d tags", len(resp_json))
for tag in resp_json:
if tag['label'].lower() == tag_name.lower():
log.debug("Found id of %s tag: %d", tag_name, tag['id'])
return tag['id']
log.debug("Tag %s with id %d did not match %s", tag['label'], tag['id'], tag_name)
else:
log.error("Failed to retrieve all tags, request response: %d", req.status_code)
except Exception:
log.exception("Exception retrieving id of tag %s: ", tag_name)
return None
@backoff.on_predicate(backoff.expo, lambda x: x is None, max_tries=4, on_backoff=backoff_handler)
def get_tags(self):
tags = {}
try:
# make request
req = requests.get(urljoin(self.server_url, 'api/tag'), headers=self.headers, timeout=30)
log.debug("Request URL: %s", req.url)
log.debug("Request Response: %d", req.status_code)
if req.status_code == 200:
resp_json = req.json()
log.debug("Found %d tags", len(resp_json))
for tag in resp_json:
tags[tag['label']] = tag['id']
return tags
else:
log.error("Failed to retrieve all tags, request response: %d", req.status_code)
except Exception:
log.exception("Exception retrieving tags: ")
return None
@backoff.on_predicate(backoff.expo, lambda x: x is None, max_tries=4, on_backoff=backoff_handler)
def add_series(self, series_tvdbid, series_title, series_title_slug, profile_id, root_folder, tag_ids=None,
search_missing=False):
try:
# generate payload
payload = {
'tvdbId': series_tvdbid, 'title': series_title, 'titleSlug': series_title_slug,
'qualityProfileId': profile_id, 'images': [],
'qualityProfileId': profile_id, 'tags': [] if not tag_ids or not isinstance(tag_ids, list) else tag_ids,
'images': [],
'seasons': [], 'seasonFolder': True,
'monitored': True, 'rootFolderPath': root_folder,
'addOptions': {'ignoreEpisodesWithFiles': False,
@ -105,5 +151,5 @@ class Sonarr:
log.error("Failed to add %s (%d), unexpected response:\n%s", series_title, series_tvdbid, req.text)
return False
except Exception:
log.exception("Exception adding series %s (%d): ", series_title, series_tvdbid)
log.exception("Exception adding show %s (%d): ", series_title, series_tvdbid)
return None

@ -16,7 +16,11 @@ base_config = {
'url': 'http://localhost:8989',
'api_key': '',
'profile': 'HD-1080p',
'root_folder': '/tv/'
'root_folder': '/tv/',
'tags': {
'amzn': ['hbo', 'amc', 'usa network', 'tnt', 'starz', 'the cw', 'fx', 'fox', 'abc', 'nbc', 'cbs', 'tbs',
'amazon', 'syfy', 'cinemax']
}
},
'radarr': {
'url': 'http://localhost:7878',

@ -8,17 +8,29 @@ log = logger.get_logger(__name__)
# SONARR
############################################################
def sonarr_series_tag_id_from_network(profile_tags, network_tags, network):
try:
for tag_name, tag_networks in network_tags.items():
for tag_network in tag_networks:
if tag_network.lower() in network.lower() and tag_name.lower() in profile_tags:
log.debug("Using %s tag for network: %s", tag_name, network)
return [profile_tags[tag_name.lower()]]
except Exception:
log.exception("Exception determining tag to use for network %s: ", network)
return None
def sonarr_series_to_tvdb_dict(sonarr_series):
series = {}
try:
for tmp in sonarr_series:
if 'tvdbId' not in tmp:
log.debug("Could not handle series: %s", tmp['title'])
log.debug("Could not handle show: %s", tmp['title'])
continue
series[tmp['tvdbId']] = tmp
return series
except Exception:
log.exception("Exception processing sonarr series to tvdb dict: ")
log.exception("Exception processing Sonarr shows to TVDB dict: ")
return None
@ -47,11 +59,11 @@ def sonarr_remove_existing_series(sonarr_series, trakt_series):
new_series_list.append(tmp)
log.debug("Filtered %d trakt shows to %d shows that weren't already in Sonarr", len(trakt_series),
log.debug("Filtered %d Trakt shows to %d shows that weren't already in Sonarr", len(trakt_series),
len(new_series_list))
return new_series_list
except Exception:
log.exception("Exception removing existing series from trakt list: ")
log.exception("Exception removing existing shows from Trakt list: ")
return None
@ -133,7 +145,7 @@ def trakt_blacklisted_show_runtime(show, lowest_runtime):
blacklisted = True
elif int(show['show']['runtime']) < lowest_runtime:
log.debug("%s was blacklisted because it had a runtime of: %d", show['show']['title'],
show['movie']['runtime'])
show['show']['runtime'])
blacklisted = True
except Exception:
@ -173,7 +185,7 @@ def radarr_movies_to_tmdb_dict(radarr_movies):
movies[tmp['tmdbId']] = tmp
return movies
except Exception:
log.exception("Exception processing radarr movies to tmdb dict: ")
log.exception("Exception processing Radarr movies to TMDB dict: ")
return None
@ -202,11 +214,11 @@ def radarr_remove_existing_movies(radarr_movies, trakt_movies):
new_movies_list.append(tmp)
log.debug("Filtered %d trakt movies to %d movies that weren't already in Radarr", len(trakt_movies),
log.debug("Filtered %d Trakt movies to %d movies that weren't already in Radarr", len(trakt_movies),
len(new_movies_list))
return new_movies_list
except Exception:
log.exception("Exception removing existing movies from trakt list: ")
log.exception("Exception removing existing movies from Trakt list: ")
return None

@ -19,7 +19,7 @@ log = logger.get_logger('traktarr')
# Click
@click.group(help='Add new series/movies to Sonarr & Radarr from Trakt.')
@click.group(help='Add new shows & movies to Sonarr/Radarr from Trakt lists.')
def app():
pass
@ -28,12 +28,12 @@ def app():
# SHOWS
############################################################
@app.command(help='Add new series to Sonarr.')
@app.command(help='Add new shows to Sonarr.')
@click.option('--list-type', '-t', type=click.Choice(['anticipated', 'trending', 'popular']),
help='Trakt list to process.', required=True)
@click.option('--add-limit', '-l', default=0, help='Limit number of series added to Sonarr.', show_default=True)
@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 series to Sonarr.')
@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):
added_shows = 0
@ -61,13 +61,21 @@ def shows(list_type, add_limit=0, add_delay=2.5, no_search=False):
else:
log.info("Retrieved Profile ID for %s: %d", cfg.sonarr.profile, profile_id)
# retrieve profile tags
profile_tags = sonarr.get_tags()
if profile_tags is None:
log.error("Aborting due to failure to retrieve Tag ID's")
return
else:
log.info("Retrieved %d Tag ID's", len(profile_tags))
# get sonarr series list
sonarr_series_list = sonarr.get_series()
if not sonarr_series_list:
log.error("Aborting due to failure to retrieve Sonarr series list")
log.error("Aborting due to failure to retrieve Sonarr shows list")
return
else:
log.info("Retrieved Sonarr series list, series found: %d", len(sonarr_series_list))
log.info("Retrieved Sonarr shows list, shows found: %d", len(sonarr_series_list))
# get trakt series list
trakt_series_list = None
@ -81,23 +89,23 @@ def shows(list_type, add_limit=0, add_delay=2.5, no_search=False):
log.error("Aborting due to unknown Trakt list type")
return
if not trakt_series_list:
log.error("Aborting due to failure to retrieve Trakt %s series list", list_type)
log.error("Aborting due to failure to retrieve Trakt %s shows list", list_type)
return
else:
log.info("Retrieved Trakt %s series list, series found: %d", list_type, len(trakt_series_list))
log.info("Retrieved Trakt %s shows list, shows found: %d", list_type, len(trakt_series_list))
# build filtered series list without series that exist in sonarr
processed_series_list = helpers.sonarr_remove_existing_series(sonarr_series_list, trakt_series_list)
if not processed_series_list:
log.error("Aborting due to failure to remove existing Sonarr series from retrieved Trakt series list")
log.error("Aborting due to failure to remove existing Sonarr shows from retrieved Trakt shows list")
return
else:
log.info("Removed existing Sonarr series from Trakt series list, series left to process: %d",
log.info("Removed existing Sonarr shows from Trakt shows list, shows left to process: %d",
len(processed_series_list))
# sort filtered series list by highest votes
sorted_series_list = sorted(processed_series_list, key=lambda k: k['show']['votes'], reverse=True)
log.info("Sorted series list to process by highest votes")
log.info("Sorted shows list to process by highest votes")
# loop series_list
log.info("Processing list now...")
@ -109,13 +117,18 @@ def shows(list_type, add_limit=0, add_delay=2.5, no_search=False):
', '.join(series['show']['genres']), series['show']['network'],
series['show']['country'].upper())
# determine which tags to use when adding this series
use_tags = helpers.sonarr_series_tag_id_from_network(profile_tags, cfg.sonarr.tags,
series['show']['network'])
# add show to sonarr
if sonarr.add_series(series['show']['ids']['tvdb'], series['show']['title'],
series['show']['ids']['slug'], profile_id, cfg.sonarr.root_folder, not no_search):
log.info("ADDED %s (%d)", series['show']['title'], series['show']['year'])
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)
added_shows += 1
else:
log.error("FAILED adding %s (%d)", series['show']['title'], series['show']['year'])
log.error("FAILED adding %s (%d) with tags: %s", series['show']['title'], series['show']['year'],
use_tags)
# stop adding shows, if added_shows >= add_limit
if add_limit and added_shows >= add_limit:
@ -125,9 +138,9 @@ def shows(list_type, add_limit=0, add_delay=2.5, no_search=False):
time.sleep(add_delay)
except Exception:
log.exception("Exception while processing series %s: ", series['show']['title'])
log.exception("Exception while processing show %s: ", series['show']['title'])
log.info("Added %d new shows to Sonarr", added_shows)
log.info("Added %d new show(s) to Sonarr", added_shows)
@app.command(help='Add new movies to Radarr.')
@ -227,7 +240,7 @@ def movies(list_type, add_limit=0, add_delay=2.5, no_search=False):
except Exception:
log.exception("Exception while processing movie %s: ", movie['movie']['title'])
log.info("Added %d new movies to Radarr", added_movies)
log.info("Added %d new movie(s) to Radarr", added_movies)
############################################################

Loading…
Cancel
Save