added arrapi

This commit is contained in:
meisnate12 2021-06-13 21:51:30 -04:00
parent 1d7d2a972c
commit 8f491e70c2
4 changed files with 59 additions and 227 deletions

View file

@ -10,8 +10,8 @@ from modules.letterboxd import LetterboxdAPI
from modules.mal import MyAnimeListAPI
from modules.omdb import OMDbAPI
from modules.plex import PlexAPI
from modules.radarr import RadarrAPI
from modules.sonarr import SonarrAPI
from modules.radarr import Radarr
from modules.sonarr import Sonarr
from modules.tautulli import TautulliAPI
from modules.tmdb import TMDbAPI
from modules.trakttv import TraktAPI
@ -292,7 +292,6 @@ class Config:
self.general["radarr"] = {}
self.general["radarr"]["url"] = check_for_attribute(self.data, "url", parent="radarr", var_type="url", default_is_none=True)
self.general["radarr"]["token"] = check_for_attribute(self.data, "token", parent="radarr", default_is_none=True)
self.general["radarr"]["version"] = check_for_attribute(self.data, "version", parent="radarr", test_list=radarr_versions, default="v3")
self.general["radarr"]["add"] = check_for_attribute(self.data, "add", parent="radarr", var_type="bool", default=False)
self.general["radarr"]["root_folder_path"] = check_for_attribute(self.data, "root_folder_path", parent="radarr", default_is_none=True)
self.general["radarr"]["monitor"] = check_for_attribute(self.data, "monitor", parent="radarr", var_type="bool", default=True)
@ -304,7 +303,6 @@ class Config:
self.general["sonarr"] = {}
self.general["sonarr"]["url"] = check_for_attribute(self.data, "url", parent="sonarr", var_type="url", default_is_none=True)
self.general["sonarr"]["token"] = check_for_attribute(self.data, "token", parent="sonarr", default_is_none=True)
self.general["sonarr"]["version"] = check_for_attribute(self.data, "version", parent="sonarr", test_list=sonarr_versions, default="v3")
self.general["sonarr"]["add"] = check_for_attribute(self.data, "add", parent="sonarr", var_type="bool", default=False)
self.general["sonarr"]["root_folder_path"] = check_for_attribute(self.data, "root_folder_path", parent="sonarr", default_is_none=True)
self.general["sonarr"]["monitor"] = check_for_attribute(self.data, "monitor", parent="sonarr", test_list=sonarr_monitors, default="all")
@ -467,7 +465,6 @@ class Config:
try:
radarr_params["url"] = check_for_attribute(lib, "url", parent="radarr", var_type="url", default=self.general["radarr"]["url"], req_default=True, save=False)
radarr_params["token"] = check_for_attribute(lib, "token", parent="radarr", default=self.general["radarr"]["token"], req_default=True, save=False)
radarr_params["version"] = check_for_attribute(lib, "version", parent="radarr", test_list=radarr_versions, default=self.general["radarr"]["version"], save=False)
radarr_params["add"] = check_for_attribute(lib, "add", parent="radarr", var_type="bool", default=self.general["radarr"]["add"], save=False)
radarr_params["root_folder_path"] = check_for_attribute(lib, "root_folder_path", parent="radarr", default=self.general["radarr"]["root_folder_path"], req_default=True, save=False)
radarr_params["monitor"] = check_for_attribute(lib, "monitor", parent="radarr", var_type="bool", default=self.general["radarr"]["monitor"], save=False)
@ -475,7 +472,7 @@ class Config:
radarr_params["quality_profile"] = check_for_attribute(lib, "quality_profile", parent="radarr", default=self.general["radarr"]["quality_profile"], req_default=True, save=False)
radarr_params["tag"] = check_for_attribute(lib, "search", parent="radarr", var_type="lower_list", default=self.general["radarr"]["tag"], default_is_none=True, save=False)
radarr_params["search"] = check_for_attribute(lib, "search", parent="radarr", var_type="bool", default=self.general["radarr"]["search"], save=False)
library.Radarr = RadarrAPI(radarr_params)
library.Radarr = Radarr(radarr_params)
except Failed as e:
util.print_multiline(e, error=True)
logger.info("")
@ -491,7 +488,6 @@ class Config:
try:
sonarr_params["url"] = check_for_attribute(lib, "url", parent="sonarr", var_type="url", default=self.general["sonarr"]["url"], req_default=True, save=False)
sonarr_params["token"] = check_for_attribute(lib, "token", parent="sonarr", default=self.general["sonarr"]["token"], req_default=True, save=False)
sonarr_params["version"] = check_for_attribute(lib, "version", parent="sonarr", test_list=sonarr_versions, default=self.general["sonarr"]["version"], save=False)
sonarr_params["add"] = check_for_attribute(lib, "add", parent="sonarr", var_type="bool", default=self.general["sonarr"]["add"], save=False)
sonarr_params["root_folder_path"] = check_for_attribute(lib, "root_folder_path", parent="sonarr", default=self.general["sonarr"]["root_folder_path"], req_default=True, save=False)
sonarr_params["monitor"] = check_for_attribute(lib, "monitor", parent="sonarr", test_list=sonarr_monitors, default=self.general["sonarr"]["monitor"], save=False)
@ -505,7 +501,7 @@ class Config:
sonarr_params["tag"] = check_for_attribute(lib, "search", parent="sonarr", var_type="lower_list", default=self.general["sonarr"]["tag"], default_is_none=True, save=False)
sonarr_params["search"] = check_for_attribute(lib, "search", parent="sonarr", var_type="bool", default=self.general["sonarr"]["search"], save=False)
sonarr_params["cutoff_search"] = check_for_attribute(lib, "cutoff_search", parent="sonarr", var_type="bool", default=self.general["sonarr"]["cutoff_search"], save=False)
library.Sonarr = SonarrAPI(sonarr_params, library.Plex.language)
library.Sonarr = Sonarr(sonarr_params)
except Failed as e:
util.print_multiline(e, error=True)
logger.info("")

View file

@ -1,7 +1,8 @@
import logging, requests
import logging
from modules import util
from modules.util import Failed
from retrying import retry
from arrapi import RadarrAPI
from arrapi.exceptions import ArrException, Invalid
logger = logging.getLogger("Plex Meta Manager")
@ -12,121 +13,48 @@ availability_translation = {
"db": "preDB"
}
class RadarrAPI:
class Radarr:
def __init__(self, params):
self.url = params["url"]
self.token = params["token"]
self.version = params["version"]
self.base_url = f"{self.url}/api{'/v3' if self.version == 'v3' else ''}/"
try:
result = requests.get(f"{self.base_url}system/status", params={"apikey": f"{self.token}"}).json()
except Exception:
util.print_stacktrace()
raise Failed(f"Radarr Error: Could not connect to Radarr at {self.url}")
if "error" in result and result["error"] == "Unauthorized":
raise Failed("Radarr Error: Invalid API Key")
if "version" not in result:
raise Failed("Radarr Error: Unexpected Response Check URL")
self.api = RadarrAPI(self.url, self.token)
except ArrException as e:
raise Failed(e)
self.add = params["add"]
self.root_folder_path = params["root_folder_path"]
self.monitor = params["monitor"]
self.availability = params["availability"]
self.quality_profile_id = self.get_profile_id(params["quality_profile"])
self.quality_profile = params["quality_profile"]
self.tag = params["tag"]
self.tags = self.get_tags()
self.search = params["search"]
def get_profile_id(self, profile_name):
profiles = ""
for profile in self._get("qualityProfile" if self.version == "v3" else "profile"):
if len(profiles) > 0:
profiles += ", "
profiles += profile["name"]
if profile["name"] == profile_name:
return profile["id"]
raise Failed(f"Radarr Error: quality_profile: {profile_name} does not exist in radarr. Profiles available: {profiles}")
def get_tags(self):
return {tag["label"]: tag["id"] for tag in self._get("tag")}
def add_tags(self, tags):
added = False
for label in tags:
if str(label).lower() not in self.tags:
added = True
self._post("tag", {"label": str(label).lower()})
if added:
self.tags = self.get_tags()
def lookup(self, tmdb_id):
results = self._get("movie/lookup", params={"term": f"tmdb:{tmdb_id}"})
if results:
return results[0]
else:
raise Failed(f"Sonarr Error: TMDb ID: {tmdb_id} not found")
def add_tmdb(self, tmdb_ids, **options):
logger.info("")
util.separator(f"Adding to Radarr", space=False, border=False)
logger.info("")
logger.debug(f"TMDb IDs: {tmdb_ids}")
tag_nums = []
add_count = 0
logger.debug("")
folder = options["folder"] if "folder" in options else self.root_folder_path
monitor = options["monitor"] if "monitor" in options else self.monitor
availability = options["availability"] if "availability" in options else self.availability
quality_profile_id = self.get_profile_id(options["quality"]) if "quality" in options else self.quality_profile_id
availability = availability_translation[options["availability"] if "availability" in options else self.availability]
quality_profile = options["quality"] if "quality" in options else self.quality_profile
tags = options["tag"] if "tag" in options else self.tag
search = options["search"] if "search" in options else self.search
if tags:
self.add_tags(tags)
tag_nums = [self.tags[label.lower()] for label in tags if label.lower() in self.tags]
for tmdb_id in tmdb_ids:
try:
movie_info = self.lookup(tmdb_id)
except Failed as e:
logger.error(e)
continue
added, exists, invalid = self.api.add_multiple_movies(tmdb_ids, folder, quality_profile, monitor, search, availability, tags)
except Invalid as e:
raise Failed(f"Radarr Error: {e}")
poster_url = None
for image in movie_info["images"]:
if "coverType" in image and image["coverType"] == "poster" and "remoteUrl" in image:
poster_url = image["remoteUrl"]
if len(added) > 0:
for movie in added:
logger.info(f"Added to Radarr | {movie.tmdbId:<6} | {movie.title}")
logger.info(f"{len(added)} Movie{'s' if len(added) > 1 else ''} added to Radarr")
url_json = {
"title": movie_info["title"],
f"{'qualityProfileId' if self.version == 'v3' else 'profileId'}": quality_profile_id,
"year": int(movie_info["year"]),
"tmdbid": int(tmdb_id),
"titleslug": movie_info["titleSlug"],
"minimumAvailability": availability_translation[availability],
"monitored": monitor,
"rootFolderPath": folder,
"images": [{"covertype": "poster", "url": poster_url}],
"addOptions": {"searchForMovie": search}
}
if tag_nums:
url_json["tags"] = tag_nums
response = self._post("movie", url_json)
if response.status_code < 400:
logger.info(f"Added to Radarr | {tmdb_id:<6} | {movie_info['title']}")
add_count += 1
else:
try:
logger.error(f"Radarr Error: ({tmdb_id}) {movie_info['title']}: ({response.status_code}) {response.json()[0]['errorMessage']}")
except KeyError:
logger.debug(url_json)
logger.error(f"Radarr Error: {response.json()}")
logger.info(f"{add_count} Movie{'s' if add_count > 1 else ''} added to Radarr")
if len(exists) > 0:
for movie in exists:
logger.info(f"Already in Radarr | {movie.tmdbId:<6} | {movie.title}")
logger.info(f"{len(exists)} Movie{'s' if len(exists) > 1 else ''} already existing in Radarr")
@retry(stop_max_attempt_number=6, wait_fixed=10000)
def _get(self, url, params=None):
url_params = {"apikey": f"{self.token}"}
if params:
for param in params:
url_params[param] = params[param]
return requests.get(f"{self.base_url}{url}", params=url_params).json()
@retry(stop_max_attempt_number=6, wait_fixed=10000)
def _post(self, url, url_json):
return requests.post(f"{self.base_url}{url}", json=url_json, params={"apikey": f"{self.token}"})
for movie in invalid:
logger.info(f"Invalid TMDb ID | {movie}")

View file

@ -1,8 +1,8 @@
import logging, requests
from json.decoder import JSONDecodeError
import logging
from modules import util
from modules.util import Failed
from retrying import retry
from arrapi import SonarrAPI
from arrapi.exceptions import ArrException, Invalid
logger = logging.getLogger("Plex Meta Manager")
@ -18,149 +18,56 @@ monitor_translation = {
"none": "none"
}
class SonarrAPI:
def __init__(self, params, language):
class Sonarr:
def __init__(self, params):
self.url = params["url"]
self.token = params["token"]
self.version = params["version"]
self.base_url = f"{self.url}/api{'/v3/' if self.version == 'v3' else '/'}"
try:
result = requests.get(f"{self.base_url}system/status", params={"apikey": f"{self.token}"}).json()
except Exception:
util.print_stacktrace()
raise Failed(f"Sonarr Error: Could not connect to Sonarr at {self.url}")
if "error" in result and result["error"] == "Unauthorized":
raise Failed("Sonarr Error: Invalid API Key")
if "version" not in result:
raise Failed("Sonarr Error: Unexpected Response Check URL")
self.api = SonarrAPI(self.url, self.token)
except ArrException as e:
raise Failed(e)
self.add = params["add"]
self.root_folder_path = params["root_folder_path"]
self.monitor = params["monitor"]
self.quality_profile_id = self.get_profile_id(params["quality_profile"], "quality_profile")
self.quality_profile = params["quality_profile"]
self.language_profile_id = None
if self.version == "v3" and params["language_profile"] is not None:
self.language_profile_id = self.get_profile_id(params["language_profile"], "language_profile")
if self.language_profile_id is None:
self.language_profile_id = 1
self.language_profile = params["language_profile"]
self.series_type = params["series_type"]
self.season_folder = params["season_folder"]
self.tag = params["tag"]
self.tags = self.get_tags()
self.search = params["search"]
self.cutoff_search = params["cutoff_search"]
self.language = language
def get_profile_id(self, profile_name, profile_type):
profiles = ""
if profile_type == "quality_profile" and self.version == "v3":
endpoint = "qualityProfile"
elif profile_type == "language_profile":
endpoint = "languageProfile"
else:
endpoint = "profile"
for profile in self._get(endpoint):
if len(profiles) > 0:
profiles += ", "
profiles += profile["name"]
if profile["name"] == profile_name:
return profile["id"]
raise Failed(f"Sonarr Error: {profile_type}: {profile_name} does not exist in sonarr. Profiles available: {profiles}")
def get_tags(self):
return {tag["label"]: tag["id"] for tag in self._get("tag")}
def add_tags(self, tags):
added = False
for label in tags:
if str(label).lower() not in self.tags:
added = True
self._post("tag", {"label": str(label).lower()})
if added:
self.tags = self.get_tags()
def lookup(self, tvdb_id):
results = self._get("series/lookup", params={"term": f"tvdb:{tvdb_id}"})
if results:
return results[0]
else:
raise Failed(f"Sonarr Error: TVDb ID: {tvdb_id} not found")
def add_tvdb(self, tvdb_ids, **options):
logger.info("")
util.separator(f"Adding to Sonarr", space=False, border=False)
logger.info("")
logger.debug(f"TVDb IDs: {tvdb_ids}")
tag_nums = []
add_count = 0
logger.debug("")
folder = options["folder"] if "folder" in options else self.root_folder_path
monitor = options["monitor"] if "monitor" in options else self.monitor
quality_profile_id = self.get_profile_id(options["quality"], "quality_profile") if "quality" in options else self.quality_profile_id
language_profile_id = self.get_profile_id(options["language"], "language_profile") if "quality" in options else self.language_profile_id
monitor = monitor_translation[options["monitor"] if "monitor" in options else self.monitor]
quality_profile = options["quality"] if "quality" in options else self.quality_profile
language_profile = options["language"] if "language" in options else self.language_profile
language_profile = language_profile if self.api.v3 else 1
series = options["series"] if "series" in options else self.series_type
season = options["season"] if "season" in options else self.season_folder
tags = options["tag"] if "tag" in options else self.tag
search = options["search"] if "search" in options else self.search
cutoff_search = options["cutoff_search"] if "cutoff_search" in options else self.cutoff_search
if tags:
self.add_tags(tags)
tag_nums = [self.tags[label.lower()] for label in tags if label.lower() in self.tags]
for tvdb_id in tvdb_ids:
try:
show_info = self.lookup(tvdb_id)
except Failed as e:
logger.error(e)
continue
added, exists, invalid = self.api.add_multiple_series(tvdb_ids, folder, quality_profile, language_profile, monitor, season, search, cutoff_search, series, tags)
except Invalid as e:
raise Failed(f"Sonarr Error: {e}")
poster_url = None
for image in show_info["images"]:
if "coverType" in image and image["coverType"] == "poster" and "remoteUrl" in image:
poster_url = image["remoteUrl"]
if len(added) > 0:
for series in added:
logger.info(f"Added to Sonarr | {series.tvdbId:<6} | {series.title}")
logger.info(f"{len(added)} Series added to Sonarr")
url_json = {
"title": show_info["title"],
f"{'qualityProfileId' if self.version == 'v3' else 'profileId'}": quality_profile_id,
"languageProfileId": language_profile_id,
"tvdbId": int(tvdb_id),
"titleslug": show_info["titleSlug"],
"language": self.language,
"monitored": monitor != "none",
"seasonFolder": season,
"seriesType": series,
"rootFolderPath": folder,
"seasons": [],
"images": [{"covertype": "poster", "url": poster_url}],
"addOptions": {
"searchForMissingEpisodes": search,
"searchForCutoffUnmetEpisodes": cutoff_search,
"monitor": monitor_translation[monitor]
}
}
if tag_nums:
url_json["tags"] = tag_nums
response = self._post("series", url_json)
if response.status_code < 400:
logger.info(f"Added to Sonarr | {tvdb_id:<6} | {show_info['title']}")
add_count += 1
else:
try:
logger.error(f"Sonarr Error: ({tvdb_id}) {show_info['title']}: ({response.status_code}) {response.json()[0]['errorMessage']}")
except KeyError:
logger.debug(url_json)
logger.error(f"Sonarr Error: {response.json()}")
except JSONDecodeError:
logger.debug(url_json)
logger.error(f"Sonarr Error: {response}")
if len(exists) > 0:
for series in exists:
logger.info(f"Already in Sonarr | {series.tvdbId:<6} | {series.title}")
logger.info(f"{len(exists)} Series already existing in Sonarr")
logger.info(f"{add_count} Show{'s' if add_count > 1 else ''} added to Sonarr")
@retry(stop_max_attempt_number=6, wait_fixed=10000)
def _get(self, url, params=None):
url_params = {"apikey": f"{self.token}"}
if params:
for param in params:
url_params[param] = params[param]
return requests.get(f"{self.base_url}{url}", params=url_params).json()
@retry(stop_max_attempt_number=6, wait_fixed=10000)
def _post(self, url, url_json):
return requests.post(f"{self.base_url}{url}", json=url_json, params={"apikey": f"{self.token}"})
for series in invalid:
logger.info(f"Invalid TVDb ID | {series}")

View file

@ -3,6 +3,7 @@
PlexAPI==4.5.2
tmdbv3api==1.7.5
trakt.py==4.3.0
arrapi==1.0.0
# More common, flexible
lxml
requests>=2.4.2