diff --git a/README.md b/README.md index e44072a..6854568 100644 --- a/README.md +++ b/README.md @@ -63,6 +63,8 @@ Syncs two Radarr/Sonarr/Lidarr servers through the web API. Useful for syncing a quality_match = HD- # (Radarr only) regex match to only sync content that matches the set quality (ie if set to 1080p then only movies with matching downloaded quality of 1080p will be synced) tag_filter = Horror # (Sonarr/Radarr v3 only) sync movies by tag name (seperate multiple tags by comma (no spaces) ie horror,comedy,action) tag_filter_id = 2 # (Sonarr/Radarr v3 only) sync movies by tag id (seperate multiple tags by comma (no spaces) ie 2,3,4) + blacklist = movie-name-12,movie-name-43,432534,8e38819d-71be-9e7d-b41d-f1df91b01d3f # comma seperated list of content slugs OR IDs you want to never sync from A to B (no spaces) + # the slug is the part of the URL after "/movies/" (for Radarr), "/series/" (for Sonarr), or "/artist/" (for Lidarr) [*arrB] url = http://127.0.0.1:8080 @@ -161,7 +163,7 @@ docker run -it --rm --name syncarr -e RADARR_A_URL=https://4k.example.com:443 -e **Notes** * You can also specify the `PROFILE_ID` directly through the `*ARR_A_PROFILE_ID` and `*ARR_B_PROFILE_ID` ENV variables. -To filter by profile in docker use `ARR_A_PROFILE_FILTER` or `ARR_A_PROFILE_FILTER_ID` ENV variables. (same for `*arr_B` in bidirectional sync) +To filter by profile in docker use `*ARR_A_PROFILE_FILTER` or `*ARR_A_PROFILE_FILTER_ID` ENV variables. (same for `*arr_B` in bidirectional sync) * Language for new content (Sonarr v3 only) can be set by `SONARR_B_LANGUAGE` or `SONARR_B_LANGUAGE_ID` (and `SONARR_B` if bidirectional sync) * Set bidirectional sync with `SYNCARR_BIDIRECTIONAL_SYNC=1` (default 0) * Set disable auto searching on new content with `SYNCARR_AUTO_SEARCH=0` (default 1) @@ -169,6 +171,7 @@ To filter by profile in docker use `ARR_A_PROFILE_FILTER` or `ARR_A_PROFILE_FILT * Match regex quality profiles with `*ARR_A_QUALITY_MATCH` or `*ARR_B_QUALITY_MATCH` * Filter by tag names or ids with `*ARR_A_TAG_FILTER` / `*ARR_B_TAG_FILTER` or `*ARR_A_TAG_FILTER_ID` / `*ARR_B_TAG_FILTER_ID` * Enable test mode with `SYNCARR_TEST_RUN` +* add blacklist with `*ARR_A_BLACKLIST` and `**ARR_B_BLACKLIST` --- ## Troubleshooting diff --git a/config.py b/config.py index bd4dce2..106f501 100644 --- a/config.py +++ b/config.py @@ -80,6 +80,7 @@ radarrA_profile_filter_id = get_config_value('RADARR_A_PROFILE_FILTER_ID', 'prof radarrA_language = get_config_value('RADARR_A_LANGUAGE', 'language', 'radarrA') radarrA_language_id = get_config_value('RADARR_A_LANGUAGE_ID', 'language_id', 'radarrA') radarrA_quality_match = get_config_value('RADARR_A_QUALITY_MATCH', 'quality_match', 'radarrA') +radarrA_blacklist = get_config_value('RADARR_A_BLACKLIST', 'blacklist', 'radarrA') radarrB_url = get_config_value('RADARR_B_URL', 'url', 'radarrB') radarrB_key = get_config_value('RADARR_B_KEY', 'key', 'radarrB') @@ -91,6 +92,7 @@ radarrB_profile_filter_id = get_config_value('RADARR_B_PROFILE_FILTER_ID', 'prof radarrB_language = get_config_value('RADARR_B_LANGUAGE', 'language', 'radarrB') radarrB_language_id = get_config_value('RADARR_B_LANGUAGE_ID', 'language_id', 'radarrB') radarrB_quality_match = get_config_value('RADARR_B_QUALITY_MATCH', 'quality_match', 'radarrB') +radarrB_blacklist = get_config_value('RADARR_B_BLACKLIST', 'blacklist', 'radarrB') # get config settings from ENV or config files for Sonarr sonarrA_url = get_config_value('SONARR_A_URL', 'url', 'sonarrA') @@ -105,6 +107,7 @@ sonarrA_tag_filter_id = get_config_value('SONARR_A_TAG_FILTER_ID', 'tag_filter_i sonarrA_language = get_config_value('SONARR_A_LANGUAGE', 'language', 'sonarrA') sonarrA_language_id = get_config_value('SONARR_A_LANGUAGE_ID', 'language_id', 'sonarrA') sonarrA_quality_match = get_config_value('SONARR_A_QUALITY_MATCH', 'quality_match', 'sonarrA') +sonarrA_blacklist = get_config_value('SONARR_A_BLACKLIST', 'blacklist', 'sonarrA') sonarrB_url = get_config_value('SONARR_B_URL', 'url', 'sonarrB') sonarrB_key = get_config_value('SONARR_B_KEY', 'key', 'sonarrB') @@ -118,6 +121,7 @@ sonarrB_tag_filter_id = get_config_value('SONARR_B_TAG_FILTER_ID', 'tag_filter_i sonarrB_language = get_config_value('SONARR_B_LANGUAGE', 'language', 'sonarrB') sonarrB_language_id = get_config_value('SONARR_B_LANGUAGE_ID', 'language_id', 'sonarrB') sonarrB_quality_match = get_config_value('SONARR_B_QUALITY_MATCH', 'quality_match', 'sonarrB') +sonarrB_blacklist = get_config_value('SONARR_B_BLACKLIST', 'blacklist', 'sonarrB') # get config settings from ENV or config files for Lidarr lidarrA_url = get_config_value('LIDARR_A_URL', 'url', 'lidarrA') @@ -130,6 +134,7 @@ lidarrA_profile_filter_id = get_config_value('LIDARR_A_PROFILE_FILTER_ID', 'prof lidarrA_language = get_config_value('LIDARR_A_LANGUAGE', 'language', 'lidarrA') lidarrA_language_id = get_config_value('LIDARR_A_LANGUAGE_ID', 'language_id', 'lidarrA') lidarrA_quality_match = get_config_value('LIDARR_A_QUALITY_MATCH', 'quality_match', 'lidarrA') +lidarrA_blacklist = get_config_value('LIDARR_A_BLACKLIST', 'blacklist', 'lidarrA') lidarrB_url = get_config_value('LIDARR_B_URL', 'url', 'lidarrB') lidarrB_key = get_config_value('LIDARR_B_KEY', 'key', 'lidarrB') @@ -141,7 +146,7 @@ lidarrB_profile_filter_id = get_config_value('LIDARR_B_PROFILE_FILTER_ID', 'prof lidarrB_language = get_config_value('LIDARR_B_LANGUAGE', 'language', 'lidarrB') lidarrB_language_id = get_config_value('LIDARR_B_LANGUAGE_ID', 'language_id', 'lidarrB') lidarrB_quality_match = get_config_value('LIDARR_B_QUALITY_MATCH', 'quality_match', 'lidarrB') - +lidarrB_blacklist = get_config_value('LIDARR_B_BLACKLIST', 'blacklist', 'lidarrB') # set to search if config not set sync_bidirectionally = get_config_value('SYNCARR_BIDIRECTIONAL_SYNC', 'bidirectional', 'general') @@ -244,6 +249,7 @@ instanceA_language_id = '' instanceA_quality_match = '' instanceA_tag_filter = '' instanceA_tag_filter_id = '' +instanceA_blacklist = '' instanceB_url = '' instanceB_key = '' @@ -257,7 +263,7 @@ instanceB_language_id = '' instanceB_quality_match = '' instanceB_tag_filter = '' instanceB_tag_filter_id = '' - +instanceB_blacklist = '' api_version = '' # we are going to detect what API version we are on tested_api_version = False # only get api version once @@ -285,6 +291,7 @@ if radarrA_url and radarrB_url: instanceA_language = radarrA_language instanceA_language_id = radarrA_language_id instanceA_quality_match = radarrA_quality_match + instanceA_blacklist = radarrA_blacklist instanceB_url = radarrB_url instanceB_key = radarrB_key @@ -296,6 +303,7 @@ if radarrA_url and radarrB_url: instanceB_language = radarrB_language instanceB_language_id = radarrB_language_id instanceB_quality_match = radarrB_quality_match + instanceB_blacklist = radarrB_blacklist api_version = V2_API_PATH # radarr v2 doesnt have version in api url api_content_path = 'movie' @@ -305,37 +313,6 @@ if radarrA_url and radarrB_url: content_id_key = 'tmdbId' is_radarr = True -elif lidarrA_url and lidarrB_url: - instanceA_url = lidarrA_url - instanceA_key = lidarrA_key - instanceA_path = lidarrA_path - instanceA_profile = lidarrA_profile - instanceA_profile_id = lidarrA_profile_id - instanceA_profile_filter = lidarrA_profile_filter - instanceA_profile_filter_id = lidarrA_profile_filter_id - instanceA_language = lidarrA_language - instanceA_language_id = lidarrA_language_id - instanceA_quality_match = lidarrA_quality_match - - instanceB_url = lidarrB_url - instanceB_key = lidarrB_key - instanceB_path = lidarrB_path - instanceB_profile = lidarrB_profile - instanceB_profile_id = lidarrB_profile_id - instanceB_profile_filter = lidarrB_profile_filter - instanceB_profile_filter_id = lidarrB_profile_filter_id - instanceB_language = lidarrB_language - instanceB_language_id = lidarrB_language_id - instanceB_quality_match = lidarrB_quality_match - - api_version = V1_API_PATH - api_content_path = 'artist' - api_profile_path = 'qualityprofile' - api_status_path = 'system/status' - - content_id_key = 'foreignArtistId' - is_lidarr = True - elif sonarrA_url and sonarrB_url: instanceA_url = sonarrA_url instanceA_key = sonarrA_key @@ -349,6 +326,7 @@ elif sonarrA_url and sonarrB_url: instanceA_tag_filter = sonarrA_tag_filter and sonarrA_tag_filter.split(',') instanceA_tag_filter_id = sonarrA_tag_filter_id and sonarrA_tag_filter_id.split(',') instanceA_quality_match = sonarrA_quality_match + instanceA_blacklist = sonarrA_blacklist instanceB_url = sonarrB_url instanceB_key = sonarrB_key @@ -362,6 +340,7 @@ elif sonarrA_url and sonarrB_url: instanceB_tag_filter = sonarrB_tag_filter and sonarrB_tag_filter.split(',') instanceB_tag_filter_id = sonarrB_tag_filter_id and sonarrB_tag_filter_id.split(',') instanceB_quality_match = sonarrB_quality_match + instanceB_blacklist = sonarrB_blacklist api_version = V3_API_PATH # for sonarr try v3 first api_content_path = 'series' @@ -373,6 +352,47 @@ elif sonarrA_url and sonarrB_url: content_id_key = 'tvdbId' is_sonarr = True +elif lidarrA_url and lidarrB_url: + instanceA_url = lidarrA_url + instanceA_key = lidarrA_key + instanceA_path = lidarrA_path + instanceA_profile = lidarrA_profile + instanceA_profile_id = lidarrA_profile_id + instanceA_profile_filter = lidarrA_profile_filter + instanceA_profile_filter_id = lidarrA_profile_filter_id + instanceA_language = lidarrA_language + instanceA_language_id = lidarrA_language_id + instanceA_quality_match = lidarrA_quality_match + instanceA_blacklist = lidarrA_blacklist + + instanceB_url = lidarrB_url + instanceB_key = lidarrB_key + instanceB_path = lidarrB_path + instanceB_profile = lidarrB_profile + instanceB_profile_id = lidarrB_profile_id + instanceB_profile_filter = lidarrB_profile_filter + instanceB_profile_filter_id = lidarrB_profile_filter_id + instanceB_language = lidarrB_language + instanceB_language_id = lidarrB_language_id + instanceB_quality_match = lidarrB_quality_match + instanceB_blacklist = lidarrB_blacklist + + api_version = V1_API_PATH + api_content_path = 'artist' + api_profile_path = 'qualityprofile' + api_status_path = 'system/status' + + content_id_key = 'foreignArtistId' + is_lidarr = True + + +# format blacklists +if instanceA_blacklist: + instanceA_blacklist = instanceA_blacklist.split(',') + +if instanceB_blacklist: + instanceB_blacklist = instanceB_blacklist.split(',') + ######################################################################################################################## # path generators @@ -448,6 +468,7 @@ logger.debug({ 'instanceA_tag_filter': instanceA_tag_filter, 'instanceA_tag_filter_id': instanceA_tag_filter_id, 'instanceA_quality_match': instanceA_quality_match, + 'instanceA_blacklist': instanceA_blacklist, 'instanceB_url': instanceB_url, 'instanceB_key': instanceB_key, @@ -461,6 +482,7 @@ logger.debug({ 'instanceB_tag_filter': instanceB_tag_filter, 'instanceB_tag_filter_id': instanceB_tag_filter_id, 'instanceB_quality_match': instanceB_quality_match, + 'instanceB_blacklist': instanceB_blacklist, 'api_content_path': api_content_path, 'api_profile_path': api_profile_path, diff --git a/index.py b/index.py index ceb4038..d6f49e6 100644 --- a/index.py +++ b/index.py @@ -14,12 +14,12 @@ from config import ( instanceA_url, instanceA_key, instanceA_path, instanceA_profile, instanceA_profile_id, instanceA_profile_filter, instanceA_profile_filter_id, instanceA_language_id, instanceA_language, instanceA_quality_match, - instanceA_tag_filter_id, instanceA_tag_filter, + instanceA_tag_filter_id, instanceA_tag_filter, instanceA_blacklist, instanceB_url, instanceB_key, instanceB_path, instanceB_profile, instanceB_profile_id, instanceB_profile_filter, instanceB_profile_filter_id, instanceB_language_id, instanceB_language, instanceB_quality_match, - instanceB_tag_filter_id, instanceB_tag_filter, + instanceB_tag_filter_id, instanceB_tag_filter, instanceB_blacklist, content_id_key, logger, is_sonarr, is_radarr, is_lidarr, get_status_path, get_content_path, get_profile_path, get_language_path, get_tag_path, @@ -193,7 +193,7 @@ def get_language_from_id(instance_session, instance_url, instance_key, instance_ def sync_servers(instanceA_contents, instanceB_language_id, instanceB_contentIds, instanceB_path, instanceB_profile_id, instanceA_profile_filter_id, instanceB_session, instanceB_url, instanceB_key, instanceA_quality_match, - instanceA_tag_filter_id): + instanceA_tag_filter_id, instanceA_blacklist): global is_radarr, is_sonarr, is_test_run search_ids = [] @@ -228,6 +228,18 @@ def sync_servers(instanceA_contents, instanceB_language_id, instanceB_contentIds logging.debug(f'Skipping content {title} - mismatched content_tag_ids {content_tag_ids} with instanceA_tag_filter_id {instanceA_tag_filter_id}') continue + # if black list given then dont sync matching slugs/ids + if instanceA_blacklist: + title_slug = content.get('titleSlug') or content.get('foreignArtistId') + if title_slug in instanceA_blacklist: + logging.debug(f'Skipping content {title} - blacklist slug: {title_slug}') + continue + + content_id = str(content.get('id')) + if content_id in instanceA_blacklist: + logging.debug(f'Skipping content {title} - blacklist ID: {content_id}') + continue + logging.info(f'syncing content title "{title}"') # get the POST payload and sync content to instance B @@ -328,7 +340,7 @@ def check_status(instance_session, instance_url, instance_key, instance_name='', def sync_content(): - global instanceA_profile_id, instanceA_profile, instanceB_profile_id, instanceB_profile, instanceA_profile_filter, instanceA_profile_filter_id, instanceB_profile_filter, instanceB_profile_filter_id, tested_api_version, instanceA_language_id, instanceA_language, instanceB_language_id, instanceB_language, instanceA_quality_match, instanceB_quality_match, is_sonarr, instanceA_tag_filter_id, instanceA_tag_filter, instanceB_tag_filter_id, instanceB_tag_filter, is_radarr + global instanceA_profile_id, instanceA_profile, instanceB_profile_id, instanceB_profile, instanceA_profile_filter, instanceA_profile_filter_id, instanceB_profile_filter, instanceB_profile_filter_id, tested_api_version, instanceA_language_id, instanceA_language, instanceB_language_id, instanceB_language, instanceA_quality_match, instanceB_quality_match, is_sonarr, instanceA_tag_filter_id, instanceA_tag_filter, instanceB_tag_filter_id, instanceB_tag_filter, is_radarr, instanceA_blacklist, instanceB_blacklist # get sessions instanceA_session = requests.Session() @@ -424,6 +436,7 @@ def sync_content(): instanceB_key=instanceB_key, instanceA_quality_match=instanceA_quality_match, instanceA_tag_filter_id=instanceA_tag_filter_id, + instanceA_blacklist=instanceA_blacklist ) # if given bidirectional flag then sync from instance B to instance A @@ -442,6 +455,7 @@ def sync_content(): instanceB_key=instanceA_key, instanceA_quality_match=instanceB_quality_match, instanceA_tag_filter_id=instanceB_tag_filter_id, + instanceA_blacklist=instanceB_blacklist ) ########################################################################################################################