diff --git a/_scripts/webpack.renderer.config.js b/_scripts/webpack.renderer.config.js index 898ba0ffbc450..ecf1249adc2df 100644 --- a/_scripts/webpack.renderer.config.js +++ b/_scripts/webpack.renderer.config.js @@ -8,6 +8,12 @@ const ProcessLocalesPlugin = require('./ProcessLocalesPlugin') const isDevMode = process.env.NODE_ENV === 'development' +const processLocalesPlugin = new ProcessLocalesPlugin({ + compress: !isDevMode, + inputDir: path.join(__dirname, '../static/locales'), + outputDir: 'static/locales', +}) + const config = { name: 'renderer', mode: process.env.NODE_ENV, @@ -107,8 +113,10 @@ const config = { __filename: isDevMode }, plugins: [ + processLocalesPlugin, new webpack.DefinePlugin({ - 'process.env.IS_ELECTRON': true + 'process.env.IS_ELECTRON': true, + 'process.env.LOCALE_NAMES': JSON.stringify(processLocalesPlugin.localeNames) }), new HtmlWebpackPlugin({ excludeChunks: ['processTaskWorker'], @@ -142,22 +150,4 @@ const config = { target: 'electron-renderer', } -/** - * Adjust rendererConfig for production settings - */ -if (!isDevMode) { - const processLocalesPlugin = new ProcessLocalesPlugin({ - compress: true, - inputDir: path.join(__dirname, '../static/locales'), - outputDir: 'static/locales', - }) - - config.plugins.push( - processLocalesPlugin, - new webpack.DefinePlugin({ - 'process.env.LOCALE_NAMES': JSON.stringify(processLocalesPlugin.localeNames) - }) - ) -} - module.exports = config diff --git a/_scripts/webpack.web.config.js b/_scripts/webpack.web.config.js index 015eb8019a057..a18115e95f873 100644 --- a/_scripts/webpack.web.config.js +++ b/_scripts/webpack.web.config.js @@ -137,7 +137,7 @@ const config = { fallback: { buffer: require.resolve('buffer/'), dns: require.resolve('browserify/lib/_empty.js'), - fs: require.resolve('browserify/lib/_empty.js'), + 'fs/promises': require.resolve('browserify/lib/_empty.js'), http: require.resolve('stream-http'), https: require.resolve('https-browserify'), net: require.resolve('browserify/lib/_empty.js'), diff --git a/src/renderer/components/experimental-settings/experimental-settings.js b/src/renderer/components/experimental-settings/experimental-settings.js index ec0116809fb97..effefa6a91350 100644 --- a/src/renderer/components/experimental-settings/experimental-settings.js +++ b/src/renderer/components/experimental-settings/experimental-settings.js @@ -1,10 +1,10 @@ -import { closeSync, existsSync, openSync, rmSync } from 'fs' +import fs from 'fs/promises' import Vue from 'vue' import FtSettingsSection from '../ft-settings-section/ft-settings-section.vue' import FtFlexBox from '../ft-flex-box/ft-flex-box.vue' import FtToggleSwitch from '../ft-toggle-switch/ft-toggle-switch.vue' import FtPrompt from '../ft-prompt/ft-prompt.vue' -import { getUserDataPath } from '../../helpers/utils' +import { getUserDataPath, pathExists } from '../../helpers/utils' export default Vue.extend({ name: 'ExperimentalSettings', @@ -26,28 +26,19 @@ export default Vue.extend({ getUserDataPath().then((userData) => { this.replaceHttpCachePath = `${userData}/experiment-replace-http-cache` - this.replaceHttpCache = existsSync(this.replaceHttpCachePath) - this.replaceHttpCacheLoading = false + pathExists(this.replaceHttpCachePath).then((exists) => { + this.replaceHttpCache = exists + this.replaceHttpCacheLoading = false + }) }) }, methods: { - updateReplaceHttpCache: function () { - this.replaceHttpCache = !this.replaceHttpCache - - if (this.replaceHttpCache) { - // create an empty file - closeSync(openSync(this.replaceHttpCachePath, 'w')) - } else { - rmSync(this.replaceHttpCachePath) - } - }, - handleRestartPrompt: function (value) { this.replaceHttpCache = value this.showRestartPrompt = true }, - handleReplaceHttpCache: function (value) { + handleReplaceHttpCache: async function (value) { this.showRestartPrompt = false if (value === null || value === 'no') { @@ -57,9 +48,10 @@ export default Vue.extend({ if (this.replaceHttpCache) { // create an empty file - closeSync(openSync(this.replaceHttpCachePath, 'w')) + const handle = await fs.open(this.replaceHttpCachePath, 'w') + await handle.close() } else { - rmSync(this.replaceHttpCachePath) + await fs.rm(this.replaceHttpCachePath) } const { ipcRenderer } = require('electron') diff --git a/src/renderer/components/ft-video-player/ft-video-player.js b/src/renderer/components/ft-video-player/ft-video-player.js index a120861c8e8fc..9f4d8db8bf760 100644 --- a/src/renderer/components/ft-video-player/ft-video-player.js +++ b/src/renderer/components/ft-video-player/ft-video-player.js @@ -3,7 +3,7 @@ import { mapActions } from 'vuex' import videojs from 'video.js' import qualitySelector from '@silvermine/videojs-quality-selector' -import fs from 'fs' +import fs from 'fs/promises' import path from 'path' import 'videojs-overlay/dist/videojs-overlay' import 'videojs-overlay/dist/videojs-overlay.css' @@ -15,7 +15,12 @@ import 'videojs-mobile-ui/dist/videojs-mobile-ui.css' import { IpcChannels } from '../../../constants' import { sponsorBlockSkipSegments } from '../../helpers/sponsorblock' import { calculateColorLuminance, colors } from '../../helpers/colors' -import { getPicturesPath, showSaveDialog, showToast } from '../../helpers/utils' +import { + getPicturesPath, + pathExists, + showSaveDialog, + showToast +} from '../../helpers/utils' export default Vue.extend({ name: 'FtVideoPlayer', @@ -769,22 +774,23 @@ export default Vue.extend({ } }, - determineMaxFramerate: function() { + determineMaxFramerate: async function() { if (this.dashSrc.length === 0) { this.maxFramerate = 60 return } - fs.readFile(this.dashSrc[0].url, (err, data) => { - if (err) { - this.maxFramerate = 60 - return - } + + try { + const data = await fs.readFile(this.dashSrc[0].url) + if (data.includes('frameRate="60"')) { this.maxFramerate = 60 } else { this.maxFramerate = 30 } - }) + } catch { + this.maxFramerate = 60 + } }, determineDefaultQualityLegacy: function () { @@ -1406,7 +1412,7 @@ export default Vue.extend({ this.player.pause() } - if (this.screenshotFolder === '' || !fs.existsSync(this.screenshotFolder)) { + if (this.screenshotFolder === '' || !(await pathExists(this.screenshotFolder))) { dirPath = await getPicturesPath() } else { dirPath = this.screenshotFolder @@ -1445,9 +1451,9 @@ export default Vue.extend({ dirPath = path.join(this.screenshotFolder, subDir) } - if (!fs.existsSync(dirPath)) { + if (!(await pathExists(dirPath))) { try { - fs.mkdirSync(dirPath, { recursive: true }) + fs.mkdir(dirPath, { recursive: true }) } catch (err) { console.error(err) showToast(this.$t('Screenshot Error', { error: err })) @@ -1462,14 +1468,14 @@ export default Vue.extend({ result.arrayBuffer().then(ab => { const arr = new Uint8Array(ab) - fs.writeFile(filePath, arr, (err) => { - if (err) { + fs.writeFile(filePath, arr) + .then(() => { + showToast(this.$t('Screenshot Success', { filePath })) + }) + .catch((err) => { console.error(err) showToast(this.$t('Screenshot Error', { error: err })) - } else { - showToast(this.$t('Screenshot Success', { filePath })) - } - }) + }) }) }, mimeType, imageQuality) canvas.remove() diff --git a/src/renderer/components/general-settings/general-settings.js b/src/renderer/components/general-settings/general-settings.js index aa2f05fc55442..4c19aba53f5d5 100644 --- a/src/renderer/components/general-settings/general-settings.js +++ b/src/renderer/components/general-settings/general-settings.js @@ -114,22 +114,10 @@ export default Vue.extend({ }, localeNames: function () { - if (process.env.NODE_ENV !== 'development' || !process.env.IS_ELECTRON) { - return [ - this.$t('Settings.General Settings.System Default'), - ...process.env.LOCALE_NAMES - ] - } - - const names = [ - this.$t('Settings.General Settings.System Default') + return [ + this.$t('Settings.General Settings.System Default'), + ...process.env.LOCALE_NAMES ] - - Object.entries(this.$i18n.messages).forEach(([locale, localeData]) => { - names.push(localeData['Locale Name'] ?? locale) - }) - - return names }, backendNames: function () { diff --git a/src/renderer/helpers/api/PlayerCache.js b/src/renderer/helpers/api/PlayerCache.js index 95e2a4f1ced82..5f3bc53d91072 100644 --- a/src/renderer/helpers/api/PlayerCache.js +++ b/src/renderer/helpers/api/PlayerCache.js @@ -1,5 +1,7 @@ -import { access, mkdir, readFile, unlink, writeFile } from 'fs/promises' -import { resolve } from 'path' +import fs from 'fs/promises' +import path from 'path' + +import { pathExists } from '../utils' // 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 @@ -9,10 +11,10 @@ export class PlayerCache { } async get(key) { - const filePath = resolve(this.cacheDirectory, key) + const filePath = path.resolve(this.cacheDirectory, key) try { - const contents = await readFile(filePath) + const contents = await fs.readFile(filePath) return contents.buffer } catch (e) { if (e?.code === 'ENOENT') { @@ -23,19 +25,19 @@ export class PlayerCache { } async set(key, value) { - await mkdir(this.cacheDirectory, { recursive: true }) + await fs.mkdir(this.cacheDirectory, { recursive: true }) - const filePath = resolve(this.cacheDirectory, key) - await writeFile(filePath, new Uint8Array(value)) + const filePath = path.resolve(this.cacheDirectory, key) + await fs.writeFile(filePath, new Uint8Array(value)) } async remove(key) { - const filePath = resolve(this.cacheDirectory, key) + const filePath = path.resolve(this.cacheDirectory, key) - // only try to delete the file if it exists, access() throws an exception if the file doesn't exist - try { - await access(filePath) - await unlink(filePath) - } catch { } + if (await pathExists(filePath)) { + try { + await fs.unlink(filePath) + } catch { } + } } } diff --git a/src/renderer/helpers/utils.js b/src/renderer/helpers/utils.js index 83334c1180995..7b1cc82123a9d 100644 --- a/src/renderer/helpers/utils.js +++ b/src/renderer/helpers/utils.js @@ -1,4 +1,4 @@ -import fs from 'fs' +import fs from 'fs/promises' import { IpcChannels } from '../../constants' import FtToastEvents from '../components/ft-toast/ft-toast-events' @@ -260,13 +260,11 @@ export function readFileFromDialog(response, index = 0) { return new Promise((resolve, reject) => { if (process.env.IS_ELECTRON) { // if this is Electron, use fs - fs.readFile(response.filePaths[index], (err, data) => { - if (err) { - reject(err) - return - } - resolve(new TextDecoder('utf-8').decode(data)) - }) + fs.readFile(response.filePaths[index]) + .then(data => { + resolve(new TextDecoder('utf-8').decode(data)) + }) + .catch(reject) } else { // if this is web, use FileReader try { @@ -315,16 +313,8 @@ export async function showSaveDialog (options) { */ export async function writeFileFromDialog (response, content) { if (process.env.IS_ELECTRON) { - return await new Promise((resolve, reject) => { - const { filePath } = response - fs.writeFile(filePath, content, (error) => { - if (error) { - reject(error) - } else { - resolve() - } - }) - }) + const { filePath } = response + return await fs.writeFile(filePath, content) } else { if ('showOpenFilePicker' in window) { const { handle } = response @@ -496,3 +486,16 @@ export function extractNumberFromString(str) { return NaN } } + +/** + * Async version of existsSync, as node doesn't have it + * @param {string} path + */ +export async function pathExists(path) { + try { + await fs.access(path) + return true + } catch { + return false + } +} diff --git a/src/renderer/i18n/index.js b/src/renderer/i18n/index.js index a6b25992d67a1..56c17705d0414 100644 --- a/src/renderer/i18n/index.js +++ b/src/renderer/i18n/index.js @@ -1,27 +1,9 @@ import Vue from 'vue' import VueI18n from 'vue-i18n' -import fs from 'fs' import { createWebURL } from '../helpers/utils' // List of locales approved for use import activeLocales from '../../../static/locales/activeLocales.json' -const messages = {} - -if (process.env.NODE_ENV === 'development' && process.env.IS_ELECTRON) { - const { load } = require('js-yaml') - - // Take active locales and load respective YAML file - activeLocales.forEach((locale) => { - try { - // File location when running in dev - const doc = load(fs.readFileSync(`static/locales/${locale}.yaml`)) - messages[locale] = doc - } catch (e) { - console.error(locale, e) - } - }) -} - class CustomVueI18n extends VueI18n { constructor(options) { super(options) @@ -29,8 +11,6 @@ class CustomVueI18n extends VueI18n { } async loadLocale(locale) { - // we don't lazy load locales in development in electron - if (process.env.NODE_ENV === 'development' && process.env.IS_ELECTRON) { return } // don't need to load it if it's already loaded if (this.availableLocales.includes(locale)) { return @@ -39,14 +19,18 @@ class CustomVueI18n extends VueI18n { console.error(`Unable to load unknown locale: "${locale}"`) } - if (process.env.IS_ELECTRON) { - const { brotliDecompressSync } = require('zlib') + if (process.env.IS_ELECTRON && process.env.NODE_ENV !== 'development') { + const { readFile } = require('fs/promises') + const { promisify } = require('util') + const { brotliDecompress } = require('zlib') + const brotliDecompressAsync = promisify(brotliDecompress) // locales are only compressed in our production Electron builds try { // decompress brotli compressed json file and then load it // eslint-disable-next-line node/no-path-concat - const compressed = fs.readFileSync(`${__dirname}/static/locales/${locale}.json.br`) - const data = JSON.parse(brotliDecompressSync(compressed).toString()) + const compressed = await readFile(`${__dirname}/static/locales/${locale}.json.br`) + const decompressed = await brotliDecompressAsync(compressed) + const data = JSON.parse(decompressed.toString()) this.setLocaleMessage(locale, data) } catch (err) { console.error(locale, err) @@ -65,12 +49,9 @@ Vue.use(CustomVueI18n) const i18n = new CustomVueI18n({ locale: 'en-US', - fallbackLocale: { default: 'en-US' }, - messages + fallbackLocale: { default: 'en-US' } }) -if (process.env.NODE_ENV !== 'development' || !process.env.IS_ELECTRON) { - i18n.loadLocale('en-US') -} +i18n.loadLocale('en-US') export default i18n diff --git a/src/renderer/store/modules/invidious.js b/src/renderer/store/modules/invidious.js index dea8942ac6172..5023fbe2eefa8 100644 --- a/src/renderer/store/modules/invidious.js +++ b/src/renderer/store/modules/invidious.js @@ -1,4 +1,5 @@ -import fs from 'fs' +import fs from 'fs/promises' +import { pathExists } from '../../helpers/utils' const state = { currentInvidiousInstance: '', @@ -42,9 +43,9 @@ const actions = { const fileName = 'invidious-instances.json' /* eslint-disable-next-line */ const fileLocation = process.env.NODE_ENV === 'development' ? './static/' : `${__dirname}/static/` - if (fs.existsSync(`${fileLocation}${fileName}`)) { + if (await pathExists(`${fileLocation}${fileName}`)) { console.warn('reading static file for invidious instances') - const fileData = fs.readFileSync(`${fileLocation}${fileName}`) + const fileData = await fs.readFile(`${fileLocation}${fileName}`) instances = JSON.parse(fileData).map((entry) => { return entry.url }) diff --git a/src/renderer/store/modules/settings.js b/src/renderer/store/modules/settings.js index fbcdbb97d343a..141ebd4f8c870 100644 --- a/src/renderer/store/modules/settings.js +++ b/src/renderer/store/modules/settings.js @@ -324,9 +324,7 @@ const stateWithSideEffects = { } } - if (process.env.NODE_ENV !== 'development' || !process.env.IS_ELECTRON) { - await i18n.loadLocale(targetLocale) - } + await i18n.loadLocale(targetLocale) i18n.locale = targetLocale await dispatch('getRegionData', { diff --git a/src/renderer/store/modules/utils.js b/src/renderer/store/modules/utils.js index 77a8385cfce4c..9fbc89b44d2ba 100644 --- a/src/renderer/store/modules/utils.js +++ b/src/renderer/store/modules/utils.js @@ -1,4 +1,4 @@ -import fs from 'fs' +import fs from 'fs/promises' import path from 'path' import i18n from '../../i18n/index' @@ -6,6 +6,7 @@ import { IpcChannels } from '../../../constants' import { createWebURL, openExternalLink, + pathExists, replaceFilenameForbiddenChars, searchFiltersMatch, showSaveDialog, @@ -136,9 +137,9 @@ const actions = { folderPath = response.filePath } else { - if (!fs.existsSync(folderPath)) { + if (!(await pathExists(folderPath))) { try { - fs.mkdirSync(folderPath, { recursive: true }) + await fs.mkdir(folderPath, { recursive: true }) } catch (err) { console.error(err) showToast(err) @@ -181,14 +182,14 @@ const actions = { const blobFile = new Blob(chunks) const buffer = await blobFile.arrayBuffer() - fs.writeFile(folderPath, new DataView(buffer), (err) => { - if (err) { - console.error(err) - showToast(errorMessage) - } else { - showToast(i18n.t('Downloading has completed', { videoTitle: title })) - } - }) + try { + await fs.writeFile(folderPath, new DataView(buffer)) + + showToast(i18n.t('Downloading has completed', { videoTitle: title })) + } catch (err) { + console.error(err) + showToast(errorMessage) + } }, parseScreenshotCustomFileName: function({ rootState }, payload) { @@ -241,12 +242,12 @@ const actions = { // Exclude __dirname from path if not in electron const fileLocation = `${process.env.IS_ELECTRON ? process.env.NODE_ENV === 'development' ? '.' : __dirname : ''}/static/geolocations/` if (process.env.IS_ELECTRON) { - localePathExists = fs.existsSync(`${fileLocation}${locale}`) + localePathExists = await pathExists(`${fileLocation}${locale}`) } else { localePathExists = process.env.GEOLOCATION_NAMES.includes(locale) } const pathName = `${fileLocation}${localePathExists ? locale : 'en-US'}/countries.json` - const fileData = process.env.IS_ELECTRON ? JSON.parse(fs.readFileSync(pathName)) : await (await fetch(createWebURL(pathName))).json() + const fileData = process.env.IS_ELECTRON ? JSON.parse(await fs.readFile(pathName)) : await (await fetch(createWebURL(pathName))).json() const countries = fileData.map((entry) => { return { id: entry.id, name: entry.name, code: entry.alpha2 } }) countries.sort((a, b) => { return a.id - b.id }) @@ -512,14 +513,14 @@ const actions = { showToast(i18n.t('Video.External Player.UnsupportedActionTemplate', { externalPlayer, action })) }, - getExternalPlayerCmdArgumentsData ({ commit }, payload) { + async getExternalPlayerCmdArgumentsData ({ commit }, payload) { const fileName = 'external-player-map.json' let fileData /* eslint-disable-next-line */ const fileLocation = process.env.NODE_ENV === 'development' ? './static/' : `${__dirname}/static/` - if (fs.existsSync(`${fileLocation}${fileName}`)) { - fileData = fs.readFileSync(`${fileLocation}${fileName}`) + if (await pathExists(`${fileLocation}${fileName}`)) { + fileData = await fs.readFile(`${fileLocation}${fileName}`) } else { fileData = '[{"name":"None","value":"","cmdArguments":null}]' } diff --git a/src/renderer/views/Watch/Watch.js b/src/renderer/views/Watch/Watch.js index 6ce4172dc6f86..f82c1a1fc24b6 100644 --- a/src/renderer/views/Watch/Watch.js +++ b/src/renderer/views/Watch/Watch.js @@ -1,6 +1,6 @@ import Vue from 'vue' import { mapActions } from 'vuex' -import fs from 'fs' +import fs from 'fs/promises' import ytDashGen from 'yt-dash-manifest-generator' import FtLoader from '../../components/ft-loader/ft-loader.vue' import FtVideoPlayer from '../../components/ft-video-player/ft-video-player.vue' @@ -18,6 +18,7 @@ import { copyToClipboard, formatDurationAsTimestamp, getUserDataPath, + pathExists, showToast } from '../../helpers/utils' @@ -1199,22 +1200,22 @@ export default Vue.extend({ const dashFileLocation = `static/dashFiles/${videoId}.xml` const vttFileLocation = `static/storyboards/${videoId}.vtt` // only delete the file it actually exists - if (fs.existsSync(dashFileLocation)) { - fs.rmSync(dashFileLocation) + if (await pathExists(dashFileLocation)) { + await fs.rm(dashFileLocation) } - if (fs.existsSync(vttFileLocation)) { - fs.rmSync(vttFileLocation) + if (await pathExists(vttFileLocation)) { + await fs.rm(vttFileLocation) } } else { const userData = await getUserDataPath() const dashFileLocation = `${userData}/dashFiles/${videoId}.xml` const vttFileLocation = `${userData}/storyboards/${videoId}.vtt` - if (fs.existsSync(dashFileLocation)) { - fs.rmSync(dashFileLocation) + if (await pathExists(dashFileLocation)) { + await fs.rm(dashFileLocation) } - if (fs.existsSync(vttFileLocation)) { - fs.rmSync(vttFileLocation) + if (await pathExists(vttFileLocation)) { + await fs.rm(vttFileLocation) } } } @@ -1247,23 +1248,23 @@ export default Vue.extend({ fileLocation = `static/dashFiles/${this.videoId}.xml` uriSchema = `dashFiles/${this.videoId}.xml` // if the location does not exist, writeFileSync will not create the directory, so we have to do that manually - if (!fs.existsSync('static/dashFiles/')) { - fs.mkdirSync('static/dashFiles/') + if (!(await pathExists('static/dashFiles/'))) { + await fs.mkdir('static/dashFiles/') } - if (fs.existsSync(fileLocation)) { - fs.rmSync(fileLocation) + if (await pathExists(fileLocation)) { + await fs.rm(fileLocation) } - fs.writeFileSync(fileLocation, xmlData) + await fs.writeFile(fileLocation, xmlData) } else { fileLocation = `${userData}/dashFiles/${this.videoId}.xml` uriSchema = `file://${fileLocation}` - if (!fs.existsSync(`${userData}/dashFiles/`)) { - fs.mkdirSync(`${userData}/dashFiles/`) + if (!(await pathExists(`${userData}/dashFiles/`))) { + await fs.mkdir(`${userData}/dashFiles/`) } - fs.writeFileSync(fileLocation, xmlData) + await fs.writeFile(fileLocation, xmlData) } return [ @@ -1293,7 +1294,7 @@ export default Vue.extend({ ] }, - createLocalStoryboardUrls: function (templateUrl) { + createLocalStoryboardUrls: async function (templateUrl) { const storyboards = templateUrl.split('|') const storyboardArray = [] // Second storyboard: L1/M0 - Third storyboard: L2/M0 - Fourth: L3/M0 @@ -1318,35 +1319,33 @@ export default Vue.extend({ }) // TODO: MAKE A VARIABLE WHICH CAN CHOOSE BETWEEN STORYBOARD ARRAY ELEMENTS const results = buildVTTFileLocally(storyboardArray[1]) - getUserDataPath().then((userData) => { - let fileLocation - let uriSchema - - // Dev mode doesn't have access to the file:// schema, so we access - // storyboards differently when run in dev - if (process.env.NODE_ENV === 'development') { - fileLocation = `static/storyboards/${this.videoId}.vtt` - uriSchema = `storyboards/${this.videoId}.vtt` - // if the location does not exist, writeFileSync will not create the directory, so we have to do that manually - if (!fs.existsSync('static/storyboards/')) { - fs.mkdirSync('static/storyboards/') - } + const userData = await getUserDataPath() + let fileLocation + let uriSchema - fs.rm(fileLocation, () => { - fs.writeFileSync(fileLocation, results) - }) - } else { - if (!fs.existsSync(`${userData}/storyboards/`)) { - fs.mkdirSync(`${userData}/storyboards/`) - } - fileLocation = `${userData}/storyboards/${this.videoId}.vtt` - uriSchema = `file://${fileLocation}` + // Dev mode doesn't have access to the file:// schema, so we access + // storyboards differently when run in dev + if (process.env.NODE_ENV === 'development') { + fileLocation = `static/storyboards/${this.videoId}.vtt` + uriSchema = `storyboards/${this.videoId}.vtt` + // if the location does not exist, writeFileSync will not create the directory, so we have to do that manually + if (!(await pathExists('static/storyboards/'))) { + fs.mkdir('static/storyboards/') + } - fs.writeFileSync(fileLocation, results) + await fs.rm(fileLocation) + await fs.writeFile(fileLocation, results) + } else { + if (!(await pathExists(`${userData}/storyboards/`))) { + await fs.mkdir(`${userData}/storyboards/`) } + fileLocation = `${userData}/storyboards/${this.videoId}.vtt` + uriSchema = `file://${fileLocation}` - this.videoStoryboardSrc = uriSchema - }) + await fs.writeFile(fileLocation, results) + } + + this.videoStoryboardSrc = uriSchema }, tryAddingTranslatedLocaleCaption: function (captionTracks, locale, baseUrl) {