lofi / src /lib /utils /urlParser.ts
veltrixcode's picture
Upload 84 files
3c56493 verified
Raw
History Blame Contribute Delete
3.11 kB
/**
* 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'));
}