cmathews393 / spotify-to-plex

A python script/app to sync Spotify Playlists into Plex (optionally via Lidarr)
GNU General Public License v3.0
34 stars 3 forks source link

Copy Playlist Poster Art from Spotify Playlist to Plex Playlist #25

Closed AverageDave93 closed 1 week ago

AverageDave93 commented 1 week ago

Add the abilty to copy Playlist Poster Art from Spotify Playlist to Plex Playlist

I used to use "https://github.com/rnagabhyrava/plex-playlist-sync" and have recently moved over to this, however one thing that is missing to make this perfect that plex-playlis-sync had was the ability to copy Playlist Poster Art from Spotify Playlist to Plex Playlist

Ive added photos to show the diference it makes.

image

image

AverageDave93 commented 1 week ago

P.S. Thank you for making this

AverageDave93 commented 1 week ago

ok so ive changed the plex.py and spotify.py and now it copys the playlist cover art too

Plex.py

import requests
import urllib3
from plexapi.server import PlexServer
from plexapi.exceptions import BadRequest, NotFound
from confighandler import read_config
import copy

class PlexService:
    def __init__(self):
        self.config = read_config("plex")
        self.server_url = self.config.get("url")
        self.server_token = self.config.get("api_key")
        self.plex = self.connect_plex()
        self.replace = self.config.get("replace")

    def connect_plex(self):
        session = requests.Session()
        session.verify = False
        urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
        return PlexServer(self.server_url, self.server_token, session=session)

    def check_tracks_in_plex(self, spotify_tracks):
        print("Checking tracks in plex")
        music_lib = self.plex.library.section("Music")
        plex_tracks = []
        orig_tracks = []

        for track_name, artist_name in spotify_tracks:
            artist_tracks_in_plex = music_lib.search(title=artist_name)
            if artist_tracks_in_plex:
                for track in artist_tracks_in_plex:
                    try:
                        plex_track = track.track(title=track_name)

                        # Once we find a matching track, we want to break out of this iteration to get to the next song
                        if plex_track:
                            break
                    except NotFound:
                        # This is not really an exception, just continue
                        continue
                    except (Exception, BadRequest) as plex_search_exception:
                        print(f"Exception trying to search for title={artist_name}, title={track_name}")
                        print(plex_search_exception)
                        # While this is a fatal exception to the specific search, continue on since a subsequent match may succeed
                        continue

                if plex_track:
                    plex_tracks.append(plex_track)
                else:
                    print("Song not in plex!")
                    print(f"Found artists for '{artist_name}' ({len(artist_tracks_in_plex)})")
                    print(f"Attempted to match song '{track_name}', but could not!")
                    orig_tracks.append([track_name, "Song Not in Plex"])
            else:
                print(f"No results found for artist: {artist_name}")
                continue

        print(f"Found {len(plex_tracks)} of possible {len(spotify_tracks)} (Failed to find {len(orig_tracks)})")

        return plex_tracks

    def create_or_update_playlist(
        self, playlist_name, playlist_id, tracks
    ):
        existing_playlist = self.find_playlist_by_name(playlist_name)
        if existing_playlist:
            if self.replace:
                existing_playlist.delete()
                return self.create_playlist(playlist_name, playlist_id, tracks)
            else:
                existing_playlist.addItems(tracks)
                return existing_playlist
        else:
            return self.create_playlist(playlist_name, playlist_id, tracks)

    def find_playlist_by_name(self, playlist_name):
        playlists = self.plex.playlists()
        for playlist in playlists:
            if playlist.title == playlist_name:
                return playlist
        return None

# Playlist is created in intervals of 300 since Plex's API will return a 414 URL TOO LONG inconsistently past that
    def create_playlist(self, playlist_name, playlist_id, tracks):
        tracks_to_add = copy.deepcopy(tracks)
        try:
            iteration_tracks = tracks_to_add[:300]
            del tracks_to_add[:300]
            new_playlist = self.plex.createPlaylist(playlist_name, items=iteration_tracks)

            while len(tracks_to_add) > 0:
                iteration_tracks = tracks_to_add[:300]
                del tracks_to_add[:300]
                new_playlist.addItems(iteration_tracks)
            return new_playlist
        except Exception as e:
            print(f"Error creating playlist {playlist_name}: {e}")
            return None

Spotify.py

import spotipy
from spotipy.oauth2 import SpotifyClientCredentials
from datetime import date
from confighandler import read_config

class SpotifyService:
    def __init__(self):
        self.config = read_config("spotify")
        self.client_id = self.config.get("client_id")
        self.client_secret = self.config.get("api_key")
        self.sp = self.connect_spotify()
        self.playlist_covers = {}  # Initialize an attribute to store playlist cover URLs

    def connect_spotify(self):
        auth_manager = SpotifyClientCredentials(
            client_id=self.client_id, client_secret=self.client_secret
        )
        return spotipy.Spotify(auth_manager=auth_manager)

    def get_playlist_tracks(self, playlist_id):
        tracks = []
        try:
            results = self.sp.playlist_tracks(playlist_id)
            while results:
                tracks.extend(
                    [
                        (item["track"]["name"], item["track"]["artists"][0]["name"])
                        for item in results["items"]
                    ]
                )
                results = self.sp.next(results) if results["next"] else None
        except Exception as e:
            print(f"Error fetching tracks from Spotify: {e}")
        return tracks

    def get_playlist_name(self, playlist_id):
        try:
            playlist_data = self.sp.playlist(playlist_id, fields=["name"])
            name = playlist_data["name"]
            if "Discover Weekly" in name or "Daily Mix" in name:
                name = f"{name} {date.today()}"
            return name
        except Exception as e:
            print(f"Error retrieving playlist name: {e}")
            return None

    def get_playlist_cover(self, playlist_id):
        try:
            playlist_data = self.sp.playlist(playlist_id, fields=["images"])
            if playlist_data["images"]:
                cover_url = playlist_data["images"][0]["url"]  # Get the URL of the first image (usually highest resolution)
                self.playlist_covers[playlist_id] = cover_url  # Store cover URL in attribute for future reference
                return cover_url
            else:
                print(f"No cover art found for playlist {playlist_id}")
                return None
        except Exception as e:
            print(f"Error retrieving playlist cover art: {e}")
            return None