diff --git a/0.50.0 b/0.50.0 deleted file mode 100644 index e69de29..0000000 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md deleted file mode 100644 index bf4ff9c..0000000 --- a/CONTRIBUTING.md +++ /dev/null @@ -1,10 +0,0 @@ -# Contributing - -1. Fork the repository -2. Create a feature branch -3. Make your changes -4. Submit a pull request - -## Development Setup - -See README.md for setup instructions. \ No newline at end of file diff --git a/README.md b/README.md index c611d77..111da90 100644 --- a/README.md +++ b/README.md @@ -1,105 +1,101 @@ -# ๐ŸŽน Piano Highlight Generator +# Lesson Highlights Generator -้’ข็ด่ฏพ็ฒพๅŽ่ง†้ข‘็”Ÿๆˆๅทฅๅ…ทใ€‚่‡ชๅŠจไปŽๅฎŒๆ•ด่ฏพ็จ‹่ง†้ข‘ไธญๆๅ–็ฒพๅŽ็‰‡ๆฎต๏ผŒ่ฝฌๅฝ•ใ€็บ ้”™ใ€็”Ÿๆˆๅญ—ๅน•๏ผŒๆ‰น้‡็ƒงๅฝ•ๅˆฐ่ง†้ข‘ไธญใ€‚ +ๆ•™ๅญฆ่ง†้ข‘็ฒพๅŽ็‰‡ๆฎต็”Ÿๆˆๅทฅๅ…ทใ€‚่พ“ๅ…ฅ่ฏพ็จ‹่ง†้ข‘ + PPT๏ผŒ่‡ชๅŠจๆๅ–็ฒพๅŽ็‰‡ๆฎต๏ผŒ่ฝฌๅฝ•ใ€็บ ้”™ใ€็”Ÿๆˆๅญ—ๅน•๏ผŒๆ‰น้‡็ƒงๅฝ•ๅˆฐ่ง†้ข‘ไธญใ€‚ -## โœจ ๅŠŸ่ƒฝ็‰น็‚น +## ๅŠŸ่ƒฝ็‰น็‚น -- **ๆ™บ่ƒฝๆๅ–**: ่‡ชๅŠจๆฃ€ๆต‹่ง†้ข‘ไธญ็š„็ฒพๅฝฉ็‰‡ๆฎต -- **่ฏญ้Ÿณ่ฝฌๅฝ•**: ๆ”ฏๆŒ Whisper ๅคšๆจกๅž‹๏ผˆtiny/base/small/medium/large๏ผ‰ -- **AI ็บ ้”™**: LLM ่‡ชๅŠจ็บ ๆญฃ่ฝฌๅฝ•้”™่ฏฏ๏ผŒไผ˜ๅŒ–ๆ ‡้ข˜ -- **ๅŒ่ฏญๅญ—ๅน•**: ๆ”ฏๆŒๅŒ่ฝจๅญ—ๅน•๏ผˆๆ ‡้ข˜่ฝจ + ๅ†…ๅฎน่ฝจ๏ผ‰ -- **็Šถๆ€ๆŒไน…ๅŒ–**: ๆ”ฏๆŒๆš‚ๅœ/ๆขๅค๏ผŒๅฏไธญๆ–ญ็ปง็ปญ -- **ๆ‰‹ๅŠจ็ผ–่พ‘**: ็”Ÿๆˆๅ‰ๅฏไบบๅทฅๅฎกๆ ธ็ผ–่พ‘ๆ ‡้ข˜ๅ’Œๅญ—ๅน•ๅ†…ๅฎน +- **PPT ้ฉฑๅŠจๆๅ–**๏ผšๆ นๆฎ PPT ็Ÿฅ่ฏ†็‚นๅฎšไฝ่ง†้ข‘ไธญ็š„่ฎฒ่งฃ็‰‡ๆฎต +- **่ฏญ้Ÿณ่ฝฌๅฝ• + ็บ ้”™**๏ผšWhisper ่ฝฌๅฝ• + LLM ๆ‰น้‡ๆ กๆญฃ +- **ๅŒ่ฝจๅญ—ๅน•**๏ผšๆ ‡้ข˜่ฝจ + ๅ†…ๅฎน่ฝจ +- **CLI / GUI ๅŒๅ…ฅๅฃ**๏ผšๅ…ฑไบซๅŒไธ€ๅฅ—ๅบ•ๅฑ‚้€ป่พ‘ -## ๐Ÿ“‹ ็ณป็ปŸ่ฆๆฑ‚ +## ๅฟซ้€Ÿๅผ€ๅง‹ -- Windows 10/11 ๆˆ– macOS 10.15+ -- Python 3.10+ -- FFmpeg๏ผˆๅฟ…้กป๏ผŒๆทปๅŠ ๅˆฐ PATH๏ผ‰ +### 1. ้…็ฝฎ API -## ๐Ÿš€ ๅฟซ้€Ÿๅผ€ๅง‹ - -### 1. ๅฎ‰่ฃ… +ๅคๅˆถ้…็ฝฎๆ–‡ไปถๅนถๅกซๅ…ฅ API Key๏ผš ```bash -# ๅ…‹้š†้กน็›ฎ -git clone -cd piano-highlight-app +cp config.ini.example config.ini +# ็ผ–่พ‘ config.ini๏ผŒๅกซๅ…ฅ api_key +``` -# ๅˆ›ๅปบ่™šๆ‹Ÿ็Žฏๅขƒ๏ผˆๆŽจ่๏ผ‰ -python -m venv venv -.\venv\Scripts\activate # Windows -source venv/bin/activate # Linux/macOS +### 2. ๅฎ‰่ฃ…ไพ่ต– -# ๅฎ‰่ฃ…ไพ่ต– +```bash pip install -r requirements.txt - -# ๅฎ‰่ฃ… FFmpeg๏ผˆWindows - ไฝฟ็”จ winget๏ผ‰ -winget install Gyan.FFmpeg - -# ๆˆ– macOS -brew install ffmpeg ``` -### 2. ่ฟ่กŒ +### 3. ่ฟ่กŒ +**GUI๏ผˆๆŽจ่๏ผ‰๏ผš** ```bash -python src/main.py +.\start.bat ``` -### 3. ้…็ฝฎ +**CLI๏ผš** +```bash +.\run_lesson1.bat +``` -้ฆ–ๆฌก่ฟ่กŒ้œ€่ฆ้…็ฝฎ๏ผš -1. **API ่ฎพ็ฝฎ**: ้€‰ๆ‹ฉ API ๆไพ›ๅ•†๏ผˆDeepSeek/็ก…ๅŸบๆตๅŠจ๏ผ‰๏ผŒ่พ“ๅ…ฅ API Key -2. **่ง†้ข‘่ฎพ็ฝฎ**: ้€‰ๆ‹ฉ่พ“ๅ…ฅ่ง†้ข‘ใ€่พ“ๅ‡บ็›ฎๅฝ• -3. **่ฝฌๅฝ•่ฎพ็ฝฎ**: ้€‰ๆ‹ฉ Whisper ๆจกๅž‹๏ผˆๆŽจ่ medium๏ผ‰ +ๆˆ–้€š็”จๆ–นๅผ๏ผš +```bash +python src/cli.py --video video.mp4 --ppt presentation.pptx --output ./output +``` -### 4. ็”Ÿๆˆ +## ้กน็›ฎ็ป“ๆž„ -1. ็‚นๅ‡ปใ€Œๅผ€ๅง‹ๅค„็†ใ€ -2. ็ญ‰ๅพ…ๅ„ๆญฅ้ชคๅฎŒๆˆ -3. **ๆ ‡้ข˜็กฎ่ฎค**: LLM ็”Ÿๆˆๆ ‡้ข˜ๅŽ๏ผŒๅฎกๆ ธๅนถ็ผ–่พ‘ -4. **ๅญ—ๅน•็กฎ่ฎค**: ๆŸฅ็œ‹ๅญ—ๅน•ๅ†…ๅฎน๏ผŒๅฏ่ฟ›ไธ€ๆญฅ็ผ–่พ‘ -5. ็ญ‰ๅพ…็ƒงๅฝ•ๅฎŒๆˆ +``` +lesson-highlights/ +โ”œโ”€โ”€ src/ +โ”‚ โ”œโ”€โ”€ main.py # GUI ๅ…ฅๅฃ +โ”‚ โ”œโ”€โ”€ gui.py # GUI๏ผˆๅ‚ๆ•ฐ่พ“ๅ…ฅ๏ผŒ่ฐƒ็”จๅบ•ๅฑ‚๏ผ‰ +โ”‚ โ”œโ”€โ”€ cli.py # CLI ๅ…ฅๅฃ +โ”‚ โ””โ”€โ”€ core/ # ๅ…ฑไบซๅบ•ๅฑ‚ +โ”‚ โ”œโ”€โ”€ ppt_parser.py # PPT ่งฃๆž + clips ็”Ÿๆˆ +โ”‚ โ”œโ”€โ”€ pipeline.py # ่ง†้ข‘ๅค„็†ๆตๆฐด็บฟ +โ”‚ โ”œโ”€โ”€ subtitle.py # ๅญ—ๅน•็”Ÿๆˆ +โ”‚ โ””โ”€โ”€ ... +โ”œโ”€โ”€ config.ini # API ้…็ฝฎ๏ผˆไธๆไบค git๏ผ‰ +โ”œโ”€โ”€ config.ini.example # ้…็ฝฎๆจกๆฟ +โ”œโ”€โ”€ start.bat # ๅฏๅŠจ GUI +โ””โ”€โ”€ run_lesson1.bat # CLI ็คบไพ‹ +``` -## ๐Ÿ“ ่พ“ๅ‡บๆ–‡ไปถ +## ๅทฅไฝœๆต็จ‹ + +1. **PPT ่งฃๆž**๏ผšๆๅ– PPT ๆ–‡ๆœฌๅ’Œ็Ÿฅ่ฏ†็‚น +2. **Whisper ่ฝฌๅฝ•**๏ผšๅฐ†่ง†้ข‘่ฏญ้Ÿณ่ฝฌๆˆๆ–‡ๆœฌ +3. **LLM ๆ กๆญฃ**๏ผšๆ‰น้‡ๆ กๆญฃ่ฝฌๅฝ•้”™่ฏฏ +4. **็‰‡ๆฎตๆๅ–**๏ผšๆ นๆฎ PPT ็Ÿฅ่ฏ†็‚นๅฎšไฝ่ง†้ข‘็‰‡ๆฎต +5. **ๅญ—ๅน•็ƒงๅฝ•**๏ผš็”ŸๆˆๅŒ่ฝจๅญ—ๅน•ๅนถ็ƒงๅ…ฅ่ง†้ข‘ +6. **ๅˆๅนถ่พ“ๅ‡บ**๏ผšๆ‹ผๆŽฅๆ‰€ๆœ‰็‰‡ๆฎตไธบๆœ€็ปˆ่ง†้ข‘ + +## API ้…็ฝฎ + +็ผ–่พ‘ `config.ini`๏ผš + +```ini +[api] +api_host = "https://ark.cn-beijing.volces.com/api/coding/v3" +api_key = your_api_key_here +``` + +ๆ”ฏๆŒ็ซๅฑฑๆ–น่ˆŸ๏ผˆdoubao-seed-2.0-lite๏ผ‰ๆˆ–ๅ…ผๅฎน OpenAI API ็š„ๅŽ็ซฏใ€‚ + +## ่พ“ๅ‡บ ``` output/ -โ”œโ”€โ”€ state.json # ๅค„็†็Šถๆ€ -โ”œโ”€โ”€ clips/ # ๆๅ–็š„็‰‡ๆฎต -โ”‚ โ””โ”€โ”€ clip_001.mp4 +โ”œโ”€โ”€ generated_config.yaml # ็”Ÿๆˆ็š„ clips ้…็ฝฎ +โ”œโ”€โ”€ clips/ # ๆๅ–็š„็‰‡ๆฎต่ง†้ข‘ โ”œโ”€โ”€ subtitles/ # ๅญ—ๅน•ๆ–‡ไปถ -โ”‚ โ”œโ”€โ”€ clip_001_title.srt # ๆ ‡้ข˜่ฝจ -โ”‚ โ””โ”€โ”€ clip_001_content.srt # ๅ†…ๅฎน่ฝจ -โ””โ”€โ”€ final/ # ๆœ€็ปˆ่พ“ๅ‡บ - โ””โ”€โ”€ clip_001_final.mp4 +โ””โ”€โ”€ final.mp4 # ๆœ€็ปˆ่พ“ๅ‡บ ``` -## ๐Ÿ”ง ๆตๆฐด็บฟๆญฅ้ชค +## ็ณป็ปŸ่ฆๆฑ‚ -1. **extract** - ็‰‡ๆฎตๆๅ– -2. **transcribe** - ่ฏญ้Ÿณ่ฝฌๅฝ• -3. **title_correct** - ๆ ‡้ข˜็”ŸๆˆไธŽ็บ ้”™ -4. **generate_subtitles** - ๅญ—ๅน•็”Ÿๆˆ -5. **merge** - ็‰‡ๆฎตๅˆๅนถ -6. **burn** - ๅญ—ๅน•็ƒงๅฝ• - -## โš ๏ธ ๅธธ่ง้—ฎ้ข˜ - -### Q: ๆ็คบ "FFmpeg not found" -A: ็กฎไฟ FFmpeg ๅทฒๅฎ‰่ฃ…ๅนถๆทปๅŠ ๅˆฐ็ณป็ปŸ PATHใ€‚้‡ๅฏ็ปˆ็ซฏๅŽ้‡่ฏ•ใ€‚ - -### Q: API ่ฐƒ็”จๅคฑ่ดฅ -A: ๆฃ€ๆŸฅ API Key ๆ˜ฏๅฆๆญฃ็กฎ๏ผŒ็ฝ‘็ปœๆ˜ฏๅฆๆญฃๅธธ๏ผŒๆˆ–ๅˆ‡ๆข API ๆไพ›ๅ•†ใ€‚ - -### Q: ็ฃ็›˜็ฉบ้—ดไธ่ถณ -A: ๆธ…็†่พ“ๅ‡บ็›ฎๅฝ•ๆˆ–ๆ›ดๆขๅˆฐ็ฉบ้—ดๆ›ดๅคง็š„็ฃ็›˜ใ€‚ - -## ๐Ÿ“„ ่ฎธๅฏ่ฏ - -MIT License - -## ๐Ÿค ่ดก็Œฎ - -ๆฌข่ฟŽๆไบค Issue ๅ’Œ Pull Request๏ผ \ No newline at end of file +- Python 3.10+ +- FFmpeg๏ผˆๅทฒๆ‰“ๅŒ…ๅœจ `ffmpeg/` ็›ฎๅฝ•๏ผ‰ +- PySide6๏ผˆGUI๏ผ‰ +- faster-whisper๏ผˆ่ฝฌๅฝ•๏ผŒๅฏ้€‰๏ผ‰ diff --git a/README_BUILD.md b/README_BUILD.md deleted file mode 100644 index 04e5054..0000000 --- a/README_BUILD.md +++ /dev/null @@ -1,148 +0,0 @@ -# Piano Highlight Generator - Build Instructions - -## Prerequisites - -### 1. Python -- **Version**: Python 3.10 or higher (3.12 recommended) -- **Download**: https://www.python.org/downloads/ -- **Note**: Ensure Python is added to PATH - -### 2. FFmpeg (Runtime Requirement) -FFmpeg is required for video processing at runtime, NOT for building. - -**Windows:** -- Download from https://ffmpeg.org/download.html -- Or use: `winget install ffmpeg` -- Add FFmpeg binary location to system PATH - -**Linux (Ubuntu/Debian):** -```bash -sudo apt update -sudo apt install ffmpeg -``` - -**macOS:** -```bash -brew install ffmpeg -``` - -### 3. Additional Build Tools (Windows) -- **Visual Studio Build Tools** or **MinGW** may be required for Nuitka compilation -- Download: https://visualstudio.microsoft.com/visual-cpp-build-tools/ - ---- - -## Build Steps - -### Windows - -1. Open Command Prompt or PowerShell in project directory - -2. Run the build script: -```cmd -build.bat -``` - - Or manually: -```cmd -pip install nuitka pandas -python -m nuitka --standalone --onefile --windows-console-mode=disable --output-dir=dist --output-name=PianoHighlightGenerator --enable-plugin=pyside6 src/main.py -``` - -### Linux/macOS - -1. Make build script executable: -```bash -chmod +x build.sh -``` - -2. Run the build script: -```bash -./build.sh -``` - - Or manually: -```bash -pip3 install nuitka pandas -python3 -m nuitka --standalone --onefile --output-dir=dist --output-name=PianoHighlightGenerator --enable-plugin=pyside6 src/main.py -``` - ---- - -## Output - -| Platform | Output Location | Filename | -|----------|----------------|----------| -| Windows | `dist/` | `PianoHighlightGenerator.exe` | -| Linux | `dist/` | `PianoHighlightGenerator.bin` | -| macOS | `dist/` | `PianoHighlightGenerator.bin` | - ---- - -## Expected Size - -- **Standalone executable**: ~150-250 MB -- This includes Python interpreter, PySide6, and all dependencies - ---- - -## Testing the Built Executable - -1. Copy FFmpeg to the same directory as the executable OR ensure FFmpeg is in PATH - -2. Run the executable: - - **Windows**: Double-click `PianoHighlightGenerator.exe` or run from cmd - - **Linux/macOS**: Run `./PianoHighlightGenerator.bin` in terminal - -3. Test basic functionality: - - App should launch with GUI - - Video selection should work - - Processing pipeline should execute - ---- - -## Troubleshooting - -### "ffmpeg not found" error -- Ensure FFmpeg is installed and in system PATH -- Test by running `ffmpeg -version` in terminal - -### "Missing DLL" errors on Windows -- Install Visual C++ Redistributable: https://aka.ms/vs/17/release/vc_redist.x64.exe - -### Build fails with memory error -- Reduce parallelism: Add `--jobs=2` to build command -- Close other applications - -### PySide6 plugin issues -- Ensure `--enable-plugin=pyside6` is included -- For special PySide6 handling, add `--pyside6-option=--no-sandbox` - ---- - -## Data Files - -If you have a `prompts/` directory with template files, ensure: -- Path: `src/core/prompts/` -- Files are copied with `--include-data-files=src/core/prompts=prompts` - -Currently, no prompts directory exists in the project. Create `src/core/prompts/` if needed for custom prompt templates. - ---- - -## Nuitka Configuration (pyproject.toml) - -The project includes Nuitka settings in `pyproject.toml`: - -```toml -[tool.nuitka] -assume_yes_for_downloads = true -show_progress = true -output_dir = "dist" -output_name = "PianoHighlightGenerator" -python_version = "3.10" -standalone = true -onefile = true -``` - -You can also use the command line options documented above for more control. \ No newline at end of file diff --git a/build.bat b/build.bat deleted file mode 100644 index 083bf34..0000000 --- a/build.bat +++ /dev/null @@ -1,71 +0,0 @@ -@echo off -REM Piano Highlight Generator - Build Script for Windows -REM Prerequisites: Python 3.10+, FFmpeg (in PATH) - -echo ================================================ -echo Piano Highlight Generator - Nuitka Build -echo ================================================ -echo. - -REM Check Python version -echo Checking Python version... -python --version -if errorlevel 1 ( - echo ERROR: Python not found. Please install Python 3.10 or higher. - pause - exit /b 1 -) - -REM Check FFmpeg -echo. -echo Checking FFmpeg... -where ffmpeg >nul 2>nul -if errorlevel 1 ( - echo WARNING: FFmpeg not found in PATH. The built executable will require FFmpeg to be installed. - echo Please install FFmpeg from: https://ffmpeg.org/download.html - echo. -) - -REM Create dist directory if not exists -if not exist "dist" mkdir dist - -REM Install build dependencies -echo. -echo Installing build dependencies... -pip install nuitka pandas - -REM Build command -echo. -echo Starting Nuitka compilation... -echo This may take several minutes on first run... -echo. - -python -m nuitka ^ - --standalone ^ - --onefile ^ - --windows-console-mode=disable ^ - --output-dir=dist ^ - --output-name=PianoHighlightGenerator ^ - --enable-plugin=pyside6 ^ - --include-data-files=src/core/prompts=prompts ^ - --python-version=3.10 ^ - src/main.py - -if errorlevel 1 ( - echo. - echo BUILD FAILED! - pause - exit /b 1 -) - -echo. -echo ================================================ -echo Build complete! -echo ================================================ -echo. -echo Output: dist\PianoHighlightGenerator.exe -echo. -echo NOTE: FFmpeg must be in PATH for the executable to work. -echo If FFmpeg is not installed, download from https://ffmpeg.org -echo. -pause \ No newline at end of file diff --git a/build.sh b/build.sh deleted file mode 100644 index a284b1f..0000000 --- a/build.sh +++ /dev/null @@ -1,61 +0,0 @@ -#!/bin/bash -# Piano Highlight Generator - Build Script for Linux/macOS -# Prerequisites: Python 3.10+, FFmpeg (in PATH) - -set -e - -echo "================================================" -echo " Piano Highlight Generator - Nuitka Build" -echo "================================================" -echo "" - -# Check Python version -echo "Checking Python version..." -python3 --version || { echo "ERROR: Python not found. Please install Python 3.10 or higher."; exit 1; } - -# Check FFmpeg -echo "" -echo "Checking FFmpeg..." -if ! command -v ffmpeg &> /dev/null; then - echo "WARNING: FFmpeg not found in PATH. The built executable will require FFmpeg." - echo "Please install FFmpeg: sudo apt install ffmpeg (Ubuntu/Debian) or brew install ffmpeg (macOS)" - echo "" -fi - -# Create dist directory if not exists -mkdir -p dist - -# Install build dependencies -echo "" -echo "Installing build dependencies..." -pip3 install nuitka pandas - -# Build -echo "" -echo "Starting Nuitka compilation..." -echo "This may take several minutes on first run..." -echo "" - -python3 -m nuitka \ - --standalone \ - --onefile \ - --output-dir=dist \ - --output-name=PianoHighlightGenerator \ - --enable-plugin=pyside6 \ - --include-data-files=src/core/prompts=prompts \ - --python-version=3.10 \ - src/main.py - -echo "" -echo "================================================" -echo " Build complete!" -echo "================================================" -echo "" -echo "Output: dist/PianoHighlightGenerator.bin" -echo "" -echo "NOTE: FFmpeg must be in PATH for the executable to work." -echo "" -echo "To run FFmpeg from a specific location, either:" -echo " 1. Add FFmpeg to your PATH" -echo " 2. Place FFmpeg binary in the same directory as the executable" -echo "" \ No newline at end of file diff --git a/build_cli.bat b/build_cli.bat deleted file mode 100644 index 9e680de..0000000 --- a/build_cli.bat +++ /dev/null @@ -1,69 +0,0 @@ -@echo off -REM Piano Highlight Generator - CLI Build Script for Windows -REM Builds a standalone CLI executable from cli.py - -set "PYTHON=D:\ProgramData\anaconda3\envs\py312_cuda\python.exe" -set "PYINSTALLER=D:\ProgramData\anaconda3\envs\py312_cuda\Scripts\pyinstaller.exe" - -echo ================================================ -echo Piano Highlight Generator - CLI Build -echo ================================================ -echo. - -REM Check Python -"%PYTHON%" --version -if errorlevel 1 ( - echo ERROR: Python not found - pause - exit /b 1 -) - -REM Install pyinstaller if needed -"%PYTHON%" -c "import PyInstaller" 2>nul -if errorlevel 1 ( - echo Installing PyInstaller... - "%PYTHON%" -m pip install pyinstaller -q -) - -REM Build -echo. -echo Starting PyInstaller compilation... -echo This may take several minutes... -echo. - -cd /d "D:\F\NewI\opencode\daily-workspace\projects\piano-highlight-app" - -"%PYTHON%" -m PyInstaller ^ - --name=PianoHighlightCLI ^ - --console ^ - --onefile ^ - --clean ^ - --distpath=dist_cli ^ - --workpath=build_cli ^ - --specpath=build_cli ^ - --additional-hooks-dir= ^ - --hidden-import=pkg_resources ^ - --hidden-import=faster_whisper ^ - --hidden-import=cv2 ^ - --hidden-import= yaml ^ - --hidden-import=requests ^ - --hidden-import=PIL ^ - --collect-all=faster_whisper ^ - --collect-all=transformers ^ - src/cli.py - -if errorlevel 1 ( - echo. - echo BUILD FAILED! - pause - exit /b 1 -) - -echo. -echo ================================================ -echo Build complete! -echo ================================================ -echo. -echo Output: dist_cli\PianoHighlightCLI.exe -echo. -pause diff --git a/design.md b/design.md deleted file mode 100644 index 7bc51f5..0000000 --- a/design.md +++ /dev/null @@ -1,521 +0,0 @@ -# Piano Highlight Generator App - ๆŠ€ๆœฏ่ฎพ่ฎก - -> ่ฎพ่ฎกๆ—ฅๆœŸ๏ผš2026-05-02 -> ็‰ˆๆœฌ๏ผš1.0 -> ็Šถๆ€๏ผšDraft - ---- - -## 1. ๆŠ€ๆœฏๆ ˆ - -| ๅฑ‚็บง | ๆŠ€ๆœฏ | ้€‰ๅž‹็†็”ฑ | -|------|------|----------| -| GUI ๆก†ๆžถ | PySide6 (Qt for Python) | LGPL ่ฎธๅฏ๏ผŒๅŠŸ่ƒฝๅฎŒๅค‡๏ผŒไฟกๅทๆงฝๆœบๅˆถ้€‚ๅˆๅผ‚ๆญฅๆ›ดๆ–ฐ | -| ๆ‰“ๅŒ…ๅทฅๅ…ท | Nuitka | ็ผ–่ฏ‘ไธบ C๏ผŒๆ€ง่ƒฝๅฅฝ๏ผŒไฝ“็งฏๅฐ | -| ็Šถๆ€ๆŒไน…ๅŒ– | JSON ๆ–‡ไปถ | ็ฎ€ๅ•๏ผŒๆ— ้œ€ๆ•ฐๆฎๅบ“ไพ่ต– | -| ๆ ธๅฟƒๆจกๅ— | ๅค็”จ็Žฐๆœ‰่„šๆœฌ | video.py, subtitle.py, llm.py, corrections.py | -| ้…็ฝฎๆ ผๅผ | YAML/JSON | ็”จๆˆทๅ‹ๅฅฝ๏ผŒๅฏ่ฏปๆ€งๅฅฝ | - ---- - -## 2. ้กน็›ฎ็ป“ๆž„ - -``` -piano-highlight-app/ -โ”œโ”€โ”€ src/ -โ”‚ โ”œโ”€โ”€ __init__.py -โ”‚ โ”œโ”€โ”€ main.py # ๅบ”็”จๅ…ฅๅฃ -โ”‚ โ”œโ”€โ”€ app.py # QMainWindow ไธป็ช—ๅฃ -โ”‚ โ”œโ”€โ”€ gui/ # GUI ็ป„ไปถ -โ”‚ โ”‚ โ”œโ”€โ”€ __init__.py -โ”‚ โ”‚ โ”œโ”€โ”€ config_panel.py # ้…็ฝฎ้ขๆฟ -โ”‚ โ”‚ โ”œโ”€โ”€ progress_view.py # ่ฟ›ๅบฆ็›‘ๆŽง -โ”‚ โ”‚ โ”œโ”€โ”€ title_editor.py # ๆ ‡้ข˜็ผ–่พ‘ๅ™จ -โ”‚ โ”‚ โ””โ”€โ”€ log_view.py # ๆ—ฅๅฟ—็ช—ๅฃ -โ”‚ โ”œโ”€โ”€ logic/ # ไธšๅŠก้€ป่พ‘ -โ”‚ โ”‚ โ”œโ”€โ”€ __init__.py -โ”‚ โ”‚ โ”œโ”€โ”€ config_manager.py # ้…็ฝฎ็ฎก็† -โ”‚ โ”‚ โ”œโ”€โ”€ pipeline_controller.py # ๆตๆฐด็บฟๆŽงๅˆถ -โ”‚ โ”‚ โ”œโ”€โ”€ state_manager.py # ็Šถๆ€็ฎก็† -โ”‚ โ”‚ โ””โ”€โ”€ worker.py # ๅŽๅฐๅทฅไฝœ็บฟ็จ‹ -โ”‚ โ””โ”€โ”€ core/ # ๆ ธๅฟƒๆจกๅ—๏ผˆๅค็”จ๏ผ‰ -โ”‚ โ”œโ”€โ”€ __init__.py -โ”‚ โ”œโ”€โ”€ constants.py # ๅธธ้‡ -โ”‚ โ”œโ”€โ”€ utils.py # ๅทฅๅ…ทๅ‡ฝๆ•ฐ -โ”‚ โ”œโ”€โ”€ video.py # ่ง†้ข‘ๅค„็† -โ”‚ โ”œโ”€โ”€ subtitle.py # ๅญ—ๅน•ๅค„็† -โ”‚ โ”œโ”€โ”€ llm.py # LLM ่ฐƒ็”จ -โ”‚ โ””โ”€โ”€ corrections.py # ็บ ้”™่ง„ๅˆ™ -โ”œโ”€โ”€ assets/ # ่ต„ๆบๆ–‡ไปถ -โ”‚ โ””โ”€โ”€ icons/ -โ”œโ”€โ”€ requirements.txt # ไพ่ต– -โ”œโ”€โ”€ pyproject.toml # ้กน็›ฎ้…็ฝฎ -โ”œโ”€โ”€ nuitka_options.py # Nuitka ๆ‰“ๅŒ…้…็ฝฎ -โ””โ”€โ”€ README.md -``` - ---- - -## 3. ๆ ธๅฟƒ็ฑป่ฎพ่ฎก - -### 3.1 StateManager๏ผˆ็Šถๆ€็ฎก็†๏ผ‰ - -```python -class StateManager: - """็Šถๆ€็ฎก็†ๅ™จ - ่ดŸ่ดฃ็Šถๆ€ๆŒไน…ๅŒ–""" - - def __init__(self, state_file: str): - self.state_file = state_file - self.state = self._load() - - def _load(self) -> dict: - """ไปŽๆ–‡ไปถๅŠ ่ฝฝ็Šถๆ€""" - - def save(self): - """ไฟๅญ˜็Šถๆ€ๅˆฐๆ–‡ไปถ""" - - def get_current_step(self) -> int: - """่Žทๅ–ๅฝ“ๅ‰ๆญฅ้ชค""" - - def set_step_status(self, step: str, status: str): - """่ฎพ็ฝฎๆญฅ้ชค็Šถๆ€ (pending/in_progress/completed/failed)""" - - def update_clip_status(self, clip_index: int, **kwargs): - """ๆ›ดๆ–ฐ clip ็Šถๆ€""" - - def get_clip_titles(self) -> list: - """่Žทๅ–ๆ‰€ๆœ‰ clip ็š„ๆ ‡้ข˜๏ผˆๅซ็”จๆˆทไฟฎๆ”น๏ผ‰""" -``` - -### 3.2 PipelineController๏ผˆๆตๆฐด็บฟๆŽงๅˆถ๏ผ‰ - -```python -class PipelineController: - """ๆตๆฐด็บฟๆŽงๅˆถๅ™จ - ็ฎก็†ๅค„็†ๆต็จ‹""" - - # ๆญฅ้ชคๅฎšไน‰ - STEPS = [ - 'ready', - 'extracting', - 'transcribing', - 'title_correcting', - 'generating_subtitles', - 'merging', - 'burning', - 'completed' - ] - - def __init__(self, config: dict, state_manager: StateManager): - self.config = config - self.state = state_manager - self.is_paused = False - self.is_stopped = False - - def run(self, worker: Worker): - """่ฟ่กŒๆตๆฐด็บฟ""" - - def pause(self): - """ๆš‚ๅœๆตๆฐด็บฟ""" - - def resume(self): - """ๆขๅคๆตๆฐด็บฟ""" - - def stop(self): - """ๅœๆญขๆตๆฐด็บฟ""" - - def step_extracting(self): - """Step 1: ๆๅ–็‰‡ๆฎต""" - - def step_transcribing(self): - """Step 2: ่ฝฌๅฝ•""" - - def step_title_correcting(self) -> list: - """Step 3: ๆ ‡้ข˜็บ ๆญฃ - ่ฟ”ๅ›ž้œ€่ฆ็”จๆˆท็กฎ่ฎค็š„ๆ ‡้ข˜""" - # ่ฟ”ๅ›žๆ ‡้ข˜ๅˆ—่กจ๏ผŒ็”จๆˆทๅฏไปฅๅœจๆญคไป‹ๅ…ฅไฟฎๆ”น - - def step_generating_subtitles(self): - """Step 4: ็”Ÿๆˆๅญ—ๅน•""" - - def step_merging(self): - """Step 5: ๅˆๅนถ่ง†้ข‘""" - - def step_burning(self): - """Step 6: ็ƒงๅฝ•ๅญ—ๅน•""" -``` - -### 3.3 Worker๏ผˆๅŽๅฐๅทฅไฝœ็บฟ็จ‹๏ผ‰ - -```python -class Worker(QThread): - """ๅŽๅฐๅทฅไฝœ็บฟ็จ‹ - ๅœจ็‹ฌ็ซ‹็บฟ็จ‹ไธญๆ‰ง่กŒๆตๆฐด็บฟ""" - - progress_signal = pyqtSignal(str, int, str) # step, percent, message - clip_completed_signal = pyqtSignal(int) # clip_index - step_completed_signal = pyqtSignal(str) # step_name - titles_ready_signal = pyqtSignal(list) # ๆ ‡้ข˜ๅˆ—่กจ๏ผŒ็ญ‰ๅพ…็”จๆˆท็กฎ่ฎค - finished_signal = pyqtSignal(bool, str) # success, message - log_signal = pyqtSignal(str) # ๆ—ฅๅฟ—ๆถˆๆฏ - - def __init__(self, controller: PipelineController): - super().__init__() - self.controller = controller - - def run(self): - """ๆ‰ง่กŒๆตๆฐด็บฟ๏ผˆๅฏๆš‚ๅœ๏ผ‰""" - - def request_pause(self): - """่ฏทๆฑ‚ๆš‚ๅœ๏ผˆ็”ฑ UI ่ฐƒ็”จ๏ผ‰""" -``` - -### 3.4 ConfigPanel๏ผˆ้…็ฝฎ้ขๆฟ๏ผ‰ - -```python -class ConfigPanel(QWidget): - """้…็ฝฎ้ขๆฟ""" - - config_changed_signal = pyqtSignal(dict) - - def __init__(self): - super().__init__() - self._init_ui() - - def _init_ui(self): - """ๅˆๅง‹ๅŒ– UI""" - # API ้…็ฝฎ็ป„ - # - API Host (QLineEdit) - # - API Key (QLineEdit, ๅฏ†็ ๆจกๅผ) - # - ๆจกๅž‹้€‰ๆ‹ฉ (QComboBox) - - # ่ง†้ข‘้…็ฝฎ็ป„ - # - ่ง†้ข‘ๆ–‡ไปถ้€‰ๆ‹ฉ (QLineEdit + QPushButton) - # - ่พ“ๅ‡บ็›ฎๅฝ•้€‰ๆ‹ฉ (QLineEdit + QPushButton) - - # Whisper ้…็ฝฎ็ป„ - # - ๆจกๅž‹้€‰ๆ‹ฉ (QComboBox: base/small/medium/large) - # - ๆจกๅž‹่ทฏๅพ„ (QLineEdit) - - def load_config(self, config: dict): - """ๅŠ ่ฝฝ้…็ฝฎๅˆฐ UI""" - - def get_config(self) -> dict: - """ไปŽ UI ่Žทๅ–้…็ฝฎ""" - - def validate(self) -> tuple: - """้ชŒ่ฏ้…็ฝฎๆœ‰ๆ•ˆๆ€ง""" - # ่ฟ”ๅ›ž (is_valid, error_message) -``` - -### 3.5 ProgressView๏ผˆ่ฟ›ๅบฆ่ง†ๅ›พ๏ผ‰ - -```python -class ProgressView(QWidget): - """่ฟ›ๅบฆ็›‘ๆŽง่ง†ๅ›พ""" - - def __init__(self): - super().__init__() - self._init_ui() - - def _init_ui(self): - """ๅˆๅง‹ๅŒ– UI""" - # ๅฝ“ๅ‰ๆญฅ้ชคๆ ‡็ญพ (QLabel) - # ๆ•ดไฝ“่ฟ›ๅบฆๆก (QProgressBar) - # Clip ่ฟ›ๅบฆ (QLabel: "Clip 3/14") - # ๆ—ฅๅฟ—ๆ–‡ๆœฌๆก† (QTextEdit, ๅช่ฏป) - # ๆŽงๅˆถๆŒ‰้’ฎ (ๅผ€ๅง‹/ๆš‚ๅœ/ๅœๆญข/็ปง็ปญ) - - def update_progress(self, step: str, percent: int, message: str): - """ๆ›ดๆ–ฐ่ฟ›ๅบฆๆ˜พ็คบ""" - - def append_log(self, message: str): - """่ฟฝๅŠ ๆ—ฅๅฟ—""" - - def set_clip_progress(self, current: int, total: int): - """่ฎพ็ฝฎ Clip ่ฟ›ๅบฆ""" - - def enable_controls(self, can_start: bool, can_pause: bool, can_stop: bool, can_resume: bool): - """่ฎพ็ฝฎๆŽงๅˆถๆŒ‰้’ฎ็Šถๆ€""" -``` - -### 3.6 ContentEditor๏ผˆๅ†…ๅฎน็ผ–่พ‘ๅ™จ - ไบบๅทฅไป‹ๅ…ฅ็‚น๏ผ‰ - -```python -class ContentEditor(QWidget): - """ๅ†…ๅฎน็ผ–่พ‘ๅ™จ - ็”จไบŽไบบๅทฅไป‹ๅ…ฅไฟฎๆ”นๆ ‡้ข˜ๅ’Œๅญ—ๅน•ๅ†…ๅฎน""" - - content_confirmed_signal = pyqtSignal(dict) # ็”จๆˆท็กฎ่ฎค็š„ๅ†…ๅฎน {clip_index: {title, subtitles}} - - def __init__(self): - super().__init__() - self._init_ui() - - def _init_ui(self): - """ๅˆๅง‹ๅŒ– UI""" - # ๆ ‡็ญพ้กต๏ผšๆ ‡้ข˜็ผ–่พ‘ / ๅญ—ๅน•็ผ–่พ‘ - # ๆ ‡้ข˜็ผ–่พ‘๏ผš - # - Clip # | ๅŽŸๅง‹ๆ ‡้ข˜ | LLMๅปบ่ฎฎ | ็”จๆˆทไฟฎๆ”น | ๆ“ไฝœ - # - ็ผ–่พ‘ๆŒ‰้’ฎ (QPushButton) - # ๅญ—ๅน•็ผ–่พ‘๏ผš - # - ๆŒ‰Clipๅˆ†้กต๏ผŒๆฏไธชClipๆ˜พ็คบๅ…ถๅญ—ๅน•ๅ†…ๅฎน - # - ๆฏไธชๅญ—ๅน•ๆฎตๅฏ็ผ–่พ‘๏ผˆๅŽŸๅง‹ๆ–‡ๆœฌ โ†’ ็บ ๆญฃๅŽๆ–‡ๆœฌ โ†’ ็”จๆˆทไฟฎๆ”น๏ผ‰ - # ็กฎ่ฎคๆŒ‰้’ฎ (QPushButton) - - def set_content(self, clips_data: dict): - """่ฎพ็ฝฎๅ†…ๅฎนไพ›็”จๆˆท็ผ–่พ‘ - clips_data: { - clip_index: { - 'title': {...}, - 'subtitles': [...] - } - } - """ - - def get_user_content(self) -> dict: - """่Žทๅ–็”จๆˆทไฟฎๆ”นๅŽ็š„ๅ†…ๅฎน""" - - def edit_clip_title(self, clip_index: int): - """็ผ–่พ‘ๅ•ไธชClip็š„ๆ ‡้ข˜ - ๅผนๅ‡บๅฏน่ฏๆก†""" - - def edit_clip_subtitle(self, clip_index: int, subtitle_index: int): - """็ผ–่พ‘ๅ•ไธชๅญ—ๅน•ๆฎต - ๅผนๅ‡บๅฏน่ฏๆก†""" -``` - -### 3.7 SubtitleSegmentEditor๏ผˆๅญ—ๅน•ๆฎต็ผ–่พ‘ๅ™จ๏ผ‰ - -```python -class SubtitleSegmentEditor(QWidget): - """ๅ•ไธชๅญ—ๅน•็‰‡ๆฎต็š„็ผ–่พ‘ๅ™จ""" - - def __init__(self, segment_data: dict, parent=None): - super().__init__(parent) - # ๆ˜พ็คบ๏ผšๆ—ถ้—ด่Œƒๅ›ดใ€ๅŽŸๅง‹ๆ–‡ๆœฌใ€่ง„ๅˆ™็บ ๆญฃๅŽใ€LLM็บ ๆญฃๅŽใ€็”จๆˆทๅฏ็ผ–่พ‘ - - def get_corrected_text(self) -> str: - """่Žทๅ–็”จๆˆทไฟฎๆ”นๅŽ็š„ๆ–‡ๆœฌ""" -``` - ---- - -## 4. ๆตๆฐด็บฟ็Šถๆ€ๆœบ - -``` - โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” - โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–บโ”‚ Ready โ”‚โ—„โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” - โ”‚ โ””โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”˜ โ”‚ - โ”‚ โ”‚ start() โ”‚ reset() - โ”‚ โ–ผ โ”‚ - โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ - โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”‚Extractingโ”‚โ”€โ”€โ”€โ”€โ”€โ” โ”‚ - โ”‚ โ”‚ โ””โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”˜ โ”‚ โ”‚ - โ”‚ โ”‚ pause โ”‚ completed โ”‚ โ”‚ - โ”‚ โ”‚ โ–ผ โ”‚ โ”‚ - โ”‚ โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ โ”‚ - โ”‚ โ””โ”€โ”€โ–บโ”‚Transcribingโ”‚โ—„โ”€โ”€โ”€โ”€โ”˜ โ”‚ - โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ - โ”‚ pause โ”‚ โ”‚ completed โ”‚ - โ”‚ โ–ผ โ”‚ - โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ - โ”‚ โ”‚Title Correcting โ”‚โ—„โ”€โ”€โ” โ”‚ ไบบๅทฅไป‹ๅ…ฅ็‚น - โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ โ”‚ ็”จๆˆทๅฏๆš‚ๅœ - โ”‚ โ”‚ completed โ”‚ โ”‚ ไฟฎๆ”นๆ ‡้ข˜ - โ”‚ โ–ผ โ”‚ โ”‚ - โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ โ”‚ - โ”‚ โ”‚Generating Subtitlesโ”‚โ”€โ”€โ”€โ”˜ โ”‚ - โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ - โ”‚ pause โ”‚ โ”‚ completed โ”‚ - โ”‚ โ–ผ โ”‚ - โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ - โ”‚ โ”‚ Merging โ”‚โ—„โ”€โ”€โ”˜ โ”‚ - โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ - โ”‚ pauseโ”‚ โ”‚ completed โ”‚ - โ”‚ โ–ผ โ”‚ - โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ - โ”‚ โ”‚ Burning โ”‚โ—„โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ - โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”˜ - โ”‚ pauseโ”‚ โ”‚ completed - โ”‚ โ–ผ - โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” - โ””โ”€โ”€โ”€โ”€โ”‚ Completed โ”‚ - โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ -``` - ---- - -## 5. ไฟกๅทๆต่ฎพ่ฎก - -``` -โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” -โ”‚ UI Layer โ”‚ -โ”‚ โ”‚ -โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ -โ”‚ โ”‚ ConfigPanel โ”‚ โ”‚ ProgressView โ”‚ โ”‚ TitleEditor โ”‚ โ”‚ -โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ -โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ -โ”‚ โ”‚ config_changed โ”‚ titles_ready โ”‚ titles_confirmed โ”‚ -โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ - โ”‚ โ”‚ โ”‚ - โ–ผ โ–ผ โ–ผ -โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” -โ”‚ Controller Layer โ”‚ -โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ -โ”‚ โ”‚ PipelineController โ”‚ โ”‚ -โ”‚ โ”‚ - manage_workflow() โ”‚ โ”‚ -โ”‚ โ”‚ - handle_pause() / handle_resume() โ”‚ โ”‚ -โ”‚ โ”‚ - collect_user_titles() โ”‚ โ”‚ -โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ -โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ - โ”‚ - โ–ผ -โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” -โ”‚ Worker Thread โ”‚ -โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ -โ”‚ โ”‚ Worker (QThread) โ”‚ โ”‚ -โ”‚ โ”‚ - runs pipeline steps โ”‚ โ”‚ -โ”‚ โ”‚ - emits progress/titles signals โ”‚ โ”‚ -โ”‚ โ”‚ - respects pause/stop flags โ”‚ โ”‚ -โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ -โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ -``` - -### ไฟกๅทๅฎšไน‰ - -| ไฟกๅท | ๆ–นๅ‘ | ๅ‚ๆ•ฐ | ่ฏดๆ˜Ž | -|------|------|------|------| -| `config_changed` | UI โ†’ Controller | dict | ้…็ฝฎๅ˜ๆ›ด | -| `start_pipeline` | Controller โ†’ Worker | dict, StateManager | ๅฏๅŠจๆตๆฐด็บฟ | -| `progress_signal` | Worker โ†’ UI | step, percent, message | ่ฟ›ๅบฆๆ›ดๆ–ฐ | -| `titles_ready_signal` | Worker โ†’ UI | list | ๆ ‡้ข˜ๅˆ—่กจๅ‡†ๅค‡ๅฅฝ๏ผŒ็ญ‰ๅพ…็กฎ่ฎค | -| `titles_confirmed_signal` | UI โ†’ Controller | list | ็”จๆˆท็กฎ่ฎค็š„ๆ ‡้ข˜ | -| `pause_pipeline` | UI โ†’ Worker | - | ่ฏทๆฑ‚ๆš‚ๅœ | -| `resume_pipeline` | UI โ†’ Worker | - | ่ฏทๆฑ‚ๆขๅค | -| `stop_pipeline` | UI โ†’ Worker | - | ่ฏทๆฑ‚ๅœๆญข | - ---- - -## 6. ็Šถๆ€ๆ–‡ไปถๆ ผๅผ - -```json -{ - "version": 1, - "app_version": "1.0.0", - "project_name": "็ฆ็”ฐๅคœๆ ก-03ๆœˆ18ๆ—ฅ", - "created_at": "2026-05-02T10:00:00", - "updated_at": "2026-05-02T10:30:00", - - "config": { - "video_src": "D:/path/to/video.mp4", - "output_dir": "D:/path/to/output", - "api_key": "xxx", - "api_host": "https://ark.cn-beijing.volces.com/api/coding/v3", - "whisper_model": "large", - "whisper_model_path": "D:/AI/LM-Models/faster-whisper/large-v3", - "video_params": { - "fade_duration": 1, - "title_fontsize": 90, - "title_color": "FFFF00", - "subtitle_fontsize": 24, - "subtitle_color": "FFFFFF" - } - }, - - "pipeline": { - "current_step": 3, - "steps": { - "extracting": {"status": "completed", "started_at": "...", "completed_at": "..."}, - "transcribing": {"status": "completed", "started_at": "...", "completed_at": "..."}, - "title_correcting": {"status": "in_progress", "started_at": "...", "completed_at": null}, - "generating_subtitles": {"status": "pending", "started_at": null, "completed_at": null}, - "merging": {"status": "pending", "started_at": null, "completed_at": null}, - "burning": {"status": "pending", "started_at": null, "completed_at": null} - } - }, - - "clips": [ - { - "index": 1, - "title_original": "ๅผนๅฅ", - "title_llm": "ๅผนๅฅ", - "title_user": null, - "title_final": "ๅผนๅฅ", - "start": 412, - "end": 442, - "status": "completed", - "clip_path": "intermediates/clip1_fade.mp4", - "json_path": "intermediates/clip1.json", - "transcription_completed_at": "..." - } - ], - - "outputs": { - "subtitle_title_path": "subs/v1_title.srt", - "subtitle_content_path": "subs/v1_content.srt", - "merged_video_path": "concat_merged.mp4", - "final_video_path": "v1_final.mp4" - } -} -``` - ---- - -## 7. ้”™่ฏฏๅค„็†็ญ–็•ฅ - -| ้”™่ฏฏ็ฑปๅž‹ | ๅค„็†ๆ–นๅผ | -|----------|----------| -| ้…็ฝฎๆ— ๆ•ˆ | ้˜ปๆญขๅผ€ๅง‹๏ผŒๆ็คบ็”จๆˆทไฟฎๆญฃ | -| API ่ฐƒ็”จๅคฑ่ดฅ | ้‡่ฏ• 3 ๆฌก๏ผŒไปๅคฑ่ดฅๅˆ™ๆš‚ๅœๆตๆฐด็บฟ๏ผŒ็ญ‰ๅพ…็”จๆˆทๅค„็† | -| ่ง†้ข‘ๆ–‡ไปถไธๅญ˜ๅœจ | ๆš‚ๅœ๏ผŒๆ็คบ็”จๆˆท้€‰ๆ‹ฉๅ…ถไป–ๆ–‡ไปถ | -| ็ฃ็›˜็ฉบ้—ดไธ่ถณ | ๆš‚ๅœ๏ผŒๆ็คบ็”จๆˆทๆธ…็†็ฉบ้—ด | -| ๅค„็†ๅผ‚ๅธธๅดฉๆบƒ | ็Šถๆ€ๅทฒๆŒไน…ๅŒ–๏ผŒ้‡ๅฏๅŽๅฏๆขๅค | -| ็”จๆˆทๅ–ๆถˆ | ไฟๅญ˜ๅฝ“ๅ‰่ฟ›ๅบฆ๏ผŒๆธ…็†ไธดๆ—ถๆ–‡ไปถ๏ผˆๅฏ้€‰๏ผ‰ | - ---- - -## 8. ๆ‰“ๅŒ…้…็ฝฎ (Nuitka) - -```python -# nuitka_options.py -import nuitka - -nuitka.compile( - script="src/main.py", - mode="standalone", - output_dir="dist", - windows_icon="assets/icon.ico", - include_qt_plugins=["qt_plugins/styles", "qt_plugins/imageformats"], - data_files=[ - ("assets/icons", "assets/icons"), - ], - remove_output_dir=True, - onefile=True, # ๆ‰“ๅŒ…ๆˆๅ•ไธช exe - company_name="Piano Tools", - product_name="Piano Highlight Generator", - product_version="1.0.0", -) -``` - ---- - -## 9. ไพ่ต–ๆธ…ๅ• - -``` -PySide6>=6.6.0 -pyyaml>=6.0 -requests>=2.31.0 -pypinyin>=0.50.0 -faster-whisper>=1.0.0 # ๅฏ้€‰๏ผŒๅฆ‚้œ€ๆœฌๅœฐ่ฝฌๅฝ• -``` - ---- - -## 10. ๅผ€ๅ‘ไผ˜ๅ…ˆ็บง - -| ไผ˜ๅ…ˆ็บง | ๆจกๅ— | ๅทฅๆœŸไผฐ่ฎก | -|--------|------|----------| -| P0 | ้กน็›ฎ้ชจๆžถ + ConfigPanel + StateManager | 2h | -| P0 | ProgressView + Worker ้›†ๆˆ | 2h | -| P0 | PipelineController ๆ ธๅฟƒ้€ป่พ‘ | 2h | -| P1 | TitleEditor ๆ ‡้ข˜็ผ–่พ‘ๅ™จ | 1.5h | -| P2 | ๅฎŒๅ–„้”™่ฏฏๅค„็†ๅ’Œ่พน็•Œๆƒ…ๅ†ต | 1h | -| P2 | ๆ‰“ๅŒ…้…็ฝฎ + ๆต‹่ฏ• | 1.5h | -| P3 | README ๅ’Œ็”จๆˆทๆ–‡ๆกฃ | 0.5h | - -**ๆ€ปๅทฅๆœŸไผฐ่ฎก๏ผš็บฆ 10 ๅฐๆ—ถ** diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md index d5e28e3..35cbf6e 100644 --- a/docs/ARCHITECTURE.md +++ b/docs/ARCHITECTURE.md @@ -1,90 +1,92 @@ # ๆžถๆž„่ฎพ่ฎก -## 1. ๆŠ€ๆœฏๆ ˆ +## 1. ๆ ธๅฟƒๅŽŸๅˆ™ -| ๅฑ‚็บง | ๆŠ€ๆœฏ | ้€‰ๅž‹็†็”ฑ | -|------|------|----------| -| GUI ๆก†ๆžถ | PySide6 (Qt for Python) | LGPL ่ฎธๅฏ๏ผŒๅŠŸ่ƒฝๅฎŒๅค‡๏ผŒไฟกๅทๆงฝๆœบๅˆถ้€‚ๅˆๅผ‚ๆญฅๆ›ดๆ–ฐ | -| ๆ‰“ๅŒ…ๅทฅๅ…ท | Nuitka | ็ผ–่ฏ‘ไธบ C๏ผŒๆ€ง่ƒฝๅฅฝ๏ผŒไฝ“็งฏๅฐ | -| ็Šถๆ€ๆŒไน…ๅŒ– | JSON ๆ–‡ไปถ | ็ฎ€ๅ•๏ผŒๆ— ้œ€ๆ•ฐๆฎๅบ“ไพ่ต– | -| ๆ ธๅฟƒๆจกๅ— | ๅค็”จ็Žฐๆœ‰่„šๆœฌ | video.py, subtitle.py, llm.py, corrections.py | -| ้…็ฝฎๆ ผๅผ | YAML/JSON | ็”จๆˆทๅ‹ๅฅฝ๏ผŒๅฏ่ฏปๆ€งๅฅฝ | +**CLI ๅ’Œ GUI ๅ…ฑ็”จๅŒไธ€ๅฅ—ๅบ•ๅฑ‚็ฑปๅบ“**๏ผŒไป…ๅœจ่กจ็คบๅฑ‚ๆœ‰ๅทฎๅผ‚๏ผš +- **CLI**๏ผšๅ‘ฝไปค่กŒๅ‚ๆ•ฐ่พ“ๅ…ฅ๏ผŒๆ—ฅๅฟ—่พ“ๅ‡บๅˆฐ็ปˆ็ซฏ +- **GUI**๏ผšPySide6 ็•Œ้ข๏ผŒๅ‚ๆ•ฐ่พ“ๅ…ฅ็•Œ้ขๅŒ–๏ผŒๆ—ฅๅฟ—่พ“ๅ‡บๅˆฐๆ–‡ๆœฌๅŒบ ## 2. ้กน็›ฎ็ป“ๆž„ ``` -piano-highlight-app/ +lesson-highlights/ โ”œโ”€โ”€ src/ -โ”‚ โ”œโ”€โ”€ __init__.py -โ”‚ โ”œโ”€โ”€ main.py # ๅบ”็”จๅ…ฅๅฃ -โ”‚ โ”œโ”€โ”€ app.py # QMainWindow ไธป็ช—ๅฃ -โ”‚ โ”œโ”€โ”€ gui/ # GUI ็ป„ไปถ -โ”‚ โ”‚ โ”œโ”€โ”€ __init__.py -โ”‚ โ”‚ โ”œโ”€โ”€ config_panel.py # ้…็ฝฎ้ขๆฟ -โ”‚ โ”‚ โ”œโ”€โ”€ progress_view.py # ่ฟ›ๅบฆ็›‘ๆŽง -โ”‚ โ”‚ โ”œโ”€โ”€ title_editor.py # ๆ ‡้ข˜็ผ–่พ‘ๅ™จ -โ”‚ โ”‚ โ””โ”€โ”€ log_view.py # ๆ—ฅๅฟ—็ช—ๅฃ -โ”‚ โ”œโ”€โ”€ logic/ # ไธšๅŠก้€ป่พ‘ -โ”‚ โ”‚ โ”œโ”€โ”€ __init__.py -โ”‚ โ”‚ โ”œโ”€โ”€ config_manager.py # ้…็ฝฎ็ฎก็† -โ”‚ โ”‚ โ”œโ”€โ”€ pipeline_controller.py # ๆตๆฐด็บฟๆŽงๅˆถ -โ”‚ โ”‚ โ”œโ”€โ”€ state_manager.py # ็Šถๆ€็ฎก็† -โ”‚ โ”‚ โ””โ”€โ”€ worker.py # ๅŽๅฐๅทฅไฝœ็บฟ็จ‹ -โ”‚ โ””โ”€โ”€ core/ # ๆ ธๅฟƒๆจกๅ—๏ผˆๅค็”จ๏ผ‰ +โ”‚ โ”œโ”€โ”€ main.py # GUI ๅ…ฅๅฃ +โ”‚ โ”œโ”€โ”€ gui.py # GUI๏ผˆๅ‚ๆ•ฐ่พ“ๅ…ฅ โ†’ ่ฐƒ็”จๅบ•ๅฑ‚๏ผ‰ +โ”‚ โ”œโ”€โ”€ cli.py # CLI ๅ…ฅๅฃ +โ”‚ โ””โ”€โ”€ core/ # ๅ…ฑไบซๅบ•ๅฑ‚ โ”‚ โ”œโ”€โ”€ __init__.py -โ”‚ โ”œโ”€โ”€ constants.py # ๅธธ้‡ -โ”‚ โ”œโ”€โ”€ utils.py # ๅทฅๅ…ทๅ‡ฝๆ•ฐ -โ”‚ โ”œโ”€โ”€ video.py # ่ง†้ข‘ๅค„็† -โ”‚ โ”œโ”€โ”€ subtitle.py # ๅญ—ๅน•ๅค„็† -โ”‚ โ”œโ”€โ”€ llm.py # LLM ่ฐƒ็”จ -โ”‚ โ””โ”€โ”€ corrections.py # ็บ ้”™่ง„ๅˆ™ -โ”œโ”€โ”€ assets/ # ่ต„ๆบๆ–‡ไปถ -โ”‚ โ””โ”€โ”€ icons/ -โ”œโ”€โ”€ requirements.txt # ไพ่ต– -โ”œโ”€โ”€ pyproject.toml # ้กน็›ฎ้…็ฝฎ -โ”œโ”€โ”€ nuitka_options.py # Nuitka ๆ‰“ๅŒ…้…็ฝฎ -โ””โ”€โ”€ README.md +โ”‚ โ”œโ”€โ”€ ppt_parser.py # PPT ่งฃๆž + LLM clips ๆๅ– +โ”‚ โ”œโ”€โ”€ pipeline.py # ่ง†้ข‘ๅค„็†ๆตๆฐด็บฟ +โ”‚ โ”œโ”€โ”€ subtitle.py # ๅญ—ๅน•็”Ÿๆˆ +โ”‚ โ”œโ”€โ”€ video.py # ่ง†้ข‘ๅค„็†๏ผˆๆๅ–/ๅˆๅนถ/็ƒงๅฝ•๏ผ‰ +โ”‚ โ”œโ”€โ”€ llm.py # LLM ่ฐƒ็”จ +โ”‚ โ”œโ”€โ”€ corrections.py # ๆœฏ่ฏญ็บ ๆญฃ +โ”‚ โ”œโ”€โ”€ constants.py # ๅธธ้‡้…็ฝฎ +โ”‚ โ””โ”€โ”€ errors.py # ้”™่ฏฏๅค„็† +โ”œโ”€โ”€ config.ini # API ้…็ฝฎ๏ผˆไธๆไบค git๏ผ‰ +โ”œโ”€โ”€ config.ini.example # ้…็ฝฎๆจกๆฟ +โ”œโ”€โ”€ start.bat # GUI ๅฏๅŠจๅ™จ +โ”œโ”€โ”€ run.bat # ้€š็”จ CLI ๅฏๅŠจๅ™จ +โ””โ”€โ”€ run_lesson1.bat # ้ข„่ฎพ่ฏพ็จ‹็คบไพ‹ ``` -## 3. ๆ ธๅฟƒ็ฑป่ฎพ่ฎก +## 3. ๆ ธๅฟƒๆจกๅ— -### StateManager๏ผˆ็Šถๆ€็ฎก็†๏ผ‰ +### `parse_ppt_to_config()` -่ดŸ่ดฃ็Šถๆ€ๆŒไน…ๅŒ–๏ผŒๆ”ฏๆŒๆš‚ๅœ/ๆขๅคใ€‚ +ไธ€้”ฎๅฎŒๆˆ PPT โ†’ clips ้…็ฝฎ็š„ๅฎŒๆ•ดๆต็จ‹๏ผš -### PipelineController๏ผˆๆตๆฐด็บฟๆŽงๅˆถ๏ผ‰ +1. **PPT ่งฃๆž**๏ผšๆๅ–ๆ–‡ๆœฌๅ’Œ็Ÿฅ่ฏ†็‚น +2. **Whisper ่ฝฌๅฝ•**๏ผš่ง†้ข‘ โ†’ `full_transcript.json` +3. **LLM ๆ กๆญฃ**๏ผšๆ‰น้‡ๆ กๆญฃ โ†’ `corrected_transcript.json` +4. **LLM ๆๅ–็‰‡ๆฎต**๏ผšๆ นๆฎ็Ÿฅ่ฏ†็‚นๅฎšไฝ่ง†้ข‘็‰‡ๆฎต โ†’ clips +5. **้‡ๅ ๅˆๅนถ**๏ผšๅˆๅนถ้‡ๅ ็‰‡ๆฎต -็ฎก็†ๅค„็†ๆต็จ‹็š„ 6 ไธชๆญฅ้ชค๏ผš -1. extract - ็‰‡ๆฎตๆๅ– -2. transcribe - ่ฏญ้Ÿณ่ฝฌๅฝ• -3. title_correct - ๆ ‡้ข˜็”ŸๆˆไธŽ็บ ้”™ -4. generate_subtitles - ๅญ—ๅน•็”Ÿๆˆ -5. merge - ็‰‡ๆฎตๅˆๅนถ -6. burn - ๅญ—ๅน•็ƒงๅฝ• +### `Pipeline` -### Worker๏ผˆๅŽๅฐๅทฅไฝœ็บฟ็จ‹๏ผ‰ +่ง†้ข‘ๅค„็†ๆตๆฐด็บฟ๏ผš -ๅœจ็‹ฌ็ซ‹็บฟ็จ‹ไธญๆ‰ง่กŒๆตๆฐด็บฟ๏ผŒ้€š่ฟ‡ไฟกๅทไธŽ UI ้€šไฟกใ€‚ +1. **extract**๏ผšๆŒ‰ๆ—ถ้—ดๆˆณๆๅ–็‰‡ๆฎต +2. **transcribe**๏ผš้€็‰‡ๆฎต Whisper ่ฝฌๅฝ• +3. **correct_titles**๏ผšLLM ๆ ‡้ข˜็บ ๆญฃ +4. **generate_subtitles**๏ผš็”ŸๆˆๅŒ่ฝจๅญ—ๅน• +5. **merge**๏ผšๅˆๅนถ็‰‡ๆฎต +6. **burn**๏ผš็ƒงๅฝ•ๅญ—ๅน• -## 4. ๆตๆฐด็บฟ็Šถๆ€ๆœบ +## 4. ๆ•ฐๆฎๆต ``` -Ready โ†’ Extracting โ†’ Transcribing โ†’ Title Correcting โ†’ Generating Subtitles โ†’ Merging โ†’ Burning โ†’ Completed - โ†‘ โ†“ - โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ ็”จๆˆทๅฏๆš‚ๅœๅนถ็ผ–่พ‘ๆ ‡้ข˜ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ +่ง†้ข‘ + PPT + โ†“ +parse_ppt_to_config() + โ†“ +config = { + "video_src": ..., + "clips": [{"title": ..., "start": ..., "end": ...}, ...], + "output_dir": ..., + "term_corrections": {...} +} + โ†“ +Pipeline(config).run() + โ†“ +final.mp4 ``` -## 5. ไฟกๅทๆต +## 5. ้…็ฝฎๆฅๆบ -| ไฟกๅท | ๆ–นๅ‘ | ่ฏดๆ˜Ž | -|------|------|------| -| config_changed | UI โ†’ Controller | ้…็ฝฎๅ˜ๆ›ด | -| progress_signal | Worker โ†’ UI | ่ฟ›ๅบฆๆ›ดๆ–ฐ | -| titles_ready_signal | Worker โ†’ UI | ๆ ‡้ข˜ๅˆ—่กจๅ‡†ๅค‡ๅฅฝ | -| titles_confirmed_signal | UI โ†’ Controller | ็”จๆˆท็กฎ่ฎค็š„ๆ ‡้ข˜ | +API ้…็ฝฎ็ปŸไธ€ไปŽ `config.ini` ่ฏปๅ–๏ผŒไธ็กฌ็ผ–็ ๅœจไปฃ็ ไธญใ€‚ -## 6. ็Šถๆ€ๆ–‡ไปถๆ ผๅผ +CLI ๆ”ฏๆŒๅ‚ๆ•ฐ่ฆ†็›–๏ผš +- `--api-key` +- `--api-host` +- `--verbose` -JSON ๆ ผๅผ๏ผŒๅŒ…ๅซ้…็ฝฎใ€ๆตๆฐด็บฟ็Šถๆ€ใ€clips ๅˆ—่กจ็ญ‰ใ€‚ +## 6. ็Šถๆ€ๆŒไน…ๅŒ– -่ฏฆ่ง design.mdใ€‚ \ No newline at end of file +ไธญ้—ด็ป“ๆžœไฟๅญ˜ๅœจ `output/intermediates/`๏ผš +- `full_transcript.json` - ๅŽŸๅง‹่ฝฌๅฝ• +- `corrected_transcript.json` - LLM ๆ กๆญฃๅŽ +- `ppt_knowledge_and_cleaned.json` - PPT ็Ÿฅ่ฏ†็‚นๅ’Œๆธ…็†ๅŽๆ–‡ๆœฌ + +ๅค็”จๆ—ถๆฃ€ๆต‹ checkpoint๏ผŒ้ฟๅ…้‡ๅค LLM ่ฐƒ็”จใ€‚ \ No newline at end of file diff --git a/proposal.md b/proposal.md deleted file mode 100644 index 69d76eb..0000000 --- a/proposal.md +++ /dev/null @@ -1,288 +0,0 @@ -# Piano Highlight Generator App - ้œ€ๆฑ‚ๆๆกˆ - -> ๆๆกˆๆ—ฅๆœŸ๏ผš2026-05-02 -> ๆๆกˆไบบ๏ผšAI Dev Team -> ็Šถๆ€๏ผšDraft - ---- - -## 1. ้—ฎ้ข˜ๆ่ฟฐ - -### 1.1 ็Žฐ็Šถ - -ๅฝ“ๅ‰้’ข็ด่ฏพ็ฒพๅŽ่ง†้ข‘็”Ÿๆˆๅ™จๆ˜ฏ Python CLI ่„šๆœฌ้›†ๅˆ๏ผš -- `generate_highlights.py` - ๅ‘ฝไปค่กŒ็จ‹ๅบ๏ผŒไธ€ๆฌกๆ€งๆ‰ง่กŒๅฎŒๆ•ดๆต็จ‹ -- ๆ— ๅ›พๅฝข็•Œ้ข๏ผŒ้œ€่ฆๆ‰‹ๅŠจ็ผ–่พ‘ YAML ้…็ฝฎๆ–‡ไปถ -- ๆ— ็Šถๆ€ไฟๆŒ๏ผŒ็จ‹ๅบไธญๆ–ญๅช่ƒฝไปŽๅคดๅผ€ๅง‹ -- ๆ— ไบบๅทฅไป‹ๅ…ฅ็‚น๏ผŒๆ— ๆณ•ๅœจๅค„็†่ฟ‡็จ‹ไธญไฟฎๆ”นๆ ‡้ข˜ - -### 1.2 ็”จๆˆท้œ€ๆฑ‚ - -็”จๆˆทๅธŒๆœ›ๅฐ†ๅ…ถ้‡ๆž„ไธบ**ๅฏๅˆ†ๅ‘็š„ๆกŒ้ขๅบ”็”จ**๏ผŒๅ…ทๅค‡๏ผš - -| ้œ€ๆฑ‚ | ่ฏดๆ˜Ž | -|------|------| -| **้…็ฝฎ็•Œ้ข** | ๅ›พๅฝขๅŒ–้…็ฝฎ API ๅคงๆจกๅž‹ๅ‚ๆ•ฐ๏ผŒๆ— ้œ€็ผ–่พ‘ YAML | -| **ๅ…จ่ฟ‡็จ‹็›‘ๆŽง** | ๅฎžๆ—ถๆ˜พ็คบๆฏไธชๅค„็†ๆญฅ้ชค็š„็Šถๆ€ | -| **็Šถๆ€ไฟๆŒ** | ๅฏๅœจไปปๆ„ๆญฅ้ชคๆš‚ๅœ๏ผŒ็„ถๅŽ็ปง็ปญๅพ€ไธ‹่ฟ›่กŒ | -| **ไบบๅทฅไป‹ๅ…ฅ** | ๆš‚ๅœๆ—ถๅฏๆ‰‹ๅŠจไฟฎๆ”นๆ ‡้ข˜๏ผŒ็„ถๅŽๅ†ๆœ€ๅŽ็ƒงๅˆถ | - ---- - -## 2. ็”จๆˆทๆ•…ไบ‹ - -### 2.1 ไฝœไธบ็”จๆˆท๏ผŒๆˆ‘ๆƒณ่ฆ... - -**Story 1: ้…็ฝฎ็ฎก็†** -- ๆ‰“ๅผ€ๅบ”็”จๅŽ๏ผŒ่ƒฝ็œ‹ๅˆฐๆธ…ๆ™ฐ็š„้…็ฝฎ็•Œ้ข -- ๅฏไปฅ้€‰ๆ‹ฉ/ๅˆ‡ๆขไธๅŒ็š„ LLM API๏ผˆๅฝ“ๅ‰ๆ˜ฏ็ซๅฑฑๆ–น่ˆŸ๏ผŒๅฏๆ‰ฉๅฑ•๏ผ‰ -- ๅฏไปฅ้€‰ๆ‹ฉ Whisper ๆจกๅž‹๏ผˆ็”จไบŽ้Ÿณ้ข‘่ฝฌๅฝ•๏ผ‰ -- ้…็ฝฎๅฏไปฅไฟๅญ˜ๅ’ŒๅŠ ่ฝฝ - -**Story 2: ๅ…จ่ฟ‡็จ‹็›‘ๆŽง** -- ๅฏๅŠจๅค„็†ๅŽ๏ผŒ่ƒฝ็œ‹ๅˆฐๆฏไธชๆญฅ้ชค็š„ๅฎžๆ—ถ็Šถๆ€ -- ็œ‹ๅˆฐๅฝ“ๅ‰ๆญฃๅœจๅค„็†ๅ“ชไธช็‰‡ๆฎต๏ผˆClip 3/14๏ผ‰ -- ็œ‹ๅˆฐ้ข„ไผฐๅ‰ฉไฝ™ๆ—ถ้—ด -- ่ƒฝ็œ‹ๅˆฐๆฏไธชๆญฅ้ชค็š„ๆ—ฅๅฟ—่พ“ๅ‡บ - -**Story 3: ๆš‚ๅœไธŽๆขๅค** -- ็‚นๅ‡ป"ๆš‚ๅœ"ๆŒ‰้’ฎ๏ผŒๅค„็†็ซ‹ๅณๅœๆญข -- ๅ…ณ้—ญๅบ”็”จๅŽ๏ผŒไธ‹ๆฌกๆ‰“ๅผ€่ƒฝไปŽๆš‚ๅœ็‚น็ปง็ปญ -- ๆขๅคๅŽไปŽๆœ€่ฟ‘็š„ๆฃ€ๆŸฅ็‚น็ปง็ปญ๏ผŒไธไธขๅคฑ่ฟ›ๅบฆ - -**Story 4: ไบบๅทฅไป‹ๅ…ฅ** -- ๅœจๆ ‡้ข˜็บ ๆญฃๆญฅ้ชค๏ผŒๅฏไปฅ้ข„่งˆๆฏไธช็‰‡ๆฎต็š„ๆ ‡้ข˜ -- ๅฏไปฅๆ‰‹ๅŠจไฟฎๆ”นๆ ‡้ข˜ -- ๅฏไปฅ่ทณ่ฟ‡ๆŸไบ›็‰‡ๆฎต -- ๆ‰€ๆœ‰ไฟฎๆ”นไผš่ขซไฟๅญ˜ - -**Story 5: ๆ‰“ๅŒ…ๅˆ†ๅ‘** -- ็”Ÿๆˆไธ€ไธช .exe ๆ–‡ไปถ๏ผŒๅŒๅ‡ปๅณๅฏ่ฟ่กŒ -- ๆ— ้œ€ๅฎ‰่ฃ… Python ็Žฏๅขƒ -- ๅฏไปฅๅœจๅ…ถไป–็”ต่„‘ไธŠไฝฟ็”จ - ---- - -## 3. ้ชŒๆ”ถๆ ‡ๅ‡† - -### 3.1 ้…็ฝฎ็•Œ้ข -- [ ] ๅบ”็”จๅฏๅŠจๅŽๆ˜พ็คบ้…็ฝฎ้ขๆฟ -- [ ] ๅฏไปฅ่พ“ๅ…ฅ/็ผ–่พ‘ API Key ๅ’Œ API Host -- [ ] ๅฏไปฅ้€‰ๆ‹ฉ Whisper ๆจกๅž‹๏ผˆbase/small/medium/large๏ผ‰ -- [ ] ๅฏไปฅ้€‰ๆ‹ฉ่พ“ๅ…ฅ่ง†้ข‘ๆ–‡ไปถ๏ผˆๆ–‡ไปถ้€‰ๆ‹ฉๅฏน่ฏๆก†๏ผ‰ -- [ ] ๅฏไปฅ้€‰ๆ‹ฉ่พ“ๅ‡บ็›ฎๅฝ• -- [ ] ้…็ฝฎๅฏไปฅไฟๅญ˜ๅˆฐๆ–‡ไปถ๏ผˆJSON/YAML๏ผ‰ -- [ ] ้…็ฝฎๅฏไปฅไปŽๆ–‡ไปถๅŠ ่ฝฝ - -### 3.2 ๅ…จ่ฟ‡็จ‹็›‘ๆŽง -- [ ] ๆ˜พ็คบๅฝ“ๅ‰ๆญฅ้ชค๏ผšๅ‡†ๅค‡ โ†’ ๆๅ–็‰‡ๆฎต โ†’ ่ฝฌๅฝ• โ†’ ๆ ‡้ข˜็บ ๆญฃ โ†’ ๅˆๅนถ โ†’ ็ƒงๅฝ• -- [ ] ๆฏไธ€ๆญฅๆ˜พ็คบ่ฟ›ๅบฆๆก๏ผˆ็™พๅˆ†ๆฏ”๏ผ‰ -- [ ] ๆ˜พ็คบๅฝ“ๅ‰็‰‡ๆฎตๅบๅท๏ผˆๅฆ‚ "Clip 3/14"๏ผ‰ -- [ ] ๅฎžๆ—ถๆ˜พ็คบๅค„็†ๆ—ฅๅฟ— -- [ ] ๅค„็†ๅฎŒๆˆๅŽๆ˜พ็คบๆœ€็ปˆ่ง†้ข‘่ทฏๅพ„ - -### 3.3 ๆš‚ๅœไธŽๆขๅค -- [ ] ๆœ‰"ๆš‚ๅœ"ๆŒ‰้’ฎ๏ผŒ็‚นๅ‡ปๅŽๅค„็†ๅœๆญข -- [ ] ๆš‚ๅœๆ—ถ็Šถๆ€ๆŒไน…ๅŒ–ๅˆฐๆ–‡ไปถ -- [ ] ้‡ๆ–ฐๆ‰“ๅผ€ๅบ”็”จๅŽ๏ผŒ่‡ชๅŠจๆฃ€ๆต‹ๅˆฐๆœชๅฎŒๆˆ็š„ไปปๅŠก -- [ ] ๅฏไปฅ้€‰ๆ‹ฉ"็ปง็ปญ"ไปŽๆš‚ๅœ็‚นๆขๅค -- [ ] ไนŸๅฏไปฅ้€‰ๆ‹ฉ"้‡ๆ–ฐๅผ€ๅง‹" - -### 3.4 ไบบๅทฅไป‹ๅ…ฅ -- [ ] ๅœจๆ ‡้ข˜็บ ๆญฃๆญฅ้ชค๏ผŒๆ˜พ็คบๆ‰€ๆœ‰็‰‡ๆฎต็š„ๆ ‡้ข˜ๅˆ—่กจ -- [ ] ๆฏไธชๆ ‡้ข˜ๅฏไปฅ็‚นๅ‡ป็ผ–่พ‘ -- [ ] ็ผ–่พ‘ๅŽ่‡ชๅŠจไฟๅญ˜ -- [ ] ๅฏไปฅ้ข„่งˆไฟฎๆ”นๅŽ็š„ๅญ—ๅน•ๆ•ˆๆžœ -- [ ] ็กฎ่ฎคๅŽ็ปง็ปญๅŽ็ปญๆญฅ้ชค - -### 3.5 ๆ‰“ๅŒ…ๅˆ†ๅ‘ -- [ ] ็”Ÿๆˆ Windows ๅฏๆ‰ง่กŒๆ–‡ไปถ๏ผˆ.exe๏ผ‰ -- [ ] ๅŒๅ‡ป่ฟ่กŒ๏ผŒๆ— ้กปๅฎ‰่ฃ… Python -- [ ] ็•Œ้ข็พŽ่ง‚๏ผŒไธŽ็ŽฐไปฃๆกŒ้ขๅบ”็”จไธ€่‡ด - ---- - -## 4. ๆŠ€ๆœฏ้€‰ๅž‹ - -### 4.1 GUI ๆก†ๆžถ - -**้€‰ๆ‹ฉ๏ผšPySide6 (Qt for Python)** - -| ๅ› ็ด  | ็ป“่ฎบ | -|------|------| -| ่ฎธๅฏ่ฏ | LGPL - ๅฏ้—ญๆบๅ•†็”จ๏ผŒๆ— ้œ€่ดญไนฐ | -| ๅŠŸ่ƒฝ | ๆˆ็†ŸๅฎŒๅค‡๏ผŒ้€‚ๅˆๅคๆ‚ๆกŒ้ขๅบ”็”จ | -| ็Šถๆ€็ฎก็† | ไผ ็ปŸไฟ็•™ๆจกๅผ๏ผŒๅคฉ็„ถๆ”ฏๆŒๆš‚ๅœ/ๆขๅค | -| ๅคš็บฟ็จ‹ | ไฟกๅทๆงฝๆœบๅˆถๅฎŒๅ–„๏ผŒๆ”ฏๆŒๅคš็บฟ็จ‹ๅฎ‰ๅ…จๆ›ดๆ–ฐ UI | -| ็คพๅŒบ | ๆ–‡ๆกฃไธฐๅฏŒ๏ผŒ็คพๅŒบๆดป่ทƒ | -| ๆ‰“ๅŒ… | Nuitka ๆ‰“ๅŒ…ไฝ“็งฏๅฐ๏ผˆ~10MB๏ผ‰๏ผŒๅฏๅŠจๅฟซ | - -### 4.2 ๆ‰“ๅŒ…ๆ–นๆกˆ - -**ๅผ€ๅ‘้˜ถๆฎต**๏ผšPyInstaller๏ผˆ่ฐƒ่ฏ•ๆ–นไพฟ๏ผ‰ -**ๆญฃๅผๅ‘ๅธƒ**๏ผšNuitka๏ผˆไฝ“็งฏๅฐ๏ผŒๆ€ง่ƒฝๅฅฝ๏ผ‰ - -### 4.3 ็Šถๆ€ๆŒไน…ๅŒ– - -- ไฝฟ็”จ JSON ๆ–‡ไปถๅญ˜ๅ‚จๅค„็†่ฟ›ๅบฆ -- ๆฏไธช้กน็›ฎ็‹ฌ็ซ‹็š„็Šถๆ€ๆ–‡ไปถ -- ๅŒ…ๅซ๏ผšๅฝ“ๅ‰ๆญฅ้ชคใ€ๅทฒๅฎŒๆˆ็‰‡ๆฎตใ€็”จๆˆทไฟฎๆ”น็š„ๆ ‡้ข˜็ญ‰ - ---- - -## 5. ๆžถๆž„่ฎพ่ฎก๏ผˆ้ข„่งˆ๏ผ‰ - -``` -โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” -โ”‚ GUI Layer (PySide6) โ”‚ -โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ -โ”‚ โ”‚ ConfigPanel โ”‚ โ”‚ ProgressViewโ”‚ โ”‚ TitleEditor โ”‚ โ”‚ -โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ -โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ - โ”‚ - โ–ผ -โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” -โ”‚ Business Logic Layer โ”‚ -โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ -โ”‚ โ”‚ ConfigManagerโ”‚ โ”‚PipelineCtrl โ”‚ โ”‚ StateManager โ”‚ โ”‚ -โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ -โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ - โ”‚ - โ–ผ -โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” -โ”‚ Core Modules (Legacy) โ”‚ -โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ -โ”‚ โ”‚ video.pyโ”‚ โ”‚subtitle โ”‚ โ”‚ llm.py โ”‚ โ”‚correctionโ”‚ โ”‚ -โ”‚ โ”‚ โ”‚ โ”‚ .py โ”‚ โ”‚ โ”‚ โ”‚s.py โ”‚ โ”‚ -โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ -โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ -``` - -### ๆจกๅ—่Œ่ดฃ - -| ๆจกๅ— | ่Œ่ดฃ | -|------|------| -| ConfigPanel | ้…็ฝฎ็•Œ้ข๏ผˆAPIใ€่ง†้ข‘่ทฏๅพ„ใ€ๆจกๅž‹้€‰ๆ‹ฉ๏ผ‰ | -| ProgressView | ่ฟ›ๅบฆ็›‘ๆŽง๏ผˆๆญฅ้ชคใ€็™พๅˆ†ๆฏ”ใ€ๆ—ฅๅฟ—๏ผ‰ | -| TitleEditor | ๆ ‡้ข˜็ผ–่พ‘ๅ™จ๏ผˆ้ข„่งˆใ€็ผ–่พ‘็”จๆˆทไฟฎๆ”น๏ผ‰ | -| ConfigManager | ้…็ฝฎๅŠ ่ฝฝ/ไฟๅญ˜ | -| PipelineController | ๆตๆฐด็บฟๆŽงๅˆถ๏ผˆๅฏๅŠจ/ๆš‚ๅœ/ๆขๅค/ๅœๆญข๏ผ‰ | -| StateManager | ็Šถๆ€ๆŒไน…ๅŒ–๏ผˆไฟๅญ˜/ๆขๅค่ฟ›ๅบฆ๏ผ‰ | - ---- - -## 6. ๆตๆฐด็บฟๆญฅ้ชค - -ไธŽๅบ”็”จUIๅฏนๅบ”็š„ๅค„็†ๆญฅ้ชค๏ผš - -``` -Step 0: ๅ‡†ๅค‡ (Ready) - โ””โ”€ ๆฃ€ๆŸฅ้…็ฝฎใ€ๅˆๅง‹ๅŒ–็Žฏๅขƒ - -Step 1: ๆๅ–็‰‡ๆฎต (Extracting) - โ””โ”€ ไปŽ่ง†้ข‘ๆๅ– clips -> intermediates/clip{N}_fade.mp4 - -Step 2: ่ฝฌๅฝ• (Transcribing) - โ””โ”€ Whisper ่ฝฌๅฝ• -> intermediates/clip{N}.json - -Step 3: ๆ ‡้ข˜็บ ๆญฃ (Title Correcting) - โ””โ”€ LLM ๅˆ†ๆž + ็”จๆˆทไฟฎๆ”น -> intermediates/corrected_titles.json - [ไบบๅทฅไป‹ๅ…ฅ็‚น] ็”จๆˆทๅฏๅœจๆญคๆญฅ้ชคๆš‚ๅœๅนถไฟฎๆ”นๆ ‡้ข˜ - -Step 4: ็”Ÿๆˆๅญ—ๅน• (Generating Subtitles) - โ””โ”€ ็”ŸๆˆๅŒ่ฝจๅญ—ๅน• -> subs/v{N}_title.srt, v{N}_content.srt - -Step 5: ๅˆๅนถ่ง†้ข‘ (Merging) - โ””โ”€ FFmpeg concat -> concat_merged.mp4 - -Step 6: ็ƒงๅฝ•ๅญ—ๅน• (Burning) - โ””โ”€ FFmpeg burn -> v{N}_final.mp4 - -Step 7: ๅฎŒๆˆ (Completed) - โ””โ”€ ๆ˜พ็คบ็ป“ๆžœ๏ผŒๆธ…็†ไธดๆ—ถๆ–‡ไปถ๏ผˆๅฏ้€‰๏ผ‰ -``` - -### ๆš‚ๅœ็‚น - -็”จๆˆทๅฏไปฅๅœจไปฅไธ‹ๆญฅ้ชคๆš‚ๅœ๏ผš -- Step 1 ๅฎŒๆˆๅŽ๏ผˆ็‰‡ๆฎตๅทฒๆๅ–๏ผ‰ -- Step 2 ๅฎŒๆˆๅŽ๏ผˆ่ฝฌๅฝ•ๅทฒๅฎŒๆˆ๏ผ‰ -- Step 3 ๅฎŒๆˆๅŽ๏ผˆๆ ‡้ข˜ๅทฒ็กฎ่ฎค๏ผ‰- **ๆŽจ่ๆš‚ๅœ็‚น** -- Step 4 ๅฎŒๆˆๅŽ๏ผˆๅญ—ๅน•ๅทฒ็”Ÿๆˆ๏ผ‰ -- Step 5 ๅฎŒๆˆๅŽ๏ผˆ่ง†้ข‘ๅทฒๅˆๅนถ๏ผ‰ - ---- - -## 7. ็Šถๆ€ๆ•ฐๆฎ็ป“ๆž„ - -```json -{ - "version": 1, - "project_name": "็ฆ็”ฐๅคœๆ ก-03ๆœˆ18ๆ—ฅ", - "config": { - "video_src": "D:/path/to/video.mp4", - "output_dir": "D:/path/to/output", - "api_key": "xxx", - "api_host": "https://...", - "whisper_model": "large" - }, - "current_step": 3, - "clips": [ - { - "index": 1, - "title_original": "ๅผนๅฅ", - "title_corrected": "ๅผนๅฅ", - "title_user_modified": null, - "start": 412, - "end": 442, - "status": "completed", - "clip_path": "intermediates/clip1_fade.mp4", - "json_path": "intermediates/clip1.json" - } - ], - "step_status": { - "extracting": "completed", - "transcribing": "completed", - "title_correcting": "in_progress", - "generating_subtitles": "pending", - "merging": "pending", - "burning": "pending" - }, - "created_at": "2026-05-02T10:00:00", - "updated_at": "2026-05-02T10:30:00" -} -``` - ---- - -## 8. ่พน็•Œๆƒ…ๅ†ต - -| ๆƒ…ๅ†ต | ๅค„็†ๆ–นๅผ | -|------|----------| -| API Key ๆ— ๆ•ˆ | ๆ˜พ็คบ้”™่ฏฏๆ็คบ๏ผŒไธๅผ€ๅง‹ๅค„็† | -| ่ง†้ข‘ๆ–‡ไปถไธๅญ˜ๅœจ | ้…็ฝฎๆฃ€ๆŸฅๆ—ถๅ‘็Žฐ๏ผŒๆ็คบ็”จๆˆท | -| ๅค„็†ๅˆฐไธ€ๅŠๅดฉๆบƒ | ็Šถๆ€ๅทฒๆŒไน…ๅŒ–๏ผŒ้‡ๅฏๅŽๅฏๆขๅค | -| ็”จๆˆทๅ–ๆถˆๅค„็† | ไฟๅญ˜ๅฝ“ๅ‰่ฟ›ๅบฆ๏ผŒๅฏ้€‰ๆ‹ฉ้‡ๆ–ฐๅผ€ๅง‹ๆˆ–็ปง็ปญ | -| Whisper ๆจกๅž‹ๆœชไธ‹่ฝฝ | ๆ˜พ็คบไธ‹่ฝฝ้“พๆŽฅ๏ผŒๆˆ–ไฝฟ็”จ้ป˜่ฎคๆจกๅž‹ | -| ่พ“ๅ‡บ็›ฎๅฝ•็ฃ็›˜็ฉบ้—ดไธ่ถณ | ๅค„็†ๅ‰ๆฃ€ๆŸฅ๏ผŒๆ็คบ็”จๆˆท | - ---- - -## 9. ้žๅŠŸ่ƒฝๆ€ง้œ€ๆฑ‚ - -| ้œ€ๆฑ‚ | ๆ ‡ๅ‡† | -|------|------| -| ๅฏๅŠจๆ—ถ้—ด | < 3 ็ง’๏ผˆไฝฟ็”จ Nuitka ๆ‰“ๅŒ…ๅŽ๏ผ‰ | -| UI ๅ“ๅบ”ๆ€ง | ๆ‰€ๆœ‰ๆ“ไฝœๅœจ 100ms ๅ†…ๅ“ๅบ” | -| ๅ†…ๅญ˜ๅ ็”จ | < 500MB๏ผˆไธๅซ่ง†้ข‘ๅค„็†๏ผ‰ | -| ๆ‰“ๅŒ…ๅŽไฝ“็งฏ | < 50MB | -| ๅ…ผๅฎนๆ€ง | Windows 10/11 64-bit | - ---- - -## 10. ๅŽ็ปญๆญฅ้ชค - -1. **Phase 2**: ๆŠ€ๆœฏ่ฎพ่ฎก - ่ฏฆ็ป†ๆžถๆž„่ฎพ่ฎกใ€ๆ•ฐๆฎๅบ“้€‰ๅž‹ใ€ๆŽฅๅฃๅฎšไน‰ -2. **Phase 3**: ไปปๅŠก็ผ–ๆŽ’ - ๆ‹†ๅˆ†ๅนถ่กŒไปปๅŠกใ€ๅˆ›ๅปบ git worktree -3. **Phase 4**: ๅนถ่กŒๅผ€ๅ‘ - ๅ„ๆจกๅ—ๅฎž็Žฐ -4. **Phase 5**: ่ดจ้‡ไบคไป˜ - ๅฎกๆŸฅใ€ๆต‹่ฏ•ใ€ๆ‰“ๅŒ… diff --git a/requirements-build.txt b/requirements-build.txt deleted file mode 100644 index b23ed59..0000000 --- a/requirements-build.txt +++ /dev/null @@ -1,5 +0,0 @@ -# Build dependencies for Nuitka compilation -# Install with: pip install -r requirements-build.txt - -nuitka>=1.7.0 -pandas>=1.5.0 diff --git a/tasks.md b/tasks.md deleted file mode 100644 index a6b9113..0000000 --- a/tasks.md +++ /dev/null @@ -1,477 +0,0 @@ -# Piano Highlight Generator App - ไปปๅŠกๆ‹†่งฃ - -> ๅˆ›ๅปบๆ—ฅๆœŸ๏ผš2026-05-02 -> ๅŸบไบŽ๏ผšdesign.md - ---- - -## ไปปๅŠกๆ€ป่งˆ - -| # | ไปปๅŠก | ไพ่ต– | ไผ˜ๅ…ˆ็บง | ๅทฅๆœŸ | -|---|------|------|--------|------| -| 1 | ้กน็›ฎ้ชจๆžถๆญๅปบ | - | P0 | 1h | -| 2 | ConfigPanel ้…็ฝฎ้ขๆฟ | 1 | P0 | 1h | -| 3 | StateManager ็Šถๆ€็ฎก็† | 1 | P0 | 0.5h | -| 4 | ProgressView ่ฟ›ๅบฆ่ง†ๅ›พ | 1 | P0 | 1h | -| 5 | Worker ๅŽๅฐ็บฟ็จ‹ | 1 | P0 | 1h | -| 6 | PipelineController ๆตๆฐด็บฟๆŽงๅˆถ | 3, 5 | P0 | 1.5h | -| 7 | TitleEditor ๆ ‡้ข˜็ผ–่พ‘ๅ™จ | 6 | P1 | 1.5h | -| 8 | ไธป็ช—ๅฃ้›†ๆˆ | 2, 4, 7 | P0 | 1h | -| 9 | ๆ ธๅฟƒๆจกๅ—้€‚้… | 1 | P0 | 0.5h | -| 10 | ้”™่ฏฏๅค„็†ๅฎŒๅ–„ | 8 | P2 | 1h | -| 11 | ๆ‰“ๅŒ…้…็ฝฎ + ๆต‹่ฏ• | 10 | P2 | 1.5h | -| 12 | ๆ–‡ๆกฃๅ’Œ README | 11 | P3 | 0.5h | - ---- - -## ไปปๅŠก 1: ้กน็›ฎ้ชจๆžถๆญๅปบ - -**ๆ–‡ไปถ**๏ผš -- `src/__init__.py` -- `src/main.py` - ๅบ”็”จๅ…ฅๅฃ -- `src/app.py` - QMainWindow ไธป็ช—ๅฃ -- `src/gui/__init__.py` -- `src/logic/__init__.py` -- `src/core/__init__.py` -- `requirements.txt` -- `pyproject.toml` - -**ๅ†…ๅฎน**๏ผš -- ๅˆ›ๅปบ็›ฎๅฝ•็ป“ๆž„ -- ๅˆ›ๅปบ็ฉบ็š„ `__init__.py` -- `main.py` ไฝฟ็”จ QApplication ๅฏๅŠจ -- `app.py` ๅˆ›ๅปบ็ฉบ็š„ไธป็ช—ๅฃๆก†ๆžถ -- `requirements.txt` ๅŒ…ๅซๆ‰€ๆœ‰ไพ่ต– -- ้…็ฝฎ pyproject.toml - -**้ชŒๆ”ถๆ ‡ๅ‡†**๏ผš -- ่ฟ่กŒ `python src/main.py` ่ƒฝๅฏๅŠจ็ฉบ็ช—ๅฃ -- ็ช—ๅฃๆ ‡้ข˜ไธบ "Piano Highlight Generator" - ---- - -## ไปปๅŠก 2: ConfigPanel ้…็ฝฎ้ขๆฟ - -**ๆ–‡ไปถ**๏ผš`src/gui/config_panel.py` - -**UI ็ป„ไปถ**๏ผš -``` -โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” -โ”‚ API ้…็ฝฎ โ”‚ -โ”‚ API Host: [________________________] โ”‚ -โ”‚ API Key: [________________________] โ”‚ -โ”‚ ๆจกๅž‹: [็ซๅฑฑๆ–น่ˆŸ Ark v] โ”‚ -โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค -โ”‚ ่ง†้ข‘้…็ฝฎ โ”‚ -โ”‚ ่ง†้ข‘ๆ–‡ไปถ: [________________] [ๆต่งˆ...] โ”‚ -โ”‚ ่พ“ๅ‡บ็›ฎๅฝ•: [________________] [ๆต่งˆ...] โ”‚ -โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค -โ”‚ Whisper ้…็ฝฎ โ”‚ -โ”‚ ๆจกๅž‹: [large v] โ”‚ -โ”‚ ๆจกๅž‹่ทฏๅพ„: [________________] [ๆต่งˆ...] โ”‚ -โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค -โ”‚ [ๅผ€ๅง‹ๅค„็†] [ไฟๅญ˜้…็ฝฎ] โ”‚ -โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ -``` - -**ไฟกๅท**๏ผš -- `config_changed_signal(dict)` - ้…็ฝฎๅ˜ๆ›ดๆ—ถๅ‘ๅ‡บ - -**ๆ–นๆณ•**๏ผš -- `load_config(config: dict)` - ๅŠ ่ฝฝ้…็ฝฎๅˆฐ UI -- `get_config() -> dict` - ไปŽ UI ่Žทๅ–้…็ฝฎ -- `validate() -> (bool, str)` - ้ชŒ่ฏ้…็ฝฎๆœ‰ๆ•ˆๆ€ง - -**้ชŒๆ”ถๆ ‡ๅ‡†**๏ผš -- ๆ‰€ๆœ‰่พ“ๅ…ฅๆก†่ƒฝๆญฃๅธธ่พ“ๅ…ฅ -- ๆ–‡ไปถ้€‰ๆ‹ฉๅฏน่ฏๆก†่ƒฝ้€‰ๆ‹ฉๆ–‡ไปถๅ’Œ็›ฎๅฝ• -- ้…็ฝฎๅ˜ๆ›ดๆ—ถๅ‘ๅ‡บไฟกๅท - ---- - -## ไปปๅŠก 3: StateManager ็Šถๆ€็ฎก็† - -**ๆ–‡ไปถ**๏ผš`src/logic/state_manager.py` - -**็ฑป**๏ผš`StateManager` - -**ๆ–นๆณ•**๏ผš -```python -def __init__(self, state_file: str): - """ๅˆๅง‹ๅŒ–๏ผŒๅŠ ่ฝฝๆˆ–ๅˆ›ๅปบ็Šถๆ€ๆ–‡ไปถ""" - -def save(self): - """ไฟๅญ˜็Šถๆ€ๅˆฐ JSON ๆ–‡ไปถ""" - -def get_current_step(self) -> int: - """่Žทๅ–ๅฝ“ๅ‰ๆญฅ้ชค็ดขๅผ• (0-6)""" - -def get_step_name(self) -> str: - """่Žทๅ–ๅฝ“ๅ‰ๆญฅ้ชคๅ็งฐ""" - -def set_step_status(self, step_name: str, status: str): - """่ฎพ็ฝฎๆญฅ้ชค็Šถๆ€ (pending/in_progress/completed/failed)""" - -def update_clip(self, clip_index: int, **kwargs): - """ๆ›ดๆ–ฐ clip ไฟกๆฏ""" - -def get_clips(self) -> list: - """่Žทๅ–ๆ‰€ๆœ‰ clips""" - -def get_user_modified_titles(self) -> dict: - """่Žทๅ–็”จๆˆทไฟฎๆ”น่ฟ‡็š„ๆ ‡้ข˜ {clip_index: title}""" - -def reset(self): - """้‡็ฝฎ็Šถๆ€๏ผŒๅผ€ๅง‹ๆ–ฐ้กน็›ฎ""" -``` - -**็Šถๆ€ๆ–‡ไปถ**๏ผš`{output_dir}/state.json` - -**้ชŒๆ”ถๆ ‡ๅ‡†**๏ผš -- ๅˆ›ๅปบๆ–ฐ็Šถๆ€ๆ—ถ็”Ÿๆˆ้ป˜่ฎค็ป“ๆž„ -- ๅŠ ่ฝฝๅทฒๆœ‰็Šถๆ€ๆ—ถๆขๅคๅฎŒๆ•ดไฟกๆฏ -- ไฟๅญ˜ๅŽ JSON ๆ–‡ไปถๆ ผๅผๆญฃ็กฎ - ---- - -## ไปปๅŠก 4: ProgressView ่ฟ›ๅบฆ่ง†ๅ›พ - -**ๆ–‡ไปถ**๏ผš`src/gui/progress_view.py` - -**UI ็ป„ไปถ**๏ผš -``` -โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” -โ”‚ ๅฝ“ๅ‰ๆญฅ้ชค: ๆๅ–่ง†้ข‘็‰‡ๆฎต [Clip 3/14] โ”‚ -โ”‚ โ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–ˆโ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘ 50% โ”‚ -โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค -โ”‚ [ๅ‡†ๅค‡] โ†’ [ๆๅ–] โ†’ [่ฝฌๅฝ•] โ†’ [็บ ๆญฃ] โ†’ [ๅญ—ๅน•] โ”‚ -โ”‚ โ†’ [ๅˆๅนถ] โ†’ [็ƒงๅฝ•] โ”‚ -โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค -โ”‚ ๆ—ฅๅฟ—: โ”‚ -โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ -โ”‚ โ”‚ 10:30:01 ๅผ€ๅง‹ๆๅ–็‰‡ๆฎต 3/14 โ”‚ โ”‚ -โ”‚ โ”‚ 10:30:02 ็‰‡ๆฎต 3 ๆๅ–ๅฎŒๆˆ โ”‚ โ”‚ -โ”‚ โ”‚ 10:30:03 ๅผ€ๅง‹ๆๅ–็‰‡ๆฎต 4/14 โ”‚ โ”‚ -โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ -โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค -โ”‚ [ๆš‚ๅœ] [ๅœๆญข] [็ปง็ปญ] โ”‚ -โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ -``` - -**ไฟกๅท**๏ผš -- `start_signal` - ๅผ€ๅง‹ๅค„็† -- `pause_signal` - ๆš‚ๅœ -- `resume_signal` - ็ปง็ปญ -- `stop_signal` - ๅœๆญข - -**ๆ–นๆณ•**๏ผš -- `update_step(step_name: str, percent: int)` - ๆ›ดๆ–ฐๆญฅ้ชค่ฟ›ๅบฆ -- `update_clip_progress(current: int, total: int)` - ๆ›ดๆ–ฐ Clip ่ฟ›ๅบฆ -- `append_log(message: str)` - ่ฟฝๅŠ ๆ—ฅๅฟ— -- `show_titles_for_review(titles: list)` - ๆ˜พ็คบๆ ‡้ข˜ๅพ…ๅฎกๆ ธ๏ผˆ่งฆๅ‘ TitleEditor๏ผ‰ - -**้ชŒๆ”ถๆ ‡ๅ‡†**๏ผš -- ่ฟ›ๅบฆๆก่ƒฝๅฎžๆ—ถๆ›ดๆ–ฐ -- ๆ—ฅๅฟ—่ƒฝ่‡ชๅŠจๆปšๅŠจๅˆฐๆœ€ๆ–ฐ -- ๆŒ‰้’ฎ็Šถๆ€่ƒฝๆ นๆฎๆตๆฐด็บฟ็Šถๆ€ๅ˜ๅŒ– - ---- - -## ไปปๅŠก 5: Worker ๅŽๅฐ็บฟ็จ‹ - -**ๆ–‡ไปถ**๏ผš`src/logic/worker.py` - -**็ฑป**๏ผš`Worker(QThread)` - -**ไฟกๅท**๏ผš -```python -progress_signal = pyqtSignal(str, int, str) # step_name, percent, message -clip_completed_signal = pyqtSignal(int) # clip_index -step_started_signal = pyqtSignal(str) # step_name -step_completed_signal = pyqtSignal(str) # step_name -titles_ready_signal = pyqtSignal(list) # [{clip_index, original, llm_suggested}] -error_signal = pyqtSignal(str) # error_message -finished_signal = pyqtSignal(bool, str) # success, message -log_signal = pyqtSignal(str) # log message -``` - -**ๆ–นๆณ•**๏ผš -- `__init__(config, state_manager, controller)` -- `run()` - ๆ‰ง่กŒๆตๆฐด็บฟ -- `request_pause()` - ่ฏทๆฑ‚ๆš‚ๅœ -- `request_stop()` - ่ฏทๆฑ‚ๅœๆญข - -**ๆš‚ๅœๅฎž็Žฐ**๏ผš -```python -def run(self): - for step in self.steps: - if self.is_stopped: - break - if self.is_paused: - self.wait_for_resume() # ็ญ‰ๅพ…็”จๆˆทresumeไฟกๅท - # ๆ‰ง่กŒๆญฅ้ชค... -``` - -**้ชŒๆ”ถๆ ‡ๅ‡†**๏ผš -- UI ๅœจๅค„็†่ฟ‡็จ‹ไธญไธๅก้กฟ -- ๆš‚ๅœไฟกๅท่ƒฝๅœจ 1 ็ง’ๅ†…ๅ“ๅบ” -- ๆ‰€ๆœ‰ไฟกๅท่ƒฝๆญฃ็กฎไผ ้€’ๅˆฐ UI - ---- - -## ไปปๅŠก 6: PipelineController ๆตๆฐด็บฟๆŽงๅˆถ - -**ๆ–‡ไปถ**๏ผš`src/logic/pipeline_controller.py` - -**็ฑป**๏ผš`PipelineController` - -**ๆญฅ้ชคๅฎšไน‰**๏ผš -```python -STEPS = [ - 'ready', - 'extracting', # ๆๅ–็‰‡ๆฎต - 'transcribing', # ่ฝฌๅฝ• - 'title_correcting', # ๆ ‡้ข˜็บ ๆญฃ๏ผˆไบบๅทฅไป‹ๅ…ฅ็‚น๏ผ‰ - 'generating_subtitles', # ็”Ÿๆˆๅญ—ๅน• - 'merging', # ๅˆๅนถ - 'burning', # ็ƒงๅฝ• - 'completed' -] -``` - -**ๆ–นๆณ•**๏ผš -```python -def __init__(self, config: dict, state: StateManager): - self.config = config - self.state = state - self.is_paused = False - self.is_stopped = False - -def run(self, worker: Worker): - """่ฟ่กŒๆตๆฐด็บฟ""" - -def pause(self): - """ๆš‚ๅœ""" - -def resume(self): - """ๆขๅค""" - -def stop(self): - """ๅœๆญข""" - -def step_extracting(self, worker: Worker): - """ๆๅ–็‰‡ๆฎต""" - -def step_transcribing(self, worker: Worker): - """่ฝฌๅฝ•๏ผˆ่ฐƒ็”จ Whisper๏ผ‰""" - -def step_title_correcting(self, worker: Worker) -> list: - """ๆ ‡้ข˜็บ ๆญฃ - ่ฐƒ็”จ LLM๏ผŒ่ฟ”ๅ›ž้œ€่ฆ็”จๆˆท็กฎ่ฎค็š„ๆ ‡้ข˜""" - -def step_generating_subtitles(self, worker: Worker): - """็”Ÿๆˆๅญ—ๅน•""" - -def step_merging(self, worker: Worker): - """ๅˆๅนถ่ง†้ข‘""" - -def step_burning(self, worker: Worker): - """็ƒงๅฝ•ๅญ—ๅน•""" -``` - -**้ชŒๆ”ถๆ ‡ๅ‡†**๏ผš -- ๆฏไธชๆญฅ้ชค่ƒฝๆญฃ็กฎๆ‰ง่กŒ -- ๆš‚ๅœ/ๆขๅค่ƒฝๆญฃ็กฎๅทฅไฝœ -- ็Šถๆ€่ƒฝๆญฃ็กฎไฟๅญ˜ - ---- - -## ไปปๅŠก 7: TitleEditor ๆ ‡้ข˜็ผ–่พ‘ๅ™จ - -**ๆ–‡ไปถ**๏ผš`src/gui/title_editor.py` - -**UI ็ป„ไปถ**๏ผš -``` -โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” -โ”‚ ๆ ‡้ข˜ๅฎกๆ ธ - ่ฏท็กฎ่ฎคไปฅไธ‹ๆ ‡้ข˜ๆ˜ฏๅฆๆญฃ็กฎ โ”‚ -โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค -โ”‚ # โ”‚ ๅŽŸๅง‹ๆ ‡้ข˜ โ”‚ LLMๅปบ่ฎฎ โ”‚ ไฟฎๆ”นๅŽ โ”‚ ๆ“ไฝœ โ”‚ -โ”œโ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค -โ”‚ 1 โ”‚ ๅผนๅฅ โ”‚ ๅผนๅฅ โ”‚ [ๅผนๅฅ ] โ”‚ [็ผ–่พ‘] โ”‚ -โ”‚ 2 โ”‚ ้ž่ฟžๅฅๅผนๅฅๆณ• โ”‚ ้ž่ฟžๅฅๅผนๅฅๆณ• โ”‚ [้ž่ฟžๅฅๅผนๅฅๆณ•] โ”‚ [็ผ–่พ‘] โ”‚ -โ”‚ 3 โ”‚ ๆ—ถๅ€ผ โ”‚ ไผ‘ๆญข็ฌฆ โœ— โ”‚ [ไผ‘ๆญข็ฌฆ ] โ”‚ [็ผ–่พ‘] โ”‚ -โ”‚ 4 โ”‚ ไผ‘ๆญข็ฌฆ โ”‚ ไผ‘ๆญข็ฌฆ โ”‚ [ไผ‘ๆญข็ฌฆ ] โ”‚ [็ผ–่พ‘] โ”‚ -โ”‚ 5 โ”‚ ่Š‚ๅฅ โ”‚ ่Š‚ๅฅ โ”‚ [่Š‚ๅฅ ] โ”‚ [็ผ–่พ‘] โ”‚ -โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค -โ”‚ [ๅ…จ้ƒจ็กฎ่ฎค] [ๅ–ๆถˆ] โ”‚ -โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ -``` - -**็ผ–่พ‘ๅผน็ช—**๏ผš -``` -โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” -โ”‚ ็ผ–่พ‘ๆ ‡้ข˜ - Clip #3 โ”‚ -โ”‚ โ”‚ -โ”‚ ๅŽŸๅง‹ๆ ‡้ข˜: ๆ—ถๅ€ผ โ”‚ -โ”‚ LLMๅปบ่ฎฎ: ไผ‘ๆญข็ฌฆ โ”‚ -โ”‚ โ”‚ -โ”‚ ไฟฎๆ”นๅŽ: [_______________] โ”‚ -โ”‚ โ”‚ -โ”‚ [็กฎๅฎš] [ๅ–ๆถˆ] โ”‚ -โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ -``` - -**ไฟกๅท**๏ผš -- `titles_confirmed_signal(list)` - ็”จๆˆท็กฎ่ฎค็š„ๆ ‡้ข˜ - -**้ชŒๆ”ถๆ ‡ๅ‡†**๏ผš -- ่ƒฝๆ˜พ็คบๆ‰€ๆœ‰ๆ ‡้ข˜ -- ่ƒฝ็ผ–่พ‘ๅ•ไธชๆ ‡้ข˜ -- ่ƒฝๆ‰น้‡็กฎ่ฎค -- ็กฎ่ฎคๅŽ่ฟ”ๅ›žๅˆ—่กจ - ---- - -## ไปปๅŠก 8: ไธป็ช—ๅฃ้›†ๆˆ - -**ๆ–‡ไปถ**๏ผš`src/app.py` - -**ๅธƒๅฑ€**๏ผš -``` -โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” -โ”‚ Piano Highlight Generator [_][โ–ก][X] โ”‚ -โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค -โ”‚ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ -โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ -โ”‚ โ”‚ ้…็ฝฎ้ขๆฟ โ”‚ โ”‚ ่ฟ›ๅบฆ่ง†ๅ›พ โ”‚ โ”‚ -โ”‚ โ”‚ ConfigPanel โ”‚ โ”‚ ProgressView โ”‚ โ”‚ -โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ -โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ -โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ โ”‚ -โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค โ”‚ -โ”‚ โ”‚ โ”‚ โ”‚ ๆ ‡้ข˜็ผ–่พ‘ๅ™จ (ๆŠ˜ๅ ) โ”‚ โ”‚ -โ”‚ โ”‚ โ”‚ โ”‚ TitleEditor โ”‚ โ”‚ -โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ -โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค -โ”‚ ็Šถๆ€: ๅฐฑ็ปช v1.0 โ”‚ -โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ -``` - -**้›†ๆˆ้€ป่พ‘**๏ผš -1. ConfigPanel ๅ‘ๅ‡บ `config_changed_signal` โ†’ Controller ๆŽฅๆ”ถ -2. ็”จๆˆท็‚นๅ‡ป"ๅผ€ๅง‹" โ†’ Controller ๅฏๅŠจ Worker -3. Worker ๅ‘ๅ‡บ `titles_ready_signal` โ†’ TitleEditor ๆ˜พ็คบ -4. ็”จๆˆท็กฎ่ฎคๆ ‡้ข˜ โ†’ TitleEditor ๅ‘ๅ‡บ `titles_confirmed_signal` โ†’ Worker ็ปง็ปญ -5. Worker ๅ‘ๅ‡บ `progress_signal` โ†’ ProgressView ๆ›ดๆ–ฐ - -**้ชŒๆ”ถๆ ‡ๅ‡†**๏ผš -- ่ƒฝๅฏๅŠจๅค„็† -- ๅ„็ป„ไปถ่ƒฝๆญฃ็กฎ้€šไฟก -- ๆ ‡้ข˜็ผ–่พ‘ๅ™จๅœจๆญฃ็กฎๆ—ถๆœบๆ˜พ็คบ - ---- - -## ไปปๅŠก 9: ๆ ธๅฟƒๆจกๅ—้€‚้… - -**ๆ–‡ไปถ**๏ผš`src/core/` ๏ผˆๅค็”จ็Žฐๆœ‰ๆจกๅ—๏ผ‰ - -**้€‚้…ๅทฅไฝœ**๏ผš -1. ๅคๅˆถ `scripts/` ไธ‹็š„ๆ ธๅฟƒๆจกๅ—ๅˆฐ `src/core/` -2. ไฟฎๆ”น import ่ทฏๅพ„ -3. ็กฎไฟ `constants.py` ไธญ็š„่ทฏๅพ„้…็ฝฎๅฏไปŽๅค–้ƒจไผ ๅ…ฅ -4. ้€‚้…่ง†้ข‘ๅค„็†ๅ‡ฝๆ•ฐ่ฟ”ๅ›žๆˆๅŠŸ/ๅคฑ่ดฅ็Šถๆ€ - -**้ชŒๆ”ถๆ ‡ๅ‡†**๏ผš -- ๆ ธๅฟƒๆจกๅ—่ƒฝๅœจ GUI ไธญๆญฃๅธธ่ฐƒ็”จ -- ้”™่ฏฏ่ƒฝ่ขซๆ•่Žทๅนถไผ ้€’ๅˆฐ UI - ---- - -## ไปปๅŠก 10: ้”™่ฏฏๅค„็†ๅฎŒๅ–„ - -**ๅค„็†ๅœบๆ™ฏ**๏ผš - -| ๅœบๆ™ฏ | ๅค„็†ๆ–นๅผ | -|------|----------| -| API Key ๆ— ๆ•ˆ | 401 ้”™่ฏฏ๏ผŒๆ็คบ็”จๆˆทๆฃ€ๆŸฅ้…็ฝฎ | -| ่ง†้ข‘ๆ–‡ไปถไธๅญ˜ๅœจ | ๆš‚ๅœ๏ผŒๅผน็ช—ๆ็คบ | -| ็ฃ็›˜็ฉบ้—ดไธ่ถณ | ๆš‚ๅœ๏ผŒๅผน็ช—ๆ็คบ | -| Whisper ๆจกๅž‹ๆœชๆ‰พๅˆฐ | ๆ็คบไธ‹่ฝฝๆˆ–้€‰ๆ‹ฉๅ…ถไป–ๆจกๅž‹ | -| ๅค„็†ๅผ‚ๅธธ | ไฟๅญ˜็Šถๆ€๏ผŒๆ˜พ็คบ้”™่ฏฏๆ—ฅๅฟ— | - -**้ชŒๆ”ถๆ ‡ๅ‡†**๏ผš -- ้”™่ฏฏไธๅฏผ่‡ด็จ‹ๅบๅดฉๆบƒ -- ้”™่ฏฏไฟกๆฏๆธ…ๆ™ฐ็”จๆˆทๅ‹ๅฅฝ -- ็Šถๆ€ๆญฃ็กฎไฟๅญ˜ - ---- - -## ไปปๅŠก 11: ๆ‰“ๅŒ…้…็ฝฎ + ๆต‹่ฏ• - -**ๆ–‡ไปถ**๏ผš -- `nuitka_options.py` -- `build.bat` (Windows ๆ‰“ๅŒ…่„šๆœฌ) -- `build.sh` (Linux/Mac ๆ‰“ๅŒ…่„šๆœฌ) - -**ๆ‰“ๅŒ…ๆญฅ้ชค**๏ผš -1. ๅฎ‰่ฃ…ไพ่ต–๏ผš`pip install -r requirements.txt` -2. ๅผ€ๅ‘ๆต‹่ฏ•๏ผš`python src/main.py` -3. ๆ‰“ๅŒ…๏ผš`python -m nuitka nuitka_options.py` -4. ๆต‹่ฏ• exe๏ผš`dist/PianoHighlightGenerator.exe` - -**้ชŒๆ”ถๆ ‡ๅ‡†**๏ผš -- ๆ‰“ๅŒ…ๅŽไฝ“็งฏ < 50MB -- ๅŒๅ‡ป่ƒฝๆญฃๅธธ่ฟ่กŒ -- ๆ‰€ๆœ‰ๅŠŸ่ƒฝๅœจๆ‰“ๅŒ…ๅŽๆญฃๅธธๅทฅไฝœ - ---- - -## ไปปๅŠก 12: ๆ–‡ๆกฃๅ’Œ README - -**ๆ–‡ไปถ**๏ผš`README.md` - -**ๅ†…ๅฎน**๏ผš -- ๅบ”็”จไป‹็ปๅ’Œๆˆชๅ›พ -- ็ณป็ปŸ่ฆๆฑ‚ -- ๅฎ‰่ฃ…ๆŒ‡ๅ—๏ผˆๅผ€ๅ‘ๅฎ‰่ฃ…ใ€ๆ‰“ๅŒ…ๅฎ‰่ฃ…๏ผ‰ -- ไฝฟ็”จ่ฏดๆ˜Ž -- ๅธธ่ง้—ฎ้ข˜ -- ่ฎธๅฏ่ฏ - -**้ชŒๆ”ถๆ ‡ๅ‡†**๏ผš -- ็”จๆˆท่ƒฝๆ นๆฎ README ่ฟ่กŒๅบ”็”จ -- ๅธธ่ง้—ฎ้ข˜ๆœ‰่งฃๅ†ณๆ–นๆกˆ - ---- - -## ๅนถ่กŒๅŒ–ๅˆ†ๆž - -**ๅฏๅนถ่กŒไปปๅŠก**๏ผš - -| ไปปๅŠก็ป„ | ๅฏๅนถ่กŒไปปๅŠก | ๅŽŸๅ›  | -|--------|-----------|------| -| UI ็ป„ไปถ็ป„ | 2, 4, 7 | ็‹ฌ็ซ‹ๅผ€ๅ‘๏ผŒ็‹ฌ็ซ‹ UI ็ป„ไปถ | -| ๆ ธๅฟƒ้€ป่พ‘็ป„ | 3, 5, 6 | ๆœ‰ไพ่ต–ๅ…ณ็ณป๏ผŒ้œ€้กบๅบๅผ€ๅ‘ | -| ้›†ๆˆๆต‹่ฏ•็ป„ | 8, 10 | ไพ่ต–ๅ‰้ขๆ‰€ๆœ‰ไปปๅŠก | - -**ๆŽจ่ๅผ€ๅ‘้กบๅบ**๏ผš -1. **็ฌฌไธ€ๆณข๏ผˆๅฏๅนถ่กŒ๏ผ‰**๏ผš1, 2, 4, 7, 9 -2. **็ฌฌไบŒๆณข๏ผˆไพ่ต–็ฌฌไธ€ๆณข๏ผ‰**๏ผš3, 5, 6 -3. **็ฌฌไธ‰ๆณข๏ผˆ้›†ๆˆ๏ผ‰**๏ผš8, 10 -4. **ๆœ€ๅŽ**๏ผš11, 12 - ---- - -## Git ๅˆ†ๆ”ฏ่ง„ๅˆ’ - -**ๅปบ่ฎฎๅˆ†ๆ”ฏ**๏ผš -- `main` - ไธปๅˆ†ๆ”ฏ๏ผŒ็จณๅฎšไปฃ็  -- `feat/ui` - UI ็ป„ไปถๅผ€ๅ‘ (ไปปๅŠก 2, 4, 7) -- `feat/core` - ๆ ธๅฟƒ้€ป่พ‘ๅผ€ๅ‘ (ไปปๅŠก 3, 5, 6) -- `feat/integration` - ้›†ๆˆๅ’Œๆ‰“ๅŒ… (ไปปๅŠก 8, 10, 11, 12) - -**ๅˆๅนถ้กบๅบ**๏ผš -``` -feat/ui โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” -feat/core โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ–บ main -feat/integration โ”€โ”˜ -```