2025-06-25 13:53:24 +02:00
|
|
|
# plex_playlist_sync.py
|
2025-06-25 13:17:58 +02:00
|
|
|
|
2025-06-25 12:31:23 +00:00
|
|
|
#from config import (
|
|
|
|
|
# LASTFM_API_KEY, LASTFM_API_SECRET, LASTFM_USERNAME,
|
|
|
|
|
# PLEX_BASEURL, PLEX_TOKEN,
|
|
|
|
|
# MIN_PLAYS, RECENT_MONTHS, MAX_SIMILAR_PER_ART, SIMILAR_MATCH_MIN,
|
|
|
|
|
# CACHE_TTL_HOURS, DEBUG_PRINT
|
|
|
|
|
#)
|
|
|
|
|
from config import *
|
|
|
|
|
|
2025-06-25 13:53:24 +02:00
|
|
|
# Importera relevanta funktioner från lastfm_helpers
|
|
|
|
|
from lastfm_helpers import recent_artists, get_artist_top_tracks
|
2025-06-25 13:17:58 +02:00
|
|
|
from musicbrainz_helpers import load_cache, save_cache # Används för cache
|
|
|
|
|
|
2025-06-25 13:53:24 +02:00
|
|
|
import pylast # Behövs för pylast.LastFMNetwork initiering i denna fil
|
2025-06-25 13:17:58 +02:00
|
|
|
from plexapi.server import PlexServer
|
|
|
|
|
import datetime
|
|
|
|
|
import time
|
|
|
|
|
import logging
|
|
|
|
|
|
|
|
|
|
logging.basicConfig(level=logging.INFO)
|
|
|
|
|
log = logging.getLogger("PlexPlaylistSync")
|
|
|
|
|
|
2025-06-25 13:53:24 +02:00
|
|
|
# Initialisera Last.fm-nätverket i denna fil också, om det behövs här.
|
|
|
|
|
# OBS: lastfm_helpers initialiserar det globalt, så du kan potentiellt ta bort detta block
|
|
|
|
|
# om du är säker på att lastfm_helpers alltid laddas först och initierar nätverket korrekt.
|
|
|
|
|
# Men för att vara säker, kan du behålla det eller flytta initieringen till en central punkt.
|
|
|
|
|
LASTFM_NETWORK = None
|
2025-06-25 13:17:58 +02:00
|
|
|
try:
|
|
|
|
|
LASTFM_NETWORK = pylast.LastFMNetwork(
|
|
|
|
|
api_key=LASTFM_API_KEY,
|
|
|
|
|
api_secret=LASTFM_API_SECRET,
|
|
|
|
|
username=LASTFM_USERNAME
|
|
|
|
|
)
|
|
|
|
|
except Exception as e:
|
2025-06-25 13:53:24 +02:00
|
|
|
log.error(f"Failed to initialize Last.fm network. Check keys/username: {e}")
|
2025-06-25 13:17:58 +02:00
|
|
|
LASTFM_NETWORK = None
|
|
|
|
|
|
2025-06-25 13:53:24 +02:00
|
|
|
# get_artist_top_tracks funktionen behöver INTE definieras här
|
|
|
|
|
# om den importeras från lastfm_helpers.py
|
|
|
|
|
# Jag tar bort den härifrån, så den används korrekt importerad.
|
2025-06-25 13:17:58 +02:00
|
|
|
|
|
|
|
|
def create_lastfm_recommended_playlist():
|
|
|
|
|
start_time = time.time()
|
2025-06-25 13:53:24 +02:00
|
|
|
log.info("🚀 Starting process to create Last.fm-based playlists in Plex.")
|
2025-06-25 13:17:58 +02:00
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
plex = PlexServer(PLEX_BASEURL, PLEX_TOKEN)
|
2025-06-25 13:53:24 +02:00
|
|
|
log.info(f"✅ Connected to Plex Media Server: {plex.baseurl}")
|
2025-06-25 13:17:58 +02:00
|
|
|
except Exception as e:
|
2025-06-25 13:53:24 +02:00
|
|
|
log.error(f"❌ Could not connect to Plex Server: {e}")
|
|
|
|
|
log.error("Check PLEX_BASEURL and PLEX_TOKEN in config_local.py.")
|
2025-06-25 13:17:58 +02:00
|
|
|
return
|
|
|
|
|
|
|
|
|
|
music_library = None
|
|
|
|
|
try:
|
|
|
|
|
music_library = plex.library.section('Music')
|
2025-06-25 13:53:24 +02:00
|
|
|
log.info(f"Using Plex library: '{music_library.title}'")
|
2025-06-25 13:17:58 +02:00
|
|
|
except Exception as e:
|
2025-06-25 13:53:24 +02:00
|
|
|
log.error(f"❌ Could not find 'Music' library in Plex: {e}")
|
|
|
|
|
log.error("Check your music library name in Plex.")
|
2025-06-25 13:17:58 +02:00
|
|
|
return
|
|
|
|
|
|
|
|
|
|
artists_for_playlist = []
|
|
|
|
|
cache = load_cache()
|
|
|
|
|
added_artists_mbids = cache.get("added_artists", [])
|
|
|
|
|
|
2025-06-25 13:53:24 +02:00
|
|
|
log.info("Gathering artists for the playlist...")
|
|
|
|
|
# Get names for artists added by Lidarr sync
|
2025-06-25 13:17:58 +02:00
|
|
|
for mbid in added_artists_mbids:
|
|
|
|
|
try:
|
2025-06-25 13:53:24 +02:00
|
|
|
# Re-use the LASTFM_NETWORK initialized above or in lastfm_helpers
|
|
|
|
|
artist_info = LASTFM_NETWORK.get_artist_by_mbid(mbid) # Needs LASTFM_NETWORK from this file or lastfm_helpers
|
2025-06-25 13:17:58 +02:00
|
|
|
artists_for_playlist.append(artist_info.name)
|
|
|
|
|
except Exception:
|
2025-06-25 13:53:24 +02:00
|
|
|
log.debug(f"Could not find name for MBID: {mbid} via pylast.")
|
2025-06-25 13:17:58 +02:00
|
|
|
pass
|
|
|
|
|
|
2025-06-25 13:53:24 +02:00
|
|
|
# Add some of the most recently played artists as well
|
|
|
|
|
recent_played_artists = recent_artists() # Uses recent_artists from lastfm_helpers
|
2025-06-25 13:17:58 +02:00
|
|
|
for name, mbid in recent_played_artists[:10]:
|
|
|
|
|
if name not in artists_for_playlist:
|
|
|
|
|
artists_for_playlist.append(name)
|
|
|
|
|
|
|
|
|
|
if not artists_for_playlist:
|
2025-06-25 13:53:24 +02:00
|
|
|
log.info("No artists found to create a playlist. Ensure Last.fm sync has run.")
|
2025-06-25 13:17:58 +02:00
|
|
|
return
|
|
|
|
|
|
2025-06-25 13:53:24 +02:00
|
|
|
log.info(f"Found {len(artists_for_playlist)} unique artists to build the playlist.")
|
2025-06-25 13:17:58 +02:00
|
|
|
|
|
|
|
|
all_recommended_tracks_info = []
|
|
|
|
|
for artist_name in artists_for_playlist:
|
2025-06-25 13:53:24 +02:00
|
|
|
# Use the imported get_artist_top_tracks
|
2025-06-25 13:17:58 +02:00
|
|
|
top_tracks = get_artist_top_tracks(artist_name, limit=3)
|
|
|
|
|
all_recommended_tracks_info.extend(top_tracks)
|
|
|
|
|
|
|
|
|
|
unique_tracks_to_add = []
|
|
|
|
|
seen_track_identifiers = set()
|
|
|
|
|
|
2025-06-25 13:53:24 +02:00
|
|
|
log.info(f"Searching for {len(all_recommended_tracks_info)} potential tracks in Plex library...")
|
2025-06-25 13:17:58 +02:00
|
|
|
for track_info in all_recommended_tracks_info:
|
|
|
|
|
artist_name = track_info["artist"]
|
|
|
|
|
track_title = track_info["title"]
|
|
|
|
|
identifier = f"{artist_name}-{track_title}"
|
|
|
|
|
|
|
|
|
|
if identifier in seen_track_identifiers:
|
|
|
|
|
continue
|
|
|
|
|
|
|
|
|
|
found_plex_track = None
|
|
|
|
|
try:
|
|
|
|
|
results = music_library.search(f"{artist_name} {track_title}", libtype='track')
|
|
|
|
|
|
|
|
|
|
for item in results:
|
|
|
|
|
if item.artist() and item.artist().title.lower() == artist_name.lower() and item.title.lower() == track_title.lower():
|
|
|
|
|
found_plex_track = item
|
|
|
|
|
break
|
|
|
|
|
|
|
|
|
|
except Exception as e:
|
2025-06-25 13:53:24 +02:00
|
|
|
log.warning(f"Error searching Plex for '{track_title}' by '{artist_name}': {e}")
|
2025-06-25 13:17:58 +02:00
|
|
|
|
|
|
|
|
if found_plex_track:
|
|
|
|
|
unique_tracks_to_add.append(found_plex_track)
|
|
|
|
|
seen_track_identifiers.add(identifier)
|
2025-06-25 13:53:24 +02:00
|
|
|
log.info(f" ✅ Found: {found_plex_track.artist().title} - {found_plex_track.title}")
|
2025-06-25 13:17:58 +02:00
|
|
|
else:
|
2025-06-25 13:53:24 +02:00
|
|
|
log.info(f" ❌ Not found: '{track_title}' by '{artist_name}' in Plex.")
|
2025-06-25 13:17:58 +02:00
|
|
|
|
|
|
|
|
if not unique_tracks_to_add:
|
2025-06-25 13:53:24 +02:00
|
|
|
log.info("No new matching songs found in Plex to create a playlist.")
|
2025-06-25 13:17:58 +02:00
|
|
|
return
|
|
|
|
|
|
|
|
|
|
current_date = datetime.datetime.now().strftime("%Y-%m")
|
2025-06-25 13:53:24 +02:00
|
|
|
playlist_name = f"Last.fm Recommendations {current_date}"
|
2025-06-25 13:17:58 +02:00
|
|
|
|
|
|
|
|
playlist = None
|
|
|
|
|
try:
|
|
|
|
|
playlist = plex.playlist(playlist_name)
|
2025-06-25 13:53:24 +02:00
|
|
|
log.info(f"Playlist '{playlist_name}' already exists.")
|
2025-06-25 13:17:58 +02:00
|
|
|
except Exception:
|
2025-06-25 13:53:24 +02:00
|
|
|
log.info(f"Creating new playlist: '{playlist_name}'")
|
2025-06-25 13:17:58 +02:00
|
|
|
|
|
|
|
|
if playlist:
|
|
|
|
|
existing_playlist_items = playlist.items()
|
|
|
|
|
new_items_to_add = [track for track in unique_tracks_to_add if track not in existing_playlist_items]
|
|
|
|
|
|
|
|
|
|
if new_items_to_add:
|
|
|
|
|
try:
|
|
|
|
|
playlist.addItems(new_items_to_add)
|
2025-06-25 13:53:24 +02:00
|
|
|
log.info(f"Added {len(new_items_to_add)} new songs to existing playlist '{playlist_name}'.")
|
2025-06-25 13:17:58 +02:00
|
|
|
except Exception as e:
|
2025-06-25 13:53:24 +02:00
|
|
|
log.error(f"Could not add songs to existing playlist: {e}")
|
2025-06-25 13:17:58 +02:00
|
|
|
else:
|
2025-06-25 13:53:24 +02:00
|
|
|
log.info(f"No new songs to add to existing playlist '{playlist_name}'.")
|
2025-06-25 13:17:58 +02:00
|
|
|
else:
|
|
|
|
|
try:
|
|
|
|
|
new_playlist = plex.createPlaylist(playlist_name, items=unique_tracks_to_add)
|
2025-06-25 13:53:24 +02:00
|
|
|
log.info(f"✅ Created new playlist '{new_playlist.title}' with {len(unique_tracks_to_add)} songs.")
|
2025-06-25 13:17:58 +02:00
|
|
|
except Exception as e:
|
2025-06-25 13:53:24 +02:00
|
|
|
log.error(f"❌ Could not create playlist in Plex: {e}")
|
2025-06-25 13:17:58 +02:00
|
|
|
|
2025-06-25 13:53:24 +02:00
|
|
|
log.info(f"🎉 Done! Process took {((time.time()-start_time)/60):.1f} minutes.")
|
2025-06-25 13:17:58 +02:00
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
2025-06-25 12:31:23 +00:00
|
|
|
create_lastfm_recommended_playlist()
|