Skip to content

Commit ebf2105

Browse files
committed
Implement Innertube
1 parent 13b79ac commit ebf2105

7 files changed

Lines changed: 1127 additions & 8799 deletions

File tree

download-third-party-apps.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -191,7 +191,7 @@ async function main() {
191191
`${winOrMac}-extra-files`,
192192
'ThirdPartyApps',
193193
'ffmpeg',
194-
'ffmpeg-7.1-essentials_build'
194+
'ffmpeg-7.1.1-essentials_build'
195195
)
196196
)
197197
} else if (process.platform === 'darwin') {

main-src/fetchYtStream.cjs

Lines changed: 105 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,115 @@
1-
const { Innertube, UniversalCache, ClientType, Log } = require('youtubei.js')
1+
// This file is partially copied from: https://github.com/LuanRT/BgUtils/blob/main/examples/node/index.ts
22

3-
let innertubeInstance = null
3+
const { Innertube, UniversalCache, Log } = require('youtubei.js')
4+
const { BG } = require('bgutils-js')
5+
const { JSDOM } = require('jsdom')
6+
const fetch = require('electron-fetch').default
47

5-
module.exports.fetchYtStream = async (videoId) => {
6-
Log.setLevel(Log.Level.DEBUG)
7-
8-
if (!innertubeInstance) {
9-
innertubeInstance = await Innertube.create({
10-
cache: new UniversalCache(false),
11-
generate_session_locally: false,
12-
retrieve_player: true,
13-
client_type: ClientType.WEB,
14-
})
8+
const REQUEST_KEY = 'O43z0dpjhgX20SCx4KAo'
9+
10+
let searchInnertube = null
11+
let downloadInnertube = null
12+
13+
const setupSearchInnertube = async () => {
14+
if (searchInnertube) {
15+
return
16+
}
17+
18+
searchInnertube = await Innertube.create({ retrieve_player: false })
19+
}
20+
21+
const setupDownloadInnertube = async () => {
22+
if (downloadInnertube) {
23+
return
24+
}
25+
26+
await setupSearchInnertube()
27+
28+
if (process.env.NODE_ENV === 'dev') {
29+
Log.setLevel(Log.Level.INFO)
30+
} else {
31+
Log.setLevel(Log.Level.WARNING)
32+
}
33+
34+
const visitorData = searchInnertube.session.context.client.visitorData
35+
if (!visitorData) {
36+
throw new Error('Could not get visitorData')
37+
}
38+
39+
const dom = new JSDOM()
40+
Object.assign(globalThis, {
41+
window: dom.window,
42+
document: dom.window.document,
43+
})
44+
45+
const bgConfig = {
46+
fetch,
47+
globalObj: globalThis,
48+
identifier: visitorData,
49+
requestKey: REQUEST_KEY,
50+
}
51+
52+
const bgChallenge = await BG.Challenge.create(bgConfig)
53+
if (!bgChallenge) {
54+
throw new Error('Could not get challenge')
1555
}
1656

17-
const ytStream = await yt.download(videoId, {
57+
const interpreterJavascript =
58+
bgChallenge.interpreterJavascript.privateDoNotAccessOrElseSafeScriptWrappedValue
59+
if (interpreterJavascript) {
60+
new Function(interpreterJavascript)()
61+
} else {
62+
throw new Error('Could not load VM')
63+
}
64+
65+
const poTokenResult = await BG.PoToken.generate({
66+
program: bgChallenge.program,
67+
globalName: bgChallenge.globalName,
68+
bgConfig,
69+
})
70+
71+
downloadInnertube = await Innertube.create({
72+
po_token: poTokenResult.poToken,
73+
visitor_data: visitorData,
74+
cache: new UniversalCache(true),
75+
generate_session_locally: true,
76+
})
77+
}
78+
79+
module.exports.fetchYtStream = async (videoId) => {
80+
await setupDownloadInnertube()
81+
82+
const ytStream = await downloadInnertube.download(videoId, {
1883
type: 'audio',
1984
quality: 'best',
20-
format: 'mp4',
21-
client: ClientType.WEB,
2285
})
2386

2487
return ytStream
2588
}
89+
90+
module.exports.searchYt = async (query) => {
91+
await setupSearchInnertube()
92+
93+
const innertubeResults = await searchInnertube.search(query, {
94+
type: 'video',
95+
})
96+
97+
const plainResults = []
98+
99+
for (const result of innertubeResults.results) {
100+
if (result.type !== 'Video') {
101+
continue
102+
}
103+
104+
plainResults.push({
105+
videoId: result.video_id,
106+
title: result.title.text,
107+
length_text: result.length_text.text,
108+
author: {
109+
name: result.author.name,
110+
},
111+
})
112+
}
113+
114+
return plainResults
115+
}

main-src/main.cjs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,10 @@ const path = require('path')
66
const { compareVersions } = require('compare-versions')
77
const fetch = require('electron-fetch').default
88
const xxhash = require('xxhash-wasm')
9-
const ytSearch = require('yt-search')
109
const serve = require('electron-serve').default
1110
const Store = require('electron-store').default
1211
const processQueue = require('./processQueue.cjs')
12+
const { searchYt } = require('./fetchYtStream.cjs')
1313

1414
let electronStore = null
1515

@@ -28,7 +28,7 @@ function handleComputeLocalFileHash(event, path) {
2828
}
2929

3030
async function handleYouTubeSearch(event, query) {
31-
return JSON.parse(JSON.stringify(await ytSearch(query)))
31+
return await searchYt(query)
3232
}
3333

3434
async function handleSetProcessQueueItems(event, items) {

0 commit comments

Comments
 (0)