Skip to content

Commit

Permalink
Merge pull request #1086 from Araxeus/fix-new-downloader-issues
Browse files Browse the repository at this point in the history
Allow downloading age restricted videos
  • Loading branch information
th-ch authored Mar 26, 2023
2 parents b909df9 + f722cf8 commit 05278ab
Show file tree
Hide file tree
Showing 3 changed files with 66 additions and 16 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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": {
Expand Down
70 changes: 60 additions & 10 deletions plugins/downloader/back.js
Original file line number Diff line number Diff line change
Expand Up @@ -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');
Expand Down Expand Up @@ -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,
});
};

Expand Down Expand Up @@ -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 = () => {},
Expand All @@ -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 = '';
Expand All @@ -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';

Expand Down Expand Up @@ -252,7 +289,7 @@ async function iterableStreamToMP3(

return ffmpeg.FS('readFile', `${safeVideoName}.mp3`);
} catch (e) {
sendError(e);
sendError(e, safeVideoName);
} finally {
releaseFFmpegMutex();
}
Expand Down Expand Up @@ -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}`);
}
}

Expand Down Expand Up @@ -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 changes: 5 additions & 5 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down

0 comments on commit 05278ab

Please sign in to comment.