Syncs two Radarr/Sonarr/Lidarr servers through the web API. Useful for syncing a 4k Radarr/Sonarr instance to a 1080p Radarr/Sonarr instance.
profile
name or profile_id
profile
name or profile_id
test_run
flag (does everything but actually sync)```ini [radarrA] url = https://4k.example.com:443 key = XXXXX
[radarrB]
url = http://127.0.0.1:8080
key = XXXXX
profile = 1080p
path = /data/Movies # if not given will use RadarrA path for each movie - may not be what you want!
```
Or if you want to sync two Sonarr instances:
```ini [sonarrA] url = https://4k.example.com:443 key = XXXXX
[sonarrB] url = http://127.0.0.1:8080 key = XXXXX profile = 1080p path = /data/Shows
Or if you want to sync two Lidarr instances:
```ini [lidarrA] url = https://lossless.example.com:443 key = XXXXX
[lidarrB] url = http://127.0.0.1:8080 key = XXXXX profile = Standard path = /data/Music ```
Note you cannot have a mix of Radarr, Lidarr, or Sonarr config setups at the same time.
Optional Configuration
```ini [*arrA] url = http://127.0.0.1:8080 key = XXXXX profile_filter = 1080p # add a filter to only sync contents belonging to this profile (can set by profile_filter_id as well) 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) sync movies by tag name (seperate multiple tags by comma (no spaces) ie horror,comedy,action) tag_filter_id = 2 # (Sonarr/Radarr) 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 key = XXXXX profile_id = 1 # Syncarr will try to find id from name but you can specify the id directly if you want language = Vietnamese # can set language for new content added (Sonarr) (can set by language_id as well) path = /data/Movies
[general] sync_bidirectionally = 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) (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
requirements.txt
file):
bash
pip install -r requirements.txt
bash
python index.py
This script can run through a docker container with a set interval (default every 5 minutes)
bash
syncarr:
image: syncarr/syncarr:latest
container_name: syncarr
restart: unless-stopped
environment:
RADARR_A_URL: https://4k.example.com:443
RADARR_A_KEY: XXXXX
RADARR_B_URL: http://127.0.0.1:8080
RADARR_B_KEY: XXXXX
RADARR_B_PROFILE: 1080p
RADARR_B_PATH: /data/Movies
SYNC_INTERVAL_SECONDS: 300
or
bash
syncarr:
image: syncarr/syncarr:latest
container_name: syncarr
restart: unless-stopped
environment:
SONARR_A_URL: https://4k.example.com:443
SONARR_A_KEY: XXXXX
SONARR_B_URL: http://127.0.0.1:8080
SONARR_B_KEY: XXXXX
SONARR_B_PROFILE: 1080p
SONARR_B_PATH: /data/Shows
SYNC_INTERVAL_SECONDS: 300
or
bash
syncarr:
image: syncarr/syncarr:latest
container_name: syncarr
restart: unless-stopped
environment:
LIDARR_A_URL: https://lossless.example.com:443
LIDARR_A_KEY: XXXXX
LIDARR_B_URL: http://127.0.0.1:8080
LIDARR_B_KEY: XXXXX
LIDARR_B_PROFILE: Standard
LIDARR_B_PATH: /data/Music
SYNC_INTERVAL_SECONDS: 300
For just plain docker (radarr example):
bash
docker run -it --rm --name syncarr -e RADARR_A_URL=https://4k.example.com:443 -e RADARR_A_KEY=XXXXX -e RADARR_B_URL=http://127.0.0.1:8080 -e RADARR_B_KEY=XXXXX -e RADARR_B_PROFILE=1080p -e RADARR_B_PATH=/data/Movies -e SYNC_INTERVAL_SECONDS=300 syncarr/syncarr
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)SONARR_B_LANGUAGE
or SONARR_B_LANGUAGE_ID
(and SONARR_B
if bidirectional sync)SYNCARR_BIDIRECTIONAL_SYNC=1
(default 0)SYNCARR_AUTO_SEARCH=0
(default 1)SYNCARR_MONITOR_NEW_CONTENT=0
(default 1)*ARR_A_QUALITY_MATCH
or *ARR_B_QUALITY_MATCH
*ARR_A_TAG_FILTER
/ *ARR_B_TAG_FILTER
or *ARR_A_TAG_FILTER_ID
/ *ARR_B_TAG_FILTER_ID
SYNCARR_TEST_RUN
*ARR_A_BLACKLIST
and **ARR_B_BLACKLIST
SYNCARR_SYNC_MONITOR
If you need to troubleshoot syncarr, then you can either set the log level through the config file:
ini
[general]
log_level = 10
Or in docker, set the LOG_LEVEL
ENV variable. Default is set to 20
(info only) but you can set to 10
to get debug info as well. When pasting debug logs online, make sure to remove any apikeys and any other data you don't want others to see.
Back up your instances before trying this out. I am not responsible for any lost data.
Is your feature request related to a problem? Please describe.
I have yet to find a way to cleanly dedupe two arr instances. Theres plenty of methods to add content, but no clean way that I can find to remove content. I have two radarr instances, one for foreign content and one for non-foreign. over the years, some stuff has creeped into both. So I can create a filter in radarr, lets say everything with a path starting with /mnt/movies/xyz, then tag the results with "xyz." but as far as I can tell, there is no method presently to use that movie lists comprised of that tag, and delete it from the second arr. If anyone knows of a way please share. If not, I think this would be an awesome addition to this tool.
Describe the solution you'd like Have a "--delete-mode" flag, possibly requiring a "--yes-i-am-sure" flag lol.
Is your feature request related to a problem? Please describe. I got an Anime Sonarr, and A Sonarr with all others. Now i got some programs adding to my normal sonarr instance but it also adds Anime series.
I would like syncarr to read all series in Sonarr A, and blacklist them in Sonarr B
This utility is no longer necessary (and doesn't currently work, from what I can tell). But it's one of the first results when you search for a way to sync two Radarr instances, so let's pay it forward by providing a guiding hand.
Describe the bug When sync monitor is enabled, it only acts on whether the whole show is monitored, not on a per-season basis.
Judging by the original feature request, it was intended that sync monitoring would work on individual seasons - it would allow a new season to be monitored after the initial sync.
It does work if one unmonitors the whole show - this change is propogated to Sonarr B
Version 1.13.1 (config)
Config
Sonarr 3.0.7.1477
[sonarrA] url = [redacted] key = [redacted]
[sonarrB] url = [redacted] key = [redacted] profile = Any - 1080p
[general] auto_search = 0 test_run = 0 sync_monitor = 1
Describe the bug Sync issue with radarr. Any new movie I add to A won't sync over to B with an 'Unsupported Media Type' error.
Version v1.13.1 Docker in Unraid (logs show 1.9.1 but files are the same as the 1.13.1, not sure why)
Config Using docker v4.0.4.5922 of Radarr
Debug logs
today at 9:02:37 AM2022-02-22 09:02:37,382 [MainThread ] [DEBUG] Syncarr Version 1.9.1
today at 9:02:37 AM2022-02-22 09:02:37,382 [MainThread ] [INFO ] log level 10
today at 9:02:37 AM2022-02-22 09:02:37,382 [MainThread ] [DEBUG] {'instanceA_url': 'http://radarrA', 'instanceA_key': 'removed', 'instanceA_path': None, 'instanceA_profile': None, 'instanceA_profile_id': None, 'instanceA_profile_filter': None, 'instanceA_profile_filter_id': None, 'instanceA_language': None, 'instanceA_language_id': None, 'instanceA_tag_filter': None, 'instanceA_tag_filter_id': None, 'instanceA_quality_match': None, 'instanceA_blacklist': None, 'instanceB_url': 'http://radarrB', 'instanceB_key': 'removed', 'instanceB_path': '/media/movies/UHD', 'instanceB_profile': 'Any', 'instanceB_profile_id': None, 'instanceB_profile_filter': None, 'instanceB_profile_filter_id': None, 'instanceB_language': None, 'instanceB_language_id': None, 'instanceB_tag_filter': None, 'instanceB_tag_filter_id': None, 'instanceB_quality_match': None, 'instanceB_blacklist': None, 'api_content_path': 'movie', 'api_profile_path': 'qualityprofile', 'api_language_path': '', 'is_sonarr': False, 'is_lidarr': False, 'is_radarr': True, 'monitor_new_content': 1, 'sync_bidirectionally': 0, 'auto_search': 1, 'skip_missing': 1, 'api_version': 'v3/', 'sync_monitor': 0}
today at 9:02:37 AM2022-02-22 09:02:37,382 [MainThread ] [INFO ] syncing every 300 seconds
today at 9:02:37 AM2022-02-22 09:02:37,382 [MainThread ] [DEBUG] --------------------
today at 9:02:37 AM2022-02-22 09:02:37,383 [MainThread ] [DEBUG] {'instance_url': 'http://radarrB', 'api_path': 'qualityprofile', 'api_version': 'v3/', 'is_sonarr': False, 'api_profile_path': 'qualityprofile', 'changed_api_version': False}
today at 9:02:37 AM2022-02-22 09:02:37,383 [MainThread ] [DEBUG] get_profile_path: http://radarrB/api/v3/qualityprofile?apikey=removed
today at 9:02:37 AM2022-02-22 09:02:37,383 [MainThread ] [DEBUG] Starting new HTTP connection (1): radarrB
today at 9:02:37 AM2022-02-22 09:02:37,385 [MainThread ] [DEBUG] http://radarrB "GET /api/v3/qualityprofile?apikey=removed HTTP/1.1" 200 None
today at 9:02:37 AM2022-02-22 09:02:37,386 [MainThread ] [DEBUG] found profile_id (instanceB) "1" from profile "Any"
today at 9:02:37 AM2022-02-22 09:02:37,386 [MainThread ] [DEBUG] {'instanceA_profile_id': None, 'instanceA_profile': None, 'instanceB_profile_id': 1, 'instanceB_profile': 'Any'}
today at 9:02:37 AM2022-02-22 09:02:37,386 [MainThread ] [DEBUG] {'instanceAprofile_filter_id': None, 'instanceAprofile_filter': None, 'instanceBprofile_filter_id': None, 'instanceBprofile_filter': None}
today at 9:02:37 AM2022-02-22 09:02:37,386 [MainThread ] [DEBUG] {'instanceA_tag_filter': None, 'instanceA_profile_filter': None, 'instanceB_tag_filter_id': None, 'instanceB_tag_filter': None}
today at 9:02:37 AM2022-02-22 09:02:37,386 [MainThread ] [DEBUG] --------------------
today at 9:02:37 AM2022-02-22 09:02:37,386 [MainThread ] [DEBUG] {'instance_url': 'http://radarrA', 'api_path': 'movie', 'api_version': 'v3/', 'is_sonarr': False, 'api_profile_path': 'qualityprofile', 'changed_api_version': False}
today at 9:02:37 AM2022-02-22 09:02:37,386 [MainThread ] [DEBUG] get_content_path: http://radarrA/api/v3/movie?apikey=removed
today at 9:02:37 AM2022-02-22 09:02:37,386 [MainThread ] [DEBUG] Starting new HTTP connection (1): radarrA
today at 9:02:37 AM2022-02-22 09:02:37,633 [MainThread ] [DEBUG] http://radarrA "GET /api/v3/movie?apikey=removed HTTP/1.1" 200 None
today at 9:02:37 AM2022-02-22 09:02:37,685 [MainThread ] [DEBUG] 605 contents in instance A
today at 9:02:37 AM2022-02-22 09:02:37,685 [MainThread ] [DEBUG] --------------------
today at 9:02:37 AM2022-02-22 09:02:37,685 [MainThread ] [DEBUG] {'instance_url': 'http://radarrB', 'api_path': 'movie', 'api_version': 'v3/', 'is_sonarr': False, 'api_profile_path': 'qualityprofile', 'changed_api_version': False}
today at 9:02:37 AM2022-02-22 09:02:37,685 [MainThread ] [DEBUG] get_content_path: http://radarrB/api/v3/movie?apikey=removed
today at 9:02:37 AM2022-02-22 09:02:37,936 [MainThread ] [DEBUG] http://radarrB "GET /api/v3/movie?apikey=removed HTTP/1.1" 200 None
today at 9:02:37 AM2022-02-22 09:02:37,985 [MainThread ] [DEBUG] 578 contents in instance B
today at 9:02:37 AM2022-02-22 09:02:37,985 [MainThread ] [INFO ] syncing content from instance A to instance B
today at 9:02:37 AM2022-02-22 09:02:37,989 [MainThread ] [DEBUG] {'tmdbId': 12144, 'qualityProfileId': 1, 'monitored': True, 'rootFolderPath': '/media/movies/UHD', 'images': [{'coverType': 'poster', 'url': 'http://radarrB/MediaCover/686/poster.jpg?lastWrite=637802078767281129', 'remoteUrl': 'https://image.tmdb.org/t/p/original/wSC2wxFj3ZgrIHlIaePeuHN4igo.jpg'}, {'coverType': 'fanart', 'url': 'http://radarrB/MediaCover/686/fanart.jpg?lastWrite=637802078769291094', 'remoteUrl': 'https://image.tmdb.org/t/p/original/sXfQGa7juaTpZ5IF17Xa6hI53nx.jpg'}], 'title': 'The Land Before Time', 'year': 1988, 'titleSlug': '12144', 'addOptions': {'searchForMovie': True}}
today at 9:02:37 AM2022-02-22 09:02:37,989 [MainThread ] [DEBUG] --------------------
today at 9:02:37 AM2022-02-22 09:02:37,989 [MainThread ] [DEBUG] {'instance_url': 'http://radarrB', 'api_path': 'movie', 'api_version': 'v3/', 'is_sonarr': False, 'api_profile_path': 'qualityprofile', 'changed_api_version': False}
today at 9:02:37 AM2022-02-22 09:02:37,989 [MainThread ] [DEBUG] get_content_path: http://radarrB/api/v3/movie?apikey=removed
today at 9:02:37 AM2022-02-22 09:02:37,989 [MainThread ] [INFO ] syncing content title "The Land Before Time"
today at 9:02:37 AM2022-02-22 09:02:37,991 [MainThread ] [DEBUG] http://radarrB "POST /api/v3/movie?apikey=removed HTTP/1.1" 415 None
today at 9:02:37 AM2022-02-22 09:02:37,991 [MainThread ] [ERROR] server sync error for The Land Before Time - response: {
today at 9:02:37 AM "type": "https://tools.ietf.org/html/rfc7231#section-6.5.13",
today at 9:02:37 AM "title": "Unsupported Media Type",
today at 9:02:37 AM "status": 415,
today at 9:02:37 AM "traceId": "00-3898497394065ec7ef24ce49302f21ac-09c2a7a2c846b374-00"
today at 9:02:37 AM}
today at 9:02:37 AM2022-02-22 09:02:37,991 [MainThread ] [DEBUG] {'tmdbId': 460458, 'qualityProfileId': 1, 'monitored': True, 'rootFolderPath': '/media/movies/UHD', 'images': [{'coverType': 'poster', 'url': 'http://radarrB/MediaCover/688/poster.jpg?lastWrite=637811047408544406', 'remoteUrl': 'https://image.tmdb.org/t/p/original/7uRbWOXxpWDMtnsd2PF3clu65jc.jpg'}, {'coverType': 'fanart', 'url': 'http://radarrB/MediaCover/688/fanart.jpg?lastWrite=637811047412524335', 'remoteUrl': 'https://image.tmdb.org/t/p/original/o76ZDm8PS9791XiuieNB93UZcRV.jpg'}], 'title': 'Resident Evil: Welcome to Raccoon City', 'year': 2021, 'titleSlug': '460458', 'addOptions': {'searchForMovie': True}}
today at 9:02:37 AM2022-02-22 09:02:37,991 [MainThread ] [DEBUG] --------------------
today at 9:02:37 AM2022-02-22 09:02:37,991 [MainThread ] [DEBUG] {'instance_url': 'http://radarrB', 'api_path': 'movie', 'api_version': 'v3/', 'is_sonarr': False, 'api_profile_path': 'qualityprofile', 'changed_api_version': False}
today at 9:02:37 AM2022-02-22 09:02:37,991 [MainThread ] [DEBUG] get_content_path: http://radarrB/api/v3/movie?apikey=removed
today at 9:02:37 AM2022-02-22 09:02:37,991 [MainThread ] [INFO ] syncing content title "Resident Evil: Welcome to Raccoon City"
today at 9:02:37 AM2022-02-22 09:02:37,992 [MainThread ] [DEBUG] http://radarrB "POST /api/v3/movie?apikey=removed HTTP/1.1" 415 None
today at 9:02:37 AM2022-02-22 09:02:37,992 [MainThread ] [ERROR] server sync error for Resident Evil: Welcome to Raccoon City - response: {
today at 9:02:37 AM "type": "https://tools.ietf.org/html/rfc7231#section-6.5.13",
today at 9:02:37 AM "title": "Unsupported Media Type",
today at 9:02:37 AM "status": 415,
today at 9:02:37 AM "traceId": "00-37f033c191ecdcd64e20ca80fb13265e-dac4c2f30f450ec7-00"
today at 9:02:37 AM}
today at 9:02:37 AM2022-02-22 09:02:37,992 [MainThread ] [INFO ] 0 contents synced successfully
Additional context Looks like it started around 2/11/2022 as that's when I last added a movie.
Describe the bug Hey the code worked fine for some months. Now I'm seeing this error when trying to sync two sonarr instances:
{
"propertyName": "LanguageProfileId",
"errorMessage": "Language profile does not exist",
"attemptedValue": 4,
"severity": "error",
"errorCode": "LanguageProfileExistsValidator",
"formattedMessageArguments": [],
"formattedMessagePlaceholderValues": {
"propertyName": "Language Profile Id",
"propertyValue": 4
}
}
]
Version I tried the original script and the forked more uptodate one.
Config
I tried adding
language = German
to the sonarrB block without any change.
Could somebody tell me how to get the language ID or anything else to fix that issue?
Best regards