Compare commits

...

17 Commits

Author SHA1 Message Date
emily 55df227599 Added Documentation for the API Key authentication system 2026-01-21 11:04:43 +01:00
Spanier, Pit 5c7b1ac5e9 Merge branch 'feature/ui-test' into 'develop'
Removed the "confirm alert" in the delete costume document function. It made...

See merge request proj-wise2526-video2document/video2document!102
2026-01-21 10:55:15 +01:00
Verena Schulz c202d3960b Small changes to p-menu1 2026-01-20 17:57:03 +01:00
Verena Schulz ec64564a51 Merge branch 'feature/ui-test' of gitlab.rlp.net:proj-wise2526-video2document/video2document into feature/ui-test 2026-01-20 17:55:09 +01:00
Verena Schulz c1efe77ec1 Added UI documentation to documentation.md 2026-01-20 17:54:37 +01:00
eric.minning f706dcaf79 Removed the "confirm alert" in the delete costume document function. It made the textarea unklickable 2026-01-20 16:46:32 +01:00
eric.minning dacfa2094c Fixed a problem with the costum document text display 2026-01-19 18:31:06 +01:00
eric.minning 49e2724ad3 Removed unused files 2026-01-19 15:30:56 +01:00
eric.minning c43ec93894 Fixed and expanded the change language function. fixed custome document creation visibility 2026-01-19 15:22:32 +01:00
Minning, Eric 5ab29e8423 Merge branch 'develop' into 'feature/ui-test'
Develop

See merge request proj-wise2526-video2document/video2document!101
2026-01-19 14:44:23 +01:00
Hughes, Mike 36c0fde0b1 Merge branch 'fix/document-output-type' into 'develop'
Update output type option from '.word' to '.docx' in index.html

See merge request proj-wise2526-video2document/video2document!100
2026-01-18 19:44:41 +01:00
MikeHughes-BIN 4ca0d46aa9 Update output type option from '.word' to '.docx' in index.html 2026-01-18 19:41:40 +01:00
Hughes, Mike e88e0c45c0 Merge branch 'fix/redundant-function-call' into 'develop'
Remove duplicated file_download event handler from ipcMain

See merge request proj-wise2526-video2document/video2document!98
2026-01-18 19:28:36 +01:00
Hughes, Mike 8341861ae2 Merge branch 'fix/language' into 'develop'
Fix/language

See merge request proj-wise2526-video2document/video2document!99
2026-01-18 19:27:56 +01:00
MikeHughes-BIN b6276b005e Removed Parakeet again as it requires PyTorch which is too large for this project 2026-01-18 19:13:18 +01:00
MikeHughes-BIN 8f2024df14 Refactor HTML structure for improved readability and maintainability 2026-01-18 19:12:04 +01:00
MikeHughes-BIN 0f548b3012 Add function to send speaker packages and handle errors 2026-01-18 19:05:55 +01:00
11 changed files with 568 additions and 658 deletions
+116 -7
View File
@@ -4,7 +4,9 @@
1. [How to run the Software](#how-to-run-the-software) 1. [How to run the Software](#how-to-run-the-software)
2. [How it works](#how-it-works) 2. [How it works](#how-it-works)
3. [Modules](#modules) 3. [Modules](#modules)
3. [IPC](#ipc) 4. [IPC](#ipc)
5. [Authentication](#authentication)
6. [UI](#ui)
## How to run the Software ## How to run the Software
If you read the readme file, you will see the basic setup command in order to run the program. If you read the readme file, you will see the basic setup command in order to run the program.
@@ -17,13 +19,9 @@ Next up you need to set up the .env file.
The file must contain your keys for the modules you want to use. The file must contain your keys for the modules you want to use.
The .env file looks like this: The .env file looks like this:
``` ```
ASSEMBLYAI_API_KEY=wefhjhjakeghjkahejkghjkaegh auth_username=wefhjhjakeghjkahejkghjkaegh
GOOGLE_API_KEY=wefhjhjakeghjkahejkghjkaegh auth_password=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. 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. Alternatively you can double click the start.bat if you are on Windows for example.
@@ -114,3 +112,114 @@ 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. 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. 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. You can always add fields to it, but dont remove the ones from above.
## Authentication
Our Software uses a custom API key management System.
This system itself is proprietary, and will as such not be delivered with the software.
The way it works is simply via a HTTP request.
In the current version, the main reads the username and password for authentication from the .env file, and then uses these in the header for the HTTP request.
```
hostname: "keyserver.dommymommy.xyz", // The URL to the key server
port: 443, // The Port of the
path: "/v1/auth", // The API Endpoint
method: "GET",
headers: {
"Content-Type": "application/json", // The content type should be JSON
"username": un, // the Username used to authenticate
"password": pw // The password used to authenticate
}
```
### The Important bit of this whole setup
Once the HTTP request is made, it will return a JSON object with the API keys as fields.
One such output could look like this:
```json
{
"ASSEMBLYAI_API_KEY": "eajgjkhgahghahegoikh",
"GOOGLE_API_KEY": "eajgjkhgahghahegoikh",
"SAIA_API_KEY": "eajgjkhgahghahegoikh"
}
```
The key for each entry is being used to store the key in memory.
Specifically under `process.env`
So, if everything in this request worked out, we will have:
```js
process.env.ASSEMBLYAI_API_KEY
process.env.GOOGLE_API_KEY
process.env.SAIA_API_KEY
```
These variables are accessible anywhere in the code and contain the API Keys, so make sure you dont add some untrusted modules that could steal these API Keys.
## UI
The UI has a simple, self-explanatory design, in white and blue.
For easy handling and understanding, the UI is using 6 steps to guide the user through the process and offers a help page
with more defined explanations regarding the steps of the GUI. All parts used in the GUI are stored in the directory `./electron/main`.
Files used for the UI:
- index.html
- help_page.html
- style.css
- script.js
- renderer.js
- preload.js
- languages.js
- package-lock.json
- package.json
Folders used for the UI:
- /flags
- /icons
- /node_modules
**index.html:**
This file is the basic framework of our software. Comments in the code define the different UI sections.
The comments are the headlines of the code below them.
**help_page.html:**
This is the html to the help page that is accessible though the burger menu in the software.
Currently only available in german. It describes the different parts of the program in more depth.
**style.css:**
Contains all the css code of the software used in the UI.
**script.js:**
Stores all functions used in the UI.
The code is separated by comments in their matching UI section.
**renderer.js:**
Mainly contains every listener function used in the UI, which listens to any events occuring in the UI,
to handle these events as intended.
The code is separated by comments in their matching UI section.
**preload.js:**
Contains IPC functions to allow communication between the UI and the main process.
**languages.js:**
Holds one JSON, which is used to store the different language variables. These are used
in the script.js for the change of the displayed language of the UI. Add languages here, if
you want to add more options in the language selection.
How to add more languages:
1. Add another language block, like an existing one in the file. (Note: Use every key, which is also used in the other sections,
beside the first key like "eng". This first key should be always unique from the others)
2. Assign the desired values to the keys in the new language section.
How to add more text which changes languages:
1. Create the element in the html file with an unique id.
2. Add this id to every language section and assign them a matching value.
3. Add inside the `script.js` file, inside the `changeLanguage()` function a document call like the others. Except with our id.
**package-log.json:**
It's an electron module file. No changes needed.
**package.json:**
This is an electron base file. No changes needed.
**/flags:**
This directory contains the flags used for the language selection dropdown menu.
**/icons:**
Pictures for the document preview are stored here.
**/node_modules:**
Contains nodes used by electron.
-154
View File
@@ -1,154 +0,0 @@
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Custom Document</title>
<style>
</style>
</head>
<body>
<div class="container">
<h1>Manage document types</h1>
<label for="existingDocs">Vorhandene Dokumententypen auswählen (optional):</label>
<!--Drop Down-->
<select name="existingDocs" id="existingDocs">
<option value="newDoc">-- Neuen Dokumententyp erstellen --</option>
</select>
<div id="docNameWrapper">
<label for="docName">Name des Dokumententyps:</label>
<input type="text" id="docName" placeholder="Gib hier den Namen für den Dokumententyp ein">
</div>
<label for="prompt">Dein Prompt:</label>
<textarea id="prompt" placeholder="Schreibe hier den Prompt für dein Dokumententyp..."></textarea>
<div class="buttons">
<a href="index.html">
<button id="goBackBtn">Abbrechen</button>
</a>
<button id="deleteBtn">Dokumententyp löschen</button>
<button id="generateBtn">Dokumententyp speichern</button>
</div>
<div id="result"></div>
</div>
<script src="languages.js"></script>
<script>
const goBackBtn = document.getElementById("goBackBtn");
const generateBtn = document.getElementById("generateBtn");
const deleteBtn = document.getElementById("deleteBtn");
const existingDocs = document.getElementById("existingDocs");
const docNameInput = document.getElementById("docName");
const promptInput = document.getElementById("prompt");
const resultDiv = document.getElementById("result");
const exampleText = "";
// dokumente speichern
generateBtn.addEventListener("click", () => {
const name = docNameInput.value.trim();
const content = promptInput.value.trim();
if (!name || !content) {
resultDiv.textContent = "Bitte Name des Dokumententyps und Prompt ausfüllen.";
setTimeout(() => {
resultDiv.textContent = "";
}, 3000);
return;
}
window.api.saveTxtFile(name, content).then();
resultDiv.textContent = "Dokumententyp erfolgreich gespeichert!";
setTimeout(() => {
resultDiv.textContent = "";
}, 3000);
reloadDocuments();
});
// dokumente löschen
deleteBtn.addEventListener("click", () => {
const name = docNameInput.value.trim();
if (!name) {
resultDiv.textContent = "Bitte Name des Dokumententyps angeben.";
setTimeout(() => {
resultDiv.textContent = "";
}, 3000);
return;
}
const confirmDelete = confirm(
`Möchtest du den Dokumententyp "${name}" wirklich löschen?`
);
if (!confirmDelete) return;
window.api.deleteTxtFile(name).then((success) => {
if (success) {
resultDiv.textContent = "Dokumententyp erfolgreich gelöscht!";
reloadDocuments();
existingDocs.value = "newDoc";
existingDocs.dispatchEvent(new Event("change"));
} else {
resultDiv.textContent = "Dokumententyp konnte nicht gelöscht werden.";
}
setTimeout(() => {
resultDiv.textContent = "";
}, 3000);
});
});
//function to load existingDoc options to the drop down list
const select = document.getElementById('existingDocs');
window.api.getTxtFiles().then(files => {
reloadDocuments();
});
//content anzeigen
const docNameWrapper = document.getElementById("docNameWrapper");
existingDocs.addEventListener("change", async () => {
const selected = existingDocs.value;
if (selected === "newDoc") {
docNameWrapper.classList.remove("hidden");
docNameInput.value = "";
promptInput.value = exampleText;
return;
}
docNameWrapper.classList.add("hidden");
const content = await window.api.readTxtFile(selected);
promptInput.value = content;
docNameInput.value = selected.replace(".txt", "");
});
//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);
});
});
}
</script>
</body>
</html>
+328 -243
View File
@@ -1,255 +1,340 @@
<!DOCTYPE html> <!doctype html>
<html lang="de"> <html lang="de">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title id="title">Video to document</title>
<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>
<meta charset="UTF-8"> <div id="h1-wrapper">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <section class="p-menu1">
<title id="title">Video to document</title> <nav id="navbar" class="navigation" role="navigation">
<link rel="stylesheet" href="style.css"> <input id="toggle1" type="checkbox" />
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/lc-select@1.3.0/themes/light.css"> <label class="hamburger1" for="toggle1">
</head> <div class="top"></div>
<div class="meat"></div>
<div class="bottom"></div>
</label>
<body> <nav class="menu1">
<button id="customDocBtn" onclick="showCD()">
<div id="h1-wrapper"> Manage document types
<section class="p-menu1"> </button>
<nav id="navbar" class="navigation" role="navigation"> <a href="help_page.html" class="li1">Help</a>
<input id="toggle1" type="checkbox" /> </nav>
<label class="hamburger1" for="toggle1">
<div class="top"></div>
<div class="meat"></div>
<div class="bottom"></div>
</label>
<nav class="menu1">
<button id="customDocBtn" onclick="showCD()">Manage document types</button>
<a href="help_page.html" class="li1">Help</a>
</nav> </nav>
</nav> </section>
</section>
<h1 id="h1">Video to document</h1> <h1 id="h1">Video to document</h1>
<div class="gui-language"> <div class="gui-language">
<select name="language_option" id="language_option"></select> <select name="language_option" id="language_option"></select>
</div>
</div>
<div class="step-nav">
<div class="step-item active" data-step="1" id="step_nav1">1. Step</div>
<div class="step-item" data-step="2" id="step_nav2">2. Step</div>
<div class="step-item" data-step="3" id="step_nav3">3. Step</div>
<div class="step-item" data-step="4" id="step_nav4">4. Step</div>
<div class="step-item" data-step="5" id="step_nav5">5. Step</div>
<div class="step-item" data-step="6" id="step_nav6">6. Step</div>
</div>
<div id="middleContainerWrapper" class="middle-container-wrapper">
<button id="prevBtn" class="navBtn" disabled>&larr;</button>
<!-- Visible middle part-->
<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">
<h2 class="h2">Upload your video here:</h2>
<div class="upload-container" id="uploadContainer">
<p id="p1">Drag and drop video file</p>
<video id="previewThumbnail" autoplay="false">
</video>
<div class="file-name" id="fileName">No video chosen</div>
<div id="thumbnailContainer">
<img id="thumbnailImage" style="display:none;">
</div>
<button class="custom-btn" id="manualUploadBtn">Search video</button>
<input type="file" id="videoUpload" accept="video/*">
</div>
</div>
<!-- Here starts code from step 2-->
<div class="step" id="step2" style="display:none;">
<h2 class="h2">Choose your preferences:</h2>
<div class="step2-form">
<div class="step2-row">
<label>Select ki:</label>
<select id="ai_type"></select>
</div>
<div class="step2-row">
<label>Select transcription:</label>
<select id="transkript_type"></select>
</div>
<div class="step2-row">
<label>Select type:</label>
<select id="output_type">
<option>.pdf</option>
<option>.docx</option>
<option>.txt</option>
</select>
</div>
<div class="step2-row">
<label>Select language:</label>
<select id="document_language_option"></select>
</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="checkbox-group">
<h2 class="h2">Choose prefered document style:</h2>
<div class="checkbox-container">
<input type="checkbox" name="docFormat" id="docFormat" value="followup-report">
<label id="label_format" for="docFormat">Follow-up Report</label>
<div class="figure1">
<img class="img-icon" src="icons/question-mark-button-icon--free-clip-art-30.png">
<img class="img-hover1" src="flags/germany-flag-png-large.jpg">
</div>
</div>
<div class="checkbox-container">
<input type="checkbox" name="docFormat" id="docFormatSummary1" value="agenda">
<label id="label_summary" for="docFormatSummary">Agenda</label>
<div class="figure2">
<img class="img-icon" src="icons/question-mark-button-icon--free-clip-art-30.png">
<img class="img-hover2" src="flags/india-flag-png-large.png">
</div>
</div>
<div class="checkbox-container">
<input type="checkbox" name="docFormat" id="docFormatSummary2" value="result-protocol">
<label id="label_summary" for="docFormatSummary">Resultprotocol</label>
<div class="figure3">
<img class="img-icon" src="icons/question-mark-button-icon--free-clip-art-30.png">
<img class="img-hover3" src="flags/united-kingdom-flag-png-large.jpg">
</div>
</div>
<div class="checkbox-container">
<input type="checkbox" name="docFormat" id="docFormatSummary3" value="sprint-planning">
<label id="label_summary" for="docFormatSummary">Sprint Planning Note</label>
<div class="figure4">
<img class="img-icon" src="icons/question-mark-button-icon--free-clip-art-30.png">
<img class="img-hover4" src="flags/germany-flag-png-large.jpg">
</div>
</div>
<div class="checkbox-container">
<input type="checkbox" name="docFormat" id="docFormatCustom" value="custom">
<select name="customDocumentTypes" id="customDocumentTypes">
</select>
</div>
</div>
</div>
<!-- Here starts code from step 4-->
<div class="step" id="step4" style="display:none;">
<h2 class="h2">Click to submit:</h2>
<button class="submit-btn" id="submitButton" onclick="checkBoxes()" disabled>Submit</button>
<div class="testy" id="testy">
<div class="box2" id="box1">
</div>
<p id="box1_p1">---Starting---</p>
<div class="box2" id="box2">
</div>
<p id="box2_p2">---Transkribing---</p>
<div class="box2" id="box3">
</div>
<p id="box3_p3">---Document creation---</p>
<div class="box2" id="box4">
</div>
</div>
</div>
<!-- Here starts code from step 5-->
<div class="step" id="step5" style="display:none;">
<h2 class="h2">Change names of the speakers:</h2>
<div class="speaker-container">
<table class="speaker-table">
<tbody>
<tr>
<td class="label-cell">
<label id="labelSpeaker" for="cur_speaker">Select Speaker:</label>
</td>
<td class="input-cell">
<select name="cur_speaker" id="cur_speaker"></select>
</td>
</tr>
<tr>
<td class="label-cell">
<label id="labelSpeakerAudio">Speaker Audio:</label>
</td>
<td class="input-cell">
<audio controls id="speakerAudioViewer">
Currently there is no audio file here.
</audio>
</td>
</tr>
<tr>
<td class="label-cell">
<label id="labelSpeakerWriter" for="newSpeaker">New Name:</label>
</td>
<td class="input-cell">
<input type="text" id="newSpeaker" placeholder="Enter new speaker name">
</td>
</tr>
</tbody>
</table>
<div class="speaker-button-group">
<button id="speakerLocker" onclick="rewriteSpeakerName()">Rename Speaker</button>
<button id="speakerResender" onclick="sendSpeakerPackages()">Rewrite Document</button>
</div>
</div>
</div>
<!-- Here starts code from step 6-->
<div class="step" id="step6" style="display:none;">
<h2 class="h2">Click to download your document:</h2>
<button class="download-btn" id="downloadButton" onclick="fileDownload()">Download</button>
</div> </div>
</div> </div>
<button id="nextBtn" class="navBtn">&rarr;</button> <div class="step-nav">
<div class="step-item active" data-step="1" id="step_nav1">1. Step</div>
<div class="step-item" data-step="2" id="step_nav2">2. Step</div>
<div class="step-item" data-step="3" id="step_nav3">3. Step</div>
<div class="step-item" data-step="4" id="step_nav4">4. Step</div>
<div class="step-item" data-step="5" id="step_nav5">5. Step</div>
<div class="step-item" data-step="6" id="step_nav6">6. Step</div>
</div>
</div> <div id="middleContainerWrapper" class="middle-container-wrapper">
<script src="https://cdn.jsdelivr.net/npm/lc-select@1.3.0/lc_select.min.js"></script> <button id="prevBtn" class="navBtn" disabled>&larr;</button>
<script src="languages.js"></script>
<script src="script.js"></script>
<script src="./renderer.js"></script>
</body>
<!-- Visible middle part-->
<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">
<h2 class="h2" id="step1_h2">Upload your video here:</h2>
<div class="upload-container" id="uploadContainer">
<p id="p1">Drag and drop video file</p>
<video id="previewThumbnail" autoplay="false"></video>
<div class="file-name" id="fileName">No video chosen</div>
<div id="thumbnailContainer">
<img id="thumbnailImage" style="display: none" />
</div>
<button class="custom-btn" id="manualUploadBtn">
Search video
</button>
<input type="file" id="videoUpload" accept="video/*" />
</div>
</div>
<!-- Here starts code from step 2-->
<div class="step" id="step2" style="display: none">
<h2 class="h2" id="step2_h2">Choose your preferences:</h2>
<div class="step2-form">
<div class="KI-wrapper">
<label id="labelKI">Select ki:</label>
<select name="ai_type" id="ai_type"></select>
</div>
<div class="transcript-wrap">
<label id="labelTranscription">Select transcription:</label>
<select name="transkript_type" id="transkript_type"></select>
</div>
<div class="type-wrapper">
<label id="labelType">Select type:</label>
<select name="output_type" id="output_type">
<option value="pdf">.pdf</option>
<option value="docx">.docx</option>
<option value="txt">.txt</option>
</select>
</div>
<div class="language-wrapper">
<label id="labelLanguage">Select language:</label>
<select
name="document_language_option"
id="document_language_option"
></select>
</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="checkbox-group">
<h2 class="h2" id="step3_h2">Choose prefered document style:</h2>
<div class="checkbox-container">
<input
type="checkbox"
name="docFormat"
id="docFormat"
value="followup-report"
/>
<label id="label_format" for="docFormat">Follow-up Report</label>
<div class="figure1">
<img
class="img-icon"
src="icons/question-mark-button-icon--free-clip-art-30.png"
/>
<img
class="img-hover1"
src="flags/germany-flag-png-large.jpg"
/>
</div>
</div>
<div class="checkbox-container">
<input
type="checkbox"
name="docFormat"
id="docFormatSummary1"
value="agenda"
/>
<label id="label_summary" for="docFormatSummary">Agenda</label>
<div class="figure2">
<img
class="img-icon"
src="icons/question-mark-button-icon--free-clip-art-30.png"
/>
<img class="img-hover2" src="flags/india-flag-png-large.png" />
</div>
</div>
<div class="checkbox-container">
<input
type="checkbox"
name="docFormat"
id="docFormatSummary2"
value="result-protocol"
/>
<label id="label_summary" for="docFormatSummary"
>Resultprotocol</label
>
<div class="figure3">
<img
class="img-icon"
src="icons/question-mark-button-icon--free-clip-art-30.png"
/>
<img
class="img-hover3"
src="flags/united-kingdom-flag-png-large.jpg"
/>
</div>
</div>
<div class="checkbox-container">
<input
type="checkbox"
name="docFormat"
id="docFormatSummary3"
value="sprint-planning"
/>
<label id="label_summary" for="docFormatSummary"
>Sprint Planning Note</label
>
<div class="figure4">
<img
class="img-icon"
src="icons/question-mark-button-icon--free-clip-art-30.png"
/>
<img
class="img-hover4"
src="flags/germany-flag-png-large.jpg"
/>
</div>
</div>
<div class="checkbox-container">
<input
type="checkbox"
name="docFormat"
id="docFormatCustom"
value="custom"
/>
<select
name="customDocumentTypes"
id="customDocumentTypes"
></select>
</div>
</div>
</div>
<!-- Here starts code from step 4-->
<div class="step" id="step4" style="display: none">
<h2 class="h2" id="step4_h2">Click to submit:</h2>
<button
class="submit-btn"
id="submitButton"
onclick="checkBoxes()"
disabled
>
Submit
</button>
<div class="testy" id="testy">
<div class="box2" id="box1"></div>
<p id="box1_p1">---Starting---</p>
<div class="box2" id="box2"></div>
<p id="box2_p2">---Transkribing---</p>
<div class="box2" id="box3"></div>
<p id="box3_p3">---Document creation---</p>
<div class="box2" id="box4"></div>
</div>
</div>
<!-- Here starts code from step 5-->
<div class="step" id="step5" style="display: none">
<h2 class="h2" id="step5_h2">Change names of the speakers:</h2>
<div class="speaker-container">
<table class="speaker-table">
<tbody>
<tr>
<td class="label-cell">
<label id="labelSpeaker" for="cur_speaker"
>Select Speaker:</label
>
</td>
<td class="input-cell">
<select name="cur_speaker" id="cur_speaker"></select>
</td>
</tr>
<tr>
<td class="label-cell">
<label id="labelSpeakerAudio">Speaker Audio:</label>
</td>
<td class="input-cell">
<audio controls id="speakerAudioViewer">
Currently there is no audio file here.
</audio>
</td>
</tr>
<tr>
<td class="label-cell">
<label id="labelSpeakerWriter" for="newSpeaker"
>New Name:</label
>
</td>
<td class="input-cell">
<input
type="text"
id="newSpeaker"
placeholder="Enter new speaker name"
/>
</td>
</tr>
</tbody>
</table>
<div class="speaker-button-group">
<button id="speakerLocker" onclick="rewriteSpeakerName()">
Rename Speaker
</button>
<button id="speakerResender" onclick="sendSpeakerPackages()">
Rewrite Document
</button>
</div>
</div>
</div>
<!-- Here starts code from step 6-->
<div class="step" id="step6" style="display: none">
<h2 class="h2" id="step6_h2">Click to download your document:</h2>
<button
class="download-btn"
id="downloadButton"
onclick="fileDownload()"
>
Download
</button>
</div>
</div>
<button id="nextBtn" class="navBtn">&rarr;</button>
</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="script.js"></script>
<script src="./renderer.js"></script>
</body>
</html> </html>
+24 -3
View File
@@ -41,7 +41,14 @@ var languageOptions = {
"goBackBtn": "Return", "goBackBtn": "Return",
"deleteBtn": "Delete document", "deleteBtn": "Delete document",
"generateBtn": "Save document", "generateBtn": "Save document",
"newDoc": "-- Create new document --" "newDoc": "-- Create new document --",
"step1_h2" : "Upload your video here:",
"step2_h2" : "Choose your preferences:",
"step3_h2" : "Choose prefered document style:",
"step4_h2" : "Click to submit:",
"step5_h2" : "Change names of the speakers:",
"step6_h2" : "Click to download your document:"
}, },
"de":{ "de":{
@@ -86,7 +93,14 @@ var languageOptions = {
"goBackBtn": "Zurück", "goBackBtn": "Zurück",
"deleteBtn": "Lösche Dokument", "deleteBtn": "Lösche Dokument",
"generateBtn": "Speichere Dokument", "generateBtn": "Speichere Dokument",
"newDoc": "-- Neues Dokument erstellen --" "newDoc": "-- Neues Dokument erstellen --",
"step1_h2" : "Laden Sie Ihr Video hier hoch:",
"step2_h2" : "Wählen Sie Ihre Präferenzen:",
"step3_h2" : "Wählen Sie den gewünschten Dokumentstil:",
"step4_h2" : "Zum Absenden klicken:",
"step5_h2" : "Ändern Sie die Namen der Sprecher:",
"step6_h2" : "Klicken Sie hier, um Ihr Dokument herunterzuladen:"
}, },
"in":{ "in":{
"flagPath": "flags/india-flag-png-large.png", "flagPath": "flags/india-flag-png-large.png",
@@ -130,7 +144,14 @@ var languageOptions = {
"goBackBtn": "वापस जाएं", "goBackBtn": "वापस जाएं",
"deleteBtn": "दस्तावेज़ हटाएं", "deleteBtn": "दस्तावेज़ हटाएं",
"generateBtn": "दस्तावेज़ सहेजें", "generateBtn": "दस्तावेज़ सहेजें",
"newDoc": "-- नया दस्तावेज़ बनाएं --" "newDoc": "-- नया दस्तावेज़ बनाएं --",
"step1_h2" : "अपना वीडियो यहां अपलोड करें:",
"step2_h2" : "अपनी प्राथमिकताएँ चुनें:",
"step3_h2" : "पसंदीदा दस्तावेज़ शैली चुनें:",
"step4_h2" : "सबमिट करने के लिए क्लिक करें:",
"step5_h2" : "वक्ताओं के नाम बदलें:",
"step6_h2" : "अपना दस्तावेज़ डाउनलोड करने के लिए यहां क्लिक करें:"
} }
-44
View File
@@ -1,44 +0,0 @@
import { app, BrowserWindow, ipcMain, dialog } from 'electron';
import { exec } from 'child_process';
import path from 'path';
import { fileURLToPath } from 'url';
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
let mainWindow;
function createWindow() {
mainWindow = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
nodeIntegration: false,
contextIsolation: true,
preload: path.join(__dirname, 'preload.js')
}
});
mainWindow.loadFile('main/index.html');
}
app.whenReady().then(createWindow);
// Kommunikation vom Renderer (Frontend)
ipcMain.handle('convert-video', async (event, filePath) => {
const output = path.join(path.dirname(filePath), 'converted.mp4');
return new Promise((resolve, reject) => {
exec(`ffmpeg -i "${filePath}" -vcodec libx264 "${output}"`, (error, stdout, stderr) => {
if (error) {
console.error('Fehler beim Konvertieren:', error);
reject(error);
} else {
console.log('Konvertierung abgeschlossen:', output);
resolve(output);
}
});
});
});
+46 -58
View File
@@ -133,22 +133,7 @@ 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);
});
});
//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) => {
@@ -315,7 +300,6 @@ generateBtn.addEventListener("click", () => {
const content = document.getElementById("prompt").value.trim(); const content = document.getElementById("prompt").value.trim();
if (!name || !content) { if (!name || !content) {
result.textContent = "Bitte Dokumentname und Prompt ausfüllen."; result.textContent = "Bitte Dokumentname und Prompt ausfüllen.";
console.log(name + " " + content);
setTimeout(() => { setTimeout(() => {
result.textContent = ""; result.textContent = "";
}, 3000); }, 3000);
@@ -332,60 +316,64 @@ generateBtn.addEventListener("click", () => {
// dokumente löschen // dokumente löschen
deleteBtn.addEventListener("click", () => { deleteBtn.addEventListener("click", () => {
const name = docName.value.trim(); try {
const name = docName.value.trim();
if (!name) { if (!name) {
result.textContent = "Bitte Dokumentname angeben."; result.textContent = "Bitte Dokumentname angeben.";
setTimeout(() => { setTimeout(() => {
result.textContent = ""; result.textContent = "";
}, 3000); }, 3000);
return; 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.";
} }
var success = true;
setTimeout(() => { window.api.deleteTxtFile(name).then((success) => {
result.textContent = ""; if (success) {
}, 3000); result.textContent = "Dokument erfolgreich gelöscht!";
}); reloadDocuments();
existingDocs.dispatchEvent(new Event("change"));
} else {
result.textContent = "Dokument konnte nicht gelöscht werden.";
}
});
} catch (error) {
console.log(error)
}
}); });
//function to load existingDoc options to the drop down list //function to load existingDoc options to the drop down list
window.api.getTxtFiles().then(files => { window.api.getTxtFiles().then(files => {
reloadDocuments(); try {
reloadDocuments();
} catch (error) {
console.log(error)
}
}); });
//content anzeigen //content anzeigen
existingDocs.addEventListener("change", async () => { existingDocs.addEventListener("change", async () => {
const existingDocsed = existingDocs.value; try {
const exampleText = ""; const existingDocsed = existingDocs.value;
const exampleText = "";
if (existingDocsed === "newDoc") {
docNameWrapper.classList.remove("hidden");
docName.value = "";
document.getElementById("prompt").value = exampleText;
document.getElementById("prompt").textContent = exampleText;
return;
}
docNameWrapper.classList.add("hidden");
document.getElementById("prompt").textContent = "";
document.getElementById("prompt").value = "";
if (existingDocsed === "newDoc") { const content = await window.api.readTxtFile(existingDocsed);
docNameWrapper.classList.remove("hidden"); document.getElementById("prompt").value = content;
docName.value = ""; document.getElementById("prompt").textContent = content;
prompt.value = exampleText; docName.value = existingDocsed.replace(".txt", "");
return; } catch (error) {
console.log(error)
} }
docNameWrapper.classList.add("hidden");
const content = await window.api.readTxtFile(existingDocsed);
prompt.value = content;
docName.value = existingDocsed.replace(".txt", "");
}); });
+50 -19
View File
@@ -27,12 +27,11 @@ function showCD() {
//language changing feature => changes the language of every displayed text //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('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;
@@ -45,7 +44,6 @@ function changeLanguage(language) {
document.getElementById('step_nav4').textContent = languageOptions[language].step_nav4; document.getElementById('step_nav4').textContent = languageOptions[language].step_nav4;
document.getElementById('step_nav5').textContent = languageOptions[language].step_nav5; document.getElementById('step_nav5').textContent = languageOptions[language].step_nav5;
document.getElementById('step_nav6').textContent = languageOptions[language].step_nav6; 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('labelSpeaker').textContent = languageOptions[language].labelSpeaker;
document.getElementById('labelSpeakerAudio').textContent = languageOptions[language].labelSpeakerAudio; document.getElementById('labelSpeakerAudio').textContent = languageOptions[language].labelSpeakerAudio;
document.getElementById('labelSpeakerWriter').textContent = languageOptions[language].labelSpeakerWriter; document.getElementById('labelSpeakerWriter').textContent = languageOptions[language].labelSpeakerWriter;
@@ -69,6 +67,13 @@ function changeLanguage(language) {
document.getElementById('generateBtn').textContent = languageOptions[language].generateBtn; document.getElementById('generateBtn').textContent = languageOptions[language].generateBtn;
document.getElementById('newDoc').textContent = languageOptions[language].newDoc; document.getElementById('newDoc').textContent = languageOptions[language].newDoc;
document.getElementById("step1_h2").textContent = languageOptions[language].step1_h2;
document.getElementById("step2_h2").textContent = languageOptions[language].step2_h2;
document.getElementById("step3_h2").textContent = languageOptions[language].step3_h2;
document.getElementById("step4_h2").textContent = languageOptions[language].step4_h2;
document.getElementById("step5_h2").textContent = languageOptions[language].step5_h2;
document.getElementById("step6_h2").textContent = languageOptions[language].step6_h2;
} 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);
@@ -89,6 +94,10 @@ let currentStep = 1;
const totalSteps = steps.length; const totalSteps = steps.length;
function showStep(stepNumber) { function showStep(stepNumber) {
if(showCDValue == 1){
showCDValue = 0;
document.getElementById('cdContainer').style.display = "none";
}
if (stepNumber < 1 || stepNumber > totalSteps) { if (stepNumber < 1 || stepNumber > totalSteps) {
console.error("StepNumber out of Bounds", stepNumber); console.error("StepNumber out of Bounds", stepNumber);
return; return;
@@ -473,10 +482,6 @@ function fileDownload() {
} }
} }
/* /*
Functions for the custom document section Functions for the custom document section
@@ -485,18 +490,44 @@ Functions for the custom document section
//reload drop down //reload drop down
function reloadDocuments() { function reloadDocuments() {
[...existingDocs.querySelectorAll('option:not([value="newDoc"])')] try{
.forEach(o => o.remove()); [...existingDocs.querySelectorAll('option:not([value="newDoc"])')]
.forEach(o => o.remove());
[...customDocumentTypes.querySelectorAll('option:not([value="newDoc"])')]
.forEach(o => o.remove());
window.api.getTxtFiles().then(files => { window.api.getTxtFiles().then(files => {
files.forEach(file => { files.forEach(file => {
const option = document.createElement('option'); var option = document.createElement('option');
option.value = file; option.value = file;
option.textContent = file option.textContent = file
.replace('.txt', '') // Endung entfernen .replace('.txt', '') // Endung entfernen
.replace(/_/g, ' ') // Leerzeichen ersetzen .replace(/_/g, ' ') // Leerzeichen ersetzen
.replace(/\b\w/g, c => c.toUpperCase()) // ersten Buchstaben groß .replace(/\b\w/g, c => c.toUpperCase()); // ersten Buchstaben groß
existingDocs.appendChild(option); existingDocs.appendChild(option);
var option2 = document.createElement('option');
option2.value = file;
option2.name = file;
option2.textContent = file
.replace('.txt', '') // Endung entfernen
.replace(/_/g, ' ') // Leerzeichen ersetzen
.replace(/\b\w/g, c => c.toUpperCase()); // ersten Buchstaben groß
customDocumentTypes.appendChild(option2);
});
}); });
}); }
catch(error){
console.log(error)
}
} }
function sendSpeakerPackages() {
try {
window.submitSpeaker.speaker_submit(speakerAudios);
} catch (error) {
console.log(error);
}
}
window.sendSpeakerPackages = sendSpeakerPackages;
-1
View File
@@ -495,7 +495,6 @@ li {
} }
.p-menu1 { .p-menu1 {
margin-left: 20px;
z-index: 10; z-index: 10;
} }
View File
@@ -1,54 +0,0 @@
// -----------------------------------------------------------
// 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));
}
});
});
}
};
@@ -1,71 +0,0 @@
# -----------------------------------------------------------
# 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)