Compare commits

..

73 Commits

Author SHA1 Message Date
MikeHughes-BIN 2998799826 Window width for the GUI to fit the Arrows 2025-12-15 16:42:44 +01:00
Hughes, Mike 6146e7091b Merge branch 'feature/ui-test' into 'develop'
Added to Step 2 and 3. Added a first new version of the progressbar +...

See merge request proj-wise2526-video2document/video2document!46
2025-12-15 16:26:10 +01:00
Hughes, Mike 3a5540d865 Merge branch 'fix/use-new-meeting-report' into 'develop'
the old report type was still used

See merge request proj-wise2526-video2document/video2document!45
2025-12-15 16:10:37 +01:00
MikeHughes-BIN 948ddb4310 the old report type was still used 2025-12-15 16:06:18 +01:00
Hughes, Mike 889d455fbe Merge branch 'fix/add_puppeteer' into 'develop'
The requirements needed to include puppeteer and html-to-docs

See merge request proj-wise2526-video2document/video2document!44
2025-12-15 15:46:42 +01:00
MikeHughes-BIN 58741f0057 The requirements needed to include puppeteer and html-to-docs 2025-12-15 15:43:03 +01:00
eric.minning 950cc17878 Merge branch 'feature/ui-test' of https://gitlab.rlp.net/proj-wise2526-video2document/video2document into feature/ui-test 2025-12-15 15:22:08 +01:00
eric.minning aba8552d11 Fixes to the loading of the speaker options 2025-12-15 15:19:47 +01:00
Verena Schulz 500e62daf0 Merge branch 'feature/ui-test' of gitlab.rlp.net:proj-wise2526-video2document/video2document into feature/ui-test 2025-12-15 15:05:18 +01:00
Verena Schulz ad6d20b134 Added download button (without function) 2025-12-15 15:04:05 +01:00
Hughes, Mike a8c284edd2 Merge branch 'feature/30-backend-export-funktion-des-dokuments-in-verschiedene-datei-formate-s4-05' into 'develop'
Feature/30 backend export funktion des dokuments in verschiedene datei formate s4 05

See merge request proj-wise2526-video2document/video2document!43
2025-12-15 15:03:36 +01:00
eric.minning 6e0ea41ea6 Implemented a first version for all functions for step5 speaker (untested) 2025-12-15 14:49:43 +01:00
MikeHughes-BIN 3a5808d97c Merge branch 'develop' into feature/30-backend-export-funktion-des-dokuments-in-verschiedene-datei-formate-s4-05 2025-12-15 14:41:28 +01:00
Hughes, Mike 4083d5a5cb Merge branch 'feature/35-backend-llm-chat-gpt-integration-s4-10' into 'develop'
Feature/35 backend llm chat gpt integration s4 10

See merge request proj-wise2526-video2document/video2document!42
2025-12-15 14:33:31 +01:00
MikeHughes-BIN d655925031 merge conflict resolve 2025-12-15 14:30:14 +01:00
MikeHughes-BIN 405a32098c Remove success and error icons from console log messages for consistency 2025-12-15 14:15:40 +01:00
Hughes, Mike 1683502aa1 Merge branch 'develop' into 'feature/35-backend-llm-chat-gpt-integration-s4-10'
# Conflicts:
#   services/modules/llm-gemini/gemini.js
2025-12-15 14:07:32 +01:00
MikeHughes-BIN 789ecd3a31 Update document processing to use prompts from styles and add structured meeting report template 2025-12-15 14:01:51 +01:00
MikeHughes-BIN e72d03efbe Added Pre's Document in the correct Folder 2025-12-15 13:55:40 +01:00
eric.minning d95b6d5178 Merge branch 'feature/ui-test' of https://gitlab.rlp.net/proj-wise2526-video2document/video2document into feature/ui-test 2025-12-15 13:54:40 +01:00
eric.minning 20b4ae6a3b Checkbox fix, old progressbar removal and visibility fix in the new one 2025-12-15 13:54:31 +01:00
MikeHughes-BIN ec57411992 Refactor document generation to output HTML format and update system instructions for clarity 2025-12-15 13:53:14 +01:00
Verena Schulz accb65252c Improved stability 2025-12-15 13:48:27 +01:00
MikeHughes-BIN 271fe78b7b Changed the module to use puppeteer and html-to-docx 2025-12-14 18:14:16 +01:00
MikeHughes-BIN 1e38cc79f4 Extended the Role description 2025-12-14 16:53:35 +01:00
MikeHughes-BIN 746fec05d4 Changed model from llama to quen3 because llama was timing out due to token limit 2025-12-14 16:28:48 +01:00
Spanier, Pit d647f53790 Merge branch 'feature/implementation-of-ci-s4-09' into 'develop'
fixed the pipeline

See merge request proj-wise2526-video2document/video2document!38
2025-12-14 16:13:28 +01:00
emily 791dbab739 edited the timeouts for the transcription and LLM tests 2025-12-13 23:04:31 +01:00
emily c497fa9532 fixed a mistake i made in the pipeline 2025-12-13 23:02:29 +01:00
Verena Schulz bb589e6aad New Checkboxes 2025-12-13 21:21:17 +01:00
emily 3ce632bf8d Optimised the pipeline
Replaced the test video with a lower size version in an attempt at keeping this shitty gitlab.rlp provided action running from crashing constantly
2025-12-13 18:46:02 +01:00
Hughes, Mike 5ea8ec6a1a Merge branch 'feature/implementation-of-ci-s4-09' into 'develop'
implemented first CI pipeline version with a test test that only checks if 1...

See merge request proj-wise2526-video2document/video2document!34
2025-12-13 14:13:06 +01:00
eric.minning 4747c44c52 Merge remote-tracking branch 'origin/develop' into feature/ui-test 2025-12-11 16:23:00 +01:00
eric.minning a0abedc921 Removed a error in the speakerAudioViewer 2025-12-11 16:04:16 +01:00
MikeHughes-BIN 7cd334645f Added a module that safes the file in a requested format. 2025-12-11 14:22:04 +01:00
Spanier, Pit 15e2e35bda Merge branch 'feature/35-backend-llm-chat-gpt-integration-s4-10' into 'develop'
Feature/35 backend llm chat gpt integration s4 10

See merge request proj-wise2526-video2document/video2document!35
2025-12-11 13:30:14 +01:00
MikeHughes-BIN 53508b175a changed Display name 2025-12-11 13:27:00 +01:00
MikeHughes-BIN 3dd8485140 audit 2025-12-11 13:23:22 +01:00
MikeHughes-BIN 68c1f0ed9f removed google from requires 2025-12-11 13:20:54 +01:00
MikeHughes-BIN 2b597add6c fixed a unicode bug 2025-12-11 13:03:04 +01:00
MikeHughes-BIN 3af038d195 Multiple AI models implemented - chatgpt, llama 2025-12-11 12:41:11 +01:00
MikeHughes-BIN b546c96238 Refactor code structure for improved readability and maintainability 2025-12-11 12:08:07 +01:00
emily 6512b50d83 removal of an unneeded file 2025-12-10 22:46:25 +01:00
Verena Schulz b26ef96cfc Prototype hamburger menu for language and help buttons 2025-12-10 16:59:14 +01:00
emily faee605f12 Initial version of the working CI pipeline
Added mocha based unit tests for each module
Did a bit of cleanup in the modules to remove debug console.log calls
Removed the Progress bar in the extractor and the library requirement
Promisified the gemini module to make sure it returns the path as a promise instead of just on the cli
Fixed gitignore so that it now only ignores the content int the storage directories, and not the whole directories
Added neetingReport.json for the LLMs to use
2025-12-09 22:07:43 +01:00
emily 037d29e656 added gitkeep to the storage folders, and edited the gitignore to be more precise for our use case 2025-12-09 20:53:16 +01:00
emily 8c201db539 implemented first CI pipeline version with a test test that only checks if 1 is 1, needs implementation of actual tests 2025-12-09 17:56:59 +01:00
eric.minning 6eef82bb64 Implemented the color change of the progressbar 2025-12-09 17:03:02 +01:00
eric.minning b182e0a3c7 fixed the submitbutton 2025-12-09 15:23:49 +01:00
eric.minning 34a8b186a8 removed an event listener which was doubeld and a submitbutton call 2025-12-09 15:12:32 +01:00
eric.minning 70d3bfa201 fixed error in file lookup 2025-12-09 15:01:16 +01:00
eric.minning 5661f7c13f speaker fix 2025-12-09 14:50:19 +01:00
eric.minning 208879b497 Merge remote-tracking branch 'origin/develop' into feature/ui-test 2025-12-09 14:40:44 +01:00
eric.minning f817d4c29e Changed checkboxvalues and added more error catching 2025-12-09 14:38:26 +01:00
Spanier, Pit 882b49a489 Merge branch 'feature/audio-format-on-transcription-tool-selection-s4-08' into 'develop'
removed the need to send the transcription module defined audio output format...

See merge request proj-wise2526-video2document/video2document!33
2025-12-09 14:31:25 +01:00
eric.minning 5e6b700c36 Merge remote-tracking branch 'origin/develop' into feature/ui-test 2025-12-09 14:21:14 +01:00
Spanier, Pit 25d11d74a3 Merge branch 'fix/main-path-to-document-for-ai' into 'develop'
Currently we only have the standard meeting document and the config right now...

See merge request proj-wise2526-video2document/video2document!32
2025-12-09 14:12:14 +01:00
Verena Schulz 0efb7585ba Fixed height of middle container and several other css bugs 2025-12-07 16:09:12 +01:00
MikeHughes-BIN 0d3535128d Currently we only have the standard meeting document and the config right now prevents the programm from running. Can be adapted later 2025-12-06 16:43:32 +01:00
emily e661aefbe8 added json to send to frontend for the speaker naming functionality 2025-12-05 17:09:45 +01:00
eric.minning 59b820d917 Added to Step 2 and 3. Added a first new version of the progressbar + functions in Step 4. Implemented the basics and functions for Step 5 2025-12-05 17:02:20 +01:00
emily ed94928953 removed the need to send the transcription module defined audio output format to the frontend, and handle it on the backend instead 2025-12-05 16:45:39 +01:00
emily 8adf470f18 removed a debug console output 2025-12-05 15:58:45 +01:00
emily f858f2edbb added functionality for transcription tool to tell the frontend what audio format to use 2025-12-05 15:58:17 +01:00
MikeHughes-BIN 425e24853e Similar to the Gemini LLM we now have a ChatGPT REST call. The API Key is still missing 2025-12-04 10:58:50 +01:00
Verena Schulz 1c5d399c58 Added 5th and 6th step, headline above steps 2025-12-03 17:55:55 +01:00
Verena Schulz 33093733ef Added arrows for navigating through steps 2025-12-02 17:13:36 +01:00
Verena Schulz 4f0d2bcc4a Fixed mockup sequence 2025-11-27 18:57:17 +01:00
Spanier, Pit 05c04aaef2 Merge branch 'feature/gemini-ai' into 'develop'
REST API & Auto Naming

See merge request proj-wise2526-video2document/video2document!31
2025-11-26 21:57:51 +01:00
MikeHughes-BIN 84cc94aa34 REST API & Auto Naming 2025-11-26 21:55:19 +01:00
Verena Schulz ef379a5b7b First prototype of new mockup 2025-11-25 16:31:26 +01:00
Verena Schulz 0bbbb2f9b7 Fixed submit button bug and progessbar not showing when uploading video with button 2025-11-25 15:11:18 +01:00
Spanier, Pit 16f319f89f Merge branch 'feature/implementing-everything' into 'develop'
working on implementing everything

See merge request proj-wise2526-video2document/video2document!27
2025-11-24 17:25:37 +01:00
32 changed files with 3765 additions and 271 deletions
+15 -1
View File
@@ -964,8 +964,22 @@ app.*.symbols
!/dev/ci/**/Gemfile.lock
# Storage files
storage/
storage/documents/*
storage/transcriptionSummaries/*
storage/transcripts/*
storage/video/*
# The inverse for the .gitkeep files, to make sure the folders are there but not the local files
!storage/documents/.gitkeep
!storage/transcriptionSummaries/.gitkeep
!storage/transcripts/.gitkeep
!storage/video/.gitkeep
*.mp4
*.webm
*.mp3
*.mov
*.wav
*.flac
!testvideo.mp4
+19 -25
View File
@@ -1,33 +1,27 @@
workflow:
rules:
# Run the pipeline for merge requests or when committing to a branch
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
- if: $CI_COMMIT_BRANCH
image: python:3.14.0
image: node:latest
stages:
- setup
# - install
- test
setup_environment:
stage: setup
script:
- pip install --upgrade pip
- pip install -r requirements.txt
- echo "Dependencies installed successfully."
# job-install:
# stage: install
# script:
# - npm install
# artifacts:
# untracked: false
# when: on_success
# access: all
# expire_in: "30 days"
# paths:
# - node_modules
only:
- main
- feature/ci-pipeline-s1-09a-1 # You can add more branches if needed
test_app:
job-test:
stage: test
script:
- echo "Running V2D Framework basic test..."
- python -m unittest discover || echo "No tests found."
only:
- main
- feature/ci-pipeline-s1-09a-1
- npm install
- echo "ASSEMBLYAI_API_KEY=$apikey_assembly" > .env
- echo "GOOGLE_API_KEY=$apikey_gemini" >> .env
- npm test
+121 -25
View File
@@ -8,29 +8,41 @@
</head>
<body>
<div class="mitte" id="mitte">
<div class="labelDiv" id="labelDiv">
<label id="labelKI">Select ki:</label>
<label id="labelTranscription">Select transcription:</label>
<label id="labelLanguage">Select language:</label>
<img id="labelLanguageFlag" src="flags/united-kingdom-flag-png-large.jpg" width="20" height="10" >
</div>
<div class="dropdownMenus" id="dropdownMenus">
<select name="ai_type" id="ai_type">
</select>
<select name="transkript_type" id="transkript_type">
</select>
<select name="output_type" id="output_type">
<option value="flac">flac</option>
<option value="mp3">mp3</option>
<option value="wav">wav</option>
</select>
<select name="language_option" id="language_option">
</select>
</div>
<div id="h1-wrapper">
<section class="p-menu1">
<nav id="navbar" class="navigation" role="navigation">
<input id="toggle1" type="checkbox" />
<label class="hamburger1" for="toggle1">
<div class="top"></div>
<div class="meat"></div>
<div class="bottom"></div>
</label>
<nav class="menu1">
<li class="li1">Help</li>
<li class="li1">Language</li>
</nav>
</nav>
</section>
<h1 id="h1">Video to document</h1>
</div>
<div class="step-nav">
<div class="step-item active" data-step="1">1. Step</div>
<div class="step-item" data-step="2">2. Step</div>
<div class="step-item" data-step="3">3. Step</div>
<div class="step-item" data-step="4">4. Step</div>
<div class="step-item" data-step="5">5. Step</div>
<div class="step-item" data-step="6">6. Step</div>
</div>
<div id="middleContainerWrapper" class="middle-container-wrapper">
<button id="prevBtn" class="navBtn" disabled>&larr;</button>
<div class="mitte" id="mitte">
<div class="step" id="step1">
<div class="upload-container" id="uploadContainer">
<p id="p1">Drag and drop video file</p>
<video id="previewThumbnail" autoplay="false">
@@ -42,27 +54,111 @@
<button class="custom-btn" id="manualUploadBtn">Search video</button>
<input type="file" id="videoUpload" accept="video/*">
</div>
</div>
<div class="step" id="step2" style="display:none;">
<div class="labelDiv" id="labelDiv">
<label id="labelKI">Select ki:</label>
<label id="labelTranscription">Select transcription:</label>
<label id="labelType">Select type:</label>
<label id="labelLanguage">Select language:</label>
<img id="labelLanguageFlag" src="flags/united-kingdom-flag-png-large.jpg" width="20" height="10" >
</div>
<div class="dropdownMenus" id="dropdownMenus">
<select name="ai_type" id="ai_type">
</select>
<select name="transkript_type" id="transkript_type">
</select>
<select name="output_type" id="output_type">
<option value="pdf">.pdf</option>
<option value="word">.word</option>
<option value="txt">.txt</option>
</select>
<select name="language_option" id="language_option">
</select>
</div>
</div>
<div class="step" id="step3" style="display:none;">
<div class="checkbox-group">
<label id="checkbox_group" for="checkbox-group">Choose prefered document style:</label>
<label id="checkbox-label" for="checkbox-group">Choose prefered document style:</label>
<div class="checkbox-container">
<input type="checkbox" name ="docFormat" id="docFormat" value="Meeting report">
<label id="label_format" for="docFormat">Meeting report</label>
<label id="label_format" for="docFormat">Follow-up Report</label>
</div>
<div class="checkbox-container">
<input type="checkbox" name="docFormat" id="docFormatSummary" value="Summary with timestamps">
<label id="label_summary" for="docFormatSummary">Summary with timestamps</label>
<input type="checkbox" name="docFormat" id="docFormatSummary1" value="Summary with timestamps">
<label id="label_summary" for="docFormatSummary">Agenda</label>
</div>
<div class="checkbox-container">
<input type="checkbox" name="docFormat" id="docFormatSummary2" value="Summary with timestamps">
<label id="label_summary" for="docFormatSummary">Resultprotocol</label>
</div>
<div class="checkbox-container">
<input type="checkbox" name="docFormat" id="docFormatSummary3" value="Summary with timestamps">
<label id="label_summary" for="docFormatSummary">Sprint Planning Note</label>
</div>
<div class="checkbox-container">
<input type="checkbox" name="docFormat" id="docFormatCustom" value="Summary with timestamps">
<select name="ai_type" id="ai_type">
<option>nichts</option>
</select>
</div>
</div>
</div>
<div class="step" id="step4" style="display:none;">
<button class="submit-btn" id="submitButton" onclick="checkBoxes()" disabled>Submit</button>
<div class="testy" id="testy">
<div class="box2" id="box1">
</div>
<p>---Starting---</p>
<div class="box2" id="box2">
</div>
<p>---Transkribing---</p>
<div class="box2" id="box3">
</div>
<p>---Document creation---</p>
<div class="box2" id="box4">
</div>
</div>
</div>
<div class="step" id="step5" style="display:none;">
<div class="speakerView" id="speakerView">
<label id="labelSpeaker">Select Speaker:</label>
<select name="cur_speaker" id="cur_speaker">
<options>Stefan</options>
</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>
<button id="speakerLocker" onclick="rewriteSpeakerName()">Rename Speaker</button>
<button id="speakerResender" onclick="sendSpeakerPackages()">Rewrite document</button>
</div>
<div class="step" id="step6" style="display:none;">
<button class="download-btn" id="downloadButton" onclick="" disabled>Download</button>
<div class="progressbar" id="progressbar">
<div class="progress_fill"></div>
<span class="progress_text">0%</span>
</div>
</div>
</div>
<button id ="nextBtn" class="navBtn">&rarr;</button>
</div>
<script src="languages.js"></script>
<script src="script.js"></script>
+8 -3
View File
@@ -15,10 +15,15 @@ try {
getModuleNames: () => ipcRenderer.invoke('get-module-names')
})
contextBridge.exposeInMainWorld('electron', {
progress: (callback) => ipcRenderer.on('progress', callback)
})
ipcRenderer.on("progress", (event, resp) => {
alert(`Finished step ${resp.curstep} of ${resp.totalsteps}`)
contextBridge.exposeInMainWorld('audios', {
speakerAudios: (callback) => ipcRenderer.on('speakerAudios', callback)
})
contextBridge.exposeInMainWorld("submitSpeaker", {
submitSpeaker: (speaker_names) => {ipcRenderer.send("speaker_submit", speaker_names)}
})
ipcRenderer.on("error", (event, err) => {alert(err)})
+174 -13
View File
@@ -55,24 +55,16 @@ language_option.addEventListener('change', (e)=>{
});
//listener for the file explorer search when something got selected
videoUpload.addEventListener("change", () => {
try {
activateSubmitBtn(videoUpload.files.length > 0);
if (videoUpload.files.length > 0) {
const file = videoUpload.files;
handleFiles(file);
}
} catch (error) {
console.log(error);
}
});
//listener for the file explorer search when something got selected
videoUpload.addEventListener('change', () => {
try {
handleFiles(videoUpload.files);
} catch (error) {
console.log("Error in manualBtn EventListener change");
console.log(error);
}
});
//listener for the file explorer search
@@ -85,3 +77,172 @@ manualUploadBtn.addEventListener('click', () => {
}
});
stepButtons.forEach(btn => {
btn.addEventListener("click", () => {
try {
const step = parseInt(btn.dataset.step);
showStep(step);
} catch (error) {
}
});
});
prevBtn.addEventListener("click", () => {
try {
if (currentStep > 1) showStep(currentStep - 1);
} catch (error) {
}
});
nextBtn.addEventListener("click", () => {
try {
if(currentStep < totalSteps) showStep(currentStep + 1);
} catch (error) {
}
});
//Checkboxlistener so that only one can be selected at a time
docFormat.addEventListener("change", (e) =>{
try {
if(docFormat.checked){
docFormatSummary1.checked = false;
docFormatSummary2.checked = false;
docFormatSummary3.checked = false;
docFormatCustom.checked = false;
}
} catch (error) {
}
})
docFormatSummary1.addEventListener("change", (e) =>{
try {
if(docFormatSummary1.checked){
docFormat.checked = false;
docFormatSummary2.checked = false;
docFormatSummary3.checked = false;
docFormatCustom.checked = false;
}
} catch (error) {
}
})
docFormatSummary2.addEventListener("change", (e) =>{
try {
if(docFormatSummary2.checked){
docFormatSummary1.checked = false;
docFormat.checked = false;
docFormatSummary3.checked = false;
docFormatCustom.checked = false;
}
} catch (error) {
}
})
docFormatSummary3.addEventListener("change", (e) =>{
try {
if(docFormatSummary3.checked){
docFormatSummary1.checked = false;
docFormatSummary2.checked = false;
docFormat.checked = false;
docFormatCustom.checked = false;
}
} catch (error) {
}
})
docFormatCustom.addEventListener("change", (e) =>{
try {
if(docFormatCustom.checked){
docFormatSummary1.checked = false;
docFormatSummary2.checked = false;
docFormatSummary3.checked = false;
docFormat.checked = false;
}
} catch (error) {
}
})
//Speaker change listener
cur_speaker.addEventListener("change", (e) =>{
try {
document.getElementById("speakerAudioViewer").src = valy[e.target.value].source;
} catch (error) {
}
})
window.electron.speakerAudios((event, arg) => {
try {
setSpeakerAudiosValue(arg);
loadSpeakerOptions(arg);
} catch (error) {
}
})
window.electron.progress((event, arg) => {
if(arg.curstep == 1){
setCircleOne();
}else if(arg.curstep == 2){
setCircleZwo();
} else if(arg.curstep == 3){
setCircleThree();
}else if(arg.curstep == 4){
setCircleFour();
}
});
function setCircleOne(){
try {
if(document.getElementById("box1").style.backgroundColor == "green"){
document.getElementById("box1").style.backgroundColor = "red";
}else{
document.getElementById("box1").style.backgroundColor = "green";
}
} catch (error) {
}
}
function setCircleZwo(){
try {
if(document.getElementById("box2").style.backgroundColor == "green"){
document.getElementById("box2").style.backgroundColor = "red";
}else{
document.getElementById("box2").style.backgroundColor = "green";
}
} catch (error) {
}
}
function setCircleThree(){
try {
if(document.getElementById("box3").style.backgroundColor == "green"){
document.getElementById("box3").style.backgroundColor = "red";
}else{
document.getElementById("box3").style.backgroundColor = "green";
}
} catch (error) {
}
}
function setCircleFour(){
try {
if(document.getElementById("box4").style.backgroundColor == "green"){
document.getElementById("box4").style.backgroundColor = "red";
}else{
document.getElementById("box4").style.backgroundColor = "green";
}
} catch (error) {
}
}
+119 -3
View File
@@ -1,4 +1,4 @@
let currentVideoPath = null;
//function to check if one checkbox is at least klicked
function checkBoxes() {
try {
@@ -37,6 +37,11 @@ function checkBoxes() {
iter++;
}
});
document.getElementById("testy").style.visibility = "visible"
document.getElementById("box1").style.backgroundColor = "red";
document.getElementById("box2").style.backgroundColor = "red";
document.getElementById("box3").style.backgroundColor = "red";
document.getElementById("box4").style.backgroundColor = "red";
console.log(selectedCheckboxes);
const outputType = document.getElementById("output_type");
const transcriptionType = document.getElementById("transkript_type");
@@ -100,11 +105,13 @@ function changeLanguage(language) {
function handleFiles(files) {
try {
if (files.length > 0) {
document.getElementById("progressbar").style.visibility = "visible";
const file = files[0];
if (file.type.startsWith('video/')) {
const filePath = window.explorer.onFileDrop(files[0])
videoUpload.files = files;
fileName.textContent = `Chosen video: ${file.name}`;
currentVideoPath = filePath;
generateThumbnail(filePath);
activateSubmitBtn(true);
}
@@ -167,6 +174,25 @@ function loadTranscriptionOptions(options){
}
}
//function to load data type options to the drop down list
function loadDataTypeOptions(options){
try {
var menu = document.getElementById('output_type');
var object_holdy;
var choice ;
object_holdy = options
for(i = 0; i < options.length; i++){
choice = document.createElement('option');
choice.textContent = object_holdy[i].displayname;
choice.value = object_holdy[i].name;
menu.appendChild(choice);
}
} catch (error) {
console.log("Error in script.js function loadDataTypeOptions");
console.log(error);
}
}
//function to load language options to the drop down list
function loadLanguageOptions(){
try {
@@ -180,16 +206,48 @@ function loadLanguageOptions(){
choice.value = object_holdy[i];
menu.appendChild(choice);
}
} catch (error) {
console.log("Error in script.js loadLanguageOptions function");
console.log(error);
}
}
//function to load speaker options to the drop down list
function loadSpeakerOptions(options){
try {
var menu = document.getElementById('speaker_option');
var object_holdy;
var choice;
object_holdy = options.keys();
for(i = 0; i < options.length; i++){
choice = document.createElement('option');
choice.textContent = options[object_holdy[i]].name;
choice.value = options[object_holdy[i]].name;
menu.appendChild(choice);
}
} catch (error) {
console.log("Error in script.js loadSpeakerOptions function");
console.log(error);
}
}
//function to load speaker audio file options to the drop down list
function loadSpeakerAudio(option){
try {
var menu = document.getElementById('speakerAudioViewer');
var aud = document.createElement("source");
aud.src = options;
menu.appendChild(aud);
} catch (error) {
console.log("Error in script.js loadSpeakerAudio function");
console.log(error);
}
}
function activateSubmitBtn(hasFile){
try {
submitButton.disabled = !hasFile;
document.getElementById("submitButton").disabled = !hasFile;
} catch (error) {
console.log(error);
}
@@ -214,3 +272,61 @@ function generateThumbnail(path){
videoElement.style.maxHeight = 40;
videoElement.autoplay = false;
}
//Step-navigation
const steps = document.querySelectorAll(".step");
const stepButtons = document.querySelectorAll(".step-item");
let currentStep = 1;
const totalSteps = steps.length;
function showStep(stepNumber) {
if (stepNumber < 1 || stepNumber > totalSteps){
console.error("StepNumber out of Bounds", stepNumber);
return;
}
steps.forEach(step => step.style.display = "none");
document.getElementById("step" + stepNumber).style.display = "block";
stepButtons.forEach(btn => btn.classList.remove("active"));
document.querySelector(`.step-item[data-step="${stepNumber}"]`).classList.add("active");
const activeBtn = document.querySelector(`.step-item[data-step="${stepNumber}"]`);
if(activeBtn) activeBtn.classList.add("active");
prevBtn.disabled = stepNumber == 1;
nextBtn.disabled = stepNumber === totalSteps;
currentStep = stepNumber;
}
//Audio value setter
var speakerAudios = {};
var speakerEndValues = {};
function setSpeakerAudiosValue(valy){
try {
speakerAudios = valy;
speakerRewriten = valy;
document.getElementById("speakerAudioViewer").src = valy.speakerA.source;
} catch (error) {
}
}
function rewriteSpeakerName(){
try {
var tempy = document.getElementById("cur_speaker").textContent;
speakerAudios[tempy].name = document.getElementById("newSpeaker").textContent;
document.getElementById("cur_speaker").textContent = document.getElementById("newSpeaker").textContent;
} catch (error) {
}
}
function sendSpeakerPackages(){
try {
window.sendSpeakerPackages(speakerAudios);
} catch (error) {
}
}
+311 -20
View File
@@ -6,10 +6,32 @@ body {
align-items: center;
height: 100vh;
background-color: #f2f3f4;
gap: 15px;
gap: 10px;
margin: 0;
}
#h1 {
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
margin: 0;
z-index: 20;
}
#h1-wrapper {
position: relative;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
width: 770px;
height: 60px;
background-color: #FFF;
box-shadow: 0px 4px 10px rgba(0,0,0,0.1);
border-radius: 5px;
margin-bottom: 10px;
display: flex;
align-items: center;
}
.upload-container {
background: white;
@@ -91,25 +113,73 @@ input[type="file"] {
display: none;
}
.checkbox-group {
--borderColor: #007bfff5;
--borderWidth: .125em;
}
.checkbox-group input[type=checkbox] {
-webkit-appearance: none;
appearance: none;
vertical-align: middle;
background: #fff;
font-size: 1.8em;
border-radius: 0.125em;
display: inline-block;
border: var(--borderWidth) solid var(--borderColor);
width: 1em;
height: 1em;
position: relative;
}
.checkbox-group input[type=checkbox]:before,
.checkbox-group input[type=checkbox]:after {
content: "";
position: absolute;
background: var(--borderColor);
width: calc(var(--borderWidth) * 3);
height: var(--borderWidth);
top: 50%;
left: 10%;
transform-origin: left center;
}
.checkbox-group input[type=checkbox]:before {
transform: rotate(45deg) translate(calc(var(--borderWidth) / -2), calc(var(--borderWidth) / -2)) scaleX(0);
transition: transform 200ms ease-in 200ms;
}
.checkbox-group input[type=checkbox]:after {
width: calc(var(--borderWidth) * 5);
transform: rotate(-45deg) translateY(calc(var(--borderWidth) * 2)) scaleX(0);
transform-origin: left center;
transition: transform 200ms ease-in;
}
.checkbox-group input[type=checkbox]:checked:before {
transform: rotate(45deg) translate(calc(var(--borderWidth) / -2), calc(var(--borderWidth) / -2)) scaleX(1);
transition: transform 200ms ease-in;
}
.checkbox-group input[type=checkbox]:checked:after {
width: calc(var(--borderWidth) * 5);
transform: rotate(-45deg) translateY(calc(var(--borderWidth) * 2)) scaleX(1);
transition: transform 200ms ease-out 200ms;
}
.checkbox-group input[type=checkbox]:focus {
outline: calc(var(--borderWidth) / 2) dotted rgba(0, 0, 0, 0.25);
}
.checkbox-container{
margin-top: 8px;
display: flex;
justify-items: left;
align-items: center;
gap: 5px;
}
.checkbox-group {
margin-top: 15px;
margin-bottom: 15px;
display: flex;
flex-direction: column;
gap: 10px;
align-items: flex-start;
}
.submit-btn {
display: flex;
justify-content: center;
padding: 10px 20px;
margin-top: 10px;
margin-left: 80px;
margin-top: 30px;
margin-bottom: 10px;
background-color: #007BFF;
color: white;
@@ -126,13 +196,12 @@ gap: 5px;
}
.mitte {
background-color: #FDFCFA;
background-color: #FFF;
display: flex;
width: 700px;
flex-direction: column;
align-items: center;
padding: 5% 50px;
margin-top: 20px;
padding: 40px;
gap: 10px;
border: 0px;
border-color: black;
@@ -141,14 +210,11 @@ gap: 5px;
box-shadow: 0px 4px 10px rgba(0,0,0,0.1);
}
h1 {
align-content: center;
}
.progressbar{
position: relative;
width: 210px;
height: 30px;
margin: 50px 20px 5px 20px;
background: rgb(42, 46, 78);
border-radius: 5px;
overflow: hidden;
@@ -173,9 +239,10 @@ h1 {
.dropdownMenus {
display: flex;
justify-content: flex-end;
justify-content: center;
margin-top: 1px;
gap: 150px;
margin-bottom: 30px;
gap: 170px;
padding: 2px 10px 2px 10px;
}
@@ -184,5 +251,229 @@ h1 {
}
.labelDiv {
gap: 200px;
gap: 60px;
display: flex;
justify-content: center;
overflow-wrap:inherit;
padding-bottom: 20px;
margin-top: 40px;
margin-bottom: 10px;
}
/*Step bar*/
.step-nav {
display: flex;
gap: 20px;
justify-content: center;
background: #fff;
padding: 10px 30px;
border-radius: 6px;
box-shadow: 0 4px 10px rgba(0,0,0,0.1);
}
.step-item {
padding: 10px 25px;
border-radius: 8px;
background: #eee;
cursor: pointer;
font-weight: bold;
transition: 0.2s;
}
.step-item.active {
background: #007BFF;
color: white;
}
.step-item:hover {
background: #d9d9d9;
}
/*panels*/
.step {
margin-top: 70px;
display: flex;
flex-direction: column;
justify-content: center;
min-height: 400px;
}
/*Navigation arrows*/
.step-nav-arrows {
display: flex;
justify-content: space-between;
align-items: center;
}
.middle-container-wrapper {
display: flex;
align-items: center;
justify-content: center;
gap: 30px;
width: max-content;
height: auto;
}
.navBtn {
display: flex;
justify-content: center;
padding: 10px 25px;
background-color: #007BFF;
color: white;
border: none;
border-radius: 8px;
cursor: pointer;
font-size: 14px;
}
.navBtn:disabled {
background-color: #ccc;
cursor: not-allowed;
}
.testy{
background-color: #FFF;
display: flex;
width: auto;
flex-direction: row;
align-items: center;
margin-top: 20px;
gap: 10px;
border: 0px;
border-color: black;
border-style: solid;
border-radius: 6px;
visibility: hidden;
}
.box2 {
background-color: red;
width: 40px;
height: 40px;
padding: 5px;
border: 1px solid black;
margin: 5px;
border-radius: 100px;
}
li {
color: #FFF;
}
.p-menu1 {
margin-left: 20px;
z-index: 10;
}
.hamburger1 {
height: 45px;
margin: 0;
padding-top: 8px;
display: grid;
grid-template-rows: repeat(3, 1fr);
justify-items: center;
z-index: 120;
}
.hamburger1 div {
background-color: rgb(61, 61, 61);
position: relative;
width: 40px;
height: 5px;
margin-top: 0;
-webkit-transition: all 0.2s ease-in-out;
transition: all 0.2s ease-in-out;
}
#toggle1 {
display: none;
}
#toggle1:checked + .hamburger1 .top {
-webkit-transform: rotate(-45deg);
transform: rotate(-45deg);
margin-top: 22.5px;
}
#toggle1:checked + .hamburger1 .meat {
-webkit-transform: rotate(45deg);
transform: rotate(45deg);
margin-top: -5px;
}
#toggle1:checked + .hamburger1 .bottom {
-webkit-transform: scale(0);
transform: scale(0);
}
#toggle1:checked ~ .menu1 {
height: 150px;
width: 300px;
}
/* Menu */
.menu1 {
position: absolute;
top: 55px;
left: 20px;
width: 240px;
border-radius: 5px;
background-color: #1C3B69;
margin: 0;
display: -ms-grid;
display: grid;
grid-template-rows: 1fr repeat(4, 0.5fr);
grid-row-gap: 25px;
padding: 0;
list-style: none;
clear: both;
width: auto;
text-align: center;
height: 0px;
overflow: hidden;
transition: height 0.3s ease, width 0.3s ease;
z-index: 9999;
-webkit-transition: all 0.3s ease;
}
.menu1 li:first-child {
margin-top: 30px;
}
.menu1 li:last-child {
margin-bottom: 30px;
}
.li1 {
width: 100%;
margin: 0;
padding: 10px 0;
font: 700 20px 'Oswald', sans-serif;
}
.li1:hover {
background-color: #FFF;
color: rgb(61, 61, 61);
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
-webkit-transition: all 0.3s ease;
transition: all 0.3s ease;
}
#step2, #step3, #step5 {
font-size: larger;
}
.download-btn {
display: flex;
justify-content: center;
padding: 10px 20px;
margin-left: 80px;
margin-top: 30px;
margin-bottom: 10px;
background-color: #007BFF;
color: white;
border: none;
border-radius: 8px;
cursor: pointer;
font-size: 14px;
}
+23 -3
View File
@@ -61,7 +61,7 @@ let mainWindow;
function createWindow() {
mainWindow = new electron.BrowserWindow({
width: 800,
width: 1200,
height: 800,
webPreferences: {
nodeIntegration: false,
@@ -141,7 +141,7 @@ electron.ipcMain.on("file_submit", async (event, args) => {
console.log("\n\n Running the Video to Audio Extractor");
// This code handles the Video to Audio extraction module call
await mapFunctions.get("module-handler").function(args.video.module, {inputVideoPath: args.video.inputVideoPath, outputType: args.video.outputType}).then(resp => {
await mapFunctions.get("module-handler").function(args.video.module, {inputVideoPath: args.video.inputVideoPath, outputType: mapFunctions.get(args.transcription.module).audioformat}).then(resp => {
console.log(resp);
audiopath = resp
curstep++
@@ -183,7 +183,9 @@ electron.ipcMain.on("file_submit", async (event, args) => {
// TODO implement documentation module
// This code handles the Text to Document processing module call
for (let i = 0; i < args.document.styles.length; i++) {
await mapFunctions.get("module-handler").function(args.document.module, {prompt: args.document.styles[i].prompt, transcript: transcriptpath}).then(resp => {
console.log(`\n\n Running the LLM for Document Style ${i+1}`);
await mapFunctions.get("module-handler").function(args.document.module, {inputTranscriptPath: transcriptpath, documentTypePath: "./storage/documentType/standard_meeting_report.txt", language: "en"}).then(resp => {
console.log(resp);
transcriptpath = resp
curstep++
@@ -194,6 +196,24 @@ electron.ipcMain.on("file_submit", async (event, args) => {
})
}
// TODO actually implement this functionality
// Module to get the first few lines for each speaker to send to the frontend
// await mapFunctions.get("speaker-getter-idfk").function(transcriptpath).then(resp => {
// console.log(resp);
// transcriptpath = resp
// curstep++
// mainWindow.webContents.send("progress", {curstep:curstep, totalsteps:totalsteps})
// // {
// // speakerA: {source: "Pfad zur Audio File"},
// // speakerB:.....
// // }
mainWindow.webContents.send("speakers", {speakerA:"pfad1", speakerB:"pfad2"})
// }).catch(err => {
// mainWindow.webContents.send("error", err)
// return
// })
} catch (error) {
console.log(error);
+1885 -40
View File
File diff suppressed because it is too large Load Diff
+5 -3
View File
@@ -3,12 +3,14 @@
"@google/genai": "^1.30.0",
"@types/axios": "^0.9.36",
"axios": "^1.13.2",
"cli-progress": "^3.12.0",
"dotenv": "^17.2.3",
"electron": "^39.1.1",
"express": "^5.1.0",
"ffmpeg-static": "^5.2.0",
"fluent-ffmpeg": "^2.1.3"
"fluent-ffmpeg": "^2.1.3",
"html-to-docx": "^1.8.0",
"mocha": "^11.7.5",
"puppeteer": "^24.33.0"
},
"devDependencies": {
"@types/cli-progress": "^3.11.6",
@@ -27,7 +29,7 @@
},
"scripts": {
"start": "electron main.js",
"test": "echo \"Error: no test specified\" && exit 1"
"test": "mocha ./test/unit/test.js"
},
"repository": {
"type": "git",
-3
View File
@@ -1,3 +0,0 @@
fastapi
uvicorn
pytest
+4 -2
View File
@@ -6,17 +6,19 @@ platform = process.platform
mainDir = __dirname
fs = require("fs")
readline = require("readline")
puppeteer = require("puppeteer")
htmltodocx = require("html-to-docx")
config = require("./config/config")
ffmpegPath = require('ffmpeg-static');
ffmpeg = require('fluent-ffmpeg');
path = require('path');
cliProgress = require('cli-progress');
// cliProgress = require('cli-progress');
// { app, BrowserWindow, ipcMain, dialog } = require('electron');
electron = require('electron');
genai = require("@google/genai");
axios = require("axios")
+47
View File
@@ -0,0 +1,47 @@
const fs = require('fs');
const path = require('path');
//node show-models.js, remember to set SAIA_API_KEY in your environment before running the script
const SAIA_API_KEY = process.env.SAIA_API_KEY;
const SAIA_MODELS_URL = "https://chat-ai.academiccloud.de/v1/models";
// Script to list available models
(async () => {
if (!SAIA_API_KEY) {
console.error("ERROR: SAIA_API_KEY environment variable is not set!");
process.exit(1);
}
console.log("Fetching available models from SAIA...\n");
try {
const response = await fetch(SAIA_MODELS_URL, {
method: "GET",
headers: {
"Authorization": `Bearer ${SAIA_API_KEY}`,
"Accept": "application/json"
}
});
if (!response.ok) {
const text = await response.text();
throw new Error(`SAIA API error (${response.status}): ${text}`);
}
const data = await response.json();
console.log("Available models:");
console.log(JSON.stringify(data, null, 2));
if (data.data && Array.isArray(data.data)) {
console.log("\n\nModel IDs:");
data.data.forEach(model => {
console.log(`- ${model.id}`);
});
}
} catch (error) {
console.error("Error fetching models:", error);
}
})();
+194
View File
@@ -0,0 +1,194 @@
const fs = require('fs');
const path = require('path');
const puppeteer = require('puppeteer');
const htmlToDocx = require('html-to-docx');
const { execSync } = require('child_process');
const os = require('os');
const outputDir = path.join(__dirname, "../../../storage/documents");
if (!fs.existsSync(outputDir)) {
fs.mkdirSync(outputDir, { recursive: true });
}
async function showSaveDialog(defaultName, format) {
const platform = os.platform();
if (platform === 'darwin') {
// macOS
const applescript = `
set defaultName to "${defaultName}.${format}"
set theFile to choose file name with prompt "Dokument speichern als:" default name defaultName
POSIX path of theFile
`;
try {
const result = execSync(`osascript -e '${applescript}'`, { encoding: 'utf8' });
return result.trim();
} catch (err) {
if (err.status === 1) return null; // User canceled
throw err;
}
} else if (platform === 'win32') {
// Windows
const powershell = `
Add-Type -AssemblyName System.Windows.Forms
$dialog = New-Object System.Windows.Forms.SaveFileDialog
$dialog.FileName = "${defaultName}.${format}"
$dialog.Filter = "${format.toUpperCase()} Dateien (*.${format})|*.${format}|Alle Dateien (*.*)|*.*"
$dialog.Title = "Dokument speichern als"
$result = $dialog.ShowDialog()
if ($result -eq 'OK') { $dialog.FileName }
`;
try {
const result = execSync(`powershell -Command "${powershell.replace(/\n/g, '; ')}"`, {
encoding: 'utf8'
});
return result.trim() || null;
} catch (err) {
throw err;
}
} 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 = {
name: "htmlDocumentConverter",
type: "converter",
displayname: "HTML Document Converter",
description: "Converts LLM-generated HTML to PDF, DOCX, TXT, or HTML",
/**
* Main conversion function
* @param {Object} options
* @param {string} options.inputPath - Path to the HTML input
* @param {string} options.format - 'pdf' | 'docx' | 'html' | 'txt'
* @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)
*/
async convert({ inputPath, format = 'pdf', outputName, showDialog = false }) {
if (!fs.existsSync(inputPath)) {
throw new Error(`Input file not found: ${inputPath}`);
}
const ext = path.extname(inputPath).toLowerCase();
const baseName = outputName || path.basename(inputPath, ext);
let outputFile;
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()}`);
}
let htmlContent = fs.readFileSync(inputPath, 'utf8');
// Remove <think> tags if present
htmlContent = htmlContent.replace(/<think>[\s\S]*?<\/think>/gi, '');
switch (format.toLowerCase()) {
case 'html':
fs.writeFileSync(outputFile, htmlContent, 'utf8');
break;
case 'pdf':
await this.htmlToPDF(htmlContent, outputFile);
break;
case 'docx':
await this.htmlToDOCX(htmlContent, outputFile);
break;
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) {
const 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' }
});
await browser.close();
},
// HTML → DOCX
async htmlToDOCX(html, outputPath) {
const buffer = await htmlToDocx(html);
fs.writeFileSync(outputPath, buffer);
},
// HTML → TXT (rudimentär)
htmlToTXT(html) {
return html.replace(/<[^>]*>/g, '').replace(/\s+\n/g, '\n').trim();
}
};
module.exports = module_exports;
// CLI usage mit Dialog
if (require.main === module) {
(async () => {
const args = process.argv.slice(2);
if (args.length < 1) {
console.log('Usage: node htmlDocumentConverter.js <input.html> [format]');
console.log('Formats: pdf (default), docx, html, txt');
console.log('');
console.log('Ein nativer "Speichern unter" Dialog wird automatisch geöffnet.');
process.exit(1);
}
const inputPath = args[0];
const format = args[1] || 'pdf';
try {
await module_exports.convert({
inputPath,
format,
showDialog: true
});
} catch (err) {
console.error('Konvertierung fehlgeschlagen:', err.message);
process.exit(1);
}
})();
}
+5 -22
View File
@@ -24,17 +24,11 @@ module.exports = {
outputType: String // Audio file output format
}
*/
let progressBar = new cliProgress.SingleBar({
format: 'Processing |{bar}| {percentage}% | {timemark}',
barCompleteChar: '\u2588',
barIncompleteChar: '\u2591',
hideCursor: true
});
try {
return new Promise((resolve, reject) => {
this.extractAudioFromVideo(parameter.inputVideoPath, progressBar, parameter.outputType)
this.extractAudioFromVideo(parameter.inputVideoPath, parameter.outputType)
.then((resp) => resolve(resp))
.catch((err) => console.error(err));
.catch((err) => {reject(err)});
})
} catch (error) {
console.log(parameter.outputType);
@@ -52,7 +46,7 @@ module.exports = {
* - Shows CLI progress bar
* - Handles errors gracefully (without errors)
*/
extractAudioFromVideo: async function (videoFilePath, progressBar, outputType){
extractAudioFromVideo: async function (videoFilePath, outputType){
let inputVideoName = path.basename(videoFilePath, path.extname(videoFilePath));
let outputAudioPath = path.join(outputDir, `${inputVideoName}.${outputType}`);
@@ -63,28 +57,17 @@ module.exports = {
// .audioCodec('pcm_s16le')
.audioChannels(1)
.audioFrequency(16000)
// .setFfmpegPath("./ffmpeg.exe")
.on('progress', (progress) => {
if (!progressBar.isActive) progressBar.start(100, 0, { timemark: '00:00:00' });
if (progress.percent) {
progressBar.update(progress.percent, { timemark: progress.timemark });
}
})
.on('end', () => {
progressBar.update(100, { timemark: 'done' });
progressBar.stop();
console.log(`Extraction completed: ${outputAudioPath}`);
resolve(outputAudioPath);
})
.on('error', (err) => {
progressBar.stop();
console.error(`failed_audio_extraction on type ${outputType}: ${err.message}`);
// console.error(`failed_audio_extraction on type ${outputType}: ${err.message}`);
reject(err);
})
.save(outputAudioPath);
} catch (error) {
console.log();
// console.log(error);
}
});
}
@@ -21,30 +21,36 @@ module.exports = {
const raw = fs.readFileSync(args.jsonPath, "utf-8");
inputJson = JSON.parse(raw);
} catch (e) {
console.error("Failed to load JSON from file:", e);
return { error: "Could not read JSON from file path." };
// console.error("Failed to load JSON from file:", e);
reject(e)
return
}
}
// JSON parsen
if (typeof args === "string") {
try {
await new Promise((res) => {
await new Promise((res, rej) => {
fs.readFile(args, 'utf8', function (err, data) {
if (err) throw err;
if (err){
rej(err)
return
}
inputJson = JSON.parse(data);
res()
});
})
} catch (e) {
console.log("Invalid JSON in summarize-transcription");
console.log(e)
return { error: "Invalid JSON" };
// console.log("Invalid JSON in summarize-transcription");
// console.log(e)
reject(e)
return
}
}
const words = inputJson.words;
if (!Array.isArray(words)) {
return { error: "No words Array found" };
reject("No words Array found")
return
}
const ENDINGS = [".", "!", "?"]; // '...' auch als Satzende ?
@@ -136,11 +142,11 @@ module.exports = {
const txtPath = path.join(outputDir, "transcription_result.txt");
fs.writeFileSync(txtPath, output.join("\n"), "utf-8");
console.log(`Summary successfully saved:\n- ${jsonPath}\n- ${txtPath}`);
// console.log(`Summary successfully saved:\n- ${jsonPath}\n- ${txtPath}`);
resolve(jsonPath);
} catch (err) {
console.error("Error saving Summary:", err);
// console.error("Error saving Summary:", err);
reject(err);
}
})
@@ -32,29 +32,35 @@ module.exports = {
inputJson = JSON.parse(raw);
} catch (e) {
console.error("Failed to load JSON from file:", e);
return { error: "Could not read JSON from file path." };
reject("Could not read JSON from file path.")
return
}
}
// JSON parsen
if (typeof args === "string") {
try {
await new Promise((res) => {
await new Promise((res, rej) => {
fs.readFile(args, 'utf8', function (err, data) {
if (err) throw err;
if (err){
rej(err)
return
}
inputJson = JSON.parse(data);
res()
});
})
} catch (e) {
console.log("Invalid JSON in summarize-transcription");
console.log(e)
return { error: "Invalid JSON" };
// console.log("Invalid JSON in summarize-transcription");
// console.log(e)
reject(e)
return
}
}
const words = inputJson.words;
if (!Array.isArray(words)) {
return { error: "No words Array found" };
reject("No words Array found")
return;
}
const ENDINGS = [".", "!", "?"]; // '...' auch als Satzende ?
@@ -132,10 +138,10 @@ module.exports = {
const txtPath = path.join(outputDir, `${filename}-${new Date().getTime()}.txt`);
fs.writeFileSync(txtPath, output.join("\n"), "utf-8");
console.log(`Summary successfully saved:\n- ${jsonPath}\n- ${txtPath}`);
// console.log(`Summary successfully saved:\n- ${jsonPath}\n- ${txtPath}`);
resolve(jsonPath);
} catch (err) {
console.error("Error saving Summary:", err);
// console.error("Error saving Summary:", err);
reject(err);
}
})
+128 -5
View File
@@ -1,8 +1,131 @@
module.exports = {
name:"chatgpt", // Unique name for our function that will later be used to get the function from the map via "mapFunctions.get("example").function()"
type:"llm", // value used to differentiate each module to order them in the UI
displayname:"ChatGPT", // The displayname used within the UI
const fs = require('fs');
const path = require('path');
const outputDir = path.join(__dirname, "../../../storage/documents"); // path for output directory
if (!fs.existsSync(outputDir)) {
fs.mkdirSync(outputDir, { recursive: true }); // Create output directory if it doesn't exist
}
// Ensure SAIA API key is set in environment variables: export SAIA_API_KEY="your_api_key_here"
const SAIA_API_KEY = process.env.SAIA_API_KEY; // Ensure SAIA API key is set in environment variables
const SAIA_URL = "https://chat-ai.academiccloud.de/v1/chat/completions"; //URL for the REST call, used model and action
const module_exports = {
name: "llm-saia_openai_gpt",
type: "llm",
displayname: "GPT 120B",
description: "Generates documents using OpenAI GPT OSS 120B via SAIA platform",
async function(parameter) {
// TODO add code to actually send the transcript to ChatGPT and get a response back
try {
console.log("SAIA OpenAI GPT module invoked with parameters:", parameter);
await this.createDocumentFromTranscript( //Call the function to create document with transcript, document type and language
parameter.inputTranscriptPath, // Path to input transcript file
parameter.documentTypePath, // Path to document type file which is chosen in the front end by the user
parameter.language // Language for the document which is chosen in the front end by the user
);
} catch (error) {
console.error("Error in SAIA OpenAI GPT module:", error);
}
},
createDocumentFromTranscript: async function(transcriptPath, documentTypePath, language = "en") { // default language is English
try {
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 promptText = `${documentType}, in language ${language}, transcript:\n\n${transcript}`; //combine doc type, language and transcript - Change prompt here if needed
// --- REST CALL ---
const response = await fetch(SAIA_URL, {
method: "POST",
headers: {
"Authorization": `Bearer ${SAIA_API_KEY}`,
"Accept": "application/json",
"Content-Type": "application/json"
},
body: JSON.stringify({
model: "openai-gpt-oss-120b",
messages: [
{ role: "system", content: "You are a helpful assistant that generates HTML documents from transcripts. Output only valid HTML content without any preamble, explanations, or markdown formatting." },
{ role: "user", content: promptText }
],
temperature: 0
})
});
if (!response.ok) { //ok is true when a response was successful
const text = await response.text();
throw new Error(`SAIA API error (${response.status}): ${text}`);
}
const data = await response.json();
// Get generated text from response or default to empty string (if null)
// SAIA uses OpenAI-compatible structure: data.choices[x].message.content
const output = data.choices?.[0]?.message?.content || "";
let inputTranscriptName = path.basename(transcriptPath, path.extname(transcriptPath)); // Name for the output file
console.log(inputTranscriptName);
const outPath = path.join(outputDir, `${inputTranscriptName}.html`); // Output file path & name to make naming dynamic. Pulled from input transcript name
fs.writeFileSync(outPath, output, "utf8"); // Write output to file
console.log("Generated document written to:", outPath);
} catch (error) {
console.error("Error generating SAIA content:", error);
}
}
};
module.exports = module_exports;
// CLI Mode: Allow direct execution
if (require.main === module) {
(async () => {
const args = process.argv.slice(2);
if (args.length < 2) {
console.error("Usage: node llm-openai-gpt.js <transcriptPath> <documentTypePath> [language]");
console.error("Example: node llm-openai-gpt.js ./transcript.json ./docType.txt de");
process.exit(1);
}
const [transcriptPath, documentTypePath, language] = args;
// Check if API key is set
if (!SAIA_API_KEY) {
console.error("ERROR: SAIA_API_KEY environment variable is not set!");
console.error("Please set it with: export SAIA_API_KEY='your_api_key_here'");
process.exit(1);
}
// Check if files exist
if (!fs.existsSync(transcriptPath)) {
console.error(`ERROR: Transcript file not found: ${transcriptPath}`);
process.exit(1);
}
if (!fs.existsSync(documentTypePath)) {
console.error(`ERROR: Document type file not found: ${documentTypePath}`);
process.exit(1);
}
console.log("Starting document generation...");
console.log(`Transcript: ${transcriptPath}`);
console.log(`Document Type: ${documentTypePath}`);
console.log(`Language: ${language || 'en (default)'}`);
await module_exports.createDocumentFromTranscript(
transcriptPath,
documentTypePath,
language || 'en'
);
console.log("Done!");
})();
}
+98 -19
View File
@@ -1,55 +1,134 @@
const fs = require('fs');
const path = require('path');
const outputDir = path.join(__dirname, "../../../storage/documents"); // path for output directory
if (!fs.existsSync(outputDir)) {
fs.mkdirSync(outputDir, { recursive: true }); // Create output directory if it doesn't exist
}
const ai = new genai.GoogleGenAI({
apiKey: process.env.GOOGLE_API_KEY // Ensure Google API key is set in environment variables: export GOOGLE_API_KEY="your_api_key_here"
});
// Ensure Google API key is set in environment variables: export GOOGLE_API_KEY="your_api_key_here"
const GEMINI_API_KEY = process.env.GOOGLE_API_KEY; // Ensure Google API key is set in environment variables: export GOOGLE_API_KEY="your_api_key_here"
const GEMINI_URL = "https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:generateContent"; // URL for the REST call, used model and action
module.exports = {
const module_exports = {
name: "llm-gemini",
type: "llm",
displayname: "Gemini LLM",
description: "Generates documents using Google Gemini LLM",
async function(parameter) {
return new Promise(async (resolve, reject) => {
try {
console.log("Gemini LLM module invoked with parameters:", parameter);
// console.log("Gemini LLM module invoked with parameters:", parameter);
await this.createDocumentFromTranscript( //Call the function to create document with transcript, document type and language
resolve(await this.createDocumentFromTranscript( //Call the function to create document with transcript, document type and language
parameter.inputTranscriptPath, // Path to input transcript file
parameter.documentTypePath, // Path to document type file which is chosen in the front end by the user
parameter.language // Language for the document which is chosen in the front end by the user
);
));
} catch (error) {
console.error("Error in Gemini LLM module:", error);
// console.error("Error in Gemini LLM module:", error);
reject(error)
}
})
},
createDocumentFromTranscript: async function(transcriptPath, documentTypePath, language = "en") { // default language is English
return new Promise(async(resolve, reject) => {
try {
const transcript = await fs.promises.readFile(transcriptPath, "utf-8");
const documentType = await fs.promises.readFile(documentTypePath, "utf-8");
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 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}`;
const response = await ai.models.generateContent({
model: "gemini-2.5-flash", // Specify the Gemini model to use
contents: promptText // Input prompt for content generation
// --- REST CALL ---
const response = await fetch(`${GEMINI_URL}?key=${GEMINI_API_KEY}`, { //safe model response in variable
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({
contents: [
{
parts: [
{ text: promptText } // Input prompt for content generation
]
}
]
})
});
const output = response.text || ""; // Get generated text from response or default to empty string (if null)
if (!response.ok) { //ok is true when a responce was successfull
const text = await response.text();
throw new Error(`Gemini API error (${response.status}): ${text}`);
}
const outPath = path.join(outputDir, "test.md"); // Output file path & name TO BE DONE to make dynamic out of input transcript name
const data = await response.json();
// Get generated text from response or default to empty string (if null)
const output = data?.candidates?.[0]?.content?.parts?.[0]?.text || "";
let inputTranscriptName = path.basename(transcriptPath, path.extname(transcriptPath)); // Name for the output file
// console.log(inputTranscriptName);
const outPath = path.join(outputDir, `${inputTranscriptName}.md`); // Output file path & name to make naming dynamic. Pulled from input transcript name
fs.writeFileSync(outPath, output, "utf8"); // Write output to file
console.log("Generated document written to:", outPath);
// console.log("Generated document written to:", outPath);
resolve(outPath)
} catch (error) {
console.error("Error generating Gemini content:", error);
// console.error("Error generating Gemini content:", error);
reject(error)
}
})
}
};
module.exports = module_exports;
// CLI Mode: Allow direct execution
if (require.main === module) {
(async () => {
const args = process.argv.slice(2);
if (args.length < 2) {
console.error("Usage: node llm-gemini.js <transcriptPath> <documentTypePath> [language]");
console.error("Example: node llm-gemini.js ./transcript.json ./docType.txt de");
process.exit(1);
}
const [transcriptPath, documentTypePath, language] = args;
// Check if API key is set
if (!GEMINI_API_KEY) {
console.error("ERROR: GOOGLE_API_KEY environment variable is not set!");
console.error("Please set it with: export GOOGLE_API_KEY='your_api_key_here'");
process.exit(1);
}
// Check if files exist
if (!fs.existsSync(transcriptPath)) {
console.error(`ERROR: Transcript file not found: ${transcriptPath}`);
process.exit(1);
}
if (!fs.existsSync(documentTypePath)) {
console.error(`ERROR: Document type file not found: ${documentTypePath}`);
process.exit(1);
}
console.log("Starting document generation...");
console.log(`Transcript: ${transcriptPath}`);
console.log(`Document Type: ${documentTypePath}`);
console.log(`Language: ${language || 'en (default)'}`);
await module_exports.createDocumentFromTranscript(
transcriptPath,
documentTypePath,
language || 'en'
);
console.log("Done!");
})();
}
+130
View File
@@ -0,0 +1,130 @@
const fs = require('fs');
const path = require('path');
const outputDir = path.join(__dirname, "../../../storage/documents"); // path for output directory
if (!fs.existsSync(outputDir)) {
fs.mkdirSync(outputDir, { recursive: true }); // Create output directory if it doesn't exist
}
// Ensure SAIA API key is set in environment variables: export SAIA_API_KEY="your_api_key_here"
const SAIA_API_KEY = process.env.SAIA_API_KEY;
const SAIA_URL = "https://chat-ai.academiccloud.de/v1/chat/completions"; // URL for the REST call, used model and action
const module_exports = {
name: "qwen3-235b-a22b",
type: "llm",
displayname: "QWEN 3 235B",
description: "Generates documents using QWEN 3 235B via SAIA platform",
async function(parameter) {
try {
console.log("SAIA QWEN 3 235B module invoked with parameters:", parameter);
await this.createDocumentFromTranscript( // Call the function to create document with transcript, document type and language
parameter.inputTranscriptPath, // Path to input transcript file
parameter.documentTypePath, // Path to document type file which is chosen in the front end by the user
parameter.language // Language for the document which is chosen in the front end by the user
);
} catch (error) {
console.error("Error in SAIA QWEN 3 235B module:", error);
}
},
createDocumentFromTranscript: async function(transcriptPath, documentTypePath, language = "en") { // default language is English
try {
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 promptText = `${documentType}, in language ${language}, transcript:\n\n${transcript}`; // combine doc type, language and transcript - Change prompt here if needed
// --- REST CALL ---
const response = await fetch(SAIA_URL, {
method: "POST",
headers: {
"Authorization": `Bearer ${SAIA_API_KEY}`,
"Accept": "application/json",
"Content-Type": "application/json"
},
body: JSON.stringify({
model: "qwen3-235b-a22b",
messages: [
{ role: "system", content: "You are a helpful assistant that generates HTML documents from transcripts. Output only valid HTML content without any preamble, explanations, or markdown formatting." },
{ role: "user", content: promptText }
],
temperature: 0
})
});
if (!response.ok) { // ok is true when a response was successful
const text = await response.text();
throw new Error(`SAIA API error (${response.status}): ${text}`);
}
const data = await response.json();
// Get generated text from response or default to empty string (if null)
// SAIA uses OpenAI-compatible structure: data.choices[x].message.content
const output = data.choices?.[0]?.message?.content || "";
let inputTranscriptName = path.basename(transcriptPath, path.extname(transcriptPath)); // Name for the output file
console.log(inputTranscriptName);
const outPath = path.join(outputDir, `${inputTranscriptName}.html`); // Output file path & name to make naming dynamic. Pulled from input transcript name
fs.writeFileSync(outPath, output, "utf8"); // Write output to file
console.log("Generated document written to:", outPath);
} catch (error) {
console.error("Error generating SAIA content:", error);
}
}
};
module.exports = module_exports;
// CLI Mode: Allow direct execution
if (require.main === module) {
(async () => {
const args = process.argv.slice(2);
if (args.length < 2) {
console.error("Usage: node qwen3.js <transcriptPath> <documentTypePath> [language]");
console.error("Example: node qwen3.js ./transcript.json ./docType.txt de");
process.exit(1);
}
const [transcriptPath, documentTypePath, language] = args;
// Check if API key is set
if (!SAIA_API_KEY) {
console.error("ERROR: SAIA_API_KEY environment variable is not set!");
console.error("Please set it with: export SAIA_API_KEY='your_api_key_here'");
process.exit(1);
}
// Check if files exist
if (!fs.existsSync(transcriptPath)) {
console.error(`ERROR: Transcript file not found: ${transcriptPath}`);
process.exit(1);
}
if (!fs.existsSync(documentTypePath)) {
console.error(`ERROR: Document type file not found: ${documentTypePath}`);
process.exit(1);
}
console.log("Starting document generation...");
console.log(`Transcript: ${transcriptPath}`);
console.log(`Document Type: ${documentTypePath}`);
console.log(`Language: ${language || 'en (default)'}`);
await module_exports.createDocumentFromTranscript(
transcriptPath,
documentTypePath,
language || 'en'
);
console.log("Done!");
})();
}
Submodule services/modules/transcription-local/whisper.cpp added at 999a7e0cbf
@@ -78,7 +78,7 @@ function saveTranscript(transcript, sessionId) {
const outputPath = path.join(outputDir, `${sessionId}.json`);
fs.writeFileSync(outputPath, JSON.stringify(transcript, null, 2));
console.log(`Transcript saved: ${outputPath}`);
// console.log(`Transcript saved: ${outputPath}`);
return outputPath;
}
@@ -89,6 +89,7 @@ module.exports = {
name: 'assembly',
type: 'transcription',
displayname: 'AssemblyAI',
audioformat: "mp3",
async function(audioFileName) {
return new Promise(async (resolve, reject) => {
@@ -115,8 +116,9 @@ module.exports = {
resolve(saveTranscript(transcript, sessionId));
} catch (error) {
console.error('Transcription error:', error.message);
// console.error('Transcription error:', error.message);
reject(error);
return
}
})
}
+1
View File
@@ -1,3 +1,4 @@
module.exports = {
name:"Startup_function",
async function(){
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
@@ -0,0 +1,217 @@
Generate a structured meeting report in HTML using STRUCTURE and STYLE.
Output ONLY the final .md document — no meta comments, no explanations.
Follow exactly the STRUCTURE defined below.
Follow exactly the STYLE rules.
Use timestamps in HH:MM:SS format.
If information is missing, use: Unclear:<reason>.
==================== STRUCTURE & RULES ====================
{
"FORMAT": "HTML",
"STRUCTURE": {
"titlepage": [
"title",
"date",
"start",
"end",
"duration",
"location",
"host",
"participants"
],
"toc": "[section](#anchor) — HH:MM:SS",
"section": {
"h2": "<topic> — HH:MM:SS",
"summary": "exactly 1 concise sentence",
"key_points": "maximum 5 bullet points; quotes optional",
"decisions": "list items formatted as: decision text | owner | due date",
"actions": "HTML table: id | task | owner | due | status"
},
"exec_summary": "exactly 3 short sentences",
"consolidated": [
"decisions",
"actions"
],
"appendix": "optional"
},
"STYLE": {
"tone": "neutral, concise, professional",
"ts_format": "HH:MM:SS",
"no_meta": true
},
"PROCESS": {
"timestamps": "use transcript timestamps if present; otherwise estimate minimal",
"speakers": "use names if available; else Speaker X",
"long_transcripts": "split → summarize → merge",
"unclear": "Unclear:<reason>"
},
"PROMPT_SNIPPET": "Generate meeting report in HTML using STRUCTURE and STYLE. Output only the report."
}
============================================================
Insert all generated content into the following HTML TEMPLATE:
# {{title}}
**Date:** {{date}}
**Start:** {{start}}
**End:** {{end}}
**Duration:** {{duration}}
**Location:** {{location}}
**Host:** {{host}}
**Participants:** {{participants}}
---
## Table of Contents
{{toc}}
---
Generate a structured meeting report in HTML using STRUCTURE and STYLE.
Output ONLY the final .md document — no meta comments, no explanations.
Follow exactly the STRUCTURE defined below.
Follow exactly the STYLE rules.
Use timestamps in HH:MM:SS format.
If information is missing, use: UNKLAR:<reason>.
==================== STRUCTURE & RULES ====================
{
"FORMAT": "HTML",
"STRUCTURE": {
"titlepage": [
"title",
"date",
"start",
"end",
"duration",
"location",
"host",
"participants"
],
"toc": "[section](#anchor) — HH:MM:SS",
"section": {
"h2": "<topic> — HH:MM:SS",
"summary": "exactly 1 concise sentence",
"key_points": "maximum 5 bullet points; quotes optional",
"decisions": "list items formatted as: decision text | owner | due date",
"actions": "HTML table: id | task | owner | due | status"
},
"exec_summary": "exactly 3 short sentences",
"consolidated": [
"decisions",
"actions"
],
"appendix": "optional"
},
"STYLE": {
"tone": "neutral, concise, professional",
"ts_format": "HH:MM:SS",
"no_meta": true
},
"PROCESS": {
"timestamps": "use transcript timestamps if present; otherwise estimate minimal",
"speakers": "use names if available; else Speaker X",
"long_transcripts": "split → summarize → merge",
"unclear": "UNKLAR:<reason>"
},
"PROMPT_SNIPPET": "Generate meeting report in HTML using STRUCTURE and STYLE. Output only the report."
}
============================================================
Insert all generated content into the following HTML TEMPLATE:
# {{title}}
**Date:** {{date}}
**Start:** {{start}}
**End:** {{end}}
**Duration:** {{duration}}
**Location:** {{location}}
**Host:** {{host}}
**Participants:** {{participants}}
---
## Table of Contents
{{toc}}
---
## Executive Summary
{{exec_summary}}
---
## Sections
{{sections}}
---
## Consolidated Decisions
{{consolidated_decisions}}
---
## Consolidated Actions
{{consolidated_actions}}
---
## Appendix
{{appendix}}
============================================================
Final Requirement:
Output ONLY the completed HTML meeting report.
## Executive Summary
{{exec_summary}}
---
## Sections
{{sections}}
---
## Consolidated Decisions
{{consolidated_decisions}}
---
## Consolidated Actions
{{consolidated_actions}}
---
## Appendix
{{appendix}}
============================================================
Final Requirement:
Output ONLY the completed HTML meeting report.
-8
View File
@@ -1,8 +0,0 @@
from fastapi.testclient import TestClient
from app.main import app
client = TestClient(app)
def test_health():
response = client.get("/health")
assert response.status_code == 200
+170
View File
@@ -0,0 +1,170 @@
// DO NOT TOUCH THIS
require("../../requires.js")
mapFunctions = new Map()
// Loading the Function Map
var path = `${mainDir}/services/modules`
var folders = fs.readdirSync(path).filter(function (file) {
return fs.statSync(path+'/'+file).isDirectory();
});
folders.forEach(element => {
var commandFiles = fs.readdirSync(`${path}/${element}`).filter(file => file.endsWith('.js') && !file.startsWith("index"));
for (const file of commandFiles) {
delete require.cache[require.resolve(`${path}/${element}/${file}`)];
const command = require(`${path}/${element}/${file}`);
mapFunctions.set(command.name, command);
}
});
// You can touch beyond this point
let audiopath
let transcriptPath
let summarizePath
let llmpath
describe("Unit Tests", function() {
describe('Audio Extraction', function () {
this.slow(1000)
this.timeout(3000)
it('Extract .mp4 to .mp3', function (done) {
mapFunctions.get("extraction-video-to-audio").function({inputVideoPath: __dirname.replaceAll("\\","/")+"/testvideo.mp4", outputType: "mp3"}).then(resp => {
audiopath = resp
// console.log(resp);
done()
}).catch(err => {
throw err;
})
})
it('Extract .mp4 to .flac', function (done) {
mapFunctions.get("extraction-video-to-audio").function({inputVideoPath: __dirname.replaceAll("\\","/")+"/testvideo.mp4", outputType: "flac"}).then(resp => {
// console.log(resp);
done()
}).catch(err => {
throw err;
})
})
it('Extracting to a nonexistant format', function (done) {
mapFunctions.get("extraction-video-to-audio").function({inputVideoPath: __dirname.replaceAll("\\","/")+"/testvideo.mp4", outputType: "qqq"}).then(resp => {
// console.log(resp);
done("Didnt crash")
}).catch(err => {
done()
})
})
it('Extracting from nonexistant file', function (done) {
mapFunctions.get("extraction-video-to-audio").function({inputVideoPath: "a", outputType: "mp3"}).then(resp => {
// console.log(resp);
done("Didnt crash")
}).catch(err => {
done()
})
})
it('Extracting from nonexistant file to nonexistant format', function (done) {
mapFunctions.get("extraction-video-to-audio").function({inputVideoPath: "a", outputType: "qqq"}).then(resp => {
// console.log(resp);
done("Didnt crash")
}).catch(err => {
done()
})
})
});
describe("Audio Transcription", function() {
this.slow(20000)
this.timeout(120000)
it('Assembly', function (done) {
mapFunctions.get("assembly").function(audiopath).then(resp => {
// console.log(resp);
transcriptPath = resp
done()
}).catch(err => {
throw err
})
})
it('Assembly Wrong file', function (done) {
mapFunctions.get("assembly").function("a").then(resp => {
// console.log(resp);
// transcriptPath = resp
done("Didnt crash")
}).catch(err => {
// console.log(err);
done()
})
})
// TODO add more Transcription Tool tests here
})
describe("Transcript Summarizer", function() {
this.slow(100)
this.timeout(1000)
it("Summarizer 1", function (done){
mapFunctions.get("summarize-transcription").function(transcriptPath).then(resp => {
done()
}).catch(err => {
throw err
})
})
it("Summarizer 1 Wrong File", function (done){
mapFunctions.get("summarize-transcription").function("a").then(resp => {
done("Didnt crash")
}).catch(err => {
done()
})
})
it("Summarizer 2 (Main)", function (done){
mapFunctions.get("summarize-transcription2").function(transcriptPath).then(resp => {
summarizePath = resp
done()
}).catch(err => {
throw err
})
})
it("Summarizer 2 (Main) Wrong File", function (done){
mapFunctions.get("summarize-transcription2").function("a").then(resp => {
done("Didnt crash")
}).catch(err => {
done()
})
})
})
describe("Large Language Model", function() {
this.slow(30000)
this.timeout(120000)
// it("ChatGPT", function (done){
// mapFunctions.get("chatgpt").function({inputTranscriptPath: summarizePath, documentTypePath: "./storage/documentType/meetingReport.json", language: "en"}).then(resp => {
// done()
// }).catch(err => {
// throw err
// })
// })
it("Gemini", function (done){
mapFunctions.get("llm-gemini").function({inputTranscriptPath: summarizePath, documentTypePath: "./storage/documentType/meetingReport.json", language: "en"}).then(resp => {
llmpath = resp
done()
}).catch(err => {
throw err
})
})
})
after(function() {
console.log(`\n\n\n${audiopath} \n${transcriptPath} \n${summarizePath} \n${llmpath}`);
})
})
Binary file not shown.