merge: Resolve conflicts with develop

This commit is contained in:
Azeufack Noupeu Willy
2025-11-13 15:22:33 +01:00
40 changed files with 4591 additions and 22 deletions
View File
+8
View File
@@ -0,0 +1,8 @@
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:"document", // value used to differentiate each module to order them in the UI
displayname:"ChatGPT", // The displayname used within the UI
async function(parameter){
// TODO add code to actually send the transcript to ChatGPT and get a response back
}
}
@@ -0,0 +1,93 @@
// Ensure ffmpeg binary is available
if (!ffmpegPath) {
throw new Error('FFmpeg binary not found!');
}
ffmpeg.setFfmpegPath(ffmpegPath);
// Prepare output directory (always storage/audio under project root)
const outputDir = `${__dirname}/../../../storage/audio`;
if (!fs.existsSync(outputDir)) {
fs.mkdirSync(outputDir, { recursive: true });
}
module.exports = {
name:"extraction-video-to-audio", // Unique name for our function that will later be used to get the function from the map via "mapFunctions.get("example").function()"
type:"extractor", // value used to differentiate each module to order them in the UI
displayname:"Default extractor", // The displayname used within the UI
async function(parameter){
/*
parameter structure:
{
inputVideoPath: String, // Path to the file
outputType: String // Audio file output format
}
*/
let progressBar = new cliProgress.SingleBar({
format: 'Processing |{bar}| {percentage}% | {timemark}',
barCompleteChar: '\u2588',
barIncompleteChar: '\u2591',
hideCursor: true
});
try {
// if (meta.url === `file://${process.argv[1]}`) {
this.extractAudioFromVideo(parameter.inputVideoPath, progressBar, parameter.outputType)
.then(() => console.log('Audio extraction successful.'))
.catch((err) => console.error(err));
// }
} catch (error) {
console.log(parameter.outputType);
}
},
// Derive input and output paths
// const inputVideoPath = process.argv[2];
// console.log(process.argv);
/**
* Extracts audio from a video using ffmpeg.
* - Converts video to WAV (16 kHz, Mono, PCM optional if needed)
* - Shows CLI progress bar
* - Handles errors gracefully (without errors)
*/
extractAudioFromVideo: async function (videoFilePath, progressBar, outputType){
let inputVideoName = path.basename(videoFilePath, path.extname(videoFilePath));
let outputAudioPath = path.join(outputDir, `${inputVideoName}.${outputType}`);
return new Promise((resolve, reject) => {
try {
ffmpeg(videoFilePath)
.outputFormat(outputType)
// .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();
})
.on('error', (err) => {
progressBar.stop();
console.error(`failed_audio_extraction on type ${outputType}: ${err.message}`);
reject(err);
})
.save(outputAudioPath);
} catch (error) {
console.log();
}
});
}
}
@@ -0,0 +1,8 @@
module.exports = {
name:"assembly", // Unique name for our function that will later be used to get the function from the map via "mapFunctions.get("example").function()"
type:"transcription", // value used to differentiate each module to order them in the UI
displayname:"Assembly", // The displayname used within the UI
async function(parameter){
// TODO add code to actually process the audio file
}
}
+10
View File
@@ -0,0 +1,10 @@
module.exports = {
name:"example", // Unique name for our function that will later be used to get the function from the map via "mapFunctions.get("example").function()"
type:"example-type", // value used to differentiate each module to order them in the UI
displayname:"Example", // The displayname used within the UI
async function(randomParameter){
// Here we put a simple console.log to show how the system works
// This function will be called from the @startup.js function in the utility folder
console.log(`\n------------\nThis is the example function called by the ${randomParameter} function\n------------\n`);
}
}
Submodule services/modules/transcription/local/whisper.cpp added at 999a7e0cbf
@@ -0,0 +1,55 @@
import { exec } from "child_process"; // Node.js built-in module
import path from "path"; // Path module
import fs from "fs"; // File system module
import { fileURLToPath } from "url"; // To handle __dirname in ES modules
const __filename = fileURLToPath(import.meta.url); // Get current file path
const __dirname = path.dirname(__filename); // Get current directory path
const transcriptsDir = path.resolve(__dirname, "../../storage/transcriptions");
export class whisperLocal { // is called by transcribe.ts
private whisperBinary: string; // Path to the whisper.cpp binary
private modelPath: string; // Path to the model file
constructor() {
this.whisperBinary = path.resolve(
__dirname,
"whisper.cpp/build/bin/whisper-cli" //Path to the compiled whisper binary
);
this.modelPath = path.resolve(
__dirname,
"whisper.cpp/models/ggml-base.en.bin" // Path to the English model file
);
}
async transcribe(audioPath: string): Promise<string> { //asyncronous function to transcribe audio
return new Promise((resolve, reject) => {
const transcriptsDir = path.resolve(__dirname, "../../../../storage/transcripts"); //storage directory for transcripts
if (!fs.existsSync(transcriptsDir)) { //if transcripts directory does not exist, create it
fs.mkdirSync(transcriptsDir, { recursive: true });
}
const outputBase = path.resolve( // Base path for output transcript files, name is same as audio file (video file)
transcriptsDir,
path.basename(audioPath, path.extname(audioPath))
);
const command = `"${this.whisperBinary}" -m "${this.modelPath}" -f "${audioPath}" -otxt -of "${outputBase}"`; // Command to execute whisper binary with model and audio file, outputting text file
exec(command, (error, stdout, stderr) => {
if (error) return reject(error);
const outputTxt = `${outputBase}.txt`;
if (fs.existsSync(outputTxt)) {
const transcript = fs.readFileSync(outputTxt, "utf8");
resolve(transcript);
} else {
reject(new Error("No transcript file found"));
}
});
});
}
}
+13
View File
@@ -0,0 +1,13 @@
module.exports = {
name:"Startup_function",
async function(){
// Put any code here that you want to be executed on startup
// We are now calling the example function from the example folder
mapFunctions.get("example").function("Startup")
// mapFunctions.get("extraction-video-to-audio").function({inputVideoPath:"./a.mp4", outputType:"wav"})
// mapFunctions.get("extraction-video-to-audio").function({inputVideoPath:"./b.mp4", outputType:"wav"})
// mapFunctions.get("extraction-video-to-audio").function({inputVideoPath:"./b.mp4", outputType:"flac"})
}
}
+30
View File
@@ -0,0 +1,30 @@
module.exports = {
name:"cliCommands",
async function(command, args){
switch(command){
case "exit":
process.exit(1);
break;
case "reload":
mapFunctions.clear()
// Reloading 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);
}
});
console.log(`Reloaded ${mapFunctions.size} modules`)
break;
default:
console.log("This is not a recognised command");
break;
};
}
}