Add preload script and disable node integration in renderer processes (#7424)

This commit is contained in:
absidue
2025-05-29 17:50:19 +02:00
committed by GitHub
parent 01f1a8255a
commit 45ecc6b427
26 changed files with 499 additions and 350 deletions

View File

@@ -18,6 +18,7 @@ const web = process.argv.indexOf('--web') !== -1
let mainConfig
let rendererConfig
let preloadConfig
let botGuardScriptConfig
let webConfig
let SHAKA_LOCALES_TO_BE_BUNDLED
@@ -25,6 +26,7 @@ let SHAKA_LOCALES_TO_BE_BUNDLED
if (!web) {
mainConfig = require('./webpack.main.config')
rendererConfig = require('./webpack.renderer.config')
preloadConfig = require('./webpack.preload.config.js')
botGuardScriptConfig = require('./webpack.botGuardScript.config')
SHAKA_LOCALES_TO_BE_BUNDLED = rendererConfig.SHAKA_LOCALES_TO_BE_BUNDLED
@@ -132,6 +134,36 @@ function startMain() {
})
}
function startPreload() {
const compiler = webpack(preloadConfig)
const { name } = compiler
let firstTime = true
compiler.hooks.afterEmit.tap('afterEmit', async () => {
console.log(`\nCompiled ${name} script!`)
if (firstTime) {
firstTime = false
} else {
manualRestart = true
await restartElectron()
setTimeout(() => {
manualRestart = false
}, 2500)
}
console.log(`\nWatching file changes for ${name} script...`)
})
compiler.watch({
aggregateTimeout: 500,
},
err => {
if (err) console.error(err)
})
}
function startRenderer(callback) {
const compiler = webpack(rendererConfig)
const { name } = compiler
@@ -208,6 +240,7 @@ function startWeb () {
if (!web) {
startRenderer(() => {
startBotGuardScript()
startPreload()
startMain()
})
} else {

View File

@@ -21,6 +21,8 @@ const paths = readdirSync(distDirectory, {
// disallow the renderer process/browser windows to read the main.js file
dirent.name !== 'main.js' &&
dirent.name !== 'main.js.LICENSE.txt' &&
// disallow the renderer process/browser windows to read the preload.js file
dirent.name !== 'preload.js' &&
// disallow the renderer process/browser windows to read the botGuardScript.js file
dirent.name !== 'botGuardScript.js' &&
// filter out any web build files, in case the dist directory contains a web build

View File

@@ -0,0 +1,34 @@
const path = require('path')
const isDevMode = process.env.NODE_ENV === 'development'
/** @type {import('webpack').Configuration} */
const config = {
name: 'preload',
mode: process.env.NODE_ENV,
devtool: isDevMode ? 'eval-cheap-module-source-map' : false,
entry: {
preload: path.join(__dirname, '../src/preload/main.js'),
},
infrastructureLogging: {
// Only warnings and errors
// level: 'none' disable logging
// Please read https://webpack.js.org/configuration/other-options/#infrastructurelogginglevel
level: isDevMode ? 'info' : 'none'
},
output: {
path: path.join(__dirname, '../dist'),
filename: '[name].js'
},
externals: [
'electron/renderer'
],
externalsType: 'commonjs',
node: {
__dirname: false,
__filename: false
},
target: 'electron-preload',
}
module.exports = config

View File

@@ -39,7 +39,7 @@ const config = {
level: isDevMode ? 'info' : 'none'
},
output: {
libraryTarget: 'commonjs2',
scriptType: 'text/javascript',
path: path.join(__dirname, '../dist'),
filename: '[name].js',
},
@@ -191,7 +191,7 @@ const config = {
},
extensions: ['.js', '.vue']
},
target: 'electron-renderer',
target: 'web',
}
if (isDevMode) {

View File

@@ -43,9 +43,10 @@
"lint-style": "stylelint \"src/**/*.{css,scss}\"",
"lint-style-fix": "stylelint --fix \"src/**/*.{css,scss}\"",
"lint-yml": "eslint --config eslint.config.mjs \"**/*.yml\" \"**/*.yaml\"",
"pack": "run-p pack:main pack:renderer pack:botGuardScript && node _scripts/injectAllowedPaths.mjs",
"pack": "run-p pack:main pack:renderer pack:preload pack:botGuardScript && node _scripts/injectAllowedPaths.mjs",
"pack:main": "webpack --mode=production --node-env=production --config _scripts/webpack.main.config.js",
"pack:renderer": "webpack --mode=production --node-env=production --config _scripts/webpack.renderer.config.js",
"pack:preload": "webpack --mode=production --node-env=production --config _scripts/webpack.preload.config.js",
"pack:web": "webpack --mode=production --node-env=production --config _scripts/webpack.web.config.js",
"pack:botGuardScript": "webpack --config _scripts/webpack.botGuardScript.config.js",
"postinstall": "yarn run --silent patch-shaka",

View File

@@ -12,9 +12,6 @@ const IpcChannels = {
APP_READY: 'app-ready',
RELAUNCH_REQUEST: 'relaunch-request',
REQUEST_FULLSCREEN: 'request-fullscreen',
REQUEST_PIP: 'request-pip',
SEARCH_INPUT_HANDLING_READY: 'search-input-handling-ready',
UPDATE_SEARCH_INPUT_TEXT: 'update-search-input-text',

View File

@@ -1,323 +1,193 @@
import { ipcRenderer } from 'electron'
import { IpcChannels, DBActions } from '../../constants'
import { DBActions } from '../../constants'
class Settings {
static find() {
return ipcRenderer.invoke(
IpcChannels.DB_SETTINGS,
{ action: DBActions.GENERAL.FIND }
)
return window.ftElectron.dbSettings(DBActions.GENERAL.FIND)
}
static upsert(_id, value) {
return ipcRenderer.invoke(
IpcChannels.DB_SETTINGS,
{ action: DBActions.GENERAL.UPSERT, data: { _id, value } }
)
return window.ftElectron.dbSettings(DBActions.GENERAL.UPSERT, { _id, value })
}
}
class History {
static find() {
return ipcRenderer.invoke(
IpcChannels.DB_HISTORY,
{ action: DBActions.GENERAL.FIND }
)
return window.ftElectron.dbHistory(DBActions.GENERAL.FIND)
}
static upsert(record) {
return ipcRenderer.invoke(
IpcChannels.DB_HISTORY,
{ action: DBActions.GENERAL.UPSERT, data: record }
)
return window.ftElectron.dbHistory(DBActions.GENERAL.UPSERT, record)
}
static overwrite(records) {
return ipcRenderer.invoke(
IpcChannels.DB_HISTORY,
{ action: DBActions.HISTORY.OVERWRITE, data: records }
)
return window.ftElectron.dbHistory(DBActions.HISTORY.OVERWRITE, records)
}
static updateWatchProgress(videoId, watchProgress) {
return ipcRenderer.invoke(
IpcChannels.DB_HISTORY,
{
action: DBActions.HISTORY.UPDATE_WATCH_PROGRESS,
data: { videoId, watchProgress }
}
return window.ftElectron.dbHistory(
DBActions.HISTORY.UPDATE_WATCH_PROGRESS,
{ videoId, watchProgress }
)
}
static updateLastViewedPlaylist(videoId, lastViewedPlaylistId, lastViewedPlaylistType, lastViewedPlaylistItemId) {
return ipcRenderer.invoke(
IpcChannels.DB_HISTORY,
{
action: DBActions.HISTORY.UPDATE_PLAYLIST,
data: { videoId, lastViewedPlaylistId, lastViewedPlaylistType, lastViewedPlaylistItemId }
}
return window.ftElectron.dbHistory(
DBActions.HISTORY.UPDATE_PLAYLIST,
{ videoId, lastViewedPlaylistId, lastViewedPlaylistType, lastViewedPlaylistItemId }
)
}
static delete(videoId) {
return ipcRenderer.invoke(
IpcChannels.DB_HISTORY,
{ action: DBActions.GENERAL.DELETE, data: videoId }
)
return window.ftElectron.dbHistory(DBActions.GENERAL.DELETE, videoId)
}
static deleteAll() {
return ipcRenderer.invoke(
IpcChannels.DB_HISTORY,
{ action: DBActions.GENERAL.DELETE_ALL }
)
return window.ftElectron.dbHistory(DBActions.GENERAL.DELETE_ALL)
}
}
class Profiles {
static create(profile) {
return ipcRenderer.invoke(
IpcChannels.DB_PROFILES,
{ action: DBActions.GENERAL.CREATE, data: profile }
)
return window.ftElectron.dbProfiles(DBActions.GENERAL.CREATE, profile)
}
static find() {
return ipcRenderer.invoke(
IpcChannels.DB_PROFILES,
{ action: DBActions.GENERAL.FIND }
)
return window.ftElectron.dbProfiles(DBActions.GENERAL.FIND)
}
static upsert(profile) {
return ipcRenderer.invoke(
IpcChannels.DB_PROFILES,
{ action: DBActions.GENERAL.UPSERT, data: profile }
)
return window.ftElectron.dbProfiles(DBActions.GENERAL.UPSERT, profile)
}
static addChannelToProfiles(channel, profileIds) {
return ipcRenderer.invoke(
IpcChannels.DB_PROFILES,
{
action: DBActions.PROFILES.ADD_CHANNEL,
data: { channel, profileIds }
}
)
return window.ftElectron.dbProfiles(DBActions.PROFILES.ADD_CHANNEL, { channel, profileIds })
}
static removeChannelFromProfiles(channelId, profileIds) {
return ipcRenderer.invoke(
IpcChannels.DB_PROFILES,
{
action: DBActions.PROFILES.REMOVE_CHANNEL,
data: { channelId, profileIds }
}
)
return window.ftElectron.dbProfiles(DBActions.PROFILES.REMOVE_CHANNEL, { channelId, profileIds })
}
static delete(id) {
return ipcRenderer.invoke(
IpcChannels.DB_PROFILES,
{ action: DBActions.GENERAL.DELETE, data: id }
)
return window.ftElectron.dbProfiles(DBActions.GENERAL.DELETE, id)
}
}
class Playlists {
static create(playlists) {
return ipcRenderer.invoke(
IpcChannels.DB_PLAYLISTS,
{ action: DBActions.GENERAL.CREATE, data: playlists }
)
return window.ftElectron.dbPlaylists(DBActions.GENERAL.CREATE, playlists)
}
static find() {
return ipcRenderer.invoke(
IpcChannels.DB_PLAYLISTS,
{ action: DBActions.GENERAL.FIND }
)
return window.ftElectron.dbPlaylists(DBActions.GENERAL.FIND)
}
static upsert(playlist) {
return ipcRenderer.invoke(
IpcChannels.DB_PLAYLISTS,
{ action: DBActions.GENERAL.UPSERT, data: playlist }
)
return window.ftElectron.dbPlaylists(DBActions.GENERAL.UPSERT, playlist)
}
static upsertVideoByPlaylistId(_id, videoData) {
return ipcRenderer.invoke(
IpcChannels.DB_PLAYLISTS,
{
action: DBActions.PLAYLISTS.UPSERT_VIDEO,
data: { _id, videoData }
}
)
return window.ftElectron.dbPlaylists(DBActions.PLAYLISTS.UPSERT_VIDEO, { _id, videoData })
}
static upsertVideosByPlaylistId(_id, videos) {
return ipcRenderer.invoke(
IpcChannels.DB_PLAYLISTS,
{
action: DBActions.PLAYLISTS.UPSERT_VIDEOS,
data: { _id, videos }
}
)
return window.ftElectron.dbPlaylists(DBActions.PLAYLISTS.UPSERT_VIDEOS, { _id, videos })
}
static delete(_id) {
return ipcRenderer.invoke(
IpcChannels.DB_PLAYLISTS,
{ action: DBActions.GENERAL.DELETE, data: _id }
)
return window.ftElectron.dbPlaylists(DBActions.GENERAL.DELETE, _id)
}
static deleteVideoIdByPlaylistId(_id, videoId, playlistItemId) {
return ipcRenderer.invoke(
IpcChannels.DB_PLAYLISTS,
{
action: DBActions.PLAYLISTS.DELETE_VIDEO_ID,
data: { _id, videoId, playlistItemId }
}
return window.ftElectron.dbPlaylists(
DBActions.PLAYLISTS.DELETE_VIDEO_ID,
{ _id, videoId, playlistItemId }
)
}
static deleteVideoIdsByPlaylistId(_id, playlistItemIds) {
return ipcRenderer.invoke(
IpcChannels.DB_PLAYLISTS,
{
action: DBActions.PLAYLISTS.DELETE_VIDEO_IDS,
data: { _id, playlistItemIds }
}
return window.ftElectron.dbPlaylists(
DBActions.PLAYLISTS.DELETE_VIDEO_IDS,
{ _id, playlistItemIds }
)
}
static deleteAllVideosByPlaylistId(_id) {
return ipcRenderer.invoke(
IpcChannels.DB_PLAYLISTS,
{
action: DBActions.PLAYLISTS.DELETE_ALL_VIDEOS,
data: _id
}
)
return window.ftElectron.dbPlaylists(DBActions.PLAYLISTS.DELETE_ALL_VIDEOS, _id)
}
static deleteMultiple(ids) {
return ipcRenderer.invoke(
IpcChannels.DB_PLAYLISTS,
{ action: DBActions.GENERAL.DELETE_MULTIPLE, data: ids }
)
return window.ftElectron.dbPlaylists(DBActions.GENERAL.DELETE_MULTIPLE, ids)
}
static deleteAll() {
return ipcRenderer.invoke(
IpcChannels.DB_PLAYLISTS,
{ action: DBActions.GENERAL.DELETE_ALL }
)
return window.ftElectron.dbPlaylists(DBActions.GENERAL.DELETE_ALL)
}
}
class SearchHistory {
static find() {
return ipcRenderer.invoke(
IpcChannels.DB_SEARCH_HISTORY,
{ action: DBActions.GENERAL.FIND }
)
return window.ftElectron.dbSearchHistory(DBActions.GENERAL.FIND)
}
static upsert(searchHistoryEntry) {
return ipcRenderer.invoke(
IpcChannels.DB_SEARCH_HISTORY,
{ action: DBActions.GENERAL.UPSERT, data: searchHistoryEntry }
)
return window.ftElectron.dbSearchHistory(DBActions.GENERAL.UPSERT, searchHistoryEntry)
}
static delete(_id) {
return ipcRenderer.invoke(
IpcChannels.DB_SEARCH_HISTORY,
{ action: DBActions.GENERAL.DELETE, data: _id }
)
return window.ftElectron.dbSearchHistory(DBActions.GENERAL.DELETE, _id)
}
static deleteAll() {
return ipcRenderer.invoke(
IpcChannels.DB_SEARCH_HISTORY,
{ action: DBActions.GENERAL.DELETE_ALL }
)
return window.ftElectron.dbSearchHistory(DBActions.GENERAL.DELETE_ALL)
}
}
class SubscriptionCache {
static find() {
return ipcRenderer.invoke(
IpcChannels.DB_SUBSCRIPTION_CACHE,
{ action: DBActions.GENERAL.FIND }
)
return window.ftElectron.dbSubscriptionCache(DBActions.GENERAL.FIND)
}
static updateVideosByChannelId(channelId, entries, timestamp) {
return ipcRenderer.invoke(
IpcChannels.DB_SUBSCRIPTION_CACHE,
{
action: DBActions.SUBSCRIPTION_CACHE.UPDATE_VIDEOS_BY_CHANNEL,
data: { channelId, entries, timestamp },
}
return window.ftElectron.dbSubscriptionCache(
DBActions.SUBSCRIPTION_CACHE.UPDATE_VIDEOS_BY_CHANNEL,
{ channelId, entries, timestamp }
)
}
static updateLiveStreamsByChannelId(channelId, entries, timestamp) {
return ipcRenderer.invoke(
IpcChannels.DB_SUBSCRIPTION_CACHE,
{
action: DBActions.SUBSCRIPTION_CACHE.UPDATE_LIVE_STREAMS_BY_CHANNEL,
data: { channelId, entries, timestamp },
}
return window.ftElectron.dbSubscriptionCache(
DBActions.SUBSCRIPTION_CACHE.UPDATE_LIVE_STREAMS_BY_CHANNEL,
{ channelId, entries, timestamp }
)
}
static updateShortsByChannelId(channelId, entries, timestamp) {
return ipcRenderer.invoke(
IpcChannels.DB_SUBSCRIPTION_CACHE,
{
action: DBActions.SUBSCRIPTION_CACHE.UPDATE_SHORTS_BY_CHANNEL,
data: { channelId, entries, timestamp },
}
return window.ftElectron.dbSubscriptionCache(
DBActions.SUBSCRIPTION_CACHE.UPDATE_SHORTS_BY_CHANNEL,
{ channelId, entries, timestamp }
)
}
static updateShortsWithChannelPageShortsByChannelId(channelId, entries) {
return ipcRenderer.invoke(
IpcChannels.DB_SUBSCRIPTION_CACHE,
{
action: DBActions.SUBSCRIPTION_CACHE.UPDATE_SHORTS_WITH_CHANNEL_PAGE_SHORTS_BY_CHANNEL,
data: { channelId, entries },
}
return window.ftElectron.dbSubscriptionCache(
DBActions.SUBSCRIPTION_CACHE.UPDATE_SHORTS_WITH_CHANNEL_PAGE_SHORTS_BY_CHANNEL,
{ channelId, entries }
)
}
static updateCommunityPostsByChannelId(channelId, entries, timestamp) {
return ipcRenderer.invoke(
IpcChannels.DB_SUBSCRIPTION_CACHE,
{
action: DBActions.SUBSCRIPTION_CACHE.UPDATE_COMMUNITY_POSTS_BY_CHANNEL,
data: { channelId, entries, timestamp },
}
return window.ftElectron.dbSubscriptionCache(
DBActions.SUBSCRIPTION_CACHE.UPDATE_COMMUNITY_POSTS_BY_CHANNEL,
{ channelId, entries, timestamp }
)
}
static deleteMultipleChannels(channelIds) {
return ipcRenderer.invoke(
IpcChannels.DB_SUBSCRIPTION_CACHE,
{ action: DBActions.GENERAL.DELETE_MULTIPLE, data: channelIds }
)
return window.ftElectron.dbSubscriptionCache(DBActions.GENERAL.DELETE_MULTIPLE, channelIds)
}
static deleteAll() {
return ipcRenderer.invoke(
IpcChannels.DB_SUBSCRIPTION_CACHE,
{ action: DBActions.GENERAL.DELETE_ALL }
)
return window.ftElectron.dbSubscriptionCache(DBActions.GENERAL.DELETE_ALL)
}
}

View File

@@ -291,7 +291,7 @@ function runApp() {
if (mainWindow.isMinimized()) mainWindow.restore()
mainWindow.focus()
if (url) mainWindow.webContents.send(IpcChannels.OPEN_URL, url)
if (url) mainWindow.webContents.send(IpcChannels.OPEN_URL, url, false)
}
} else {
if (url) startupUrl = url
@@ -760,11 +760,11 @@ function runApp() {
autoHideMenuBar: true,
// useContentSize: true,
webPreferences: {
nodeIntegration: true,
nodeIntegrationInWorker: false,
webSecurity: false,
backgroundThrottling: false,
contextIsolation: false
preload: process.env.NODE_ENV === 'development'
? path.resolve(__dirname, '../../dist/preload.js')
: path.resolve(__dirname, 'preload.js')
},
minWidth: 340,
minHeight: 380
@@ -915,7 +915,7 @@ function runApp() {
ipcMain.on(IpcChannels.APP_READY, () => {
if (startupUrl) {
mainWindow.webContents.send(IpcChannels.OPEN_URL, startupUrl, { isLaunchLink: true })
mainWindow.webContents.send(IpcChannels.OPEN_URL, startupUrl, true)
}
startupUrl = null
})
@@ -1025,18 +1025,6 @@ function runApp() {
return app.getSystemLocale()
})
// Allows programmatic toggling of fullscreen without accompanying user interaction.
// See: https://developer.mozilla.org/en-US/docs/Web/Security/User_activation#transient_activation
ipcMain.on(IpcChannels.REQUEST_FULLSCREEN, ({ sender }) => {
sender.executeJavaScript('document.querySelector("video.player").ui.getControls().toggleFullScreen()', true)
})
// Allows programmatic toggling of picture-in-picture mode without accompanying user interaction.
// See: https://developer.mozilla.org/en-US/docs/Web/Security/User_activation#transient_activation
ipcMain.on(IpcChannels.REQUEST_PIP, ({ sender }) => {
sender.executeJavaScript('document.querySelector("video.player").ui.getControls().togglePiP()', true)
})
ipcMain.handle(IpcChannels.GET_SCREENSHOT_FALLBACK_FOLDER, (event) => {
if (!isFreeTubeUrl(event.senderFrame.url)) {
return
@@ -1204,8 +1192,8 @@ function runApp() {
})
})
ipcMain.on(IpcChannels.OPEN_IN_EXTERNAL_PLAYER, (_, payload) => {
const child = cp.spawn(payload.executable, payload.args, { detached: true, stdio: 'ignore' })
ipcMain.on(IpcChannels.OPEN_IN_EXTERNAL_PLAYER, (_, executable, args) => {
const child = cp.spawn(executable, args, { detached: true, stdio: 'ignore' })
child.unref()
})
@@ -1777,7 +1765,7 @@ function runApp() {
event.preventDefault()
if (mainWindow && mainWindow.webContents) {
mainWindow.webContents.send(IpcChannels.OPEN_URL, baseUrl(url))
mainWindow.webContents.send(IpcChannels.OPEN_URL, baseUrl(url), false)
} else {
startupUrl = baseUrl(url)
if (app.isReady()) createWindow()
@@ -1844,10 +1832,7 @@ function runApp() {
return
}
browserWindow.webContents.send(
IpcChannels.CHANGE_VIEW,
{ route: path }
)
browserWindow.webContents.send(IpcChannels.CHANGE_VIEW, path)
}
async function setMenu() {

302
src/preload/interface.js Normal file
View File

@@ -0,0 +1,302 @@
import { ipcRenderer, webFrame } from 'electron/renderer'
import { IpcChannels } from '../constants.js'
/**
* Linux fix for dynamically updating theme preference, this works on
* all systems running the electron app.
*/
ipcRenderer.on(IpcChannels.NATIVE_THEME_UPDATE, (_, shouldUseDarkColors) => {
webFrame.executeJavaScript(`document.body.dataset.systemTheme = "${shouldUseDarkColors ? 'dark' : 'light'}"`).catch()
})
let currentUpdateSearchInputTextListener
export default {
/**
* @returns {Promise<string>}
*/
getSystemLocale: () => {
return ipcRenderer.invoke(IpcChannels.GET_SYSTEM_LOCALE)
},
/**
* @param {string} path
* @param {Record<string, string> | null | undefined} query
* @param {string | null | undefined} searchQueryText
*/
openInNewWindow: (path, query, searchQueryText) => {
ipcRenderer.send(IpcChannels.CREATE_NEW_WINDOW, path, query, searchQueryText)
},
/**
* @param {string} url
*/
enableProxy: (url) => {
ipcRenderer.send(IpcChannels.ENABLE_PROXY, url)
},
disableProxy: () => {
ipcRenderer.send(IpcChannels.DISABLE_PROXY)
},
/**
* @param {string} authorization
* @param {string} url
*/
setInvidiousAuthorization: (authorization, url) => {
ipcRenderer.send(IpcChannels.SET_INVIDIOUS_AUTHORIZATION, authorization, url)
},
clearInvidiousAuthorization: () => {
ipcRenderer.send(IpcChannels.SET_INVIDIOUS_AUTHORIZATION, null)
},
startPowerSaveBlocker: () => {
ipcRenderer.send(IpcChannels.START_POWER_SAVE_BLOCKER)
},
stopPowerSaveBlocker: () => {
ipcRenderer.send(IpcChannels.STOP_POWER_SAVE_BLOCKER)
},
/**
* @returns {Promise<boolean>}
*/
getReplaceHttpCache: () => {
return ipcRenderer.invoke(IpcChannels.GET_REPLACE_HTTP_CACHE)
},
toggleReplaceHttpCache: () => {
ipcRenderer.send(IpcChannels.TOGGLE_REPLACE_HTTP_CACHE)
},
// Allows programmatic toggling of picture-in-picture mode without accompanying user interaction.
// See: https://developer.mozilla.org/en-US/docs/Web/Security/User_activation#transient_activation
requestPiP: () => {
webFrame.executeJavaScript('document.querySelector("video.player")?.ui.getControls().togglePiP()', true).catch()
},
// Allows programmatic toggling of fullscreen without accompanying user interaction.
// See: https://developer.mozilla.org/en-US/docs/Web/Security/User_activation#transient_activation
requestFullscreen: () => {
webFrame.executeJavaScript('document.querySelector("video.player")?.ui.getControls().toggleFullScreen()', true).catch()
},
/**
* @param {string} key
* @returns {Promise<ArrayBuffer>}
*/
playerCacheGet: (key) => {
return ipcRenderer.invoke(IpcChannels.PLAYER_CACHE_GET, key)
},
/**
* @param {string} key
* @param {ArrayBuffer} value
*/
playerCacheSet: async (key, value) => {
await ipcRenderer.invoke(IpcChannels.PLAYER_CACHE_SET, key, value)
},
/**
* @param {string} videoId
* @param {string} visitorData
* @param {string} context
* @returns {Promise<{ contentPoToken: string, sessionPoToken: string }>}
*/
generatePoTokens: (videoId, visitorData, context) => {
return ipcRenderer.invoke(IpcChannels.GENERATE_PO_TOKENS, videoId, visitorData, context)
},
/**
* @param {0 | 1} kind
*/
chooseDefaultFolder: (kind) => {
ipcRenderer.send(IpcChannels.CHOOSE_DEFAULT_FOLDER, kind)
},
/**
* @param {0 | 1} kind
* @param {string} filename
* @param {ArrayBuffer} contents
*/
writeToDefaultFolder: async (kind, filename, contents) => {
await ipcRenderer.invoke(IpcChannels.WRITE_TO_DEFAULT_FOLDER, kind, filename, contents)
},
/**
* @returns {Promise<string>}
*/
getScreenshotFallbackFolder: () => {
return ipcRenderer.invoke(IpcChannels.GET_SCREENSHOT_FALLBACK_FOLDER)
},
relaunch: () => {
ipcRenderer.send(IpcChannels.RELAUNCH_REQUEST)
},
/**
* @param {string} executable
* @param {string} args
*/
openInExternalPlayer: (executable, args) => {
ipcRenderer.send(IpcChannels.OPEN_IN_EXTERNAL_PLAYER, executable, args)
},
/**
* @param {number} factor
*/
setZoomFactor: (factor) => {
if (typeof factor === 'number' && factor > 0) {
webFrame.setZoomFactor(factor)
}
},
/**
* @returns {Promise<{ label: string, value: number, active: boolean }[]>}
*/
getNavigationHistory: () => {
return ipcRenderer.invoke(IpcChannels.GET_NAVIGATION_HISTORY)
},
/**
* @param {number} action
* @param {any} [data]
*/
dbSettings: (action, data) => {
return ipcRenderer.invoke(IpcChannels.DB_SETTINGS, data ? { action, data } : { action })
},
/**
* @param {number} action
* @param {any} [data]
*/
dbHistory: (action, data) => {
return ipcRenderer.invoke(IpcChannels.DB_HISTORY, data ? { action, data } : { action })
},
/**
* @param {number} action
* @param {any} [data]
*/
dbProfiles: (action, data) => {
return ipcRenderer.invoke(IpcChannels.DB_PROFILES, data ? { action, data } : { action })
},
/**
* @param {number} action
* @param {any} [data]
*/
dbPlaylists: (action, data) => {
return ipcRenderer.invoke(IpcChannels.DB_PLAYLISTS, data ? { action, data } : { action })
},
/**
* @param {number} action
* @param {any} [data]
*/
dbSearchHistory: (action, data) => {
return ipcRenderer.invoke(IpcChannels.DB_SEARCH_HISTORY, data ? { action, data } : { action })
},
/**
* @param {number} action
* @param {any} [data]
*/
dbSubscriptionCache: (action, data) => {
return ipcRenderer.invoke(IpcChannels.DB_SUBSCRIPTION_CACHE, data ? { action, data } : { action })
},
/**
* @param {(route: string) => void} handler
*/
handleChangeView: (handler) => {
ipcRenderer.on(IpcChannels.CHANGE_VIEW, (_, route) => {
handler(route)
})
},
/**
* @param {(url: string, isLaunchLink: boolean) => void} handler
*/
handleOpenUrl: (handler) => {
ipcRenderer.on(IpcChannels.OPEN_URL, (_, url, isLaunchLink) => {
handler(url, isLaunchLink)
})
ipcRenderer.send(IpcChannels.APP_READY)
},
/**
* Pass `null` to clear the handler
* @param {(text: string) => void | null} handler
*/
handleUpdateSearchInputText: (handler) => {
if (currentUpdateSearchInputTextListener) {
ipcRenderer.off(IpcChannels.UPDATE_SEARCH_INPUT_TEXT, currentUpdateSearchInputTextListener)
currentUpdateSearchInputTextListener = undefined
}
if (handler) {
currentUpdateSearchInputTextListener = (_, text) => {
handler(text)
}
ipcRenderer.on(IpcChannels.UPDATE_SEARCH_INPUT_TEXT, currentUpdateSearchInputTextListener)
ipcRenderer.send(IpcChannels.SEARCH_INPUT_HANDLING_READY)
}
},
/**
* @param {(event: number, data: any) => void} handler
*/
handleSyncSettings: (handler) => {
ipcRenderer.on(IpcChannels.SYNC_SETTINGS, (_, { event, data }) => {
handler(event, data)
})
},
/**
* @param {(event: number, data: any) => void} handler
*/
handleSyncHistory: (handler) => {
ipcRenderer.on(IpcChannels.SYNC_HISTORY, (_, { event, data }) => {
handler(event, data)
})
},
/**
* @param {(event: number, data: any) => void} handler
*/
handleSyncSearchHistory: (handler) => {
ipcRenderer.on(IpcChannels.SYNC_SEARCH_HISTORY, (_, { event, data }) => {
handler(event, data)
})
},
/**
* @param {(event: number, data: any) => void} handler
*/
handleSyncProfiles: (handler) => {
ipcRenderer.on(IpcChannels.SYNC_PROFILES, (_, { event, data }) => {
handler(event, data)
})
},
/**
* @param {(event: number, data: any) => void} handler
*/
handleSyncPlaylists: (handler) => {
ipcRenderer.on(IpcChannels.SYNC_PLAYLISTS, (_, { event, data }) => {
handler(event, data)
})
},
/**
* @param {(event: number, data: any) => void} handler
*/
handleSyncSubscriptionCache: (handler) => {
ipcRenderer.on(IpcChannels.SYNC_PLAYLISTS, (_, { event, data }) => {
handler(event, data)
})
}
}

4
src/preload/main.js Normal file
View File

@@ -0,0 +1,4 @@
import { contextBridge } from 'electron/renderer'
import api from './interface.js'
contextBridge.exposeInMainWorld('ftElectron', api)

7
src/preload/preload-interface.d.ts vendored Normal file
View File

@@ -0,0 +1,7 @@
import api from './interface.js'
declare global {
interface Window {
ftElectron: typeof api
}
}

View File

@@ -13,13 +13,10 @@ import FtCreatePlaylistPrompt from './components/FtCreatePlaylistPrompt/FtCreate
import FtKeyboardShortcutPrompt from './components/FtKeyboardShortcutPrompt/FtKeyboardShortcutPrompt.vue'
import FtSearchFilters from './components/FtSearchFilters/FtSearchFilters.vue'
import { marked } from 'marked'
import { IpcChannels } from '../constants'
import packageDetails from '../../package.json'
import { openExternalLink, openInternalPath, showToast } from './helpers/utils'
import { translateWindowTitle } from './helpers/strings'
let ipcRenderer = null
export default defineComponent({
name: 'App',
components: {
@@ -197,12 +194,10 @@ export default defineComponent({
this.grabSearchHistoryEntries()
if (process.env.IS_ELECTRON) {
ipcRenderer = require('electron').ipcRenderer
this.setupListenersToSyncWindows()
this.activateKeyboardShortcuts()
this.openAllLinksExternally()
this.enableOpenUrl()
this.watchSystemTheme()
await this.checkExternalPlayer()
}
@@ -499,24 +494,12 @@ export default defineComponent({
})
},
/**
* Linux fix for dynamically updating theme preference, this works on
* all systems running the electron app.
*/
watchSystemTheme: function () {
ipcRenderer.on(IpcChannels.NATIVE_THEME_UPDATE, (event, shouldUseDarkColors) => {
document.body.dataset.systemTheme = shouldUseDarkColors ? 'dark' : 'light'
})
},
enableOpenUrl: function () {
ipcRenderer.on(IpcChannels.OPEN_URL, (event, url, { isLaunchLink = false } = { }) => {
window.ftElectron.handleOpenUrl((url, isLaunchLink) => {
if (url) {
this.handleYoutubeLink(url, { doCreateNewWindow: this.openDeepLinksInNewWindow && !isLaunchLink })
}
})
ipcRenderer.send(IpcChannels.APP_READY)
},
handleExternalLinkOpeningPromptAnswer: function (option) {

View File

@@ -60,7 +60,7 @@ import FtToggleSwitch from '../ft-toggle-switch/ft-toggle-switch.vue'
import store from '../../store/index'
import { DefaultFolderKind, IpcChannels } from '../../../constants'
import { DefaultFolderKind } from '../../../constants'
const DOWNLOAD_BEHAVIOR_VALUES = [
'download',
@@ -99,8 +99,7 @@ const downloadFolderPath = computed(() => store.getters.getDownloadFolderPath)
function chooseDownloadFolder() {
if (process.env.IS_ELECTRON) {
const { ipcRenderer } = require('electron')
ipcRenderer.send(IpcChannels.CHOOSE_DEFAULT_FOLDER, DefaultFolderKind.DOWNLOADS)
window.ftElectron.chooseDefaultFolder(DefaultFolderKind.DOWNLOADS)
}
}
</script>

View File

@@ -34,16 +34,13 @@ import FtFlexBox from '../ft-flex-box/ft-flex-box.vue'
import FtToggleSwitch from '../ft-toggle-switch/ft-toggle-switch.vue'
import FtPrompt from '../FtPrompt/FtPrompt.vue'
import { IpcChannels } from '../../../constants'
const replaceHttpCacheLoading = ref(true)
const replaceHttpCache = ref(false)
const showRestartPrompt = ref(false)
onMounted(async () => {
if (process.env.IS_ELECTRON) {
const { ipcRenderer } = require('electron')
replaceHttpCache.value = await ipcRenderer.invoke(IpcChannels.GET_REPLACE_HTTP_CACHE)
replaceHttpCache.value = await window.ftElectron.getReplaceHttpCache()
}
replaceHttpCacheLoading.value = false
@@ -69,8 +66,7 @@ function handleReplaceHttpCache(value) {
}
if (process.env.IS_ELECTRON) {
const { ipcRenderer } = require('electron')
ipcRenderer.send(IpcChannels.TOGGLE_REPLACE_HTTP_CACHE)
window.ftElectron.toggleReplaceHttpCache()
}
}
</script>

View File

@@ -106,7 +106,6 @@ import FtFlexBox from '../ft-flex-box/ft-flex-box.vue'
import store from '../../store/index'
import { IpcChannels } from '../../../constants'
import { debounce, showToast } from '../../helpers/utils'
const { locale, t } = useI18n()
@@ -233,8 +232,7 @@ function handleUpdateProxyPort(value) {
function enableProxy() {
if (process.env.IS_ELECTRON) {
const { ipcRenderer } = require('electron')
ipcRenderer.send(IpcChannels.ENABLE_PROXY, proxyUrl.value)
window.ftElectron.enableProxy(proxyUrl.value)
}
}
@@ -242,8 +240,7 @@ const debouncedEnableProxy = debounce(enableProxy, 200)
function disableProxy() {
if (process.env.IS_ELECTRON) {
const { ipcRenderer } = require('electron')
ipcRenderer.send(IpcChannels.DISABLE_PROXY)
window.ftElectron.disableProxy()
}
dataAvailable.value = false

View File

@@ -107,7 +107,6 @@ import FtPrompt from './FtPrompt/FtPrompt.vue'
import store from '../store/index'
import { colors } from '../helpers/colors'
import { IpcChannels } from '../../constants'
import { useColorTranslations } from '../composables/colors'
const { t } = useI18n()
@@ -317,8 +316,7 @@ function handleSmoothScrolling(value) {
store.dispatch('updateDisableSmoothScrolling',
disableSmoothScrollingToggleValue.value
).then(() => {
const { ipcRenderer } = require('electron')
ipcRenderer.send(IpcChannels.RELAUNCH_REQUEST)
window.ftElectron.relaunch()
})
}
}

View File

@@ -129,7 +129,7 @@ import FtIconButton from '../ft-icon-button/ft-icon-button.vue'
import store from '../../store/index'
import { IpcChannels, KeyboardShortcuts, MOBILE_WIDTH_THRESHOLD, SEARCH_RESULTS_DISPLAY_LIMIT } from '../../../constants'
import { KeyboardShortcuts, MOBILE_WIDTH_THRESHOLD, SEARCH_RESULTS_DISPLAY_LIMIT } from '../../../constants'
import { debounce, localizeAndAddKeyboardShortcutToActionTitle, openInternalPath } from '../../helpers/utils'
import { translateWindowTitle } from '../../helpers/strings'
import { clearLocalSearchSuggestionsSession, getLocalSearchSuggestions } from '../../helpers/api/local'
@@ -327,9 +327,7 @@ let pendingNavigationHistoryLabel = null
async function setNavigationHistoryDropdownOptions() {
if (process.env.IS_ELECTRON) {
isLoadingNavigationHistory = true
const { ipcRenderer } = require('electron')
const dropdownOptions = await ipcRenderer.invoke(IpcChannels.GET_NAVIGATION_HISTORY)
const dropdownOptions = await window.ftElectron.getNavigationHistory()
const activeEntry = dropdownOptions.find(option => option.active)
@@ -618,16 +616,6 @@ function handleWindowResize() {
}
}
/**
* @param {import('electron').IpcRendererEvent} event
* @param {string} searchQueryText
*/
function handleUpdateSearchInputText(event, searchQueryText) {
if (searchQueryText) {
updateSearchInputText(searchQueryText)
}
}
onMounted(() => {
previousWindowWidth = window.innerWidth
if (window.innerWidth <= MOBILE_WIDTH_THRESHOLD) {
@@ -646,9 +634,11 @@ onMounted(() => {
if (process.env.IS_ELECTRON) {
window.addEventListener('keydown', handleKeyboardShortcuts)
const { ipcRenderer } = require('electron')
ipcRenderer.on(IpcChannels.UPDATE_SEARCH_INPUT_TEXT, handleUpdateSearchInputText)
ipcRenderer.send(IpcChannels.SEARCH_INPUT_HANDLING_READY)
window.ftElectron.handleUpdateSearchInputText((searchQueryText) => {
if (searchQueryText) {
updateSearchInputText(searchQueryText)
}
})
}
})
@@ -658,8 +648,7 @@ onBeforeUnmount(() => {
if (process.env.IS_ELECTRON) {
window.removeEventListener('keydown', handleKeyboardShortcuts)
const { ipcRenderer } = require('electron')
ipcRenderer.off(IpcChannels.UPDATE_SEARCH_INPUT_TEXT, handleUpdateSearchInputText)
window.ftElectron.handleUpdateSearchInputText(null)
}
})
</script>

View File

@@ -3,7 +3,7 @@ import shaka from 'shaka-player'
import { useI18n } from '../../composables/use-i18n-polyfill'
import store from '../../store/index'
import { DefaultFolderKind, IpcChannels, KeyboardShortcuts } from '../../../constants'
import { DefaultFolderKind, KeyboardShortcuts } from '../../../constants'
import { AudioTrackSelection } from './player-components/AudioTrackSelection'
import { FullWindowButton } from './player-components/FullWindowButton'
import { LegacyQualitySelection } from './player-components/LegacyQualitySelection'
@@ -1119,15 +1119,13 @@ export default defineComponent({
function startPowerSaveBlocker() {
if (process.env.IS_ELECTRON) {
const { ipcRenderer } = require('electron')
ipcRenderer.send(IpcChannels.START_POWER_SAVE_BLOCKER)
window.ftElectron.startPowerSaveBlocker()
}
}
function stopPowerSaveBlocker() {
if (process.env.IS_ELECTRON) {
const { ipcRenderer } = require('electron')
ipcRenderer.send(IpcChannels.STOP_POWER_SAVE_BLOCKER)
window.ftElectron.stopPowerSaveBlocker()
}
}
@@ -1165,8 +1163,7 @@ export default defineComponent({
// PiP can only be activated once the video's readState and video track are populated
if (startInPip && props.format !== 'audio' && ui.getControls().isPiPAllowed() && process.env.IS_ELECTRON) {
startInPip = false
const { ipcRenderer } = require('electron')
ipcRenderer.send(IpcChannels.REQUEST_PIP)
window.ftElectron.requestPiP()
}
}
@@ -1656,14 +1653,7 @@ export default defineComponent({
} else {
const arrayBuffer = await blob.arrayBuffer()
const { ipcRenderer } = require('electron')
await ipcRenderer.invoke(
IpcChannels.WRITE_TO_DEFAULT_FOLDER,
DefaultFolderKind.SCREENSHOTS,
filenameWithExtension,
arrayBuffer
)
await window.ftElectron.writeToDefaultFolder(DefaultFolderKind.SCREENSHOTS, filenameWithExtension, arrayBuffer)
showToast(t('Screenshot Success'))
}
@@ -2673,8 +2663,7 @@ export default defineComponent({
if (startInFullscreen && process.env.IS_ELECTRON) {
startInFullscreen = false
const { ipcRenderer } = require('electron')
ipcRenderer.send(IpcChannels.REQUEST_FULLSCREEN)
window.ftElectron.requestFullscreen()
}
}

View File

@@ -8,7 +8,7 @@ import FtFlexBox from '../ft-flex-box/ft-flex-box.vue'
import FtButton from '../FtButton/FtButton.vue'
import FtInput from '../ft-input/ft-input.vue'
import FtTooltip from '../FtTooltip/FtTooltip.vue'
import { DefaultFolderKind, IpcChannels } from '../../../constants'
import { DefaultFolderKind } from '../../../constants'
export default defineComponent({
name: 'PlayerSettings',
@@ -277,8 +277,7 @@ export default defineComponent({
getScreenshotEmptyFolderPlaceholder: async function() {
if (process.env.IS_ELECTRON) {
const { ipcRenderer } = require('electron')
return await ipcRenderer.invoke(IpcChannels.GET_SCREENSHOT_FALLBACK_FOLDER)
return await window.ftElectron.getScreenshotFallbackFolder()
} else {
return ''
}
@@ -296,11 +295,10 @@ export default defineComponent({
}
},
chooseScreenshotFolder: async function() {
chooseScreenshotFolder: function() {
// only use with electron
if (process.env.IS_ELECTRON) {
const { ipcRenderer } = require('electron')
ipcRenderer.send(IpcChannels.CHOOSE_DEFAULT_FOLDER, DefaultFolderKind.SCREENSHOTS)
window.ftElectron.chooseDefaultFolder(DefaultFolderKind.SCREENSHOTS)
}
},

View File

@@ -1,18 +1,10 @@
import { IpcChannels } from '../../../constants'
export class PlayerCache {
async get(key) {
if (process.env.IS_ELECTRON) {
const { ipcRenderer } = require('electron')
return await ipcRenderer.invoke(IpcChannels.PLAYER_CACHE_GET, key)
}
return await window.ftElectron.playerCacheGet(key)
}
async set(key, value) {
if (process.env.IS_ELECTRON) {
const { ipcRenderer } = require('electron')
await ipcRenderer.invoke(IpcChannels.PLAYER_CACHE_SET, key, value)
}
await window.ftElectron.playerCacheSet(key, value)
}
async remove(_key) {

View File

@@ -1,6 +1,6 @@
import { ClientType, Innertube, Misc, Parser, UniversalCache, Utils, YT, YTNodes } from 'youtubei.js'
import Autolinker from 'autolinker'
import { IpcChannels, SEARCH_CHAR_LIMIT } from '../../../constants'
import { SEARCH_CHAR_LIMIT } from '../../../constants'
import { PlayerCache } from './PlayerCache'
import {
@@ -206,11 +206,8 @@ export async function getLocalVideoInfo(id) {
let sessionPoToken
if (process.env.IS_ELECTRON) {
const { ipcRenderer } = require('electron')
try {
({ contentPoToken, sessionPoToken } = await ipcRenderer.invoke(
IpcChannels.GENERATE_PO_TOKENS,
({ contentPoToken, sessionPoToken } = await window.ftElectron.generatePoTokens(
id,
webInnertube.session.context.client.visitorData,
JSON.stringify(webInnertube.session.context)

View File

@@ -1,4 +1,3 @@
import { IpcChannels } from '../../constants'
import i18n from '../i18n/index'
import router from '../router/index'
import { nextTick } from 'vue'
@@ -226,9 +225,7 @@ export async function openExternalLink(url) {
*/
export function openInternalPath({ path, query = undefined, doCreateNewWindow, searchQueryText = null }) {
if (process.env.IS_ELECTRON && doCreateNewWindow) {
const { ipcRenderer } = require('electron')
ipcRenderer.send(IpcChannels.CREATE_NEW_WINDOW, path, query, searchQueryText)
window.ftElectron.openInNewWindow(path, query, searchQueryText)
} else {
router.push({
path,
@@ -532,8 +529,7 @@ export function replaceFilenameForbiddenChars(filenameOriginal) {
export async function getSystemLocale() {
let locale
if (process.env.IS_ELECTRON) {
const { ipcRenderer } = require('electron')
locale = await ipcRenderer.invoke(IpcChannels.GET_SYSTEM_LOCALE)
locale = await window.ftElectron.getSystemLocale()
} else {
if (navigator && navigator.language) {
locale = navigator.language

View File

@@ -4,7 +4,6 @@ import i18n from './i18n/index'
import router from './router/index'
import store from './store/index'
import App from './App.vue'
import { IpcChannels } from '../constants'
import { library } from '@fortawesome/fontawesome-svg-core'
import { register as registerSwiper } from 'swiper/element'
@@ -278,12 +277,7 @@ Vue.use(PortalVue)
// to avoid accessing electron api from web app build
if (process.env.IS_ELECTRON) {
const { ipcRenderer } = require('electron')
// handle menu event updates from main script
ipcRenderer.on(IpcChannels.CHANGE_VIEW, (event, data) => {
if (data.route) {
router.push(data.route)
}
window.ftElectron.handleChangeView((route) => {
router.push(route)
})
}

View File

@@ -1,4 +1,3 @@
import { IpcChannels } from '../../../constants'
import { base64EncodeUtf8, createWebURL, fetchWithTimeout, randomArrayItem } from '../../helpers/utils'
const state = {
@@ -106,12 +105,10 @@ const mutations = {
state.currentInvidiousInstanceUrl = instanceUrl
if (process.env.IS_ELECTRON) {
const { ipcRenderer } = require('electron')
if (authorization) {
ipcRenderer.send(IpcChannels.SET_INVIDIOUS_AUTHORIZATION, authorization, instanceUrl)
window.ftElectron.setInvidiousAuthorization(authorization, instanceUrl)
} else {
ipcRenderer.send(IpcChannels.SET_INVIDIOUS_AUTHORIZATION, null)
window.ftElectron.clearInvidiousAuthorization()
}
}
},

View File

@@ -1,6 +1,6 @@
import i18n, { loadLocale } from '../../i18n/index'
import allLocales from '../../../../static/locales/activeLocales.json'
import { MAIN_PROFILE_ID, IpcChannels, SyncEvents } from '../../../constants'
import { MAIN_PROFILE_ID, SyncEvents } from '../../../constants'
import { DBSettingHandlers } from '../../../datastores/handlers/index'
import { getSystemLocale, showToast } from '../../helpers/utils'
@@ -398,8 +398,7 @@ const sideEffectHandlers = {
uiScale: (_, value) => {
if (process.env.IS_ELECTRON) {
const { webFrame } = require('electron')
webFrame.setZoomFactor(value / 100)
window.ftElectron.setZoomFactor(value / 100)
}
},
@@ -461,9 +460,7 @@ const customActions = {
// Should be a root action, but we'll tolerate
setupListenersToSyncWindows: ({ commit, dispatch }) => {
if (process.env.IS_ELECTRON) {
const { ipcRenderer } = require('electron')
ipcRenderer.on(IpcChannels.SYNC_SETTINGS, (_, { event, data }) => {
window.ftElectron.handleSyncSettings((event, data) => {
switch (event) {
case SyncEvents.GENERAL.UPSERT:
if (settingsWithSideEffects.includes(data._id)) {
@@ -478,7 +475,7 @@ const customActions = {
}
})
ipcRenderer.on(IpcChannels.SYNC_HISTORY, (_, { event, data }) => {
window.ftElectron.handleSyncHistory((event, data) => {
switch (event) {
case SyncEvents.GENERAL.UPSERT:
commit('upsertToHistoryCache', data)
@@ -518,7 +515,7 @@ const customActions = {
}
})
ipcRenderer.on(IpcChannels.SYNC_SEARCH_HISTORY, (_, { event, data }) => {
window.ftElectron.handleSyncSearchHistory((event, data) => {
switch (event) {
case SyncEvents.GENERAL.UPSERT:
commit('upsertSearchHistoryEntryToList', data)
@@ -537,7 +534,7 @@ const customActions = {
}
})
ipcRenderer.on(IpcChannels.SYNC_PROFILES, (_, { event, data }) => {
window.ftElectron.handleSyncProfiles((event, data) => {
switch (event) {
case SyncEvents.GENERAL.CREATE:
commit('addProfileToList', data)
@@ -564,7 +561,7 @@ const customActions = {
}
})
ipcRenderer.on(IpcChannels.SYNC_PLAYLISTS, (_, { event, data }) => {
window.ftElectron.handleSyncPlaylists((event, data) => {
switch (event) {
case SyncEvents.GENERAL.CREATE:
commit('addPlaylists', data)
@@ -599,7 +596,7 @@ const customActions = {
}
})
ipcRenderer.on(IpcChannels.SYNC_SUBSCRIPTION_CACHE, (_, { event, data }) => {
window.ftElectron.handleSyncSubscriptionCache((event, data) => {
switch (event) {
case SyncEvents.SUBSCRIPTION_CACHE.UPDATE_VIDEOS_BY_CHANNEL:
commit('updateVideoCacheByChannel', data)

View File

@@ -1,7 +1,7 @@
import i18n from '../../i18n/index'
import { set as vueSet } from 'vue'
import { DefaultFolderKind, IpcChannels } from '../../../constants'
import { DefaultFolderKind } from '../../../constants'
import {
CHANNEL_HANDLE_REGEX,
createWebURL,
@@ -260,14 +260,7 @@ const actions = {
const arrayBuffer = await response.arrayBuffer()
if (process.env.IS_ELECTRON) {
const { ipcRenderer } = require('electron')
await ipcRenderer.invoke(
IpcChannels.WRITE_TO_DEFAULT_FOLDER,
DefaultFolderKind.DOWNLOADS,
fileName,
arrayBuffer
)
await window.ftElectron.writeToDefaultFolder(DefaultFolderKind.DOWNLOADS, fileName, arrayBuffer)
}
showToast(i18n.t('Downloading has completed', { videoTitle: title }))
@@ -800,8 +793,7 @@ const actions = {
showToast(i18n.t('Video.External Player.OpeningTemplate', { videoOrPlaylist, externalPlayer }))
if (process.env.IS_ELECTRON) {
const { ipcRenderer } = require('electron')
ipcRenderer.send(IpcChannels.OPEN_IN_EXTERNAL_PLAYER, { executable, args })
window.ftElectron.openInExternalPlayer(executable, args)
}
},
}