commit 89c69114bd917d8e1187c8161181d8527c811f70 Author: Ümit Tunç Date: Thu Apr 23 07:34:37 2026 +0300 first commit diff --git a/docs/implementation_plan.md b/docs/implementation_plan.md new file mode 100644 index 0000000..21498fe --- /dev/null +++ b/docs/implementation_plan.md @@ -0,0 +1,76 @@ +# Voicext - Local AI Transcription (Voice-to-SRT) Implementation Plan + +Voicext, Premiere Pro ve diğer kurgu programları için hızlı, yerel (offline) ve yüksek kaliteli SRT dosyaları üreten bir masaüstü uygulamasıdır. `whisper.cpp` motorunu kullanarak RTX GPU gücünden yararlanır ve "al gülüm ver gülüm" sadeliğinde bir iş akışı sunar. + +## 🎯 Proje Hedefleri +- **Gizlilik:** Tamamen yerel çalışma (İnternet gerekmez). +- **Kullanılabilirlik:** Tek tıkla MP3 -> SRT dönüşümü. +- **Estetik:** Modern, karanlık tema, premium tasarım. + +## 🏗️ Mimari ve Teknoloji Yığını + +### Çekirdek (Core) +- **Framework:** Electron.js (Vite ile optimize edilmiş). +- **Processing:** `whisper.cpp` (C++ tabanlı yerel inferans). +- **Audio Engine:** `FFmpeg` (16kHz Mono WAV dönüşümü için). +- **Hardware Acceleration:** + - **GPU:** CUDA/cuBLAS (NVIDIA kullanıcıları için). + - **CPU:** AVX/AVX2/AVX-512 optimizasyonu (GPU olmayan sistemler için otomatik fallback). + +### Arayüz (Frontend) +- **UI:** React + Vanilla CSS (Custom premium components). +- **Animations:** Framer Motion (Akıcı geçişler). +- **State:** Zustand (Hafif ve hızlı durum yönetimi). + +--- + +## 🛠️ Uygulama Fazları + +### Faz 1: Altyapı ve Binary Yönetimi +Uygulamanın kalbi olan binary dosyalarının hazırlanması. +- [ ] `whisper.cpp`'nin Windows CUDA desteğiyle derlenmesi. +- [ ] `ffmpeg` static binary'lerinin projeye dahil edilmesi. +- [ ] Model yönetimi: Uygulama içinden `tiny`, `base`, `small`, `medium`, `large` modellerinin indirilmesi ve yönetilmesi. + +### Faz 2: İş Akışı (The "Al Gülüm Ver Gülüm" Engine) +1. **Input:** Kullanıcı MP3/WAV/M4A dosyasını sürükler. +2. **Pre-processing:** FFmpeg ile `16kHz, mono, 16-bit PCM WAV` formatına anlık dönüşüm. +3. **Inference:** `whisper.exe -m model.bin -f input.wav -osrt -l tr` komutunun child_process ile çalıştırılması. +4. **Output:** `.srt` dosyasının otomatik olarak orijinal dosyanın yanına veya seçilen klasöre kaydedilmesi. + +### Faz 3: Kullanıcı Deneyimi ve Arayüz (Premium UX) +- **Glassmorphic Dashboard:** Sürükle-bırak alanı için neon efektli modern bir "Drop Zone". +- **Real-time Progress:** Whisper'ın stdout çıktısını parse ederek canlı ilerleme çubuğu ve "transcription streaming" (ekrana dökülen kelimeler). +- **SRT Preview:** Oluşturulan SRT dosyasını uygulama içinde önizleme ve basit zamanlama düzeltmeleri yapma imkanı. +- **One-Click Export:** "Export for Premiere" butonu ile standartlara en uygun SRT formatını dışa aktarma. + +--- + +## 🖥️ Teknik Detaylar (Hack Önerileri) + +### Hibrit Donanım Desteği (Auto-Detection) +Uygulama her sistemde çalışabilmesi için donanımı otomatik algılayacak: +- **NVIDIA Sistemler:** `whisper-cuda.exe` ile maksimum hız. +- **Diğer Sistemler:** `whisper-cpu.exe` ile optimize edilmiş işlemci kullanımı. +- **Dinamik Seçim:** Ayarlar kısmında kullanıcıya "CPU vs GPU" seçme imkanı sunulacak. + +### SRT Optimizasyonu +Premiere Pro'da altyazıların çok uzun olmaması için whisper.cpp'nin segmentasyon ayarları optimize edilecek: +- `--max-len`: Satır başına maksimum karakter (Örn: 42). +- `--max-context`: Daha iyi bağlam için önceki metni hatırlama. + +--- + +## 📅 Yol Haritası (Milestones) +1. **MVP:** Dosya seç -> Dönüştür -> SRT al (Görsel odak yok). +2. **Design Update:** Premium UI elementlerinin ve animasyonların eklenmesi. +3. **Advanced Features:** Çoklu dil seçimi, GPU/CPU anahtarı, SRT editörü. + +## ❓ Açık Sorular +- Uygulama ilk açıldığında `base` modelini (yaklaşık 140MB) paket içine gömmeli miyiz yoksa ilk açılışta mı indirtmeliyiz? +- Birden fazla dosyayı aynı anda işleme (Batch Processing) özelliği ilk aşamada gerekli mi? + +## 🧪 Doğrulama Planı +- Farklı MP3 kalitelerinde (128kbps, 320kbps) FFmpeg dönüşüm testi. +- Uzun videolarda (1 saat+) bellek kullanımı ve GPU sıcaklık takibi. +- Üretilen SRT'nin Premiere Pro ve VLC'de karakter kodlaması (UTF-8) kontrolü. diff --git a/docs/logo/voicext-icon.png b/docs/logo/voicext-icon.png new file mode 100644 index 0000000..5f6ef01 Binary files /dev/null and b/docs/logo/voicext-icon.png differ diff --git a/docs/logo/voicext-logo.png b/docs/logo/voicext-logo.png new file mode 100644 index 0000000..3d8c7f0 Binary files /dev/null and b/docs/logo/voicext-logo.png differ diff --git a/docs/ux/dashboard.png b/docs/ux/dashboard.png new file mode 100644 index 0000000..cdc5506 Binary files /dev/null and b/docs/ux/dashboard.png differ diff --git a/docs/ux/one-click-export.png b/docs/ux/one-click-export.png new file mode 100644 index 0000000..58cb146 Binary files /dev/null and b/docs/ux/one-click-export.png differ diff --git a/docs/ux/real-time-progress.png b/docs/ux/real-time-progress.png new file mode 100644 index 0000000..65a6e05 Binary files /dev/null and b/docs/ux/real-time-progress.png differ diff --git a/docs/ux/srt-preview.png b/docs/ux/srt-preview.png new file mode 100644 index 0000000..7bd102f Binary files /dev/null and b/docs/ux/srt-preview.png differ diff --git a/electron.vite.config.mjs b/electron.vite.config.mjs new file mode 100644 index 0000000..5b54e20 --- /dev/null +++ b/electron.vite.config.mjs @@ -0,0 +1,20 @@ +import { resolve } from 'path' +import { defineConfig, externalizeDepsPlugin } from 'electron-vite' +import react from '@vitejs/plugin-react' + +export default defineConfig({ + main: { + plugins: [externalizeDepsPlugin()] + }, + preload: { + plugins: [externalizeDepsPlugin()] + }, + renderer: { + resolve: { + alias: { + '@renderer': resolve('src/renderer/src') + } + }, + plugins: [react()] + } +}) diff --git a/package.json b/package.json new file mode 100644 index 0000000..603a7b6 --- /dev/null +++ b/package.json @@ -0,0 +1,32 @@ +{ + "name": "voicext", + "version": "1.0.0", + "description": "Local AI Transcription Tool", + "main": "./out/main/index.js", + "author": "Voicext", + "scripts": { + "dev": "electron-vite dev", + "build": "electron-vite build", + "preview": "electron-vite preview", + "package": "electron-vite build && electron-builder", + "postinstall": "electron-builder install-app-deps" + }, + "dependencies": { + "@headlessui/react": "^2.0.0", + "clsx": "^2.1.1", + "framer-motion": "^11.2.10", + "lucide-react": "^0.395.0", + "react": "^18.3.1", + "react-dom": "^18.3.1", + "tailwind-merge": "^2.3.0", + "zustand": "^4.5.2" + }, + "devDependencies": { + "@electron-vite/cli": "^2.3.0", + "@vitejs/plugin-react": "^4.3.1", + "electron": "^31.0.1", + "electron-builder": "^24.13.3", + "electron-vite": "^2.3.0", + "vite": "^5.3.1" + } +} diff --git a/src/main/index.js b/src/main/index.js new file mode 100644 index 0000000..4f38ba5 --- /dev/null +++ b/src/main/index.js @@ -0,0 +1,64 @@ +import { app, shell, BrowserWindow, ipcMain } from 'electron' +import { join } from 'path' +import { electronApp, optimizer, is } from '@electron-toolkit/utils' +import icon from '../../resources/icon.png?asset' + +function createWindow() { + const mainWindow = new BrowserWindow({ + width: 1100, + height: 670, + show: false, + autoHideMenuBar: true, + ...(process.platform === 'linux' ? { icon } : {}), + webPreferences: { + preload: join(__dirname, '../preload/index.mjs'), + sandbox: false + }, + frame: false, // Custom frame for premium look + transparent: true, + backgroundColor: '#00000000' + }) + + mainWindow.on('ready-to-show', () => { + mainWindow.show() + }) + + mainWindow.webContents.setWindowOpenHandler((details) => { + shell.openExternal(details.url) + return { action: 'deny' } + }) + + if (is.dev && process.env['ELECTRON_RENDERER_URL']) { + mainWindow.loadURL(process.env['ELECTRON_RENDERER_URL']) + } else { + mainWindow.loadFile(join(__dirname, '../renderer/index.html')) + } +} + +app.whenReady().then(() => { + electronApp.setAppId('com.voicext.app') + + app.on('browser-window-created', (_, window) => { + optimizer.watchWindowShortcuts(window) + }) + + // IPC handlers + ipcMain.on('window-controls', (event, action) => { + const win = BrowserWindow.fromWebContents(event.sender) + if (action === 'minimize') win.minimize() + if (action === 'maximize') win.isMaximized() ? win.unmaximize() : win.maximize() + if (action === 'close') win.close() + }) + + createWindow() + + app.on('activate', function () { + if (BrowserWindow.getAllWindows().length === 0) createWindow() + }) +}) + +app.on('window-all-closed', () => { + if (process.platform !== 'darwin') { + app.quit() + } +})