Compare commits

..

51 Commits

Author SHA1 Message Date
Hughes, Mike b6f887466b Merge branch 'develop' into 'feature/speaker_renaming'
# Conflicts:
#   main.js
2026-01-15 13:06:07 +01:00
MikeHughes-BIN e40be401c8 Case sensitivity doesnt matter anymore 2026-01-15 10:45:32 +01:00
MikeHughes-BIN 1f96d90a2d 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 2026-01-15 10:38:28 +01:00
Spanier, Pit 33ce22ec45 Merge branch 'feature/authentication-implementation' into 'develop'
implemented initial version of the authentication system

See merge request proj-wise2526-video2document/video2document!88
2026-01-15 09:59:29 +01:00
Spanier, Pit b9f3967b61 Merge branch 'feature/documentation' into 'develop'
Feature/documentation

See merge request proj-wise2526-video2document/video2document!87
2026-01-15 09:58:38 +01:00
Hughes, Mike 8cd654bcbc Merge branch 'feature/45-extend-readme-s5-01' into 'develop'
Feature/45 extend readme s5 01

See merge request proj-wise2526-video2document/video2document!86
2026-01-15 09:57:56 +01:00
emily 6b3724b765 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
2026-01-14 03:01:30 +01:00
emily 5a85a7da4c 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
2026-01-14 02:57:51 +01:00
emily 80eb9dac23 removed console.log from the .env file loader 2026-01-14 02:56:14 +01:00
Aarthi Manivannan, Premanathan Aarthi Manivannan 641b17820f Update file README.md 2026-01-12 21:40:11 +01:00
MikeHughes-BIN 11f7db1b1c speaker-renaming module created 2026-01-12 19:07:42 +01:00
Minning, Eric 947e981ac0 Merge branch 'feature/ui-test' into 'develop'
Implemented the script part from the custom_document.html file into the base...

See merge request proj-wise2526-video2document/video2document!85
2026-01-12 18:42:43 +01:00
eric.minning bef9c44a2f Fixed a smal error in the document generation button of the costum document creation section 2026-01-12 18:37:39 +01:00
Verena Schulz 60b9f8a3b9 Small style changes 2026-01-12 17:15:38 +01:00
Aarthi Manivannan, Premanathan Aarthi Manivannan 0010e400b5 Update file README.md 2026-01-11 19:35:15 +01:00
eric.minning 4dd7b512a1 Expanded the function to change the language of the program. Added the dedicated language dropdown list 2026-01-11 15:56:21 +01:00
Verena Schulz d21bbbce12 Implemented the visibility regulation of the custom document section 2026-01-10 19:51:29 +01:00
eric.minning 7bce7c8d36 Implemented the script part from the custom_document.html file into the base script.js and renderer.js 2026-01-10 19:04:37 +01:00
Verena Schulz 3c3d7a1d3e Joined custom_document html and css elements with index and style 2026-01-10 18:08:21 +01:00
Heyne, Stefan Norbert Robert 1c85272abc Merge branch 'feature/customdocument26' into 'feature/ui-test'
delete button and function implemented

See merge request proj-wise2526-video2document/video2document!84
2026-01-10 14:41:13 +01:00
Heyne, Stefan Norbert Robert 18b624736d Merge branch 'feature/ui-test' into 'feature/customdocument26'
# Conflicts:
#   electron/main/custom_document.html
#   electron/main/index.html
2026-01-10 14:38:40 +01:00
Heyne, Stefan Norbert Robert 4bd6804567 Merge branch 'feature/customdocument26' into 'main'
delete button and function implemented

See merge request proj-wise2526-video2document/video2document!83
2026-01-10 14:29:42 +01:00
santa 12f99e9f2b delete button and function implemented, help page 2026-01-10 14:22:27 +01:00
emily 6c41da781e initial version of the software documentation 2026-01-08 20:37:06 +01:00
emily 9b8561e4ce euh, removed another unneeded variable .-. 2026-01-08 19:33:10 +01:00
emily 39af2c3312 removed unneeded variable in main.js 2026-01-08 19:32:23 +01:00
emily 9ad335d872 just a line removed so i have something to push to see if the branch creation succeeded 2026-01-08 19:19:09 +01:00
eric.minning c833d5bb74 Fixed errors and added the loading of step 3 document types 2026-01-08 17:18:43 +01:00
Azeufack Noupeu, Willy 7647398974 Merge branch 'feature/s4-07-parakeet' into 'develop'
S4-07 – Local Parakeet transcription module (Python + NeMo)

See merge request proj-wise2526-video2document/video2document!54
2026-01-08 15:50:47 +01:00
Heyne, Stefan Norbert Robert 43e21cfd8d Merge branch 'feature/customdocument26' into 'feature/ui-test'
Add start scripts for Windows and Unix environments that checks if node is...

See merge request proj-wise2526-video2document/video2document!82
2026-01-08 15:41:36 +01:00
Heyne, Stefan Norbert Robert 4c1897741f Merge branch 'feature/customdocument26' into 'develop'
custom document backend implemented

See merge request proj-wise2526-video2document/video2document!81
2026-01-08 15:39:49 +01:00
eric.minning f3cc6b5798 Changed the language selection so it also shows the flags 2026-01-08 15:32:30 +01:00
Azeufack Noupeu Willy 7e4cf93cae Merge branch 'feature/s4-07-parakeet' of https://gitlab.rlp.net/proj-wise2526-video2document/video2document into feature/s4-07-parakeet 2026-01-08 15:26:59 +01:00
Azeufack Noupeu Willy fe86fa1a2f Implemented local Parakeet transcription module (S4-07) 2026-01-08 15:09:59 +01:00
Hughes, Mike cf56947c3b Merge branch 'feature/customdocument26' into 'main'
custom document backend implemented

See merge request proj-wise2526-video2document/video2document!80
2026-01-08 14:32:35 +01:00
santa ed5a86dce0 custom document backend implemented 2026-01-08 14:25:14 +01:00
Azeufack Noupeu Willy 1e4bde93b1 Merge branch 'develop' of https://gitlab.rlp.net/proj-wise2526-video2document/video2document into develop 2026-01-08 13:46:41 +01:00
Hughes, Mike c62ee5aa41 Merge branch 'fix/remove-misleading-env-file' into 'develop'
env example is in UTF 16 which, when copied and used as the .env, is...

See merge request proj-wise2526-video2document/video2document!79
2026-01-07 21:15:29 +01:00
Verena Schulz 0a5fa79668 Added comments and to do 2026-01-07 17:34:14 +01:00
eric.minning 30b94c4e62 Polished the code in the script.js and renderer.js 2026-01-07 16:14:22 +01:00
eric.minning cc883905bc Expanded the change languages file and function 2026-01-06 14:03:28 +01:00
Verena Schulz 40edcaa69d Some changes for step 5 2026-01-04 21:33:57 +01:00
Verena Schulz c7625e5713 Fixed download button positioning 2026-01-04 21:20:44 +01:00
Verena Schulz f5c2e2700b Added headings, some finetuning 2025-12-30 16:53:25 +01:00
MikeHughes-BIN 9c156a7df3 env example is in UTF 16 which, when copied and used as the .env, is unreadable and breaks the Programm 2025-12-23 13:48:27 +01:00
Hughes, Mike 797898fb8b Merge branch 'develop' into 'main'
Develop

See merge request proj-wise2526-video2document/video2document!78
2025-12-22 14:27:25 +01:00
Minning, Eric f81c9bc4e8 Merge branch 'fix/add-error-logging-main' into 'develop'
Add error logging to file submission process

See merge request proj-wise2526-video2document/video2document!77
2025-12-22 14:25:29 +01:00
Minning, Eric 7b630646cc Merge branch 'develop' into 'main'
Develop

See merge request proj-wise2526-video2document/video2document!76
2025-12-22 14:25:10 +01:00
Azeufack Noupeu Willy ee31d26116 Implemented local Parakeet transcription module (S4-07) 2025-12-11 14:52:48 +01:00
Azeufack Noupeu Willy a13fea6734 Merge branch 'develop' of https://gitlab.rlp.net/proj-wise2526-video2document/video2document into develop 2025-12-06 14:51:45 +01:00
Azeufack Noupeu Willy e82cf779da Merge branch 'main' of https://gitlab.rlp.net/proj-wise2526-video2document/video2document into develop 2025-12-06 14:48:31 +01:00
25 changed files with 2071 additions and 855 deletions
Vendored
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
+323 -1
View File
@@ -53,4 +53,326 @@ Make sure that **ffmpeg** is installed on your system, as it may be required for
--- ---
**Installation complete!** You're now ready to use Video2Document. **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 Googles 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
+116
View File
@@ -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.
+5 -149
View File
@@ -1,161 +1,17 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="de"> <html lang="de">
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Custom Document</title> <title>Custom Document</title>
<style> <style>
body {
font-family: Arial, sans-serif;
background: #f0f2f5;
margin: 0;
padding: 0;
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
}
.container {
background: white;
padding: 30px;
margin-top: 50px;
border-radius: 12px;
box-shadow: 0 4px 20px rgba(0,0,0,0.1);
width: 90%;
max-width: 600px;
}
h1 {
text-align: center;
color: #333;
}
label {
font-weight: bold;
margin-top: 15px;
display: block;
color: #555;
}
input[type="text"], textarea, select {
width: 100%;
padding: 10px;
margin-top: 5px;
border-radius: 6px;
border: 1px solid #ccc;
font-size: 14px;
}
textarea {
height: 120px;
resize: vertical;
}
.buttons {
display: flex;
justify-content: space-between;
margin-top: 25px;
}
button {
padding: 10px 20px;
font-size: 14px;
border: none;
border-radius: 6px;
cursor: pointer;
transition: 0.2s;
background-color: #007BFF;
color: white;
}
button:hover {
background-color: #0056b3;
}
@media (max-width: 500px) {
.buttons {
flex-direction: column;
}
.buttons button {
width: 100%;
margin-top: 10px;
}
}
#result {
margin-top: 20px;
color: #333;
word-break: break-word;
}
</style> </style>
</head> </head>
<body> <body>
<div class="container"> <script src="languages.js"></script>
<h1>Custom Document Generator</h1>
<label for="docName">Dokumentname:</label>
<input type="text" id="docName" placeholder="Gib hier den Dokumentnamen ein">
<label for="existingDocs">Vorhandene Dokumente auswählen (optional):</label>
<select id="existingDocs">
<option value="">-- Neues Dokument erstellen --</option>
<option value="meeting_report_001">Meeting Report 001</option>
<option value="summary_01">Summary 01</option>
<option value="project_plan_A">Project Plan A</option>
</select>
<label for="prompt">Dein Prompt:</label>
<textarea id="prompt" placeholder="Schreibe hier den Prompt für dein Dokument..."></textarea>
<div class="buttons">
<a href="index.html">
<button id="goBackBtn">Abbrechen</button>
</a>
<button id="generateBtn">Dokument speichern</button>
</div>
<div id="result"></div>
</div>
<script>
const goBackBtn = document.getElementById("goBackBtn");
const generateBtn = document.getElementById("generateBtn");
const existingDocs = document.getElementById("existingDocs");
const docNameInput = document.getElementById("docName");
const promptInput = document.getElementById("prompt");
const resultDiv = document.getElementById("result");
// Zurück zur Haupt-GUI
goBackBtn.addEventListener("click", () => {
window.electronAPI.goBackToMain();
});
// Generiere Dokument
generateBtn.addEventListener("click", () => {
const prompt = promptInput.value.trim();
let docName = docNameInput.value.trim();
const selectedExisting = existingDocs.value;
if (!prompt) {
alert("Bitte gib einen Prompt ein!");
return;
}
// Wenn ein vorhandenes Dokument ausgewählt wurde, hängt der Prompt daran
if (selectedExisting) {
docName = selectedExisting; // prompt wird an vorhandenes Dokument angehängt
} else if (!docName) {
alert("Bitte gib einen Dokumentnamen ein, wenn du ein neues Dokument erstellen möchtest!");
return;
}
// Demo-Ausgabe im Result-Div
resultDiv.innerHTML = `<strong>Dokumentname:</strong> ${docName}<br><strong>Prompt:</strong> ${prompt}`;
// Hier kannst du den Prompt an dein LLM oder Module-Handler senden
// z.B. window.submit.submit({documentName: docName, prompt: prompt})
});
</script>
</body> </body>
</html>
</html>
+66 -18
View File
@@ -5,6 +5,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<title id="title">Video to document</title> <title id="title">Video to document</title>
<link rel="stylesheet" href="style.css"> <link rel="stylesheet" href="style.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/lc-select@1.3.0/themes/light.css">
</head> </head>
<body> <body>
@@ -19,30 +20,66 @@
</label> </label>
<nav class="menu1"> <nav class="menu1">
<a href="custom_document.html" class="li1">Custom document</a> <button id="customDocBtn" onclick="showCD()">Manage document types</button>
<a href="" class="li1">Help</a> <a href="help_page.html" class="li1">Help</a>
</nav> </nav>
</nav> </nav>
</section> </section>
<h1 id="h1">Video to document</h1> <h1 id="h1">Video to document</h1>
<div class="gui-language">
<!-- to do: Ausprobieren mit li, a oder button, im Notfall ohne Flaggen Icons, kein hover-->
<select name="language_option" id="language_option"></select>
</div>
</div> </div>
<div class="step-nav"> <div class="step-nav">
<div class="step-item active" data-step="1">1. Step</div> <div class="step-item active" data-step="1" id="step_nav1">1. Step</div>
<div class="step-item" data-step="2">2. Step</div> <div class="step-item" data-step="2" id="step_nav2">2. Step</div>
<div class="step-item" data-step="3">3. Step</div> <div class="step-item" data-step="3" id="step_nav3">3. Step</div>
<div class="step-item" data-step="4">4. Step</div> <div class="step-item" data-step="4" id="step_nav4">4. Step</div>
<div class="step-item" data-step="5">5. Step</div> <div class="step-item" data-step="5" id="step_nav5">5. Step</div>
<div class="step-item" data-step="6">6. Step</div> <div class="step-item" data-step="6" id="step_nav6">6. Step</div>
</div> </div>
<div id="middleContainerWrapper" class="middle-container-wrapper"> <div id="middleContainerWrapper" class="middle-container-wrapper">
<button id="prevBtn" class="navBtn" disabled>&larr;</button> <button id="prevBtn" class="navBtn" disabled>&larr;</button>
<!-- Visible middle part-->
<div class="mitte" id="mitte"> <div class="mitte" id="mitte">
<!--Costum document section-->
<div class="container" id="cdContainer" style="display:none;">
<h1 id="cd_h1">Manage document types</h1>
<label for="existingDocs" id="cd_existingDocs">Select existing documents (optional):</label>
<!--Drop Down-->
<select name="existingDocs" id="existingDocs">
<option value="newDoc" id="newDoc">-- Create new document --</option>
</select>
<div id="docNameWrapper">
<label for="docName" id="cd_docName">Document name:</label>
<input type="text" id="docName" placeholder="Enter the document name here">
</div>
<label for="prompt" id="cd_promt">Your prompt:</label>
<textarea id="prompt" placeholder="Type the prompt for your document here..."></textarea>
<div class="buttons">
<button id="goBackBtn">Return</button>
<button id="deleteBtn">Delete document</button>
<button id="generateBtn">Save document</button>
</div>
<div id="result"></div>
</div>
<!-- Here starts code from step 1-->
<div class="step" id="step1"> <div class="step" id="step1">
<h2 class="h2">Upload your video here:</h2>
<div class="upload-container" id="uploadContainer"> <div class="upload-container" id="uploadContainer">
<p id="p1">Drag and drop video file</p> <p id="p1">Drag and drop video file</p>
<video id="previewThumbnail" autoplay="false"> <video id="previewThumbnail" autoplay="false">
@@ -56,7 +93,9 @@
</div> </div>
</div> </div>
<!-- Here starts code from step 2-->
<div class="step" id="step2" style="display:none;"> <div class="step" id="step2" style="display:none;">
<h2 class="h2">Choose your preferences:</h2>
<div class="KI-wrapper"> <div class="KI-wrapper">
<label id="labelKI">Select ki:</label> <label id="labelKI">Select ki:</label>
<select name="ai_type" id="ai_type"></select> <select name="ai_type" id="ai_type"></select>
@@ -78,15 +117,19 @@
<div class="language-wrapper"> <div class="language-wrapper">
<label id="labelLanguage">Select language:</label> <label id="labelLanguage">Select language:</label>
<img id="labelLanguageFlag" src="flags/united-kingdom-flag-png-large.jpg" width="20" height="10" >
<select name="language_option" id="language_option"> <select name="document_language_option" id="document_language_option">
</select> </select>
</div> </div>
</div> </div>
<!-- Here starts code from step 3-->
<!-- Hover Effekt für Dokumentenvorschau, Fragezeichen hinter Text, drüber hoven zeigt Beispieldokument -->
<div class="step" id="step3" style="display:none;"> <div class="step" id="step3" style="display:none;">
<div class="checkbox-group"> <div class="checkbox-group">
<label id="checkbox-label" for="checkbox-group">Choose prefered document style:</label> <h2 class="h2">Choose prefered document style:</h2>
<div class="checkbox-container"> <div class="checkbox-container">
<input type="checkbox" name ="docFormat" id="docFormat" value="followup-report"> <input type="checkbox" name ="docFormat" id="docFormat" value="followup-report">
<label id="label_format" for="docFormat">Follow-up Report</label> <label id="label_format" for="docFormat">Follow-up Report</label>
@@ -105,39 +148,42 @@
</div> </div>
<div class="checkbox-container"> <div class="checkbox-container">
<input type="checkbox" name="docFormat" id="docFormatCustom" value="custom"> <input type="checkbox" name="docFormat" id="docFormatCustom" value="custom">
<select name="ai_type" id="ai_type"> <select name="customDocumentTypes" id="customDocumentTypes">
<option>nichts</option>
</select> </select>
</div> </div>
</div> </div>
</div> </div>
<!-- Here starts code from step 4-->
<div class="step" id="step4" style="display:none;"> <div class="step" id="step4" style="display:none;">
<h2 class="h2">Klick to submit:</h2>
<button class="submit-btn" id="submitButton" onclick="checkBoxes()" disabled>Submit</button> <button class="submit-btn" id="submitButton" onclick="checkBoxes()" disabled>Submit</button>
<div class="testy" id="testy"> <div class="testy" id="testy">
<div class="box2" id="box1"> <div class="box2" id="box1">
</div> </div>
<p>---Starting---</p> <p id="box1_p1">---Starting---</p>
<div class="box2" id="box2"> <div class="box2" id="box2">
</div> </div>
<p>---Transkribing---</p> <p id="box2_p2">---Transkribing---</p>
<div class="box2" id="box3"> <div class="box2" id="box3">
</div> </div>
<p>---Document creation---</p> <p id="box3_p3">---Document creation---</p>
<div class="box2" id="box4"> <div class="box2" id="box4">
</div> </div>
</div> </div>
</div> </div>
<!-- Here starts code from step 5-->
<div class="step" id="step5" style="display:none;"> <div class="step" id="step5" style="display:none;">
<h2 class="h2">Change names of the speakers:</h2>
<div class="speakerView" id="speakerView"> <div class="speakerView" id="speakerView">
<label id="labelSpeaker">Select Speaker:</label> <label id="labelSpeaker">Select Speaker:</label>
<select name="cur_speaker" id="cur_speaker"> <select name="cur_speaker" id="cur_speaker">
</select> </select>
</div> </div>
<div class="speakerAudio" id="speakerAutio"> <div class="speakerAudio" id="speakerAutio">
<label id="labelSpeakerAudio">Selected Speaker</label> <label id="labelSpeakerAudio">Selected Speaker:</label>
<audio controls id="speakerAudioViewer"> <audio controls id="speakerAudioViewer">
Currently there is no audio file here. Currently there is no audio file here.
</audio> </audio>
@@ -152,7 +198,9 @@
</div> </div>
</div> </div>
<!-- Here starts code from step 6-->
<div class="step" id="step6" style="display:none;"> <div class="step" id="step6" style="display:none;">
<h2 class="h2">Klick to download your document:</h2>
<button class="download-btn" id="downloadButton" onclick="fileDownload()">Download</button> <button class="download-btn" id="downloadButton" onclick="fileDownload()">Download</button>
</div> </div>
</div> </div>
@@ -160,7 +208,7 @@
<button id ="nextBtn" class="navBtn">&rarr;</button> <button id ="nextBtn" class="navBtn">&rarr;</button>
</div> </div>
<script src="https://cdn.jsdelivr.net/npm/lc-select@1.3.0/lc_select.min.js"></script>
<script src="languages.js"></script> <script src="languages.js"></script>
<script src="script.js"></script> <script src="script.js"></script>
<script src="./renderer.js"></script> <script src="./renderer.js"></script>
+92 -3
View File
@@ -12,7 +12,37 @@ var languageOptions = {
"checkbox_group": "Choose prefered document style:", "checkbox_group": "Choose prefered document style:",
"label_format": "Meeting report", "label_format": "Meeting report",
"label_summary": "Summary with timestamps", "label_summary": "Summary with timestamps",
"submitButton": "Submit" "submitButton": "Submit",
"step_nav1": "Step 1",
"step_nav2": "Step 2",
"step_nav3": "Step 3",
"step_nav4": "Step 4",
"step_nav5": "Step 5",
"step_nav6": "Step 6",
"h2": "Upload your video here:",
"labelSpeaker": "Select Speaker:",
"labelSpeakerAudio": "Selected Speaker:",
"labelSpeakerWriter": "Write name:",
"speakerLocker": "Rename speaker",
"speakerResender": "Rewrite document",
"downloadButton": "Download",
"box1_p1": "---Starting---",
"box2_p2": "---Transkribing---",
"box3_p3": "---Document creation---",
"labelType": "Select document type:",
"customDocBtn": "Manage document types",
"cd_h1": "Manage document types",
"cd_existingDocs": "Select existing documents (optional):",
"cd_docName": "Document name",
"docName": "Enter the document name here",
"cd_promt": "Your prompt:",
"prompt": "Type the prompt for your document here...",
"goBackBtn": "Return",
"deleteBtn": "Delete document",
"generateBtn": "Save document",
"newDoc": "-- Create new document --"
}, },
"de":{ "de":{
"flagPath": "flags/germany-flag-png-large.jpg", "flagPath": "flags/germany-flag-png-large.jpg",
@@ -27,7 +57,36 @@ var languageOptions = {
"checkbox_group": "Bevorzugte Dokumentvarianten:", "checkbox_group": "Bevorzugte Dokumentvarianten:",
"label_format": "Meeting Bericht", "label_format": "Meeting Bericht",
"label_summary": "Zusammenfassung mit Zeitstempeln", "label_summary": "Zusammenfassung mit Zeitstempeln",
"submitButton": "Absenden" "submitButton": "Absenden",
"step_nav1": "Schritt 1",
"step_nav2": "Schritt 2",
"step_nav3": "Schritt 3",
"step_nav4": "Schritt 4",
"step_nav5": "Schritt 5",
"step_nav6": "Schritt 6",
"h2": "Uploade dein Video hier:",
"labelSpeaker": "Wähle Sprecher:",
"labelSpeakerAudio": "Ausgewählter Sprecher:",
"labelSpeakerWriter": "Schreib Namen:",
"speakerLocker": "Ersetze Namen",
"speakerResender": "Überschreibe Dokument",
"downloadButton": "Download",
"box1_p1": "---Startet---",
"box2_p2": "---Transkribing---",
"box3_p3": "---Dokument kreieren---",
"labelType": "Wähle Dokumenttype:",
"customDocBtn": "Dokumenttypen verwalten",
"cd_h1": "Dokumenttypen verwalten",
"cd_existingDocs": "Vorhandene Dokumente auswählen (optional):",
"cd_docName": "Dokument Name",
"docName": "Geben Sie hier den Dokumentnamen ein",
"cd_promt": "Ihr Prompt:",
"prompt": "Geben Sie hier die Eingabeaufforderung für Ihr Dokument ein...",
"goBackBtn": "Zurück",
"deleteBtn": "Lösche Dokument",
"generateBtn": "Speicher Dokument",
"newDoc": "-- Neues Dokument erstellen --"
}, },
"in":{ "in":{
"flagPath": "flags/india-flag-png-large.png", "flagPath": "flags/india-flag-png-large.png",
@@ -42,7 +101,37 @@ var languageOptions = {
"checkbox_group": "पसंदीदा दस्तावेज़ शैली चुनें:", "checkbox_group": "पसंदीदा दस्तावेज़ शैली चुनें:",
"label_format": "बैठक रिपोर्ट", "label_format": "बैठक रिपोर्ट",
"label_summary": "टाइमस्टैम्प के साथ सारांश", "label_summary": "टाइमस्टैम्प के साथ सारांश",
"submitButton": "जमा करना" "submitButton": "जमा करना",
"step_nav1": "स्टेप 1",
"step_nav2": "स्टेप 2",
"step_nav3": "स्टेप 3",
"step_nav4": "स्टेप 4",
"step_nav5": "स्टेप 5",
"step_nav6": "स्टेप 6",
"h2": "अपना वीडियो यहां अपलोड करें:",
"labelSpeaker": "स्पीकर चुनें:",
"labelSpeakerAudio": "चयनित वक्ता:",
"labelSpeakerWriter": "नाम लिखें:",
"speakerLocker": "स्पीकर का नाम बदलें",
"speakerResender": "दस्तावेज़ पुनः लिखें",
"downloadButton": "डाउनलोड करना",
"box1_p1": "---प्रारंभ---",
"box2_p2": "---प्रतिलेखन---",
"box3_p3": "---दस्तावेज़ निर्माण---",
"labelType": "दस्तावेज़ प्रकार चुनें:",
"customDocBtn": "दस्तावेज़ प्रकार प्रबंधित करें",
"cd_h1": "दस्तावेज़ प्रकार प्रबंधित करें",
"cd_existingDocs": "मौजूदा दस्तावेज़ों का चयन करें (वैकल्पिक):",
"cd_docName": "दस्तावेज़ का नाम",
"docName": "यहां दस्तावेज़ का नाम दर्ज करें",
"cd_promt": "आपका संकेत:",
"prompt": "अपने दस्तावेज़ के लिए प्रॉम्प्ट यहां टाइप करें...",
"goBackBtn": "वापस करना",
"deleteBtn": "दस्तावेज़ हटाएँ",
"generateBtn": "दस्तावेज़ सहेजें",
"newDoc": "-- नया दस्तावेज़ बनाएँ --"
} }
}; };
+17 -6
View File
@@ -5,10 +5,10 @@ try {
onFileDrop: (file) => webUtils.getPathForFile(file) onFileDrop: (file) => webUtils.getPathForFile(file)
}) })
contextBridge.exposeInMainWorld("submit", { contextBridge.exposeInMainWorld("submit", {
submit: (meeting_specifications) => {ipcRenderer.send("file_submit", meeting_specifications)} submit: (meeting_specifications) => { ipcRenderer.send("file_submit", meeting_specifications) }
}) })
contextBridge.exposeInMainWorld("electronAPI", { contextBridge.exposeInMainWorld("electronAPI", {
getFilePath: (file) => {return webUtils.getPathForFile(file)} getFilePath: (file) => { return webUtils.getPathForFile(file) }
}) })
contextBridge.exposeInMainWorld("onStartup", { contextBridge.exposeInMainWorld("onStartup", {
@@ -23,15 +23,26 @@ try {
speakerAudios: (callback) => ipcRenderer.on('speakerAudios', callback) speakerAudios: (callback) => ipcRenderer.on('speakerAudios', callback)
}) })
contextBridge.exposeInMainWorld("submitSpeaker", { 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", { contextBridge.exposeInMainWorld("download", {
file_download: () => {ipcRenderer.send("file_download")} file_download: () => { ipcRenderer.send("file_download") }
}) })
ipcRenderer.on("error", (event, err) => {alert(err)}) //documenttypes
contextBridge.exposeInMainWorld('api', {
getTxtFiles: () => ipcRenderer.invoke('get-txt-files'),
saveTxtFile: (name, content) =>
ipcRenderer.invoke('save-txt-file', name, content),
readTxtFile: (fileName) =>
ipcRenderer.invoke('read-txt-file', fileName),
deleteTxtFile: (fileName) =>
ipcRenderer.invoke('delete-txt-file', fileName)
});
ipcRenderer.on("error", (event, err) => { alert(err) })
} catch (error) { } catch (error) {
console.log("Error in preload.js"); console.log("Error in preload.js");
} }
+270 -122
View File
@@ -1,58 +1,102 @@
/*
uploadContainer.addEventListener("dragover", (e) =>{ Listeners for the program setup and changes
*/
//Listener for when the gui has been loaded so it can fill the dop down menus in step 2 and the language options
window.addEventListener('load', async (e) => {
try {
loadLanguageOptions();
const value = await window.onStartup.getModuleNames();
loadAiOptions(value.ai_modules);
loadTranscriptionOptions(value.transcription_modules);
} catch (error) {
console.log("Error in the window listener load in the renderer.js \n");
console.log(error);
}
});
//Listener if the language of the displayed text's in the gui should be changed
language_option.addEventListener('change', (e) => {
try {
const select = document.getElementById('language_option');
changeLanguage(select.value);
} catch (error) {
console.log("Error in the language_option change listener in the renderer.js");
console.log(error);
}
});
stepButtons.forEach(btn => {
btn.addEventListener("click", () => {
try { try {
e.stopPropagation(); const step = parseInt(btn.dataset.step);
e.preventDefault(); showStep(step);
} catch (error) { } catch (error) {
console.log("Error in renderer.js dragover listener function")
} }
});
});
//Listener if the button to change the current step to the previus one is pressed
prevBtn.addEventListener("click", () => {
try {
if (currentStep > 1) showStep(currentStep - 1);
} catch (error) {
}
});
//Listener if the button to change the current step to the next one is pressed
nextBtn.addEventListener("click", () => {
try {
if (currentStep < totalSteps) showStep(currentStep + 1);
} catch (error) {
}
});
/*
Listeners for Step 1
*/
//Listener if a file has been draged over the drop down field
uploadContainer.addEventListener("dragover", (e) => {
try {
e.stopPropagation();
e.preventDefault();
} catch (error) {
console.log("Error in renderer.js dragover listener function")
}
}); });
//listener for when a file get dropped on the drag&drop field //listener for when a file get dropped on the drag&drop field
uploadContainer.addEventListener("drop", (e) => { uploadContainer.addEventListener("drop", (e) => {
try {
e.stopPropagation()
e.preventDefault()
const files = e.dataTransfer.files
const filePath = window.explorer.onFileDrop(files[0])
const testEndings = [".mp4", ".mov", ".avi", ".mkv"];
var pathToLower = filePath.toLowerCase();
if(testEndings.some(e => pathToLower.endsWith(e))){
const files1 = e.dataTransfer.files;
handleFiles(files1);
}else{
alert('The given file is not compatible. These are the available types: [".mp4", ".mov", ".avi", ".mkv"].');
}
} catch (error) {
console.log("Error in renderer.js with the listerner for the drop function");
console.log(error);
}
})
window.addEventListener('load', async (e) => {
try { try {
loadLanguageOptions(); e.stopPropagation()
const value = await window.onStartup.getModuleNames(); e.preventDefault()
loadAiOptions(value.ai_modules); const files = e.dataTransfer.files
loadTranscriptionOptions(value.transcription_modules); const filePath = window.explorer.onFileDrop(files[0])
const testEndings = [".mp4", ".mov", ".avi", ".mkv"];
var pathToLower = filePath.toLowerCase();
if (testEndings.some(e => pathToLower.endsWith(e))) {
const files1 = e.dataTransfer.files;
handleFiles(files1);
} else {
alert('The given file is not compatible. These are the available types: [".mp4", ".mov", ".avi", ".mkv"].');
}
} catch (error) { } catch (error) {
console.log("Error in renderer.js with the listerner for the drop function");
console.log(error);
} }
})
});
language_option.addEventListener('change', (e)=>{
try {
const select = document.getElementById('language_option');
changeLanguage(select.value);
} catch (error) {
}
});
//listener for the file explorer search when something got selected //listener for the file explorer search when something got selected
videoUpload.addEventListener("change", () => { videoUpload.addEventListener("change", () => {
@@ -74,170 +118,274 @@ manualUploadBtn.addEventListener('click', () => {
console.log("Error in manualBtn EventListener click"); console.log("Error in manualBtn EventListener click");
console.log(error); console.log(error);
} }
}); });
stepButtons.forEach(btn => { /*
btn.addEventListener("click", () => {
try { Listeners for Step 2
const step = parseInt(btn.dataset.step);
showStep(step); */
} catch (error) {
/*
}
Listeners for Step 3
*/
window.api.getTxtFiles().then(files => {
var menu = document.getElementById('customDocumentTypes');
var l = document.getElementById('customDocumentTypes').options.length - 1;
for (i = l; i >= 0; i--) {
menu.remove(i);
}
files.forEach(file => {
const option = document.createElement('option');
option.value = file;
option.textContent = file
.replace('.txt', '') // Endung entfernen
.replace(/_/g, ' ') // Leerzeichen ersetzen
.replace(/\b\w/g, c => c.toUpperCase()) // ersten Buchstaben groß
menu.appendChild(option);
}); });
}); });
prevBtn.addEventListener("click", () => {
try {
if (currentStep > 1) showStep(currentStep - 1);
} catch (error) {
}
});
nextBtn.addEventListener("click", () => {
try {
if(currentStep < totalSteps) showStep(currentStep + 1);
} catch (error) {
}
});
//Checkboxlistener so that only one can be selected at a time //Checkboxlistener so that only one can be selected at a time
docFormat.addEventListener("change", (e) =>{ docFormat.addEventListener("change", (e) => {
try { try {
if(docFormat.checked){ if (docFormat.checked) {
docFormatSummary1.checked = false; docFormatSummary1.checked = false;
docFormatSummary2.checked = false; docFormatSummary2.checked = false;
docFormatSummary3.checked = false; docFormatSummary3.checked = false;
docFormatCustom.checked = false; docFormatCustom.checked = false;
} }
} catch (error) { } catch (error) {
} }
}); });
docFormatSummary1.addEventListener("change", (e) =>{ docFormatSummary1.addEventListener("change", (e) => {
try { try {
if(docFormatSummary1.checked){ if (docFormatSummary1.checked) {
docFormat.checked = false; docFormat.checked = false;
docFormatSummary2.checked = false; docFormatSummary2.checked = false;
docFormatSummary3.checked = false; docFormatSummary3.checked = false;
docFormatCustom.checked = false; docFormatCustom.checked = false;
} }
} catch (error) { } catch (error) {
} }
}); });
docFormatSummary2.addEventListener("change", (e) =>{ docFormatSummary2.addEventListener("change", (e) => {
try { try {
if(docFormatSummary2.checked){ if (docFormatSummary2.checked) {
docFormatSummary1.checked = false; docFormatSummary1.checked = false;
docFormat.checked = false; docFormat.checked = false;
docFormatSummary3.checked = false; docFormatSummary3.checked = false;
docFormatCustom.checked = false; docFormatCustom.checked = false;
} }
} catch (error) { } catch (error) {
} }
}); });
docFormatSummary3.addEventListener("change", (e) =>{ docFormatSummary3.addEventListener("change", (e) => {
try { try {
if(docFormatSummary3.checked){ if (docFormatSummary3.checked) {
docFormatSummary1.checked = false; docFormatSummary1.checked = false;
docFormatSummary2.checked = false; docFormatSummary2.checked = false;
docFormat.checked = false; docFormat.checked = false;
docFormatCustom.checked = false; docFormatCustom.checked = false;
} }
} catch (error) { } catch (error) {
} }
}); });
docFormatCustom.addEventListener("change", (e) =>{ docFormatCustom.addEventListener("change", (e) => {
try { try {
if(docFormatCustom.checked){ if (docFormatCustom.checked) {
docFormatSummary1.checked = false; docFormatSummary1.checked = false;
docFormatSummary2.checked = false; docFormatSummary2.checked = false;
docFormatSummary3.checked = false; docFormatSummary3.checked = false;
docFormat.checked = false; docFormat.checked = false;
} }
} catch (error) { } catch (error) {
} }
}); });
//Speaker change listener /*
cur_speaker.addEventListener("change", (e) =>{
try {
document.getElementById("speakerAudioViewer").src = speakerAudios[document.getElementById("cur_speaker").value].src;
} catch (error) {
}
});
window.audios.speakerAudios((event, arg) => { Listeners for Step 4
loadSpeakerOptions(arg);
setSpeakerAudiosValue(arg);
});
*/
//Functions the the displayed progress in the progressbar can be changed out of the main process
window.electron.progress((event, arg) => { window.electron.progress((event, arg) => {
if(arg.curstep == 1){ if (arg.curstep == 1) {
setCircleOne(); setCircleOne();
}else if(arg.curstep == 2){ } else if (arg.curstep == 2) {
setCircleZwo(); setCircleZwo();
} else if(arg.curstep == 3){ } else if (arg.curstep == 3) {
setCircleThree(); setCircleThree();
}else if(arg.curstep == 4){ } else if (arg.curstep == 4) {
setCircleFour(); setCircleFour();
} }
}); });
function setCircleOne(){ function setCircleOne() {
try { try {
if(document.getElementById("box1").style.backgroundColor == "green"){ if (document.getElementById("box1").style.backgroundColor == "green") {
document.getElementById("box1").style.backgroundColor = "red"; document.getElementById("box1").style.backgroundColor = "red";
}else{ } else {
document.getElementById("box1").style.backgroundColor = "green"; document.getElementById("box1").style.backgroundColor = "green";
} }
} catch (error) { } catch (error) {
} }
}; };
function setCircleZwo(){ function setCircleZwo() {
try { try {
if(document.getElementById("box2").style.backgroundColor == "green"){ if (document.getElementById("box2").style.backgroundColor == "green") {
document.getElementById("box2").style.backgroundColor = "red"; document.getElementById("box2").style.backgroundColor = "red";
}else{ } else {
document.getElementById("box2").style.backgroundColor = "green"; document.getElementById("box2").style.backgroundColor = "green";
} }
} catch (error) { } catch (error) {
} }
}; };
function setCircleThree(){ function setCircleThree() {
try { try {
if(document.getElementById("box3").style.backgroundColor == "green"){ if (document.getElementById("box3").style.backgroundColor == "green") {
document.getElementById("box3").style.backgroundColor = "red"; document.getElementById("box3").style.backgroundColor = "red";
}else{ } else {
document.getElementById("box3").style.backgroundColor = "green"; document.getElementById("box3").style.backgroundColor = "green";
} }
} catch (error) { } catch (error) {
} }
}; };
function setCircleFour(){ function setCircleFour() {
try { try {
if(document.getElementById("box4").style.backgroundColor == "green"){ if (document.getElementById("box4").style.backgroundColor == "green") {
document.getElementById("box4").style.backgroundColor = "red"; document.getElementById("box4").style.backgroundColor = "red";
}else{ } else {
document.getElementById("box4").style.backgroundColor = "green"; document.getElementById("box4").style.backgroundColor = "green";
} }
} catch (error) { } catch (error) {
} }
}; };
/*
Listeners for Step 5
*/
//Speaker change listener
cur_speaker.addEventListener("change", (e) => {
try {
document.getElementById("speakerAudioViewer").src = speakerAudios[document.getElementById("cur_speaker").value].src;
} catch (error) {
}
});
//Function so the main process can give the gui a json with the speakers and their audio
window.audios.speakerAudios((event, arg) => {
loadSpeakerOptions(arg);
setSpeakerAudiosValue(arg);
});
/*
Listeners for the costum documents section
*/
goBackBtn.addEventListener("click", () => {
showCD();
});
// dokumente speichern
generateBtn.addEventListener("click", () => {
const name = docName.value.trim();
const content = document.getElementById("prompt").value.trim();
if (!name || !content) {
result.textContent = "Bitte Dokumentname und Prompt ausfüllen.";
console.log(name + " " + content);
setTimeout(() => {
result.textContent = "";
}, 3000);
return;
}
window.api.saveTxtFile(name, content).then();
result.textContent = "Dokument erfolgreich gespeichert!";
setTimeout(() => {
result.textContent = "";
}, 3000);
reloadDocuments();
});
// dokumente löschen
deleteBtn.addEventListener("click", () => {
const name = docName.value.trim();
if (!name) {
result.textContent = "Bitte Dokumentname angeben.";
setTimeout(() => {
result.textContent = "";
}, 3000);
return;
}
const confirmDelete = confirm(
`Möchtest du das Dokument "${name}" wirklich löschen?`
);
if (!confirmDelete) return;
window.api.deleteTxtFile(name).then((success) => {
if (success) {
result.textContent = "Dokument erfolgreich gelöscht!";
reloadDocuments();
existingDocs.value = "newDoc";
existingDocs.dispatchEvent(new Event("change"));
} else {
result.textContent = "Dokument konnte nicht gelöscht werden.";
}
setTimeout(() => {
result.textContent = "";
}, 3000);
});
});
//function to load existingDoc options to the drop down list
window.api.getTxtFiles().then(files => {
reloadDocuments();
});
//content anzeigen
existingDocs.addEventListener("change", async () => {
const existingDocsed = existingDocs.value;
const exampleText = "";
if (existingDocsed === "newDoc") {
docNameWrapper.classList.remove("hidden");
docName.value = "";
prompt.value = exampleText;
return;
}
docNameWrapper.classList.add("hidden");
const content = await window.api.readTxtFile(existingDocsed);
prompt.value = content;
docName.value = existingDocsed.replace(".txt", "");
});
+308 -160
View File
@@ -1,103 +1,118 @@
let currentVideoPath = null; let currentVideoPath = null;
//function to check if one checkbox is at least klicked
function checkBoxes() {
try {
const checkboxes = document.querySelectorAll('input[name="docFormat"]');
let isChecked = false;
var checkedCounter = 0;
checkboxes.forEach(function(checkbox){
if(checkbox.checked){
isChecked = true;
checkedCounter++;
}
});
if (isChecked) { /*
//Code to submit the video
var selectedCheckboxes = {};
checkboxes.forEach(function(checkbox){
if(checkbox.checked){
selectedCheckboxes[checkbox.nextElementSibling.textContent] = "";
}
});
const testEndings = [".mp4", ".mov", ".avi", ".mkv"]; Functions used in the setup or affect most of the gui
var pathTest = window.electronAPI.getFilePath(videoUpload.files[0]);
var pathToLower = pathTest.toLowerCase();
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; var showCDValue = 0;
if(document.getElementById("docFormatSummary1").checked) typeCheckbox = document.getElementById("docFormatSummary1").value; function showCD() {
if(document.getElementById("docFormatSummary2").checked) typeCheckbox = document.getElementById("docFormatSummary2").value; if(showCDValue == 0){
if(document.getElementById("docFormatSummary3").checked) typeCheckbox = document.getElementById("docFormatSummary3").value; document.getElementById('cdContainer').style.display = "block";
if(document.getElementById("docFormatCustom").checked) typeCheckbox = document.getElementById("docFormatCustom").value; document.getElementById('step1').style.display ="none";
document.getElementById('step2').style.display ="none";
document.getElementById("testy").style.visibility = "visible" document.getElementById('step3').style.display ="none";
document.getElementById("box1").style.backgroundColor = "red"; document.getElementById('step4').style.display ="none";
document.getElementById("box2").style.backgroundColor = "red"; document.getElementById('step5').style.display ="none";
document.getElementById("box3").style.backgroundColor = "red"; document.getElementById('step6').style.display ="none";
document.getElementById("box4").style.backgroundColor = "red"; showCDValue = 1;
console.log(selectedCheckboxes); } else {
const outputType = document.getElementById("output_type"); currentStep = 1;
const transcriptionType = document.getElementById("transkript_type"); showCDValue = 0;
const aiType = document.getElementById("ai_type"); document.getElementById('cdContainer').style.display = "none";
const sendingPackage = { showStep(1);
"video": {
"module":"extraction-video-to-audio",
"inputVideoPath": pathTest
},
"transcription": {
"module": transcriptionType.value
},
"document": {
"module":aiType.value,
"type": typeCheckbox,
"outputType": outputType.value
}
};
window.submit.submit(sendingPackage)
}else{
alert('The given file is not compatible. These are the available types: [".mp4", ".mov", ".avi", ".mkv"].');
}
} else {
//language only english at the moment
alert('Please select at least one document type.');
}
} catch (error) {
console.log("Error in script.js checkBoxes function");
console.log(error);
} }
} }
//language changing feature //language changing feature => changes the language of every displayed text
function changeLanguage(language) { function changeLanguage(language) {
try { try {
document.getElementById('labelLanguageFlag').src = languageOptions[language].flagPath; //document.getElementById('labelLanguageFlag').src = languageOptions[language].flagPath;
document.getElementById('labelKI').textContent = languageOptions[language].labelKI; document.getElementById('labelKI').textContent = languageOptions[language].labelKI;
document.getElementById('labelTranscription').textContent = languageOptions[language].labelTranscription; document.getElementById('labelTranscription').textContent = languageOptions[language].labelTranscription;
document.getElementById('labelLanguage').textContent = languageOptions[language].labelLanguage; document.getElementById('labelLanguage').textContent = languageOptions[language].labelLanguage;
document.getElementById('title').textContent = languageOptions[language].title; document.getElementById('title').textContent = languageOptions[language].title;
document.getElementById('h1').textContent = languageOptions[language].h1; //document.getElementById('h1').textContent = languageOptions[language].h1;
document.getElementById('p1').textContent = languageOptions[language].p1; document.getElementById('p1').textContent = languageOptions[language].p1;
document.getElementById('fileName').textContent = languageOptions[language].fileName; document.getElementById('fileName').textContent = languageOptions[language].fileName;
document.getElementById('manualUploadBtn').textContent = languageOptions[language].manualUploadBtn; document.getElementById('manualUploadBtn').textContent = languageOptions[language].manualUploadBtn;
document.getElementById('checkbox_group').textContent = languageOptions[language].checkbox_group;
document.getElementById('label_format').textContent = languageOptions[language].label_format; document.getElementById('label_format').textContent = languageOptions[language].label_format;
document.getElementById('label_summary').textContent = languageOptions[language].label_summary; document.getElementById('label_summary').textContent = languageOptions[language].label_summary;
document.getElementById('submitButton').textContent = languageOptions[language].submitButton; document.getElementById('submitButton').textContent = languageOptions[language].submitButton;
document.getElementById('step_nav1').textContent = languageOptions[language].step_nav1;
document.getElementById('step_nav2').textContent = languageOptions[language].step_nav2;
document.getElementById('step_nav3').textContent = languageOptions[language].step_nav3;
document.getElementById('step_nav4').textContent = languageOptions[language].step_nav4;
document.getElementById('step_nav5').textContent = languageOptions[language].step_nav5;
document.getElementById('step_nav6').textContent = languageOptions[language].step_nav6;
//document.getElementById('h2').textContent = languageOptions[language].h2;
document.getElementById('labelSpeaker').textContent = languageOptions[language].labelSpeaker;
document.getElementById('labelSpeakerAudio').textContent = languageOptions[language].labelSpeakerAudio;
document.getElementById('labelSpeakerWriter').textContent = languageOptions[language].labelSpeakerWriter;
document.getElementById('speakerLocker').textContent = languageOptions[language].speakerLocker;
document.getElementById('speakerResender').textContent = languageOptions[language].speakerResender;
document.getElementById('downloadButton').textContent = languageOptions[language].downloadButton;
document.getElementById('box1_p1').textContent = languageOptions[language].box1_p1;
document.getElementById('box2_p2').textContent = languageOptions[language].box2_p2;
document.getElementById('box3_p3').textContent = languageOptions[language].box3_p3;
document.getElementById('labelType').textContent = languageOptions[language].labelType;
document.getElementById('customDocBtn').textContent = languageOptions[language].customDocBtn;
document.getElementById('cd_h1').textContent = languageOptions[language].cd_h1;
document.getElementById('cd_existingDocs').textContent = languageOptions[language].cd_existingDocs;
document.getElementById('cd_docName').textContent = languageOptions[language].cd_docName;
document.getElementById('docName').placeholder = languageOptions[language].docName;
document.getElementById('cd_promt').textContent = languageOptions[language].cd_promt;
document.getElementById('prompt').placeholder = languageOptions[language].prompt;
document.getElementById('goBackBtn').textContent = languageOptions[language].goBackBtn;
document.getElementById('deleteBtn').textContent = languageOptions[language].deleteBtn;
document.getElementById('generateBtn').textContent = languageOptions[language].generateBtn;
document.getElementById('newDoc').textContent = languageOptions[language].newDoc;
} catch (error) { } catch (error) {
console.log("Error in script.js changeLanguage function"); console.log("Error in script.js changeLanguage function");
console.log(error); console.log(error);
} }
} }
/*
Functions used for the step navigation
*/
//Step-navigation
const steps = document.querySelectorAll(".step");
const stepButtons = document.querySelectorAll(".step-item");
let currentStep = 1;
const totalSteps = steps.length;
function showStep(stepNumber) {
if (stepNumber < 1 || stepNumber > totalSteps) {
console.error("StepNumber out of Bounds", stepNumber);
return;
}
steps.forEach(step => step.style.display = "none");
document.getElementById("step" + stepNumber).style.display = "flex";
stepButtons.forEach(btn => btn.classList.remove("active"));
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");
prevBtn.disabled = stepNumber == 1;
nextBtn.disabled = stepNumber === totalSteps;
currentStep = stepNumber;
}
/*
Functions used in Step 1
*/
//function to display the file path in the drop down box //function to display the file path in the drop down box
function handleFiles(files) { function handleFiles(files) {
try { try {
@@ -119,27 +134,40 @@ function handleFiles(files) {
} }
//function to regulate the progress on the progressbar //Video thumbnail generation. Shows a scene from the selected video file as a preview
function updateProgressBar(bar, value) { function generateThumbnail(path) {
try { try {
value = Math.round(value); const videoElement = document.getElementById("previewThumbnail");
bar.querySelector(".progress_fill").style.width = `${value}%`; while (videoElement.firstChild) videoElement.removeChild(videoElement.firstChild);
bar.querySelector(".progress_text").textContent = `${value}%`; videoElement.src = path;
videoElement.type = "video/mov";
videoElement.load();
videoElement.style.maxWidth = 40;
videoElement.style.maxHeight = 40;
videoElement.autoplay = false;
} catch (error) { } catch (error) {
console.log("Error in scripts.js updateProgressBar function"); console.log("Error in the generateThumbnail function in the script.js file \n");
console.log(error); console.log(error);
} }
} }
/*
Functions used in Step 2
*/
//function to load ai options to the drop down list //function to load ai options to the drop down list
function loadAiOptions(options){ function loadAiOptions(options) {
try { try {
var menu = document.getElementById('ai_type'); var menu = document.getElementById('ai_type');
var object_holdy; var object_holdy;
var choice ; var choice;
object_holdy = options object_holdy = options
for(i = 0; i < options.length; i++){ for (i = 0; i < options.length; i++) {
choice = document.createElement('option'); choice = document.createElement('option');
choice.textContent = object_holdy[i].displayname; choice.textContent = object_holdy[i].displayname;
choice.value = object_holdy[i].name; choice.value = object_holdy[i].name;
@@ -152,13 +180,13 @@ function loadAiOptions(options){
} }
//function to load transcription options to the drop down list //function to load transcription options to the drop down list
function loadTranscriptionOptions(options){ function loadTranscriptionOptions(options) {
try { try {
var menu = document.getElementById('transkript_type'); var menu = document.getElementById('transkript_type');
var object_holdy; var object_holdy;
var choice ; var choice;
object_holdy = options object_holdy = options
for(i = 0; i < options.length; i++){ for (i = 0; i < options.length; i++) {
choice = document.createElement('option'); choice = document.createElement('option');
choice.textContent = object_holdy[i].displayname; choice.textContent = object_holdy[i].displayname;
choice.value = object_holdy[i].name; choice.value = object_holdy[i].name;
@@ -171,13 +199,13 @@ function loadTranscriptionOptions(options){
} }
//function to load data type options to the drop down list //function to load data type options to the drop down list
function loadDataTypeOptions(options){ function loadDataTypeOptions(options) {
try { try {
var menu = document.getElementById('output_type'); var menu = document.getElementById('output_type');
var object_holdy; var object_holdy;
var choice ; var choice;
object_holdy = options object_holdy = options
for(i = 0; i < options.length; i++){ for (i = 0; i < options.length; i++) {
choice = document.createElement('option'); choice = document.createElement('option');
choice.textContent = object_holdy[i].displayname; choice.textContent = object_holdy[i].displayname;
choice.value = object_holdy[i].name; choice.value = object_holdy[i].name;
@@ -190,36 +218,177 @@ function loadDataTypeOptions(options){
} }
//function to load language options to the drop down list //function to load language options to the drop down list
function loadLanguageOptions(){ function loadLanguageOptions() {
try { try {
var menu = document.getElementById('language_option'); var menu = document.getElementById('language_option');
var menu_doc = document.getElementById('document_language_option');
var object_holdy; var object_holdy;
var choice ; var choice;
var choice2;
object_holdy = Object.keys(languageOptions); 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 = document.createElement('option');
choice.textContent = object_holdy[i]; choice.textContent = object_holdy[i];
choice.value = object_holdy[i]; choice.value = object_holdy[i];
choice.setAttribute('data-image', languageOptions[object_holdy[i]].flagPath);
choice2 = document.createElement('option');
choice2.textContent = object_holdy[i];
choice2.value = object_holdy[i];
choice2.setAttribute('data-image', languageOptions[object_holdy[i]].flagPath);
menu_doc.appendChild(choice2);
menu.appendChild(choice); menu.appendChild(choice);
} }
new lc_select(document.getElementById('language_option')); //loads the flag images so they get displayed in the gui
new lc_select(document.getElementById('document_language_option'));
} catch (error) { } catch (error) {
console.log("Error in script.js loadLanguageOptions function"); console.log("Error in script.js loadLanguageOptions function");
console.log(error); console.log(error);
} }
} }
/*
Functions used in Step 3
*/
/*
Functions used in Step 4
*/
//function to check if one checkbox is at least klicked. Final controll function before sending the input to the generation
function checkBoxes() {
try {
const checkboxes = document.querySelectorAll('input[name="docFormat"]');
let isChecked = false;
var checkedCounter = 0;
checkboxes.forEach(function (checkbox) {
if (checkbox.checked) {
isChecked = true;
checkedCounter++;
}
});
if (isChecked) {
//Code to submit the video
var selectedCheckboxes = {};
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))) {
//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;
document.getElementById("testy").style.visibility = "visible"
document.getElementById("box1").style.backgroundColor = "red";
document.getElementById("box2").style.backgroundColor = "red";
document.getElementById("box3").style.backgroundColor = "red";
document.getElementById("box4").style.backgroundColor = "red";
console.log(selectedCheckboxes);
const outputType = document.getElementById("output_type");
const transcriptionType = document.getElementById("transkript_type");
const aiType = document.getElementById("ai_type");
const sendingPackage = {
"video": {
"module": "extraction-video-to-audio",
"inputVideoPath": pathTest
},
"transcription": {
"module": transcriptionType.value
},
"document": {
"module": aiType.value,
"type": typeCheckbox,
"outputType": outputType.value
}
};
window.submit.submit(sendingPackage)
} else {
alert('The given file is not compatible. These are the available types: [".mp4", ".mov", ".avi", ".mkv"].');
}
} else {
//language only english at the moment
alert('Please select at least one document type.');
}
} catch (error) {
console.log("Error in script.js checkBoxes function");
console.log(error);
}
}
//function the the submit button on step 4 can be pressed
function activateSubmitBtn(hasFile) {
try {
document.getElementById("submitButton").disabled = !hasFile;
} catch (error) {
console.log(error);
}
}
//function to regulate the progress on the progressbar
function updateProgressBar(bar, value) {
try {
value = Math.round(value);
bar.querySelector(".progress_fill").style.width = `${value}%`;
bar.querySelector(".progress_text").textContent = `${value}%`;
} catch (error) {
console.log("Error in scripts.js updateProgressBar function");
console.log(error);
}
}
//function to hide the progressbar
function deaktivateProgressbar() {
try {
document.getElementById("progressbar").style.visibility = "hidden";
} catch (error) {
console.log(error);
}
}
/*
Functions used in Step 5
*/
//function to load speaker options to the drop down list //function to load speaker options to the drop down list
function loadSpeakerOptions(options){ function loadSpeakerOptions(options) {
try { try {
var menu = document.getElementById('cur_speaker'); var menu = document.getElementById('cur_speaker');
var l = document.getElementById('cur_speaker').options.length -1; var l = document.getElementById('cur_speaker').options.length - 1;
for(i = l; i >= 0; i--){ for (i = l; i >= 0; i--) {
menu.remove(i); menu.remove(i);
} }
var object_holdy; var object_holdy;
var choice; var choice;
object_holdy = Object.keys(options); 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 = document.createElement('option');
choice.textContent = options[object_holdy[i]].name; choice.textContent = options[object_holdy[i]].name;
choice.value = object_holdy[i]; choice.value = object_holdy[i];
@@ -233,7 +402,7 @@ function loadSpeakerOptions(options){
} }
//function to load speaker audio file options to the drop down list //function to load speaker audio file options to the drop down list
function loadSpeakerAudio(option){ function loadSpeakerAudio(option) {
try { try {
var menu = document.getElementById('speakerAudioViewer'); var menu = document.getElementById('speakerAudioViewer');
var aud = document.createElement("source"); var aud = document.createElement("source");
@@ -245,75 +414,20 @@ function loadSpeakerAudio(option){
} }
} }
function activateSubmitBtn(hasFile){
try {
document.getElementById("submitButton").disabled = !hasFile;
} catch (error) {
console.log(error);
}
}
function deaktivateProgressbar(){
try {
document.getElementById("progressbar").style.visibility = "hidden";
} catch (error) {
console.log(error);
}
}
//Video thumbnail generation
function generateThumbnail(path){
const videoElement = document.getElementById("previewThumbnail");
while (videoElement.firstChild) videoElement.removeChild(videoElement.firstChild);
videoElement.src = path;
videoElement.type = "video/mov";
videoElement.load();
videoElement.style.maxWidth = 40;
videoElement.style.maxHeight = 40;
videoElement.autoplay = false;
}
//Step-navigation
const steps = document.querySelectorAll(".step");
const stepButtons = document.querySelectorAll(".step-item");
let currentStep = 1;
const totalSteps = steps.length;
function showStep(stepNumber) {
if (stepNumber < 1 || stepNumber > totalSteps){
console.error("StepNumber out of Bounds", stepNumber);
return;
}
steps.forEach(step => step.style.display = "none");
document.getElementById("step" + stepNumber).style.display = "flex";
stepButtons.forEach(btn => btn.classList.remove("active"));
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");
prevBtn.disabled = stepNumber == 1;
nextBtn.disabled = stepNumber === totalSteps;
currentStep = stepNumber;
}
//Audio value setter //Audio value setter
var speakerAudios = {}; var speakerAudios = {};
var speakerEndValues = {}; var speakerEndValues = {};
function setSpeakerAudiosValue(valy){ function setSpeakerAudiosValue(valy) {
try { try {
speakerAudios = valy; speakerAudios = valy;
speakerRewriten = valy; speakerRewriten = valy;
document.getElementById("speakerAudioViewer").src = valy.speakerA.src; document.getElementById("speakerAudioViewer").src = valy.speakerA.src;
} catch (error) { } catch (error) {
} }
} }
//Function to rewrite the speaker name in the json
function rewriteSpeakerName(){ function rewriteSpeakerName() {
try { try {
var tempy = document.getElementById("cur_speaker").value; var tempy = document.getElementById("cur_speaker").value;
speakerAudios[tempy].name = document.getElementById("newSpeaker").value; speakerAudios[tempy].name = document.getElementById("newSpeaker").value;
@@ -322,15 +436,21 @@ function rewriteSpeakerName(){
console.log("\n\n\n" + error + "\n\n\n") console.log("\n\n\n" + error + "\n\n\n")
} }
} }
//Function to send the json with the given names back to the program to rewrite the document file
function sendSpeakerPackages(){ function sendSpeakerPackages() {
try { try {
window.submitSpeaker.speaker_submit(speakerAudios); window.submitSpeaker.speaker_submit(speakerAudios);
} catch (error) { } catch (error) {
console.log(error);
} }
} }
/*
Functions for Step 6
*/
function fileDownload() { function fileDownload() {
try { try {
window.download.file_download(); window.download.file_download();
@@ -338,3 +458,31 @@ function fileDownload() {
console.error("Download failed:", error); console.error("Download failed:", error);
} }
} }
/*
Functions for the custom document section
*/
//reload drop down
function reloadDocuments() {
[...existingDocs.querySelectorAll('option:not([value="newDoc"])')]
.forEach(o => o.remove());
window.api.getTxtFiles().then(files => {
files.forEach(file => {
const option = document.createElement('option');
option.value = file;
option.textContent = file
.replace('.txt', '') // Endung entfernen
.replace(/_/g, ' ') // Leerzeichen ersetzen
.replace(/\b\w/g, c => c.toUpperCase()) // ersten Buchstaben groß
existingDocs.appendChild(option);
});
});
}
+201 -111
View File
@@ -9,7 +9,7 @@ body {
gap: 10px; gap: 10px;
margin: 0; margin: 0;
} }
#h1 { #h1 {
position: absolute; position: absolute;
left: 50%; left: 50%;
@@ -22,42 +22,42 @@ body {
#h1-wrapper { #h1-wrapper {
position: relative; position: relative;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
width: 770px; width: 780px;
height: 60px; height: 60px;
background-color: #FFF; background-color: #FFF;
box-shadow: 0px 4px 10px rgba(0,0,0,0.1); box-shadow: 0px 4px 10px rgba(0, 0, 0, 0.1);
border-radius: 5px; border-radius: 5px;
margin-bottom: 10px; margin-bottom: 10px;
display: flex; display: flex;
align-items: center; align-items: center;
} }
.upload-container { .upload-container {
background: white; background: white;
padding: 40px; padding: 40px;
border-radius: 12px; border-radius: 12px;
box-shadow: 0 4px 10px rgba(0,0,0,0.1); box-shadow: 0 4px 10px rgba(0, 0, 0, 0.1);
text-align: center; text-align: center;
width: 350px; width: 350px;
height: 200px; height: 200px;
transition: border 0.3s, background-color 0.3s; transition: border 0.3s, background-color 0.3s;
border: 2px dashed #7378c9; border: 2px dashed #7378c9;
} }
.upload-container.dragover { .upload-container.dragover {
border-color: #007BFF; border-color: #007BFF;
background-color: #eaf0ff; background-color: #eaf0ff;
} }
.upload-container p { .upload-container p {
margin: 0 0 15px 0; margin: 0 0 15px 0;
font-size: 16px; font-size: 16px;
color: #555; color: #555;
} }
.file-name { .file-name {
margin-top: 10px; margin-top: 10px;
font-size: 14px; font-size: 14px;
@@ -66,19 +66,19 @@ body {
text-overflow: ellipsis; text-overflow: ellipsis;
white-space: nowrap; white-space: nowrap;
} }
#thumbnailContainer { #thumbnailContainer {
width: 100%; width: 100%;
display: flex; display: flex;
justify-content: center; justify-content: center;
margin-bottom: 15px; margin-bottom: 15px;
} }
#thumbnailImage { #thumbnailImage {
width: 200px; width: 200px;
height: auto; height: auto;
border-radius: 10px; border-radius: 10px;
box-shadow: 0px 4px 10px rgba(0,0,0,0.1); box-shadow: 0px 4px 10px rgba(0, 0, 0, 0.1);
object-fit: cover; object-fit: cover;
} }
@@ -87,7 +87,7 @@ body {
height: 100px; height: 100px;
/*border: 1px dashed black;*/ /*border: 1px dashed black;*/
} }
.custom-btn { .custom-btn {
padding: 10px 20px; padding: 10px 20px;
margin-top: 10px; margin-top: 10px;
@@ -98,81 +98,87 @@ body {
cursor: pointer; cursor: pointer;
font-size: 14px; font-size: 14px;
} }
.custom-btn:hover { .custom-btn:hover {
background-color: #0056b3; background-color: #0056b3;
} }
.submit-btn:hover { .submit-btn:hover {
background-color: #0056b3; background-color: #0056b3;
} }
#step2 { #step2 {
gap: 30px; gap: 25px;
} }
.KI-wrapper { .KI-wrapper {
margin-top: 40px; margin-top: 10px;
} }
input[type="file"] { input[type="file"] {
display: none; display: none;
} }
.checkbox-group { .checkbox-group {
--borderColor: #007bfff5; --borderColor: #007bfff5;
--borderWidth: .125em; --borderWidth: .125em;
} }
.checkbox-group input[type=checkbox] { .checkbox-group input[type=checkbox] {
-webkit-appearance: none; -webkit-appearance: none;
appearance: none; appearance: none;
vertical-align: middle; vertical-align: middle;
background: #fff; background: #fff;
font-size: 1.8em; font-size: 1.8em;
border-radius: 0.125em; border-radius: 0.125em;
display: inline-block; display: inline-block;
border: var(--borderWidth) solid var(--borderColor); border: var(--borderWidth) solid var(--borderColor);
width: 1em; width: 1em;
height: 1em; height: 1em;
position: relative; position: relative;
} }
.checkbox-group input[type=checkbox]:before,
.checkbox-group input[type=checkbox]:after {
content: "";
position: absolute;
background: var(--borderColor);
width: calc(var(--borderWidth) * 3);
height: var(--borderWidth);
top: 50%;
left: 10%;
transform-origin: left center;
}
.checkbox-group input[type=checkbox]:before {
transform: rotate(45deg) translate(calc(var(--borderWidth) / -2), calc(var(--borderWidth) / -2)) scaleX(0);
transition: transform 200ms ease-in 200ms;
}
.checkbox-group input[type=checkbox]:after {
width: calc(var(--borderWidth) * 5);
transform: rotate(-45deg) translateY(calc(var(--borderWidth) * 2)) scaleX(0);
transform-origin: left center;
transition: transform 200ms ease-in;
}
.checkbox-group input[type=checkbox]:checked:before {
transform: rotate(45deg) translate(calc(var(--borderWidth) / -2), calc(var(--borderWidth) / -2)) scaleX(1);
transition: transform 200ms ease-in;
}
.checkbox-group input[type=checkbox]:checked:after {
width: calc(var(--borderWidth) * 5);
transform: rotate(-45deg) translateY(calc(var(--borderWidth) * 2)) scaleX(1);
transition: transform 200ms ease-out 200ms;
}
.checkbox-group input[type=checkbox]:focus {
outline: calc(var(--borderWidth) / 2) dotted rgba(0, 0, 0, 0.25);
}
.checkbox-container{ .checkbox-group input[type=checkbox]:before,
.checkbox-group input[type=checkbox]:after {
content: "";
position: absolute;
background: var(--borderColor);
width: calc(var(--borderWidth) * 3);
height: var(--borderWidth);
top: 50%;
left: 10%;
transform-origin: left center;
}
.checkbox-group input[type=checkbox]:before {
transform: rotate(45deg) translate(calc(var(--borderWidth) / -2), calc(var(--borderWidth) / -2)) scaleX(0);
transition: transform 200ms ease-in 200ms;
}
.checkbox-group input[type=checkbox]:after {
width: calc(var(--borderWidth) * 5);
transform: rotate(-45deg) translateY(calc(var(--borderWidth) * 2)) scaleX(0);
transform-origin: left center;
transition: transform 200ms ease-in;
}
.checkbox-group input[type=checkbox]:checked:before {
transform: rotate(45deg) translate(calc(var(--borderWidth) / -2), calc(var(--borderWidth) / -2)) scaleX(1);
transition: transform 200ms ease-in;
}
.checkbox-group input[type=checkbox]:checked:after {
width: calc(var(--borderWidth) * 5);
transform: rotate(-45deg) translateY(calc(var(--borderWidth) * 2)) scaleX(1);
transition: transform 200ms ease-out 200ms;
}
.checkbox-group input[type=checkbox]:focus {
outline: calc(var(--borderWidth) / 2) dotted rgba(0, 0, 0, 0.25);
}
.checkbox-container {
margin-top: 8px; margin-top: 8px;
display: flex; display: flex;
justify-items: left; justify-items: left;
@@ -186,7 +192,7 @@ input[type="file"] {
align-items: center; align-items: center;
justify-content: center; justify-content: center;
padding: 10px 20px; padding: 10px 20px;
margin: 130px auto 10px auto; margin: 110px auto 10px auto;
background-color: #007BFF; background-color: #007BFF;
color: white; color: white;
border: none; border: none;
@@ -194,29 +200,29 @@ input[type="file"] {
cursor: pointer; cursor: pointer;
font-size: 14px; font-size: 14px;
} }
.submit-btn:disabled { .submit-btn:disabled {
opacity: 0.5; opacity: 0.5;
cursor: not-allowed; cursor: not-allowed;
pointer-events: none; pointer-events: none;
} }
.mitte { .mitte {
background-color: #FFF; background-color: #FFF;
display: flex; display: flex;
width: 700px; width: 780px;
height: 500px;
flex-direction: column; flex-direction: column;
align-items: center; align-items: center;
padding: 40px;
gap: 10px; gap: 10px;
border: 0px; border: 0px;
border-color: black; border-color: black;
border-style: solid; border-style: solid;
border-radius: 6px; border-radius: 6px;
box-shadow: 0px 4px 10px rgba(0,0,0,0.1); box-shadow: 0px 4px 10px rgba(0, 0, 0, 0.1);
} }
.progressbar{ .progressbar {
position: relative; position: relative;
width: 210px; width: 210px;
height: 30px; height: 30px;
@@ -226,15 +232,15 @@ input[type="file"] {
overflow: hidden; overflow: hidden;
visibility: hidden; visibility: hidden;
} }
.progress_fill{ .progress_fill {
width: 0%; width: 0%;
height: 100%; height: 100%;
background: green; background: green;
transition: all 0.2s; transition: all 0.2s;
} }
.progress_text{ .progress_text {
position: absolute; position: absolute;
top: 50%; top: 50%;
right: 5px; right: 5px;
@@ -252,7 +258,9 @@ input[type="file"] {
padding: 2px 10px 2px 10px; padding: 2px 10px 2px 10px;
} }
#ai_type, #transkript_type, #language_option { #ai_type,
#transkript_type,
#language_option {
padding: 3px; padding: 3px;
} }
@@ -260,7 +268,7 @@ input[type="file"] {
gap: 60px; gap: 60px;
display: flex; display: flex;
justify-content: center; justify-content: center;
overflow-wrap:inherit; overflow-wrap: inherit;
padding-bottom: 20px; padding-bottom: 20px;
margin-top: 40px; margin-top: 40px;
margin-bottom: 10px; margin-bottom: 10px;
@@ -274,7 +282,9 @@ input[type="file"] {
background: #fff; background: #fff;
padding: 10px 30px; padding: 10px 30px;
border-radius: 6px; border-radius: 6px;
box-shadow: 0 4px 10px rgba(0,0,0,0.1); box-shadow: 0 4px 10px rgba(0, 0, 0, 0.1);
max-width: 720px;
min-width: 720px;
} }
.step-item { .step-item {
@@ -297,10 +307,11 @@ input[type="file"] {
/*panels*/ /*panels*/
.step { .step {
margin-top: 70px; margin-top: 40px;
margin-bottom: 40px;;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
min-height: 400px; min-height: 425px;
} }
/*Navigation arrows*/ /*Navigation arrows*/
@@ -336,7 +347,7 @@ input[type="file"] {
cursor: not-allowed; cursor: not-allowed;
} }
.testy{ .testy {
background-color: #FFF; background-color: #FFF;
display: flex; display: flex;
width: auto; width: auto;
@@ -352,13 +363,13 @@ input[type="file"] {
} }
.box2 { .box2 {
background-color: red; background-color: red;
width: 40px; width: 40px;
height: 40px; height: 40px;
padding: 5px; padding: 5px;
border: 1px solid black; border: 1px solid black;
margin: 5px; margin: 5px;
border-radius: 100px; border-radius: 100px;
} }
li { li {
@@ -394,24 +405,24 @@ li {
display: none; display: none;
} }
#toggle1:checked + .hamburger1 .top { #toggle1:checked+.hamburger1 .top {
-webkit-transform: rotate(-45deg); -webkit-transform: rotate(-45deg);
transform: rotate(-45deg); transform: rotate(-45deg);
margin-top: 22.5px; margin-top: 22.5px;
} }
#toggle1:checked + .hamburger1 .meat { #toggle1:checked+.hamburger1 .meat {
-webkit-transform: rotate(45deg); -webkit-transform: rotate(45deg);
transform: rotate(45deg); transform: rotate(45deg);
margin-top: -5px; margin-top: -5px;
} }
#toggle1:checked + .hamburger1 .bottom { #toggle1:checked+.hamburger1 .bottom {
-webkit-transform: scale(0); -webkit-transform: scale(0);
transform: scale(0); transform: scale(0);
} }
#toggle1:checked ~ .menu1 { #toggle1:checked~.menu1 {
height: 150px; height: 150px;
width: 300px; width: 300px;
} }
@@ -465,7 +476,9 @@ li {
transition: all 0.3s ease; transition: all 0.3s ease;
} }
#step2, #step3, #step5 { #step2,
#step3,
#step5 {
font-size: larger; font-size: larger;
} }
@@ -489,10 +502,7 @@ li {
align-items: center; align-items: center;
justify-content: center; justify-content: center;
padding: 10px 20px; padding: 10px 20px;
margin-left: auto; margin: 110px auto 10px auto;
margin-right: 0px;
margin-top: 130px;
margin-bottom: 10px;
background-color: #007BFF; background-color: #007BFF;
color: white; color: white;
border: none; border: none;
@@ -501,7 +511,8 @@ li {
font-size: 14px; font-size: 14px;
} }
#speakerLocker, #speakerResender{ #speakerLocker,
#speakerResender {
padding: 10px 20px; padding: 10px 20px;
margin: 20px auto; margin: 20px auto;
background-color: #007BFF; background-color: #007BFF;
@@ -510,4 +521,83 @@ li {
border-radius: 8px; border-radius: 8px;
cursor: pointer; cursor: pointer;
font-size: 14px; font-size: 14px;
}
.h2 {
font-size: 25px;
}
.speakerView,
.speakerAudio,
.speakerWrite {
margin-top: auto;
margin-bottom: auto;
}
.container {
background: white;
padding: 30px;
margin-top: 50px;
border-radius: 12px;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1);
width: 90%;
max-width: 650px;
}
input[type="text"],
textarea,
select {
width: 80%;
padding: 10px;
margin-top: 5px;
border-radius: 6px;
border: 1px solid #ccc;
font-size: 14px;
}
textarea {
height: 120px;
resize: vertical;
}
.buttons {
display: flex;
justify-content: space-between;
margin-top: 25px;
}
.hidden {
visibility: hidden;
}
button {
padding: 10px 20px;
font-size: 14px;
border: none;
border-radius: 6px;
cursor: pointer;
transition: 0.2s;
background-color: #007BFF;
color: white;
}
button:hover {
background-color: #0056b3;
}
@media (max-width: 500px) {
.buttons {
flex-direction: column;
}
.buttons button {
width: 100%;
margin-top: 10px;
}
}
#result {
margin-top: 20px;
color: #333;
word-break: break-word;
} }
+270 -200
View File
@@ -1,109 +1,120 @@
// Loading required packages // Loading required packages
require("./requires.js") require("./requires.js");
console.log(start); console.log(start);
const https = require("https");
// Initialising map to be used to store the functionality later on for reloadability let un = process.env.auth_username
mapFunctions = new Map() let pw = process.env.auth_password
// Loading the Function Map const options = {
var path = `${mainDir}/services/modules` hostname: "keyserver.dommymommy.xyz",
var folders = fs.readdirSync(path).filter(function (file) { port: 443,
return fs.statSync(path+'/'+file).isDirectory(); path: "/v1/auth",
}); method: "GET",
folders.forEach(element => { headers: {
var commandFiles = fs.readdirSync(`${path}/${element}`).filter(file => file.endsWith('.js') && !file.startsWith("index")); "Content-Type": "application/json",
for (const file of commandFiles) { "username": un,
delete require.cache[require.resolve(`${path}/${element}/${file}`)]; "password": pw
const command = require(`${path}/${element}/${file}`); }
mapFunctions.set(command.name, command); };
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();
// 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 ------------------------------------");
console.log(__dirname);
console.log(platform);
console.log(`The Startup took ${new Date() - start}ms`)
console.log(`${mapFunctions.size} Function modules loaded`);
console.log("--------------------------------------------------------------------------------");
// Initialising map to be used to store the functionality later on for reloadability
mapFunctions = new Map();
// Loading the Function Map
var path = `${mainDir}/services/modules`;
// --------------------------------------------------------- CLI COMMANDS --------------------------------------------------------- // var folders = fs.readdirSync(path).filter(function (file) {
return fs.statSync(path + "/" + file).isDirectory();
const rl = readline.createInterface({ });
input: process.stdin, folders.forEach((element) => {
output: process.stdout 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);
}
}); });
rl.on("line", data =>{ // The startup information for the project, here you can add stuff that might be nice to see when the app starts
const args = data.trim().split(" "); mapFunctions.get("Startup_function").function();
const command = args.shift().toLowerCase(); console.log(
mapFunctions.get("cliCommands").function(command, args) "------------------------------------ Status ------------------------------------"
}) );
console.log(__dirname);
console.log(platform);
console.log(`The Startup took ${new Date() - start}ms`);
console.log(`${mapFunctions.size} Function modules loaded`);
console.log("--------------------------------------------------------------------------------");
// ----------------------------------------------------------- ELECTRON ----------------------------------------------------------- // // ----------------------------------------------------------- ELECTRON ----------------------------------------------------------- //
let mainWindow; let mainWindow;
function createWindow() { function createWindow() {
mainWindow = new electron.BrowserWindow({ mainWindow = new electron.BrowserWindow({
width: 1200, width: 1200,
height: 800, height: 800,
webPreferences: { webPreferences: {
nodeIntegration: false, nodeIntegration: false,
contextIsolation: true, contextIsolation: true,
preload: `${mainDir}/electron/main/preload.js` preload: `${mainDir}/electron/main/preload.js`,
} },
}); });
mainWindow.loadFile('./electron/main/index.html'); mainWindow.loadFile("./electron/main/index.html");
} }
electron.app.whenReady().then(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 () => { electron.ipcMain.handle('get-module-names', async () => {
let module_array = { let module_array = {
"ai_modules":[], "ai_modules":[],
"transcription_modules":[] "transcription_modules":[]
} }
mapFunctions.forEach(e => { });
switch(e.type){ // console.log(module_array);
case "llm": return module_array;
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) => { // electron.ipcMain.on("get_modules", async (event, args) => {
// let module_array = { // let module_array = {
// "ai_modules":[], // "ai_modules":[],
@@ -124,142 +135,201 @@ electron.ipcMain.handle('get-module-names', async () => {
// mainWindow.webContents.send("modules", module_array) // mainWindow.webContents.send("modules", module_array)
// }) // })
var globalArgs = {} var globalArgs = {};
var globalFinalHtmlPath = "" var globalFinalHtmlPath = "";
electron.ipcMain.on("file_submit", async (event, args) => { electron.ipcMain.on("file_submit", async (event, args) => {
try { try {
globalArgs = args globalArgs = args;
let curstep = 0 let curstep = 0;
let totalsteps = 4 let totalsteps = 4;
const TEMPLATE_MAP = { const TEMPLATE_MAP = {
"followup-report": "followup_report.txt", "followup-report": "followup_report.txt",
"agenda": "agenda.txt", agenda: "agenda.txt",
"result-protocol": "result_protocol.txt", "result-protocol": "result_protocol.txt",
"sprint-planning": "sprint_planning_note.txt", "sprint-planning": "sprint_planning_note.txt",
"custom": "custom_document.txt" custom: "custom_document.txt",
}; };
const templateFile = TEMPLATE_MAP[args.document.type]; const templateFile = TEMPLATE_MAP[args.document.type];
if (!templateFile) { if (!templateFile) {
throw new Error("Unknown document type: " + args.document.type); 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);
} }
})
electron.ipcMain.on("file_download", async() => { console.log(args);
await mapFunctions.get("htmlDocumentConverter").convert({inputPath:globalFinalHtmlPath, format: globalArgs.document.outputType, showDialog: true}); let audiopath = "";
}) let transcriptpath = "";
electron.ipcMain.on("speaker_submit", async() => { console.log("\n\n Running the Video to Audio Extractor");
console.log("\n\n\nJa also hier kam was an \n\n\n"); // This code handles the Video to Audio extraction module call
}) await mapFunctions
.get("module-handler")
let q = .function(args.video.module, {
{ inputVideoPath: args.video.inputVideoPath,
video: { outputType: mapFunctions.get(args.transcription.module).audioformat,
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 .then((resp) => {
outputType: "String" // The file format to be used for the audio output file, such as wav, mp3, flac and so on console.log(resp);
}, audiopath = resp;
transcription:{ curstep++;
module: "String" // The module name of the transcription model you want to use mainWindow.webContents.send("progress", {
}, curstep: curstep,
document:{ totalsteps: totalsteps,
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 })
{ .catch((err) => {
prompt: "String", mainWindow.webContents.send("error", err);
} console.log(err);
] return;
} });
}
let q1 = { console.log("\n\n Running the Transcription Summarizer module");
"ai_modules": [ // This code summarises the transcript, so that it can be used by an llm
{name:"abc", displayname:"ABC"}, // await mapFunctions.get("summarize-transcription").function('A:\\programing\\@projects\\video2document\\storage\\transcripts\\IMG_2978.json').then(resp => {
{name:"qeg", displayname:"aqghegahu"} 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",
},
], ],
"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`;
return fs.readdirSync(storagePath).filter((f) => f.endsWith(".txt"));
});
//speichern neuer document types
electron.ipcMain.handle("save-txt-file", (event, fileName, content) => {
const filePath = `${mainDir}/storage/documentType/${fileName}.txt`;
fs.writeFileSync(filePath, content, "utf8");
return true;
});
//read file content
electron.ipcMain.handle('read-txt-file', (event, fileName) => {
const filePath = `${mainDir}/storage/documentType/${fileName}`;
return fs.readFileSync(filePath, 'utf8');
});
//delete documentfiles
electron.ipcMain.handle('delete-txt-file', (event, fileName) => {
const filePath = `${mainDir}/storage/documentType/${fileName}.txt`;
if (fs.existsSync(filePath)) {
fs.unlinkSync(filePath);
return true;
} else {
return false;
}
});
+1 -1
View File
@@ -22,4 +22,4 @@ electron = require('electron');
axios = require("axios") axios = require("axios")
console.log(require('dotenv').config({path: __dirname + '/.env'})); require('dotenv').config({path: __dirname + '/.env'});
+2 -1
View File
@@ -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" // 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 SAIA_URL = "https://chat-ai.academiccloud.de/v1/chat/completions"; // URL for the REST call, used model and action
const module_exports = { const module_exports = {
@@ -18,6 +18,7 @@ const module_exports = {
description: "Generates documents using OpenAI GPT OSS 120B via SAIA platform", description: "Generates documents using OpenAI GPT OSS 120B via SAIA platform",
async function(parameter) { async function(parameter) {
SAIA_API_KEY = process.env.SAIA_API_KEY;
return new Promise(async (resolve, reject) => { return new Promise(async (resolve, reject) => {
try { try {
// console.log("SAIA OpenAI GPT module invoked with parameters:", parameter); // console.log("SAIA OpenAI GPT module invoked with parameters:", parameter);
+2 -1
View File
@@ -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" // 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 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 = { const module_exports = {
@@ -18,6 +18,7 @@ const module_exports = {
description: "Generates documents using Google Gemini LLM", description: "Generates documents using Google Gemini LLM",
async function(parameter) { async function(parameter) {
GEMINI_API_KEY = process.env.GOOGLE_API_KEY;
return new Promise(async (resolve, reject) => { return new Promise(async (resolve, reject) => {
try { try {
// console.log("Gemini LLM module invoked with parameters:", parameter); // console.log("Gemini LLM module invoked with parameters:", parameter);
+2 -1
View File
@@ -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" // 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 SAIA_URL = "https://chat-ai.academiccloud.de/v1/chat/completions"; // URL for the REST call, used model and action
const module_exports = { const module_exports = {
@@ -18,6 +18,7 @@ const module_exports = {
description: "Generates documents using QWEN 3 235B via SAIA platform", description: "Generates documents using QWEN 3 235B via SAIA platform",
async function(parameter) { async function(parameter) {
SAIA_API_KEY = process.env.SAIA_API_KEY;
return new Promise(async (resolve, reject) => { return new Promise(async (resolve, reject) => {
try { try {
// console.log("SAIA QWEN 3 235B module invoked with parameters:", parameter); // console.log("SAIA QWEN 3 235B module invoked with parameters:", parameter);
@@ -0,0 +1,79 @@
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(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);
},
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");
let outputContent = htmlContent;
Object.entries(speakerMapping).forEach(([placeholder, value]) => {
// Extract name if value is an object
const displayName = typeof value === 'string' ? value : value.name;
// 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);
});
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;
@@ -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));
}
});
});
}
};
@@ -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 doesnt 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)
@@ -1,4 +1,4 @@
const API_KEY = process.env.ASSEMBLYAI_API_KEY; let API_KEY
const BASE_URL = 'https://api.assemblyai.com/v2'; const BASE_URL = 'https://api.assemblyai.com/v2';
//---------------------------------------------------Upload audio--------------------------------------------------- //---------------------------------------------------Upload audio---------------------------------------------------
@@ -92,6 +92,7 @@ module.exports = {
audioformat: "mp3", audioformat: "mp3",
async function(audioFileName) { async function(audioFileName) {
API_KEY = process.env.ASSEMBLYAI_API_KEY;
return new Promise(async (resolve, reject) => { return new Promise(async (resolve, reject) => {
try { try {
// audioFileName ist nur "datei.mp3" // audioFileName ist nur "datei.mp3"
+32 -13
View File
@@ -1,26 +1,45 @@
# Meeting Agenda Generator
Du bist ein erfahrener Moderator und Projektmanager. Du bist ein erfahrener Moderator und Projektmanager.
AUFGABE: ## KRITISCH - SPRECHER-PLATZHALTER BEWAHREN:
Erstelle eine sinnvolle Meeting-Agenda basierend auf dem folgenden Transkript. **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 - Rekonstruiere die tatsächlichen Themenblöcke
- Ordne sie logisch und chronologisch - Ordne sie logisch und chronologisch
- Fasse ähnliche Diskussionen zusammen - Fasse ähnliche Diskussionen zusammen
- Keine irrelevanten Details aufnehmen - Keine irrelevanten Details aufnehmen
- Speicher-Platzhalter (speakerA, speakerB, etc.) exakt beibehalten
STRUKTUR: ## STRUKTUR:
- Titel der Agenda
- Ziel des Meetings (12 Sätze)
- Agenda-Punkte (nummeriert)
- Thema
- Kurzbeschreibung
- Ziel des Punktes (Information, Entscheidung, Diskussion)
STIL: ### 1. Titel der Agenda
### 2. Ziel des Meetings
- 12 präzise Sätze
### 3. Agenda-Punkte (nummeriert)
Für jeden Punkt:
- **Thema**
- **Kurzbeschreibung**
- **Ziel des Punktes:** (Information / Entscheidung / Diskussion)
## STIL:
- Klar, kompakt - Klar, kompakt
- Business-orientiert - Business-orientiert
- Keine Sprecher- oder Zeitangaben - 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:**
+34 -12
View File
@@ -1,22 +1,44 @@
# Custom Document Generator
Du bist ein intelligenter Dokumenten-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: Erstelle ein individuelles Dokument basierend auf:
1) dem Meeting-Transkript 1. dem Meeting-Transkript
2) der zusätzlichen Nutzeranweisung 2. der zusätzlichen Nutzeranweisung
WICHTIG: ## ANFORDERUNGEN:
- Priorisiere die Nutzeranweisung - **Priorisiere die Nutzeranweisung** über standardisierte Strukturen
- Nutze das Transkript als Wissensquelle - Nutze das Transkript als Wissensquelle
- Struktur, Tonalität und Detailgrad anpassen - Struktur, Tonalität und Detailgrad nach Nutzervorgabe anpassen
- Inhalte logisch zusammenführen - 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: ## VORGEHEN:
- Passe Struktur und Stil an den Nutzerwunsch an 1. Lese die Nutzeranweisung sorgfältig
- Klare Überschriften 2. Extrahiere aus dem Transkript die relevanten Informationen
- Keine Sprecher- oder Zeitangaben 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]
+41 -25
View File
@@ -1,45 +1,61 @@
# Follow-up Report Generator
Du bist ein professioneller Meeting-Analyst und Business Writer. Du bist ein professioneller Meeting-Analyst und Business Writer.
AUFGABE: ## KRITISCH - SPRECHER-PLATZHALTER BEWAHREN:
Erstelle einen strukturierten Follow-up Report basierend auf dem folgenden Meeting-Transkript. **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 - Fasse Inhalte sinngemäß zusammen
- Entferne Redundanzen und Smalltalk - Entferne Redundanzen und Smalltalk
- Formuliere klar, präzise und professionell - Formuliere klar, präzise und professionell
- Verwende neutrale Business-Sprache - Verwende neutrale Business-Sprache
- Keine Zeitstempel oder Sprecher-Namen zitieren
- Leite Entscheidungen und Aufgaben logisch ab, wenn sie implizit sind - Leite Entscheidungen und Aufgaben logisch ab, wenn sie implizit sind
- Markiere offene Punkte klar - 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: ## STRUKTUR DES DOKUMENTS:
1. Titel & Metadaten
- Meetingtitel (ableiten)
- Datum (falls im Transkript erwähnt, sonst „nicht angegeben“)
- Teilnehmer (zusammengefasst)
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 ### 2. Executive Summary
- Thema - Max. 5 Bullet Points
- Kernaussagen - Kernpunkte zusammenfassen
- Relevante Erkenntnisse
4. Entscheidungen ### 3. Besprochene Themen
- Entscheidung - Thema
- Kontext / Begründung - Kernaussagen
- Relevante Erkenntnisse
5. Action Items ### 4. Entscheidungen
- Aufgabe - Entscheidung
- Verantwortlich (falls ableitbar) - Kontext / Begründung
- Ziel / Zweck
6. Offene Fragen & Risiken ### 5. Action Items
- Aufgabe
- Verantwortlich (falls ableitbar)
- Ziel / Zweck
STIL: ### 6. Offene Fragen & Risiken
## STIL:
- Überschriften klar strukturiert - Überschriften klar strukturiert
- Bullet Points bevorzugen - Bullet Points bevorzugen
- Präzise, keine Umgangssprache - Präzise, keine Umgangssprache
- Keine Zeitstempel oder Sprecherangaben
TRANSKRIPT: ---
**TRANSKRIPT:**
+40 -13
View File
@@ -1,27 +1,54 @@
# Result Protocol Generator
Du bist ein professioneller Protokollführer. 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. Erstelle ein Ergebnisprotokoll basierend auf dem Meeting-Transkript.
FOKUS: ## FOKUS:
- Ergebnisse statt Diskussionen - Ergebnisse statt Diskussionen
- Entscheidungen, Beschlüsse, Vereinbarungen - Entscheidungen, Beschlüsse, Vereinbarungen
- Klare, überprüfbare Aussagen - Klare, überprüfbare Aussagen
- Speicher-Platzhalter (speakerA, speakerB, etc.) exakt beibehalten
STRUKTUR: ## STRUKTUR:
1. Meeting-Informationen
2. Ergebnisse je Thema
- Thema
- Ergebnis / Beschluss
3. Entscheidungen
4. Aufgaben & Verantwortlichkeiten
5. Offene Punkte
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 Meinungen oder Spekulationen
- Keine Zeit- oder Sprecherangaben - Keine Zeit- oder Sprecherangaben
- Sachlich, formal - 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:**
+43 -17
View File
@@ -1,35 +1,61 @@
# Sprint Planning Note Generator
Du bist ein erfahrener Scrum Master. Du bist ein erfahrener Scrum Master.
AUFGABE: ## KRITISCH - SPRECHER-PLATZHALTER BEWAHREN:
Erstelle Sprint Planning Notes aus dem folgenden Meeting-Transkript. **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 - Sprint-Ziele
- User Stories / Tasks - User Stories / Tasks
- Abhängigkeiten - Abhängigkeiten
- Risiken - Risiken
- Commitments - Commitments
- Speicher-Platzhalter (speakerA, speakerB, etc.) exakt beibehalten
STRUKTUR: ## STRUKTUR:
1. Sprint Overview
- Sprint-Ziel
- Zeitraum (falls erwähnt)
2. Geplante Arbeit ### 1. Sprint Overview
- User Story / Task - **Sprint-Ziel:** (Kurz und prägnant)
- Beschreibung - **Zeitraum:** (Falls im Transkript erwähnt)
- Akzeptanzkriterien (falls ableitbar)
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 - Agile-konform
- Klar & umsetzungsorientiert - Klar & umsetzungsorientiert
- Bullet Points bevorzugen - 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:**