mirror of
https://github.com/th-ch/youtube-music.git
synced 2026-01-11 10:31:47 +00:00
@ -3,26 +3,22 @@ import { join } from 'node:path';
|
||||
import { randomBytes } from 'node:crypto';
|
||||
|
||||
import { app, BrowserWindow, dialog, ipcMain } from 'electron';
|
||||
import {
|
||||
ClientType,
|
||||
Innertube,
|
||||
UniversalCache,
|
||||
Utils,
|
||||
YTNodes,
|
||||
} from 'youtubei.js';
|
||||
import { Innertube, UniversalCache, Utils, YTNodes } from 'youtubei.js';
|
||||
import is from 'electron-is';
|
||||
import filenamify from 'filenamify';
|
||||
import { Mutex } from 'async-mutex';
|
||||
import { createFFmpeg } from '@ffmpeg.wasm/main';
|
||||
import NodeID3, { TagConstants } from 'node-id3';
|
||||
|
||||
import { Window } from 'happy-dom';
|
||||
import { BG, type BgConfig } from 'bgutils-js';
|
||||
|
||||
import {
|
||||
cropMaxWidth,
|
||||
getFolder,
|
||||
sendFeedback as sendFeedback_,
|
||||
setBadge,
|
||||
} from './utils';
|
||||
|
||||
import { fetchFromGenius } from '@/plugins/lyrics-genius/main';
|
||||
import { isEnabled } from '@/config/plugins';
|
||||
import registerCallback, {
|
||||
@ -33,21 +29,17 @@ import registerCallback, {
|
||||
SongInfoEvent,
|
||||
} from '@/providers/song-info';
|
||||
import { getNetFetchAsFetch } from '@/plugins/utils/main';
|
||||
|
||||
import { t } from '@/i18n';
|
||||
|
||||
import { DefaultPresetList, type Preset, YoutubeFormatList } from '../types';
|
||||
|
||||
import type { DownloaderPluginConfig } from '../index';
|
||||
|
||||
import type { BackendContext } from '@/types/contexts';
|
||||
|
||||
import type { FormatOptions } from 'youtubei.js/dist/src/types/FormatUtils';
|
||||
import type PlayerErrorMessage from 'youtubei.js/dist/src/parser/classes/PlayerErrorMessage';
|
||||
import type { Playlist } from 'youtubei.js/dist/src/parser/ytmusic';
|
||||
import type { VideoInfo } from 'youtubei.js/dist/src/parser/youtube';
|
||||
import type TrackInfo from 'youtubei.js/dist/src/parser/ytmusic/TrackInfo';
|
||||
|
||||
import type { GetPlayerResponse } from '@/types/get-player-response';
|
||||
|
||||
type CustomSongInfo = SongInfo & { trackId?: string };
|
||||
@ -119,6 +111,61 @@ export const onMainLoad = async ({
|
||||
generate_session_locally: true,
|
||||
fetch: getNetFetchAsFetch(),
|
||||
});
|
||||
|
||||
const requestKey = 'O43z0dpjhgX20SCx4KAo';
|
||||
const visitorData = yt.session.context.client.visitorData;
|
||||
|
||||
if (visitorData) {
|
||||
try {
|
||||
const [width, height] = win.getSize();
|
||||
// emulate jsdom using linkedom
|
||||
const window = new Window({
|
||||
width,
|
||||
height,
|
||||
console,
|
||||
});
|
||||
const document = window.document;
|
||||
|
||||
Object.assign(globalThis, {
|
||||
window,
|
||||
document,
|
||||
});
|
||||
|
||||
const bgConfig: BgConfig = {
|
||||
fetch: getNetFetchAsFetch(),
|
||||
globalObj: globalThis,
|
||||
identifier: visitorData,
|
||||
requestKey,
|
||||
};
|
||||
|
||||
const bgChallenge = await BG.Challenge.create(bgConfig);
|
||||
const interpreterJavascript =
|
||||
bgChallenge?.interpreterJavascript
|
||||
.privateDoNotAccessOrElseSafeScriptWrappedValue;
|
||||
|
||||
if (interpreterJavascript) {
|
||||
// This is a workaround to run the interpreterJavascript code
|
||||
// Maybe there is a better way to do this (e.g. https://github.com/Siubaak/sval ?)
|
||||
// eslint-disable-next-line @typescript-eslint/no-implied-eval,@typescript-eslint/no-unsafe-call
|
||||
new Function(interpreterJavascript)();
|
||||
|
||||
const poTokenResult = await BG.PoToken.generate({
|
||||
program: bgChallenge.program,
|
||||
globalName: bgChallenge.globalName,
|
||||
bgConfig,
|
||||
});
|
||||
|
||||
yt.session.po_token = poTokenResult.poToken;
|
||||
}
|
||||
} finally {
|
||||
// Bypass TypeScript checks
|
||||
((x: Partial<typeof globalThis>) => {
|
||||
delete x.window;
|
||||
delete x.document;
|
||||
})(globalThis);
|
||||
}
|
||||
}
|
||||
|
||||
ipc.handle('download-song', (url: string) => downloadSong(url));
|
||||
ipc.on('ytmd:video-src-changed', (data: GetPlayerResponse) => {
|
||||
playingUrl = data.microformat.microformatDataRenderer.urlCanonical;
|
||||
@ -776,13 +823,7 @@ const getMetadata = (info: TrackInfo): CustomSongInfo => ({
|
||||
|
||||
// This is used to bypass age restrictions
|
||||
const getAndroidTvInfo = async (id: string): Promise<VideoInfo> => {
|
||||
const innertube = await Innertube.create({
|
||||
client_type: ClientType.TV_EMBEDDED,
|
||||
generate_session_locally: true,
|
||||
retrieve_player: true,
|
||||
fetch: getNetFetchAsFetch(),
|
||||
});
|
||||
// GetInfo 404s with the bypass, so we use getBasicInfo instead
|
||||
// that's fine as we only need the streaming data
|
||||
return await innertube.getBasicInfo(id, 'TV_EMBEDDED');
|
||||
return await yt.getBasicInfo(id, 'TV_EMBEDDED');
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user