Import playlists into Plex using plexapi
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python3 | |
""" | |
Import playlists into a local Plex server. I used this to import my Google Play | |
Music playlists when it was being sunset. This consumes the JSON output by my | |
playlist-reconstructor.py at https://gist.github.com/cpeel/e18d3e1d6f9881039b6a2e024fb5dce1 | |
The script will die if it can't find a track, only creates a playlist if it can | |
find all the tracks, and will skip playlists that already exist. This allows the | |
script to be used in an iterative process by tweaking the track title contents | |
in the JSON file as necessary and rerunning it. If you set the "Removed" field | |
to 1 in the JSON, this will skip the track and continue. | |
This is a pure hack job but it only had to work once and it saved me hours | |
of time recreating dozens of ordered playlists. | |
I'm posting it as a gist primarily as an example for how to search for tracks | |
(and artists) in using plexapi and create playlists. | |
You will need to populate the global variables to values for your Plex server | |
and library. | |
""" | |
import json | |
import sys | |
import plexapi | |
from plexapi.server import PlexServer | |
# URL and token to your Plex server. See also: | |
# https://support.plex.tv/articles/204059436-finding-an-authentication-token-x-plex-token/ | |
baseurl = "http://localhost:32400" | |
token = "TOKEN" | |
# Your Plex library name | |
library_name = "My Music" | |
# JSON playlist file | |
playlist_json_file = "playlists.json" | |
def main(): | |
# connect to plex | |
plex = PlexServer(baseurl, token) | |
# load all playlist names from Plex | |
plex_playlists = [] | |
for playlist in plex.playlists(): | |
plex_playlists.append(playlist.title) | |
plex_library = plex.library.section(library_name) | |
# load playlists from JSON | |
with open(playlist_json_file) as inputfile: | |
playlists = json.load(inputfile) | |
for playlist in playlists: | |
if playlist["Title"] in plex_playlists: | |
print(f"Skipping {playlist['Title']}") | |
continue | |
print(f"Importing {playlist['Title']}") | |
plex_items = [] | |
for track in playlist["Tracks"]: | |
if track["Removed"]: | |
print(f"Skipping {track['Title']}") | |
continue | |
plex_track = None | |
print(f"Searching for {track['Title']}") | |
# Find the plex items matching on title and artist | |
results = plex_library.search(track["Title"], libtype="track") | |
if len(results) == 1: | |
plex_track = results[0] | |
else: | |
for result in results: | |
if result.artist().title == track["Artist"]: | |
plex_track = result | |
break | |
if not plex_track: | |
# ok try to find it by artist then title and normalize the | |
# track title a bit to improve matching | |
print("Falling back to artist search") | |
normalized_track_names = [ | |
track["Title"].lower(), | |
track["Title"].lower().replace("’", "'"), | |
track["Title"].lower().replace("'", "’"), | |
track["Title"].lower().replace("“", '"'), | |
track["Title"].lower().replace('"', "“"), | |
track["Title"].lower().replace("&", "and"), | |
track["Title"].lower().replace("and", "&"), | |
] | |
results = plex_library.search(track["Artist"], libtype="artist") | |
for result in results: | |
for search_track in result.tracks(): | |
if search_track.title.lower() in normalized_track_names: | |
plex_track = search_track | |
break | |
if plex_track: | |
break | |
if plex_track: | |
plex_items.append(plex_track) | |
else: | |
print( | |
f"Unable to find track for {track['Title']} by {track['Artist']}" | |
) | |
sys.exit(1) | |
plexapi.playlist.Playlist.create(plex, playlist["Title"], plex_items) | |
if __name__ == "__main__": | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment