mirror of
https://github.com/meisnate12/Plex-Meta-Manager
synced 2024-11-10 06:54:21 +00:00
added arrapi
This commit is contained in:
parent
1d7d2a972c
commit
8f491e70c2
4 changed files with 59 additions and 227 deletions
|
@ -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("")
|
||||
|
|
|
@ -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
|
||||
try:
|
||||
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}")
|
||||
|
|
|
@ -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
|
||||
try:
|
||||
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}")
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue