You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
50 lines
1.8 KiB
50 lines
1.8 KiB
/** |
|
* Parse a YouTube watch / Shorts / Live / embed / youtu.be URL for the player API. |
|
* Covers common host variants (www, m, music, youtube-nocookie). |
|
*/ |
|
export function parseYoutubeUrl(url: string): { videoId: string | null; isShort: boolean } { |
|
try { |
|
const u = new URL(url.trim()) |
|
if (u.protocol !== 'http:' && u.protocol !== 'https:') { |
|
return { videoId: null, isShort: false } |
|
} |
|
const host = u.hostname.toLowerCase() |
|
if (host === 'youtu.be') { |
|
const id = u.pathname.split('/').filter(Boolean)[0] |
|
const videoId = id ? decodeURIComponent(id.split('?')[0]!).trim() : null |
|
return { videoId: videoId && videoId.length >= 6 ? videoId : null, isShort: false } |
|
} |
|
if ( |
|
!( |
|
host === 'youtube.com' || |
|
host === 'www.youtube.com' || |
|
host === 'm.youtube.com' || |
|
host === 'music.youtube.com' || |
|
host === 'youtube-nocookie.com' || |
|
host === 'www.youtube-nocookie.com' |
|
) |
|
) { |
|
return { videoId: null, isShort: false } |
|
} |
|
const path = u.pathname |
|
if (path.startsWith('/shorts/')) { |
|
const id = path.slice('/shorts/'.length).split('/')[0]?.trim() |
|
return { videoId: id || null, isShort: true } |
|
} |
|
if (path.startsWith('/live/')) { |
|
const id = path.slice('/live/'.length).split('/')[0]?.trim() |
|
return { videoId: id || null, isShort: false } |
|
} |
|
if (path.startsWith('/embed/')) { |
|
const id = path.slice('/embed/'.length).split('/')[0]?.trim() |
|
return { videoId: id || null, isShort: false } |
|
} |
|
if (path === '/watch' || path.startsWith('/watch/')) { |
|
const v = u.searchParams.get('v')?.trim() |
|
if (v) return { videoId: v, isShort: false } |
|
} |
|
return { videoId: null, isShort: false } |
|
} catch { |
|
return { videoId: null, isShort: false } |
|
} |
|
}
|
|
|