-
-
Save rustymyers/a68f405909224e023088c8cac6834c32 to your computer and use it in GitHub Desktop.
| #!/usr/bin/python3 | |
| """ | |
| #------------------------------------------------------------------------------------------------ | |
| #-- Movie Tag Updater for Radarr | |
| #-------------------------------------------------------------------------------------------------- | |
| # Program : Radarr_tagarr | |
| # To Complie : n/a | |
| # | |
| # Purpose : Add tags and profiles to radarr movies. Tags are retreived from Radarr. | |
| # Say Y to add a tag. N to skip a tag. N for all tags will remove all tags. | |
| # | |
| # Called By : | |
| # Calls : | |
| # | |
| # Author : Rusty Myers <[email protected]> | |
| # Based Upon : | |
| # | |
| # Note : | |
| # | |
| # Revisions : | |
| # 2017-05-03 <rusty> Initial Version | |
| # 2017-05-03 <rusty> Updated to dynamically retreive tags and bulk set the ones chosen | |
| # 2018-02-13 <rusty> New method to check for matching tags without concern for order, | |
| # Adding code to combine tags with existing movie tags instead of replace, | |
| # Updating hostname fields to make it easier to support http or https | |
| # 2019-07-26 <rusty> Updating to Python3 | |
| # 2020-08-25 <rusty> Adding --dry-run for testing changes | |
| # 2020-09-01 <rusty> Adding name resolution for hostname | |
| # | |
| # Version : 1.5 | |
| #------------------------------------------------------------------------------------------------""" | |
| # | |
| from __future__ import print_function | |
| import requests, argparse, socket | |
| # Function to return json of all movies | |
| def get_data(radarrhost): | |
| """Get data from API""" | |
| response = requests.get(radarrhost) | |
| return response.json() | |
| # function to put updated data | |
| def put_data(radarrhost, moviedata): | |
| """Put data into API""" | |
| result = requests.put(radarrhost, json=moviedata) | |
| return result | |
| # Variables | |
| # Radarr API Key, get it in Settings->General->Security of Sonarr pages | |
| APIKEY = "" | |
| # Radarr Host Name: Add reverse proxy or direct IP:port, no trailing slash | |
| HOSTNAME = "" | |
| # Set dryrun default to false | |
| dryrun = False | |
| # Get our arguments | |
| parser = argparse.ArgumentParser(description='Check, Print, and Save DHCP & BSDP Information.') | |
| parser.add_argument('-d', '--dry-run', action='store_true', \ | |
| dest='dryrun', help='Dry run of tagging, displays expected outcomes.') | |
| parser.add_argument('-k', '-apikey' , action='store', default=APIKEY, \ | |
| dest='APIKEY', help='Path to plist for saving results to plist. Default: /Library/Preferences/edu.psu.sdt.plist') | |
| parser.add_argument('-n', '--hostname', action='store', default=HOSTNAME, \ | |
| dest='HOSTNAME', help='Test code with stored BSDP response from OS X Server, writing to /tmp/org.network.plist.') | |
| args = parser.parse_args() | |
| if args.dryrun: | |
| print("!!! Dry Run...not making any changes...") | |
| dryrun = True | |
| if args.APIKEY: | |
| APIKEY = args.APIKEY | |
| if args.HOSTNAME: | |
| HOSTNAME = args.HOSTNAME | |
| host_address = "" | |
| HOST = "".join(HOSTNAME.split("/")[2:3]) | |
| try: | |
| host_address = socket.gethostbyname(HOST) | |
| except: | |
| print("Unable to resolve hostname! {}".format(HOST)) | |
| exit(1) | |
| # Get All Tags | |
| ALLTAGSJSON = get_data(HOSTNAME + "/api/tag?apikey=" + APIKEY) | |
| # Get all Profiles | |
| ALLPROFILESJSON = get_data(HOSTNAME + "/api/profile?apikey=" + APIKEY) | |
| print("Connecting to: {}".format(HOSTNAME + "/api/tag?apikey=" + APIKEY)) | |
| # Set a profile | |
| PROFILEINT = "" | |
| PROFILENAMES = {} | |
| # Print Profiles | |
| for profile in ALLPROFILESJSON: | |
| if profile == "error": | |
| print("Could not connect to Radarr!") | |
| exit(0) | |
| print("Which profile should we add to the movie?") | |
| print("ID: {1} Name: {0}".format(profile["name"], profile["id"])) | |
| # Add tag to lookup by it's id | |
| PROFILENAMES[profile["id"]] = profile["name"] | |
| # Ask if we should add it to all movies? | |
| ADDPROFILE = input('Which profile to all movies? [1-{0}] : '.format(len(PROFILENAMES))) | |
| # Add the profiles to our list of tag numbers | |
| PROFILEINT = int(ADDPROFILE) | |
| ADDPROFILE = input('Are you sure you want to set all movies to profile \"{0}\"? [y/n] : '.format(PROFILENAMES[PROFILEINT])) | |
| # If the answer not starts with any case Y | |
| if not ADDPROFILE.lower().startswith("y"): | |
| print("Skipping profile...") | |
| else: | |
| print("Adding profile {0}...".format(PROFILENAMES[PROFILEINT])) | |
| # Make list of tags to add | |
| TAGS = [] | |
| # Make a tag lookup for later | |
| TAGNAMES = {} | |
| # For each tag | |
| for tag in ALLTAGSJSON: | |
| # Add tag to lookup by it's id | |
| TAGNAMES[tag['id']] = tag['label'] | |
| # Ask if we should add it to all movies? | |
| ADDTAG = input('Add Tag \"{0}\" to all movies? [y/n] : '.format(tag['label'])) | |
| # If the answer starts with any case Y | |
| if ADDTAG.lower().startswith("y"): | |
| # Add the tag to our list of tag numbers | |
| TAGS.append(int(tag['id'])) | |
| # Ask if we should contiunue? | |
| ADDTAG = input("Add Tag(s) '{0}' to all movies? [y/n] : ".format(", ".join(TAGNAMES[int(p)] for p in TAGS))) | |
| # If the answer starts with any case Y | |
| if ADDTAG.lower().startswith("y"): | |
| # Get All Movies | |
| ALLMOVIESJSON = get_data(HOSTNAME + "/api/movie?apikey=" + APIKEY) | |
| # for each movie | |
| for movie in ALLMOVIESJSON: | |
| # get movie ID for updating data with put_data | |
| movieID = movie['id'] | |
| # Set tagMissing to false and check each tag | |
| dataMissing = False | |
| # For each tag we want to add | |
| for tag in TAGS: | |
| # If that tag does not exist currenlty | |
| if tag not in movie['tags']: | |
| # Set boolean to update tags | |
| dataMissing = True | |
| else: | |
| TAGLIST = ", ".join(TAGNAMES[int(p)] for p in movie['tags']) | |
| print("Found existing tags: {}".format(TAGLIST)) | |
| if movie['profileId'] != PROFILEINT: | |
| dataMissing = True | |
| if dryrun: | |
| TAGLIST = ", ".join(TAGNAMES[int(p)] for p in movie['tags']) | |
| MOVIETITLE = movie['title'].encode('utf-8') | |
| print("!!! Dry-Run: Movie {0} has existing tags: {1} ".format(MOVIETITLE, TAGLIST)) | |
| if not dataMissing: | |
| # Print movies that have the correct tags | |
| TAGLIST = ", ".join(TAGNAMES[int(p)] for p in movie['tags']) | |
| MOVIETITLE = movie['title'].encode('utf-8') | |
| print("Skipping {0} with existing tags: {1} ".format(MOVIETITLE, TAGLIST)) | |
| else: | |
| # Print name to statisfy user something is happening | |
| TAGLIST = ", ".join(TAGNAMES[int(p)] for p in TAGS) | |
| MOVIETITLE = movie['title'].encode('utf-8') | |
| PNAMES = PROFILENAMES[PROFILEINT] | |
| print("Adding '{0}' tags and {2} profile to Movie: {1}"\ | |
| .format(TAGLIST, MOVIETITLE, PNAMES)) | |
| # Combine movie tags and new tags | |
| tag_set = list(set(movie['tags']+TAGS)) | |
| # Add unique tags | |
| movie['tags'] = tag_set | |
| movie['profileId'] = PROFILEINT | |
| # Update radarrHost to include movie ID | |
| radarrHost = HOSTNAME + "/api/movie/" + str(movieID) + "?apikey=" + APIKEY | |
| if dryrun: | |
| print("!!! Dry-run, not updating movie: {}!".format(MOVIETITLE)) | |
| print("CHANGES: Adding '{0}' tags and '{2}' profile to Movie: {1}".format(TAGLIST, MOVIETITLE, PNAMES)) | |
| # print(movie) | |
| else: | |
| # Put movie back to radarr | |
| updateMovie = put_data(radarrHost, movie) | |
| # If the update worked, just tell 'em that | |
| if updateMovie.status_code == 202: | |
| print("Sucessfully updated \"{0}\"".format(movie['title'].encode('utf-8'))) | |
| else: | |
| # If the update didn't work, let 'em know | |
| MOVIETITLE = movie['title'].encode('utf-8') | |
| STATUSCODE = updateMovie.status_code | |
| print("Failed: \"{0}\" returned response code: {1}".format(MOVIETITLE, STATUSCODE)) | |
| if b"Dragnet" in movie['title'].encode(): | |
| exit(0) |
Anything I'm doing wrong here?
I entered my API and hostname variables and then tried running the script.
C:\Users\elesn\Google Drive\Scripts>python RadarrTagarr.py
Traceback (most recent call last):
File "RadarrTagarr.py", line 49, in
allTagsJSON = getData(hostName + "/radarr/api/tag?apikey=" + apiKey)
File "RadarrTagarr.py", line 36, in getData
json = response.json()
File "C:\Python27\lib\site-packages\requests\models.py", line 896, in json
return complexjson.loads(self.text, **kwargs)
File "C:\Python27\Lib\json_init_.py", line 339, in loads
return _default_decoder.decode(s)
File "C:\Python27\Lib\json\decoder.py", line 364, in decode
obj, end = self.raw_decode(s, idx=_w(s, 0).end())
File "C:\Python27\Lib\json\decoder.py", line 382, in raw_decode
raise ValueError("No JSON object could be decoded")
ValueError: No JSON object could be decoded
Thanks and apologize if I'm not commenting in the appropriate location.
@xxcamoulfagexx It looks like you might not have any tags? That might be why the ValueError occurs. Try adding a tag and running it again?
Any chance of adding a dry-run switch?
The reasoning is that if I've already set a tag, I want to make sure that it is not overwritten.
@ak2766 Try version 1.4. You can now pass variables to the command line, including --dry-run or -d:
./radarr_tagarr3.py --dry-run -k "myAPIkey21384y01h" -n "https://radarr.com/myhost"
@rustymyers I've tried running this:
./radarr_tagarr3.py --dry-run -k "mykey" -n "https://radarr.mydomain.co.uk"
But I get an error on conn=connection.create_connection... Any ideas?
My radarr is behind a reverse proxy.
@hughesjs Can you load a URL like this in your browser: https://radarr.mydomain.co.uk/api/profile?apikey=mykey
It returns some JSON aye.
Here's the full error:
Traceback (most recent call last):
File "/usr/lib/python3.8/site-packages/urllib3/connection.py", line 159, in _new_conn
conn = connection.create_connection(
File "/usr/lib/python3.8/site-packages/urllib3/util/connection.py", line 61, in create_connection
for res in socket.getaddrinfo(host, port, family, socket.SOCK_STREAM):
File "/usr/lib/python3.8/socket.py", line 918, in getaddrinfo
for res in _socket.getaddrinfo(host, port, family, type, proto, flags):
socket.gaierror: [Errno -2] Name or service not known
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/usr/lib/python3.8/site-packages/urllib3/connectionpool.py", line 670, in urlopen
httplib_response = self._make_request(
File "/usr/lib/python3.8/site-packages/urllib3/connectionpool.py", line 381, in _make_request
self._validate_conn(conn)
File "/usr/lib/python3.8/site-packages/urllib3/connectionpool.py", line 978, in _validate_conn
conn.connect()
File "/usr/lib/python3.8/site-packages/urllib3/connection.py", line 309, in connect
conn = self._new_conn()
File "/usr/lib/python3.8/site-packages/urllib3/connection.py", line 171, in _new_conn
raise NewConnectionError(
urllib3.exceptions.NewConnectionError: <urllib3.connection.HTTPSConnection object at 0x7fdc0ca7cbe0>: Failed to establish a new connection: [Errno -2] Name or service not known
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/usr/lib/python3.8/site-packages/requests/adapters.py", line 439, in send
resp = conn.urlopen(
File "/usr/lib/python3.8/site-packages/urllib3/connectionpool.py", line 726, in urlopen
retries = retries.increment(
File "/usr/lib/python3.8/site-packages/urllib3/util/retry.py", line 439, in increment
raise MaxRetryError(_pool, url, error or ResponseError(cause))
urllib3.exceptions.MaxRetryError: HTTPSConnectionPool(host='hostname', port=443): Max retries exceeded with url: /api/tag?apikey=API (Caused by NewConnectionError('<urllib3.connection.HTTPSConnection object at 0x7fdc0ca7cbe0>: Failed to establish a new connection: [Errno -2] Name or service not known'))
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "./radarr_tagarr3.py", line 53, in <module>
ALLTAGSJSON = get_data(HOSTNAME + "/api/tag?apikey=" + APIKEY)
File "./radarr_tagarr3.py", line 36, in get_data
response = requests.get(radarrhost)
File "/usr/lib/python3.8/site-packages/requests/api.py", line 76, in get
return request('get', url, params=params, **kwargs)
File "/usr/lib/python3.8/site-packages/requests/api.py", line 61, in request
return session.request(method=method, url=url, **kwargs)
File "/usr/lib/python3.8/site-packages/requests/sessions.py", line 530, in request
resp = self.send(prep, **send_kwargs)
File "/usr/lib/python3.8/site-packages/requests/sessions.py", line 643, in send
r = adapter.send(request, **kwargs)
File "/usr/lib/python3.8/site-packages/requests/adapters.py", line 516, in send
raise ConnectionError(e, request=request)
requests.exceptions.ConnectionError: HTTPSConnectionPool(host='hostname', port=443): Max retries exceeded with url: /api/tag?apikey=API (Caused by NewConnectionError('<urllib3.connection.HTTPSConnection object at 0x7fdc0ca7cbe0>: Failed to establish a new connection: [Errno -2] Name or service not known'))
@hughesjs Looks like it's your host name not being able to resolve to an IP address. I can reproduce this is I use a HOSTNAME that doesn't exist. For example: radarr_tagarr3.py -d -k "KEY" -n "http://1234example.com/radarr"
Make sure you can resolve your hostname from the terminal or try using the IP address. Hope that helps!
Rusty
@hughesjs Try version 1.5. I added a check to see if it could resolve the hostname when run:
/radarr_tagarr3.py -d -k "KEY" -n "http://1234example.com/radarr"
!!! Dry Run...not making any changes...
Unable to resolve hostname! 1234example.com
See if you get a similar error for your host. Feel free to email me directly your hostname, too, if you want me to see if I can determine the issue.
Thanks
@ak2766 Try version 1.4. You can now pass variables to the command line, including --dry-run or -d:
./radarr_tagarr3.py --dry-run -k "myAPIkey21384y01h" -n "https://radarr.com/myhost"
I've just downloaded v1.5 - it works beautifully.
Many thanks.
Hello I would like to know how to install this and how it is run on a nas? thank you