From the Quality Definitions (File Sizes) page of the TRaSH guides, you can now sync the anime or non-anime quality profile to your Sonarr instance. There is also a hybrid mode but that is not yet implemented.pull/5/head
parent
7a5969874a
commit
c5cc2035a8
@ -0,0 +1,268 @@
|
||||
[{
|
||||
"quality": {
|
||||
"id": 0,
|
||||
"name": "Unknown",
|
||||
"source": "unknown",
|
||||
"resolution": 0
|
||||
},
|
||||
"title": "Unknown",
|
||||
"weight": 1,
|
||||
"minSize": 1.0,
|
||||
"maxSize": 227.5,
|
||||
"id": 1
|
||||
},
|
||||
{
|
||||
"quality": {
|
||||
"id": 1,
|
||||
"name": "SDTV",
|
||||
"source": "television",
|
||||
"resolution": 480
|
||||
},
|
||||
"title": "SDTV",
|
||||
"weight": 2,
|
||||
"minSize": 2.0,
|
||||
"maxSize": 100.0,
|
||||
"id": 2
|
||||
},
|
||||
{
|
||||
"quality": {
|
||||
"id": 12,
|
||||
"name": "WEBRip-480p",
|
||||
"source": "webRip",
|
||||
"resolution": 480
|
||||
},
|
||||
"title": "WEBRip-480p",
|
||||
"weight": 3,
|
||||
"minSize": 2.0,
|
||||
"maxSize": 100.0,
|
||||
"id": 3
|
||||
},
|
||||
{
|
||||
"quality": {
|
||||
"id": 8,
|
||||
"name": "WEBDL-480p",
|
||||
"source": "web",
|
||||
"resolution": 480
|
||||
},
|
||||
"title": "WEBDL-480p",
|
||||
"weight": 3,
|
||||
"minSize": 2.0,
|
||||
"maxSize": 100.0,
|
||||
"id": 4
|
||||
},
|
||||
{
|
||||
"quality": {
|
||||
"id": 2,
|
||||
"name": "DVD",
|
||||
"source": "dvd",
|
||||
"resolution": 480
|
||||
},
|
||||
"title": "DVD",
|
||||
"weight": 4,
|
||||
"minSize": 2.0,
|
||||
"maxSize": 100.0,
|
||||
"id": 5
|
||||
},
|
||||
{
|
||||
"quality": {
|
||||
"id": 13,
|
||||
"name": "Bluray-480p",
|
||||
"source": "bluray",
|
||||
"resolution": 480
|
||||
},
|
||||
"title": "Bluray-480p",
|
||||
"weight": 5,
|
||||
"minSize": 2.0,
|
||||
"maxSize": 100.0,
|
||||
"id": 6
|
||||
},
|
||||
{
|
||||
"quality": {
|
||||
"id": 4,
|
||||
"name": "HDTV-720p",
|
||||
"source": "television",
|
||||
"resolution": 720
|
||||
},
|
||||
"title": "HDTV-720p",
|
||||
"weight": 6,
|
||||
"minSize": 3.0,
|
||||
"maxSize": 125.0,
|
||||
"id": 7
|
||||
},
|
||||
{
|
||||
"quality": {
|
||||
"id": 9,
|
||||
"name": "HDTV-1080p",
|
||||
"source": "television",
|
||||
"resolution": 1080
|
||||
},
|
||||
"title": "HDTV-1080p",
|
||||
"weight": 7,
|
||||
"minSize": 4.0,
|
||||
"maxSize": 125.0,
|
||||
"id": 8
|
||||
},
|
||||
{
|
||||
"quality": {
|
||||
"id": 10,
|
||||
"name": "Raw-HD",
|
||||
"source": "televisionRaw",
|
||||
"resolution": 1080
|
||||
},
|
||||
"title": "Raw-HD",
|
||||
"weight": 8,
|
||||
"minSize": 4.0,
|
||||
"id": 9
|
||||
},
|
||||
{
|
||||
"quality": {
|
||||
"id": 14,
|
||||
"name": "WEBRip-720p",
|
||||
"source": "webRip",
|
||||
"resolution": 720
|
||||
},
|
||||
"title": "WEBRip-720p",
|
||||
"weight": 9,
|
||||
"minSize": 3.0,
|
||||
"maxSize": 130.0,
|
||||
"id": 10
|
||||
},
|
||||
{
|
||||
"quality": {
|
||||
"id": 5,
|
||||
"name": "WEBDL-720p",
|
||||
"source": "web",
|
||||
"resolution": 720
|
||||
},
|
||||
"title": "WEBDL-720p",
|
||||
"weight": 9,
|
||||
"minSize": 3.0,
|
||||
"maxSize": 130.0,
|
||||
"id": 11
|
||||
},
|
||||
{
|
||||
"quality": {
|
||||
"id": 6,
|
||||
"name": "Bluray-720p",
|
||||
"source": "bluray",
|
||||
"resolution": 720
|
||||
},
|
||||
"title": "Bluray-720p",
|
||||
"weight": 10,
|
||||
"minSize": 4.0,
|
||||
"maxSize": 130.0,
|
||||
"id": 12
|
||||
},
|
||||
{
|
||||
"quality": {
|
||||
"id": 15,
|
||||
"name": "WEBRip-1080p",
|
||||
"source": "webRip",
|
||||
"resolution": 1080
|
||||
},
|
||||
"title": "WEBRip-1080p",
|
||||
"weight": 11,
|
||||
"minSize": 4.0,
|
||||
"maxSize": 130.0,
|
||||
"id": 13
|
||||
},
|
||||
{
|
||||
"quality": {
|
||||
"id": 3,
|
||||
"name": "WEBDL-1080p",
|
||||
"source": "web",
|
||||
"resolution": 1080
|
||||
},
|
||||
"title": "WEBDL-1080p",
|
||||
"weight": 11,
|
||||
"minSize": 4.0,
|
||||
"maxSize": 130.0,
|
||||
"id": 14
|
||||
},
|
||||
{
|
||||
"quality": {
|
||||
"id": 7,
|
||||
"name": "Bluray-1080p",
|
||||
"source": "bluray",
|
||||
"resolution": 1080
|
||||
},
|
||||
"title": "Bluray-1080p",
|
||||
"weight": 12,
|
||||
"minSize": 4.0,
|
||||
"maxSize": 155.0,
|
||||
"id": 15
|
||||
},
|
||||
{
|
||||
"quality": {
|
||||
"id": 20,
|
||||
"name": "Bluray-1080p Remux",
|
||||
"source": "blurayRaw",
|
||||
"resolution": 1080
|
||||
},
|
||||
"title": "Bluray-1080p Remux",
|
||||
"weight": 13,
|
||||
"minSize": 35.0,
|
||||
"id": 16
|
||||
},
|
||||
{
|
||||
"quality": {
|
||||
"id": 16,
|
||||
"name": "HDTV-2160p",
|
||||
"source": "television",
|
||||
"resolution": 2160
|
||||
},
|
||||
"title": "HDTV-2160p",
|
||||
"weight": 14,
|
||||
"minSize": 35.0,
|
||||
"maxSize": 199.9,
|
||||
"id": 17
|
||||
},
|
||||
{
|
||||
"quality": {
|
||||
"id": 17,
|
||||
"name": "WEBRip-2160p",
|
||||
"source": "webRip",
|
||||
"resolution": 2160
|
||||
},
|
||||
"title": "WEBRip-2160p",
|
||||
"weight": 15,
|
||||
"minSize": 59.0,
|
||||
"id": 18
|
||||
},
|
||||
{
|
||||
"quality": {
|
||||
"id": 18,
|
||||
"name": "WEBDL-2160p",
|
||||
"source": "web",
|
||||
"resolution": 2160
|
||||
},
|
||||
"title": "WEBDL-2160p",
|
||||
"weight": 15,
|
||||
"minSize": 59.0,
|
||||
"id": 19
|
||||
},
|
||||
{
|
||||
"quality": {
|
||||
"id": 19,
|
||||
"name": "Bluray-2160p",
|
||||
"source": "bluray",
|
||||
"resolution": 2160
|
||||
},
|
||||
"title": "Bluray-2160p",
|
||||
"weight": 16,
|
||||
"minSize": 59.0,
|
||||
"id": 20
|
||||
},
|
||||
{
|
||||
"quality": {
|
||||
"id": 21,
|
||||
"name": "Bluray-2160p Remux",
|
||||
"source": "blurayRaw",
|
||||
"resolution": 2160
|
||||
},
|
||||
"title": "Bluray-2160p Remux",
|
||||
"weight": 17,
|
||||
"minSize": 58.2,
|
||||
"id": 21
|
||||
}
|
||||
]
|
@ -1,15 +1,51 @@
|
||||
import argparse
|
||||
|
||||
# class args: pass
|
||||
class NoAction(argparse.Action):
|
||||
def __init__(self, **kwargs):
|
||||
kwargs.setdefault('default', argparse.SUPPRESS)
|
||||
kwargs.setdefault('nargs', 0)
|
||||
super(NoAction, self).__init__(**kwargs)
|
||||
|
||||
def setup_and_parse_args():
|
||||
argparser = argparse.ArgumentParser(description='Automatically mirror TRaSH guides to your *darr instance.')
|
||||
argparser.add_argument('--preview', help='Only display the processed markdown results and nothing else.',
|
||||
def __call__(self, parser, namespace, values, option_string=None):
|
||||
pass
|
||||
|
||||
def add_choices_argument(parser, variable_name, help_text, choices: dict):
|
||||
parser.register('action', 'none', NoAction)
|
||||
parser.add_argument(variable_name, help=help_text, metavar=variable_name.upper(), choices=choices.keys())
|
||||
group = parser.add_argument_group(title=f'Choices for {variable_name.upper()}')
|
||||
for choice,choice_help in choices.items():
|
||||
group.add_argument(choice, help=choice_help, action='none')
|
||||
|
||||
def setup_and_parse_args(args_override=None):
|
||||
parent_p = argparse.ArgumentParser(add_help=False)
|
||||
parent_p.add_argument('--base-uri', help='The base URL for your Sonarr/Radarr instance, for example `http://localhost:8989`. Required if not doing --preview.')
|
||||
parent_p.add_argument('--api-key', help='Your API key. Required if not doing --preview.')
|
||||
parent_p.add_argument('--preview', help='Only display the processed markdown results and nothing else.',
|
||||
action='store_true')
|
||||
argparser.add_argument('--debug', help='Display additional logs useful for development/debug purposes',
|
||||
parent_p.add_argument('--debug', help='Display additional logs useful for development/debug purposes',
|
||||
action='store_true')
|
||||
argparser.add_argument('--tags', help='Tags to assign to the profiles that are created or updated. These tags will replace any existing tags when updating profiles.',
|
||||
|
||||
parser = argparse.ArgumentParser(description='Automatically mirror TRaSH guides to your Sonarr/Radarr instance.')
|
||||
subparsers = parser.add_subparsers(description='Operations specific to different parts of the TRaSH guides', dest='subcommand')
|
||||
|
||||
# Subcommands for 'profile'
|
||||
profile_p = subparsers.add_parser('profile', help='Pages of the guide that define profiles',
|
||||
parents=[parent_p])
|
||||
add_choices_argument(profile_p, 'type', 'The specific guide type/page to pull data from.', {
|
||||
'sonarr:anime': 'The anime release profile for Sonarr v3',
|
||||
'sonarr:web-dl': 'The WEB-DL release profile for Sonarr v3'
|
||||
})
|
||||
profile_p.add_argument('--tags', help='Tags to assign to the profiles that are created or updated. These tags will replace any existing tags when updating profiles.',
|
||||
nargs='+')
|
||||
argparser.add_argument('base_uri', help='The base URL for your Sonarr instance, for example `http://localhost:8989`.')
|
||||
argparser.add_argument('api_key', help='Your API key.')
|
||||
return argparser.parse_args()#namespace=args)
|
||||
|
||||
# Subcommands for 'quality'
|
||||
quality_p = subparsers.add_parser('quality', help='Pages in the guide that provide quality definitions',
|
||||
parents=[parent_p])
|
||||
add_choices_argument(quality_p, 'type', 'The specific guide type/page to pull data from.', {
|
||||
'sonarr:anime': 'Choose the Sonarr quality definition best fit for anime',
|
||||
'sonarr:non-anime': 'Choose the Sonarr quality definition best fit for tv shows (non-anime)',
|
||||
'sonarr:hybrid': 'The script will generate a Sonarr quality definition that works best for all show types'
|
||||
})
|
||||
|
||||
return parser.parse_args(args=args_override)
|
||||
|
@ -0,0 +1,31 @@
|
||||
import requests
|
||||
import re
|
||||
from collections import defaultdict
|
||||
|
||||
header_regex = re.compile(r'^#+')
|
||||
table_row_regex = re.compile(r'\| *(.*?) *\| *([\d.]+) *\| *([\d.]+) *\|')
|
||||
|
||||
# --------------------------------------------------------------------------------------------------
|
||||
def get_markdown():
|
||||
trash_anime_markdown_url = 'https://raw.githubusercontent.com/TRaSH-/Guides/master/docs/Sonarr/V3/Sonarr-Quality-Settings-File-Size.md'
|
||||
response = requests.get(trash_anime_markdown_url)
|
||||
return response.content.decode('utf8')
|
||||
|
||||
# --------------------------------------------------------------------------------------------------
|
||||
def parse_markdown(logger, markdown_content):
|
||||
results = defaultdict(list)
|
||||
table = None
|
||||
|
||||
for line in markdown_content.splitlines():
|
||||
if not line:
|
||||
continue
|
||||
|
||||
if header_regex.search(line):
|
||||
category = 'sonarr:anime' if 'anime' in line.lower() else 'sonarr:non-anime'
|
||||
table = results[category]
|
||||
if len(table) > 0:
|
||||
table = None
|
||||
elif (match := table_row_regex.search(line)) and table is not None:
|
||||
table.append((match.group(1), float(match.group(2)), float(match.group(3))))
|
||||
|
||||
return results
|
@ -0,0 +1,39 @@
|
||||
## Sonarr Quality Definitions
|
||||
|
||||
| Quality | Minimum | Maximum |
|
||||
| ------------------ | ------- | ------- |
|
||||
| HDTV-720p | 17.9 | 67.5 |
|
||||
| HDTV-1080p | 20 | 137.3 |
|
||||
| WEBRip-720p | 20 | 137.3 |
|
||||
| WEBDL-720p | 20 | 137.3 |
|
||||
| Bluray-720p | 34.9 | 137.3 |
|
||||
| WEBRip-1080p | 22 | 137.3 |
|
||||
| WEBDL-1080p | 22 | 137.3 |
|
||||
| Bluray-1080p | 50.4 | 227 |
|
||||
| Bluray-1080p Remux | 69.1 | 400 |
|
||||
| HDTV-2160p | 84.5 | 350 |
|
||||
| WEBRip-2160p | 84.5 | 350 |
|
||||
| WEBDL-2160p | 84.5 | 350 |
|
||||
| Bluray-2160p | 94.6 | 400 |
|
||||
| Bluray-2160p Remux | 204.4 | 400 |
|
||||
|
||||
------
|
||||
|
||||
### Sonarr Quality Definitions - Anime (Work in Progress)
|
||||
|
||||
| Quality | Minimum | Maximum |
|
||||
| ------------------ | ------- | ------- |
|
||||
| HDTV-720p | 2.3 | 51.4 |
|
||||
| HDTV-1080p | 2.3 | 100 |
|
||||
| WEBRip-720p | 4.3 | 100 |
|
||||
| WEBDL-720p | 4.3 | 51.4 |
|
||||
| Bluray-720p | 4.3 | 102.2 |
|
||||
| WEBRip-1080p | 4.5 | 257.4 |
|
||||
| WEBDL-1080p | 4.3 | 253.6 |
|
||||
| Bluray-1080p | 4.3 | 258.1 |
|
||||
| Bluray-1080p Remux | 0 | 400 |
|
||||
| HDTV-2160p | 84.5 | 350 |
|
||||
| WEBRip-2160p | 84.5 | 350 |
|
||||
| WEBDL-2160p | 84.5 | 350 |
|
||||
| Bluray-2160p | 94.6 | 400 |
|
||||
| Bluray-2160p Remux | 204.4 | 400 |
|
@ -0,0 +1,55 @@
|
||||
import app.guide.quality as quality
|
||||
from pathlib import Path
|
||||
from tests.mock_logger import MockLogger
|
||||
|
||||
data_files = Path(__file__).parent / 'data'
|
||||
|
||||
def test_parse_markdown_complete_doc():
|
||||
md_file = data_files / 'test_parse_markdown_sonarr_quality_definitions.md'
|
||||
with open(md_file) as file:
|
||||
test_markdown = file.read()
|
||||
|
||||
results = quality.parse_markdown(MockLogger(), test_markdown)
|
||||
|
||||
# Dictionary: Key (header name (anime or non-anime)), list (quality definitions table rows)
|
||||
assert len(results) == 2
|
||||
|
||||
table = results.get('sonarr:anime')
|
||||
assert len(table) == 14
|
||||
table_expected = [
|
||||
('HDTV-720p', 2.3, 51.4),
|
||||
('HDTV-1080p', 2.3, 100.0),
|
||||
('WEBRip-720p', 4.3, 100.0),
|
||||
('WEBDL-720p', 4.3, 51.4),
|
||||
('Bluray-720p', 4.3, 102.2),
|
||||
('WEBRip-1080p', 4.5, 257.4),
|
||||
('WEBDL-1080p', 4.3, 253.6),
|
||||
('Bluray-1080p', 4.3, 258.1),
|
||||
('Bluray-1080p Remux', 0.0, 400.0),
|
||||
('HDTV-2160p', 84.5, 350.0),
|
||||
('WEBRip-2160p', 84.5, 350.0),
|
||||
('WEBDL-2160p', 84.5, 350.0),
|
||||
('Bluray-2160p', 94.6, 400.0),
|
||||
('Bluray-2160p Remux', 204.4, 400.0)
|
||||
]
|
||||
assert sorted(table) == sorted(table_expected)
|
||||
|
||||
table = results.get('sonarr:non-anime')
|
||||
assert len(table) == 14
|
||||
table_expected = [
|
||||
('HDTV-720p', 17.9, 67.5),
|
||||
('HDTV-1080p', 20.0, 137.3),
|
||||
('WEBRip-720p', 20.0, 137.3),
|
||||
('WEBDL-720p', 20.0, 137.3),
|
||||
('Bluray-720p', 34.9, 137.3),
|
||||
('WEBRip-1080p', 22.0, 137.3),
|
||||
('WEBDL-1080p', 22.0, 137.3),
|
||||
('Bluray-1080p', 50.4, 227.0),
|
||||
('Bluray-1080p Remux', 69.1, 400.0),
|
||||
('HDTV-2160p', 84.5, 350.0),
|
||||
('WEBRip-2160p', 84.5, 350.0),
|
||||
('WEBDL-2160p', 84.5, 350.0),
|
||||
('Bluray-2160p', 94.6, 400.0),
|
||||
('Bluray-2160p Remux', 204.4, 400.0)
|
||||
]
|
||||
assert sorted(table) == sorted(table_expected)
|
@ -0,0 +1,3 @@
|
||||
class MockLogger:
|
||||
def info(self, msg): pass
|
||||
def debug(self, msg): pass
|
@ -0,0 +1,19 @@
|
||||
import pytest
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
from app import cmd
|
||||
from tests.mock_logger import MockLogger
|
||||
|
||||
sys.path.insert(0, Path(__name__).parent.parent)
|
||||
import trash
|
||||
|
||||
class TestEntrypoint:
|
||||
logger = MockLogger()
|
||||
|
||||
@staticmethod
|
||||
def test_throw_without_required_arguments():
|
||||
with pytest.raises(ValueError):
|
||||
args = cmd.setup_and_parse_args(['profile', 'sonarr:anime', '--base-uri', 'value'])
|
||||
trash.process_sonarr_profile(args, TestEntrypoint.logger)
|
||||
|
@ -1,69 +1,94 @@
|
||||
import requests
|
||||
from packaging import version # pip install packaging
|
||||
|
||||
from app import guide
|
||||
from app.api.sonarr import Sonarr
|
||||
from app.guide import anime, utils
|
||||
from app.guide import anime, utils, quality
|
||||
from app.cmd import setup_and_parse_args
|
||||
from app.logger import Logger
|
||||
|
||||
def process_sonarr_profile(args, logger):
|
||||
profiles = anime.parse_markdown(logger, anime.get_trash_anime_markdown())
|
||||
|
||||
# A few false-positive profiles are added sometimes. We filter these out by checking if they
|
||||
# actually have meaningful data attached to them, such as preferred terms. If they are mostly empty,
|
||||
# we remove them here.
|
||||
utils.filter_profiles(profiles)
|
||||
|
||||
if args.preview:
|
||||
utils.print_terms_and_scores(profiles)
|
||||
exit(0)
|
||||
|
||||
sonarr = Sonarr(args, logger)
|
||||
|
||||
# If tags were provided, ensure they exist. Tags that do not exist are added first, so that we
|
||||
# may specify them with the release profile request payload.
|
||||
tag_ids = []
|
||||
if args.tags:
|
||||
tags = sonarr.get_tags()
|
||||
tags = sonarr.create_missing_tags(tags, args.tags[:])
|
||||
logger.debug(f'Tags JSON: {tags}')
|
||||
|
||||
# Get a list of IDs that we can pass along with the request to update/create release
|
||||
# profiles
|
||||
tag_ids = [t['id'] for t in tags if t['label'] in args.tags]
|
||||
logger.debug(f'Tag IDs: {tag_ids}')
|
||||
|
||||
# Obtain all of the existing release profiles first. If any were previously created by our script
|
||||
# here, we favor replacing those instead of creating new ones, which would just be mostly duplicates
|
||||
# (but with some differences, since there have likely been updates since the last run).
|
||||
existing_profiles = sonarr.get_release_profiles()
|
||||
|
||||
for name, profile in profiles.items():
|
||||
new_profile_name = f'[Trash] Anime - {name}'
|
||||
profile_to_update = guide.utils.find_existing_profile(new_profile_name, existing_profiles)
|
||||
|
||||
if profile_to_update:
|
||||
print(f'Updating existing profile: {new_profile_name}')
|
||||
sonarr.update_existing_profile(profile_to_update, profile, tag_ids)
|
||||
else:
|
||||
print(f'Creating new profile: {new_profile_name}')
|
||||
sonarr.create_release_profile(new_profile_name, profile, tag_ids)
|
||||
|
||||
def process_sonarr_quality(args, logger):
|
||||
guide_definitions = quality.parse_markdown(logger, quality.get_markdown())
|
||||
|
||||
if args.type == 'sonarr:hybrid':
|
||||
raise ValueError('Hybrid profile not implemented yet')
|
||||
|
||||
selected_definition = guide_definitions.get(args.type)
|
||||
|
||||
if args.preview:
|
||||
utils.quality_preview(args.type, selected_definition)
|
||||
exit(0)
|
||||
|
||||
print(f'Updating quality definition using {args.type}')
|
||||
sonarr = Sonarr(args, logger)
|
||||
definition = sonarr.get_quality_definition()
|
||||
sonarr.update_quality_definition(definition, selected_definition)
|
||||
|
||||
def main():
|
||||
args = setup_and_parse_args()
|
||||
logger = Logger(args)
|
||||
if args.subcommand == 'profile':
|
||||
if args.type.startswith('sonarr:'):
|
||||
process_sonarr_profile(args, logger)
|
||||
elif args.type.startswith('radarr:'):
|
||||
raise NotImplementedError('Radarr guide support is not implemented yet')
|
||||
|
||||
elif args.subcommand == 'quality':
|
||||
if args.type.startswith('sonarr:'):
|
||||
process_sonarr_quality(args, logger)
|
||||
elif args.type.startswith('radarr:'):
|
||||
raise NotImplementedError('Radarr quality support is not implemented yet')
|
||||
|
||||
if __name__ == '__main__':
|
||||
try:
|
||||
args = setup_and_parse_args()
|
||||
logger = Logger(args)
|
||||
sonarr = Sonarr(args, logger)
|
||||
|
||||
profiles = anime.parse_markdown(logger, anime.get_trash_anime_markdown())
|
||||
|
||||
# A few false-positive profiles are added sometimes. We filter these out by checking if they
|
||||
# actually have meaningful data attached to them, such as preferred terms. If they are mostly empty,
|
||||
# we remove them here.
|
||||
utils.filter_profiles(profiles)
|
||||
|
||||
if args.preview:
|
||||
utils.print_terms_and_scores(profiles)
|
||||
exit(0)
|
||||
|
||||
# Since this script requires a specific version of v3 Sonarr that implements name support for
|
||||
# release profiles, we perform that version check here and bail out if it does not meet a minimum
|
||||
# required version.
|
||||
minimum_version = version.parse('3.0.4.1098')
|
||||
version = sonarr.get_version()
|
||||
if version < minimum_version:
|
||||
print(f'ERROR: Your Sonarr version ({version}) does not meet the minimum required version of {minimum_version} to use this script.')
|
||||
exit(1)
|
||||
|
||||
# If tags were provided, ensure they exist. Tags that do not exist are added first, so that we
|
||||
# may specify them with the release profile request payload.
|
||||
tag_ids = []
|
||||
if args.tags:
|
||||
tags = sonarr.get_tags()
|
||||
tags = sonarr.create_missing_tags(tags, args.tags[:])
|
||||
logger.debug(f'Tags JSON: {tags}')
|
||||
|
||||
# Get a list of IDs that we can pass along with the request to update/create release
|
||||
# profiles
|
||||
tag_ids = [t['id'] for t in tags if t['label'] in args.tags]
|
||||
logger.debug(f'Tag IDs: {tag_ids}')
|
||||
|
||||
# Obtain all of the existing release profiles first. If any were previously created by our script
|
||||
# here, we favor replacing those instead of creating new ones, which would just be mostly duplicates
|
||||
# (but with some differences, since there have likely been updates since the last run).
|
||||
existing_profiles = sonarr.get_release_profiles()
|
||||
|
||||
for name, profile in profiles.items():
|
||||
new_profile_name = f'[Trash] Anime - {name}'
|
||||
profile_to_update = guide.utils.find_existing_profile(new_profile_name, existing_profiles)
|
||||
|
||||
if profile_to_update:
|
||||
print(f'Updating existing profile: {new_profile_name}')
|
||||
sonarr.update_existing_profile(profile_to_update, profile, tag_ids)
|
||||
else:
|
||||
print(f'Creating new profile: {new_profile_name}')
|
||||
sonarr.create_release_profile(new_profile_name, profile, tag_ids)
|
||||
|
||||
main()
|
||||
except requests.exceptions.HTTPError as e:
|
||||
print(e)
|
||||
if error_msg := Sonarr.get_error_message(e.response):
|
||||
print(f'Response Message: {error_msg}')
|
||||
exit(1)
|
||||
except Exception as e:
|
||||
print(f'ERROR: {e}')
|
||||
exit(1)
|
Loading…
Reference in new issue