cmathews393 / spotify-to-plex

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

Error processing playlist: cannot access local variable 'plex_track' where it is not associated with a value #26

Closed AverageDave93 closed 3 months ago

AverageDave93 commented 3 months ago

Im not sure about this one as it seems like everyhing is functioning as it should. (The Playlists are being created in Plex from the Spotify import list in Lidarr.)

But i keep getting this error for every playlist;

Error processing playlist '37i9dQZF1DX32NsLKyzScr': cannot access local variable 'plex_track' where it is not associated with a value

'37i9dQZF1DX32NsLKyzScr' changing for each aplicable playlist

cmathews393 commented 3 months ago

Can you verify if there are missing tracks? Think I know where the issue lies but want to confirm. Meaning, tracks in Spotify, not in Plex?

AverageDave93 commented 3 months ago

spotiplex (2).csv

It looks like a lot of missing but ive attached my log so you can see

AverageDave93 commented 3 months ago

not sure if applicable but im running on a synology nas, Installation was done using the following;

docker run -d --name=spotiplex \ -e SPOTIPLEX_LIDARR_SYNC=True \ -e USERS=REDACTED \ -e WORKERS=5 \ -e REPLACE=False \ -e INTERVAL=86400 \ -e SPOTIPLEX_MANUAL_PLAYLISTS= \ -e SPOTIFY_API_KEY=REDACTED \ -e SPOTIFY_API_ID=REDACTED \ -e PLEX_URL=http://192.168.50.153:32400 \ -e PLEX_TOKEN=REDACTED \ -e LIDARR_IP=http://192.168.50.153:8686 \ -e LIDARR_TOKEN=REDACTED \ --restart unless-stopped \ 0xchloe/spotiplex:latest

AverageDave93 commented 3 months ago

ok so the below scripts get rid of this issue and also add the funcionality to grab playlist poster 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
from spotify import SpotifyService  # Import the SpotifyService class

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")
        self.spotify_service = SpotifyService()  # Initialize an instance of SpotifyService

    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:
                plex_track = None
                for track in artist_tracks_in_plex:
                    try:
                        plex_track = track.track(title=track_name)
                        if plex_track:
                            break  # Found the track, break out of the loop
                    except NotFound:
                        # Handle not found case if needed
                        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)
                        continue

                if not plex_track:
                    print(f"Failed to find track '{track_name}' for artist '{artist_name}'")
                    orig_tracks.append([track_name, "Track Not Found"])
                else:
                    plex_tracks.append(plex_track)
            else:
                print(f"No results found for artist: {artist_name}")

        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()
                new_playlist = self.create_playlist(playlist_name, playlist_id, tracks)
            else:
                existing_playlist.addItems(tracks)
                new_playlist = existing_playlist
        else:
            new_playlist = self.create_playlist(playlist_name, playlist_id, tracks)

        # Set playlist poster if new playlist is created
        if new_playlist:
            self.set_playlist_poster(new_playlist, playlist_id)

        return new_playlist

    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

    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

    def set_playlist_poster(self, playlist, playlist_id):
        try:
            cover_url = self.spotify_service.get_playlist_cover(playlist_id)
            if cover_url:
                playlist.uploadPoster(url=cover_url)
                print(f"Playlist poster set successfully for {playlist.title}")
            else:
                print(f"No cover art found for playlist {playlist_id}")
        except requests.exceptions.RequestException as e:
            print(f"Request error setting playlist poster: {e}")
        except Exception as e:
            print(f"Error setting playlist poster: {e}")

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()

    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 and playlist_data["images"]:
                cover_url = playlist_data["images"][0]["url"]
                return cover_url
            else:
                print(f"No cover art found for playlist {playlist_id}")
                return None
        except Exception as e:
            print(f"Error retrieving cover art for playlist {playlist_id}: {e}")
            return None
AverageDave93 commented 3 months ago

@cmathews393 if you decide to use these can you let me know as im currently using my branched image but would prefer to use yours going forward haha

cmathews393 commented 3 months ago

Currently rewriting a lot of stuff anyways, I'll include the cover art stuff and fix this and a few other bugs hopefully tomorrow or this weekend and update the issues.

AverageDave93 commented 3 months ago

@cmathews393 woould there also be a way to wait a period of time once the script finishes and loop through again? as im running in docker and it looks lke it starts when the container starts, but when i finishes, thats it and then ill have to manually restart after a period of time to get it to cycle again.

cmathews393 commented 3 months ago

It should be doing that already but it's broken I think. New version is going to use supercronic to schedule a cronjob in the container based on environment variables I think, but I need to test it a bit more.

AverageDave93 commented 3 months ago

that sounds perfect! i cant wait haha also are you planning any imports back into lidarr of missing tracks?

cmathews393 commented 3 months ago

Not sure what you mean I guess. If you're trying to import all the tracks from a spotify list into lidarr, you can do that natively in lidarr? I suppose I could work on something that initiates a manual search if something is missing, but that definitely won't be in this update.

AverageDave93 commented 3 months ago

yep 100 percent just being dumb, ignore me please lol

cmathews393 commented 3 months ago

Trying to build the latest version with supercronic and everything now, gonna take a minute to fix some issues but I'll probably have it working tomorrow evening (including cover art stuff)

cmathews393 commented 3 months ago

@AverageDave93 latest version should include the fixes requested. Open an issue if you find a bug :)