newpipe-extractor-js
Version:
JavaScript/Node.js port of NewPipeExtractor
199 lines • 6.96 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.YoutubeLinkHandler = void 0;
class YoutubeLinkHandler {
acceptUrl(url) {
try {
this.getId(url);
return true;
}
catch (error) {
return false;
}
}
getId(url) {
try {
// Handle scheme-specific URLs first
const uri = new URL(url);
const scheme = uri.protocol.replace(':', '');
if (scheme === 'vnd.youtube' || scheme === 'vnd.youtube.launch') {
const schemeSpecificPart = uri.pathname;
if (schemeSpecificPart.startsWith('//')) {
const extractedId = this.extractId(schemeSpecificPart.substring(2));
if (extractedId) {
return extractedId;
}
url = 'https:' + schemeSpecificPart;
}
else {
return this.assertIsId(schemeSpecificPart);
}
}
}
catch (e) {
// Continue with regular URL parsing
}
let urlObj;
try {
urlObj = new URL(url);
}
catch (e) {
throw new Error('The given URL is not valid');
}
const host = urlObj.hostname;
let path = urlObj.pathname;
// Remove leading "/" of URL-path if URL-path is given
if (path.startsWith('/')) {
path = path.substring(1);
}
if (!this.isValidYouTubeHost(host)) {
if (host.toLowerCase() === 'googleads.g.doubleclick.net') {
throw new Error('Error: found ad: ' + url);
}
throw new Error('The URL is not a YouTube URL');
}
// Handle different host patterns
switch (host.toUpperCase()) {
case 'WWW.YOUTUBE-NOCOOKIE.COM':
if (path.startsWith('embed/')) {
return this.assertIsId(path.substring(6));
}
break;
case 'YOUTUBE.COM':
case 'WWW.YOUTUBE.COM':
case 'M.YOUTUBE.COM':
case 'MUSIC.YOUTUBE.COM':
if (path === 'attribution_link') {
const uQueryValue = urlObj.searchParams.get('u');
if (uQueryValue) {
const decodedURL = new URL('https://www.youtube.com' + uQueryValue);
const viewQueryValue = decodedURL.searchParams.get('v');
return this.assertIsId(viewQueryValue);
}
}
// Check subpaths
const maybeId = this.getIdFromSubpathsInPath(path);
if (maybeId) {
return maybeId;
}
// Check 'v' parameter
const viewQueryValue = urlObj.searchParams.get('v');
return this.assertIsId(viewQueryValue);
case 'Y2U.BE':
case 'YOUTU.BE':
const vParam = urlObj.searchParams.get('v');
if (vParam) {
return this.assertIsId(vParam);
}
return this.assertIsId(path);
default:
// Handle Invidious and other alternative frontends
if (this.isAlternativeFrontend(host)) {
const maybeIdAlt = this.getIdFromSubpathsInPath(path);
if (maybeIdAlt) {
return maybeIdAlt;
}
const vParamAlt = urlObj.searchParams.get('v');
return this.assertIsId(vParamAlt);
}
break;
}
throw new Error('Could not extract video ID from URL: ' + url);
}
getUrl(id) {
return `https://www.youtube.com/watch?v=${id}`;
}
extractId(id) {
if (!id)
return null;
const match = YoutubeLinkHandler.VIDEO_ID_REGEX.exec(id);
return match ? match[1] : null;
}
assertIsId(id) {
const extractedId = this.extractId(id);
if (extractedId) {
return extractedId;
}
throw new Error('The given string is not a YouTube video ID');
}
getIdFromSubpathsInPath(path) {
for (const subpath of YoutubeLinkHandler.SUBPATHS) {
if (path.startsWith(subpath)) {
const remainingPath = path.substring(subpath.length);
// Extract video ID from remaining path
const slashIndex = remainingPath.indexOf('/');
const videoId = slashIndex > 0 ? remainingPath.substring(0, slashIndex) : remainingPath;
const extractedId = this.extractId(videoId);
if (extractedId) {
return extractedId;
}
}
}
return null;
}
isValidYouTubeHost(host) {
const upperHost = host.toUpperCase();
// Main YouTube domains
const youtubeDomains = [
'YOUTUBE.COM',
'WWW.YOUTUBE.COM',
'M.YOUTUBE.COM',
'MUSIC.YOUTUBE.COM',
'WWW.YOUTUBE-NOCOOKIE.COM'
];
// Short URL domains
const shortDomains = [
'YOUTU.BE',
'Y2U.BE'
];
return youtubeDomains.includes(upperHost) ||
shortDomains.includes(upperHost) ||
this.isAlternativeFrontend(host);
}
isAlternativeFrontend(host) {
const upperHost = host.toUpperCase();
// Alternative frontends (based on Java implementation)
const altFrontends = [
'HOOKTUBE.COM',
'INVIDIO.US',
'DEV.INVIDIO.US',
'WWW.INVIDIO.US',
'REDIRECT.INVIDIOUS.IO',
'INVIDIOUS.SNOPYTA.ORG',
'YEWTU.BE',
'TUBE.CONNECT.CAFE',
'TUBUS.EDUVID.ORG',
'INVIDIOUS.KAVIN.ROCKS',
'INVIDIOUS-US.KAVIN.ROCKS',
'PIPED.KAVIN.ROCKS',
'INVIDIOUS.SITE',
'VID.MINT.LGBT',
'INVIDIOU.SITE',
'INVIDIOUS.FDN.FR',
'INVIDIOUS.048596.XYZ',
'INVIDIOUS.ZEE.LI',
'VID.PUFFYAN.US',
'YTPRIVATE.COM',
'INVIDIOUS.NAMAZSO.EU',
'INVIDIOUS.SILKKY.CLOUD',
'INVIDIOUS.EXONIP.DE',
'INV.RIVERSIDE.ROCKS',
'INVIDIOUS.BLAMEFRAN.NET',
'INVIDIOUS.MOOMOO.ME',
'YTB.TROM.TF',
'YT.CYBERHOST.UK'
];
return altFrontends.includes(upperHost);
}
}
exports.YoutubeLinkHandler = YoutubeLinkHandler;
YoutubeLinkHandler.VIDEO_ID_REGEX = /^([a-zA-Z0-9_-]{11})/;
YoutubeLinkHandler.SUBPATHS = [
'embed/',
'live/',
'shorts/',
'watch/',
'v/',
'w/'
];
//# sourceMappingURL=YoutubeLinkHandler.js.map