Skip to content

Instantly share code, notes, and snippets.

@JonnyWong16
Last active March 19, 2024 17:31
Show Gist options
  • Star 35 You must be signed in to star a gist
  • Fork 9 You must be signed in to fork a gist
  • Save JonnyWong16/f554f407832076919dc6864a78432db2 to your computer and use it in GitHub Desktop.
Save JonnyWong16/f554f407832076919dc6864a78432db2 to your computer and use it in GitHub Desktop.
Updates all metadata in the Tautulli database after moving Plex libraries.
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# Description: Updates all metadata in the Tautulli database after moving Plex libraries.
# Author: /u/SwiftPanda16
# Requires: plexapi, requests
from plexapi.server import PlexServer
import requests
### EDIT SETTINGS ###
## Install the required modules listed above with:
## python -m pip install plexapi
## python -m pip install requests
TAUTULLI_URL = 'http://localhost:8181'
TAUTULLI_APIKEY = 'xxxxxxxxxx'
PLEX_URL = 'http://localhost:32400'
PLEX_TOKEN = 'xxxxxxxxxx'
FALLBACK_MATCH_TITLE_YEAR = True # True or False, fallback to matching by title and year if matching by ID fails
FALLBACK_MATCH_TITLE = True # True or False, fallback to matching by title ONLY if matching by title and year fails
DRY_RUN = True # True to dry run without making changes to the Tautulli database, False to make changes
## CODE BELOW ##
def get_id_from_guid(guid):
id = None
if 'imdb://' in guid:
id = 'imdb://' + guid.split('imdb://')[1].split('?')[0]
elif 'themoviedb://' in guid:
id = 'tmdb://' + guid.split('themoviedb://')[1].split('?')[0]
elif 'thetvdb://' in guid:
id = 'tvdb://' + guid.split('thetvdb://')[1].split('?')[0].split('/')[0]
elif 'plex://' in guid:
id = 'plex://' + guid.split('plex://')[1]
elif 'tmdb://' in guid or 'tvdb://' in guid or 'mbid://' in guid:
id = guid
return id
def main():
session = requests.Session()
new_key_map = {}
old_key_map = {}
# Check for DRY_RUN. Backup Tautulli database if needed.
if DRY_RUN:
print("Dry run enabled. No changes will be made to the Tautulli database.")
else:
print("Not dry run. Creating a backup of the Tautulli database.")
params = {
'cmd': 'backup_db',
'apikey': TAUTULLI_APIKEY,
}
session.post(TAUTULLI_URL.rstrip('/') + '/api/v2', params=params)
# Get all old items from the Tautulli database (using raw SQL)
print("Retrieving all history items from the Tautulli database...")
recordsFiltered = None
count = 0
start = 0
while recordsFiltered is None or count < recordsFiltered:
params = {
'cmd': 'get_history',
'apikey': TAUTULLI_APIKEY,
'grouping': 0,
'include_activity': 0,
'media_type': 'movie,episode,track',
'order_column': 'date',
'order_dir': 'desc',
'start': start,
'length': 1000
}
r = session.get(TAUTULLI_URL.rstrip('/') + '/api/v2', params=params).json()
if r['response']['result'] == 'error':
print("Error retrieving Tautulli history: {}".format(r['response']['message']))
print("Exiting script...")
return
else:
if recordsFiltered is None:
recordsFiltered = r['response']['data']['recordsFiltered']
for row in r['response']['data']['data']:
count += 1
if row['media_type'] not in ('movie', 'episode'):
continue
id = get_id_from_guid(row['guid'])
if id:
key = row['grandparent_rating_key'] or row['rating_key']
media_type = 'show' if row['media_type'] == 'episode' else row['media_type']
title = row['grandparent_title'] or row['title']
year = str(row['year'])
old_key_map[id] = (key, media_type, title, year)
else:
title = row['grandparent_title'] or row['title']
print("\tUnsupported guid for '{title}' in the Tautulli database [Guid: {guid}]. Skipping...".format(title=title, guid=row['guid']))
start += 1000
# Get all new items from the Plex server
print("Retrieving all library items from the Plex server...")
plex = PlexServer(PLEX_URL, PLEX_TOKEN)
for library in plex.library.sections():
if library.type not in ('movie', 'show') or library.agent == 'com.plexapp.agents.none':
print("\tSkipping library: {title}".format(title=library.title))
continue
print("\tScanning library: {title}".format(title=library.title))
for item in library.all():
id = get_id_from_guid(item.guid)
if id:
new_key_map[id] = (item.ratingKey, item.type, item.title, str(item.year))
else:
print("\t\tUnsupported guid for '{title}' in the Plex library [Guid: {guid}]. Skipping...".format(title=item.title, guid=item.guid))
for guid in item.guids: # Also parse <Guid> tags for new Plex agents
id = get_id_from_guid(guid.id)
if id:
new_key_map[id] = (item.ratingKey, item.type, item.title, str(item.year))
new_title_year_map = {(title, year): (id, key, media_type) for id, (key, media_type, title, year) in new_key_map.items()}
new_title_map = {title: (id, key, media_type, year) for id, (key, media_type, title, year) in new_key_map.items()}
# Update metadata in the Tautulli database
print("{}Matching Tautulli items with Plex items...".format("(DRY RUN) " if DRY_RUN else ""))
if FALLBACK_MATCH_TITLE_YEAR:
print("\tUsing fallback to match by title and year.")
if FALLBACK_MATCH_TITLE:
print("\tUsing fallback to match by title ONLY.")
if not FALLBACK_MATCH_TITLE_YEAR and not FALLBACK_MATCH_TITLE:
print("\tNot using any fallback to title or year.")
updated = []
no_mapping = set()
for id, (old_rating_key, old_type, title, year) in old_key_map.items():
new_rating_key, new_type, _, _ = new_key_map.get(id, (None, None, None, None))
new_year = None
warning_year = False
if not new_rating_key and FALLBACK_MATCH_TITLE_YEAR:
_, new_rating_key, new_type = new_title_year_map.get((title, year), (None, None, None))
if not new_rating_key and FALLBACK_MATCH_TITLE:
_, new_rating_key, new_type, new_year = new_title_map.get(title, (None, None, None, None))
if new_rating_key:
if new_rating_key != old_rating_key and new_type == old_type:
if new_year is not None and new_year != year:
warning_year = True
updated.append((title, year, old_rating_key, new_rating_key, new_type, new_year, warning_year))
else:
no_mapping.add((title, year, old_rating_key))
if updated:
if not DRY_RUN:
url = TAUTULLI_URL.rstrip('/') + '/api/v2'
for title, year, old_rating_key, new_rating_key, new_type, new_year, warning_year in updated:
params = {
'cmd': 'update_metadata_details',
'apikey': TAUTULLI_APIKEY,
'old_rating_key': old_rating_key,
'new_rating_key': new_rating_key,
'media_type': new_type
}
session.post(url, params=params)
print("{}Updated metadata for {} items:".format("(DRY RUN) " if DRY_RUN else "", len(updated)))
for title, year, old_rating_key, new_rating_key, new_type, new_year, warning_year in updated:
if warning_year:
print("\t{title} ({year} --> {new_year}) [Rating Key: {old} --> {new}]".format(
title=title, year=year, new_year=new_year, old=old_rating_key, new=new_rating_key))
else:
print("\t{title} ({year}) [Rating Key: {old} --> {new}]".format(
title=title, year=year, old=old_rating_key, new=new_rating_key))
if no_mapping:
print("{}No match found for {} Tautulli items on the Plex server:".format("(DRY RUN) " if DRY_RUN else "", len(no_mapping)))
for title, year, old_rating_key in no_mapping:
print("\t{title} ({year}) [Rating Key: {old}]".format(
title=title, year=year, old=old_rating_key))
# Clear all recently added items in the Tautulli database
print("{}Clearing all recently added items in the Tautulli database...".format("(DRY RUN) " if DRY_RUN else ""))
if not DRY_RUN:
params = {
'cmd': 'delete_recently_added',
'apikey': TAUTULLI_APIKEY
}
r = session.get(TAUTULLI_URL.rstrip('/') + '/api/v2', params=params).json()
if r['response']['result'] == 'error':
print("Error clearing the Tautulli recently added database table: {}".format(r['response']['message']))
print("Exiting script...")
return
print("Cleared all items from the Tautulli recently added database table.")
if __name__ == "__main__":
main()
print("Done.")
@gold007eye
Copy link

I am getting the following in the log when trying to run this script. Any ideas to fix the issue?

Tautulli Notifiers :: Script error: Traceback (most recent call last): File "/share/CACHEDEV1_DATA/.qpkg/PlexPy/DATADIR/scripts/update_all_metadata.py", line 138, in <module> main() File "/share/CACHEDEV1_DATA/.qpkg/PlexPy/DATADIR/scripts/update_all_metadata.py", line 63, in main r = requests.get(TAUTULLI_URL.rstrip('/') + '/api/v2', params=params).json() File "/share/CACHEDEV1_DATA/.qpkg/PlexPy/plexpy-master/lib/requests/models.py", line 892, in json return complexjson.loads(self.text, **kwargs) File "/share/CACHEDEV1_DATA/.qpkg/PlexPy/lib/python2.7/json/__init__.py", line 339, in loads return _default_decoder.decode(s) File "/share/CACHEDEV1_DATA/.qpkg/PlexPy/lib/python2.7/json/decoder.py", line 364, in decode obj, end = self.raw_decode(s, idx=_w(s, 0).end()) File "/share/CACHEDEV1_DATA/.qpkg/PlexPy/lib/python2.7/json/decoder.py", line 382, in raw_decode raise ValueError("No JSON object could be decoded") ValueError: No JSON object could be decoded

@Napsterbater
Copy link

Pretty much worked like a charm for over 99.9% of my 17k+ movies/shows.

Was so relieved to find this.

Thanks.

@jrac86
Copy link

jrac86 commented Dec 13, 2018

I am getting the following in the log when trying to run this script. Any ideas to fix the issue?

Tautulli Notifiers :: Script error: Traceback (most recent call last): File "/share/CACHEDEV1_DATA/.qpkg/PlexPy/DATADIR/scripts/update_all_metadata.py", line 138, in <module> main() File "/share/CACHEDEV1_DATA/.qpkg/PlexPy/DATADIR/scripts/update_all_metadata.py", line 63, in main r = requests.get(TAUTULLI_URL.rstrip('/') + '/api/v2', params=params).json() File "/share/CACHEDEV1_DATA/.qpkg/PlexPy/plexpy-master/lib/requests/models.py", line 892, in json return complexjson.loads(self.text, **kwargs) File "/share/CACHEDEV1_DATA/.qpkg/PlexPy/lib/python2.7/json/__init__.py", line 339, in loads return _default_decoder.decode(s) File "/share/CACHEDEV1_DATA/.qpkg/PlexPy/lib/python2.7/json/decoder.py", line 364, in decode obj, end = self.raw_decode(s, idx=_w(s, 0).end()) File "/share/CACHEDEV1_DATA/.qpkg/PlexPy/lib/python2.7/json/decoder.py", line 382, in raw_decode raise ValueError("No JSON object could be decoded") ValueError: No JSON object could be decoded

I am getting this same error... did anyone find out how to fix it?

@mrtumnus
Copy link

mrtumnus commented Jan 8, 2019

@gold007eye @jrac86
I got this error because I have a custom HTTP Root. I updated my TAUTULLI_URL accordingly (e.g., http://localhost:8181/myroot), which fixed the issue.

@Naesstrom
Copy link

I'm getting the same error to, py files looks like this:

TAUTULLI_URL = 'http://localhost:8181'
TAUTULLI_APIKEY = 'supers3cret'
PLEX_URL = 'http://192.168.1.10:32400'
PLEX_TOKEN = 'evnm0res3cret'

But I still get the same error as @gold007eye and @jrac86:

root@PlexPy:/usr/local/share/plexpy/scripts # ./update_all_metadata.py
Dry run enabled. No changes will be made to the Tautulli database.
Retrieving all history items from the Tautulli database...
Traceback (most recent call last):
  File "./update_all_metadata.py", line 139, in <module>
    main()
  File "./update_all_metadata.py", line 64, in main
    r = requests.get(TAUTULLI_URL.rstrip('/') + '/api/v2', params=params).json()
  File "/usr/local/lib/python2.7/site-packages/requests/models.py", line 897, in json
    return complexjson.loads(self.text, **kwargs)
  File "/usr/local/lib/python2.7/json/__init__.py", line 339, in loads
    return _default_decoder.decode(s)
  File "/usr/local/lib/python2.7/json/decoder.py", line 364, in decode
    obj, end = self.raw_decode(s, idx=_w(s, 0).end())
  File "/usr/local/lib/python2.7/json/decoder.py", line 382, in raw_decode
    raise ValueError("No JSON object could be decoded")
ValueError: No JSON object could be decoded

@Dekalox
Copy link

Dekalox commented Mar 4, 2019

Hey, i'm running the script and a connection to the PMS can be made. Some history titles are getting pulled from the database (not recent ones it seems) but the script aborts after trying to get all the items next. Can anybody tell me whats happening here?

sudo python update_all_metadata.py
Dry run enabled. No changes will be made to the Tautulli database.
Retrieving all history items from the Tautulli database...
...Invalid guid for 'Schulmädchenreport 8' in the Tautulli database. Guid: local://20530
...Invalid guid for 'Schulmädchenreport 8' in the Tautulli database. Guid: local://20530
...Invalid guid for 'One Tree Hill' in the Tautulli database. Guid: local://29097
...Invalid guid for 'One Tree Hill' in the Tautulli database. Guid: local://29097
...Invalid guid for 'Bud Spencer Und Terence Hill Collection 43 Zwei Sind Nicht Zu Bremsen' in the Tautulli database. Guid: local://25427
...Invalid guid for 'Bud Spencer Und Terence Hill Collection 43 Zwei Sind Nicht Zu Bremsen' in the Tautulli database. Guid: local://25427
...Invalid guid for 'Bud Spencer Und Terence Hill Collection 43 Zwei Sind Nicht Zu Bremsen' in the Tautulli database. Guid: local://25427
...Invalid guid for 'Bud Spencer Und Terence Hill Collection 08 Ruf Der Wälder' in the Tautulli database. Guid: local://14773
...Invalid guid for 'Family Guy' in the Tautulli database. Guid: local://30333
...Invalid guid for 'Family Guy' in the Tautulli database. Guid: local://30333
...Invalid guid for 'Family Guy' in the Tautulli database. Guid: local://30334
...Invalid guid for 'Who Is America?' in the Tautulli database. Guid: local://30718
...Invalid guid for 'Who Is America?' in the Tautulli database. Guid: local://30719
...Invalid guid for 'Final Space' in the Tautulli database. Guid: local://30730
...Invalid guid for 'Final Space' in the Tautulli database. Guid: local://30731
...Invalid guid for 'Final Space' in the Tautulli database. Guid: local://30732
...Invalid guid for 'Family Guy' in the Tautulli database. Guid: local://30326
...Invalid guid for 'Family Guy' in the Tautulli database. Guid: local://30335
...Invalid guid for 'Family Guy' in the Tautulli database. Guid: local://30335
...Invalid guid for 'Family Guy' in the Tautulli database. Guid: local://30336
...Invalid guid for 'Family Guy' in the Tautulli database. Guid: local://30336
...Invalid guid for 'Family Guy' in the Tautulli database. Guid: local://30337
...Invalid guid for 'Family Guy' in the Tautulli database. Guid: local://30338
...Invalid guid for 'Family Guy' in the Tautulli database. Guid: local://30339
...Invalid guid for 'Family Guy' in the Tautulli database. Guid: local://30340
...Invalid guid for 'Family Guy' in the Tautulli database. Guid: local://30341
...Invalid guid for '153 Butch Cassidy Und Sundance Kid German' in the Tautulli database. Guid: tt0064115
...Invalid guid for 'The Walking Dead' in the Tautulli database. Guid: local://30991
...Invalid guid for 'Family Guy' in the Tautulli database. Guid: local://30342
...Invalid guid for 'Family Guy' in the Tautulli database. Guid: local://30343
...Invalid guid for 'Franklin Zauberhafte Weihnachten' in the Tautulli database. Guid: local://21661
...Invalid guid for 'Franklin Zauberhafte Weihnachten' in the Tautulli database. Guid: local://21661
...Invalid guid for 'Walt Disneys Micky Maus Wunderhaus Wunderhaus Maerchen German' in the Tautulli database. Guid: local://11533
...Invalid guid for 'Walt Disneys Micky Maus Wunderhaus Wunderhaus Maerchen German' in the Tautulli database. Guid: local://11533
...Invalid guid for 'Family Guy' in the Tautulli database. Guid: local://30344
...Invalid guid for '20150116_204243A' in the Tautulli database. Guid: com.plexapp.agents.none://254d86c3a60ecd8bcfa88ae1684a0574bd36fe5c?lang=xn
...Invalid guid for '20150116_204243A' in the Tautulli database. Guid: com.plexapp.agents.none://254d86c3a60ecd8bcfa88ae1684a0574bd36fe5c?lang=xn
Retrieving all library items from the Plex server...
Traceback (most recent call last):
File "update_all_metadata.py", line 138, in
main()
File "update_all_metadata.py", line 93, in main
plex = PlexServer(PLEX_URL, PLEX_TOKEN)
File "/usr/local/lib/python2.7/dist-packages/plexapi/server.py", line 102, in init
data = self.query(self.key, timeout=timeout)
File "/usr/local/lib/python2.7/dist-packages/plexapi/server.py", line 343, in query
response = method(url, headers=headers, timeout=timeout, **kwargs)
File "/usr/local/lib/python2.7/dist-packages/requests/sessions.py", line 546, in get
return self.request('GET', url, **kwargs)
File "/usr/local/lib/python2.7/dist-packages/requests/sessions.py", line 533, in request
resp = self.send(prep, **send_kwargs)
File "/usr/local/lib/python2.7/dist-packages/requests/sessions.py", line 646, in send
r = adapter.send(request, **kwargs)
File "/usr/local/lib/python2.7/dist-packages/requests/adapters.py", line 498, in send
raise ConnectionError(err, request=request)
requests.exceptions.ConnectionError: ('Connection aborted.', error(0, 'Error'))

@ac123
Copy link

ac123 commented May 14, 2019

I received error
Error retrieving Tautulli history: SQL not enabled for the API.
any ideas on how to enable it

@haklc
Copy link

haklc commented Jul 26, 2019

Has someone adapted this for music libraries? If so, please share your code.

@captainnapalm
Copy link

I think I'm missing something obvious. Does this script need to be run somewhere specific? I'm getting the following error which I don't know how to resolve.

Traceback (most recent call last): File "update_all_metadata.py", line 8, in <module> from plexapi.server import PlexServer ImportError: No module named plexapi.server

@joffer77
Copy link

I think I'm missing something obvious. Does this script need to be run somewhere specific? I'm getting the following error which I don't know how to resolve.

Traceback (most recent call last): File "update_all_metadata.py", line 8, in <module> from plexapi.server import PlexServer ImportError: No module named plexapi.server

make it executable and run it:

chmod +x update_all_metadata.py
./update_all_metadata.py

That worked for me to get past that error..

@TheNewAcct
Copy link

TheNewAcct commented Sep 2, 2019

Should Tautulli be running or shut down when running this? Assume shut down but wanted to check

@SawkeeReemo
Copy link

I think I'm missing something obvious. Does this script need to be run somewhere specific? I'm getting the following error which I don't know how to resolve.
Traceback (most recent call last): File "update_all_metadata.py", line 8, in <module> from plexapi.server import PlexServer ImportError: No module named plexapi.server

make it executable and run it:

chmod +x update_all_metadata.py
./update_all_metadata.py

That worked for me to get past that error..

I put this script in the Tautulli folder on my source machine. I tried to run it from there and got this "no module named plexapi.server" error. Then I did what you said by using the two commands you listed here... and I still get that same error. Any ideas on how to get this to run? (My source machine is a Mac, and I've migrated my Plex server over to my Synology NAS)

@iceman73
Copy link

I think I'm missing something obvious. Does this script need to be run somewhere specific? I'm getting the following error which I don't know how to resolve.
Traceback (most recent call last): File "update_all_metadata.py", line 8, in <module> from plexapi.server import PlexServer ImportError: No module named plexapi.server

make it executable and run it:

chmod +x update_all_metadata.py
./update_all_metadata.py

That worked for me to get past that error..

I put this script in the Tautulli folder on my source machine. I tried to run it from there and got this "no module named plexapi.server" error. Then I did what you said by using the two commands you listed here... and I still get that same error. Any ideas on how to get this to run? (My source machine is a Mac, and I've migrated my Plex server over to my Synology NAS)

Ensure you have the following python modules installed, by issuing the following commands

pip install plexapi
pip install requests
pip install mock

@Dubardo
Copy link

Dubardo commented Jun 3, 2020

I'm trying to make this work and it doesn't. I'm on windows 10. Tried to run it as Tautulli with a shortcut with:
"C:\Program Files (x86)\Python27\python.exe" D:\Plex\Tautulli\SCRIPT\update_all_metadata.py
as a command. It opens a cmd prompt but close as fast with not idea what's the error.

@sherwinmartin
Copy link

I received error
Error retrieving Tautulli history: SQL not enabled for the API.
any ideas on how to enable it

if you check the code regarding SQL on line 18-19...

NOTE: Script requires 'api_sql = 1' to be enabled in the Tautulli config.ini file.

Tautulli must be shut down before editing the config file.

so edit the config.ini file and change api_sql = 1. by default it set to 0

just in case you haven't reviewed code comments below this line, you will have to set dry run from True to False. otherwise, it will run but not save in the database. hope that helps

@JDA88
Copy link

JDA88 commented Feb 11, 2021

The script works great but I have an edge case.

I have 2 movies libraries (on the same plex server) that contains exactly the same files. Weird? Not completely, the reason behind this is to provide a feature to my users that Plex doesn’t provide, one of the library have titles and synopsis in English (for English users) the other is in French, for.. well, you get the point.

At first, I though the script will try to fix only items that are unmatched, but it is more aggressive than that and will try to fix “everything he can” and therefore try to match entries between my 2 libraries even though they still exist.

This might also be an issue where some people maintain multiple versions of the same movies in differences libraries (720p/4k) to avoid unnecessary transcoding.

Is there a way to tell the script to fix only orphan entries?

@Hyphesten
Copy link

hyphesten@Debian-109-buster-64-minimal:$ sudo python3 -m pip install plexapi
Collecting plexapi
Downloading https://files.pythonhosted.org/packages/cb/b2/a44953441fc5484ea548392daa5be6e90e2283f15e6df03b4cd8b4548432/PlexA PI-4.6.1-py3-none-any.whl (131kB)
100% |████████████████████████████████| 133kB 6.9MB/s
Collecting requests (from plexapi)
Using cached https://files.pythonhosted.org/packages/92/96/144f70b972a9c0eabbd4391ef93ccd49d0f2747f4f6a2a2738e99e5adc65/requ ests-2.26.0-py2.py3-none-any.whl
Collecting urllib3<1.27,>=1.21.1 (from requests->plexapi)
Using cached https://files.pythonhosted.org/packages/5f/64/43575537846896abac0b15c3e5ac678d787a4021e906703f1766bfb8ea11/urll ib3-1.26.6-py2.py3-none-any.whl
Collecting certifi>=2017.4.17 (from requests->plexapi)
Using cached https://files.pythonhosted.org/packages/05/1b/0a0dece0e8aa492a6ec9e4ad2fe366b511558cdc73fd3abc82ba7348e875/cert ifi-2021.5.30-py2.py3-none-any.whl
Collecting charset-normalizer
=2.0.0; python_version >= "3" (from requests->plexapi)
Downloading https://files.pythonhosted.org/packages/c4/1d/e6ce112f7237fc746e632e1cbdc24890cad95505c6cd4b711f4fd17f4735/chars et_normalizer-2.0.3-py3-none-any.whl
Collecting idna<4,>=2.5; python_version >= "3" (from requests->plexapi)
Using cached https://files.pythonhosted.org/packages/d7/77/ff688d1504cdc4db2a938e2b7b9adee5dd52e34efbd2431051efc9984de9/idna -3.2-py3-none-any.whl
Installing collected packages: urllib3, certifi, charset-normalizer, idna, requests, plexapi
Successfully installed certifi-2021.5.30 charset-normalizer-2.0.3 idna-3.2 plexapi-4.6.1 requests-2.26.0 urllib3-1.26.6
hyphesten@Debian-109-buster-64-minimal:$ python3 -m pip install requests
WARNING: Value for scheme.platlib does not match. Please report this to pypa/pip#10151
distutils: /usr/local/lib/python3.7/dist-packages
sysconfig: /usr/lib/python3.7/site-packages
WARNING: Value for scheme.purelib does not match. Please report this to pypa/pip#10151
distutils: /usr/local/lib/python3.7/dist-packages
sysconfig: /usr/lib/python3.7/site-packages
WARNING: Value for scheme.headers does not match. Please report this to pypa/pip#10151
distutils: /usr/local/include/python3.7/UNKNOWN
sysconfig: /usr/include/python3.7m/UNKNOWN
WARNING: Value for scheme.scripts does not match. Please report this to pypa/pip#10151
distutils: /usr/local/bin
sysconfig: /usr/bin
WARNING: Value for scheme.data does not match. Please report this to pypa/pip#10151
distutils: /usr/local
sysconfig: /usr
WARNING: Additional context:
user = False
home = None
root = None
prefix = None
Defaulting to user installation because normal site-packages is not writeable
Requirement already satisfied: requests in /usr/local/lib/python3.7/dist-packages (2.26.0)
Requirement already satisfied: certifi>=2017.4.17 in /usr/local/lib/python3.7/dist-packages (from requests) (2021.5.30)
Requirement already satisfied: charset-normalizer
=2.0.0 in /usr/local/lib/python3.7/dist-packages (from requests) (2.0.3)
Requirement already satisfied: idna<4,>=2.5 in /usr/local/lib/python3.7/dist-packages (from requests) (3.2)
Requirement already satisfied: urllib3<1.27,>=1.21.1 in /usr/local/lib/python3.7/dist-packages (from requests) (1.26.6)
WARNING: Value for scheme.headers does not match. Please report this to pypa/pip#10151
distutils: /home/hyphesten/.local/include/python3.7m/UNKNOWN
sysconfig: /home/hyphesten/.local/include/python3.7/UNKNOWN
WARNING: Additional context:
user = True
home = None
root = None
prefix = None
hyphesten@Debian-109-buster-64-minimal:$ python3 update_all_metadata.py
Dry run enabled. No changes will be made to the Tautulli database.
Retrieving all history items from the Tautulli database...
Traceback (most recent call last):
File "update_all_metadata.py", line 192, in
main()
File "update_all_metadata.py", line 70, in main
r = requests.get(TAUTULLI_URL.rstrip('/') + '/api/v2', params=params).json()
File "/usr/local/lib/python3.7/dist-packages/requests/models.py", line 910, in json
return complexjson.loads(self.text, **kwargs)
File "/usr/lib/python3.7/json/init.py", line 348, in loads
return _default_decoder.decode(s)
File "/usr/lib/python3.7/json/decoder.py", line 337, in decode
obj, end = self.raw_decode(s, idx=_w(s, 0).end())
File "/usr/lib/python3.7/json/decoder.py", line 355, in raw_decode
raise JSONDecodeError("Expecting value", s, err.value) from None
json.decoder.JSONDecodeError: Expecting value: line 2 column 1 (char 1)
hyphesten@Debian-109-buster-64-minimal:
$ python3 update_all_metadata.py
Not dry run. Creating a backup of the Tautulli database.
Retrieving all history items from the Tautulli database...
Traceback (most recent call last):
File "update_all_metadata.py", line 192, in
main()
File "update_all_metadata.py", line 70, in main
r = requests.get(TAUTULLI_URL.rstrip('/') + '/api/v2', params=params).json()
File "/usr/local/lib/python3.7/dist-packages/requests/models.py", line 910, in json
return complexjson.loads(self.text, **kwargs)
File "/usr/lib/python3.7/json/init.py", line 348, in loads
return _default_decoder.decode(s)
File "/usr/lib/python3.7/json/decoder.py", line 337, in decode
obj, end = self.raw_decode(s, idx=_w(s, 0).end())
File "/usr/lib/python3.7/json/decoder.py", line 355, in raw_decode
raise JSONDecodeError("Expecting value", s, err.value) from None
json.decoder.JSONDecodeError: Expecting value: line 2 column 1 (char 1)

To be honest, i'm a noob, so i don't know how to fix this problem

@adefaria
Copy link

adefaria commented Aug 25, 2021

I received error
Error retrieving Tautulli history: SQL not enabled for the API.
any ideas on how to enable it

Indeed. This is not working:

Jupiter:./update_all_metadata.py 
Not dry run. Creating a backup of the Tautulli database.
Retrieving all history items from the Tautulli database...
Error retrieving Tautulli history: SQL not enabled for the API.
Exiting script...
Done.
Jupiter:grep api_sql config.ini 
api_sql = 1
Jupiter:

Note: Updated my Docker container for Tautulli to V2.7.5, re-ran the script and it worked. Lots of improvement could go into this script.

Anyway, initially, this didn't work for me until I updated the Docker container for Tautulli. Then it supposedly worked but if I go to the Tautulli web page and look under libraries I see no changes. Many of the libraries say there's nothing there like under the column Total Episodes/Tracks. For example, my Movies library, which has some 7000 movies, lists that there are none.

Note I'm on Synology DSM 7.x. Suddenly my Plex database got corrupted. I tried to repair it several ways but none worked. I had to eventually delete the database and have Plex recreate it. That's when I noticed that Tautulli was messed up too. This didn't fix it.

@THGhost
Copy link

THGhost commented Nov 29, 2022

Thanks for this. Very easy to set up. My server died for no reason and had to make a new one.

@nebb00
Copy link

nebb00 commented Sep 1, 2023

I have a handful of unsupported guid. But cannot figure out why.

Unsupported guid for 'Battlestar Galactica' in the Tautulli database [Guid: local://94409]. Skipping...

@declan727
Copy link

I am getting this when it gets to communicating with Plex. I have 4 Movie Libraries and it appears to fail on the first one. I am trying to merge libraries in Tautulli after I had a failure on Plex and had to reinstall. Any help would be appreciated. thanks

Retrieving all library items from the Plex server...
Scanning library: Concerts
Traceback (most recent call last):
File "update_all_metadata.py", line 214, in
main()
File "update_all_metadata.py", line 129, in main
for guid in item.guids: # Also parse tags for new Plex agents
File "/home/bcarty/.local/lib/python3.8/site-packages/plexapi/base.py", line 279, in getattribute
value = super(PlexPartialObject, self).getattribute(attr)
AttributeError: 'Movie' object has no attribute 'guids'

@rdenimal
Copy link

Worked like a charm, thanks for the script!

@mikedorsi
Copy link

mikedorsi commented Mar 19, 2024

I had removed some media in-between shutting down my old Plex server and spinning up a new one. So when I went to merge the libraries with this script it would hang. So I added a print command to see what it was hanging on so I could manually fix it in Tautulli.

Added on line 154:
`for id, (old_rating_key, old_type, title, year) in old_key_map.items():
new_rating_key, new_type, _, _ = new_key_map.get(id, (None, None, None, None))
new_year = None
warning_year = False

    print("\t{title} is NEXT!".format(
                  title=title))`

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment