|
| 1 | +# Copilot Coding Agent Instructions for ttmp32gme |
| 2 | + |
| 3 | +## Repository Overview |
| 4 | + |
| 5 | +**ttmp32gme** is a cross-platform tool that converts MP3/audio files into TipToi GME (Game Music Engine) files playable on the TipToi pen. It generates printable control sheets with OID codes for music/audiobook playback control. |
| 6 | + |
| 7 | +### Key Stats |
| 8 | +- **Languages**: Python (backend), JavaScript (frontend), Shell scripts (testing) |
| 9 | +- **Size**: ~500 files, primarily Python source in `src/ttmp32gme/` |
| 10 | +- **Framework**: Flask 3.0+ web application with Jinja2 templates |
| 11 | +- **Database**: SQLite with custom DBHandler class |
| 12 | +- **Python Version**: 3.11+ required |
| 13 | +- **Testing**: 45+ tests (unit, integration, E2E with Selenium) |
| 14 | + |
| 15 | +### Architecture |
| 16 | +- **Backend**: Python Flask application migrated from Perl |
| 17 | +- **Database Layer**: Unified DBHandler class (`src/ttmp32gme/db_handler.py`) |
| 18 | + - All database operations go through DBHandler singleton |
| 19 | + - Thread-safe SQLite with `check_same_thread=False` |
| 20 | + - Never use raw cursors outside DBHandler - always use `db.execute()`, `db.fetchone()`, etc. |
| 21 | +- **Validation**: Pydantic models in `db_handler.py` validate all frontend input |
| 22 | +- **Dependencies**: tttool (external binary), ffmpeg (optional for OGG) |
| 23 | + |
| 24 | +## Development Workflow |
| 25 | + |
| 26 | +### Bootstrap & Setup |
| 27 | + |
| 28 | +```bash |
| 29 | +# Clone repository |
| 30 | +git clone https://github.com/thawn/ttmp32gme.git && cd ttmp32gme |
| 31 | + |
| 32 | +# Install Python dependencies (recommended: use uv) |
| 33 | +uv pip install -e ".[test]" |
| 34 | +# OR |
| 35 | +pip install -e ".[test]" |
| 36 | + |
| 37 | +# Install external dependencies |
| 38 | +# - tttool: https://github.com/entropia/tip-toi-reveng#installation |
| 39 | +# - ffmpeg: sudo apt-get install ffmpeg (Ubuntu) or brew install ffmpeg (macOS) |
| 40 | +``` |
| 41 | + |
| 42 | +**Verification**: `python -m ttmp32gme.ttmp32gme --help` should show usage info. |
| 43 | + |
| 44 | +### Running the Application |
| 45 | + |
| 46 | +```bash |
| 47 | +# Start server (default: localhost:10020) |
| 48 | +python -m ttmp32gme.ttmp32gme |
| 49 | + |
| 50 | +# Or use entry point |
| 51 | +ttmp32gme |
| 52 | + |
| 53 | +# Custom host/port |
| 54 | +ttmp32gme --host 0.0.0.0 --port 8080 |
| 55 | + |
| 56 | +# Access UI at http://localhost:10020 |
| 57 | +``` |
| 58 | + |
| 59 | +**Verification**: `curl http://localhost:10020/` should return HTML. |
| 60 | + |
| 61 | +### Testing |
| 62 | + |
| 63 | +#### Unit Tests (Fast, no dependencies) |
| 64 | +```bash |
| 65 | +# Run all unit tests |
| 66 | +pytest tests/unit/ -v |
| 67 | + |
| 68 | +# Run specific test file |
| 69 | +pytest tests/unit/test_library_handler.py -v |
| 70 | +``` |
| 71 | + |
| 72 | +#### Integration Tests (Requires running server) |
| 73 | +```bash |
| 74 | +# Terminal 1: Start server |
| 75 | +python -m ttmp32gme.ttmp32gme |
| 76 | + |
| 77 | +# Terminal 2: Run integration tests |
| 78 | +pytest tests/test_web_frontend.py -v |
| 79 | +``` |
| 80 | + |
| 81 | +#### E2E Tests (Selenium, requires full setup) |
| 82 | +```bash |
| 83 | +# One-time setup (installs Chrome, tttool, etc.) |
| 84 | +./ttmp32gme > /tmp/server.log 2>&1 & sleep(2) # Start server in background |
| 85 | + |
| 86 | +# Run all E2E tests |
| 87 | +./pytest tests/e2e/ -v |
| 88 | + |
| 89 | +# Run specific test |
| 90 | +pytest tests/e2e/test_upload_album_with_files.py -v |
| 91 | + |
| 92 | +# Run tests matching keyword |
| 93 | +pytest -k upload -v |
| 94 | +``` |
| 95 | + |
| 96 | +**E2E Test Markers**: |
| 97 | +- Skip E2E tests: `pytest -m "not e2e"` |
| 98 | +- Skip slow tests: `pytest -m "not slow"` |
| 99 | + |
| 100 | +### Building & Linting |
| 101 | + |
| 102 | +**No formal linting configured** - follow existing code style: |
| 103 | +- 4-space indentation |
| 104 | +- Type hints encouraged (especially with Pydantic) |
| 105 | +- Descriptive variable names |
| 106 | + |
| 107 | +**No build step required** - Python source runs directly. |
| 108 | + |
| 109 | +## Code Patterns & Conventions |
| 110 | + |
| 111 | +### Database Access (CRITICAL) |
| 112 | +❌ **NEVER do this**: |
| 113 | +```python |
| 114 | +cursor = db.cursor() |
| 115 | +cursor.execute("SELECT ...") |
| 116 | +``` |
| 117 | + |
| 118 | +✅ **ALWAYS do this**: |
| 119 | +```python |
| 120 | +result = db.fetchone("SELECT ...") # or db.fetchall() |
| 121 | +``` |
| 122 | +All database operations MUST go through DBHandler methods (except in unit tests). |
| 123 | + |
| 124 | +### Input Validation |
| 125 | +All frontend input MUST be validated with Pydantic models in `db_handler.py`: |
| 126 | +```python |
| 127 | +from pydantic import ValidationError |
| 128 | + |
| 129 | +try: |
| 130 | + validated = AlbumUpdateModel(**data) |
| 131 | + db.update_album(validated.model_dump(exclude_none=True)) |
| 132 | +except ValidationError as e: |
| 133 | + return jsonify({"success": False, "error": str(e)}), 400 |
| 134 | +``` |
| 135 | + |
| 136 | +### Test Fixtures |
| 137 | +Use context managers for test file management: |
| 138 | +```python |
| 139 | +with test_audio_files_context() as test_files: |
| 140 | + # Files created automatically |
| 141 | + driver.find_element(...).send_keys(test_files[0]) |
| 142 | + # Files cleaned up automatically on exit |
| 143 | +``` |
| 144 | + |
| 145 | +### Threading |
| 146 | +SQLite connection uses `check_same_thread=False` for Flask's multi-threaded environment. DBHandler is safe to use across request threads. |
| 147 | + |
| 148 | +## Common Tasks |
| 149 | + |
| 150 | +### Adding a New Database Operation |
| 151 | +1. Add method to `DBHandler` class in `src/ttmp32gme/db_handler.py` |
| 152 | +2. Use `self.execute()`, `self.fetchone()`, `self.commit()` internally |
| 153 | +3. Call from Flask route: `db.new_method()` |
| 154 | + |
| 155 | +### Adding Input Validation |
| 156 | +1. Create/extend Pydantic model in `db_handler.py` |
| 157 | +2. Add field constraints (`Field()`, regex patterns, value ranges) |
| 158 | +3. Validate in Flask route before calling DBHandler |
| 159 | + |
| 160 | +### Adding a New Route |
| 161 | +1. Add route in `src/ttmp32gme/ttmp32gme.py` |
| 162 | +2. Validate input with Pydantic |
| 163 | +3. Use `db` (DBHandler instance) for database operations |
| 164 | +4. Return JSON for AJAX or render template for pages |
| 165 | + |
| 166 | +### Fixing E2E Test Issues |
| 167 | +1. Before re-running specific test, start the server manually: |
| 168 | +```bash |
| 169 | + ./ttmp32gme > /tmp/server.log 2>&1 & sleep(2) # Start server in background |
| 170 | +``` |
| 171 | +2. Check server logs in `/tmp/server.log` for errors |
| 172 | +3. Add debug statements to test for element visibility |
| 173 | +4. Use explicit waits: `WebDriverWait(driver, 5).until(...)` |
| 174 | + |
| 175 | +## File Locations |
| 176 | + |
| 177 | +- **Source**: `src/ttmp32gme/` (main application) |
| 178 | +- **Templates**: `resources/` (Jinja2 HTML templates) |
| 179 | +- **Tests**: `tests/unit/`, `tests/e2e/`, `tests/test_web_frontend.py` |
| 180 | +- **Static files**: `resources/assets/` (CSS, JS, images) |
| 181 | +- **Config**: `pyproject.toml` (dependencies, pytest config) |
| 182 | + |
| 183 | +## Quick Reference Commands |
| 184 | + |
| 185 | +```bash |
| 186 | +# Install dependencies |
| 187 | +uv pip install -e ".[test]" |
| 188 | + |
| 189 | +# Run app |
| 190 | +python -m ttmp32gme.ttmp32gme |
| 191 | + |
| 192 | +# Run all tests |
| 193 | +pytest tests/ -v |
| 194 | + |
| 195 | +# Run unit tests only |
| 196 | +pytest tests/unit/ -v |
| 197 | + |
| 198 | +# Setup E2E environment |
| 199 | +./setup_e2e_environment.sh -b |
| 200 | + |
| 201 | +# Run E2E tests |
| 202 | +./run_e2e_tests.sh |
| 203 | + |
| 204 | +# Run specific E2E test |
| 205 | +./run_e2e_tests.sh -t test_upload_album_with_files |
| 206 | +``` |
| 207 | + |
| 208 | +## CI/CD |
| 209 | + |
| 210 | +GitHub Actions workflows run automatically on PRs: |
| 211 | +- **python-tests.yml**: Unit and integration tests (Python 3.11, 3.12, 3.13) |
| 212 | +- **javascript-tests.yml**: Frontend Jest tests (Node 18.x, 20.x) |
| 213 | +- **e2e-tests.yml**: Full E2E suite (manual trigger or workflow_dispatch) |
| 214 | + |
| 215 | +Tests must pass before merging. |
0 commit comments