From 56d17de4a5edefbf27055cb69f1959eb8e615137 Mon Sep 17 00:00:00 2001 From: MikeHughes-BIN Date: Sat, 10 Jan 2026 15:03:35 +0100 Subject: [PATCH] Refactor speaker management and replace functionality; add IPC for saving speaker mappings and update HTML structure --- electron/.DS_Store | Bin 0 -> 6148 bytes electron/main/index.html | 2 +- electron/main/preload.js | 36 ++--- electron/main/script.js | 107 ++++++++------- main.js | 16 +++ .../modules/replace_speaker/replaceSpeaker.js | 128 ++++++------------ storage/speakerMapping/speakerMapping.json | 5 + 7 files changed, 137 insertions(+), 157 deletions(-) create mode 100644 electron/.DS_Store create mode 100644 storage/speakerMapping/speakerMapping.json diff --git a/electron/.DS_Store b/electron/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..00981308abf03d5aeef82e771979efe0aa5c67cf GIT binary patch literal 6148 zcmeHKO-~a+7=8z;?SjYxEkdHn#$HSyv0^X87}kPOVhn^5ECSZub}4JOGtKUn3WTI* zJ^2It0saFMFCM*k^=AADdiCZzALYZs)fk;OnR(uMzh>vz?92`TkV30^3%~?`6gG~D z0qo8Y?&qyX`jn*~k?Ct4dc1Db)qjRF4cCZGWh_&`s7ziu<|qd1rQ zLi+j>7cM4~MshH@Ror3iV(i4NsNjSfe6-3~;C6M#UGu$m*-Xzf8aqDqLXqHCJq+1= z;?pW?7g&o{BavH_4#P+pWpm=-;C3!QZOzQ)52vkz*_qt5m7kqCJTy|7?9DqX<$BPd z?3E;t;C>@yNMRRVqjIz!zp|_LBl5Yso^Ph9R+KUZ1}_a=9yUit$41g4nM`K<%EaXO z_|>vG<~o&nE#yx+&jJ<}f|j>Y3;k-b=~R4LE5+W+xajYDWpnhm&=s%x?AWxr*Tk6g zu|br35PBXJ^_I4`BQGwU(AUf6a8wCd5G=FEk9|hhHzVYEG4>jZberLfyU6iUle?ld zFN_d-a!Tt?7&^4(7526Q$Mu3GT3~U^8a$F$t1!A8dc0D}N1d-FJNHAfn6)2ul^aqq zAuAOCBi+>$lI+KdE%enozF@=1+RMt7f+@HMk6{h!un%wGExdyd@Cm-cclb$?WRhGb zQzTFBk~#8_ERv_BLUxExUda5u?n}O=>M0-*Dn~DQw;0lZ(Hh#i@u$P}=o|(VU8%uy z{MxJ;&WV>R zI@&E2XDjSTl<7d!<%6i1iMpW>F+0|`L^u#zqV6>Vnt{^{@PB_4lb`?h@f$wp{x5=b zry0-;{F@Ar#6oeQfNN7{YtJhAS!-c?j*SbC>qrzS*s0@KDEug%z$OHJ7FQ5$g&m1# RLD4?~f(G4b2L31mzW{M_$bA3+ literal 0 HcmV?d00001 diff --git a/electron/main/index.html b/electron/main/index.html index 6d045ab..0fa59e0 100644 --- a/electron/main/index.html +++ b/electron/main/index.html @@ -19,7 +19,7 @@ diff --git a/electron/main/preload.js b/electron/main/preload.js index 356ada0..90573fb 100644 --- a/electron/main/preload.js +++ b/electron/main/preload.js @@ -3,35 +3,39 @@ const { contextBridge, ipcRenderer, webUtils } = require('electron') try { contextBridge.exposeInMainWorld("explorer", { onFileDrop: (file) => webUtils.getPathForFile(file) - }) + }); + contextBridge.exposeInMainWorld("submit", { - submit: (meeting_specifications) => {ipcRenderer.send("file_submit", meeting_specifications)} - }) + submit: (meeting_specifications) => ipcRenderer.send("file_submit", meeting_specifications) + }); + + // ALLE electronAPI Funktionen in EINEM Objekt contextBridge.exposeInMainWorld("electronAPI", { - getFilePath: (file) => {return webUtils.getPathForFile(file)} - }) + getFilePath: (file) => webUtils.getPathForFile(file), + saveSpeakerMapping: (data) => ipcRenderer.send("save-speaker-mapping", data) + }); contextBridge.exposeInMainWorld("onStartup", { getModuleNames: () => ipcRenderer.invoke('get-module-names') - }) + }); contextBridge.exposeInMainWorld('electron', { progress: (callback) => ipcRenderer.on('progress', callback) - }) + }); contextBridge.exposeInMainWorld('audios', { speakerAudios: (callback) => ipcRenderer.on('speakerAudios', callback) - }) + }); + contextBridge.exposeInMainWorld("submitSpeaker", { - speaker_submit: (speaker_names) => {ipcRenderer.send("speaker_submit", speaker_names)} - }) + speaker_submit: (speaker_names) => ipcRenderer.send("speaker_submit", speaker_names) + }); contextBridge.exposeInMainWorld("download", { - file_download: () => {ipcRenderer.send("file_download")} - }) + file_download: () => ipcRenderer.send("file_download") + }); - - ipcRenderer.on("error", (event, err) => {alert(err)}) + ipcRenderer.on("error", (event, err) => { alert(err) }); } catch (error) { - console.log("Error in preload.js"); -} + console.log("Error in preload.js", error); +} \ No newline at end of file diff --git a/electron/main/script.js b/electron/main/script.js index d2ff938..016e2b6 100644 --- a/electron/main/script.js +++ b/electron/main/script.js @@ -5,8 +5,8 @@ function checkBoxes() { const checkboxes = document.querySelectorAll('input[name="docFormat"]'); let isChecked = false; var checkedCounter = 0; - checkboxes.forEach(function(checkbox){ - if(checkbox.checked){ + checkboxes.forEach(function (checkbox) { + if (checkbox.checked) { isChecked = true; checkedCounter++; } @@ -15,24 +15,24 @@ function checkBoxes() { if (isChecked) { //Code to submit the video var selectedCheckboxes = {}; - checkboxes.forEach(function(checkbox){ - if(checkbox.checked){ + checkboxes.forEach(function (checkbox) { + if (checkbox.checked) { selectedCheckboxes[checkbox.nextElementSibling.textContent] = ""; - } + } }); const testEndings = [".mp4", ".mov", ".avi", ".mkv"]; var pathTest = window.electronAPI.getFilePath(videoUpload.files[0]); var pathToLower = pathTest.toLowerCase(); - if(testEndings.some(e => pathToLower.endsWith(e))){ + if (testEndings.some(e => pathToLower.endsWith(e))) { //assembly of the json for the main var typeCheckbox; - if(document.getElementById("docFormat").checked) typeCheckbox = document.getElementById("docFormat").value; - if(document.getElementById("docFormatSummary1").checked) typeCheckbox = document.getElementById("docFormatSummary1").value; - if(document.getElementById("docFormatSummary2").checked) typeCheckbox = document.getElementById("docFormatSummary2").value; - if(document.getElementById("docFormatSummary3").checked) typeCheckbox = document.getElementById("docFormatSummary3").value; - if(document.getElementById("docFormatCustom").checked) typeCheckbox = document.getElementById("docFormatCustom").value; + if (document.getElementById("docFormat").checked) typeCheckbox = document.getElementById("docFormat").value; + if (document.getElementById("docFormatSummary1").checked) typeCheckbox = document.getElementById("docFormatSummary1").value; + if (document.getElementById("docFormatSummary2").checked) typeCheckbox = document.getElementById("docFormatSummary2").value; + if (document.getElementById("docFormatSummary3").checked) typeCheckbox = document.getElementById("docFormatSummary3").value; + if (document.getElementById("docFormatCustom").checked) typeCheckbox = document.getElementById("docFormatCustom").value; document.getElementById("testy").style.visibility = "visible" document.getElementById("box1").style.backgroundColor = "red"; @@ -45,20 +45,20 @@ function checkBoxes() { const aiType = document.getElementById("ai_type"); const sendingPackage = { "video": { - "module":"extraction-video-to-audio", + "module": "extraction-video-to-audio", "inputVideoPath": pathTest }, "transcription": { "module": transcriptionType.value }, "document": { - "module":aiType.value, + "module": aiType.value, "type": typeCheckbox, "outputType": outputType.value } }; window.submit.submit(sendingPackage) - }else{ + } else { alert('The given file is not compatible. These are the available types: [".mp4", ".mov", ".avi", ".mkv"].'); } @@ -71,7 +71,7 @@ function checkBoxes() { console.log("Error in script.js checkBoxes function"); console.log(error); } - + } //language changing feature @@ -95,7 +95,7 @@ function changeLanguage(language) { console.log("Error in script.js changeLanguage function"); console.log(error); } - + } //function to display the file path in the drop down box @@ -129,17 +129,17 @@ function updateProgressBar(bar, value) { console.log("Error in scripts.js updateProgressBar function"); console.log(error); } - + } //function to load ai options to the drop down list -function loadAiOptions(options){ +function loadAiOptions(options) { try { var menu = document.getElementById('ai_type'); var object_holdy; - var choice ; + var choice; object_holdy = options - for(i = 0; i < options.length; i++){ + for (i = 0; i < options.length; i++) { choice = document.createElement('option'); choice.textContent = object_holdy[i].displayname; choice.value = object_holdy[i].name; @@ -152,13 +152,13 @@ function loadAiOptions(options){ } //function to load transcription options to the drop down list -function loadTranscriptionOptions(options){ +function loadTranscriptionOptions(options) { try { var menu = document.getElementById('transkript_type'); var object_holdy; - var choice ; + var choice; object_holdy = options - for(i = 0; i < options.length; i++){ + for (i = 0; i < options.length; i++) { choice = document.createElement('option'); choice.textContent = object_holdy[i].displayname; choice.value = object_holdy[i].name; @@ -171,13 +171,13 @@ function loadTranscriptionOptions(options){ } //function to load data type options to the drop down list -function loadDataTypeOptions(options){ +function loadDataTypeOptions(options) { try { var menu = document.getElementById('output_type'); var object_holdy; - var choice ; + var choice; object_holdy = options - for(i = 0; i < options.length; i++){ + for (i = 0; i < options.length; i++) { choice = document.createElement('option'); choice.textContent = object_holdy[i].displayname; choice.value = object_holdy[i].name; @@ -190,13 +190,13 @@ function loadDataTypeOptions(options){ } //function to load language options to the drop down list -function loadLanguageOptions(){ +function loadLanguageOptions() { try { var menu = document.getElementById('language_option'); var object_holdy; - var choice ; + var choice; object_holdy = Object.keys(languageOptions); - for(i = 0; i < object_holdy.length; i++){ + for (i = 0; i < object_holdy.length; i++) { choice = document.createElement('option'); choice.textContent = object_holdy[i]; choice.value = object_holdy[i]; @@ -209,17 +209,17 @@ function loadLanguageOptions(){ } //function to load speaker options to the drop down list -function loadSpeakerOptions(options){ +function loadSpeakerOptions(options) { try { var menu = document.getElementById('cur_speaker'); - var l = document.getElementById('cur_speaker').options.length -1; - for(i = l; i >= 0; i--){ + var l = document.getElementById('cur_speaker').options.length - 1; + for (i = l; i >= 0; i--) { menu.remove(i); } var object_holdy; var choice; object_holdy = Object.keys(options); - for(i = 0; i < object_holdy.length; i++){ + for (i = 0; i < object_holdy.length; i++) { choice = document.createElement('option'); choice.textContent = options[object_holdy[i]].name; choice.value = object_holdy[i]; @@ -233,7 +233,7 @@ function loadSpeakerOptions(options){ } //function to load speaker audio file options to the drop down list -function loadSpeakerAudio(option){ +function loadSpeakerAudio(option) { try { var menu = document.getElementById('speakerAudioViewer'); var aud = document.createElement("source"); @@ -246,7 +246,7 @@ function loadSpeakerAudio(option){ } -function activateSubmitBtn(hasFile){ +function activateSubmitBtn(hasFile) { try { document.getElementById("submitButton").disabled = !hasFile; } catch (error) { @@ -254,7 +254,7 @@ function activateSubmitBtn(hasFile){ } } -function deaktivateProgressbar(){ +function deaktivateProgressbar() { try { document.getElementById("progressbar").style.visibility = "hidden"; } catch (error) { @@ -263,7 +263,7 @@ function deaktivateProgressbar(){ } //Video thumbnail generation -function generateThumbnail(path){ +function generateThumbnail(path) { const videoElement = document.getElementById("previewThumbnail"); while (videoElement.firstChild) videoElement.removeChild(videoElement.firstChild); videoElement.src = path; @@ -281,7 +281,7 @@ let currentStep = 1; const totalSteps = steps.length; function showStep(stepNumber) { - if (stepNumber < 1 || stepNumber > totalSteps){ + if (stepNumber < 1 || stepNumber > totalSteps) { console.error("StepNumber out of Bounds", stepNumber); return; } @@ -292,7 +292,7 @@ function showStep(stepNumber) { document.querySelector(`.step-item[data-step="${stepNumber}"]`).classList.add("active"); const activeBtn = document.querySelector(`.step-item[data-step="${stepNumber}"]`); - if(activeBtn) activeBtn.classList.add("active"); + if (activeBtn) activeBtn.classList.add("active"); prevBtn.disabled = stepNumber == 1; nextBtn.disabled = stepNumber === totalSteps; @@ -303,31 +303,40 @@ function showStep(stepNumber) { //Audio value setter var speakerAudios = {}; var speakerEndValues = {}; -function setSpeakerAudiosValue(valy){ +function setSpeakerAudiosValue(valy) { try { speakerAudios = valy; speakerRewriten = valy; document.getElementById("speakerAudioViewer").src = valy.speakerA.src; } catch (error) { - + } } -function rewriteSpeakerName(){ +function rewriteSpeakerName() { try { - var tempy = document.getElementById("cur_speaker").value; - speakerAudios[tempy].name = document.getElementById("newSpeaker").value; + const oldKey = document.getElementById("cur_speaker").value; + const newName = document.getElementById("newSpeaker").value; + + speakerAudios[oldKey].name = newName; loadSpeakerOptions(speakerAudios); + + // IPC-Aufruf an Electron main process + window.electronAPI.saveSpeakerMapping({ + speakerId: oldKey, + speakerName: newName + }); + } catch (error) { - console.log("\n\n\n" + error + "\n\n\n") + console.log(error); } } -function sendSpeakerPackages(){ +function sendSpeakerPackages() { try { window.submitSpeaker.speaker_submit(speakerAudios); } catch (error) { - + } } @@ -338,7 +347,3 @@ function fileDownload() { console.error("Download failed:", error); } } - -function rewriteSpeakerName(){ - console.log("Rewriting speaker name..."); -} diff --git a/main.js b/main.js index 7e2dd9a..262b906 100644 --- a/main.js +++ b/main.js @@ -103,7 +103,23 @@ electron.ipcMain.handle('get-module-names', async () => { return module_array }); +electron.ipcMain.on("save-speaker-mapping", (event, data) => { + const filePath = "/Users/mikehughes/PROJ/video2document/storage/speakerMapping/speakerMapping.json"; + + const payload = { + speakerId: data.speakerId, + speakerName: data.speakerName, + updated: new Date().toISOString() + }; + + try { + fs.writeFileSync(filePath, JSON.stringify(payload, null, 2), "utf8"); + console.log("Speaker mapping saved!"); + } catch (error) { + console.error("Failed to save speaker mapping", error); + } +}); // electron.ipcMain.on("get_modules", async (event, args) => { // let module_array = { // "ai_modules":[], diff --git a/services/modules/replace_speaker/replaceSpeaker.js b/services/modules/replace_speaker/replaceSpeaker.js index 3101c6b..34a0e44 100644 --- a/services/modules/replace_speaker/replaceSpeaker.js +++ b/services/modules/replace_speaker/replaceSpeaker.js @@ -1,120 +1,70 @@ const fs = require('fs'); const path = require('path'); -const outputDir = path.join(__dirname, "../../../storage/documents"); // path for output directory - -if (!fs.existsSync(outputDir)) { - fs.mkdirSync(outputDir, { recursive: true }); // Create output directory if it doesn't exist -} - const module_exports = { name: "replace_speaker", type: "processor", displayname: "Speaker Name Replacer", description: "Replaces speaker placeholder names with actual names based on a mapping in HTML files", - async function(parameter) { - return new Promise(async (resolve, reject) => { - try { - // console.log("Speaker replacer module invoked with parameters:", parameter); - - resolve(await this.replaceNames( - parameter.inputHtmlPath, // Path to input HTML file - parameter.speakerMappingPath // Path to speaker mapping file (JSON) - )); - - } catch (error) { - // console.error("Error in speaker replacer module:", error); - reject(error) - } - }) + async function({ inputHtmlPath, speakerMappingPath }) { + return await this.replaceNames(inputHtmlPath, speakerMappingPath); }, replaceNames: async function(inputHtmlPath, speakerMappingPath) { - return new Promise(async(resolve, reject) => { + try { + const htmlContent = await fs.promises.readFile(inputHtmlPath, "utf-8"); + const mappingData = await fs.promises.readFile(speakerMappingPath, "utf-8"); + + let speakerMap = {}; try { - const htmlContent = await fs.promises.readFile(inputHtmlPath, "utf-8"); // read HTML file - const mappingData = await fs.promises.readFile(speakerMappingPath, "utf-8"); // read mapping file - - // Parse mapping - supports JSON or simple format - let speakerMap = {}; - try { - speakerMap = JSON.parse(mappingData); // Try to parse as JSON - } catch (e) { - // If not JSON, try simple format: "Speaker A,Mike\nSpeaker B,Stefan" - const lines = mappingData.trim().split('\n'); - lines.forEach(line => { - const [placeholder, realName] = line.split(',').map(s => s.trim()); - if (placeholder && realName) { - speakerMap[placeholder] = realName; - } - }); + const parsed = JSON.parse(mappingData); + if (parsed.speakerId && parsed.speakerName) { + speakerMap[parsed.speakerId] = parsed.speakerName; + } else { + Object.assign(speakerMap, parsed); } - - // Replace all speaker names in HTML content - let outputContent = htmlContent; - Object.entries(speakerMap).forEach(([placeholder, realName]) => { - // Create regex to replace all occurrences (case-sensitive) - const regex = new RegExp(`\\b${placeholder}\\b`, 'g'); - outputContent = outputContent.replace(regex, realName); + } catch (e) { + const lines = mappingData.trim().split('\n'); + lines.forEach(line => { + const [placeholder, realName] = line.split(',').map(s => s.trim()); + if (placeholder && realName) speakerMap[placeholder] = realName; }); - - // Generate output file path based on input file name - const inputFileName = path.basename(inputHtmlPath, path.extname(inputHtmlPath)); - const outPath = path.join(outputDir, `${inputFileName}_replaced.html`); - - // Write output to file - fs.writeFileSync(outPath, outputContent, "utf8"); - - // console.log("Replaced HTML file written to:", outPath); - resolve(outPath) - - } catch (error) { - // console.error("Error replacing speaker names:", error); - reject(error) } - }) + + let outputContent = htmlContent; + Object.entries(speakerMap).forEach(([placeholder, realName]) => { + const regex = new RegExp(`[\$begin:math:text$\\\\\[\]\?\$\{placeholder\}\[\\$end:math:text$\\]]?`, 'g'); + outputContent = outputContent.replace(regex, realName); + }); + + await fs.promises.writeFile(inputHtmlPath, outputContent, "utf-8"); + + return inputHtmlPath; + + } catch (error) { + console.error("Error replacing speaker names:", error); + throw error; + } } }; module.exports = module_exports; -// CLI Mode: Allow direct execution if (require.main === module) { (async () => { const args = process.argv.slice(2); - - if (args.length < 2) { - console.error("Usage: node string-replacer.js "); - console.error("Example: node string-replacer.js ./document.html ./speaker_mapping.json"); - console.error("\nMapping file formats:"); - console.error("JSON: {\"Speaker A\": \"Mike\", \"Speaker B\": \"Stefan\"}"); - console.error("or simple: Speaker A,Mike\\nSpeaker B,Stefan"); - process.exit(1); - } + if (args.length < 2) process.exit(1); const [inputHtmlPath, speakerMappingPath] = args; - // Check if files exist - if (!fs.existsSync(inputHtmlPath)) { - console.error(`ERROR: HTML file not found: ${inputHtmlPath}`); + if (!fs.existsSync(inputHtmlPath)) process.exit(1); + if (!fs.existsSync(speakerMappingPath)) process.exit(1); + + try { + await module_exports.replaceNames(inputHtmlPath, speakerMappingPath); + } catch (err) { process.exit(1); } - - if (!fs.existsSync(speakerMappingPath)) { - console.error(`ERROR: Speaker mapping file not found: ${speakerMappingPath}`); - process.exit(1); - } - - console.log("Starting speaker name replacement..."); - console.log(`HTML file: ${inputHtmlPath}`); - console.log(`Mapping file: ${speakerMappingPath}`); - - await module_exports.replaceNames( - inputHtmlPath, - speakerMappingPath - ); - - console.log("Done!"); })(); } \ No newline at end of file diff --git a/storage/speakerMapping/speakerMapping.json b/storage/speakerMapping/speakerMapping.json new file mode 100644 index 0000000..5a213f6 --- /dev/null +++ b/storage/speakerMapping/speakerMapping.json @@ -0,0 +1,5 @@ +{ + "speakerId": "speakerB", + "speakerName": "Peter", + "updated": "2026-01-10T13:54:55.608Z" +} \ No newline at end of file