fix: Fix corrupt database, Add soundcloud and spotify embeds

This commit is contained in:
2026-04-17 19:41:16 +02:00
parent 28797a0141
commit 3ba8a2c9eb
17 changed files with 463 additions and 39 deletions

View File

@@ -0,0 +1,149 @@
export type SpotifyResourceType = 'album' | 'artist' | 'episode' | 'playlist' | 'show' | 'track';
export type SoundcloudResourceType = 'playlist' | 'track';
export interface SpotifyResource {
type: SpotifyResourceType;
id: string;
}
export interface SoundcloudResource {
canonicalUrl: string;
type: SoundcloudResourceType;
}
const SPOTIFY_RESOURCE_TYPES = new Set<SpotifyResourceType>([
'album',
'artist',
'episode',
'playlist',
'show',
'track'
]);
const SPOTIFY_URI_PATTERN = /^spotify:(album|artist|episode|playlist|show|track):([a-zA-Z0-9]+)$/i;
const SOUNDCLOUD_HOST_PATTERN = /^(?:www\.|m\.)?soundcloud\.com$/i;
const YOUTUBE_HOST_PATTERN = /^(?:www\.|m\.|music\.)?youtube\.com$/i;
const YOUTU_BE_HOST_PATTERN = /^(?:www\.)?youtu\.be$/i;
const YOUTUBE_VIDEO_ID_PATTERN = /^[\w-]{11}$/;
function parseUrl(url: string): URL | null {
try {
return new URL(url);
} catch {
return null;
}
}
export function extractYoutubeVideoId(url: string): string | null {
const parsedUrl = parseUrl(url);
if (!parsedUrl) {
return null;
}
if (YOUTU_BE_HOST_PATTERN.test(parsedUrl.hostname)) {
const shortId = parsedUrl.pathname.split('/').filter(Boolean)[0] ?? '';
return YOUTUBE_VIDEO_ID_PATTERN.test(shortId) ? shortId : null;
}
if (!YOUTUBE_HOST_PATTERN.test(parsedUrl.hostname)) {
return null;
}
const pathSegments = parsedUrl.pathname.split('/').filter(Boolean);
if (parsedUrl.pathname === '/watch') {
const queryId = parsedUrl.searchParams.get('v') ?? '';
return YOUTUBE_VIDEO_ID_PATTERN.test(queryId) ? queryId : null;
}
if (pathSegments.length >= 2 && (pathSegments[0] === 'embed' || pathSegments[0] === 'shorts')) {
const pathId = pathSegments[1];
return YOUTUBE_VIDEO_ID_PATTERN.test(pathId) ? pathId : null;
}
return null;
}
export function isYoutubeUrl(url?: string): boolean {
return !!url && extractYoutubeVideoId(url) !== null;
}
export function extractSpotifyResource(url: string): SpotifyResource | null {
const spotifyUriMatch = url.match(SPOTIFY_URI_PATTERN);
if (spotifyUriMatch?.[1] && spotifyUriMatch[2]) {
return {
type: spotifyUriMatch[1].toLowerCase() as SpotifyResourceType,
id: spotifyUriMatch[2]
};
}
const parsedUrl = parseUrl(url);
if (!parsedUrl || !/^(?:open|play)\.spotify\.com$/i.test(parsedUrl.hostname)) {
return null;
}
const segments = parsedUrl.pathname.split('/').filter(Boolean);
if (segments.length >= 2 && SPOTIFY_RESOURCE_TYPES.has(segments[0] as SpotifyResourceType)) {
return {
type: segments[0] as SpotifyResourceType,
id: segments[1]
};
}
if (segments.length >= 4 && segments[0] === 'user' && segments[2] === 'playlist') {
return {
type: 'playlist',
id: segments[3]
};
}
return null;
}
export function isSpotifyUrl(url?: string): boolean {
return !!url && extractSpotifyResource(url) !== null;
}
export function extractSoundcloudResource(url: string): SoundcloudResource | null {
const parsedUrl = parseUrl(url);
if (!parsedUrl || !SOUNDCLOUD_HOST_PATTERN.test(parsedUrl.hostname)) {
return null;
}
const segments = parsedUrl.pathname.split('/').filter(Boolean);
if (segments.length === 2) {
const canonicalUrl = new URL(`https://soundcloud.com/${segments[0]}/${segments[1]}`);
return {
canonicalUrl: canonicalUrl.toString(),
type: 'track'
};
}
if (segments.length === 3 && segments[1] === 'sets') {
const canonicalUrl = new URL(`https://soundcloud.com/${segments[0]}/sets/${segments[2]}`);
return {
canonicalUrl: canonicalUrl.toString(),
type: 'playlist'
};
}
return null;
}
export function isSoundcloudUrl(url?: string): boolean {
return !!url && extractSoundcloudResource(url) !== null;
}
export function hasDedicatedChatEmbed(url?: string): boolean {
return isYoutubeUrl(url) || isSpotifyUrl(url) || isSoundcloudUrl(url);
}