diff --git a/src/constants.js b/src/constants.js index 56b260bfcaebe..bd888eb24ce93 100644 --- a/src/constants.js +++ b/src/constants.js @@ -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', @@ -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 = { diff --git a/src/main/index.js b/src/main/index.js index dad2c9fb0e8ca..dd2a65667a916 100644 --- a/src/main/index.js +++ b/src/main/index.js @@ -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 @@ -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') @@ -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') }) @@ -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 // *********** // diff --git a/src/renderer/helpers/api/PlayerCache.js b/src/renderer/helpers/api/PlayerCache.js index 345d9153c16ed..d05036719c122 100644 --- a/src/renderer/helpers/api/PlayerCache.js +++ b/src/renderer/helpers/api/PlayerCache.js @@ -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 } } diff --git a/src/renderer/helpers/api/local.js b/src/renderer/helpers/api/local.js index 2db5278f03110..6f0a8bf61d944 100644 --- a/src/renderer/helpers/api/local.js +++ b/src/renderer/helpers/api/local.js @@ -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' @@ -9,7 +8,6 @@ import { calculatePublishedDate, escapeHTML, extractNumberFromString, - getUserDataPath, toLocalePublicationString } from '../utils' @@ -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) } diff --git a/src/renderer/helpers/utils.js b/src/renderer/helpers/utils.js index b0ee1fb4d7189..e061e9b7bcea8 100644 --- a/src/renderer/helpers/utils.js +++ b/src/renderer/helpers/utils.js @@ -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')