From ee31d26116046318bf8b5790170af3158505d0e1 Mon Sep 17 00:00:00 2001 From: Azeufack Noupeu Willy Date: Thu, 11 Dec 2025 14:52:48 +0100 Subject: [PATCH 01/17] Implemented local Parakeet transcription module (S4-07) --- .../modules/transcription-local/parakeet.js | 54 ++++++++++++++ .../parakeet_transcribe.py | 71 +++++++++++++++++++ 2 files changed, 125 insertions(+) create mode 100644 services/modules/transcription-local/parakeet.js create mode 100644 services/modules/transcription-local/parakeet_transcribe.py diff --git a/services/modules/transcription-local/parakeet.js b/services/modules/transcription-local/parakeet.js new file mode 100644 index 0000000..10c95e0 --- /dev/null +++ b/services/modules/transcription-local/parakeet.js @@ -0,0 +1,54 @@ +// ----------------------------------------------------------- +// Parakeet (Step 3A: spawn Python minimal integration) +// ----------------------------------------------------------- + +const fs = require("fs"); +const path = require("path"); +const { spawn } = require("child_process"); + +module.exports = { + name: "parakeet", + type: "transcription", + displayname: "NVIDIA Parakeet", + + async function(audioFilePath) { + console.log("🦜 [Parakeet] Starting test integration (spawn only)..."); + console.log("🦜 Input audio:", audioFilePath); + + // Check audio exists + if (!fs.existsSync(audioFilePath)) { + throw new Error("Audio file does not exist: " + audioFilePath); + } + + // Output path in storage/transcripts + const sessionId = path.basename(audioFilePath).replace(/\.[^.]+$/, ""); + const outputDir = path.join(__dirname, "../../../storage/transcripts"); + fs.mkdirSync(outputDir, { recursive: true }); + + const outputPath = path.join(outputDir, `${sessionId}.json`); + + // ------------------------------------------------------- + // SPAWN PYTHON SCRIPT (step 3A — dummy script) + // ------------------------------------------------------- + return new Promise((resolve, reject) => { + const python310 = "C:\\Users\\smith\\AppData\\Local\\Programs\\Python\\Python310\\python.exe"; + const py = spawn(python310, [ + path.join(__dirname, "parakeet_transcribe.py"), + audioFilePath, + outputPath + ]); + + py.stdout.on("data", data => console.log("🦜 [Python]", data.toString().trim())); + py.stderr.on("data", data => console.error("🦜 [Python ERR]", data.toString().trim())); + + py.on("close", code => { + if (code === 0) { + console.log("🦜 [Parakeet] Done (spawn test). Output:", outputPath); + resolve(outputPath); + } else { + reject(new Error("Python script failed with exit code " + code)); + } + }); + }); + } +}; diff --git a/services/modules/transcription-local/parakeet_transcribe.py b/services/modules/transcription-local/parakeet_transcribe.py new file mode 100644 index 0000000..1272e46 --- /dev/null +++ b/services/modules/transcription-local/parakeet_transcribe.py @@ -0,0 +1,71 @@ +# ----------------------------------------------------------- +# Parakeet Real Transcriber (NVIDIA NeMo + PyTorch GPU) +# ----------------------------------------------------------- + +import sys +import json +import soundfile as sf +import torch +from nemo.collections.asr.models import ASRModel + +# Args: +# sys.argv[1] = input audio path +# sys.argv[2] = output JSON path + +audio_path = sys.argv[1] +output_path = sys.argv[2] + +print("🔥 Starting Parakeet model...") +device = "cuda" if torch.cuda.is_available() else "cpu" +print("🔥 Using device:", device) + +# ----------------------------------------------------------- +# Load Parakeet model (NVIDIA pretrained ASR) +# ----------------------------------------------------------- +model = ASRModel.from_pretrained(model_name="nvidia/parakeet-ctc-0.6b") +model = model.to(device) +model.eval() + +# ----------------------------------------------------------- +# Load audio +# ----------------------------------------------------------- +print("🎧 Loading audio:", audio_path) +audio, sr = sf.read(audio_path) + +# model expects mono float32 +if len(audio.shape) > 1: + audio = audio.mean(axis=1) + +audio = audio.astype("float32") + +# ----------------------------------------------------------- +# Run inference +# ----------------------------------------------------------- +print("🧠 Running inference...") +with torch.no_grad(): + hyp = model.transcribe([audio])[0] + +# Extract only the text +if hasattr(hyp, "text"): + transcript = hyp.text +else: + # fallback: convert to string (rare) + transcript = str(hyp) + +print("📄 Transcript:", transcript) + +# ----------------------------------------------------------- +# Save JSON format compatible with V2D pipeline +# ----------------------------------------------------------- +result = { + "id": output_path.split("/")[-1].replace(".json", ""), + "tool": "nemo_parakeet", + "status": "completed", + "text": transcript, + "words": [] # Parakeet XS doesn’t return word timestamps +} + +with open(output_path, "w", encoding="utf-8") as f: + json.dump(result, f, indent=2, ensure_ascii=False) + +print("✔ JSON saved at:", output_path) From 9c156a7df3d1e6c2d0c24b440ff0b204d8996946 Mon Sep 17 00:00:00 2001 From: MikeHughes-BIN Date: Tue, 23 Dec 2025 13:48:27 +0100 Subject: [PATCH 02/17] env example is in UTF 16 which, when copied and used as the .env, is unreadable and breaks the Programm --- .DS_Store | Bin 0 -> 6148 bytes .env.example | Bin 106 -> 0 bytes 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 .DS_Store delete mode 100644 .env.example diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..5008ddfcf53c02e82d7eee2e57c38e5672ef89f6 GIT binary patch literal 6148 zcmeH~Jr2S!425mzP>H1@V-^m;4Wg<&0T*E43hX&L&p$$qDprKhvt+--jT7}7np#A3 zem<@ulZcFPQ@L2!n>{z**++&mCkOWA81W14cNZlEfg7;MkzE(HCqgga^y>{tEnwC%0;vJ&^%eQ zLs35+`xjp>T0<#0094+5a<8^ From fe86fa1a2ff30a201c2e4c5ecf8f641feeb10b85 Mon Sep 17 00:00:00 2001 From: Azeufack Noupeu Willy Date: Thu, 11 Dec 2025 14:52:48 +0100 Subject: [PATCH 03/17] Implemented local Parakeet transcription module (S4-07) --- .../modules/transcription-local/parakeet.js | 54 ++++++++++++++ .../parakeet_transcribe.py | 71 +++++++++++++++++++ 2 files changed, 125 insertions(+) create mode 100644 services/modules/transcription-local/parakeet.js create mode 100644 services/modules/transcription-local/parakeet_transcribe.py diff --git a/services/modules/transcription-local/parakeet.js b/services/modules/transcription-local/parakeet.js new file mode 100644 index 0000000..10c95e0 --- /dev/null +++ b/services/modules/transcription-local/parakeet.js @@ -0,0 +1,54 @@ +// ----------------------------------------------------------- +// Parakeet (Step 3A: spawn Python minimal integration) +// ----------------------------------------------------------- + +const fs = require("fs"); +const path = require("path"); +const { spawn } = require("child_process"); + +module.exports = { + name: "parakeet", + type: "transcription", + displayname: "NVIDIA Parakeet", + + async function(audioFilePath) { + console.log("🦜 [Parakeet] Starting test integration (spawn only)..."); + console.log("🦜 Input audio:", audioFilePath); + + // Check audio exists + if (!fs.existsSync(audioFilePath)) { + throw new Error("Audio file does not exist: " + audioFilePath); + } + + // Output path in storage/transcripts + const sessionId = path.basename(audioFilePath).replace(/\.[^.]+$/, ""); + const outputDir = path.join(__dirname, "../../../storage/transcripts"); + fs.mkdirSync(outputDir, { recursive: true }); + + const outputPath = path.join(outputDir, `${sessionId}.json`); + + // ------------------------------------------------------- + // SPAWN PYTHON SCRIPT (step 3A — dummy script) + // ------------------------------------------------------- + return new Promise((resolve, reject) => { + const python310 = "C:\\Users\\smith\\AppData\\Local\\Programs\\Python\\Python310\\python.exe"; + const py = spawn(python310, [ + path.join(__dirname, "parakeet_transcribe.py"), + audioFilePath, + outputPath + ]); + + py.stdout.on("data", data => console.log("🦜 [Python]", data.toString().trim())); + py.stderr.on("data", data => console.error("🦜 [Python ERR]", data.toString().trim())); + + py.on("close", code => { + if (code === 0) { + console.log("🦜 [Parakeet] Done (spawn test). Output:", outputPath); + resolve(outputPath); + } else { + reject(new Error("Python script failed with exit code " + code)); + } + }); + }); + } +}; diff --git a/services/modules/transcription-local/parakeet_transcribe.py b/services/modules/transcription-local/parakeet_transcribe.py new file mode 100644 index 0000000..1272e46 --- /dev/null +++ b/services/modules/transcription-local/parakeet_transcribe.py @@ -0,0 +1,71 @@ +# ----------------------------------------------------------- +# Parakeet Real Transcriber (NVIDIA NeMo + PyTorch GPU) +# ----------------------------------------------------------- + +import sys +import json +import soundfile as sf +import torch +from nemo.collections.asr.models import ASRModel + +# Args: +# sys.argv[1] = input audio path +# sys.argv[2] = output JSON path + +audio_path = sys.argv[1] +output_path = sys.argv[2] + +print("🔥 Starting Parakeet model...") +device = "cuda" if torch.cuda.is_available() else "cpu" +print("🔥 Using device:", device) + +# ----------------------------------------------------------- +# Load Parakeet model (NVIDIA pretrained ASR) +# ----------------------------------------------------------- +model = ASRModel.from_pretrained(model_name="nvidia/parakeet-ctc-0.6b") +model = model.to(device) +model.eval() + +# ----------------------------------------------------------- +# Load audio +# ----------------------------------------------------------- +print("🎧 Loading audio:", audio_path) +audio, sr = sf.read(audio_path) + +# model expects mono float32 +if len(audio.shape) > 1: + audio = audio.mean(axis=1) + +audio = audio.astype("float32") + +# ----------------------------------------------------------- +# Run inference +# ----------------------------------------------------------- +print("🧠 Running inference...") +with torch.no_grad(): + hyp = model.transcribe([audio])[0] + +# Extract only the text +if hasattr(hyp, "text"): + transcript = hyp.text +else: + # fallback: convert to string (rare) + transcript = str(hyp) + +print("📄 Transcript:", transcript) + +# ----------------------------------------------------------- +# Save JSON format compatible with V2D pipeline +# ----------------------------------------------------------- +result = { + "id": output_path.split("/")[-1].replace(".json", ""), + "tool": "nemo_parakeet", + "status": "completed", + "text": transcript, + "words": [] # Parakeet XS doesn’t return word timestamps +} + +with open(output_path, "w", encoding="utf-8") as f: + json.dump(result, f, indent=2, ensure_ascii=False) + +print("✔ JSON saved at:", output_path) From 9ad335d872f6ed06c5e82e085c36d307b79bb934 Mon Sep 17 00:00:00 2001 From: Emily Date: Thu, 8 Jan 2026 19:19:09 +0100 Subject: [PATCH 04/17] just a line removed so i have something to push to see if the branch creation succeeded --- main.js | 1 - 1 file changed, 1 deletion(-) diff --git a/main.js b/main.js index a2c7e34..bd09986 100644 --- a/main.js +++ b/main.js @@ -22,7 +22,6 @@ folders.forEach(element => { }); - // The startup information for the project, here you can add stuff that might be nice to see when the app starts mapFunctions.get("Startup_function").function() console.log("------------------------------------ Status ------------------------------------"); From 39af2c331267d9d9c73897c3f894c87be3570f44 Mon Sep 17 00:00:00 2001 From: Emily Date: Thu, 8 Jan 2026 19:32:23 +0100 Subject: [PATCH 05/17] removed unneeded variable in main.js --- main.js | 20 -------------------- 1 file changed, 20 deletions(-) diff --git a/main.js b/main.js index bd09986..d61c897 100644 --- a/main.js +++ b/main.js @@ -231,26 +231,6 @@ electron.ipcMain.on("speaker_submit", async() => { console.log("\n\n\nJa also hier kam was an \n\n\n"); }) -let q = -{ - video: { - module: "String", // The name of the module, idk if we ever implement other extraction modules, the default one is extraction-video-to-audio - inputVideoPath: "String", // See script.js on line 27 for an example of what this should look like - outputType: "String" // The file format to be used for the audio output file, such as wav, mp3, flac and so on - }, - transcription:{ - module: "String" // The module name of the transcription model you want to use - }, - document:{ - module: "String", // The module name of the AI model you want to use to create the document - styles: [ // An array of all the document styles/prompts you want to have the document be processed with - { - prompt: "String", - } - ] - } -} - let q1 = { "ai_modules": [ From 9b8561e4ce688379316ef9baee7a9671d4d63849 Mon Sep 17 00:00:00 2001 From: Emily Date: Thu, 8 Jan 2026 19:33:10 +0100 Subject: [PATCH 06/17] euh, removed another unneeded variable .-. --- main.js | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/main.js b/main.js index d61c897..8e74579 100644 --- a/main.js +++ b/main.js @@ -232,16 +232,6 @@ electron.ipcMain.on("speaker_submit", async() => { }) -let q1 = { - "ai_modules": [ - {name:"abc", displayname:"ABC"}, - {name:"qeg", displayname:"aqghegahu"} - ], - "transcription_modules": [ - {name:"abc", displayname:"ABC"}, - {name:"qeg", displayname:"aqghegahu"} - ] -} //gibt Documentfiles an preload zurück From 6c41da781e8111d282c19ba9c0f8813e2561d527 Mon Sep 17 00:00:00 2001 From: Emily Date: Thu, 8 Jan 2026 20:37:06 +0100 Subject: [PATCH 07/17] initial version of the software documentation --- documentation.md | 116 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 116 insertions(+) create mode 100644 documentation.md diff --git a/documentation.md b/documentation.md new file mode 100644 index 0000000..eaf64b5 --- /dev/null +++ b/documentation.md @@ -0,0 +1,116 @@ +# Documenation of our Program + +## Table of Contents +1. [How to run the Software](#how-to-run-the-software) +2. [How it works](#how-it-works) +3. [Modules](#modules) +3. [IPC](#ipc) + +## How to run the Software +If you read the readme file, you will see the basic setup command in order to run the program. +You will need nodejs, the newer the better. +The software is tested with `nodejs-19`, `nodejs-20`, `nodejs-22` and `nodejs-24`. +These versions are confirmed to work with our software, but prior versions may work aswell. +To get nodejs, simply go to their [website](https://nodejs.org/en/download) and download whatever version you want and install it. +Afterward go into the directory of our program, and run the command `npm i`, this will install all the necessary libraries. +Next up you need to set up the .env file. +The file must contain your keys for the modules you want to use. +The .env file looks like this: +``` +ASSEMBLYAI_API_KEY=wefhjhjakeghjkahejkghjkaegh +GOOGLE_API_KEY=wefhjhjakeghjkahejkghjkaegh +SAIA_API_KEY=wefhjhjakeghjkahejkghjkaegh +``` +Note that if you write your module in the same format we did, then you will only need to supply the api keys to the individual services you will actually use. +If you dont want to use Assembly AI, you can for example just leave this row out of your .env, and the program will just work fine. +Only issue will be that it will throw an error if you do run the Assembly AI module anyways. +Once that is done, you can run the command `npm start` to actually start the program. +Alternatively you can double click the start.bat if you are on Windows for example. + +## How it works +Our software is fully modular, this means, every part can easily be edited, replaced, removed or added without needing to make many adjustments in the code. +The modules can be found under `./services/modules` +The Structure is as follows: +Inside of `modules` are folders named after the general thing the module does. +For example, `transcription-remote`, this folder contains all the modules that do transcription on a remote service, such as assembly-ai for example. +Inside of these folders we put our modules. +The name of the folder and the module dont matter, as long as the structure is kept, and the module filename ends with .js, it will work. +The program iterates through all of the folders within `./services/modules`, and then iterates over each `.js` file within each of these folders, and then loads them into a specific map called `mapFunctions`. +This map is available ANYWHERE in the backend code, even within a module, which means, you can call a module, from within a module, from within a module, from within yet another module. + +## Modules +Building a new module is super easy, any idiot can get it done. +All you need to do is follow the previously mentioned structure. +When you have created your module file, the `.js` file, you simply copy paste this code snippet into it. +```js +module.exports = { + name:"example", // Unique name for our function that will later be used to get the function from the map via "mapFunctions.get("example").function()" + type:"example-type", // value used to differentiate each module to order them in the UI + displayname:"Example", // The displayname used within the UI + async function(randomParameter){ + // Here we put a simple console.log to show how the system works + // This function will be called from the @startup.js function in the utility folder + console.log(`\n------------\nThis is the example function called by the ${randomParameter} function\n------------\n`); + } +} +``` +If by this point, you cared enough to actually look at the code and the modules and so on, you might have spotted a file in `./services/modules/utility` called `example.js`. +This is a template file that you can just copy paste and use as a base for your new module. +It has the same exact code as mentioned right above. +Now as for how the code works. +Each module is essentially just a JSON object that is being exported, so that the main process can load it into the `mapFunctions` map. +the required fields are as follows +- name: This field contains the internal name through which it will be called. It HAS TO BE unique. +- type: This field contains the type of the module. Is it an LLM module, transcription module, or whatever. +- displayname: This field contains the Displayname used in the UI. + +And lastly the function call. +This function call is what is being called by other functions, it is generally the main entrypoint for a module. +Sure, you can always set custom function names, but this is a general solution that works without having to manage function names. +You can always define custom functions inside your module that you call from the entrypoint function, but it is highly advised just to call the entrypoint function from outside of the module as it prevents headaches by just working as intended. + +Note, there are also other module fields for specific module types, such as: +- description: Used by any module that needs to be shown on the UI, such as Transcription and LLM modules. +- audioformat: Used by transcription modules to tell the audio extraction module what audio format to use. + +## IPC +Our software is split up into 2 pieces, the main process (Backend) and the frontend. +The frontend is written in Electron, so it is essentially just a website. +This makes it relatively easy to edit the frontend. +But it comes with one downside, which is, the frontend and backend cant just directly communicate, you first need to set up an IPC channel between them. +As for the base functionality, all of this is already done. +The frontend gets a list of all the available LLM and Transcription modules sent by the backend on startup. +The JSON object for this information looks like this: +```json +{ + "ai_modules":[ + {"name": "Example", "displayname": "Example"} + ], + "transcription_modules":[ + {"name": "Example", "displayname": "Example"} + ] +} +``` +The backend needs a specific set of informations from the frontend in order to start the pipeline. +The JSON for that looks like this: +```json +{ + "video": { + "module": "extraction-video-to-audio", + "inputVideoPath": "A:\\programing\\@projects\\video2document\\test\\unit\\testvideo.mp4" + }, + "transcription": { + "module": "assembly" + }, + "document": { + "module": "llm-saia_openai_gpt", + "type": "followup-report", + "outputType": "pdf" + } +} +``` +As you can see in this JSON object, each part specifies which module is being used for each step. +The module names are each the name field specified in the module itself. +As for the rest of the fields, they are pretty self explanatory except `document.type`, that is a predefined report type. +This is the minimum required setup for the currently implemented pipeline to work. +You can always add fields to it, but dont remove the ones from above. From 0010e400b5e84bcbeb3119cf00f4344de3f08028 Mon Sep 17 00:00:00 2001 From: "Aarthi Manivannan, Premanathan Aarthi Manivannan" Date: Sun, 11 Jan 2026 19:35:15 +0100 Subject: [PATCH 08/17] Update file README.md --- README.md | 312 +++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 311 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 5268290..2d97aa1 100644 --- a/README.md +++ b/README.md @@ -53,4 +53,314 @@ Make sure that **ffmpeg** is installed on your system, as it may be required for --- -**Installation complete!** You're now ready to use Video2Document. \ No newline at end of file +**Installation complete!** You're now ready to use Video2Document. + +###General Despription +## Project Overview + +V2D (Video to Document Framework) is a student project developed to convert video and audio content into structured, readable documents using AI-based text processing. The tool is designed to help users automatically generate documents such as meeting reports, summaries or structured texts from recorded videos or audio files. + +The main problem V2D addresses is the time-consuming manual effort required to listen to recordings and write documents afterwards. By automating transcription, structuring and document generation, the tool improves efficiency and usability for students, teams and small organizations. + +The project was created as part of a university software engineering course and is developed using agile methods (Scrum). It focuses on modular design, usability and extensibility, allowing new document types and features to be added easily. + +### Used Packages and Dependencies + +This project uses several Node.js packages to handle video processing, AI-based document generation, backend communication, and the desktop user interface. Below is an overview of the most important dependencies and their purpose. + +Core Dependencies: + +Electron: +Used to build the desktop application. It allows the project to run as a cross-platform GUI application using web technologies. + +Express: +Acts as a lightweight backend server that handles internal API requests and communication between different parts of the application. + +Axios: +Used for making HTTP requests, for example when communicating with external APIs such as AI services. + +dotenv: +Used to securely load API keys and other sensitive configuration values from environment variables instead of hardcoding them in the source code. + +@google/genai: +Provides access to Google’s Generative AI models. This package is used to generate structured documents from transcribed text based on configurable prompts. + +ffmpeg-static: +Provides a static FFmpeg binary so that video and audio processing works without requiring FFmpeg to be installed separately on the system. + +fluent-ffmpeg: +Used together with FFmpeg to process video and audio files, for example extracting audio tracks from uploaded videos. + +puppeteer: +Used to render and process HTML content programmatically. This is helpful for document previews and automated content generation. + +html-to-docx: +Converts generated HTML documents into downloadable DOCX files, which are provided to the user as the final output. + +mocha: +A testing framework used to run unit tests and ensure that core functionalities work as expected. + +##Development Dependencies: + +TypeScript: +Used to add static typing to the project, improving code quality, readability, and maintainability. + +ts-node: +Allows running TypeScript files directly without manually compiling them first. + +@types/node: +Provides TypeScript type definitions for Node.js APIs. + +@types/fluent-ffmpeg: +Type definitions for fluent-ffmpeg to improve development experience and error checking. + +@types/cli-progress: +Provides type support for progress bar functionality used during processing tasks. + +##Why These Packages Are Needed + +Together, these packages enable: + +Processing video and audio files + +Communicating with AI models for document generation + +Secure handling of API keys + +Generating structured documents (DOCX) + +Providing a user-friendly desktop interface + +Ensuring code quality through testing + +##API Keys and Configuration + +The V2D – Video to Document tool uses external AI and media processing services to convert video and audio content into structured documents. +To access these services, several API keys are required. For security reasons, API keys are not stored in the repository and must be provided via environment variables. + + #Supported API Keys + +The project currently supports the following API keys. Depending on the configuration and selected provider, one or more of these keys may be used. + +Google Gemini API + +Environment variable: GOOGLE_API_KEY + +Usage: +Used for AI-based document generation. The Large Language Model processes transcripts and creates structured documents such as meeting reports. + +OpenAI (ChatGPT) + +Environment variable: OPENAI_API_KEY + +Usage: +Alternative AI provider for text processing and document generation. + +AssemblyAI + +Environment variable: ASSEMBLYAI_API_KEY + +Usage: +Speech-to-text processing for audio and video files. + +Saya + +Environment variable: SAYA_API_KEY + +Usage: +Additional or experimental AI provider that can be integrated into the document generation pipeline. + +How to Set API Keys + +API keys must be configured as environment variables before starting the application. + +Linux / macOS +export GOOGLE_API_KEY="your_api_key_here" +export OPENAI_API_KEY="your_api_key_here" +export ASSEMBLYAI_API_KEY="your_api_key_here" +export SAYA_API_KEY="your_api_key_here" + +Windows (PowerShell) +setx GOOGLE_API_KEY "your_api_key_here" +setx OPENAI_API_KEY "your_api_key_here" +setx ASSEMBLYAI_API_KEY "your_api_key_here" +setx SAYA_API_KEY "your_api_key_here" + + +Alternatively, for local development, a .env file can be used: + +GOOGLE_API_KEY=your_api_key_here +OPENAI_API_KEY=your_api_key_here +ASSEMBLYAI_API_KEY=your_api_key_here +SAYA_API_KEY=your_api_key_here + + +⚠️ Important: +The .env file must not be committed to the repository and should be listed in .gitignore. + +Security Notes: + +API keys are injected at runtime + +No secrets are stored in the source code + +Prevents accidental exposure of sensitive data + +Supports secure collaboration in GitLab and CI/CD environments + +Follows best practices for secret management + +End-to-End User Guide (Video → Final Document) + +This section describes how a user can create a structured document from a video using the V2D – Video to Document tool. + +Start the Application: + +Ensure all required API keys are configured as environment variables. + + +Install dependencies: + +npm install + + +Start the application: + +npm start + + +The Electron-based GUI will open. + +Upload a Video File: + +In the application interface, select Upload Video. + +Choose a supported video file (e.g. .mp4, .mov). + +The video is loaded into the system for processing. + +Audio Extraction: + +The application automatically extracts audio from the uploaded video. + +This is handled internally using FFmpeg. + +No user interaction is required for this step. + +Speech-to-Text Transcription: + +The extracted audio is sent to the speech-to-text service. + +The transcription process converts spoken content into text. + +The generated transcript is stored internally and used for further processing. + +Select Document Type: + +The user selects a document type (e.g. Meeting Report). + +Each document type is based on a predefined prompt template. + +The selected template defines the structure and style of the final document. + +Document Generation: + +The transcript and selected prompt are sent to the AI service. + +The AI model processes the input and generates a structured document. + +The output is formatted in Markdown. + +Document Preview: + +The generated document is displayed in the application preview. + +Users can review the content before exporting. + +No manual editing is required, but validation is possible. + +Export the Final Document: + +The user exports the document in the desired format. + +Supported formats include: + +Markdown (.md) + +Word (.docx) + +The document is saved locally. + +Completion: + +The final document is now ready for use. + +The user can repeat the process with another video if needed. + + +Resources + +This section lists the main technologies, libraries, and external resources used in the V2D (Video to Document) project. These resources are required to understand, run, and further develop the application. + +Project Dependencies + +The following packages and tools are used in this project (as defined in package.json): + +Application & Backend + +Node.js – JavaScript runtime environment used for backend processing + +Electron – Framework for building the desktop application + +Express – Backend web framework for handling requests and internal APIs + +AI & API Communication + +@google/genai – Used for AI-based document generation + +Axios – HTTP client for communicating with external APIs (LLMs) + +Video & Audio Processing + +ffmpeg-static – Extracts audio from uploaded video files + +fluent-ffmpeg – Controls FFmpeg operations programmatically + +Document Generation & Preview + +html-to-docx – Converts structured content into Word documents + +Puppeteer – Renders HTML content for document preview and processing + +Configuration & Security + +dotenv – Loads API keys securely from environment variables + +Testing & Development + +Mocha – Unit testing framework + +TypeScript – Improves code quality and type safety + +These dependencies enable the complete end-to-end workflow from video input to structured document output. + +Relevant Repositories + +V2D Main Repository: +https://gitlab.rlp.net/proj-wise2526-video2document/video2document + +This repository contains the full source code, configuration files, and documentation for the V2D project. + +Downloads & External Resources + +The following tools and documentation may be required to run or understand the project: + +Node.js: https://nodejs.org + +Electron Documentation: https://www.electronjs.org/docs + +FFmpeg: https://ffmpeg.org + +Google Generative AI: https://ai.google.dev + +Puppeteer: https://pptr.dev \ No newline at end of file From 11f7db1b1cd314021475c723b6ebb59ee7756e41 Mon Sep 17 00:00:00 2001 From: MikeHughes-BIN Date: Mon, 12 Jan 2026 19:07:42 +0100 Subject: [PATCH 09/17] speaker-renaming module created --- .../modules/replace_speaker/replaceSpeaker.js | 70 +++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 services/modules/replace_speaker/replaceSpeaker.js diff --git a/services/modules/replace_speaker/replaceSpeaker.js b/services/modules/replace_speaker/replaceSpeaker.js new file mode 100644 index 0000000..34a0e44 --- /dev/null +++ b/services/modules/replace_speaker/replaceSpeaker.js @@ -0,0 +1,70 @@ +const fs = require('fs'); +const path = require('path'); + +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({ inputHtmlPath, speakerMappingPath }) { + return await this.replaceNames(inputHtmlPath, speakerMappingPath); + }, + + replaceNames: async function(inputHtmlPath, speakerMappingPath) { + try { + const htmlContent = await fs.promises.readFile(inputHtmlPath, "utf-8"); + const mappingData = await fs.promises.readFile(speakerMappingPath, "utf-8"); + + let speakerMap = {}; + try { + const parsed = JSON.parse(mappingData); + if (parsed.speakerId && parsed.speakerName) { + speakerMap[parsed.speakerId] = parsed.speakerName; + } else { + Object.assign(speakerMap, parsed); + } + } 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; + }); + } + + 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; + +if (require.main === module) { + (async () => { + const args = process.argv.slice(2); + if (args.length < 2) process.exit(1); + + const [inputHtmlPath, speakerMappingPath] = args; + + 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); + } + })(); +} \ No newline at end of file From 641b17820fa1ec511fe21980ee1c7b5819810697 Mon Sep 17 00:00:00 2001 From: "Aarthi Manivannan, Premanathan Aarthi Manivannan" Date: Mon, 12 Jan 2026 21:40:11 +0100 Subject: [PATCH 10/17] Update file README.md --- README.md | 70 ++++++++++++++++++++++++++++++++----------------------- 1 file changed, 41 insertions(+), 29 deletions(-) diff --git a/README.md b/README.md index 2d97aa1..24e9282 100644 --- a/README.md +++ b/README.md @@ -68,7 +68,7 @@ The project was created as part of a university software engineering course and This project uses several Node.js packages to handle video processing, AI-based document generation, backend communication, and the desktop user interface. Below is an overview of the most important dependencies and their purpose. -Core Dependencies: +##Core Dependencies: Electron: Used to build the desktop application. It allows the project to run as a cross-platform GUI application using web technologies. @@ -117,28 +117,28 @@ Type definitions for fluent-ffmpeg to improve development experience and error c @types/cli-progress: Provides type support for progress bar functionality used during processing tasks. -##Why These Packages Are Needed +###Why These Packages Are Needed Together, these packages enable: -Processing video and audio files +*Processing video and audio files -Communicating with AI models for document generation +*Communicating with AI models for document generation -Secure handling of API keys +*Secure handling of API keys -Generating structured documents (DOCX) +*Generating structured documents (DOCX) -Providing a user-friendly desktop interface +*Providing a user-friendly desktop interface -Ensuring code quality through testing +*Ensuring code quality through testing -##API Keys and Configuration +###API Keys and Configuration The V2D – Video to Document tool uses external AI and media processing services to convert video and audio content into structured documents. To access these services, several API keys are required. For security reasons, API keys are not stored in the repository and must be provided via environment variables. - #Supported API Keys + ##Supported API Keys The project currently supports the following API keys. Depending on the configuration and selected provider, one or more of these keys may be used. @@ -170,7 +170,7 @@ Environment variable: SAYA_API_KEY Usage: Additional or experimental AI provider that can be integrated into the document generation pipeline. -How to Set API Keys +##How to Set API Keys API keys must be configured as environment variables before starting the application. @@ -200,17 +200,19 @@ The .env file must not be committed to the repository and should be listed in .g Security Notes: -API keys are injected at runtime +*API keys are injected at runtime -No secrets are stored in the source code +*No secrets are stored in the source code -Prevents accidental exposure of sensitive data +*Prevents accidental exposure of sensitive data -Supports secure collaboration in GitLab and CI/CD environments +*Supports secure collaboration in GitLab and CI/CD environments -Follows best practices for secret management +*Follows best practices for secret management -End-to-End User Guide (Video → Final Document) + + +###End-to-End User Guide (Video → Final Document) This section describes how a user can create a structured document from a video using the V2D – Video to Document tool. @@ -231,7 +233,7 @@ npm start The Electron-based GUI will open. -Upload a Video File: +#Upload a Video File: In the application interface, select Upload Video. @@ -239,7 +241,9 @@ Choose a supported video file (e.g. .mp4, .mov). The video is loaded into the system for processing. -Audio Extraction: + + +#Audio Extraction: The application automatically extracts audio from the uploaded video. @@ -247,7 +251,8 @@ This is handled internally using FFmpeg. No user interaction is required for this step. -Speech-to-Text Transcription: + +#Speech-to-Text Transcription: The extracted audio is sent to the speech-to-text service. @@ -255,7 +260,8 @@ The transcription process converts spoken content into text. The generated transcript is stored internally and used for further processing. -Select Document Type: + +#Select Document Type: The user selects a document type (e.g. Meeting Report). @@ -263,7 +269,8 @@ Each document type is based on a predefined prompt template. The selected template defines the structure and style of the final document. -Document Generation: + +#Document Generation: The transcript and selected prompt are sent to the AI service. @@ -271,7 +278,8 @@ The AI model processes the input and generates a structured document. The output is formatted in Markdown. -Document Preview: + +#Document Preview: The generated document is displayed in the application preview. @@ -279,11 +287,13 @@ Users can review the content before exporting. No manual editing is required, but validation is possible. -Export the Final Document: + +#Export the Final Document: The user exports the document in the desired format. -Supported formats include: + +#Supported formats include: Markdown (.md) @@ -291,18 +301,19 @@ Word (.docx) The document is saved locally. -Completion: + +#Completion: The final document is now ready for use. The user can repeat the process with another video if needed. -Resources +###Resources This section lists the main technologies, libraries, and external resources used in the V2D (Video to Document) project. These resources are required to understand, run, and further develop the application. -Project Dependencies +##Project Dependencies The following packages and tools are used in this project (as defined in package.json): @@ -344,7 +355,8 @@ TypeScript – Improves code quality and type safety These dependencies enable the complete end-to-end workflow from video input to structured document output. -Relevant Repositories + +##Relevant Repositories V2D Main Repository: https://gitlab.rlp.net/proj-wise2526-video2document/video2document From 80eb9dac2362f81ba43920931e5f02bf8e69d33f Mon Sep 17 00:00:00 2001 From: Emily Date: Wed, 14 Jan 2026 02:56:14 +0100 Subject: [PATCH 11/17] removed console.log from the .env file loader --- requires.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requires.js b/requires.js index 6a7e20a..c714753 100644 --- a/requires.js +++ b/requires.js @@ -22,4 +22,4 @@ electron = require('electron'); axios = require("axios") -console.log(require('dotenv').config({path: __dirname + '/.env'})); \ No newline at end of file +require('dotenv').config({path: __dirname + '/.env'}); \ No newline at end of file From 5a85a7da4c9ca9329f003aa562807ee77b020432 Mon Sep 17 00:00:00 2001 From: Emily Date: Wed, 14 Jan 2026 02:57:51 +0100 Subject: [PATCH 12/17] implemented initial version of the authentication system Also removed some code that as it turns out, is useless, as electron is a shit software which completely takes over stdin, so there is no way to actually write anything on the CLI --- main.js | 83 +++++++++++++++++++++++++++++++++++---------------------- 1 file changed, 51 insertions(+), 32 deletions(-) diff --git a/main.js b/main.js index efe940b..26bfcb7 100644 --- a/main.js +++ b/main.js @@ -2,6 +2,57 @@ require("./requires.js") console.log(start); +const https = require("https"); +let un = process.env.auth_username +let pw = process.env.auth_password + + +const options = { + hostname: "keyserver.dommymommy.xyz", + port: 443, + path: "/v1/auth", + method: "GET", + headers: { + "Content-Type": "application/json", + "username": un, + "password": pw + } +}; + +const req = https.request(options, (res) => { + if (res.statusCode === 200) { + res.setEncoding("utf8"); + let data = ""; + res.on("data", (chunk) => { + data += chunk; + }); + res.on("end", () => { + const myJson = JSON.parse(data); + Object.keys(myJson).forEach(el => { + // console.log(el, myJson[el]); + process.env[el] = myJson[el] + }) + }); + } else if (res.statusCode === 401) { + res.setEncoding("utf8"); + let data = ""; + res.on("data", (chunk) => { + data += chunk; + }); + res.on("end", () => { + console.log(data); + process.exit() + }); + } +}); + +req.on("error", (error) => { + console.error(error); +}); + +req.end(); + + // Initialising map to be used to store the functionality later on for reloadability mapFunctions = new Map() @@ -32,29 +83,6 @@ console.log(`The Startup took ${new Date() - start}ms`) console.log(`${mapFunctions.size} Function modules loaded`); console.log("--------------------------------------------------------------------------------"); - - - - - -// --------------------------------------------------------- CLI COMMANDS --------------------------------------------------------- // - -const rl = readline.createInterface({ - input: process.stdin, - output: process.stdout -}); - - -rl.on("line", data =>{ - const args = data.trim().split(" "); - const command = args.shift().toLowerCase(); - mapFunctions.get("cliCommands").function(command, args) -}) - - - - - // ----------------------------------------------------------- ELECTRON ----------------------------------------------------------- // let mainWindow; @@ -75,15 +103,6 @@ function createWindow() { electron.app.whenReady().then(createWindow); - -// electron.ipcMain.on("extract", (event, args) => { -// mapFunctions.get("extraction-video-to-audio").function(args) -// }) - -// setTimeout(() => { -// mainWindow.webContents.send("fuck", "worked uwu") -// }, 5000); - electron.ipcMain.handle('get-module-names', async () => { let module_array = { "ai_modules":[], From 6b3724b7657cfd550f4b5984c7a25a8f69a0ec9b Mon Sep 17 00:00:00 2001 From: Emily Date: Wed, 14 Jan 2026 03:01:30 +0100 Subject: [PATCH 13/17] edited the way api keys are handled. Since API keys are now loaded from the auth server, this means that there is a delay between starting the software, loading the keys, and loading all the module files. Due to nodejs running Async, the modules are being loaded BEFORE the api keys have been returned from the auth server. So now the api keys are being set inside the module every time the module is being called. This has absolutely no impact on performance, or security, plus, this was the easiest solution, especially as it required no big changes to the rest of the code --- services/modules/llm-chat_gpt/chatgpt.js | 3 ++- services/modules/llm-gemini/gemini.js | 3 ++- services/modules/quen3/qwen3.js | 3 ++- services/modules/transcription-remote/assembly.js | 3 ++- 4 files changed, 8 insertions(+), 4 deletions(-) diff --git a/services/modules/llm-chat_gpt/chatgpt.js b/services/modules/llm-chat_gpt/chatgpt.js index 93b9746..95afd56 100644 --- a/services/modules/llm-chat_gpt/chatgpt.js +++ b/services/modules/llm-chat_gpt/chatgpt.js @@ -8,7 +8,7 @@ if (!fs.existsSync(outputDir)) { } // Ensure SAIA API key is set in environment variables: export SAIA_API_KEY="your_api_key_here" -const SAIA_API_KEY = process.env.SAIA_API_KEY; // Ensure SAIA API key is set in environment variables +let SAIA_API_KEY // Ensure SAIA API key is set in environment variables const SAIA_URL = "https://chat-ai.academiccloud.de/v1/chat/completions"; // URL for the REST call, used model and action const module_exports = { @@ -18,6 +18,7 @@ const module_exports = { description: "Generates documents using OpenAI GPT OSS 120B via SAIA platform", async function(parameter) { + SAIA_API_KEY = process.env.SAIA_API_KEY; return new Promise(async (resolve, reject) => { try { // console.log("SAIA OpenAI GPT module invoked with parameters:", parameter); diff --git a/services/modules/llm-gemini/gemini.js b/services/modules/llm-gemini/gemini.js index beff750..ff7e361 100644 --- a/services/modules/llm-gemini/gemini.js +++ b/services/modules/llm-gemini/gemini.js @@ -8,7 +8,7 @@ if (!fs.existsSync(outputDir)) { } // Ensure Google API key is set in environment variables: export GOOGLE_API_KEY="your_api_key_here" -const GEMINI_API_KEY = process.env.GOOGLE_API_KEY; // Ensure Google API key is set in environment variables: export GOOGLE_API_KEY="your_api_key_here" +let GEMINI_API_KEY; // Ensure Google API key is set in environment variables: export GOOGLE_API_KEY="your_api_key_here" const GEMINI_URL = "https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:generateContent"; // URL for the REST call, used model and action const module_exports = { @@ -18,6 +18,7 @@ const module_exports = { description: "Generates documents using Google Gemini LLM", async function(parameter) { + GEMINI_API_KEY = process.env.GOOGLE_API_KEY; return new Promise(async (resolve, reject) => { try { // console.log("Gemini LLM module invoked with parameters:", parameter); diff --git a/services/modules/quen3/qwen3.js b/services/modules/quen3/qwen3.js index 0bc913a..349c0b2 100644 --- a/services/modules/quen3/qwen3.js +++ b/services/modules/quen3/qwen3.js @@ -8,7 +8,7 @@ if (!fs.existsSync(outputDir)) { } // Ensure SAIA API key is set in environment variables: export SAIA_API_KEY="your_api_key_here" -const SAIA_API_KEY = process.env.SAIA_API_KEY; +let SAIA_API_KEY; const SAIA_URL = "https://chat-ai.academiccloud.de/v1/chat/completions"; // URL for the REST call, used model and action const module_exports = { @@ -18,6 +18,7 @@ const module_exports = { description: "Generates documents using QWEN 3 235B via SAIA platform", async function(parameter) { + SAIA_API_KEY = process.env.SAIA_API_KEY; return new Promise(async (resolve, reject) => { try { // console.log("SAIA QWEN 3 235B module invoked with parameters:", parameter); diff --git a/services/modules/transcription-remote/assembly.js b/services/modules/transcription-remote/assembly.js index 442d290..1e33e4f 100644 --- a/services/modules/transcription-remote/assembly.js +++ b/services/modules/transcription-remote/assembly.js @@ -1,4 +1,4 @@ -const API_KEY = process.env.ASSEMBLYAI_API_KEY; +let API_KEY const BASE_URL = 'https://api.assemblyai.com/v2'; //---------------------------------------------------Upload audio--------------------------------------------------- @@ -92,6 +92,7 @@ module.exports = { audioformat: "mp3", async function(audioFileName) { + API_KEY = process.env.ASSEMBLYAI_API_KEY; return new Promise(async (resolve, reject) => { try { // audioFileName ist nur "datei.mp3" From 1f96d90a2d690ebbda766a401379cadb737baeda Mon Sep 17 00:00:00 2001 From: MikeHughes-BIN Date: Thu, 15 Jan 2026 10:38:28 +0100 Subject: [PATCH 14/17] Remade replace Speaker module which now takes the premade speaker mapping json and directly replaces speakers in the document. Also refined the Standard documents so consistency is improved --- .DS_Store | Bin 6148 -> 6148 bytes main.js | 450 ++++++++++-------- .../modules/replace_speaker/replaceSpeaker.js | 85 ++-- storage/documentType/agenda.txt | 45 +- storage/documentType/custom_document.txt | 46 +- storage/documentType/followup_report.txt | 66 ++- storage/documentType/result_protocol.txt | 53 ++- storage/documentType/sprint_planning_note.txt | 60 ++- 8 files changed, 483 insertions(+), 322 deletions(-) diff --git a/.DS_Store b/.DS_Store index 5008ddfcf53c02e82d7eee2e57c38e5672ef89f6..433fa9002a9ae7b18882f27383ea56d5a216fd52 100644 GIT binary patch delta 121 zcmZoMXfc=|&e%3FQEZ}~q9`K+0|O8XFfb%Cq%)+J1sCPzeKJh@*W_At%4o20D8^1G8<`+@q1WGX^ TfYeMj;Zfe4AhLvcVgm~RE=&+7 diff --git a/main.js b/main.js index a2c7e34..597a7c6 100644 --- a/main.js +++ b/main.js @@ -1,81 +1,72 @@ // Loading required packages -require("./requires.js") +require("./requires.js"); console.log(start); - // Initialising map to be used to store the functionality later on for reloadability -mapFunctions = new Map() - +mapFunctions = new Map(); // Loading the Function Map -var path = `${mainDir}/services/modules` +var path = `${mainDir}/services/modules`; var folders = fs.readdirSync(path).filter(function (file) { - return fs.statSync(path+'/'+file).isDirectory(); + return fs.statSync(path + "/" + file).isDirectory(); }); -folders.forEach(element => { - var commandFiles = fs.readdirSync(`${path}/${element}`).filter(file => file.endsWith('.js') && !file.startsWith("index")); - for (const file of commandFiles) { - delete require.cache[require.resolve(`${path}/${element}/${file}`)]; - const command = require(`${path}/${element}/${file}`); - mapFunctions.set(command.name, command); - } +folders.forEach((element) => { + var commandFiles = fs + .readdirSync(`${path}/${element}`) + .filter((file) => file.endsWith(".js") && !file.startsWith("index")); + for (const file of commandFiles) { + delete require.cache[require.resolve(`${path}/${element}/${file}`)]; + const command = require(`${path}/${element}/${file}`); + mapFunctions.set(command.name, command); + } }); - - // The startup information for the project, here you can add stuff that might be nice to see when the app starts -mapFunctions.get("Startup_function").function() -console.log("------------------------------------ Status ------------------------------------"); +mapFunctions.get("Startup_function").function(); +console.log( + "------------------------------------ Status ------------------------------------" +); console.log(__dirname); console.log(platform); -console.log(`The Startup took ${new Date() - start}ms`) +console.log(`The Startup took ${new Date() - start}ms`); console.log(`${mapFunctions.size} Function modules loaded`); -console.log("--------------------------------------------------------------------------------"); - - - - - +console.log( + "--------------------------------------------------------------------------------" +); // --------------------------------------------------------- CLI COMMANDS --------------------------------------------------------- // const rl = readline.createInterface({ - input: process.stdin, - output: process.stdout + input: process.stdin, + output: process.stdout, }); - -rl.on("line", data =>{ - const args = data.trim().split(" "); - const command = args.shift().toLowerCase(); - mapFunctions.get("cliCommands").function(command, args) -}) - - - - +rl.on("line", (data) => { + const args = data.trim().split(" "); + const command = args.shift().toLowerCase(); + mapFunctions.get("cliCommands").function(command, args); +}); // ----------------------------------------------------------- ELECTRON ----------------------------------------------------------- // let mainWindow; function createWindow() { - mainWindow = new electron.BrowserWindow({ - width: 1200, - height: 800, - webPreferences: { - nodeIntegration: false, - contextIsolation: true, - preload: `${mainDir}/electron/main/preload.js` - } - }); + mainWindow = new electron.BrowserWindow({ + width: 1200, + height: 800, + webPreferences: { + nodeIntegration: false, + contextIsolation: true, + preload: `${mainDir}/electron/main/preload.js`, + }, + }); - mainWindow.loadFile('./electron/main/index.html'); + mainWindow.loadFile("./electron/main/index.html"); } electron.app.whenReady().then(createWindow); - // electron.ipcMain.on("extract", (event, args) => { // mapFunctions.get("extraction-video-to-audio").function(args) // }) @@ -84,26 +75,31 @@ electron.app.whenReady().then(createWindow); // mainWindow.webContents.send("fuck", "worked uwu") // }, 5000); -electron.ipcMain.handle('get-module-names', async () => { - let module_array = { - "ai_modules":[], - "transcription_modules":[] +electron.ipcMain.handle("get-module-names", async () => { + let module_array = { + ai_modules: [], + transcription_modules: [], + }; + mapFunctions.forEach((e) => { + switch (e.type) { + case "llm": + module_array.ai_modules.push({ + name: e.name, + displayname: e.displayname, + }); + break; + case "transcription": + module_array.transcription_modules.push({ + name: e.name, + displayname: e.displayname, + }); + break; } - mapFunctions.forEach(e => { - switch(e.type){ - case "llm": - module_array.ai_modules.push({"name": e.name, "displayname": e.displayname}) - break; - case "transcription": - module_array.transcription_modules.push({"name": e.name, "displayname": e.displayname}) - break; - } - }) - // console.log(module_array); - return module_array + }); + // console.log(module_array); + return module_array; }); - // electron.ipcMain.on("get_modules", async (event, args) => { // let module_array = { // "ai_modules":[], @@ -124,165 +120,219 @@ electron.ipcMain.handle('get-module-names', async () => { // mainWindow.webContents.send("modules", module_array) // }) -var globalArgs = {} -var globalFinalHtmlPath = "" +var globalArgs = {}; +var globalFinalHtmlPath = ""; electron.ipcMain.on("file_submit", async (event, args) => { - try { - globalArgs = args - let curstep = 0 - let totalsteps = 4 + try { + globalArgs = args; + let curstep = 0; + let totalsteps = 4; - const TEMPLATE_MAP = { - "followup-report": "followup_report.txt", - "agenda": "agenda.txt", - "result-protocol": "result_protocol.txt", - "sprint-planning": "sprint_planning_note.txt", - "custom": "custom_document.txt" - }; + const TEMPLATE_MAP = { + "followup-report": "followup_report.txt", + agenda: "agenda.txt", + "result-protocol": "result_protocol.txt", + "sprint-planning": "sprint_planning_note.txt", + custom: "custom_document.txt", + }; - const templateFile = TEMPLATE_MAP[args.document.type]; + const templateFile = TEMPLATE_MAP[args.document.type]; - if (!templateFile) { - throw new Error("Unknown document type: " + args.document.type); - } - - console.log(args); - let audiopath = "" - let transcriptpath = "" - - console.log("\n\n Running the Video to Audio Extractor"); - // This code handles the Video to Audio extraction module call - await mapFunctions.get("module-handler").function(args.video.module, {inputVideoPath: args.video.inputVideoPath, outputType: mapFunctions.get(args.transcription.module).audioformat}).then(resp => { - console.log(resp); - audiopath = resp - curstep++ - mainWindow.webContents.send("progress", {curstep:curstep, totalsteps:totalsteps}) - }).catch(err => { - mainWindow.webContents.send("error", err) - console.log(err); - return - }) - - - console.log("\n\n Running the Audio to Transcription module"); - // TODO implement transcription module - // This code handles the Audio to Text transcription module call - await mapFunctions.get("module-handler").function(args.transcription.module, audiopath).then(resp => { - console.log(resp); - transcriptpath = resp - curstep++ - mainWindow.webContents.send("progress", {curstep:curstep, totalsteps:totalsteps}) - }).catch(err => { - mainWindow.webContents.send("error", err) - console.log(err); - return - }) - - - console.log("\n\n Running the Transcription Summarizer module"); - // This code summarises the transcript, so that it can be used by an llm - // await mapFunctions.get("summarize-transcription").function('A:\\programing\\@projects\\video2document\\storage\\transcripts\\IMG_2978.json').then(resp => { - await mapFunctions.get("summarize-transcription2").function(transcriptpath).then(resp => { - console.log(resp); - transcriptpath = resp - curstep++ - mainWindow.webContents.send("progress", {curstep:curstep, totalsteps:totalsteps}) - }).catch(err => { - mainWindow.webContents.send("error", err) - console.log(err); - return - }) - - console.log("\n\n Running the LLM module"); - // TODO implement documentation module - // This code handles the Text to Document processing module call - - console.log(`\n\n Running the LLM for Document Style ${args.document.type}`); - - await mapFunctions.get("module-handler").function(args.document.module, { inputTranscriptPath: transcriptpath, documentTypePath: "./storage/documentType/" + templateFile, language: "en" }).then(resp => { - console.log(resp); - globalFinalHtmlPath = resp - curstep++ - mainWindow.webContents.send("progress", {curstep:curstep, totalsteps:totalsteps}) - }).catch(err => { - mainWindow.webContents.send("error", err) - console.log(err); - return - }) - - - await mapFunctions.get("extract-speaker-snippets").function({audioPath: audiopath, jsonPath: transcriptpath }).then(resp => { - mainWindow.webContents.send("speakerAudios", resp) - }).catch(err => { - mainWindow.webContents.send("error", err) - console.log(err); - return - }) - } catch (error) { - console.log(error); + if (!templateFile) { + throw new Error("Unknown document type: " + args.document.type); } -}) -electron.ipcMain.on("file_download", async() => { - await mapFunctions.get("htmlDocumentConverter").convert({inputPath:globalFinalHtmlPath, format: globalArgs.document.outputType, showDialog: true}); -}) + console.log(args); + let audiopath = ""; + let transcriptpath = ""; -electron.ipcMain.on("speaker_submit", async() => { - console.log("\n\n\nJa also hier kam was an \n\n\n"); -}) + console.log("\n\n Running the Video to Audio Extractor"); + // This code handles the Video to Audio extraction module call + await mapFunctions + .get("module-handler") + .function(args.video.module, { + inputVideoPath: args.video.inputVideoPath, + outputType: mapFunctions.get(args.transcription.module).audioformat, + }) + .then((resp) => { + console.log(resp); + audiopath = resp; + curstep++; + mainWindow.webContents.send("progress", { + curstep: curstep, + totalsteps: totalsteps, + }); + }) + .catch((err) => { + mainWindow.webContents.send("error", err); + console.log(err); + return; + }); -let q = -{ - video: { - module: "String", // The name of the module, idk if we ever implement other extraction modules, the default one is extraction-video-to-audio - inputVideoPath: "String", // See script.js on line 27 for an example of what this should look like - outputType: "String" // The file format to be used for the audio output file, such as wav, mp3, flac and so on - }, - transcription:{ - module: "String" // The module name of the transcription model you want to use - }, - document:{ - module: "String", // The module name of the AI model you want to use to create the document - styles: [ // An array of all the document styles/prompts you want to have the document be processed with - { - prompt: "String", - } - ] - } -} + console.log("\n\n Running the Audio to Transcription module"); + // TODO implement transcription module + // This code handles the Audio to Text transcription module call + await mapFunctions + .get("module-handler") + .function(args.transcription.module, audiopath) + .then((resp) => { + console.log(resp); + transcriptpath = resp; + curstep++; + mainWindow.webContents.send("progress", { + curstep: curstep, + totalsteps: totalsteps, + }); + }) + .catch((err) => { + mainWindow.webContents.send("error", err); + console.log(err); + return; + }); + console.log("\n\n Running the Transcription Summarizer module"); + // This code summarises the transcript, so that it can be used by an llm + // await mapFunctions.get("summarize-transcription").function('A:\\programing\\@projects\\video2document\\storage\\transcripts\\IMG_2978.json').then(resp => { + await mapFunctions + .get("summarize-transcription2") + .function(transcriptpath) + .then((resp) => { + console.log(resp); + transcriptpath = resp; + curstep++; + mainWindow.webContents.send("progress", { + curstep: curstep, + totalsteps: totalsteps, + }); + }) + .catch((err) => { + mainWindow.webContents.send("error", err); + console.log(err); + return; + }); + + console.log("\n\n Running the LLM module"); + // TODO implement documentation module + // This code handles the Text to Document processing module call + + console.log( + `\n\n Running the LLM for Document Style ${args.document.type}` + ); + + await mapFunctions + .get("module-handler") + .function(args.document.module, { + inputTranscriptPath: transcriptpath, + documentTypePath: "./storage/documentType/" + templateFile, + language: "en", + }) + .then((resp) => { + console.log(resp); + globalFinalHtmlPath = resp; + curstep++; + mainWindow.webContents.send("progress", { + curstep: curstep, + totalsteps: totalsteps, + }); + }) + .catch((err) => { + mainWindow.webContents.send("error", err); + console.log(err); + return; + }); + + await mapFunctions + .get("extract-speaker-snippets") + .function({ audioPath: audiopath, jsonPath: transcriptpath }) + .then((resp) => { + mainWindow.webContents.send("speakerAudios", resp); + }) + .catch((err) => { + mainWindow.webContents.send("error", err); + console.log(err); + return; + }); + } catch (error) { + console.log(error); + } +}); + +electron.ipcMain.on("file_download", async () => { + await mapFunctions + .get("htmlDocumentConverter") + .convert({ + inputPath: globalFinalHtmlPath, + format: globalArgs.document.outputType, + showDialog: true, + }); +}); + +electron.ipcMain.on("speaker_submit", async (event, args) => { + console.log("\n\n\nJa also hier kam was an \n\n\n"); + console.log(args); + + try { + await mapFunctions.get("replace_speaker").function(args); + event.reply("speaker_submit_response", { success: true }); + } catch (error) { + console.error("Error:", error); + event.reply("speaker_submit_response", { + success: false, + error: error.message, + }); + } +}); + +let q = { + video: { + module: "String", // The name of the module, idk if we ever implement other extraction modules, the default one is extraction-video-to-audio + inputVideoPath: "String", // See script.js on line 27 for an example of what this should look like + outputType: "String", // The file format to be used for the audio output file, such as wav, mp3, flac and so on + }, + transcription: { + module: "String", // The module name of the transcription model you want to use + }, + document: { + module: "String", // The module name of the AI model you want to use to create the document + styles: [ + // An array of all the document styles/prompts you want to have the document be processed with + { + prompt: "String", + }, + ], + }, +}; let q1 = { - "ai_modules": [ - {name:"abc", displayname:"ABC"}, - {name:"qeg", displayname:"aqghegahu"} - ], - "transcription_modules": [ - {name:"abc", displayname:"ABC"}, - {name:"qeg", displayname:"aqghegahu"} - ] -} + ai_modules: [ + { name: "abc", displayname: "ABC" }, + { name: "qeg", displayname: "aqghegahu" }, + ], + transcription_modules: [ + { name: "abc", displayname: "ABC" }, + { name: "qeg", displayname: "aqghegahu" }, + ], +}; //gibt Documentfiles an preload zurück -electron.ipcMain.handle('get-txt-files', () => { - const storagePath = `${mainDir}/storage/documentType` +electron.ipcMain.handle("get-txt-files", () => { + const storagePath = `${mainDir}/storage/documentType`; -return fs.readdirSync(storagePath) - .filter(f => f.endsWith('.txt')) + return fs.readdirSync(storagePath).filter((f) => f.endsWith(".txt")); }); //speichern neuer document types -electron.ipcMain.handle('save-txt-file', (event, fileName, content) => { +electron.ipcMain.handle("save-txt-file", (event, fileName, content) => { const filePath = `${mainDir}/storage/documentType/${fileName}.txt`; - fs.writeFileSync(filePath, content, 'utf8'); + fs.writeFileSync(filePath, content, "utf8"); return true; }); // -electron.ipcMain.handle('read-txt-file', (event, fileName) => { +electron.ipcMain.handle("read-txt-file", (event, fileName) => { const filePath = `${mainDir}/storage/documentType/${fileName}`; - return fs.readFileSync(filePath, 'utf8'); -}); \ No newline at end of file + return fs.readFileSync(filePath, "utf8"); +}); diff --git a/services/modules/replace_speaker/replaceSpeaker.js b/services/modules/replace_speaker/replaceSpeaker.js index 34a0e44..bcc36f8 100644 --- a/services/modules/replace_speaker/replaceSpeaker.js +++ b/services/modules/replace_speaker/replaceSpeaker.js @@ -7,35 +7,54 @@ const module_exports = { displayname: "Speaker Name Replacer", description: "Replaces speaker placeholder names with actual names based on a mapping in HTML files", - async function({ inputHtmlPath, speakerMappingPath }) { - return await this.replaceNames(inputHtmlPath, speakerMappingPath); + async function(speakerMapping) { + // Relativ von dieser Datei aus + const documentsDir = path.resolve(__dirname, '../../../storage/documents'); + const inputHtmlPath = await this.getNewestFile(documentsDir, '.html'); + + if (!inputHtmlPath) { + throw new Error(`No HTML files found in ${documentsDir}`); + } + + return await this.replaceNames(inputHtmlPath, speakerMapping); }, - replaceNames: async function(inputHtmlPath, speakerMappingPath) { + getNewestFile: async function(dirPath, extension) { + try { + const files = await fs.promises.readdir(dirPath); + const filtered = files.filter(f => f.endsWith(extension)); + + if (filtered.length === 0) return null; + + const filesWithStats = await Promise.all( + filtered.map(async (f) => { + const fullPath = path.join(dirPath, f); + const stats = await fs.promises.stat(fullPath); + return { path: fullPath, time: stats.mtimeMs }; + }) + ); + + return filesWithStats.reduce((newest, curr) => + curr.time > newest.time ? curr : newest + ).path; + } catch (error) { + console.error("Error reading directory:", error); + throw error; + } + }, + + replaceNames: async function(inputHtmlPath, speakerMapping) { try { const htmlContent = await fs.promises.readFile(inputHtmlPath, "utf-8"); - const mappingData = await fs.promises.readFile(speakerMappingPath, "utf-8"); - - let speakerMap = {}; - try { - const parsed = JSON.parse(mappingData); - if (parsed.speakerId && parsed.speakerName) { - speakerMap[parsed.speakerId] = parsed.speakerName; - } else { - Object.assign(speakerMap, parsed); - } - } 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; - }); - } 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); + Object.entries(speakerMapping).forEach(([placeholder, value]) => { + // Extract name if value is an object + const displayName = typeof value === 'string' ? value : value.name; + + // Replace speaker placeholder with display name + const regex = new RegExp(`\\b${placeholder}\\b`, 'g'); + outputContent = outputContent.replace(regex, displayName); }); await fs.promises.writeFile(inputHtmlPath, outputContent, "utf-8"); @@ -49,22 +68,4 @@ const module_exports = { } }; -module.exports = module_exports; - -if (require.main === module) { - (async () => { - const args = process.argv.slice(2); - if (args.length < 2) process.exit(1); - - const [inputHtmlPath, speakerMappingPath] = args; - - 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); - } - })(); -} \ No newline at end of file +module.exports = module_exports; \ No newline at end of file diff --git a/storage/documentType/agenda.txt b/storage/documentType/agenda.txt index 02d4d47..66764db 100644 --- a/storage/documentType/agenda.txt +++ b/storage/documentType/agenda.txt @@ -1,26 +1,45 @@ +# Meeting Agenda Generator + Du bist ein erfahrener Moderator und Projektmanager. -AUFGABE: -Erstelle eine sinnvolle Meeting-Agenda basierend auf dem folgenden Transkript. +## KRITISCH - SPRECHER-PLATZHALTER BEWAHREN: +**speakerA, speakerB, speakerC, etc. sind technische Platzhalter und DÜRFEN NICHT verändert werden.** +- RICHTIG: "speakerA hat entschieden..." +- FALSCH: "Speaker A", "speakerA (Name)", Klammern, Leerzeichen, Ergänzungen -ANFORDERUNGEN: +Diese Token werden später durch echte Namen ersetzt. Jede Änderung bricht diesen Prozess. + +--- + +## AUFGABE: +Erstelle eine sinnvolle Meeting-Agenda basierend auf dem Meeting-Transkript. + +## ANFORDERUNGEN: - Rekonstruiere die tatsächlichen Themenblöcke - Ordne sie logisch und chronologisch - Fasse ähnliche Diskussionen zusammen - Keine irrelevanten Details aufnehmen +- Speicher-Platzhalter (speakerA, speakerB, etc.) exakt beibehalten -STRUKTUR: -- Titel der Agenda -- Ziel des Meetings (1–2 Sätze) -- Agenda-Punkte (nummeriert) - - Thema - - Kurzbeschreibung - - Ziel des Punktes (Information, Entscheidung, Diskussion) +## STRUKTUR: -STIL: +### 1. Titel der Agenda + +### 2. Ziel des Meetings +- 1–2 präzise Sätze + +### 3. Agenda-Punkte (nummeriert) +Für jeden Punkt: +- **Thema** +- **Kurzbeschreibung** +- **Ziel des Punktes:** (Information / Entscheidung / Diskussion) + +## STIL: - Klar, kompakt - Business-orientiert - Keine Sprecher- oder Zeitangaben -- Namen aus dem Transkript speakerA, speakerB etc. sollen weiterhin bestehen bleiben wie sie sind und nicht im Dokument ersetzt werden +- Logische Reihenfolge -TRANSKRIPT: +--- + +**TRANSKRIPT:** \ No newline at end of file diff --git a/storage/documentType/custom_document.txt b/storage/documentType/custom_document.txt index 58cc983..075d962 100644 --- a/storage/documentType/custom_document.txt +++ b/storage/documentType/custom_document.txt @@ -1,22 +1,44 @@ +# Custom Document Generator + Du bist ein intelligenter Dokumenten-Generator. -AUFGABE: +## KRITISCH - SPRECHER-PLATZHALTER BEWAHREN: +**speakerA, speakerB, speakerC, etc. sind technische Platzhalter und DÜRFEN NICHT verändert werden.** +- RICHTIG: "speakerA hat entschieden..." +- FALSCH: "Speaker A", "speakerA (Name)", Klammern, Leerzeichen, Ergänzungen + +Diese Token werden später durch echte Namen ersetzt. Jede Änderung bricht diesen Prozess. + +--- + +## AUFGABE: Erstelle ein individuelles Dokument basierend auf: -1) dem Meeting-Transkript -2) der zusätzlichen Nutzeranweisung +1. dem Meeting-Transkript +2. der zusätzlichen Nutzeranweisung -WICHTIG: -- Priorisiere die Nutzeranweisung +## ANFORDERUNGEN: +- **Priorisiere die Nutzeranweisung** über standardisierte Strukturen - Nutze das Transkript als Wissensquelle -- Struktur, Tonalität und Detailgrad anpassen +- Struktur, Tonalität und Detailgrad nach Nutzervorgabe anpassen - Inhalte logisch zusammenführen -- Namen aus dem Transkript speakerA, speakerB etc. sollen weiterhin bestehen bleiben wie sie sind und nicht im Dokument ersetzt werden +- Speicher-Platzhalter (speakerA, speakerB, etc.) exakt beibehalten -FORMAT: -- Passe Struktur und Stil an den Nutzerwunsch an -- Klare Überschriften -- Keine Sprecher- oder Zeitangaben +## VORGEHEN: +1. Lese die Nutzeranweisung sorgfältig +2. Extrahiere aus dem Transkript die relevanten Informationen +3. Erstelle das Dokument nach der gewünschten Struktur +4. Behalte Sprecher-Platzhalter unverändert -TRANSKRIPT & NUTZERANWEISUNG: +## ALLGEMEINE RICHTLINIEN: +- Entferne Redundanzen und Smalltalk +- Keine Zeitstempel oder Sprecherangaben (wenn nicht gefordert) +- Sachlich, präzise, professionell +- Keine Spekulationen oder Meinungen (wenn nicht gefordert) +--- +**NUTZERANWEISUNG:** +[Hier kommt die Anforderung des Nutzers hin] + +**TRANSKRIPT:** +[Hier kommt das Meeting-Transkript hin] \ No newline at end of file diff --git a/storage/documentType/followup_report.txt b/storage/documentType/followup_report.txt index 048d05d..1db3ed3 100644 --- a/storage/documentType/followup_report.txt +++ b/storage/documentType/followup_report.txt @@ -1,45 +1,61 @@ +# Follow-up Report Generator + Du bist ein professioneller Meeting-Analyst und Business Writer. -AUFGABE: -Erstelle einen strukturierten Follow-up Report basierend auf dem folgenden Meeting-Transkript. +## KRITISCH - SPRECHER-PLATZHALTER BEWAHREN: +**speakerA, speakerB, speakerC, etc. sind technische Platzhalter und DÜRFEN NICHT verändert werden.** +- RICHTIG: "speakerA hat entschieden..." +- FALSCH: "Speaker A", "speakerA (Name)", Klammern, Leerzeichen, Ergänzungen -ANFORDERUNGEN: +Diese Token werden später durch echte Namen ersetzt. Jede Änderung bricht diesen Prozess. + +--- + +## AUFGABE: +Erstelle einen strukturierten Follow-up Report basierend auf dem Meeting-Transkript. + +## ANFORDERUNGEN: - Fasse Inhalte sinngemäß zusammen - Entferne Redundanzen und Smalltalk - Formuliere klar, präzise und professionell - Verwende neutrale Business-Sprache -- Keine Zeitstempel oder Sprecher-Namen zitieren - Leite Entscheidungen und Aufgaben logisch ab, wenn sie implizit sind - Markiere offene Punkte klar -- Namen aus dem Transkript speakerA, speakerB etc. sollen weiterhin bestehen bleiben wie sie sind und nicht im Dokument ersetzt werden +- Speicher-Platzhalter (speakerA, speakerB, etc.) exakt beibehalten -STRUKTUR DES DOKUMENTS: -1. Titel & Metadaten - - Meetingtitel (ableiten) - - Datum (falls im Transkript erwähnt, sonst „nicht angegeben“) - - Teilnehmer (zusammengefasst) +## STRUKTUR DES DOKUMENTS: -2. Executive Summary (max. 5 Bullet Points) +### 1. Titel & Metadaten +- Meetingtitel (ableiten) +- Datum (falls im Transkript erwähnt, sonst „nicht angegeben") +- Teilnehmer (zusammengefasst) -3. Besprochene Themen - - Thema - - Kernaussagen - - Relevante Erkenntnisse +### 2. Executive Summary +- Max. 5 Bullet Points +- Kernpunkte zusammenfassen -4. Entscheidungen - - Entscheidung - - Kontext / Begründung +### 3. Besprochene Themen +- Thema +- Kernaussagen +- Relevante Erkenntnisse -5. Action Items - - Aufgabe - - Verantwortlich (falls ableitbar) - - Ziel / Zweck +### 4. Entscheidungen +- Entscheidung +- Kontext / Begründung -6. Offene Fragen & Risiken +### 5. Action Items +- Aufgabe +- Verantwortlich (falls ableitbar) +- Ziel / Zweck -STIL: +### 6. Offene Fragen & Risiken + +## STIL: - Überschriften klar strukturiert - Bullet Points bevorzugen - Präzise, keine Umgangssprache +- Keine Zeitstempel oder Sprecherangaben -TRANSKRIPT: +--- + +**TRANSKRIPT:** \ No newline at end of file diff --git a/storage/documentType/result_protocol.txt b/storage/documentType/result_protocol.txt index 867289f..0cc8ae6 100644 --- a/storage/documentType/result_protocol.txt +++ b/storage/documentType/result_protocol.txt @@ -1,27 +1,54 @@ +# Result Protocol Generator + Du bist ein professioneller Protokollführer. -AUFGABE: +## KRITISCH - SPRECHER-PLATZHALTER BEWAHREN: +**speakerA, speakerB, speakerC, etc. sind technische Platzhalter und DÜRFEN NICHT verändert werden.** +- RICHTIG: "speakerA hat entschieden..." +- FALSCH: "Speaker A", "speakerA (Name)", Klammern, Leerzeichen, Ergänzungen + +Diese Token werden später durch echte Namen ersetzt. Jede Änderung bricht diesen Prozess. + +--- + +## AUFGABE: Erstelle ein Ergebnisprotokoll basierend auf dem Meeting-Transkript. -FOKUS: +## FOKUS: - Ergebnisse statt Diskussionen - Entscheidungen, Beschlüsse, Vereinbarungen - Klare, überprüfbare Aussagen +- Speicher-Platzhalter (speakerA, speakerB, etc.) exakt beibehalten -STRUKTUR: -1. Meeting-Informationen -2. Ergebnisse je Thema - - Thema - - Ergebnis / Beschluss -3. Entscheidungen -4. Aufgaben & Verantwortlichkeiten -5. Offene Punkte +## STRUKTUR: -REGELN: +### 1. Meeting-Informationen +- Titel +- Datum +- Teilnehmer + +### 2. Ergebnisse je Thema +- **Thema** +- **Ergebnis / Beschluss** + +### 3. Entscheidungen +- Klare Entscheidungen mit Kontext + +### 4. Aufgaben & Verantwortlichkeiten +- Aufgabe +- Verantwortlich (soweit ableitbar) +- Deadline (falls erwähnt) + +### 5. Offene Punkte +- Ungelöste Fragen +- Ausstehende Klärungen + +## REGELN: - Keine Meinungen oder Spekulationen - Keine Zeit- oder Sprecherangaben - Sachlich, formal -- Namen aus dem Transkript speakerA, speakerB etc. sollen weiterhin bestehen bleiben wie sie sind und nicht im Dokument ersetzt werden +- Fokus auf Fakten und Ergebnisse +--- -TRANSKRIPT: +**TRANSKRIPT:** \ No newline at end of file diff --git a/storage/documentType/sprint_planning_note.txt b/storage/documentType/sprint_planning_note.txt index 0193922..5ab5fe0 100644 --- a/storage/documentType/sprint_planning_note.txt +++ b/storage/documentType/sprint_planning_note.txt @@ -1,35 +1,61 @@ +# Sprint Planning Note Generator + Du bist ein erfahrener Scrum Master. -AUFGABE: -Erstelle Sprint Planning Notes aus dem folgenden Meeting-Transkript. +## KRITISCH - SPRECHER-PLATZHALTER BEWAHREN: +**speakerA, speakerB, speakerC, etc. sind technische Platzhalter und DÜRFEN NICHT verändert werden.** +- RICHTIG: "speakerA hat entschieden..." +- FALSCH: "Speaker A", "speakerA (Name)", Klammern, Leerzeichen, Ergänzungen -FOKUS: +Diese Token werden später durch echte Namen ersetzt. Jede Änderung bricht diesen Prozess. + +--- + +## AUFGABE: +Erstelle Sprint Planning Notes aus dem Meeting-Transkript. + +## FOKUS: - Sprint-Ziele - User Stories / Tasks - Abhängigkeiten - Risiken - Commitments +- Speicher-Platzhalter (speakerA, speakerB, etc.) exakt beibehalten -STRUKTUR: -1. Sprint Overview - - Sprint-Ziel - - Zeitraum (falls erwähnt) +## STRUKTUR: -2. Geplante Arbeit - - User Story / Task - - Beschreibung - - Akzeptanzkriterien (falls ableitbar) +### 1. Sprint Overview +- **Sprint-Ziel:** (Kurz und prägnant) +- **Zeitraum:** (Falls im Transkript erwähnt) -3. Abhängigkeiten & Blocker +### 2. Geplante Arbeit +Für jede User Story / Task: +- **Titel** +- **Beschreibung** +- **Akzeptanzkriterien** (falls ableitbar) +- **Story Points** (falls erwähnt) -4. Risiken & Annahmen +### 3. Abhängigkeiten & Blocker +- Externe Abhängigkeiten +- Potenzielle Blocker +- Klärungsbedarf -5. Vereinbarungen / Team-Commitments +### 4. Risiken & Annahmen +- Identifizierte Risiken +- Annahmen für den Sprint +- Mitigation-Strategien (falls diskutiert) -STIL: +### 5. Team-Commitments / Vereinbarungen +- Vereinbarte Commitments +- Rollen und Verantwortlichkeiten +- Definition of Done (falls diskutiert) + +## STIL: - Agile-konform - Klar & umsetzungsorientiert - Bullet Points bevorzugen -- Namen aus dem Transkript speakerA, speakerB etc. sollen weiterhin bestehen bleiben wie sie sind und nicht im Dokument ersetzt werden +- Keine Zeitstempel oder Sprecherangaben -TRANSKRIPT: +--- + +**TRANSKRIPT:** \ No newline at end of file From e40be401c890a201ad582fc77c70c3352954eb0c Mon Sep 17 00:00:00 2001 From: MikeHughes-BIN Date: Thu, 15 Jan 2026 10:45:32 +0100 Subject: [PATCH 15/17] Case sensitivity doesnt matter anymore --- services/modules/replace_speaker/replaceSpeaker.js | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/services/modules/replace_speaker/replaceSpeaker.js b/services/modules/replace_speaker/replaceSpeaker.js index bcc36f8..6f21cdb 100644 --- a/services/modules/replace_speaker/replaceSpeaker.js +++ b/services/modules/replace_speaker/replaceSpeaker.js @@ -52,8 +52,16 @@ const module_exports = { // Extract name if value is an object const displayName = typeof value === 'string' ? value : value.name; - // Replace speaker placeholder with display name - const regex = new RegExp(`\\b${placeholder}\\b`, 'g'); + // Normalize placeholder for matching (remove case sensitivity) + const normalizedPlaceholder = placeholder.toLowerCase(); + + // Replace all variations: speakerA, SpeakerA, SPEAKERA, speaker_a, Speaker A, etc. + // Matches with optional spaces, underscores, and parentheses + const regex = new RegExp( + `\\b[Ss]peaker\\s*[_-]?\\s*${placeholder.charAt(placeholder.length - 1)}\\b|\\b${placeholder}\\b`, + 'gi' + ); + outputContent = outputContent.replace(regex, displayName); }); From 969643414544a615861f6dbf8d1e8de13e6b5498 Mon Sep 17 00:00:00 2001 From: MikeHughes-BIN Date: Thu, 15 Jan 2026 16:38:59 +0100 Subject: [PATCH 16/17] Enhance module retrieval by categorizing AI and transcription modules --- main.js | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/main.js b/main.js index 1a0f530..23fe5fa 100644 --- a/main.js +++ b/main.js @@ -110,9 +110,18 @@ electron.ipcMain.handle('get-module-names', async () => { "ai_modules":[], "transcription_modules":[] } - }); - // console.log(module_array); - return module_array; + mapFunctions.forEach(e => { + switch(e.type){ + case "llm": + module_array.ai_modules.push({"name": e.name, "displayname": e.displayname}) + break; + case "transcription": + module_array.transcription_modules.push({"name": e.name, "displayname": e.displayname}) + break; + } + }) + // console.log(module_array); + return module_array }); // electron.ipcMain.on("get_modules", async (event, args) => { From df5ac8913df6dbf7a6ac6978ca0da7558ba4208e Mon Sep 17 00:00:00 2001 From: Emily Date: Thu, 15 Jan 2026 16:54:09 +0100 Subject: [PATCH 17/17] fixed pipeline --- main.js | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/main.js b/main.js index 23fe5fa..ceb787d 100644 --- a/main.js +++ b/main.js @@ -195,6 +195,29 @@ electron.ipcMain.on("file_submit", async (event, args) => { }); + console.log("\n\n Running the Audio to Transcription module"); + // TODO implement transcription module + // This code handles the Audio to Text transcription module call + await mapFunctions + .get("module-handler") + .function(args.transcription.module, audiopath) + .then((resp) => { + console.log(resp); + transcriptpath = resp; + curstep++; + mainWindow.webContents.send("progress", { + curstep: curstep, + totalsteps: totalsteps, + }); + }) + .catch((err) => { + mainWindow.webContents.send("error", err); + console.log(err); + return; + }); + + + console.log("\n\n Running the Transcription Summarizer module"); // This code summarises the transcript, so that it can be used by an llm // await mapFunctions.get("summarize-transcription").function('A:\\programing\\@projects\\video2document\\storage\\transcripts\\IMG_2978.json').then(resp => {