"use strict" const unzipper = require('unzipper') const er = require('@electron/remote') /** * String.prototype.replaceAll() polyfill * https://gomakethings.com/how-to-replace-a-section-of-a-string-with-another-one-with-vanilla-js/ * @author Chris Ferdinandi * @license MIT */ if (!String.prototype.replaceAll) { String.prototype.replaceAll = function(str, newStr){ // If a regex pattern if (Object.prototype.toString.call(str).toLowerCase() === '[object regexp]') { return this.replace(str, newStr); } // If a string return this.replace(new RegExp(str, 'g'), newStr); }; } window.toggleSpinnerButtons = (spinnerVisible=undefined) => { if (spinnerVisible===undefined) { spinnerVisible = window.getComputedStyle(spinner).display == "block" } spinner.style.display = spinnerVisible ? "none" : "block" keepSampleButton.style.display = spinnerVisible ? "block" : "none" generateVoiceButton.style.display = spinnerVisible ? "block" : "none" samplePlayPause.style.display = spinnerVisible ? "flex" : "none" } window.infoModal = message => new Promise(resolve => resolve(createModal("info", message))) window.confirmModal = message => new Promise(resolve => resolve(createModal("confirm", message))) window.spinnerModal = message => new Promise(resolve => resolve(createModal("spinner", message))) window.errorModal = message => { window.errorModalHasOpened = true if (window.userSettings.useErrorSound) { const audioPreview = createElem("audio", {autoplay: false}, createElem("source", { src: window.userSettings.errorSoundFile })) audioPreview.setSinkId(window.userSettings.base_speaker) } window.electronBrowserWindow.setProgressBar(window.batch_state.taskBarPercent?window.batch_state.taskBarPercent:1, {mode: "error"}) return new Promise(topResolve => { createModal("error", message).then(() => { window.electronBrowserWindow.setProgressBar(window.batch_state.taskBarPercent?window.batch_state.taskBarPercent:-1, {mode: window.batch_state.state?"normal":"paused"}) topResolve() }) }) } window.createModal = (type, message) => { dialogueInput.blur() return new Promise(resolve => { modalContainer.innerHTML = "" const displayMessage = message.prompt ? message.prompt : message const modal = createElem("div.modal#activeModal", {style: {opacity: 0}}, createElem("span.createModalContents", displayMessage)) modal.dataset.type = type if (type=="confirm") { const yesButton = createElem("button", {style: {background: `#${themeColour}`}}) yesButton.innerHTML = window.i18n.YES const noButton = createElem("button", {style: {background: `#${themeColour}`}}) noButton.innerHTML = window.i18n.NO modal.appendChild(createElem("div", yesButton, noButton)) yesButton.addEventListener("click", () => { closeModal(modalContainer).then(() => { resolve(true) }) }) noButton.addEventListener("click", () => { closeModal(modalContainer).then(() => { resolve(false) }) }) } else if (type=="error" || type=="info") { const closeButton = createElem("button", {style: {background: `#${themeColour}`}}) closeButton.innerHTML = window.i18n.CLOSE modal.appendChild(createElem("div", closeButton)) closeButton.addEventListener("click", () => { closeModal(modalContainer).then(() => { resolve(false) }) }) } else if (type=="prompt") { const closeButton = createElem("button", {style: {background: `#${themeColour}`}}) closeButton.innerHTML = window.i18n.SUBMIT const inputElem = createElem("input", {type: "text", value: message.value}) modal.appendChild(createElem("div", inputElem)) modal.appendChild(createElem("div", closeButton)) closeButton.addEventListener("click", () => { closeModal(modalContainer).then(() => { resolve(inputElem.value) }) }) } else { modal.appendChild(createElem("div.spinner", {style: {borderLeftColor: document.querySelector("button").style.background}})) } modalContainer.appendChild(modal) modalContainer.style.opacity = 0 modalContainer.style.display = "flex" requestAnimationFrame(() => requestAnimationFrame(() => modalContainer.style.opacity = 1)) requestAnimationFrame(() => requestAnimationFrame(() => chromeBar.style.opacity = 1)) }) } window.closeModal = (container=undefined, notThisOne=undefined, skipIfErrorOpen=false) => { return new Promise(resolve => { if (window.errorModalHasOpened && skipIfErrorOpen) { return resolve() } window.errorModalHasOpened = false const allContainers = [batchGenerationContainer, gameSelectionContainer, updatesContainer, infoContainer, settingsContainer, patreonContainer, pluginsContainer, modalContainer, nexusContainer, embeddingsContainer, totdContainer, nexusReposContainer, EULAContainer, arpabetContainer, styleEmbeddingsContainer, workbenchContainer] const containers = container==undefined ? allContainers : (Array.isArray(container) ? container.filter(c=>c!=undefined) : [container]) notThisOne = Array.isArray(notThisOne) ? notThisOne : (notThisOne==undefined ? [] : [notThisOne]) containers.forEach(cont => { // Fade out the containers except the exceptions if (cont!=undefined && !notThisOne.includes(cont)) { cont.style.opacity = 0 } }) const someOpenContainer = allContainers.filter(c=>c!=undefined).find(cont => cont.style.opacity==1 && cont.style.display!="none" && cont!=modalContainer) if (!someOpenContainer || someOpenContainer==container) { chromeBar.style.opacity = 0.88 } setTimeout(() => { if (window.errorModalHasOpened && skipIfErrorOpen) { } else { containers.forEach(cont => { // Hide the containers except the exceptions if (cont!=undefined && !notThisOne.includes(cont)) { cont.style.display = "none" const someOpenContainer2 = allContainers.filter(c=>c!=undefined).find(cont => cont.style.opacity==1 && cont.style.display!="none" && cont!=modalContainer) if (!someOpenContainer2 || someOpenContainer2==container) { chromeBar.style.opacity = 0.88 } } }) window.errorModalHasOpened = false } // resolve() }, 200) try { activeModal.remove() } catch (e) {} resolve() }) } window.setTheme = (meta) => { const primaryColour = meta.themeColourPrimary const secondaryColour = meta.themeColourSecondary const gameName = meta.gameName if (window.userSettings.showDiscordStatus) { ipcRenderer.send('updateDiscord', {details: gameName}) } // Change batch panel colours, if it is initialized try { Array.from(batchRecordsHeader.children).forEach(item => item.style.backgroundColor = `#${primaryColour}`) } catch (e) {} try { Array.from(pluginsRecordsHeader.children).forEach(item => item.style.backgroundColor = `#${primaryColour}`) } catch (e) {} try { Array.from(styleembsRecordsHeader.children).forEach(item => item.style.backgroundColor = `#${primaryColour}`) } catch (e) {} try { Array.from(nexusRecordsHeader.children).forEach(item => item.style.backgroundColor = `#${primaryColour}`) } catch (e) {} try { Array.from(nexusSearchHeader.children).forEach(item => item.style.backgroundColor = `#${primaryColour}`) } catch (e) {} try { Array.from(nexusReposUsedHeader.children).forEach(item => item.style.backgroundColor = `#${primaryColour}`) } catch (e) {} try { Array.from(embeddingsRecordsHeader.children).forEach(item => item.style.backgroundColor = `#${primaryColour}`) } catch (e) {} try { Array.from(arpabetWordsListHeader.children).forEach(item => item.style.backgroundColor = `#${primaryColour}`) } catch (e) {} try { window.sequenceEditor.grabbers.forEach(grabber => grabber.fillStyle = `#${primaryColour}`) window.sequenceEditor.energyGrabbers.forEach(grabber => grabber.fillStyle = `#${primaryColour}`) } catch (e) {} const background = `linear-gradient(0deg, rgba(128,128,128,${window.userSettings.bg_gradient_opacity}) 0px, rgba(0,0,0,0)), url("assets/${meta.assetFile}")` Array.from(document.querySelectorAll("button:not(.fixedColour)")).forEach(e => e.style.background = `#${primaryColour}`) Array.from(document.querySelectorAll(".voiceType")).forEach(e => e.style.background = `#${primaryColour}`) Array.from(document.querySelectorAll(".spinner")).forEach(e => e.style.borderLeftColor = `#${primaryColour}`) Array.from(document.querySelectorAll(".checkbox")).forEach(e => e.style.accentColor = `#${primaryColour}`) Array.from(document.querySelectorAll(".input[type=range]")).forEach(e => e.style.accentColor = `#${primaryColour}`) if (secondaryColour) { Array.from(document.querySelectorAll("button:not(.fixedColour)")).forEach(e => e.style.color = `#${secondaryColour}`) Array.from(document.querySelectorAll(".voiceType")).forEach(e => e.style.color = `#${secondaryColour}`) Array.from(document.querySelectorAll("button")).forEach(e => e.style.textShadow = `none`) Array.from(document.querySelectorAll(".voiceType")).forEach(e => e.style.textShadow = `none`) } else { Array.from(document.querySelectorAll("button:not(.fixedColour)")).forEach(e => e.style.color = `white`) Array.from(document.querySelectorAll(".voiceType")).forEach(e => e.style.color = `white`) Array.from(document.querySelectorAll("button")).forEach(e => e.style.textShadow = `0 0 2px black`) Array.from(document.querySelectorAll(".voiceType")).forEach(e => e.style.textShadow = `0 0 2px black`) } if (window.wavesurfer) { window.wavesurfer.setWaveColor(`#${window.currentGame.themeColourPrimary}`) } // Fade the background image transition rightBG1.style.background = background rightBG2.style.opacity = 0 setTimeout(() => { rightBG2.style.background = rightBG1.style.background rightBG2.style.opacity = 1 }, 1000) cssHack.innerHTML = `::selection { background: #${primaryColour}; } ::-webkit-scrollbar-thumb { background-color: #${primaryColour} !important; } .slider::-webkit-slider-thumb { background-color: #${primaryColour} !important; } input[type=checkbox], input[type=range] {accent-color: #${primaryColour} !important;} a {color: #${primaryColour}}; #batchRecordsHeader > div {background-color: #${primaryColour} !important;} #pluginsRecordsHeader > div {background-color: #${primaryColour} !important;} .invertedButton { background: none !important; border: 2px solid #${primaryColour} !important; } ` if (secondaryColour) { cssHack.innerHTML += ` #batchRecordsHeader > div {color: #${secondaryColour} !important;text-shadow: none} #pluginsRecordsHeader > div {color: #${secondaryColour} !important;text-shadow: none} ` } else { cssHack.innerHTML += ` #batchRecordsHeader > div {color: white !important;text-shadow: 0 0 2px black;} #pluginsRecordsHeader > div {color: white !important;text-shadow: 0 0 2px black;} ` } } window.getAudioPlayTriangleSVG = () => { const div = createElem("div", ` `) return div } window.addEventListener("resize", e => { window.userSettings.customWindowSize = `${window.innerHeight},${window.innerWidth}` saveUserSettings() }) // Keyboard actions // ================ window.addEventListener("keyup", event => { if (!event.ctrlKey) { window.ctrlKeyIsPressed = false } if (!event.shiftKey) { window.shiftKeyIsPressed = false } }) dialogueInput.addEventListener("keydown", event => { if (event.target==dialogueInput || event.target==letterPitchNumb || event.target==letterLengthNumb) { // Enter: Generate sample if (event.key=="Enter") { generateVoiceButton.click() event.preventDefault() } return } }) window.addEventListener("click", event => { if (event.target.id!="dialogueInput") { window.hideAutocomplete() } }) window.addEventListener("keydown", event => { if (event.ctrlKey) { window.ctrlKeyIsPressed = true } if (event.shiftKey) { window.shiftKeyIsPressed = true } if (event.ctrlKey && event.key.toLowerCase()=="r") { location.reload() } if (event.ctrlKey && event.shiftKey && event.key.toLowerCase()=="i") { window.electron = require("electron") er.BrowserWindow.getFocusedWindow().webContents.openDevTools() return } if (event.ctrlKey) { window.ctrlKeyIsPressed = true } // Re-gen the line if the user presses CTRL-ENTER, evne outside the prompt box if (event.key=="Enter" && window.ctrlKeyIsPressed) { generateVoiceButton.click() } // The Enter key to submit text input prompts in modals if (event.key=="Enter" && modalContainer.style.display!="none" && event.target.tagName=="INPUT") { activeModal.querySelector("button").click() } const key = event.key.toLowerCase() // CTRL-S: Keep sample if (key=="s" && event.ctrlKey && !event.shiftKey) { keepSampleFunction(false) } // CTRL-SHIFT-S: Keep sample (but with rename prompt) if (key=="s" && event.ctrlKey && event.shiftKey) { keepSampleFunction(true) } // Disable keyboard controls while in a text input if (event.target.tagName=="INPUT" && event.target.tagName!=dialogueInput || event.target.tagName=="TEXTAREA") { return } if (event.target==dialogueInput || event.target==letterPitchNumb || event.target==letterLengthNumb) { // Enter: Generate sample if (event.key=="Enter") { generateVoiceButton.click() event.preventDefault() } return } // Escape: close modals if (key=="escape") { closeModal() } // Space: bring focus to the input textarea if (key==" ") { setTimeout(() => dialogueInput.focus(), 0) } // Create selection for all of the editor letters if (key=="a" && event.ctrlKey && !event.shiftKey) { window.sequenceEditor.letterFocus = [] window.sequenceEditor.sliderBoxes.forEach((_,i) => { window.sequenceEditor.letterFocus.push(i) window.sequenceEditor.setLetterFocus(i, true) }) event.preventDefault() return } // Y/N for prompt modals if (key=="y" || key=="n" || key==" ") { if (document.querySelector("#activeModal")) { const buttons = Array.from(document.querySelector("#activeModal").querySelectorAll("button")) const yesBtn = buttons.find(btn => btn.innerHTML.toLowerCase()=="yes") const noBtn = buttons.find(btn => btn.innerHTML.toLowerCase()=="no") if (key=="y") yesBtn.click() if (key==" ") yesBtn.click() if (key=="n") noBtn.click() } } // Left/Right arrows: Move between letter focused (clears multi-select, picks the min(0,first-1) if left, max($L, last+1) if right) // SHIFT-Left/Right: multi-letter create selection range if ((key=="arrowleft" || key=="arrowright") && !event.ctrlKey) { event.preventDefault() if (window.sequenceEditor.letterFocus.length==0) { window.sequenceEditor.setLetterFocus(0) } else if (window.sequenceEditor.letterFocus.length==1) { const curr_l = window.sequenceEditor.letterFocus[0] const newIndex = key=="arrowleft" ? Math.max(0, curr_l-1) : Math.min(curr_l+1, window.sequenceEditor.letters.length-1) window.sequenceEditor.setLetterFocus(newIndex, event.shiftKey) } else { if (key=="arrowleft") { window.sequenceEditor.setLetterFocus(Math.max(0, Math.min(...window.sequenceEditor.letterFocus)-1), event.shiftKey) } else { window.sequenceEditor.setLetterFocus(Math.min(Math.max(...window.sequenceEditor.letterFocus)+1, window.sequenceEditor.letters.length-1), event.shiftKey) } } } // Up/Down arrows: Move pitch up/down for the letter(s) selected if ((key=="arrowup" || key=="arrowdown") && !event.ctrlKey) { event.preventDefault() if (window.sequenceEditor.letterFocus.length) { window.sequenceEditor.letterFocus.forEach(li => { window.sequenceEditor.pitchNew[li] += (key=="arrowup" ? 0.1 : -0.1) window.sequenceEditor.grabbers[li].setValueFromValue(window.sequenceEditor.pitchNew[li]) window.sequenceEditor.hasChanged = true }) if (window.sequenceEditor.autoInferTimer != null) { clearTimeout(window.sequenceEditor.autoInferTimer) window.sequenceEditor.autoInferTimer = null } if (autoplay_ckbx.checked) { window.sequenceEditor.autoInferTimer = setTimeout(infer, 500) } if (window.sequenceEditor.letterFocus.length==1) { letterPitchNumb.value = window.sequenceEditor.pitchNew[window.sequenceEditor.letterFocus[0]] } } } // CTRL+Left/Right arrows: change the sequence-wide pacing if ((key=="arrowleft" || key=="arrowright") && event.ctrlKey) { if (event.altKey) { window.sequenceEditor.letterFocus.forEach(li => { window.sequenceEditor.dursNew[li] = window.sequenceEditor.dursNew[li] + (key=="arrowleft"? -0.1 : 0.1) window.sequenceEditor.hasChanged = true }) if (window.sequenceEditor.autoInferTimer != null) { clearTimeout(window.sequenceEditor.autoInferTimer) window.sequenceEditor.autoInferTimer = null } if (autoplay_ckbx.checked) { window.sequenceEditor.autoInferTimer = setTimeout(infer, 500) } window.sequenceEditor.sliderBoxes.forEach((box, i) => box.setValueFromValue(window.sequenceEditor.dursNew[i])) if (window.sequenceEditor.letterFocus.length==1) { letterLengthNumb.value = parseInt(window.sequenceEditor.dursNew[window.sequenceEditor.letterFocus[0]]*100)/100 } } else { pace_slid.value = parseFloat(pace_slid.value) + (key=="arrowleft"? -0.01 : 0.01) paceNumbInput.value = pace_slid.value const new_lengths = window.sequenceEditor.dursNew.map((v,l) => v * pace_slid.value) window.sequenceEditor.sliderBoxes.forEach((box, i) => box.setValueFromValue(window.sequenceEditor.dursNew[i])) window.sequenceEditor.pacing = parseFloat(pace_slid.value) window.sequenceEditor.init() } } // CTRL+Up/Down arrows: increase/decrease buttons if (key=="arrowup" && event.ctrlKey && !event.shiftKey) { increase_btn.click() } if (key=="arrowdown" && event.ctrlKey && !event.shiftKey) { decrease_btn.click() } // CTRL+SHIFT+Up/Down arrows: amplify/flatten buttons if (key=="arrowup" && event.ctrlKey && event.shiftKey) { amplify_btn.click() } if (key=="arrowdown" && event.ctrlKey && event.shiftKey) { flatten_btn.click() } }) window.setupModal = (openingButton, modalContainerElem, callback, exitCallback) => { if (openingButton) { openingButton.addEventListener("click", () => { closeModal(undefined, modalContainerElem).then(() => { modalContainerElem.style.opacity = 0 modalContainerElem.style.display = "flex" requestAnimationFrame(() => requestAnimationFrame(() => { modalContainerElem.style.opacity = 1 chromeBar.style.opacity = 1 requestAnimationFrame(() => { setTimeout(() => { if (callback) { callback() } }, 250) }) })) }) }) } modalContainerElem.addEventListener("click", event => { if (event.target==modalContainerElem) { if (exitCallback) { exitCallback() } window.closeModal(modalContainerElem) } }) } window.checkVersionRequirements = (requirements, appVersion, checkMax=false) => { if (!requirements) { return true } const appVersionRequirement = requirements.toString().split(".").map(v=>parseInt(v)) const appVersionInts = appVersion.replace("v", "").split(".").map(v=>parseInt(v)) let appVersionOk = true if (checkMax) { if (appVersionRequirement[0] >= appVersionInts[0] ) { if (appVersionRequirement.length>1 && parseInt(appVersionRequirement[0]) == appVersionInts[0]) { if (appVersionRequirement[1] >= appVersionInts[1] ) { if (appVersionRequirement.length>2 && parseInt(appVersionRequirement[1]) == appVersionInts[1]) { if (appVersionRequirement[2] >= appVersionInts[2] ) { } else { appVersionOk = false } } } else { appVersionOk = false } } } else { appVersionOk = false } } else { if (appVersionRequirement[0] <= appVersionInts[0] ) { if (appVersionRequirement.length>1 && parseInt(appVersionRequirement[0]) == appVersionInts[0]) { if (appVersionRequirement[1] <= appVersionInts[1] ) { if (appVersionRequirement.length>2 && parseInt(appVersionRequirement[1]) == appVersionInts[1]) { if (appVersionRequirement[2] <= appVersionInts[2] ) { } else { appVersionOk = false } } } else { appVersionOk = false } } } else { appVersionOk = false } } return appVersionOk } // https://stackoverflow.com/questions/18052762/remove-directory-which-is-not-empty const path = require('path') window.deleteFolderRecursive = function (directoryPath, keepRoot=false) { if (fs.existsSync(directoryPath)) { fs.readdirSync(directoryPath).forEach((file, index) => { const curPath = path.join(directoryPath, file); if (fs.lstatSync(curPath).isDirectory()) { // recurse window.deleteFolderRecursive(curPath); } else { // delete file fs.unlinkSync(curPath); } }); if (!keepRoot) { fs.rmdirSync(directoryPath); } } }; window.createFolderRecursive = (pathToMake) => { console.log("createFolderRecursive", pathToMake) pathToMake.split('/').reduce((directories, directory) => { directories += `${directory}/` if (!fs.existsSync(directories)) { fs.mkdirSync(directories) } return directories }, '') } window.uuidv4 = () => { return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) { var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8) return v.toString(16) }) } // https://stackoverflow.com/questions/2450954/how-to-randomize-shuffle-a-javascript-array window.shuffle = (array) => { var currentIndex = array.length, randomIndex; // While there remain elements to shuffle... while (0 !== currentIndex) { // Pick a remaining element... randomIndex = Math.floor(Math.random() * currentIndex); currentIndex--; // And swap it with the current element. [array[currentIndex], array[randomIndex]] = [ array[randomIndex], array[currentIndex]]; } return array; } // Just for easier packaging of the voice models for publishing - yes, lazy window.packageVoice_variantIndex = 0 window.packageVoice = (doVoicePreviewCreate, variants, {modelsPath, gameId}={}) => { const {voiceId, hifi} = variants[window.packageVoice_variantIndex] if (doVoicePreviewCreate) { const files = fs.readdirSync(`./output`).filter(fname => fname.includes("temp-") && fname.includes(".wav")) if (files.length) { const options = { hz: window.userSettings.audio.hz, padStart: window.userSettings.audio.padStart, padEnd: window.userSettings.audio.padEnd, bit_depth: window.userSettings.audio.bitdepth, amplitude: window.userSettings.audio.amplitude, pitchMult: window.userSettings.audio.pitchMult, tempo: window.userSettings.audio.tempo } doFetch(`http://localhost:8008/outputAudio`, { method: "Post", body: JSON.stringify({ input_path: `./output/${files[0]}`, isBatchMode: false, output_path: `${modelsPath}/${voiceId}_raw.wav`, options: JSON.stringify(options) }) }).then(r=>r.text()).then(() => { try { fs.unlinkSync(`${modelsPath}/${voiceId}.wav`) } catch (e) {} doFetch(`http://localhost:8008/normalizeAudio`, { method: "Post", body: JSON.stringify({ input_path: `${modelsPath}/${voiceId}_raw.wav`, output_path: `${modelsPath}/${voiceId}.wav` }) }).then(r=>r.text()).then((resp) => { console.log(resp) fs.unlinkSync(`${modelsPath}/${voiceId}_raw.wav`) }) }) } } else { fs.mkdirSync(`./build/${voiceId}`) fs.mkdirSync(`./build/${voiceId}/resources`) fs.mkdirSync(`./build/${voiceId}/resources/app`) fs.mkdirSync(`./build/${voiceId}/resources/app/models`) fs.mkdirSync(`./build/${voiceId}/resources/app/models/${gameId}`) fs.copyFileSync(`${modelsPath}/${voiceId}.json`, `./build/${voiceId}/resources/app/models/${gameId}/${voiceId}.json`) fs.copyFileSync(`${modelsPath}/${voiceId}.wav`, `./build/${voiceId}/resources/app/models/${gameId}/${voiceId}.wav`) fs.copyFileSync(`${modelsPath}/${voiceId}.pt`, `./build/${voiceId}/resources/app/models/${gameId}/${voiceId}.pt`) // if (hifi) { // fs.copyFileSync(`${modelsPath}/${voiceId}.hg.pt`, `./build/${voiceId}/resources/app/models/${gameId}/${voiceId}.hg.pt`) // } zipdir(`./build/${voiceId}`, {saveTo: `./build/${voiceId}.zip`}, (err, buffer) => deleteFolderRecursive(`./build/${voiceId}`)) } } const assetFiles = fs.readdirSync(`${window.path}/assets`) const jsonFiles = fs.readdirSync(`${window.path}/assets`).filter(fn => fn.endsWith(".json")) const missingAssetFiles = jsonFiles.filter(jsonFile => !(assetFiles.includes(jsonFile.replace(".json", ".png"))||assetFiles.includes(jsonFile.replace(".json", ".jpg"))) ) if (missingAssetFiles.length) { noAssetFilesFoundMessage.style.display = "block" assetDirLink.addEventListener("click", () => { // shell.showItemInFolder((require("path")).resolve(`${window.path}/assets/other.jpg`)) er.shell.showItemInFolder((require("path")).resolve(`${window.path}/assets/other.jpg`)) spawn(`explorer`, [(require("path")).resolve(`${window.path}/assets`)], {stdio: "ignore"}) }) } window.getSpeakerEmbeddingFromFilePath = (filePath) => { spinnerModal(`${window.i18n.GETTING_SPEAKER_EMBEDDING}`) return new Promise(resolve => { doFetch(`http://localhost:8008/getWavV3StyleEmb`, { method: "Post", body: JSON.stringify({wav_path: filePath}) }).then(r=>r.text()).then(v => { closeModal(undefined, [styleEmbeddingsContainer, workbenchContainer]) if (v=="ENOENT") { window.errorModal(`${window.i18n.SOMETHING_WENT_WRONG}

ENOENT`) } else { resolve(v) } }).catch(() => { window.errorModal(`${window.i18n.SOMETHING_WENT_WRONG}`) }) }) } window.unzipFileTo = (zipPath, outputFolder) => { return fs.createReadStream(zipPath).pipe(unzipper.Parse()).on("entry", entry => { const fileName = entry.path const dirOrFile = entry.type if (/\/$/.test(fileName)) { // It's a directory return } let fileContainerFolderPath = fileName.split("/").reverse() const justFileName = fileContainerFolderPath[0] entry.pipe(fs.createWriteStream(`${outputFolder}/${justFileName}`)) }) .promise() }