Spaces:
Sleeping
Sleeping
| /** | |
| * Utility functions for parsing Tidal URLs | |
| */ | |
| export type TidalUrlType = 'track' | 'album' | 'artist' | 'playlist' | 'unknown'; | |
| export interface TidalUrlParseResult { | |
| type: TidalUrlType; | |
| trackId?: number; | |
| albumId?: number; | |
| artistId?: number; | |
| playlistId?: string; | |
| } | |
| /** | |
| * Parse a Tidal URL and extract the relevant IDs | |
| * | |
| * Supported URL formats: | |
| * - Track: https://tidal.com/album/{albumId}/track/{trackId} | |
| * - Album: https://tidal.com/album/{albumId} | |
| * - Artist: https://tidal.com/artist/{artistId} | |
| * - Playlist: https://tidal.com/playlist/{playlistId} | |
| * | |
| * Also supports: | |
| * - listen.tidal.com domain | |
| * - browse/ prefix (e.g., https://tidal.com/browse/track/{trackId}) | |
| */ | |
| export function parseTidalUrl(url: string): TidalUrlParseResult { | |
| try { | |
| // Clean up the URL - remove whitespace | |
| const cleanUrl = url.trim(); | |
| // Parse the URL | |
| let urlObj: URL; | |
| try { | |
| urlObj = new URL(cleanUrl); | |
| } catch { | |
| // If it's not a valid URL, try adding https:// | |
| urlObj = new URL(`https://${cleanUrl}`); | |
| } | |
| // Check if it's a Tidal domain | |
| const hostname = urlObj.hostname.toLowerCase(); | |
| if (!hostname.includes('tidal.com')) { | |
| return { type: 'unknown' }; | |
| } | |
| const pathname = urlObj.pathname; | |
| // Track URL patterns: | |
| // /album/{albumId}/track/{trackId} | |
| // /browse/track/{trackId} | |
| const trackMatch = pathname.match(/\/(?:album\/\d+\/)?track\/(\d+)/); | |
| if (trackMatch) { | |
| const trackId = parseInt(trackMatch[1]!, 10); | |
| if (!isNaN(trackId)) { | |
| // Also try to extract album ID if present | |
| const albumMatch = pathname.match(/\/album\/(\d+)\//); | |
| const albumId = albumMatch ? parseInt(albumMatch[1]!, 10) : undefined; | |
| return { | |
| type: 'track', | |
| trackId, | |
| albumId: albumId && !isNaN(albumId) ? albumId : undefined | |
| }; | |
| } | |
| } | |
| // Album URL pattern: /album/{albumId} | |
| const albumMatch = pathname.match(/\/album\/(\d+)(?:\/|$)/); | |
| if (albumMatch) { | |
| const albumId = parseInt(albumMatch[1]!, 10); | |
| if (!isNaN(albumId)) { | |
| return { | |
| type: 'album', | |
| albumId | |
| }; | |
| } | |
| } | |
| // Artist URL pattern: /artist/{artistId} | |
| const artistMatch = pathname.match(/\/artist\/(\d+)/); | |
| if (artistMatch) { | |
| const artistId = parseInt(artistMatch[1]!, 10); | |
| if (!isNaN(artistId)) { | |
| return { | |
| type: 'artist', | |
| artistId | |
| }; | |
| } | |
| } | |
| // Playlist URL pattern: /playlist/{playlistId} | |
| // Note: Playlist IDs are UUIDs (strings), not numbers | |
| const playlistMatch = pathname.match(/\/playlist\/([a-f0-9-]+)/i); | |
| if (playlistMatch) { | |
| return { | |
| type: 'playlist', | |
| playlistId: playlistMatch[1]! | |
| }; | |
| } | |
| return { type: 'unknown' }; | |
| } catch (error) { | |
| console.error('Failed to parse Tidal URL:', error); | |
| return { type: 'unknown' }; | |
| } | |
| } | |
| /** | |
| * Validate if a string looks like a Tidal URL | |
| */ | |
| export function isTidalUrl(url: string): boolean { | |
| const cleaned = url.trim().toLowerCase(); | |
| return cleaned.includes('tidal.com') && | |
| (cleaned.startsWith('http://') || | |
| cleaned.startsWith('https://') || | |
| cleaned.startsWith('tidal.com') || | |
| cleaned.startsWith('listen.tidal.com')); | |
| } | |