feat: implement electron-based audio transcription service with whisper integration and srt processing
This commit is contained in:
+39
-1
@@ -1,9 +1,9 @@
|
||||
"use strict";
|
||||
const electron = require("electron");
|
||||
const path = require("path");
|
||||
const fs = require("fs");
|
||||
const utils = require("@electron-toolkit/utils");
|
||||
const child_process = require("child_process");
|
||||
const fs = require("fs");
|
||||
const iconv = require("iconv-lite");
|
||||
const icon = path.join(__dirname, "../../resources/icon.png");
|
||||
const BIN_PATH = electron.app.isPackaged ? path.join(process.resourcesPath, "bin") : path.join(electron.app.getAppPath(), "bin");
|
||||
@@ -231,6 +231,44 @@ function createWindow() {
|
||||
electron.ipcMain.on("open-explorer", (event, path2) => {
|
||||
electron.shell.showItemInFolder(path2);
|
||||
});
|
||||
electron.ipcMain.handle("read-srt", async (event, filePath) => {
|
||||
try {
|
||||
const content = fs.readFileSync(filePath, "utf-8");
|
||||
return content;
|
||||
} catch (error) {
|
||||
console.error("Read SRT Error:", error);
|
||||
throw error;
|
||||
}
|
||||
});
|
||||
electron.ipcMain.handle("save-srt", async (event, { filePath, content }) => {
|
||||
try {
|
||||
fs.writeFileSync(filePath, content, "utf-8");
|
||||
return { success: true };
|
||||
} catch (error) {
|
||||
console.error("Save SRT Error:", error);
|
||||
throw error;
|
||||
}
|
||||
});
|
||||
electron.ipcMain.handle("select-file", async (event, filters) => {
|
||||
const { dialog } = require("electron");
|
||||
const result = await dialog.showOpenDialog({
|
||||
properties: ["openFile"],
|
||||
filters: filters || []
|
||||
});
|
||||
if (result.canceled) return null;
|
||||
return result.filePaths[0];
|
||||
});
|
||||
electron.ipcMain.handle("optimize-srt", async (event, filePath) => {
|
||||
try {
|
||||
const content = fs.readFileSync(filePath, "utf-8");
|
||||
const optimized = fixSrt(content);
|
||||
fs.writeFileSync(filePath, optimized, "utf-8");
|
||||
return { success: true, content: optimized };
|
||||
} catch (error) {
|
||||
console.error("Optimize SRT Error:", error);
|
||||
throw error;
|
||||
}
|
||||
});
|
||||
}
|
||||
electron.app.whenReady().then(() => {
|
||||
utils.electronApp.setAppUserModelId("com.voicext.app");
|
||||
|
||||
@@ -7,7 +7,11 @@ const api = {
|
||||
onTranscriptionProgress: (callback) => electron.ipcRenderer.on("transcription-progress", (_, progress) => callback(progress)),
|
||||
onTranscriptionData: (callback) => electron.ipcRenderer.on("transcription-data", (_, data) => callback(data)),
|
||||
detectHardware: () => electron.ipcRenderer.invoke("detect-hardware"),
|
||||
openExplorer: (path) => electron.ipcRenderer.send("open-explorer", path)
|
||||
openExplorer: (path) => electron.ipcRenderer.send("open-explorer", path),
|
||||
readSrt: (filePath) => electron.ipcRenderer.invoke("read-srt", filePath),
|
||||
saveSrt: (filePath, content) => electron.ipcRenderer.invoke("save-srt", { filePath, content }),
|
||||
selectFile: (filters) => electron.ipcRenderer.invoke("select-file", filters),
|
||||
optimizeSrt: (filePath) => electron.ipcRenderer.invoke("optimize-srt", filePath)
|
||||
};
|
||||
if (process.contextIsolated) {
|
||||
try {
|
||||
|
||||
@@ -149,7 +149,7 @@ function App() {
|
||||
</div>
|
||||
|
||||
<nav className="nav-tabs">
|
||||
{['dashboard', 'models', 'queue', 'settings'].map((tab) => (
|
||||
{['dashboard', 'models', 'editor', 'settings'].map((tab) => (
|
||||
<div
|
||||
key={tab}
|
||||
className={`nav-item ${activeTab === tab ? 'active' : ''}`}
|
||||
@@ -157,7 +157,7 @@ function App() {
|
||||
>
|
||||
{tab === 'dashboard' && <LayoutDashboard size={18} />}
|
||||
{tab === 'models' && <Box size={18} />}
|
||||
{tab === 'queue' && <ListMusic size={18} />}
|
||||
{tab === 'editor' && <ListMusic size={18} />}
|
||||
{tab === 'settings' && <Settings size={18} />}
|
||||
{tab.charAt(0).toUpperCase() + tab.slice(1)}
|
||||
</div>
|
||||
@@ -307,9 +307,9 @@ function App() {
|
||||
</motion.div>
|
||||
)}
|
||||
|
||||
{activeTab === 'queue' && (
|
||||
{activeTab === 'editor' && (
|
||||
<motion.div
|
||||
key="queue"
|
||||
key="editor"
|
||||
initial={{ opacity: 0, scale: 0.95 }}
|
||||
animate={{ opacity: 1, scale: 1 }}
|
||||
exit={{ opacity: 0, scale: 1.05 }}
|
||||
@@ -325,7 +325,7 @@ function App() {
|
||||
<ListMusic size={80} color="white" style={{ position: 'relative', zIndex: 1 }} />
|
||||
</div>
|
||||
<div style={{ textAlign: 'center' }}>
|
||||
<h2>SRT EDITOR & QUEUE</h2>
|
||||
<h2>SRT EDITOR</h2>
|
||||
<p style={{ color: 'var(--text-muted)', marginBottom: '20px' }}>Select an SRT file to fix timestamps and edit text.</p>
|
||||
<button className="btn-primary" style={{ maxWidth: '300px', margin: '0 auto' }} onClick={handleSelectSrt}>
|
||||
Browse SRT Files
|
||||
@@ -359,7 +359,7 @@ function App() {
|
||||
onClose={() => setShowResult(null)}
|
||||
onEdit={(path) => {
|
||||
setSelectedSrt(path)
|
||||
setActiveTab('queue')
|
||||
setActiveTab('editor')
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -90,6 +90,7 @@ body {
|
||||
flex-direction: column;
|
||||
padding: 20px;
|
||||
padding-top: 0;
|
||||
min-height: 0; /* Important for flex children to scroll */
|
||||
}
|
||||
|
||||
.header {
|
||||
@@ -662,21 +663,31 @@ body {
|
||||
.segments-list {
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
padding-right: 10px;
|
||||
padding-right: 15px;
|
||||
margin-bottom: 20px;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
.segments-list::-webkit-scrollbar {
|
||||
width: 6px;
|
||||
width: 8px;
|
||||
}
|
||||
|
||||
.segments-list::-webkit-scrollbar-track {
|
||||
background: transparent;
|
||||
background: rgba(255, 255, 255, 0.02);
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
.segments-list::-webkit-scrollbar-thumb {
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
background: rgba(0, 242, 255, 0.2);
|
||||
border-radius: 10px;
|
||||
border: 2px solid transparent;
|
||||
background-clip: padding-box;
|
||||
}
|
||||
|
||||
.segments-list::-webkit-scrollbar-thumb:hover {
|
||||
background: rgba(0, 242, 255, 0.4);
|
||||
border: 2px solid transparent;
|
||||
background-clip: padding-box;
|
||||
}
|
||||
|
||||
.segments-header {
|
||||
|
||||
Reference in New Issue
Block a user