Spaces:
Runtime error
Runtime error
| import streamlit as st | |
| from typing import Any, Dict, List, Optional | |
| import time | |
| def safe_get(data: Dict, key: str, default: Any = None) -> Any: | |
| """Safely get nested dictionary values""" | |
| try: | |
| for k in key.split('.'): | |
| data = data[k] | |
| return data | |
| except (KeyError, TypeError): | |
| return default | |
| def format_duration(ms: int) -> str: | |
| """Convert milliseconds to MM:SS format""" | |
| seconds = ms // 1000 | |
| minutes = seconds // 60 | |
| seconds = seconds % 60 | |
| return f"{minutes}:{seconds:02d}" | |
| def parse_spotify_url(url: str) -> tuple: | |
| """Parse Spotify URL to get type and ID""" | |
| if 'track' in url: | |
| parts = url.split('/track/') | |
| if len(parts) > 1: | |
| track_id = parts[1].split('?')[0] | |
| return 'track', track_id | |
| elif 'playlist' in url: | |
| parts = url.split('/playlist/') | |
| if len(parts) > 1: | |
| playlist_id = parts[1].split('?')[0] | |
| return 'playlist', playlist_id | |
| elif 'artist' in url: | |
| parts = url.split('/artist/') | |
| if len(parts) > 1: | |
| artist_id = parts[1].split('?')[0] | |
| return 'artist', artist_id | |
| elif 'album' in url: | |
| parts = url.split('/album/') | |
| if len(parts) > 1: | |
| album_id = parts[1].split('?')[0] | |
| return 'album', album_id | |
| return None, None | |
| def validate_api_keys() -> bool: | |
| """Check if required API keys are set""" | |
| provider = st.session_state.ai_provider | |
| if provider == "OpenAI" and st.session_state.openai_api_key: | |
| return True | |
| elif provider == "Anthropic" and st.session_state.anthropic_api_key: | |
| return True | |
| elif provider == "OpenRouter" and st.session_state.openrouter_api_key: | |
| return True | |
| return False | |
| def get_spotify_image_url(item: Dict, size: str = 'medium') -> str: | |
| """Get image URL from Spotify item""" | |
| images = safe_get(item, 'images', []) | |
| if not images: | |
| images = safe_get(item, 'album.images', []) | |
| if not images: | |
| return "" | |
| if size == 'large': | |
| return images[0]['url'] | |
| elif size == 'medium': | |
| return images[len(images)//2]['url'] if len(images) > 1 else images[0]['url'] | |
| else: | |
| return images[-1]['url'] | |
| def create_track_uri(track_id: str) -> str: | |
| """Create Spotify track URI from ID""" | |
| return f"spotify:track:{track_id}" | |
| def create_playlist_uri(playlist_id: str) -> str: | |
| """Create Spotify playlist URI from ID""" | |
| return f"spotify:playlist:{playlist_id}" | |
| def chunk_list(lst: List, chunk_size: int) -> List[List]: | |
| """Split list into chunks""" | |
| return [lst[i:i + chunk_size] for i in range(0, len(lst), chunk_size)] | |
| def retry_with_backoff(func, max_retries=3, initial_delay=1): | |
| """Retry function with exponential backoff""" | |
| for i in range(max_retries): | |
| try: | |
| return func() | |
| except Exception as e: | |
| if i == max_retries - 1: | |
| raise e | |
| delay = initial_delay * (2 ** i) | |
| time.sleep(delay) | |
| return None |