mirror of
https://github.com/th-ch/youtube-music.git
synced 2026-01-10 10:11:46 +00:00
Allow downloading age restricted videos
* Bypass age restriction using `androidTvInfo` * Bump youtubei.js fix #1084 * Add more detailed error messages, including song name or url
This commit is contained in:
@ -134,7 +134,7 @@
|
||||
"node-fetch": "^2.6.8",
|
||||
"simple-youtube-age-restriction-bypass": "https://gitpkg.now.sh/api/pkg.tgz?url=zerodytrash/Simple-YouTube-Age-Restriction-Bypass&commit=v2.5.4",
|
||||
"vudio": "^2.1.1",
|
||||
"youtubei.js": "^3.1.1",
|
||||
"youtubei.js": "^4.1.0",
|
||||
"ytpl": "^2.3.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@ -20,7 +20,7 @@ const {
|
||||
|
||||
const { ipcMain, app, dialog } = require('electron');
|
||||
const is = require('electron-is');
|
||||
const { Innertube, UniversalCache, Utils } = require('youtubei.js');
|
||||
const { Innertube, UniversalCache, Utils, ClientType } = require('youtubei.js');
|
||||
const ytpl = require('ytpl'); // REPLACE with youtubei getplaylist https://github.com/LuanRT/YouTube.js#getplaylistid
|
||||
|
||||
const filenamify = require('filenamify');
|
||||
@ -48,20 +48,22 @@ let yt;
|
||||
let win;
|
||||
let playingUrl = undefined;
|
||||
|
||||
const sendError = (error) => {
|
||||
const sendError = (error, source) => {
|
||||
win.setProgressBar(-1); // close progress bar
|
||||
setBadge(0); // close badge
|
||||
sendFeedback_(win); // reset feedback
|
||||
|
||||
console.error(error);
|
||||
const songNameMessage = source ? `\nin ${source}` : '';
|
||||
const cause = error.cause ? `\n\n${error.cause.toString()}` : '';
|
||||
const message = `${error.toString()}${songNameMessage}${cause}`;
|
||||
|
||||
console.error(message);
|
||||
dialog.showMessageBox({
|
||||
type: 'info',
|
||||
buttons: ['OK'],
|
||||
title: 'Error in download!',
|
||||
message: 'Argh! Apologies, download failed…',
|
||||
detail: `${error.toString()} ${
|
||||
error.cause ? `\n\n${error.cause.toString()}` : ''
|
||||
}`,
|
||||
detail: message,
|
||||
});
|
||||
};
|
||||
|
||||
@ -92,20 +94,23 @@ async function downloadSong(
|
||||
trackId = undefined,
|
||||
increasePlaylistProgress = () => {},
|
||||
) {
|
||||
let resolvedName = undefined;
|
||||
try {
|
||||
await downloadSongUnsafe(
|
||||
url,
|
||||
name=>resolvedName=name,
|
||||
playlistFolder,
|
||||
trackId,
|
||||
increasePlaylistProgress,
|
||||
);
|
||||
} catch (error) {
|
||||
sendError(error);
|
||||
sendError(error, resolvedName || url);
|
||||
}
|
||||
}
|
||||
|
||||
async function downloadSongUnsafe(
|
||||
url,
|
||||
setName,
|
||||
playlistFolder = undefined,
|
||||
trackId = undefined,
|
||||
increasePlaylistProgress = () => {},
|
||||
@ -122,7 +127,11 @@ async function downloadSongUnsafe(
|
||||
sendFeedback('Downloading...', 2);
|
||||
|
||||
const id = getVideoId(url);
|
||||
const info = await yt.music.getInfo(id);
|
||||
let info = await yt.music.getInfo(id);
|
||||
|
||||
if (!info) {
|
||||
throw new Error('Video not found');
|
||||
}
|
||||
|
||||
const metadata = getMetadata(info);
|
||||
if (metadata.album === 'N/A') metadata.album = '';
|
||||
@ -133,6 +142,34 @@ async function downloadSongUnsafe(
|
||||
const name = `${metadata.artist ? `${metadata.artist} - ` : ''}${
|
||||
metadata.title
|
||||
}`;
|
||||
setName(name);
|
||||
|
||||
let playabilityStatus = info.playability_status;
|
||||
let bypassedResult = null;
|
||||
if (playabilityStatus.status === "LOGIN_REQUIRED") {
|
||||
// try to bypass the age restriction
|
||||
bypassedResult = await getAndroidTvInfo(id);
|
||||
playabilityStatus = bypassedResult.playability_status;
|
||||
|
||||
if (playabilityStatus.status === "LOGIN_REQUIRED") {
|
||||
throw new Error(
|
||||
`[${playabilityStatus.status}] ${playabilityStatus.reason}`,
|
||||
);
|
||||
}
|
||||
|
||||
info = bypassedResult;
|
||||
}
|
||||
|
||||
if (playabilityStatus.status === "UNPLAYABLE") {
|
||||
/**
|
||||
* @typedef {import('youtubei.js/dist/src/parser/classes/PlayerErrorMessage').default} PlayerErrorMessage
|
||||
* @type {PlayerErrorMessage}
|
||||
*/
|
||||
const errorScreen = playabilityStatus.error_screen;
|
||||
throw new Error(
|
||||
`[${playabilityStatus.status}] ${errorScreen.reason.text}: ${errorScreen.subreason.text}`,
|
||||
);
|
||||
}
|
||||
|
||||
const extension = presets[config.get('preset')]?.extension || 'mp3';
|
||||
|
||||
@ -252,7 +289,7 @@ async function iterableStreamToMP3(
|
||||
|
||||
return ffmpeg.FS('readFile', `${safeVideoName}.mp3`);
|
||||
} catch (e) {
|
||||
sendError(e);
|
||||
sendError(e, safeVideoName);
|
||||
} finally {
|
||||
releaseFFmpegMutex();
|
||||
}
|
||||
@ -307,7 +344,7 @@ async function writeID3(buffer, metadata, sendFeedback) {
|
||||
writer.addTag();
|
||||
return Buffer.from(writer.arrayBuffer);
|
||||
} catch (e) {
|
||||
sendError(e);
|
||||
sendError(e, `${metadata.artist} - ${metadata.title}`);
|
||||
}
|
||||
}
|
||||
|
||||
@ -482,3 +519,16 @@ const getMetadata = (info) => ({
|
||||
album: info.player_overlays?.browser_media_session?.album?.text,
|
||||
image: info.basic_info.thumbnail[0].url,
|
||||
});
|
||||
|
||||
// This is used to bypass age restrictions
|
||||
const getAndroidTvInfo = async (id) => {
|
||||
const innertube = await Innertube.create({
|
||||
clientType: ClientType.TV_EMBEDDED,
|
||||
generate_session_locally: true,
|
||||
retrieve_player: true,
|
||||
});
|
||||
const info = await innertube.getBasicInfo(id, 'TV_EMBEDDED');
|
||||
// getInfo 404s with the bypass, so we use getBasicInfo instead
|
||||
// that's fine as we only need the streaming data
|
||||
return info;
|
||||
}
|
||||
|
||||
10
yarn.lock
10
yarn.lock
@ -8910,19 +8910,19 @@ __metadata:
|
||||
simple-youtube-age-restriction-bypass: "https://gitpkg.now.sh/api/pkg.tgz?url=zerodytrash/Simple-YouTube-Age-Restriction-Bypass&commit=v2.5.4"
|
||||
vudio: ^2.1.1
|
||||
xo: ^0.53.1
|
||||
youtubei.js: ^3.1.1
|
||||
youtubei.js: ^4.1.0
|
||||
ytpl: ^2.3.0
|
||||
languageName: unknown
|
||||
linkType: soft
|
||||
|
||||
"youtubei.js@npm:^3.1.1":
|
||||
version: 3.1.1
|
||||
resolution: "youtubei.js@npm:3.1.1"
|
||||
"youtubei.js@npm:^4.1.0":
|
||||
version: 4.1.0
|
||||
resolution: "youtubei.js@npm:4.1.0"
|
||||
dependencies:
|
||||
jintr: ^0.4.1
|
||||
linkedom: ^0.14.12
|
||||
undici: ^5.19.1
|
||||
checksum: 1280e2ddacec3034ee8e1b398ba80662a6854e184416d3484119e7cf47b69ab2e58b4f1efdf468dcad3e50bdc7bd42b6ee66b95660ffb521efb5f0634ef60fb7
|
||||
checksum: fa0090aa5b86c06a765757b0716ad9e5742c401b4fe662460db82495751e1fda3380b78f5fb916699f1707ab9b7c2783312dceac974afea3a5d101be62906bea
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
|
||||
Reference in New Issue
Block a user