Compare commits

...

18 Commits

Author SHA1 Message Date
Spanier, Pit 6cff6b9981 Merge branch 'develop' into 'feature/38-sprecher-audio-snippets-s4-11'
# Conflicts:
#   main.js
2025-12-16 16:18:17 +01:00
santa 0003d99041 extract speaker snippets mit main verknüpft. 2025-12-16 15:32:31 +01:00
Hughes, Mike 6a94f88e86 Merge branch 'feature/meeting_document_types' into 'develop'
Added Meeting Document Forms

See merge request proj-wise2526-video2document/video2document!55
2025-12-16 14:43:54 +01:00
MikeHughes-BIN 30f73f7bb7 Added Meeting Document Forms 2025-12-16 14:24:13 +01:00
Hughes, Mike 74439d680e Merge branch 'fix/download-button' into 'develop'
oopsies missed a </div>

See merge request proj-wise2526-video2document/video2document!53
2025-12-15 18:49:19 +01:00
MikeHughes-BIN 1b76b2e96d oopsies missed a </div> 2025-12-15 18:47:41 +01:00
Hughes, Mike 363ba2d1b5 Merge branch 'fix/download-button' into 'develop'
Enable download button functionality and improve error handling in file download

See merge request proj-wise2526-video2document/video2document!52
2025-12-15 18:43:50 +01:00
MikeHughes-BIN 6aa62ed534 Enable download button functionality and improve error handling in file download 2025-12-15 18:41:14 +01:00
Hughes, Mike cd474d7101 Merge branch 'feature/ui-test' into 'develop'
Removed a line which caused an error

See merge request proj-wise2526-video2document/video2document!51
2025-12-15 18:11:01 +01:00
eric.minning bd47a194c7 Removed a line which caused an error 2025-12-15 18:09:50 +01:00
Spanier, Pit ef080063a8 Merge branch 'feature/export-function-integration' into 'develop'
Changes to the LLMs to return a Promise (outp path) and main now calls the export process

See merge request proj-wise2526-video2document/video2document!50
2025-12-15 18:05:20 +01:00
Hughes, Mike c2c1aa1b17 Merge branch 'fix/gui-width' into 'develop'
Increased window witdth from 800px to 1200px

See merge request proj-wise2526-video2document/video2document!47
2025-12-15 17:59:12 +01:00
Hughes, Mike ee3bcdcd05 Merge branch 'feature/ui-test' into 'develop'
Implemented the function for the download button.

See merge request proj-wise2526-video2document/video2document!49
2025-12-15 17:58:33 +01:00
eric.minning bac6e2b7f0 Implemented the function for the download button. 2025-12-15 17:21:02 +01:00
Spanier, Pit 9760704883 Merge branch 'feature/ui-test' into 'develop'
Feature/ui test

See merge request proj-wise2526-video2document/video2document!48
2025-12-15 17:09:06 +01:00
eric.minning aee1428cb6 Changed location of the output type value in the response package 2025-12-15 17:07:07 +01:00
eric.minning 7494e13c8c Fixed an a wrong call of the speakerAudios function. 2025-12-15 17:01:04 +01:00
MikeHughes-BIN 2998799826 Window width for the GUI to fit the Arrows 2025-12-15 16:42:44 +01:00
15 changed files with 277 additions and 243 deletions
+1 -6
View File
@@ -147,12 +147,7 @@
</div> </div>
<div class="step" id="step6" style="display:none;"> <div class="step" id="step6" style="display:none;">
<button class="download-btn" id="downloadButton" onclick="" disabled>Download</button> <button class="download-btn" id="downloadButton" onclick="fileDownload()">Download</button>
<div class="progressbar" id="progressbar">
<div class="progress_fill"></div>
<span class="progress_text">0%</span>
</div>
</div> </div>
</div> </div>
+5
View File
@@ -26,6 +26,11 @@ try {
submitSpeaker: (speaker_names) => {ipcRenderer.send("speaker_submit", speaker_names)} submitSpeaker: (speaker_names) => {ipcRenderer.send("speaker_submit", speaker_names)}
}) })
contextBridge.exposeInMainWorld("download", {
file_download: () => {ipcRenderer.send("file_download")}
})
ipcRenderer.on("error", (event, err) => {alert(err)}) ipcRenderer.on("error", (event, err) => {alert(err)})
} catch (error) { } catch (error) {
console.log("Error in preload.js"); console.log("Error in preload.js");
+12 -17
View File
@@ -33,7 +33,6 @@ uploadContainer.addEventListener("drop", (e) => {
window.addEventListener('load', async (e) => { window.addEventListener('load', async (e) => {
try { try {
console.log("test");
loadLanguageOptions(); loadLanguageOptions();
const value = await window.onStartup.getModuleNames(); const value = await window.onStartup.getModuleNames();
loadAiOptions(value.ai_modules); loadAiOptions(value.ai_modules);
@@ -117,7 +116,7 @@ docFormat.addEventListener("change", (e) =>{
} catch (error) { } catch (error) {
} }
}) });
docFormatSummary1.addEventListener("change", (e) =>{ docFormatSummary1.addEventListener("change", (e) =>{
try { try {
if(docFormatSummary1.checked){ if(docFormatSummary1.checked){
@@ -129,7 +128,7 @@ docFormatSummary1.addEventListener("change", (e) =>{
} catch (error) { } catch (error) {
} }
}) });
docFormatSummary2.addEventListener("change", (e) =>{ docFormatSummary2.addEventListener("change", (e) =>{
try { try {
if(docFormatSummary2.checked){ if(docFormatSummary2.checked){
@@ -141,7 +140,7 @@ docFormatSummary2.addEventListener("change", (e) =>{
} catch (error) { } catch (error) {
} }
}) });
docFormatSummary3.addEventListener("change", (e) =>{ docFormatSummary3.addEventListener("change", (e) =>{
try { try {
if(docFormatSummary3.checked){ if(docFormatSummary3.checked){
@@ -153,7 +152,7 @@ docFormatSummary3.addEventListener("change", (e) =>{
} catch (error) { } catch (error) {
} }
}) });
docFormatCustom.addEventListener("change", (e) =>{ docFormatCustom.addEventListener("change", (e) =>{
try { try {
if(docFormatCustom.checked){ if(docFormatCustom.checked){
@@ -165,7 +164,7 @@ docFormatCustom.addEventListener("change", (e) =>{
} catch (error) { } catch (error) {
} }
}) });
//Speaker change listener //Speaker change listener
cur_speaker.addEventListener("change", (e) =>{ cur_speaker.addEventListener("change", (e) =>{
@@ -174,16 +173,12 @@ cur_speaker.addEventListener("change", (e) =>{
} catch (error) { } catch (error) {
} }
}) });
window.electron.speakerAudios((event, arg) => { window.audios.speakerAudios((event, arg) => {
try {
setSpeakerAudiosValue(arg); setSpeakerAudiosValue(arg);
loadSpeakerOptions(arg); loadSpeakerOptions(arg);
} catch (error) { });
}
})
window.electron.progress((event, arg) => { window.electron.progress((event, arg) => {
if(arg.curstep == 1){ if(arg.curstep == 1){
@@ -207,7 +202,7 @@ function setCircleOne(){
} catch (error) { } catch (error) {
} }
} };
function setCircleZwo(){ function setCircleZwo(){
try { try {
if(document.getElementById("box2").style.backgroundColor == "green"){ if(document.getElementById("box2").style.backgroundColor == "green"){
@@ -219,7 +214,7 @@ function setCircleZwo(){
} }
} };
function setCircleThree(){ function setCircleThree(){
try { try {
if(document.getElementById("box3").style.backgroundColor == "green"){ if(document.getElementById("box3").style.backgroundColor == "green"){
@@ -231,7 +226,7 @@ function setCircleThree(){
} }
} };
function setCircleFour(){ function setCircleFour(){
try { try {
if(document.getElementById("box4").style.backgroundColor == "green"){ if(document.getElementById("box4").style.backgroundColor == "green"){
@@ -242,7 +237,7 @@ function setCircleFour(){
} catch (error) { } catch (error) {
} }
} };
+10 -5
View File
@@ -25,7 +25,6 @@ function checkBoxes() {
var pathTest = window.electronAPI.getFilePath(videoUpload.files[0]); var pathTest = window.electronAPI.getFilePath(videoUpload.files[0]);
var pathToLower = pathTest.toLowerCase(); var pathToLower = pathTest.toLowerCase();
if(testEndings.some(e => pathToLower.endsWith(e))){ if(testEndings.some(e => pathToLower.endsWith(e))){
document.getElementById("progressbar").style.visibility = "visible";
//assembly of the json for the main //assembly of the json for the main
const selectedStyles = [checkedCounter]; const selectedStyles = [checkedCounter];
@@ -49,15 +48,15 @@ function checkBoxes() {
const sendingPackage = { const sendingPackage = {
"video": { "video": {
"module":"extraction-video-to-audio", "module":"extraction-video-to-audio",
"inputVideoPath": pathTest, "inputVideoPath": pathTest
"outputType": outputType.value
}, },
"transcription": { "transcription": {
"module": transcriptionType.value "module": transcriptionType.value
}, },
"document": { "document": {
"module":aiType.value, "module":aiType.value,
"styles": selectedStyles "styles": selectedStyles,
"outputType": outputType.value
} }
}; };
window.submit.submit(sendingPackage) window.submit.submit(sendingPackage)
@@ -105,7 +104,6 @@ function changeLanguage(language) {
function handleFiles(files) { function handleFiles(files) {
try { try {
if (files.length > 0) { if (files.length > 0) {
document.getElementById("progressbar").style.visibility = "visible";
const file = files[0]; const file = files[0];
if (file.type.startsWith('video/')) { if (file.type.startsWith('video/')) {
const filePath = window.explorer.onFileDrop(files[0]) const filePath = window.explorer.onFileDrop(files[0])
@@ -330,3 +328,10 @@ function sendSpeakerPackages(){
} }
} }
function fileDownload() {
try {
window.download.file_download();
} catch (error) {
console.error("Download failed:", error);
}
}
+7 -2
View File
@@ -61,7 +61,7 @@ let mainWindow;
function createWindow() { function createWindow() {
mainWindow = new electron.BrowserWindow({ mainWindow = new electron.BrowserWindow({
width: 800, width: 1200,
height: 800, height: 800,
webPreferences: { webPreferences: {
nodeIntegration: false, nodeIntegration: false,
@@ -198,6 +198,11 @@ electron.ipcMain.on("file_submit", async (event, args) => {
}) })
} }
await mapFunctions.get("extract-speaker-snippets").function({audioPath: audiopath, jsonPath: transcriptpath }).then(resp => {
mainWindow.webContents.send("submitSpeaker", resp)
console.log(resp)
})
// TODO actually implement this functionality // TODO actually implement this functionality
// Module to get the first few lines for each speaker to send to the frontend // 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 => { // await mapFunctions.get("speaker-getter-idfk").function(transcriptpath).then(resp => {
@@ -210,7 +215,7 @@ electron.ipcMain.on("file_submit", async (event, args) => {
// // speakerA: {source: "Pfad zur Audio File"}, // // speakerA: {source: "Pfad zur Audio File"},
// // speakerB:..... // // speakerB:.....
// // } // // }
mainWindow.webContents.send("speakers", {speakerA:"pfad1", speakerB:"pfad2"}) // mainWindow.webContents.send("speakers", {speakerA:"pfad1", speakerB:"pfad2"})
// }).catch(err => { // }).catch(err => {
// mainWindow.webContents.send("error", err) // mainWindow.webContents.send("error", err)
// return // return
+5 -4
View File
@@ -15,7 +15,7 @@
"dotenv": "^17.2.3", "dotenv": "^17.2.3",
"electron": "^39.1.1", "electron": "^39.1.1",
"express": "^5.1.0", "express": "^5.1.0",
"ffmpeg-static": "^5.2.0", "ffmpeg-static": "^5.3.0",
"fluent-ffmpeg": "^2.1.3", "fluent-ffmpeg": "^2.1.3",
"html-to-docx": "^1.8.0", "html-to-docx": "^1.8.0",
"mocha": "^11.7.5", "mocha": "^11.7.5",
@@ -1773,9 +1773,9 @@
} }
}, },
"node_modules/ffmpeg-static": { "node_modules/ffmpeg-static": {
"version": "5.2.0", "version": "5.3.0",
"resolved": "https://registry.npmjs.org/ffmpeg-static/-/ffmpeg-static-5.2.0.tgz", "resolved": "https://registry.npmjs.org/ffmpeg-static/-/ffmpeg-static-5.3.0.tgz",
"integrity": "sha512-WrM7kLW+do9HLr+H6tk7LzQ7kPqbAgLjdzNE32+u3Ff11gXt9Kkkd2nusGFrlWMIe+XaA97t+I8JS7sZIrvRgA==", "integrity": "sha512-H+K6sW6TiIX6VGend0KQwthe+kaceeH/luE8dIZyOP35ik7ahYojDuqlTV1bOrtEwl01sy2HFNGQfi5IDJvotg==",
"hasInstallScript": true, "hasInstallScript": true,
"license": "GPL-3.0-or-later", "license": "GPL-3.0-or-later",
"dependencies": { "dependencies": {
@@ -1832,6 +1832,7 @@
"resolved": "https://registry.npmjs.org/fluent-ffmpeg/-/fluent-ffmpeg-2.1.3.tgz", "resolved": "https://registry.npmjs.org/fluent-ffmpeg/-/fluent-ffmpeg-2.1.3.tgz",
"integrity": "sha512-Be3narBNt2s6bsaqP6Jzq91heDgOEaDCJAXcE3qcma/EJBSy5FB4cvO31XBInuAuKBx8Kptf8dkhjK0IOru39Q==", "integrity": "sha512-Be3narBNt2s6bsaqP6Jzq91heDgOEaDCJAXcE3qcma/EJBSy5FB4cvO31XBInuAuKBx8Kptf8dkhjK0IOru39Q==",
"deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.", "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.",
"license": "MIT",
"dependencies": { "dependencies": {
"async": "^0.2.9", "async": "^0.2.9",
"which": "^1.1.1" "which": "^1.1.1"
+1 -1
View File
@@ -6,7 +6,7 @@
"dotenv": "^17.2.3", "dotenv": "^17.2.3",
"electron": "^39.1.1", "electron": "^39.1.1",
"express": "^5.1.0", "express": "^5.1.0",
"ffmpeg-static": "^5.2.0", "ffmpeg-static": "^5.3.0",
"fluent-ffmpeg": "^2.1.3", "fluent-ffmpeg": "^2.1.3",
"html-to-docx": "^1.8.0", "html-to-docx": "^1.8.0",
"mocha": "^11.7.5", "mocha": "^11.7.5",
@@ -0,0 +1,95 @@
const ffmpeg = require("fluent-ffmpeg");
const ffmpegPath = require("ffmpeg-static");
ffmpeg.setFfmpegPath(ffmpegPath);
module.exports = {
name: "extract-speaker-snippets",
type: "audio",
displayname: "Extract Speaker Snippets",
async function(parameter) {
return new Promise(async (resolve, reject) => {
let output = {}
console.log("Extract Speaker Snippets\n");
// Pfade
const AUDIO_PATH = parameter.audioPath; // Gesamt-Audio
const JSON_PATH = parameter.jsonPath; // json summary
const OUTPUT_DIR = path.join(__dirname, "/../../../storage/audio/speakerSnippets");
if (!AUDIO_PATH || !JSON_PATH) {
console.error("no audioPath or jsonPath available");
return;
}
// Output-Ordner
if (!fs.existsSync(OUTPUT_DIR)) {
fs.mkdirSync(OUTPUT_DIR, { recursive: true });
}
// JSON laden
let entries;
try {
entries = JSON.parse(fs.readFileSync(JSON_PATH, "utf8"));
} catch (err) {
console.error("JSON reading failed", err);
return;
}
if (!Array.isArray(entries)) {
console.error("JSON is not an Array");
return;
}
// Pro Speaker genau EINEN Satz merken
const speakerMap = {};
for (const item of entries) {
if (!speakerMap[item.speaker]) {
speakerMap[item.speaker] = item;
}
}
// FFmpeg pro Speaker ausführen (sequenziell)
for (const speaker of Object.keys(speakerMap)) {
const data = speakerMap[speaker];
// ms → Sekunden
const startSec = data.start / 1000;
const durationSec = (data.end - data.start) / 1000;
if (durationSec <= 0) {
console.log(`invalid times for Speaker ${speaker}`);
continue;
}
const outFile = path.join(OUTPUT_DIR, `speaker_${speaker}.wav`);
await new Promise((res, rej) => {
ffmpeg(AUDIO_PATH)
.setStartTime(startSec)
.setDuration(durationSec)
.output(outFile)
.on("end", () => {
output[`speaker${speaker}`] = {src: outFile, name: `speaker${speaker}`}
console.log(`Snippet erstellt: speaker_${speaker}.wav`);
res();
})
.on("error", (err) => {
console.error(`FFmpeg Fehler (${speaker})`, err.message);
rej();
})
.run();
});
}
resolve(output)
console.log("\nAlle Speaker-Snippets erstellt\n");
})
}
};
+1
View File
@@ -10,6 +10,7 @@ module.exports = {
// let transcript = await mapFunctions.get("assembly").function('../../storage/audio/IMG_2978.wav'); // let transcript = await mapFunctions.get("assembly").function('../../storage/audio/IMG_2978.wav');
// let summary = await mapFunctions.get("summarize-transcription").function({jsonPath:'/Users/santa/Proj25/video2document/storage/transcripts/IMG_2978.json'}); // let summary = await mapFunctions.get("summarize-transcription").function({jsonPath:'/Users/santa/Proj25/video2document/storage/transcripts/IMG_2978.json'});
// let snippets = await mapFunctions.get("extract-speaker-snippets").function({audioPath:'/Users/santa/Proj25/video2document/storage/audio/KittyKat.wav', jsonPath1: '/Users/santa/Proj25/video2document/storage/transcriptionSummaries/KittyKat-1765806474958.json' });
BIN
View File
Binary file not shown.
+25
View File
@@ -0,0 +1,25 @@
Du bist ein erfahrener Moderator und Projektmanager.
AUFGABE:
Erstelle eine sinnvolle Meeting-Agenda basierend auf dem folgenden Transkript.
ANFORDERUNGEN:
- Rekonstruiere die tatsächlichen Themenblöcke
- Ordne sie logisch und chronologisch
- Fasse ähnliche Diskussionen zusammen
- Keine irrelevanten Details aufnehmen
STRUKTUR:
- Titel der Agenda
- Ziel des Meetings (12 Sätze)
- Agenda-Punkte (nummeriert)
- Thema
- Kurzbeschreibung
- Ziel des Punktes (Information, Entscheidung, Diskussion)
STIL:
- Klar, kompakt
- Business-orientiert
- Keine Sprecher- oder Zeitangaben
TRANSKRIPT:
+21
View File
@@ -0,0 +1,21 @@
Du bist ein intelligenter Dokumenten-Generator.
AUFGABE:
Erstelle ein individuelles Dokument basierend auf:
1) dem Meeting-Transkript
2) der zusätzlichen Nutzeranweisung
WICHTIG:
- Priorisiere die Nutzeranweisung
- Nutze das Transkript als Wissensquelle
- Struktur, Tonalität und Detailgrad anpassen
- Inhalte logisch zusammenführen
FORMAT:
- Passe Struktur und Stil an den Nutzerwunsch an
- Klare Überschriften
- Keine Sprecher- oder Zeitangaben
TRANSKRIPT & NUTZERANWEISUNG:
+25
View File
@@ -0,0 +1,25 @@
Du bist ein professioneller Protokollführer.
AUFGABE:
Erstelle ein Ergebnisprotokoll basierend auf dem Meeting-Transkript.
FOKUS:
- Ergebnisse statt Diskussionen
- Entscheidungen, Beschlüsse, Vereinbarungen
- Klare, überprüfbare Aussagen
STRUKTUR:
1. Meeting-Informationen
2. Ergebnisse je Thema
- Thema
- Ergebnis / Beschluss
3. Entscheidungen
4. Aufgaben & Verantwortlichkeiten
5. Offene Punkte
REGELN:
- Keine Meinungen oder Spekulationen
- Keine Zeit- oder Sprecherangaben
- Sachlich, formal
TRANSKRIPT:
@@ -0,0 +1,34 @@
Du bist ein erfahrener Scrum Master.
AUFGABE:
Erstelle Sprint Planning Notes aus dem folgenden Meeting-Transkript.
FOKUS:
- Sprint-Ziele
- User Stories / Tasks
- Abhängigkeiten
- Risiken
- Commitments
STRUKTUR:
1. Sprint Overview
- Sprint-Ziel
- Zeitraum (falls erwähnt)
2. Geplante Arbeit
- User Story / Task
- Beschreibung
- Akzeptanzkriterien (falls ableitbar)
3. Abhängigkeiten & Blocker
4. Risiken & Annahmen
5. Vereinbarungen / Team-Commitments
STIL:
- Agile-konform
- Klar & umsetzungsorientiert
- Bullet Points bevorzugen
TRANSKRIPT:
+34 -207
View File
@@ -1,217 +1,44 @@
Generate a structured meeting report in HTML using STRUCTURE and STYLE. Du bist ein professioneller Meeting-Analyst und Business Writer.
Output ONLY the final .md document — no meta comments, no explanations.
Follow exactly the STRUCTURE defined below. AUFGABE:
Follow exactly the STYLE rules. Erstelle einen strukturierten Follow-up Report basierend auf dem folgenden Meeting-Transkript.
Use timestamps in HH:MM:SS format.
If information is missing, use: Unclear:<reason>.
==================== STRUCTURE & RULES ==================== ANFORDERUNGEN:
- Fasse Inhalte sinngemäß zusammen
- Entferne Redundanzen und Smalltalk
- Formuliere klar, präzise und professionell
- Verwende neutrale Business-Sprache
- Keine Zeitstempel oder Sprecher-Namen zitieren
- Leite Entscheidungen und Aufgaben logisch ab, wenn sie implizit sind
- Markiere offene Punkte klar
{ STRUKTUR DES DOKUMENTS:
"FORMAT": "HTML", 1. Titel & Metadaten
- Meetingtitel (ableiten)
- Datum (falls im Transkript erwähnt, sonst „nicht angegeben“)
- Teilnehmer (zusammengefasst)
"STRUCTURE": { 2. Executive Summary (max. 5 Bullet Points)
"titlepage": [
"title",
"date",
"start",
"end",
"duration",
"location",
"host",
"participants"
],
"toc": "[section](#anchor) — HH:MM:SS", 3. Besprochene Themen
- Thema
- Kernaussagen
- Relevante Erkenntnisse
"section": { 4. Entscheidungen
"h2": "<topic> — HH:MM:SS", - Entscheidung
"summary": "exactly 1 concise sentence", - Kontext / Begründung
"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", 5. Action Items
- Aufgabe
- Verantwortlich (falls ableitbar)
- Ziel / Zweck
"consolidated": [ 6. Offene Fragen & Risiken
"decisions",
"actions"
],
"appendix": "optional" STIL:
}, - Überschriften klar strukturiert
- Bullet Points bevorzugen
- Präzise, keine Umgangssprache
"STYLE": { TRANSKRIPT:
"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.