diff --git a/main.js b/main.js index 4e9d673..9ba8c05 100644 --- a/main.js +++ b/main.js @@ -207,7 +207,12 @@ electron.ipcMain.on("file_submit", async (event, args) => { return }) - + + await mapFunctions.get("extract-speaker-snippets").function({audioPath: audiopath, jsonPath: transcriptpath }).then(resp => { + mainWindow.webContents.send("submitSpeaker", resp) + console.log(resp) + }) + // TODO actually implement this functionality // Module to get the first few lines for each speaker to send to the frontend // await mapFunctions.get("speaker-getter-idfk").function(transcriptpath).then(resp => { @@ -220,7 +225,7 @@ electron.ipcMain.on("file_submit", async (event, args) => { // // speakerA: {source: "Pfad zur Audio File"}, // // speakerB:..... // // } - mainWindow.webContents.send("speakers", {speakerA:"pfad1", speakerB:"pfad2"}) +// mainWindow.webContents.send("speakers", {speakerA:"pfad1", speakerB:"pfad2"}) // }).catch(err => { // mainWindow.webContents.send("error", err) // return diff --git a/package-lock.json b/package-lock.json index b28ee01..5b4651a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,7 +15,7 @@ "dotenv": "^17.2.3", "electron": "^39.1.1", "express": "^5.1.0", - "ffmpeg-static": "^5.2.0", + "ffmpeg-static": "^5.3.0", "fluent-ffmpeg": "^2.1.3", "html-to-docx": "^1.8.0", "mocha": "^11.7.5", @@ -1773,9 +1773,9 @@ } }, "node_modules/ffmpeg-static": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ffmpeg-static/-/ffmpeg-static-5.2.0.tgz", - "integrity": "sha512-WrM7kLW+do9HLr+H6tk7LzQ7kPqbAgLjdzNE32+u3Ff11gXt9Kkkd2nusGFrlWMIe+XaA97t+I8JS7sZIrvRgA==", + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/ffmpeg-static/-/ffmpeg-static-5.3.0.tgz", + "integrity": "sha512-H+K6sW6TiIX6VGend0KQwthe+kaceeH/luE8dIZyOP35ik7ahYojDuqlTV1bOrtEwl01sy2HFNGQfi5IDJvotg==", "hasInstallScript": true, "license": "GPL-3.0-or-later", "dependencies": { @@ -1832,6 +1832,7 @@ "resolved": "https://registry.npmjs.org/fluent-ffmpeg/-/fluent-ffmpeg-2.1.3.tgz", "integrity": "sha512-Be3narBNt2s6bsaqP6Jzq91heDgOEaDCJAXcE3qcma/EJBSy5FB4cvO31XBInuAuKBx8Kptf8dkhjK0IOru39Q==", "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.", + "license": "MIT", "dependencies": { "async": "^0.2.9", "which": "^1.1.1" diff --git a/package.json b/package.json index 88a0690..c5ea093 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,7 @@ "dotenv": "^17.2.3", "electron": "^39.1.1", "express": "^5.1.0", - "ffmpeg-static": "^5.2.0", + "ffmpeg-static": "^5.3.0", "fluent-ffmpeg": "^2.1.3", "html-to-docx": "^1.8.0", "mocha": "^11.7.5", diff --git a/services/modules/audioSnippets/extract-speaker-snippets.js b/services/modules/audioSnippets/extract-speaker-snippets.js new file mode 100644 index 0000000..0b2ea14 --- /dev/null +++ b/services/modules/audioSnippets/extract-speaker-snippets.js @@ -0,0 +1,95 @@ + +const ffmpeg = require("fluent-ffmpeg"); +const ffmpegPath = require("ffmpeg-static"); + +ffmpeg.setFfmpegPath(ffmpegPath); + +module.exports = { + name: "extract-speaker-snippets", + type: "audio", + displayname: "Extract Speaker Snippets", + + async function(parameter) { + return new Promise(async (resolve, reject) => { + + let output = {} + + console.log("Extract Speaker Snippets\n"); + + // Pfade + const AUDIO_PATH = parameter.audioPath; // Gesamt-Audio + const JSON_PATH = parameter.jsonPath; // json summary + const OUTPUT_DIR = path.join(__dirname, "/../../../storage/audio/speakerSnippets"); + + + if (!AUDIO_PATH || !JSON_PATH) { + console.error("no audioPath or jsonPath available"); + return; + } + + // Output-Ordner + if (!fs.existsSync(OUTPUT_DIR)) { + fs.mkdirSync(OUTPUT_DIR, { recursive: true }); + } + + // JSON laden + let entries; + try { + entries = JSON.parse(fs.readFileSync(JSON_PATH, "utf8")); + } catch (err) { + console.error("JSON reading failed", err); + return; + } + + if (!Array.isArray(entries)) { + console.error("JSON is not an Array"); + return; + } + + // Pro Speaker genau EINEN Satz merken + const speakerMap = {}; + + for (const item of entries) { + if (!speakerMap[item.speaker]) { + speakerMap[item.speaker] = item; + } + } + + // FFmpeg pro Speaker ausführen (sequenziell) + for (const speaker of Object.keys(speakerMap)) { + const data = speakerMap[speaker]; + + // ms → Sekunden + const startSec = data.start / 1000; + const durationSec = (data.end - data.start) / 1000; + + if (durationSec <= 0) { + console.log(`invalid times for Speaker ${speaker}`); + continue; + } + + const outFile = path.join(OUTPUT_DIR, `speaker_${speaker}.wav`); + + await new Promise((res, rej) => { + ffmpeg(AUDIO_PATH) + .setStartTime(startSec) + .setDuration(durationSec) + .output(outFile) + .on("end", () => { + output[`speaker${speaker}`] = {src: outFile, name: `speaker${speaker}`} + console.log(`Snippet erstellt: speaker_${speaker}.wav`); + res(); + }) + .on("error", (err) => { + console.error(`FFmpeg Fehler (${speaker})`, err.message); + rej(); + }) + .run(); + }); + + } + resolve(output) + console.log("\nAlle Speaker-Snippets erstellt\n"); + }) + } +}; \ No newline at end of file diff --git a/services/modules/utility/@startup.js b/services/modules/utility/@startup.js index 64f0b20..09b1ae5 100644 --- a/services/modules/utility/@startup.js +++ b/services/modules/utility/@startup.js @@ -10,6 +10,7 @@ module.exports = { // let transcript = await mapFunctions.get("assembly").function('../../storage/audio/IMG_2978.wav'); // let summary = await mapFunctions.get("summarize-transcription").function({jsonPath:'/Users/santa/Proj25/video2document/storage/transcripts/IMG_2978.json'}); + // let snippets = await mapFunctions.get("extract-speaker-snippets").function({audioPath:'/Users/santa/Proj25/video2document/storage/audio/KittyKat.wav', jsonPath1: '/Users/santa/Proj25/video2document/storage/transcriptionSummaries/KittyKat-1765806474958.json' });