Skip to content

Commit 4b1b044

Browse files
committed
Fix Innertube caching
1 parent 0d88fd9 commit 4b1b044

2 files changed

Lines changed: 566 additions & 533 deletions

File tree

main-src/fetchYtStream.js

Lines changed: 156 additions & 125 deletions
Original file line numberDiff line numberDiff line change
@@ -1,125 +1,156 @@
1-
// This file is partially copied from: https://github.com/LuanRT/BgUtils/blob/main/examples/node/index.ts
2-
3-
import { Innertube, UniversalCache, Log } from 'youtubei.js'
4-
import { BG } from 'bgutils-js'
5-
import { JSDOM } from 'jsdom'
6-
import electronFetch from 'electron-fetch'
7-
const fetch = electronFetch.default
8-
9-
const REQUEST_KEY = 'O43z0dpjhgX20SCx4KAo'
10-
11-
let searchInnertube = null
12-
let downloadInnertube = null
13-
14-
const setupSearchInnertube = async () => {
15-
if (searchInnertube) {
16-
return
17-
}
18-
19-
searchInnertube = await Innertube.create({ retrieve_player: false })
20-
}
21-
22-
const setupDownloadInnertube = async () => {
23-
if (downloadInnertube) {
24-
return
25-
}
26-
27-
await setupSearchInnertube()
28-
29-
if (process.env.NODE_ENV === 'dev') {
30-
Log.setLevel(Log.Level.INFO)
31-
} else {
32-
Log.setLevel(Log.Level.WARNING)
33-
}
34-
35-
const visitorData = searchInnertube.session.context.client.visitorData
36-
if (!visitorData) {
37-
throw new Error('Could not get visitorData')
38-
}
39-
40-
const dom = new JSDOM()
41-
Object.assign(globalThis, {
42-
window: dom.window,
43-
document: dom.window.document,
44-
})
45-
46-
const bgConfig = {
47-
fetch,
48-
globalObj: globalThis,
49-
identifier: visitorData,
50-
requestKey: REQUEST_KEY,
51-
}
52-
53-
const bgChallenge = await BG.Challenge.create(bgConfig)
54-
if (!bgChallenge) {
55-
throw new Error('Could not get challenge')
56-
}
57-
58-
const interpreterJavascript =
59-
bgChallenge.interpreterJavascript.privateDoNotAccessOrElseSafeScriptWrappedValue
60-
if (interpreterJavascript) {
61-
new Function(interpreterJavascript)()
62-
} else {
63-
throw new Error('Could not load VM')
64-
}
65-
66-
const poTokenResult = await BG.PoToken.generate({
67-
program: bgChallenge.program,
68-
globalName: bgChallenge.globalName,
69-
bgConfig,
70-
})
71-
72-
downloadInnertube = await Innertube.create({
73-
po_token: poTokenResult.poToken,
74-
visitor_data: visitorData,
75-
cache: new UniversalCache(true),
76-
generate_session_locally: true,
77-
})
78-
}
79-
80-
export const fetchYtStream = async (videoId) => {
81-
await setupDownloadInnertube()
82-
83-
const ytStream = await downloadInnertube.download(videoId, {
84-
type: 'audio',
85-
quality: 'best',
86-
})
87-
88-
return ytStream
89-
}
90-
91-
export const searchYt = async (query) => {
92-
await setupSearchInnertube()
93-
94-
const innertubeResults = await searchInnertube.search(query, {
95-
type: 'video',
96-
})
97-
98-
const plainResults = []
99-
100-
for (const result of innertubeResults.results) {
101-
if (
102-
result.type !== 'Video' ||
103-
typeof result.video_id === 'undefined' ||
104-
typeof result.title === 'undefined' ||
105-
typeof result.title.text === 'undefined' ||
106-
typeof result.author === 'undefined' ||
107-
typeof result.author.name === 'undefined' ||
108-
typeof result.length_text === 'undefined' ||
109-
typeof result.length_text.text === 'undefined'
110-
) {
111-
continue
112-
}
113-
114-
plainResults.push({
115-
videoId: result.video_id,
116-
title: result.title.text,
117-
length_text: result.length_text.text,
118-
author: {
119-
name: result.author.name,
120-
},
121-
})
122-
}
123-
124-
return plainResults
125-
}
1+
// This file is partially copied from: https://github.com/LuanRT/BgUtils/blob/main/examples/node/index.ts
2+
3+
import fs from 'fs/promises'
4+
import path from 'path'
5+
import os from 'os'
6+
import { Innertube, UniversalCache, Log } from 'youtubei.js'
7+
import { BG } from 'bgutils-js'
8+
import { JSDOM } from 'jsdom'
9+
import electronFetch from 'electron-fetch'
10+
const fetch = electronFetch.default
11+
12+
const REQUEST_KEY = 'O43z0dpjhgX20SCx4KAo'
13+
14+
let cacheDir = null
15+
let searchInnertube = null
16+
let downloadInnertube = null
17+
18+
const createYtCacheDir = async () => {
19+
if (cacheDir) {
20+
return
21+
}
22+
23+
cacheDir = await fs.mkdtemp(path.join(os.tmpdir(), 'StemRoller-cache-'))
24+
}
25+
26+
export const deleteYtCacheDir = async () => {
27+
if (!cacheDir) {
28+
return
29+
}
30+
31+
try {
32+
await fs.rm(cacheDir, {
33+
recursive: true,
34+
maxRetries: 5,
35+
retryDelay: 1000,
36+
})
37+
console.log(`Deleted cache folder "${cacheDir}"`)
38+
} catch (error) {
39+
console.trace(error)
40+
}
41+
}
42+
43+
const setupSearchInnertube = async () => {
44+
if (searchInnertube) {
45+
return
46+
}
47+
48+
searchInnertube = await Innertube.create({ retrieve_player: false })
49+
}
50+
51+
const setupDownloadInnertube = async () => {
52+
if (downloadInnertube) {
53+
return
54+
}
55+
56+
await setupSearchInnertube()
57+
58+
if (process.env.NODE_ENV === 'dev') {
59+
Log.setLevel(Log.Level.INFO)
60+
} else {
61+
Log.setLevel(Log.Level.WARNING)
62+
}
63+
64+
const visitorData = searchInnertube.session.context.client.visitorData
65+
if (!visitorData) {
66+
throw new Error('Could not get visitorData')
67+
}
68+
69+
const dom = new JSDOM()
70+
Object.assign(globalThis, {
71+
window: dom.window,
72+
document: dom.window.document,
73+
})
74+
75+
const bgConfig = {
76+
fetch,
77+
globalObj: globalThis,
78+
identifier: visitorData,
79+
requestKey: REQUEST_KEY,
80+
}
81+
82+
const bgChallenge = await BG.Challenge.create(bgConfig)
83+
if (!bgChallenge) {
84+
throw new Error('Could not get challenge')
85+
}
86+
87+
const interpreterJavascript =
88+
bgChallenge.interpreterJavascript.privateDoNotAccessOrElseSafeScriptWrappedValue
89+
if (interpreterJavascript) {
90+
new Function(interpreterJavascript)()
91+
} else {
92+
throw new Error('Could not load VM')
93+
}
94+
95+
const poTokenResult = await BG.PoToken.generate({
96+
program: bgChallenge.program,
97+
globalName: bgChallenge.globalName,
98+
bgConfig,
99+
})
100+
101+
await createYtCacheDir()
102+
103+
downloadInnertube = await Innertube.create({
104+
po_token: poTokenResult.poToken,
105+
visitor_data: visitorData,
106+
cache: new UniversalCache(true, cacheDir),
107+
generate_session_locally: true,
108+
})
109+
}
110+
111+
export const fetchYtStream = async (videoId) => {
112+
await setupDownloadInnertube()
113+
114+
const ytStream = await downloadInnertube.download(videoId, {
115+
type: 'audio',
116+
quality: 'best',
117+
})
118+
119+
return ytStream
120+
}
121+
122+
export const searchYt = async (query) => {
123+
await setupSearchInnertube()
124+
125+
const innertubeResults = await searchInnertube.search(query, {
126+
type: 'video',
127+
})
128+
129+
const plainResults = []
130+
131+
for (const result of innertubeResults.results) {
132+
if (
133+
result.type !== 'Video' ||
134+
typeof result.video_id === 'undefined' ||
135+
typeof result.title === 'undefined' ||
136+
typeof result.title.text === 'undefined' ||
137+
typeof result.author === 'undefined' ||
138+
typeof result.author.name === 'undefined' ||
139+
typeof result.length_text === 'undefined' ||
140+
typeof result.length_text.text === 'undefined'
141+
) {
142+
continue
143+
}
144+
145+
plainResults.push({
146+
videoId: result.video_id,
147+
title: result.title.text,
148+
length_text: result.length_text.text,
149+
author: {
150+
name: result.author.name,
151+
},
152+
})
153+
}
154+
155+
return plainResults
156+
}

0 commit comments

Comments
 (0)