fix: Fix corrupt database, Add soundcloud and spotify embeds
This commit is contained in:
149
toju-app/src/app/domains/chat/domain/rules/link-embed.rules.ts
Normal file
149
toju-app/src/app/domains/chat/domain/rules/link-embed.rules.ts
Normal 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);
|
||||
}
|
||||
Reference in New Issue
Block a user