Compare commits

..

2 Commits

16 changed files with 865 additions and 1032 deletions
+8 -117
View File
@@ -4,9 +4,7 @@
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)
4. [IPC](#ipc) 3. [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.
@@ -19,9 +17,13 @@ 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:
``` ```
auth_username=wefhjhjakeghjkahejkghjkaegh ASSEMBLYAI_API_KEY=wefhjhjakeghjkahejkghjkaegh
auth_password=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. 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.
@@ -111,115 +113,4 @@ As you can see in this JSON object, each part specifies which module is being us
The module names are each the name field specified in the module itself. 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
@@ -0,0 +1,154 @@
<!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>
+200 -317
View File
@@ -1,340 +1,223 @@
<!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 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 <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/lc-select@1.3.0/themes/light.css">
rel="stylesheet" </head>
href="https://cdn.jsdelivr.net/npm/lc-select@1.3.0/themes/light.css" <body>
/>
</head>
<body> <div id="h1-wrapper">
<div id="h1-wrapper"> <section class="p-menu1">
<section class="p-menu1"> <nav id="navbar" class="navigation" role="navigation">
<nav id="navbar" class="navigation" role="navigation"> <input id="toggle1" type="checkbox" />
<input id="toggle1" type="checkbox" /> <label class="hamburger1" for="toggle1">
<label class="hamburger1" for="toggle1"> <div class="top"></div>
<div class="top"></div> <div class="meat"></div>
<div class="meat"></div> <div class="bottom"></div>
<div class="bottom"></div> </label>
</label>
<nav class="menu1"> <nav class="menu1">
<button id="customDocBtn" onclick="showCD()"> <button id="customDocBtn" onclick="showCD()">Manage document types</button>
Manage document types <a href="help_page.html" class="li1">Help</a>
</button> </nav>
<a href="help_page.html" class="li1">Help</a> </nav>
</nav> </section>
</nav>
</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> <!-- to do: Ausprobieren mit li, a oder button, im Notfall ohne Flaggen Icons, kein hover-->
</div> <select name="language_option" id="language_option"></select>
</div> </div>
<div class="step-nav"> </div>
<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"> <div class="step-nav">
<button id="prevBtn" class="navBtn" disabled>&larr;</button> <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--> <!-- 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" <!--Costum document section-->
>Select existing documents (optional):</label <div class="container" id="cdContainer" style="display:none;">
> <h1 id="cd_h1">Manage document types</h1>
<!--Drop Down-->
<select name="existingDocs" id="existingDocs">
<option value="newDoc" id="newDoc">
-- Create new document --
</option>
</select>
<div id="docNameWrapper"> <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> <label for="docName" id="cd_docName">Document name:</label>
<input <input type="text" id="docName" placeholder="Enter the document name here">
type="text" </div>
id="docName"
placeholder="Enter the document name here"
/>
</div>
<label for="prompt" id="cd_promt">Your prompt:</label> <label for="prompt" id="cd_promt">Your prompt:</label>
<textarea <textarea id="prompt" placeholder="Type the prompt for your document here..."></textarea>
id="prompt"
placeholder="Type the prompt for your document here..."
></textarea>
<div class="buttons"> <div class="buttons">
<button id="goBackBtn">Return</button> <button id="goBackBtn">Return</button>
<button id="deleteBtn">Delete document</button> <button id="deleteBtn">Delete document</button>
<button id="generateBtn">Save document</button> <button id="generateBtn">Save document</button>
</div>
<div id="result"></div>
</div> </div>
<!-- Here starts code from step 1--> <div id="result"></div>
<div class="step" id="step1"> </div>
<h2 class="h2" id="step1_h2">Upload your video here:</h2>
<div class="upload-container" id="uploadContainer"> <!-- Here starts code from step 1-->
<p id="p1">Drag and drop video file</p> <div class="step" id="step1">
<video id="previewThumbnail" autoplay="false"></video> <h2 class="h2">Upload your video here:</h2>
<div class="file-name" id="fileName">No video chosen</div> <div class="upload-container" id="uploadContainer">
<div id="thumbnailContainer"> <p id="p1">Drag and drop video file</p>
<img id="thumbnailImage" style="display: none" /> <video id="previewThumbnail" autoplay="false">
</div> </video>
<button class="custom-btn" id="manualUploadBtn"> <div class="file-name" id="fileName">No video chosen</div>
Search video <div id="thumbnailContainer">
</button> <img id="thumbnailImage" style="display:none;">
<input type="file" id="videoUpload" accept="video/*" />
</div>
</div> </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--> <!-- 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" id="step2_h2">Choose your preferences:</h2> <h2 class="h2">Choose your preferences:</h2>
<div class="step2-form"> <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>
</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> </div>
<button id="nextBtn" class="navBtn">&rarr;</button> <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="word">.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>
<script src="https://cdn.jsdelivr.net/npm/lc-select@1.3.0/lc_select.min.js"></script>
<script src="languages.js"></script> <!-- Here starts code from step 3-->
<script src="script.js"></script>
<script src="./renderer.js"></script> <!-- Hover Effekt für Dokumentenvorschau, Fragezeichen hinter Text, drüber hoven zeigt Beispieldokument -->
</body> <div class="step" id="step3" style="display:none;">
</html> <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>
<img class="img-icon" src="icons/question-mark-button-icon--free-clip-art-30.png">
</div>
<div class="checkbox-container">
<input type="checkbox" name="docFormat" id="docFormatSummary2" value="result-protocol">
<label id="label_summary" for="docFormatSummary">Resultprotocol</label>
<img class="img-icon" src="icons/question-mark-button-icon--free-clip-art-30.png">
</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>
<img class="img-icon" src="icons/question-mark-button-icon--free-clip-art-30.png">
</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="speakerView" id="speakerView">
<label id="labelSpeaker">Select Speaker:</label>
<select name="cur_speaker" id="cur_speaker">
</select>
</div>
<div class="speakerAudio" id="speakerAutio">
<label id="labelSpeakerAudio">Selected Speaker:</label>
<audio controls id="speakerAudioViewer">
Currently there is no audio file here.
</audio>
</div>
<div class="speakerWrite" id="speakerWrite">
<label id="labelSpeakerWriter">Write name:</label>
<input type="text" id="newSpeaker">
</div>
<div class="speakerButton-group">
<button id="speakerLocker" onclick="rewriteSpeakerName()">Rename Speaker</button>
<button id="speakerResender" onclick="sendSpeakerPackages()">Rewrite document</button>
</div>
</div>
<!-- Here starts code from step 6-->
<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>
</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>
+24 -45
View File
@@ -1,7 +1,7 @@
var languageOptions = { var languageOptions = {
"eng":{ "eng":{
"flagPath": "flags/united-kingdom-flag-png-large.jpg", "flagPath": "flags/united-kingdom-flag-png-large.jpg",
"labelKI": "Select AI:", "labelKI": "Select ki:",
"labelTranscription": "Select transcription:", "labelTranscription": "Select transcription:",
"labelLanguage": "Select language:", "labelLanguage": "Select language:",
"title": "Video to document", "title": "Video to document",
@@ -9,7 +9,7 @@ var languageOptions = {
"p1": "Drag and drop video file", "p1": "Drag and drop video file",
"fileName": "No video chosen", "fileName": "No video chosen",
"manualUploadBtn": "Search video", "manualUploadBtn": "Search video",
"checkbox_group": "Choose preferred 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",
@@ -27,7 +27,7 @@ var languageOptions = {
"speakerResender": "Rewrite document", "speakerResender": "Rewrite document",
"downloadButton": "Download", "downloadButton": "Download",
"box1_p1": "---Starting---", "box1_p1": "---Starting---",
"box2_p2": "---Transcribing---", "box2_p2": "---Transkribing---",
"box3_p3": "---Document creation---", "box3_p3": "---Document creation---",
"labelType": "Select document type:", "labelType": "Select document type:",
@@ -41,25 +41,18 @@ 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":{
"flagPath": "flags/germany-flag-png-large.jpg", "flagPath": "flags/germany-flag-png-large.jpg",
"labelKI": "Wähle KI:", "labelKI": "Waehle KI:",
"labelTranscription": "Wähle Transkription:", "labelTranscription": "Waehle Transkription:",
"labelLanguage": "Wähle Sprache:", "labelLanguage": "Waehle Sprache:",
"title": "Video zu Dokument", "title": "Video zu Dokument",
"h1": "Video zu Dokument", "h1": "Video zu Dokument",
"p1": "Video per Drag & Drop ablegen", "p1": "Video per Drag & Drop ablegen",
"fileName": "Kein Video ausgewählt", "fileName": "Kein Video ausgewaehlt",
"manualUploadBtn": "Video suchen", "manualUploadBtn": "Video suchen",
"checkbox_group": "Bevorzugte Dokumentvarianten:", "checkbox_group": "Bevorzugte Dokumentvarianten:",
"label_format": "Meeting Bericht", "label_format": "Meeting Bericht",
@@ -71,7 +64,7 @@ var languageOptions = {
"step_nav4": "Schritt 4", "step_nav4": "Schritt 4",
"step_nav5": "Schritt 5", "step_nav5": "Schritt 5",
"step_nav6": "Schritt 6", "step_nav6": "Schritt 6",
"h2": "Lade dein Video hier hoch:", "h2": "Uploade dein Video hier:",
"labelSpeaker": "Wähle Sprecher:", "labelSpeaker": "Wähle Sprecher:",
"labelSpeakerAudio": "Ausgewählter Sprecher:", "labelSpeakerAudio": "Ausgewählter Sprecher:",
"labelSpeakerWriter": "Schreib Namen:", "labelSpeakerWriter": "Schreib Namen:",
@@ -79,34 +72,27 @@ var languageOptions = {
"speakerResender": "Überschreibe Dokument", "speakerResender": "Überschreibe Dokument",
"downloadButton": "Download", "downloadButton": "Download",
"box1_p1": "---Startet---", "box1_p1": "---Startet---",
"box2_p2": "---Transkribierung---", "box2_p2": "---Transkribing---",
"box3_p3": "---Dokument erstellen---", "box3_p3": "---Dokument kreieren---",
"labelType": "Wähle Dokumenttyp:", "labelType": "Wähle Dokumenttype:",
"customDocBtn": "Dokumenttypen verwalten", "customDocBtn": "Dokumenttypen verwalten",
"cd_h1": "Dokumenttypen verwalten", "cd_h1": "Dokumenttypen verwalten",
"cd_existingDocs": "Vorhandene Dokumente auswählen (optional):", "cd_existingDocs": "Vorhandene Dokumente auswählen (optional):",
"cd_docName": "Dokumentname", "cd_docName": "Dokument Name",
"docName": "Geben Sie hier den Dokumentnamen ein", "docName": "Geben Sie hier den Dokumentnamen ein",
"cd_promt": "Ihr Prompt:", "cd_promt": "Ihr Prompt:",
"prompt": "Geben Sie hier die Eingabeaufforderung für Ihr Dokument ein...", "prompt": "Geben Sie hier die Eingabeaufforderung für Ihr Dokument ein...",
"goBackBtn": "Zurück", "goBackBtn": "Zurück",
"deleteBtn": "Lösche Dokument", "deleteBtn": "Lösche Dokument",
"generateBtn": "Speichere Dokument", "generateBtn": "Speicher 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",
"labelKI": "KI का चयन करें:", "labelKI": "की का चयन करें:",
"labelTranscription": "प्रतिलेखन चुनें:", "labelTranscription": "प्रतिलेखन चुनें:",
"labelLanguage": "भाषा चुने:", "labelLanguage": "भाषा चुने:",
"title": "दस्तावेज़ के लिए वीडियो", "title": "दस्तावेज़ के लिए वीडियो",
"h1": "दस्तावेज़ के लिए वीडियो", "h1": "दस्तावेज़ के लिए वीडियो",
"p1": "वीडियो फ़ाइल खींचें और छोड़ें", "p1": "वीडियो फ़ाइल खींचें और छोड़ें",
@@ -115,7 +101,7 @@ var languageOptions = {
"checkbox_group": "पसंदीदा दस्तावेज़ शैली चुनें:", "checkbox_group": "पसंदीदा दस्तावेज़ शैली चुनें:",
"label_format": "बैठक रिपोर्ट", "label_format": "बैठक रिपोर्ट",
"label_summary": "टाइमस्टैम्प के साथ सारांश", "label_summary": "टाइमस्टैम्प के साथ सारांश",
"submitButton": "जमा करें", "submitButton": "जमा करना",
"step_nav1": "स्टेप 1", "step_nav1": "स्टेप 1",
"step_nav2": "स्टेप 2", "step_nav2": "स्टेप 2",
"step_nav3": "स्टेप 3", "step_nav3": "स्टेप 3",
@@ -124,11 +110,11 @@ var languageOptions = {
"step_nav6": "स्टेप 6", "step_nav6": "स्टेप 6",
"h2": "अपना वीडियो यहां अपलोड करें:", "h2": "अपना वीडियो यहां अपलोड करें:",
"labelSpeaker": "स्पीकर चुनें:", "labelSpeaker": "स्पीकर चुनें:",
"labelSpeakerAudio": "चयनित स्पीकर:", "labelSpeakerAudio": "चयनित वक्ता:",
"labelSpeakerWriter": "नाम लिखें:", "labelSpeakerWriter": "नाम लिखें:",
"speakerLocker": "स्पीकर का नाम बदलें", "speakerLocker": "स्पीकर का नाम बदलें",
"speakerResender": "दस्तावेज़ फिर से लिखें", "speakerResender": "दस्तावेज़ पुनः लिखें",
"downloadButton": "डाउनलोड करें", "downloadButton": "डाउनलोड करना",
"box1_p1": "---प्रारंभ---", "box1_p1": "---प्रारंभ---",
"box2_p2": "---प्रतिलेखन---", "box2_p2": "---प्रतिलेखन---",
"box3_p3": "---दस्तावेज़ निर्माण---", "box3_p3": "---दस्तावेज़ निर्माण---",
@@ -141,17 +127,10 @@ var languageOptions = {
"docName": "यहां दस्तावेज़ का नाम दर्ज करें", "docName": "यहां दस्तावेज़ का नाम दर्ज करें",
"cd_promt": "आपका संकेत:", "cd_promt": "आपका संकेत:",
"prompt": "अपने दस्तावेज़ के लिए प्रॉम्प्ट यहां टाइप करें...", "prompt": "अपने दस्तावेज़ के लिए प्रॉम्प्ट यहां टाइप करें...",
"goBackBtn": "वापस जाएं", "goBackBtn": "वापस करना",
"deleteBtn": "दस्तावेज़ हटाए", "deleteBtn": "दस्तावेज़ हटाए",
"generateBtn": "दस्तावेज़ सहेजें", "generateBtn": "दस्तावेज़ सहेजें",
"newDoc": "-- नया दस्तावेज़ बनाए --", "newDoc": "-- नया दस्तावेज़ बनाए --"
"step1_h2" : "अपना वीडियो यहां अपलोड करें:",
"step2_h2" : "अपनी प्राथमिकताएँ चुनें:",
"step3_h2" : "पसंदीदा दस्तावेज़ शैली चुनें:",
"step4_h2" : "सबमिट करने के लिए क्लिक करें:",
"step5_h2" : "वक्ताओं के नाम बदलें:",
"step6_h2" : "अपना दस्तावेज़ डाउनलोड करने के लिए यहां क्लिक करें:"
} }
+44
View File
@@ -0,0 +1,44 @@
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);
}
});
});
});
+59 -47
View File
@@ -133,7 +133,22 @@ 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) => {
@@ -300,6 +315,7 @@ 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);
@@ -316,64 +332,60 @@ generateBtn.addEventListener("click", () => {
// dokumente löschen // dokumente löschen
deleteBtn.addEventListener("click", () => { deleteBtn.addEventListener("click", () => {
try { const name = docName.value.trim();
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;
}
var success = true;
window.api.deleteTxtFile(name).then((success) => {
if (success) {
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)
} }
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 //function to load existingDoc options to the drop down list
window.api.getTxtFiles().then(files => { window.api.getTxtFiles().then(files => {
try { reloadDocuments();
reloadDocuments();
} catch (error) {
console.log(error)
}
}); });
//content anzeigen //content anzeigen
existingDocs.addEventListener("change", async () => { existingDocs.addEventListener("change", async () => {
try { const existingDocsed = existingDocs.value;
const existingDocsed = existingDocs.value; const exampleText = "";
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 = "";
const content = await window.api.readTxtFile(existingDocsed); if (existingDocsed === "newDoc") {
document.getElementById("prompt").value = content; docNameWrapper.classList.remove("hidden");
document.getElementById("prompt").textContent = content; docName.value = "";
docName.value = existingDocsed.replace(".txt", ""); prompt.value = exampleText;
} catch (error) { return;
console.log(error)
} }
docNameWrapper.classList.add("hidden");
const content = await window.api.readTxtFile(existingDocsed);
prompt.value = content;
docName.value = existingDocsed.replace(".txt", "");
}); });
+32 -74
View File
@@ -27,11 +27,12 @@ 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;
@@ -44,6 +45,7 @@ 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;
@@ -67,13 +69,6 @@ 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);
@@ -94,10 +89,6 @@ 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;
@@ -441,30 +432,19 @@ function setSpeakerAudiosValue(valy) {
//Function to rewrite the speaker name in the json //Function to rewrite the speaker name in the json
function rewriteSpeakerName() { function rewriteSpeakerName() {
try { try {
const select = document.getElementById("cur_speaker"); var tempy = document.getElementById("cur_speaker").value;
const newName = document.getElementById("newSpeaker").value.trim(); speakerAudios[tempy].name = document.getElementById("newSpeaker").value;
loadSpeakerOptions(speakerAudios);
if (!newName) {
alert("Please enter a new speaker name");
return;
}
const selectedIndex = select.selectedIndex;
const selectedValue = select.value;
// Update speakerAudios data
speakerAudios[selectedValue].name = newName;
// Update the specific option text and keep value
select.options[selectedIndex].text = newName;
select.options[selectedIndex].value = selectedValue;
// Keep it selected
select.selectedIndex = selectedIndex;
console.log("Speaker renamed:", newName);
} catch (error) { } catch (error) {
console.log("Error renaming speaker:", error); 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() {
try {
window.submitSpeaker.speaker_submit(speakerAudios);
} catch (error) {
console.log(error);
} }
} }
@@ -482,6 +462,10 @@ function fileDownload() {
} }
} }
/* /*
Functions for the custom document section Functions for the custom document section
@@ -490,44 +474,18 @@ Functions for the custom document section
//reload drop down //reload drop down
function reloadDocuments() { function reloadDocuments() {
try{ [...existingDocs.querySelectorAll('option:not([value="newDoc"])')]
[...existingDocs.querySelectorAll('option:not([value="newDoc"])')] .forEach(o => o.remove());
.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 => {
var option = document.createElement('option'); const 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;
+29 -209
View File
@@ -11,12 +11,12 @@ body {
} }
#h1 { #h1 {
position: static; position: absolute;
transform: none; left: 50%;
top: 50%;
transform: translate(-50%, -50%);
margin: 0; margin: 0;
z-index: 20; z-index: 20;
flex: 1;
text-align: center;
} }
#h1-wrapper { #h1-wrapper {
@@ -30,26 +30,6 @@ body {
margin-bottom: 10px; margin-bottom: 10px;
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: space-between;
padding: 0 20px;
box-sizing: border-box;
}
.gui-language {
position: absolute;
right: 20px;
top: 50%;
transform: translateY(-50%);
z-index: 100;
pointer-events: auto;
}
#language_option {
padding: 8px 12px;
border-radius: 4px;
border: 1px solid #ccc;
font-size: 14px;
cursor: pointer;
} }
.upload-container { .upload-container {
@@ -105,6 +85,7 @@ body {
#previewThumbnail { #previewThumbnail {
width: 150px; width: 150px;
height: 100px; height: 100px;
/*border: 1px dashed black;*/
} }
.custom-btn { .custom-btn {
@@ -127,9 +108,8 @@ body {
background-color: #0056b3; background-color: #0056b3;
} }
.step h2 { #step2 {
width: 100%; gap: 25px;
text-align: center;
} }
.KI-wrapper { .KI-wrapper {
@@ -206,8 +186,6 @@ input[type="file"] {
gap: 5px; gap: 5px;
} }
/* Hover effects for all different document options (with placeholders)*/
.figure1 { .figure1 {
position: relative; position: relative;
} }
@@ -223,79 +201,12 @@ input[type="file"] {
object-fit: contain; object-fit: contain;
display: none; display: none;
transition: opacity .2s; transition: opacity .2s;
z-index: 999;
} }
.figure1:hover .img-hover1 { .figure1:hover .img-hover1 {
display: flex; display: flex;
} }
.figure2 {
position: relative;
}
.img-hover2 {
position: absolute;
width: 200px;
height: 200px;
top: 0;
right: 40%;
left: 0;
bottom: 0;
object-fit: contain;
display: none;
transition: opacity .2s;
z-index: 999;
}
.figure2:hover .img-hover2 {
display: flex;
}
.figure3 {
position: relative;
}
.img-hover3 {
position: absolute;
width: 200px;
height: 200px;
top: 0;
right: 40%;
left: 0;
bottom: 0;
object-fit: contain;
display: none;
transition: opacity .2s;
z-index: 999;
}
.figure3:hover .img-hover3 {
display: flex;
}
.figure4 {
position: relative;
}
.img-hover4 {
position: absolute;
width: 200px;
height: 200px;
top: 0;
right: 40%;
left: 0;
bottom: 0;
object-fit: contain;
display: none;
transition: opacity .2s;
z-index: 999;
}
.figure4:hover .img-hover4 {
display: flex;
}
.img-icon { .img-icon {
width: 15px; width: 15px;
height: 15px; height: 15px;
@@ -325,7 +236,7 @@ input[type="file"] {
background-color: #FFF; background-color: #FFF;
display: flex; display: flex;
width: 780px; width: 780px;
height: 550px; height: 500px;
flex-direction: column; flex-direction: column;
align-items: center; align-items: center;
gap: 10px; gap: 10px;
@@ -334,7 +245,6 @@ input[type="file"] {
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);
padding-top: 50px;
} }
.progressbar { .progressbar {
@@ -376,10 +286,7 @@ input[type="file"] {
#ai_type, #ai_type,
#transkript_type, #transkript_type,
#language_option { #language_option {
padding: 8px 12px; padding: 3px;
border-radius: 4px;
border: 1px solid #ccc;
font-size: 14px;
} }
.labelDiv { .labelDiv {
@@ -427,6 +334,7 @@ input[type="file"] {
.step { .step {
margin-top: 40px; margin-top: 40px;
margin-bottom: 40px; margin-bottom: 40px;
;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
min-height: 425px; min-height: 425px;
@@ -495,6 +403,7 @@ li {
} }
.p-menu1 { .p-menu1 {
margin-left: 20px;
z-index: 10; z-index: 10;
} }
@@ -568,14 +477,7 @@ li {
-webkit-transition: all 0.3s ease; -webkit-transition: all 0.3s ease;
} }
#customDocBtn { .menu1 a:first-child {
border: none;
background-color:#1C3B69;
font: 700 20px 'Oswald', sans-serif;
border-radius: 0%;
}
.menu1 button:first-child {
margin-top: 30px; margin-top: 30px;
} }
@@ -592,7 +494,7 @@ li {
text-decoration: none; text-decoration: none;
} }
.li1:hover, #customDocBtn:hover{ .li1:hover {
background-color: #FFF; background-color: #FFF;
color: rgb(61, 61, 61); color: rgb(61, 61, 61);
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
@@ -600,27 +502,7 @@ li {
transition: all 0.3s ease; transition: all 0.3s ease;
} }
#step2,
#step2 {
font-size: larger;
align-items: center;
}
.step2-form {
width: 100%;
max-width: 420px;
display: flex;
flex-direction: column;
gap: 24px; /* DAS ist dein Spacing */
}
.step2-row {
display: flex;
flex-direction: column;
gap: 6px;
}
#step3, #step3,
#step5 { #step5 {
font-size: larger; font-size: larger;
@@ -631,7 +513,7 @@ li {
} }
#step5 { #step5 {
align-items: center; align-items: flex-start;
} }
.button-group { .button-group {
@@ -655,91 +537,35 @@ li {
font-size: 14px; font-size: 14px;
} }
.h2 {
font-size: 25px;
}
.speaker-container {
width: 100%;
max-width: 700px;
margin-top: 30px;
}
.speaker-table {
width: 100%;
border-collapse: collapse;
background: white;
}
.speaker-table tbody tr {
display: flex;
align-items: center;
gap: 20px;
margin-bottom: 25px;
padding: 10px 0;
}
.label-cell {
flex: 0 0 150px;
text-align: left;
}
.label-cell label {
font-weight: 400;
display: block;
}
.input-cell {
flex: 1;
}
#cur_speaker,
#newSpeaker {
width: 100%;
padding: 10px;
border-radius: 6px;
border: 1px solid #ccc;
font-size: 14px;
box-sizing: border-box;
}
#speakerAudioViewer {
width: 100%;
height: 35px;
border-radius: 6px;
}
.speaker-button-group {
display: flex;
gap: 15px;
justify-content: center;
margin-top: 30px;
}
#speakerLocker, #speakerLocker,
#speakerResender { #speakerResender {
padding: 12px 25px; padding: 10px 20px;
margin: 20px auto;
background-color: #007BFF; background-color: #007BFF;
color: white; color: white;
border: none; border: none;
border-radius: 8px; border-radius: 8px;
cursor: pointer; cursor: pointer;
font-size: 14px; font-size: 14px;
font-weight: 500;
transition: background-color 0.2s;
} }
#speakerLocker:hover, .h2 {
#speakerResender:hover { font-size: 25px;
background-color: #0056b3; }
.speakerView,
.speakerAudio,
.speakerWrite {
margin-top: auto;
margin-bottom: auto;
} }
.container { .container {
background: white; background: white;
padding: 10px; padding: 30px;
margin-top: 30px; margin-top: 50px;
border-radius: 12px; border-radius: 12px;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1);
width: 90%; width: 90%;
max-width: 650px; max-width: 650px;
} }
@@ -800,10 +626,4 @@ button:hover {
margin-top: 20px; margin-top: 20px;
color: #333; color: #333;
word-break: break-word; word-break: break-word;
}
.container input,
.container textarea,
.container select {
width: 100%;
} }
View File
+15 -35
View File
@@ -168,39 +168,9 @@ electron.ipcMain.on("file_submit", async (event, args) => {
throw new Error("Unknown document type: " + args.document.type); throw new Error("Unknown document type: " + args.document.type);
} }
electron.ipcMain.on("file_download", async (event) => { console.log(args);
try { let audiopath = "";
if (!globalFinalHtmlPath) { let transcriptpath = "";
throw new Error("No document generated yet");
}
const format = String(globalArgs?.document?.outputType || "")
.replace('.', '')
.toLowerCase();
if (!format) {
throw new Error("No output format selected");
}
const outputPath = await mapFunctions
.get("htmlDocumentConverter")
.convert({
inputPath: globalFinalHtmlPath,
format,
showDialog: true
});
event.sender.send("download_success", {
path: outputPath,
format
});
} catch (err) {
console.error("file_download failed:", err);
event.sender.send("error", err.message || String(err));
}
});
console.log("\n\n Running the Video to Audio Extractor"); console.log("\n\n Running the Video to Audio Extractor");
// This code handles the Video to Audio extraction module call // This code handles the Video to Audio extraction module call
@@ -283,7 +253,7 @@ electron.ipcMain.on("file_download", async (event) => {
.function(args.document.module, { .function(args.document.module, {
inputTranscriptPath: transcriptpath, inputTranscriptPath: transcriptpath,
documentTypePath: "./storage/documentType/" + templateFile, documentTypePath: "./storage/documentType/" + templateFile,
language: "en", language: args.document.outputLanguage
}) })
.then((resp) => { .then((resp) => {
console.log(resp); console.log(resp);
@@ -316,6 +286,16 @@ electron.ipcMain.on("file_download", async (event) => {
} }
}); });
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) => { electron.ipcMain.on("speaker_submit", async (event, args) => {
console.log("\n\n\nJa also hier kam was an \n\n\n"); console.log("\n\n\nJa also hier kam was an \n\n\n");
console.log(args); console.log(args);
@@ -385,4 +365,4 @@ electron.ipcMain.handle('delete-txt-file', (event, fileName) => {
} else { } else {
return false; return false;
} }
}); });
+149 -188
View File
@@ -1,37 +1,35 @@
const fs = require("fs"); const fs = require('fs');
const path = require("path"); const path = require('path');
const puppeteer = require("puppeteer"); const puppeteer = require('puppeteer');
const htmlToDocx = require("html-to-docx"); const htmlToDocx = require('html-to-docx');
const { execSync } = require("child_process"); const { execSync } = require('child_process');
const os = require("os"); const os = require('os');
const outputDir = path.join(__dirname, "../../../storage/documents"); const outputDir = path.join(__dirname, "../../../storage/documents");
if (!fs.existsSync(outputDir)) { if (!fs.existsSync(outputDir)) {
fs.mkdirSync(outputDir, { recursive: true }); fs.mkdirSync(outputDir, { recursive: true });
} }
async function showSaveDialog(defaultName, format) { async function showSaveDialog(defaultName, format) {
const platform = os.platform(); const platform = os.platform();
if (platform === "darwin") { if (platform === 'darwin') {
// macOS // macOS
const applescript = ` const applescript = `
set defaultName to "${defaultName}.${format}" set defaultName to "${defaultName}.${format}"
set theFile to choose file name with prompt "Dokument speichern als:" default name defaultName set theFile to choose file name with prompt "Dokument speichern als:" default name defaultName
POSIX path of theFile POSIX path of theFile
`; `;
try { try {
const result = execSync(`osascript -e '${applescript}'`, { const result = execSync(`osascript -e '${applescript}'`, { encoding: 'utf8' });
encoding: "utf8", return result.trim();
}); } catch (err) {
return result.trim(); if (err.status === 1) return null; // User canceled
} catch (err) { throw err;
if (err.status === 1) return null; // User canceled }
throw err; } else if (platform === 'win32') {
}
} else if (platform === "win32") {
const safeName = decodeURIComponent(defaultName); const safeName = decodeURIComponent(defaultName);
const powershell = ` const powershell = `
@@ -45,192 +43,155 @@ async function showSaveDialog(defaultName, format) {
`; `;
try { try {
const result = execSync(
`powershell -NoProfile -Command "${powershell.replace(/\r?\n/g, " ")}"`,
{ encoding: "utf8" },
);
return result.trim() || null;
} catch (err) {
if (err.status === 1) return null; // User cancelled
throw new Error("Save dialog failed: " + err.message);
}
} else {
// Linux - zenity oder kdialog
try {
const result = execSync(
`zenity --file-selection --save --confirm-overwrite --filename="${defaultName}.${format}"`,
{ encoding: "utf8" },
);
return result.trim();
} catch (err) {
try {
const result = execSync( const result = execSync(
`kdialog --getsavefilename . "${defaultName}.${format}"`, `powershell -NoProfile -Command "${powershell.replace(/\r?\n/g, ' ')}"`,
{ encoding: "utf8" }, { encoding: 'utf8' }
); );
return result.trim(); return result.trim() || null;
} catch (err2) { } catch (err) {
// Fallback if (err.status === 1) return null; // User cancelled
return path.join(os.homedir(), "Downloads", `${defaultName}.${format}`); throw new Error("Save dialog failed: " + err.message);
} }
} else {
// Linux - zenity oder kdialog
try {
const result = execSync(
`zenity --file-selection --save --confirm-overwrite --filename="${defaultName}.${format}"`,
{ encoding: 'utf8' }
);
return result.trim();
} catch (err) {
try {
const result = execSync(
`kdialog --getsavefilename . "${defaultName}.${format}"`,
{ encoding: 'utf8' }
);
return result.trim();
} catch (err2) {
// Fallback
return path.join(os.homedir(), 'Downloads', `${defaultName}.${format}`);
}
}
} }
}
} }
const module_exports = { const module_exports = {
name: "htmlDocumentConverter", name: "htmlDocumentConverter",
type: "converter", type: "converter",
displayname: "HTML Document Converter", displayname: "HTML Document Converter",
description: "Converts LLM-generated HTML to PDF, DOCX, TXT, or HTML", description: "Converts LLM-generated HTML to PDF, DOCX, TXT, or HTML",
/** /**
* Main conversion function * Main conversion function
* @param {Object} options * @param {Object} options
* @param {string} options.inputPath - Path to the HTML input * @param {string} options.inputPath - Path to the HTML input
* @param {string} options.format - 'pdf' | 'docx' | 'html' | 'txt' * @param {string} options.format - 'pdf' | 'docx' | 'html' | 'txt'
* @param {string} [options.outputName] - Optional output filename (without extension) * @param {string} [options.outputName] - Optional output filename (without extension)
* @param {boolean} [options.showDialog] - Show save dialog (default: false in module mode, true in CLI mode) * @param {boolean} [options.showDialog] - Show save dialog (default: false in module mode, true in CLI mode)
*/ */
async convert({ inputPath, format = "pdf", outputName, showDialog = false }) { async convert({ inputPath, format = 'pdf', outputName, showDialog = false }) {
format = format.toLowerCase().replace(".", ""); // <-- FIX if (!fs.existsSync(inputPath)) {
throw new Error(`Input file not found: ${inputPath}`);
}
if (!["pdf", "docx", "html", "txt"].includes(format)) { const ext = path.extname(inputPath).toLowerCase();
throw new Error(`Unsupported format: ${format}`); const baseName = outputName || path.basename(inputPath, ext);
}
if (!fs.existsSync(inputPath)) { let outputFile;
throw new Error(`Input file not found: ${inputPath}`);
} if (showDialog) {
// Zeige nativen Dialog
outputFile = await showSaveDialog(baseName, format);
if (!outputFile) {
console.log('Speichervorgang abgebrochen.');
return null;
}
} else {
// Nutze Standard-Ausgabeverzeichnis
outputFile = path.join(outputDir, `${baseName}.${format.toLowerCase()}`);
}
const ext = path.extname(inputPath).toLowerCase(); let htmlContent = fs.readFileSync(inputPath, 'utf8');
const baseName = outputName || path.basename(inputPath, ext);
let outputFile; // Remove <think> tags if present
htmlContent = htmlContent.replace(/<think>[\s\S]*?<\/think>/gi, '');
if (showDialog) { switch (format.toLowerCase()) {
// Zeige nativen Dialog case 'html':
outputFile = await showSaveDialog(baseName, format); fs.writeFileSync(outputFile, htmlContent, 'utf8');
if (!outputFile) { break;
console.log("Speichervorgang abgebrochen."); case 'pdf':
return null; await this.htmlToPDF(htmlContent, outputFile);
} break;
} else { case 'docx':
// Nutze Standard-Ausgabeverzeichnis await this.htmlToDOCX(htmlContent, outputFile);
outputFile = path.join(outputDir, `${baseName}.${format.toLowerCase()}`); break;
} case 'txt':
fs.writeFileSync(outputFile, this.htmlToTXT(htmlContent), 'utf8');
break;
default:
throw new Error(`Unsupported format: ${format}`);
}
let htmlContent = fs.readFileSync(inputPath, "utf8"); console.log(`Erfolgreich gespeichert: ${outputFile}`);
return outputFile;
},
// Remove <think> tags if present // HTML → PDF
htmlContent = htmlContent.replace(/<think>[\s\S]*?<\/think>/gi, ""); async htmlToPDF(html, outputPath) {
const browser = await puppeteer.launch({
switch (format.toLowerCase()) { headless: true,
case "html": args: ['--no-sandbox', '--disable-setuid-sandbox']
fs.writeFileSync(outputFile, htmlContent, "utf8"); });
break; const page = await browser.newPage();
case "pdf": await page.setContent(html, { waitUntil: 'networkidle0' });
await this.htmlToPDF(htmlContent, outputFile); await page.pdf({
break; path: outputPath,
case "docx": format: 'A4',
await this.htmlToDOCX(htmlContent, outputFile); printBackground: true,
break; margin: { top: '20mm', right: '20mm', bottom: '20mm', left: '20mm' }
case "txt": });
fs.writeFileSync(outputFile, this.htmlToTXT(htmlContent), "utf8");
break;
default:
throw new Error(`Unsupported format: ${format}`);
}
console.log(`Erfolgreich gespeichert: ${outputFile}`);
return outputFile;
},
// HTML → PDF
async htmlToPDF(html, outputPath) {
let browser;
try {
browser = await puppeteer.launch({
headless: true,
args: ["--no-sandbox", "--disable-setuid-sandbox"],
});
const page = await browser.newPage();
await page.setContent(html, { waitUntil: "networkidle0" });
await page.pdf({
path: outputPath,
format: "A4",
printBackground: true,
margin: {
top: "20mm",
right: "20mm",
bottom: "20mm",
left: "20mm",
},
});
} finally {
if (browser) {
await browser.close(); await browser.close();
} },
}
},
// HTML → DOCX // HTML → DOCX
async htmlToDOCX(html, outputPath) { async htmlToDOCX(html, outputPath) {
try { const buffer = await htmlToDocx(html);
// htmltodocx library converts HTML string into a Word .docx buffer fs.writeFileSync(outputPath, buffer);
// Usage from htmltodocx docs: },
// await HTMLtoDOCX(htmlString, headerHTMLString, documentOptions, footerHTMLString) [oai_citation:0‡GitHub](https://github.com/privateOmega/html-to-docx?utm_source=chatgpt.com)
const buffer = await htmlToDocx(html, null, {
table: { row: { cantSplit: true } },
});
fs.writeFileSync(outputPath, buffer);
} catch (err) {
throw new Error(`DOCX conversion failed: ${err.message}`);
}
},
// HTML → TXT // HTML → TXT (rudimentär)
htmlToTXT(html) { htmlToTXT(html) {
// A decent plain text conversion: strip tags and collapse whitespace return html.replace(/<[^>]*>/g, '').replace(/\s+\n/g, '\n').trim();
// If you want more advanced extraction consider using a library like `html-to-text` or `strip-html` [oai_citation:1‡GitHub](https://github.com/html-to-text/node-html-to-text?utm_source=chatgpt.com) }
return (
html
// Remove all tags
.replace(/<[^>]+>/g, "")
// Convert multiple whitespace into single spaces
.replace(/\s+/g, " ")
.trim()
);
},
}; };
module.exports = module_exports; module.exports = module_exports;
// CLI usage mit Dialog // CLI usage mit Dialog
if (require.main === module) { if (require.main === module) {
(async () => { (async () => {
const args = process.argv.slice(2); const args = process.argv.slice(2);
if (args.length < 1) { if (args.length < 1) {
console.log("Usage: node htmlDocumentConverter.js <input.html> [format]"); console.log('Usage: node htmlDocumentConverter.js <input.html> [format]');
console.log("Formats: pdf (default), docx, html, txt"); console.log('Formats: pdf (default), docx, html, txt');
console.log(""); console.log('');
console.log( console.log('Ein nativer "Speichern unter" Dialog wird automatisch geöffnet.');
'Ein nativer "Speichern unter" Dialog wird automatisch geöffnet.', process.exit(1);
); }
process.exit(1);
}
const inputPath = args[0]; const inputPath = args[0];
const format = args[1] || "pdf"; const format = args[1] || 'pdf';
try { try {
await module_exports.convert({ await module_exports.convert({
inputPath, inputPath,
format, format,
showDialog: true, showDialog: true
}); });
} catch (err) { } catch (err) {
console.error("Konvertierung fehlgeschlagen:", err.message); console.error('Konvertierung fehlgeschlagen:', err.message);
process.exit(1); process.exit(1);
} }
})(); })();
} }
+10
View File
@@ -1,6 +1,8 @@
// const fs = require('fs'); // const fs = require('fs');
// const path = require('path'); // const path = require('path');
const e = require("express");
const outputDir = path.join(__dirname, "../../../storage/documents"); // path for output directory const outputDir = path.join(__dirname, "../../../storage/documents"); // path for output directory
if (!fs.existsSync(outputDir)) { if (!fs.existsSync(outputDir)) {
@@ -40,6 +42,14 @@ const module_exports = {
createDocumentFromTranscript: async function(transcriptPath, documentTypePath, language = "en") { // default language is English createDocumentFromTranscript: async function(transcriptPath, documentTypePath, language = "en") { // default language is English
return new Promise(async(resolve, reject) => { return new Promise(async(resolve, reject) => {
try { try {
if (language.toLowerCase() === "de") {
language = "German"
}else if (language.toLowerCase() === "in") {
language = "Indish"
} else {
language = "English"
}
const transcript = await fs.promises.readFile(transcriptPath, "utf-8"); //read transcript file from Path const transcript = await fs.promises.readFile(transcriptPath, "utf-8"); //read transcript file from Path
const documentType = await fs.promises.readFile(documentTypePath, "utf-8"); //read document type from Path const documentType = await fs.promises.readFile(documentTypePath, "utf-8"); //read document type from Path
const promptText = `${documentType}, in language ${language}, transcript:\n\n${transcript}`; //combine doc type, language and transcript - Change prompt here if needed const promptText = `${documentType}, in language ${language}, transcript:\n\n${transcript}`; //combine doc type, language and transcript - Change prompt here if needed
+8
View File
@@ -40,6 +40,14 @@ const module_exports = {
createDocumentFromTranscript: async function(transcriptPath, documentTypePath, language = "en") { // default language is English createDocumentFromTranscript: async function(transcriptPath, documentTypePath, language = "en") { // default language is English
return new Promise(async(resolve, reject) => { return new Promise(async(resolve, reject) => {
try { try {
if (language.toLowerCase() === "de") {
language = "German"
}else if (language.toLowerCase() === "in") {
language = "Indish"
} else {
language = "English"
}
const transcript = await fs.promises.readFile(transcriptPath, "utf-8"); //read transcript file from Path const transcript = await fs.promises.readFile(transcriptPath, "utf-8"); //read transcript file from Path
const documentType = await fs.promises.readFile(documentTypePath, "utf-8"); //read document type from Path const documentType = await fs.promises.readFile(documentTypePath, "utf-8"); //read document type from Path
const promptText = `${documentType}, in language ${language}, transcript:\n\n${transcript}`; //combine doc type, language and transcript - Change prompt here if needed const promptText = `${documentType}, in language ${language}, transcript:\n\n${transcript}`; //combine doc type, language and transcript - Change prompt here if needed
+8
View File
@@ -40,6 +40,14 @@ const module_exports = {
createDocumentFromTranscript: async function(transcriptPath, documentTypePath, language = "en") { // default language is English createDocumentFromTranscript: async function(transcriptPath, documentTypePath, language = "en") { // default language is English
return new Promise(async(resolve, reject) => { return new Promise(async(resolve, reject) => {
try { try {
if (language.toLowerCase() === "de") {
language = "German"
}else if (language.toLowerCase() === "in") {
language = "Indish"
} else {
language = "English"
}
const transcript = await fs.promises.readFile(transcriptPath, "utf-8"); //read transcript file from Path const transcript = await fs.promises.readFile(transcriptPath, "utf-8"); //read transcript file from Path
const documentType = await fs.promises.readFile(documentTypePath, "utf-8"); //read document type from Path const documentType = await fs.promises.readFile(documentTypePath, "utf-8"); //read document type from Path
const promptText = `${documentType}, in language ${language}, transcript:\n\n${transcript}`; //combine doc type, language and transcript - Change prompt here if needed const promptText = `${documentType}, in language ${language}, transcript:\n\n${transcript}`; //combine doc type, language and transcript - Change prompt here if needed
@@ -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)