diff --git a/README.md b/README.md index b79e70b..b0e2d41 100644 --- a/README.md +++ b/README.md @@ -9,17 +9,18 @@ Syncs two Radarr/Sonarr/Lidarr servers through the web API. Useful for syncing a * Can set interval for syncing * Support two way sync (one way by default) * Skip content with missing files -* Set language profiles (Sonarr) +* Set language profiles * Filter syncing by content file quality (Radarr only) * Filter syncing by tags (Sonarr/Radarr) * Allow for a test run using `test_run` flag (does everything but actually sync) ## Configuration - 1. Edit the config.conf file and enter your servers URLs and API keys for each server. + 1. Edit the config.conf file and enter your servers URLs and API keys for each server. + 2. Add the profile name (case insensitive) and movie path for the Radarr instance the movies will be synced to: - ```ini +```ini [radarrA] url = https://4k.example.com:443 key = XXXXX @@ -29,11 +30,11 @@ Syncs two Radarr/Sonarr/Lidarr servers through the web API. Useful for syncing a key = XXXXX profile = 1080p path = /data/Movies # if not given will use RadarrA path for each movie - may not be what you want! - ``` + ``` 3. Or if you want to sync two Sonarr instances: - ```ini + ```ini [sonarrA] url = https://4k.example.com:443 key = XXXXX @@ -43,10 +44,11 @@ Syncs two Radarr/Sonarr/Lidarr servers through the web API. Useful for syncing a key = XXXXX profile = 1080p path = /data/Shows +``` 4. Or if you want to sync two Lidarr instances: - 5. - ```ini + + ```ini [lidarrA] url = https://lossless.example.com:443 key = XXXXX @@ -56,13 +58,13 @@ Syncs two Radarr/Sonarr/Lidarr servers through the web API. Useful for syncing a key = XXXXX profile = Standard path = /data/Music - ``` - - **Note** you cannot have a mix of Radarr, Lidarr, or Sonarr config setups at the same time. + ``` + + **Note** you cannot have a mix of Radarr, Lidarr, or Sonarr config setups at the same time. - 6. Optional Configuration - - ```ini + 5. Optional Configuration + +```ini [*arrA] url = http://127.0.0.1:8080 key = XXXXX @@ -81,36 +83,43 @@ Syncs two Radarr/Sonarr/Lidarr servers through the web API. Useful for syncing a path = /data/Movies [general] - sync_bidirectionally = 1 # sync from instance A to B **AND** instance B to A (default 0) + bidirectional = 1 # sync from instance A to B **AND** instance B to A (default 0) auto_search = 0 # search is automatically started on new content - disable by setting to 0 (default 1) - skip_missing = 1 # content with missing files are skipped on sync - disable by setting to 0 (default 1) + skip_missing = 1 # content with missing files are skipped on sync - disable by setting to 0 (default 1) (Radarr only) monitor_new_content = 0 # set to 0 to never monitor new content synced or to 1 to always monitor new content synced (default 1) test_run = 1 # enable test mode - will run through sync program but will not actually sync content (default 0) sync_monitor = 1 # if set to 1 will sync if the content is monitored or not to instance B (default 0) - ``` + ``` - **Note** If `sync_bidirectionally` is set to `1`, then instance A will require either `profile_id` or `profile` AND `path` as well + **Note** If `bidirectional` is set to `1`, then instance A will require either `profile_id` or `profile` AND `path` as well --- ## Requirements - * Python 3.6 or greater - * 2 Radarr, Sonarr, or Lidarr servers + +* Python 3.6 or greater +* 2 Radarr, Sonarr, or Lidarr servers --- ## How to Run + 1. install the needed python modules (you'll need pip or you can install the modules manually inside the `requirements.txt` file): - ```bash + + ```bash pip install -r requirements.txt - ``` + ``` + 2. run this script directly or through a Cron: - ```bash + + ```bash python index.py - ``` + ``` --- + ## Docker Compose + This script can run through a docker container with a set interval (default every 5 minutes) ```bash diff --git a/config.conf b/config.conf index c8d85ba..7a82815 100644 --- a/config.conf +++ b/config.conf @@ -1,12 +1,21 @@ # [radarrA] # url = https://example.com:443 # key = FCKGW-RHQQ2-YXRKT-8TG6W-2B7Q8 +# profile = HD-1080p +# profile_id = 4 +# path = /data/Movies # [radarrB] # url = http://127.0.0.1:8080 # key = FCKGW-RHQQ2-YXRKT-8TG6W-2B7Q8 # profile_id = 1 +# profile = Ultra-HD # path = /data/4k_Movies # [general] -# sync_bidirectionally = 0 +# bidirectional = 0 +# log_level = 20 +# skip_missing = 0 +# auto_search = 1 +# monitor_new_content = 1 +# test_run = 0 diff --git a/config.py b/config.py index f1c8167..9f3ac02 100644 --- a/config.py +++ b/config.py @@ -79,6 +79,8 @@ radarrA_profile = get_config_value('RADARR_A_PROFILE', 'profile', 'radarrA') radarrA_profile_id = get_config_value('RADARR_A_PROFILE_ID', 'profile_id', 'radarrA') radarrA_profile_filter = get_config_value('RADARR_A_PROFILE_FILTER', 'profile_filter', 'radarrA') radarrA_profile_filter_id = get_config_value('RADARR_A_PROFILE_FILTER_ID', 'profile_filter_id', 'radarrA') +radarrA_tag_filter = get_config_value('RADARR_A_TAG_FILTER', 'tag_filter', 'radarrA') +radarrA_tag_filter_id = get_config_value('RADARR_A_TAG_FILTER_ID', 'tag_filter_id', 'radarrA') 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') @@ -91,6 +93,9 @@ radarrB_profile = get_config_value('RADARR_B_PROFILE', 'profile', 'radarrB') radarrB_profile_id = get_config_value('RADARR_B_PROFILE_ID', 'profile_id', 'radarrB') radarrB_profile_filter = get_config_value('RADARR_B_PROFILE_FILTER', 'profile_filter', 'radarrB') radarrB_profile_filter_id = get_config_value('RADARR_B_PROFILE_FILTER_ID', 'profile_filter_id', 'radarrB') +radarrB_tag_filter = get_config_value('RADARR_B_TAG_FILTER', 'tag_filter', 'radarrB') +radarrB_tag_filter_id = get_config_value('RADARR_B_TAG_FILTER_ID', 'tag_filter_id', 'radarrB') + 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') @@ -273,6 +278,8 @@ if radarrA_url and radarrB_url: instanceA_profile_filter_id = radarrA_profile_filter_id instanceA_language = radarrA_language instanceA_language_id = radarrA_language_id + instanceA_tag_filter = radarrA_tag_filter and radarrA_tag_filter.split(',') + instanceA_tag_filter_id = radarrA_tag_filter_id and radarrA_tag_filter_id.split(',') instanceA_quality_match = radarrA_quality_match instanceA_blacklist = radarrA_blacklist @@ -285,6 +292,8 @@ if radarrA_url and radarrB_url: instanceB_profile_filter_id = radarrB_profile_filter_id instanceB_language = radarrB_language instanceB_language_id = radarrB_language_id + instanceB_tag_filter = radarrB_tag_filter and radarrB_tag_filter.split(',') + instanceB_tag_filter_id = radarrB_tag_filter_id and radarrB_tag_filter_id.split(',') instanceB_quality_match = radarrB_quality_match instanceB_blacklist = radarrB_blacklist diff --git a/index.py b/index.py index f12c1e8..ec605d0 100644 --- a/index.py +++ b/index.py @@ -211,7 +211,7 @@ def sync_servers(instanceA_contents, instanceB_language_id, instanceB_contentIds instance_path = instanceB_path or dirname(content.get('path')) # if skipping missing files, we want to skip any that don't have files - if skip_missing: + if is_radarr and skip_missing: content_has_file = content.get('hasFile') if not content_has_file: logging.debug(f'Skipping content {title} - file missing') @@ -253,7 +253,7 @@ def sync_servers(instanceA_contents, instanceB_language_id, instanceB_contentIds # generate content from instance A to sync into instance B formatted_content = get_content_details( - content=dict(content), + content, instance_path=instance_path, instance_profile_id=instanceB_profile_id, instance_url=instanceB_url, @@ -266,7 +266,7 @@ def sync_servers(instanceA_contents, instanceB_language_id, instanceB_contentIds elif content_not_synced: # sync content if not synced logging.info(f'syncing content title "{title}"') - sync_response = instanceB_session.post(instanceB_content_url, data=json.dumps(formatted_content)) + sync_response = instanceB_session.post(instanceB_content_url, json=formatted_content) # check response and save content id for searching later on if success if sync_response.status_code != 201 and sync_response.status_code != 200: logger.error(f'server sync error for {title} - response: {sync_response.text}') @@ -288,7 +288,7 @@ def sync_servers(instanceA_contents, instanceB_language_id, instanceB_contentIds if matching_content_instanceB['monitored'] != content['monitored']: matching_content_instanceB['monitored'] = content['monitored'] instanceB_content_url = get_content_put_path(instanceB_url, instanceB_key, matching_content_instanceB.get('id')) - sync_response = instanceB_session.put(instanceB_content_url, data=json.dumps(matching_content_instanceB)) + sync_response = instanceB_session.put(instanceB_content_url, json=matching_content_instanceB) # check response and save content id for searching later on if success if sync_response.status_code != 202: logger.error(f'server monitoring sync error for {title} - response: {sync_response.text}')