UNPKG

ytmusic_api_unofficial

Version:

A simple API to get music from YouTube Music

610 lines (609 loc) 32.9 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.detectType = detectType; exports.topResult = topResult; exports.rankingResponse = rankingResponse; exports.topResults = topResults; exports.parseMenuPlaylists = parseMenuPlaylists; exports.parseSearchResult = parseSearchResult; exports.parseSearchResults = parseSearchResults; exports.getFlexColumnItem = getFlexColumnItem; exports.parseSongFromFollowList = parseSongFromFollowList; exports.getItemText = getItemText; exports.parseGetResult = parseGetResult; exports.getYTIdFromText = getYTIdFromText; exports.getTypeByID = getTypeByID; exports.downloadYTDL = downloadYTDL; exports.getPlayers_dv = getPlayers_dv; exports.customThumbnailSize = customThumbnailSize; exports.thumbnail_defaults_size = thumbnail_defaults_size; const responseBuilder_1 = require("./responseBuilder"); const Music_1 = __importDefault(require("../classes/Music")); const default_1 = require("./default"); const Artist_1 = __importDefault(require("../classes/Artist")); const User_1 = __importDefault(require("../classes/User")); const Album_1 = __importDefault(require("../classes/Album")); const Playlist_1 = __importDefault(require("../classes/Playlist")); const error_1 = require("./error"); const request_1 = __importDefault(require("./request")); const decode_1 = require("./decode"); const StreamPlayer_1 = __importDefault(require("../classes/StreamPlayer")); const Player_1 = __importDefault(require("../classes/Player")); const Thumbnail_1 = require("../classes/Thumbnail"); function detectType(type, none_if_empty = false) { var _a; if (!type) return null; return (_a = default_1.all_TYPES.map(x => type.toLowerCase().split(" ")[0].startsWith(x) ? x : null).filter(x => x !== null)[0]) !== null && _a !== void 0 ? _a : (none_if_empty ? null : 'song'); } function topResult(response) { const type = detectType((0, responseBuilder_1.nav)(response, responseBuilder_1.SUBTITLE)); const search_result = { "category": (0, responseBuilder_1.nav)(response, responseBuilder_1.CARD_SHELF_TITLE), "type": type }; if (type === "album") search_result["browseId"] = (0, responseBuilder_1.nav)(response, [...responseBuilder_1.TITLE, ...responseBuilder_1.NAVIGATION_BROWSE_ID], true); if (type === 'song' || type === 'video') { if (response.onTap) { search_result.videoId = (0, responseBuilder_1.nav)(response.onTap, responseBuilder_1.WATCH_VIDEO_ID); search_result.videoType = (0, responseBuilder_1.nav)(response.onTap, responseBuilder_1.NAVIGATION_VIDEO_TYPE); } } if (type === "song" || type === "video" || type === "album") { search_result.videoId = (0, responseBuilder_1.nav)(response, ["onTap", ...responseBuilder_1.WATCH_VIDEO_ID], true); search_result.videoType = (0, responseBuilder_1.nav)(response, ["onTap", ...responseBuilder_1.NAVIGATION_VIDEO_TYPE], true); search_result.title = (0, responseBuilder_1.nav)(response, responseBuilder_1.TITLE_TEXT); const runs = (0, responseBuilder_1.nav)(response, ["subtitle", "runs"]); const songInfo = parseSongRuns(runs.slice(2)); Object.assign(search_result, songInfo); } } function parseSongRuns(runs) { const parsed = { artists: [] }; runs.forEach((run, index) => { if (run.text.length === 3 && Object.keys(run).length === 1 && index % 2 !== 0) return; const text = run.text; if ("navigationEndpoint" in run) { // Artist or album const item = { name: text, id: (0, responseBuilder_1.nav)(run, responseBuilder_1.NAVIGATION_BROWSE_ID, true), }; if (item.id && (item.id.startsWith("MPRE") || item.id.includes("release_detail"))) { parsed.album = item; } else { parsed.artists.push(item); } } else { if (/^\d([^ ])* [^ ]*$/.test(text) && index > 0) { parsed.views = text.split(" ")[0]; } else if (/^(\d+:)*\d+:\d+$/.test(text)) { parsed.duration = parseDuration(text); } else if (/^\d{4}$/.test(text)) { parsed.year = Number(text); } } }); if (parsed.artists.length === 0) parsed.artists.push({ name: runs[0].text, id: undefined, isArtist: false }); return parsed; } function parseDuration(duration) { if (!duration || !duration.trim()) return duration; const increments = [1, 60, 3600]; const timeParts = duration.split(":").reverse(); return timeParts.reduce((total, time, index) => { return total + (parseInt(time) * increments[index]); }, 0); } function countIdenticalWords(text) { // Remove punctuation, convert to lowercase, and split text into words let words = text.toLowerCase().replace(/[^\w\s]/g, '').split(/\s+/); // Create an object to count identical words let wordCount = {}; // Loop through words and count occurrences words.forEach(word => { if (wordCount[word]) { wordCount[word]++; } else { wordCount[word] = 1; } }); return wordCount; } function getWords(text) { return text .toLowerCase() // Convert to lowercase for case-insensitive comparison .replace(/[^\w\s]/g, "") // Remove non-word characters like punctuation .split(/\s+/); // Split text by whitespace } // Function to count identical words and rank them by length function countIdenticalWordsAndRank(text1, text2) { const words1 = getWords(text1); const words2 = getWords(text2); // Create a set of words from each text const set1 = new Set(words1); const set2 = new Set(words2); // Find common words in both sets const commonWords = [...set1].filter(word => set2.has(word)); // Sort common words by length in descending order and rank them const rankedWords = commonWords .sort((a, b) => b.length - a.length) .map((word, index) => ({ word, rank: index + 1, length: word.length })); return rankedWords.reduce((total, word) => total + word.length, 0); } function rankingResponse(data, query) { for (const item of data) { if (item.title === undefined) continue; let ranking = countIdenticalWordsAndRank(item.title + ' - ' + item.artists.toString(), query); if (item.title.match("clip") || item.title.match("official") || item.title.match("video")) ranking -= 10; if (item.isTopResult) ranking += 50; else { const topResult = data.find((x) => x.isTopResult); if (topResult) { if (Math.abs(item.duration.duration - topResult.duration.duration) <= 15) ranking += 20; if (topResult.artists.some((x) => item.artists.some((y) => x.id === y.id))) ranking += 15; } } if (item.isAudioOnly) ranking += 20; item.searchRanking = ranking; } return data.sort((a, b) => b.searchRanking - a.searchRanking); } function topResults(response) { let searchResult = {}; searchResult.resultType = detectType((0, responseBuilder_1.nav)(response, responseBuilder_1.SUBTITLE), true); searchResult.category = (0, responseBuilder_1.nav)(response, responseBuilder_1.CARD_SHELF_TITLE); searchResult.thumbnails = (0, responseBuilder_1.nav)(response, responseBuilder_1.THUMBNAILS, true); if (["song", "video"].includes(searchResult.resultType)) { const onTap = response.onTap; if (onTap) { searchResult["videoId"] = (0, responseBuilder_1.nav)(onTap, responseBuilder_1.WATCH_VIDEO_ID); searchResult["videoType"] = (0, responseBuilder_1.nav)(onTap, responseBuilder_1.NAVIGATION_VIDEO_TYPE); } searchResult["videoId"] = (0, responseBuilder_1.nav)(response, ["onTap", ...responseBuilder_1.WATCH_VIDEO_ID], true); searchResult["videoType"] = (0, responseBuilder_1.nav)(response, ["onTap", ...responseBuilder_1.NAVIGATION_VIDEO_TYPE], true); searchResult["title"] = (0, responseBuilder_1.nav)(response, responseBuilder_1.TITLE_TEXT); const runs = (0, responseBuilder_1.nav)(response, ["subtitle", "runs"]); const songInfo = parseSongRuns(runs.slice(2)); searchResult = Object.assign(Object.assign({}, searchResult), songInfo); } if (["artist"].includes(searchResult.resultType)) { searchResult.name = (0, responseBuilder_1.nav)(response, responseBuilder_1.TITLE_TEXT); searchResult.followers = (0, responseBuilder_1.nav)(response, ['subtitle', 'runs', 2, 'text'], true); searchResult.id = (0, responseBuilder_1.nav)(response, ['onTap', "browseEndpoint", "browseId"], true); if (searchResult.followers) searchResult.followers = searchResult.followers.split(' ')[0].includes('K') ? Number(searchResult.followers.split(' ')[0].replace('K', '')) * 1000 : searchResult.followers.split(' ')[0].includes('M') ? Number(searchResult.followers.split(' ')[0].replace('M', '')) * 1000000 : Number(searchResult.followers.split(' ')[0]); parseMenuPlaylists(response, searchResult); } switch (searchResult.resultType) { /* case "artist": searchResult.artist = getItemText(response, 0); //parse_menu_playlists(data, searchResult); new Error("Not implemented") break; case "album": searchResult.type = getItemText(response, 1); break; */ case "song": case "video": searchResult.isTopResult = true; return new Music_1.default(searchResult); case "artist": return new Artist_1.default(searchResult); default: if (process.env.YT_DEBUG_MODE === "true") console.log("tpRs", "Unknown result type:", searchResult.resultType); } } function parseMenuPlaylists(response, result) { const menuItems = (0, responseBuilder_1.nav)(response, responseBuilder_1.MENU_ITEMS, true); if (!menuItems) return; const watchMenu = (0, responseBuilder_1.findObjectsByKey)(menuItems, responseBuilder_1.MNIR); for (const item of watchMenu.map((_x) => _x[responseBuilder_1.MNIR])) { const icon = (0, responseBuilder_1.nav)(item, responseBuilder_1.ICON_TYPE); let watchKey; switch (icon) { case "MUSIC_SHUFFLE": watchKey = "shuffleId"; break; case "MIX": watchKey = "radioId"; break; default: continue; } let watchId = (0, responseBuilder_1.nav)(item, ["navigationEndpoint", "watchPlaylistEndpoint", "playlistId"], true); if (!watchId) watchId = (0, responseBuilder_1.nav)(item, ["navigationEndpoint", "watchEndpoint", "playlistId"], true); else result[watchKey] = watchId; } } function parseSearchResult(response, category = "") { var _a; const videoType = (0, responseBuilder_1.nav)(response, [...responseBuilder_1.PLAY_BUTTON, "playNavigationEndpoint", ...responseBuilder_1.NAVIGATION_VIDEO_TYPE], true); let resultType = category ? category.toLowerCase() : ""; const browseID = (0, responseBuilder_1.nav)(response, responseBuilder_1.NAVIGATION_BROWSE_ID, true); if (!resultType || !default_1.all_TYPES.includes(resultType)) { if (browseID) { let mapping = { "VM": "playlist", "RD": "playlist", "VL": "playlist", "MPLA": "artist", "MPRE": "album", "MPSP": "podcast", "MPED": "episode", "UC": "artist", }; // Return artist if browseID starts with "UC", episode if it starts with "MPED", etc. const key = Object.keys(mapping).find(key => browseID.startsWith(key)); if (key) resultType = (_a = mapping[key]) !== null && _a !== void 0 ? _a : (videoType === "MUSIC_VIDEO_TYPE_ATV" ? "song" : "video"); } else { resultType = videoType === "MUSIC_VIDEO_TYPE_ATV" ? "song" : "video"; } } let searchResult = {}; searchResult.resultType = detectType(resultType, true) || ""; if (resultType !== "artist") searchResult.title = getItemText(response, 0); else { ((0, responseBuilder_1.nav)(getFlexColumnItem(response, 1), responseBuilder_1.TEXT_RUNS, true) || []).forEach((run) => { if (run.text.includes('subscribers')) searchResult.followers = run.text.split(' ')[0].includes('K') ? Number(run.text.split(' ')[0].replace('K', '')) * 1000 : run.text.split(' ')[0].includes('M') ? Number(run.text.split(' ')[0].replace('M', '')) * 1000000 : Number(run.text.split(' ')[0]); }); searchResult.name = getItemText(response, 0); parseMenuPlaylists(response, searchResult); } if (["profile"].includes(searchResult.resultType)) searchResult.name = getItemText(response, 1, 2, true); if (["song", "video", "episode"].includes(searchResult.resultType)) { searchResult.videoId = (0, responseBuilder_1.nav)(response, [...responseBuilder_1.PLAY_BUTTON, "playNavigationEndpoint", "watchEndpoint", "videoId"], true) || (0, responseBuilder_1.nav)(response, ["playlistItemData", "videoId"], true); searchResult.videoType = videoType; } if (["song", "video", "album"].includes(searchResult.resultType)) { searchResult.duration = null; searchResult.year = null; const nav_ = (0, responseBuilder_1.nav)(getFlexColumnItem(response, 1), responseBuilder_1.TEXT_RUNS, true); if (!nav_) return null; const nav_Offset = (nav_[0].length === 1 ? 2 : 0); searchResult = Object.assign(Object.assign({}, searchResult), parseSongRuns(nav_.slice(nav_Offset))); } if (["song", "album"].includes(searchResult.resultType)) searchResult.isExplicit = (0, responseBuilder_1.nav)(response, responseBuilder_1.BADGE_LABEL, true) !== null; if (["artist", "album", "playlist", "profile", "podcast"].includes(searchResult.resultType)) searchResult.id = (0, responseBuilder_1.nav)(response, responseBuilder_1.NAVIGATION_BROWSE_ID, true); if (searchResult.resultType === "album") { searchResult.browseId = (0, responseBuilder_1.nav)(response, [...responseBuilder_1.TITLE, ...responseBuilder_1.NAVIGATION_BROWSE_ID], true); } searchResult.thumbnails = (0, responseBuilder_1.nav)(response, responseBuilder_1.THUMBNAILS, true); switch (searchResult.resultType) { case "profile": searchResult.name = getItemText(response, 1, 2, true); return new User_1.default(searchResult); case "artist": return new Artist_1.default(searchResult); case "album": return new Album_1.default(searchResult); case "song": case "video": if (!searchResult.duration) searchResult.duration = parseDuration((0, responseBuilder_1.nav)(response, ["fixedColumns", 0, "musicResponsiveListItemFixedColumnRenderer", ...responseBuilder_1.TEXT_RUN_TEXT], true) || 'NaN:NaN'); return new Music_1.default(searchResult); case "": break; default: if (process.env.YT_DEBUG_MODE === "true") console.log("srRs", "Unknown result type:", searchResult.resultType); } return null; } function parseSearchResults(response, category) { return response.map(result => parseSearchResult(result[responseBuilder_1.MRLIR], category)); } function getFlexColumnItem(item, index) { if (!(item === null || item === void 0 ? void 0 : item.flexColumns)) return null; if (item.flexColumns.length <= index || !("text" in item.flexColumns[index].musicResponsiveListItemFlexColumnRenderer) || !("runs" in item.flexColumns[index].musicResponsiveListItemFlexColumnRenderer.text)) { return null; } else return item.flexColumns[index].musicResponsiveListItemFlexColumnRenderer; } function parseSongFromFollowList(response) { var _a; let music = {}; music.resultType = "song"; music.title = (0, responseBuilder_1.nav)(response, responseBuilder_1.TITLE_TEXT, true); music.thumbnails = (0, responseBuilder_1.nav)(response, responseBuilder_1.THUMBNAIL, true); music.videoId = response.videoId; music.isExplicit = (0, responseBuilder_1.nav)(response, responseBuilder_1.BADGE_LABEL, true) !== null; music.videoType = (0, responseBuilder_1.nav)(response, ['navigationEndpoint', ...responseBuilder_1.NAVIGATION_VIDEO_TYPE], true); music = Object.assign(Object.assign({}, music), parseSongRuns((_a = response === null || response === void 0 ? void 0 : response.longBylineText) === null || _a === void 0 ? void 0 : _a.runs)); music.duration = parseDuration((0, responseBuilder_1.nav)(response, ["lengthText", "runs", 0, "text"], true) || 'NaN:NaN'); music.radioPlID = (0, responseBuilder_1.nav)(response, [...responseBuilder_1.MENU_ITEMS, 0, 'menuNavigationItemRenderer', "navigationEndpoint", "watchEndpoint"], true); return new Music_1.default(music); } function getItemText(item, index, run_index = 0, none_if_absent = false) { const column = getFlexColumnItem(item, index); if (!column) { return null; } if (none_if_absent && column.text.runs.length < run_index + 1) { return null; } return column.text.runs[run_index].text; } function parseGetResult(response, type) { var _a; let searchResult = {}; searchResult.resultType = type; if (['song'].includes(type)) { response = response.contents.singleColumnMusicWatchNextResultsRenderer.tabbedRenderer.watchNextTabbedResultsRenderer.tabs; searchResult.browseID = (0, responseBuilder_1.nav)(response, ["0", "tabRenderer", "content", "musicQueueRenderer", "queue", "playlistId"], true) || (0, responseBuilder_1.nav)(response, [1, "tabRenderer", "endpoint", "browseEndpoint", "browseId"], true); searchResult.relativeBrowseID = (0, responseBuilder_1.nav)(response, [2, "tabRenderer", "endpoint", "browseEndpoint", "browseId"], true); searchResult.radioPlID = (0, responseBuilder_1.nav)(response[0].tabRenderer.content, ['musicQueueRenderer', 'content', 'playlistPanelRenderer', 'contents', 1, "automixPreviewVideoRenderer", "content", "automixPlaylistVideoRenderer", "navigationEndpoint", "watchPlaylistEndpoint"], true); response = (0, responseBuilder_1.nav)(response[0].tabRenderer.content, ['musicQueueRenderer', 'content', 'playlistPanelRenderer', 'contents', 0, 'playlistPanelVideoRenderer'], true); searchResult.videoType = (0, responseBuilder_1.nav)(response, ["navigationEndpoint", ...responseBuilder_1.NAVIGATION_VIDEO_TYPE], true); searchResult.thumbnails = (0, responseBuilder_1.nav)(response, responseBuilder_1.THUMBNAIL, true); } if (["autoMix"].includes(type)) { response = (0, responseBuilder_1.nav)(response, ['contents', 'singleColumnMusicWatchNextResultsRenderer', 'tabbedRenderer', 'watchNextTabbedResultsRenderer', 'tabs', 0, 'tabRenderer', 'content', 'musicQueueRenderer', 'content', 'playlistPanelRenderer'], true); searchResult.name = response.title + " - AutoMix"; searchResult.musics = ((0, responseBuilder_1.nav)(response, ["contents"], true) || []).filter((e) => { var _a; return (_a = e.playlistPanelVideoRenderer) === null || _a === void 0 ? void 0 : _a.videoId; }).map((e) => parseSongFromFollowList(e.playlistPanelVideoRenderer)); searchResult.musics = searchResult.musics.filter((e) => e.title !== response.title); } if (["charts"].includes(type)) { searchResult.musics = parseSearchResults((0, responseBuilder_1.nav)(response, responseBuilder_1.CHARTS_SHELF_RENDERER, true) || [], 'song'); searchResult.name = (0, responseBuilder_1.nav)(response, responseBuilder_1.HEADER_CHART_TITLE, true); searchResult.thumbnails = (0, responseBuilder_1.nav)(response, responseBuilder_1.HEADER_CHART_THUMBNAIL, true); searchResult.description = (0, responseBuilder_1.nav)(response, [...responseBuilder_1.HEADER_CHART_DESCRIPTION], true); searchResult.id = (0, responseBuilder_1.nav)(response, [...responseBuilder_1.CHARTS_SHELF_PLAYLISTID], true); searchResult.artists = []; } if (["playlist", "album"].includes(type)) { searchResult.musics = parseSearchResults((0, responseBuilder_1.nav)(response, [...responseBuilder_1.PLAYLIST_SHELF_RENDERER, "contents"], true) || [], 'song'); searchResult.name = (0, responseBuilder_1.nav)(response, [...responseBuilder_1.PLAYLIST_SHELF_HEADER_RENDERER, ...responseBuilder_1.TITLE, "text"], true); searchResult.thumbnails = (0, responseBuilder_1.nav)(response, [...responseBuilder_1.PLAYLIST_SHELF_HEADER_RENDERER, ...responseBuilder_1.THUMBNAILS], true); searchResult.description = (0, responseBuilder_1.nav)(response, [...responseBuilder_1.PLAYLIST_SHELF_HEADER_RENDERER, ...responseBuilder_1.DESCRIPTION], true); searchResult.id = (0, responseBuilder_1.nav)(response, [...responseBuilder_1.PLAYLIST_SHELF_RENDERER, ...responseBuilder_1.PLAYLIST_ID], true); } if (["album"].includes(type)) { searchResult.musics = parseSearchResults((0, responseBuilder_1.nav)(response, [...responseBuilder_1.ALBUM_SHELF_RENDERER, "contents"], true) || [], 'song'); searchResult.description = (0, responseBuilder_1.nav)(response, [...responseBuilder_1.ALBUM_SHELF_HEADER_RENDERER, "description", ...responseBuilder_1.DESCRIPTION_SHELF, ...responseBuilder_1.DESCRIPTION], true); searchResult.id = (getYTIdFromText((0, responseBuilder_1.nav)(response, [...responseBuilder_1.ALBUM_LINK], true))).id; searchResult.artists = (_a = (parseSongRuns((0, responseBuilder_1.nav)(response, [...responseBuilder_1.ALBUM_SHELF_HEADER_RENDERER, "straplineTextOne", "runs"], true)))) === null || _a === void 0 ? void 0 : _a.artists; } if (['artist'].includes(type)) { searchResult.name = (0, responseBuilder_1.nav)(response, responseBuilder_1.HEADER_ARTIST_TITLE, true); searchResult.thumbnails = (0, responseBuilder_1.nav)(response, responseBuilder_1.HEADER_ARTIST_THUMBNAIL, true); searchResult.description = (0, responseBuilder_1.nav)(response, [...responseBuilder_1.HEADER_ARTIST, ...responseBuilder_1.DESCRIPTION], true); searchResult.id = (0, responseBuilder_1.nav)(response, [...responseBuilder_1.HEADER_ARTIST, ...responseBuilder_1.SUBSCRIBE_BUTTON, "channelId"], true); searchResult.followers = (0, responseBuilder_1.nav)(response, [...responseBuilder_1.HEADER_ARTIST, ...responseBuilder_1.SUBSCRIBE_BUTTON, "longSubscriberCountText", ...responseBuilder_1.RUN_TEXT], true); if (searchResult.followers) searchResult.followers = searchResult.followers.split(' ')[0].includes('K') ? Number(searchResult.followers.split(' ')[0].replace('K', '')) * 1000 : searchResult.followers.split(' ')[0].includes('M') ? Number(searchResult.followers.split(' ')[0].replace('M', '')) * 1000000 : Number(searchResult.followers.split(' ')[0]); } if (!searchResult.resultType) { if (searchResult.browseID) { let mapping = { "VM": "playlist", "RD": "playlist", "VL": "playlist", "MPLA": "artist", "MPRE": "album", "MPSP": "podcast", "MPED": "episode", "UC": "artist", }; const key = Object.keys(mapping).find(key => searchResult.browseID.startsWith(key)); if (key) searchResult.resultType = mapping[key]; else searchResult.resultType = searchResult.videoType === "MUSIC_VIDEO_TYPE_ATV" ? "song" : "video"; } else { searchResult.resultType = searchResult.videoType === "MUSIC_VIDEO_TYPE_ATV" ? "song" : "video"; } } if (!["artist"].includes(searchResult.resultType)) searchResult.title = (0, responseBuilder_1.nav)(response, responseBuilder_1.TITLE_TEXT, true); if (["song", "video", "episode"].includes(searchResult.resultType)) { searchResult.videoId = (0, responseBuilder_1.nav)(response, ["videoId"], true); } if (["song", "video"].includes(searchResult.resultType)) { searchResult.duration = null; searchResult.year = null; const nav_ = (0, responseBuilder_1.nav)(response, responseBuilder_1.LONGTEXT_RUNS, false); const nav_Offset = (nav_[0].length === 1 ? 2 : 0); searchResult = Object.assign(Object.assign({}, searchResult), parseSongRuns([...nav_.slice(nav_Offset), ...response.lengthText.runs])); } if (["song"].includes(searchResult.resultType)) { searchResult.isExplicit = (0, responseBuilder_1.nav)(response, responseBuilder_1.BADGE_LABEL, true) !== null; } switch (searchResult.resultType) { case "song": case "video": return new Music_1.default(searchResult); case "artist": return new Artist_1.default(searchResult); case "charts": case "playlist": case "album": case "autoMix": return new Playlist_1.default(searchResult); default: console.log("gtRs", "Unknown result type: ", searchResult); } return null; } function getYTIdFromText(text, precise = false) { const reg = /https?:\/\/(?:music\.|www\.)?(?:youtube\.com\/(?:watch\?v=|v\/|channel\/|playlist\?list=)|youtu\.be\/)([A-Za-z0-9_-]+)/m; let id = null; if (!text.includes(' ')) id = text.match(reg); if (id) { const type = getTypeByID(id[1]); if (type === "playlist" && (id[1].startsWith("PL") || id[1].startsWith("OL"))) id[1] = 'VL' + id[1]; return { id: id[1], type: type, isValidId: true }; } else return { isValidId: false, id: text, type: getTypeByID(text) }; } function getTypeByID(id = "") { if (!id) return null; if (id.startsWith("MPLA")) return "artist"; if (id.startsWith("MPRE")) return "album"; if (id.startsWith("MPSP")) return "podcast"; if (id.startsWith("MPED")) return "episode"; if (id.startsWith("UC")) return "artist"; if (id.startsWith("RD")) return "playlist"; if (id.startsWith("VL")) return "playlist"; if (id.startsWith("VM")) return "playlist"; if (id.startsWith("PL")) return "playlist"; if (id.startsWith("OL")) return "playlist"; if (id.length === 11) return "song"; return null; } function downloadYTDL(query, format = default_1.AvailableFormat[0], quality = default_1.AvailableQuality[0]) { return new Promise((resolve, reject) => { format = format === null || format === void 0 ? void 0 : format.toLowerCase(); quality = quality === null || quality === void 0 ? void 0 : quality.toLowerCase(); if (!default_1.AvailableFormat.includes(format)) return reject((0, error_1.error)(1003, `Available formats: ${default_1.AvailableFormat.join(", ")}`)); if (!default_1.AvailableQuality.includes(quality)) return reject((0, error_1.error)(1004, `Available qualities: ${default_1.AvailableQuality.join(", ")}`)); format = format.replace('mp3', 'mp4'); const id = getYTIdFromText(query, true); if (!id) return reject((0, error_1.error)(1005, query)); getPlayers_dv(id.id).then((res) => __awaiter(this, void 0, void 0, function* () { let downloads = format === 'mp4' ? res.videos : res.audios; if (quality === 'high') { downloads = downloads.sort((a, b) => b.bitrate - a.bitrate)[0]; } else if (quality === 'low') { downloads = downloads.sort((a, b) => a.bitrate - b.bitrate)[0]; } else if (quality === 'medium') { downloads = downloads.sort((a, b) => b.bitrate - a.bitrate)[Math.round(downloads.length / 2) - 1]; } if (!downloads) return reject((0, error_1.error)(2006, `No download found for ${format} ${quality}`)); downloads.urlDecoded = yield downloads.url(); resolve(new Player_1.default(downloads)); })).catch(reject); }); } function getPlayers_dv(id) { const date = new Date(); // client version: "clientVersion": "1." + date.getUTCFullYear() + (date.getUTCMonth() + 1).toString().padStart(2, '0') + date.getUTCDate().toString().padStart(2, '0') + ".01.00", return new Promise((resolve, reject) => __awaiter(this, void 0, void 0, function* () { (0, request_1.default)('player?key=', { videoId: id, "context": { "client": { "clientName": "WEB_REMIX", "clientVersion": "1." + date.getUTCFullYear() + (date.getUTCMonth() + 1).toString().padStart(2, '0') + date.getUTCDate().toString().padStart(2, '0') + ".01.00", } }, "playbackContext": { "contentPlaybackContext": { "referer": `https://music.youtube.com/watch?v=${id}`, "signatureTimestamp": yield (0, decode_1.getSignatureTimestamp)(), } } }).then((res) => __awaiter(this, void 0, void 0, function* () { var _a, _b; if (!res.streamingData) { if (process.env.YT_DEBUG_MODE === "true") console.log('gP_dv', res); return reject((0, error_1.error)(2004, (0, responseBuilder_1.nav)(res, ['playabilityStatus', 'status'], true) || (0, responseBuilder_1.nav)(res, ['playabilityStatus', 'messages', 0, 'message'], true) || 'Unknown error')); } const items = { videos: [], audios: [] }; //TODO: fix adaptative format signature for (const item of res.streamingData.formats) { if (item.audioQuality) items.audios.push(item); else items.videos.push(item); } //Sort by bitrate items.audios = items.audios.sort((a, b) => b.bitrate - a.bitrate); items.videos = items.videos.sort((a, b) => b.bitrate - a.bitrate); items.available = (!!items.audio || !!items.videos) && res.playabilityStatus.status === 'OK'; items.maxBitrate = Number((_b = (_a = res === null || res === void 0 ? void 0 : res.playerConfig) === null || _a === void 0 ? void 0 : _a.streamSelectionConfig) === null || _b === void 0 ? void 0 : _b.maxBitrate); resolve(new StreamPlayer_1.default(items)); })).catch(reject); })); } function customThumbnailSize(url, width, height) { const match = url.match(/=w(\d+)-h(\d+)/); if (match) url = url.replace(match[0], `=w${match[1]}-h${match[2]}`); return url; } const defaultsSize = [60, 120, 180, 226, 302, 480, 544, 720, 1080]; function thumbnail_defaults_size(url, thumbnails_defaults) { if (!url) return []; let thumbnails = []; for (const size of defaultsSize) { if (new URL(url).origin.includes('googleusercontent')) thumbnails.push(new Thumbnail_1.Thumbnail({ url: customThumbnailSize(url, size, size), width: size, height: size })); } if (thumbnails.length === 0) thumbnails = thumbnails_defaults || []; return thumbnails; } //# sourceMappingURL=utils.js.map