Skip to content

Commit

Permalink
Move local API player cache internals into the main process (#5068)
Browse files Browse the repository at this point in the history
  • Loading branch information
absidue authored May 11, 2024
1 parent 71b7c60 commit 7685d75
Show file tree
Hide file tree
Showing 5 changed files with 49 additions and 58 deletions.
7 changes: 4 additions & 3 deletions src/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@ const IpcChannels = {
DISABLE_PROXY: 'disable-proxy',
OPEN_EXTERNAL_LINK: 'open-external-link',
GET_SYSTEM_LOCALE: 'get-system-locale',
GET_USER_DATA_PATH: 'get-user-data-path',
GET_USER_DATA_PATH_SYNC: 'get-user-data-path-sync',
GET_PICTURES_PATH: 'get-pictures-path',
SHOW_OPEN_DIALOG: 'show-open-dialog',
SHOW_SAVE_DIALOG: 'show-save-dialog',
Expand All @@ -26,7 +24,10 @@ const IpcChannels = {
SYNC_PLAYLISTS: 'sync-playlists',

GET_REPLACE_HTTP_CACHE: 'get-replace-http-cache',
TOGGLE_REPLACE_HTTP_CACHE: 'toggle-replace-http-cache'
TOGGLE_REPLACE_HTTP_CACHE: 'toggle-replace-http-cache',

PLAYER_CACHE_GET: 'player-cache-get',
PLAYER_CACHE_SET: 'player-cache-set'
}

const DBActions = {
Expand Down
43 changes: 34 additions & 9 deletions src/main/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -199,10 +199,12 @@ function runApp() {
app.commandLine.appendSwitch('enable-features', 'VaapiVideoDecodeLinuxGL')
}

const userDataPath = app.getPath('userData')

// command line switches need to be added before the app ready event first
// that means we can't use the normal settings system as that is asynchronous,
// doing it synchronously ensures that we add it before the event fires
const REPLACE_HTTP_CACHE_PATH = `${app.getPath('userData')}/experiment-replace-http-cache`
const REPLACE_HTTP_CACHE_PATH = `${userDataPath}/experiment-replace-http-cache`
const replaceHttpCache = existsSync(REPLACE_HTTP_CACHE_PATH)
if (replaceHttpCache) {
// the http cache causes excessive disk usage during video playback
Expand All @@ -211,6 +213,8 @@ function runApp() {
app.commandLine.appendSwitch('disable-http-cache')
}

const PLAYER_CACHE_PATH = `${userDataPath}/player_cache`

// See: https://stackoverflow.com/questions/45570589/electron-protocol-handler-not-working-on-windows
// remove so we can register each time as we run the app.
app.removeAsDefaultProtocolClient('freetube')
Expand Down Expand Up @@ -866,14 +870,6 @@ function runApp() {
return app.getSystemLocale()
})

ipcMain.handle(IpcChannels.GET_USER_DATA_PATH, () => {
return app.getPath('userData')
})

ipcMain.on(IpcChannels.GET_USER_DATA_PATH_SYNC, (event) => {
event.returnValue = app.getPath('userData')
})

ipcMain.handle(IpcChannels.GET_PICTURES_PATH, () => {
return app.getPath('pictures')
})
Expand Down Expand Up @@ -938,6 +934,35 @@ function runApp() {
relaunch()
})

function playerCachePathForKey(key) {
// Remove path separators and period characters,
// to prevent any files outside of the player_cache directory,
// from being read or written
const sanitizedKey = `${key}`.replaceAll(/[./\\]/g, '__')

return path.join(PLAYER_CACHE_PATH, sanitizedKey)
}

ipcMain.handle(IpcChannels.PLAYER_CACHE_GET, async (_, key) => {
const filePath = playerCachePathForKey(key)

try {
const contents = await asyncFs.readFile(filePath)
return contents.buffer
} catch (e) {
console.error(e)
return undefined
}
})

ipcMain.handle(IpcChannels.PLAYER_CACHE_SET, async (_, key, value) => {
const filePath = playerCachePathForKey(key)

await asyncFs.mkdir(PLAYER_CACHE_PATH, { recursive: true })

await asyncFs.writeFile(filePath, new Uint8Array(value))
})

// ************************************************* //
// DB related IPC calls
// *********** //
Expand Down
42 changes: 10 additions & 32 deletions src/renderer/helpers/api/PlayerCache.js
Original file line number Diff line number Diff line change
@@ -1,43 +1,21 @@
import fs from 'fs/promises'
import path from 'path'
import { IpcChannels } from '../../../constants'

import { pathExists } from '../filesystem'

// based off https://github.com/LuanRT/YouTube.js/blob/6caa679df6ddc77d25be02dcb7355b722ab268aa/src/utils/Cache.ts
// avoids errors caused by the fully dynamic `fs` and `path` module imports that youtubei.js's UniversalCache does
export class PlayerCache {
constructor(cacheDirectory) {
this.cacheDirectory = cacheDirectory
}

async get(key) {
const filePath = path.resolve(this.cacheDirectory, key)

try {
const contents = await fs.readFile(filePath)
return contents.buffer
} catch (e) {
if (e?.code === 'ENOENT') {
return undefined
}
throw e
if (process.env.IS_ELECTRON) {
const { ipcRenderer } = require('electron')
return await ipcRenderer.invoke(IpcChannels.PLAYER_CACHE_GET, key)
}
}

async set(key, value) {
await fs.mkdir(this.cacheDirectory, { recursive: true })

const filePath = path.resolve(this.cacheDirectory, key)
await fs.writeFile(filePath, new Uint8Array(value))
if (process.env.IS_ELECTRON) {
const { ipcRenderer } = require('electron')
await ipcRenderer.invoke(IpcChannels.PLAYER_CACHE_SET, key, value)
}
}

async remove(key) {
const filePath = path.resolve(this.cacheDirectory, key)

if (await pathExists(filePath)) {
try {
await fs.unlink(filePath)
} catch { }
}
async remove(_key) {
// no-op; YouTube.js only uses remove for the OAuth credentials, but we don't use that in FreeTube
}
}
5 changes: 1 addition & 4 deletions src/renderer/helpers/api/local.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { ClientType, Endpoints, Innertube, Misc, UniversalCache, Utils, YT } from 'youtubei.js'
import Autolinker from 'autolinker'
import { join } from 'path'
import { SEARCH_CHAR_LIMIT } from '../../../constants'

import { PlayerCache } from './PlayerCache'
Expand All @@ -9,7 +8,6 @@ import {
calculatePublishedDate,
escapeHTML,
extractNumberFromString,
getUserDataPath,
toLocalePublicationString
} from '../utils'

Expand Down Expand Up @@ -41,8 +39,7 @@ async function createInnertube({ withPlayer = false, location = undefined, safet
let cache
if (withPlayer) {
if (process.env.IS_ELECTRON) {
const userData = await getUserDataPath()
cache = new PlayerCache(join(userData, 'player_cache'))
cache = new PlayerCache()
} else {
cache = new UniversalCache(false)
}
Expand Down
10 changes: 0 additions & 10 deletions src/renderer/helpers/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -611,16 +611,6 @@ export async function getSystemLocale() {
return locale || 'en-US'
}

export async function getUserDataPath() {
if (process.env.IS_ELECTRON) {
const { ipcRenderer } = require('electron')
return await ipcRenderer.invoke(IpcChannels.GET_USER_DATA_PATH)
} else {
// TODO: implement getUserDataPath web compatible callback
return null
}
}

export async function getPicturesPath() {
if (process.env.IS_ELECTRON) {
const { ipcRenderer } = require('electron')
Expand Down

0 comments on commit 7685d75

Please sign in to comment.