from qrcode.image.pil import PilImage from qrcode.image.pure import PyPNGImage import spotipy from spotipy.oauth2 import SpotifyClientCredentials from pathlib import Path import json import qrcode from io import BytesIO import base64 from PIL import Image from typing import List, Dict, Any import pandas as pd import re class Hipsterfy(object): def __init__(self, client_id, client_secret): self.client_id = client_id self.client_secret = client_secret self.sp = self.authenticate_spotify() def authenticate_spotify(self): """ Authenticate with Spotify using Client Credentials Flow. """ credentials = SpotifyClientCredentials(client_id=self.client_id, client_secret=self.client_secret) sp = spotipy.Spotify(client_credentials_manager=credentials) return sp class HipsterfyPlaylistItem(object): def _generate_qr_code_base64(self, url): qr: qrcode.QRCode[PilImage | PyPNGImage] = qrcode.QRCode(box_size=2, border=2) qr.add_data(url) qr.make(fit=True) img = qr.make_image(fill_color="black", back_color="white") buffered = BytesIO() img.save(buffered, format="PNG") img_str = base64.b64encode(buffered.getvalue()).decode() return f'QR Code' def generate_hipsterfy_front_card(self, include_preview:bool = False): # generates the square front card only with the centered qr code preview_html = self.embed_html if include_preview else "" if not self._qr_html: return "" return f"""
{self._qr_html} {preview_html}
Scan for Spotify Preview
""" def generate_hipsterfy_back_card(self, primary_property, secondary_property, additional_properties:List[str]=None, album_cover_gb:bool = True): # generates the back card with primary and secondary property and additional properties additional_html = "" if additional_properties: for prop in additional_properties: value = getattr(self, f"{prop}", "N/A") additional_html += f"
{prop.replace('_', ' ').title()}: {value}
" # Albumcover als Hintergrund album_bg = "" if album_cover_gb and self.album_images and len(self.album_images) > 0: album_url = self.album_images[0]['url'] album_bg = f""" background: linear-gradient(135deg, rgba(30,30,30,0.65) 60%, rgba(0,0,0,0.85) 100%), url('{album_url}') center center/cover no-repeat; """ else: album_bg = "background: #f9f9f9;" return f"""

{getattr(self, primary_property)}

{getattr(self, secondary_property)}

{additional_html}
""" @staticmethod def get_available_properties() -> List[str]: """ Returns a list of all available properties for the HipsterfyPlaylistItem. This can be used to dynamically generate the back card with all properties. """ return [ 'title', 'artists', 'album', 'release_date', 'release_year', 'duration', 'popularity', 'explicit', 'link', 'track_id', 'preview_url', 'track_number', 'disc_number', 'available_markets', 'is_local', 'external_ids', 'uri', 'album_images', 'album_type', 'album_release_date_precision', 'album_total_tracks', 'album_id', 'album_uri' ] def __init__(self, playlist_item:dict): self._raw_data = playlist_item self._title = self._raw_data['name'] self._artists = ', '.join([artist['name'] for artist in self._raw_data['artists']]) self._album = self._raw_data['album']['name'] self._release_date = self._raw_data['album']['release_date'] self._duration_ms = self._raw_data['duration_ms'] self._duration_min = round(self._duration_ms / 60000, 2) self._link = self._raw_data['external_urls']['spotify'] self._track_id = self._raw_data['id'] self._popularity = self._raw_data.get('popularity') self._explicit = self._raw_data.get('explicit') self._preview_url = self._raw_data.get('preview_url') self._track_number = self._raw_data.get('track_number') self._disc_number = self._raw_data.get('disc_number') self._available_markets = self._raw_data.get('available_markets') self._is_local = self._raw_data.get('is_local') self._external_ids = self._raw_data.get('external_ids') self._uri = self._raw_data.get('uri') self._album_images = self._raw_data['album'].get('images') self._album_type = self._raw_data['album'].get('album_type') self._album_release_date_precision = self._raw_data['album'].get('release_date_precision') self._album_total_tracks = self._raw_data['album'].get('total_tracks') self._album_id = self._raw_data['album'].get('id') self._album_uri = self._raw_data['album'].get('uri') self._embed_html = "" self._qr_html = "" if self._track_id: self._embed_html = f""" """ self._qr_html = self._generate_qr_code_base64(f"https://open.spotify.com/embed/track/{self._track_id}") @property def title(self): return self._title @property def artists(self): return self._artists @property def album(self): return self._album @property def release_date(self): return self._release_date @property def release_year(self): return self._release_date.split('-')[0] if self._release_date else None @property def duration(self): return self._duration_min @property def popularity(self): return self._popularity @property def explicit(self): return self._explicit @property def link(self): return self._link @property def track_id(self): return self._track_id @property def preview_url(self): return self._preview_url @property def track_number(self): return self._track_number @property def disc_number(self): return self._disc_number @property def available_markets(self): return self._available_markets @property def is_local(self): return self._is_local @property def external_ids(self): return self._external_ids @property def uri(self): return self._uri @property def album_images(self): return self._album_images @property def album_type(self): return self._album_type @property def album_release_date_precision(self): return self._album_release_date_precision @property def album_total_tracks(self): return self._album_total_tracks @property def album_id(self): return self._album_id @property def album_uri(self): return self._album_uri @property def embed_html(self): return self._embed_html @property def qr_html(self): return self._qr_html class HipsterfyPlaylist(object): def __init__(self, playlist_uri, hipsterfy:Hipsterfy): self._playlist_uri = playlist_uri self._hipsterfy = hipsterfy self._tracks_data = [] self._load_playlist() def _load_playlist(self): """Load the playlist data from Spotify and extract track information. """ playlist_id = self._playlist_uri.split("/")[-1].split("?")[0] results = self._hipsterfy.sp.playlist_items(playlist_id, additional_types=['track']) self._tracks_data = [HipsterfyPlaylistItem(item['track']) for item in results['items'] if item['track']] def get_tracks_data(self) -> List[HipsterfyPlaylistItem]: """Returns the list of HipsterfyPlaylistItem objects representing the tracks in the playlist. """ return self._tracks_data