Add Docker stack, Reverb live progress, and duplicate upload detection.
Ship FrankenPHP Compose services with Reverb WebSockets for real-time transcription status, skip re-uploading identical audio via content hash, and format disk usage without requiring intl.
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
|
||||
Upload pocket-recorder audio (MP3, WAV, OGG, and more), extract embedded metadata, and transcribe locally with [faster-whisper-server](https://github.com/fedirz/faster-whisper-server) via Docker. Audio never leaves your machine.
|
||||
|
||||
Built with Laravel 13, Blade, Tailwind CSS 4, and [Laravel AI](https://github.com/laravel/ai).
|
||||
Built with Laravel 13, Blade, Alpine.js, Tailwind CSS 4, [Laravel Reverb](https://laravel.com/docs/reverb), FrankenPHP, and [Laravel AI](https://github.com/laravel/ai).
|
||||
|
||||
## Features
|
||||
|
||||
@@ -10,65 +10,70 @@ Built with Laravel 13, Blade, Tailwind CSS 4, and [Laravel AI](https://github.co
|
||||
- Automatic metadata extraction when tags are present (title, artist, album, duration, recorded date)
|
||||
- Search recordings by title, artist, or transcript
|
||||
- Queued local transcription (faster-whisper in Docker)
|
||||
- Live transcription progress (stage, %, elapsed time)
|
||||
- Live transcription progress over WebSockets (Reverb) on the list and detail pages
|
||||
- Stop or restart a run anytime
|
||||
- Copy finished transcripts from the recording detail page
|
||||
|
||||
|
||||
|
||||
## Requirements
|
||||
|
||||
- PHP 8.3+ (8.5 recommended)
|
||||
- Composer
|
||||
- Node.js & npm
|
||||
- SQLite (default) or another supported database
|
||||
- [Docker](https://docs.docker.com/get-docker/) for the Whisper container
|
||||
- [Docker](https://docs.docker.com/get-docker/) and Docker Compose (primary way to run the app)
|
||||
- For native PHP development: PHP 8.3+ (8.5 recommended), Composer, Node.js & npm, SQLite
|
||||
|
||||
|
||||
|
||||
## Setup
|
||||
## Quick start (Docker + FrankenPHP)
|
||||
|
||||
```bash
|
||||
composer setup
|
||||
```
|
||||
|
||||
That installs PHP and JS dependencies, copies `.env` if needed, generates the app key, runs migrations, and builds frontend assets.
|
||||
|
||||
Or step by step:
|
||||
|
||||
```bash
|
||||
composer install
|
||||
cp .env.example .env
|
||||
php artisan key:generate
|
||||
touch database/database.sqlite # if using SQLite
|
||||
php artisan migrate
|
||||
npm install
|
||||
npm run build
|
||||
# Set a key (required by the app container):
|
||||
php artisan key:generate # or: docker run --rm -v "$PWD":/app -w /app composer:2 php artisan key:generate
|
||||
|
||||
docker compose up --build
|
||||
```
|
||||
|
||||
Open [http://localhost:8080/recordings](http://localhost:8080/recordings).
|
||||
|
||||
| Service | Host port | Role |
|
||||
| --- | --- | --- |
|
||||
| `app` | `8080` | FrankenPHP (Laravel) |
|
||||
| `reverb` | `8081` | WebSockets for live status |
|
||||
| `whisper` | `8090` | faster-whisper API |
|
||||
| `queue` | — | `queue:work` for transcription jobs |
|
||||
|
||||
## Local Whisper (Docker)
|
||||
### Persistent data mounts
|
||||
|
||||
Transcription calls an OpenAI-compatible HTTP API. This project ships Compose for that:
|
||||
These host directories are bind-mounted into `app`, `queue`, and `reverb`:
|
||||
|
||||
| Host path | Container path | Contents |
|
||||
| --- | --- | --- |
|
||||
| `./database` | `/app/database` | SQLite database |
|
||||
| `./storage/app` | `/app/storage/app` | Uploaded audio (`private/recordings`) |
|
||||
| `./storage/logs` | `/app/storage/logs` | Application logs |
|
||||
|
||||
Whisper model cache uses the named volume `whisper-huggingface-cache`.
|
||||
|
||||
### GPU Whisper (optional)
|
||||
|
||||
```bash
|
||||
# CPU (works everywhere; slower on long files)
|
||||
docker compose up -d whisper
|
||||
|
||||
# Optional: NVIDIA GPU
|
||||
docker compose --profile gpu up -d whisper-gpu
|
||||
docker compose --profile gpu up -d --build
|
||||
```
|
||||
|
||||
First start downloads the model into a Docker volume (can take a few minutes).
|
||||
Point `LOCAL_WHISPER_URL` at the GPU service if you run it instead of the CPU `whisper` service.
|
||||
|
||||
Check it:
|
||||
### Useful Compose env
|
||||
|
||||
```bash
|
||||
curl -s http://127.0.0.1:8090/health
|
||||
```
|
||||
Copy values from `.env.example`. Important Docker-oriented variables:
|
||||
|
||||
Laravel talks to it at `LOCAL_WHISPER_URL` (default `http://127.0.0.1:8090/v1`). Port **8090** is used so it does not conflict with `php artisan serve` on 8000.
|
||||
| Variable | Purpose |
|
||||
| --- | --- |
|
||||
| `APP_KEY` | Required — containers refuse to start without it |
|
||||
| `APP_URL` | Default `http://localhost:8080` |
|
||||
| `APP_HOST_PORT` | Host port for FrankenPHP (default `8080`) |
|
||||
| `REVERB_HOST_PORT` | Host port for Reverb WebSockets (default `8081`) |
|
||||
| `WHISPER_HOST_PORT` | Host port for Whisper (default `8090`) |
|
||||
| `REVERB_APP_*` | Reverb credentials (baked into frontend at image build for `VITE_REVERB_*`) |
|
||||
| `LOCAL_WHISPER_MODEL` | Model name for local transcription |
|
||||
| `TRANSCRIPTION_TIMEOUT` | Job/HTTP timeout in seconds (default `600`) |
|
||||
|
||||
Inside Compose, Laravel talks to Whisper at `http://whisper:8000/v1` and publishes broadcasts to the `reverb` service. The browser connects to Reverb on `localhost:8081`.
|
||||
|
||||
Stop:
|
||||
|
||||
@@ -76,60 +81,34 @@ Stop:
|
||||
docker compose down
|
||||
```
|
||||
|
||||
|
||||
|
||||
## Configuration
|
||||
|
||||
Copy values from `.env.example`. The transcription-related settings are:
|
||||
|
||||
|
||||
| Variable | Purpose |
|
||||
| ----------------------- | -------------------------------------------------------------------------------- |
|
||||
| `LOCAL_WHISPER_URL` | Local faster-whisper base URL (default `http://127.0.0.1:8090/v1`) |
|
||||
| `LOCAL_WHISPER_API_KEY` | API key for local server (often unused) |
|
||||
| `LOCAL_WHISPER_MODEL` | Model name for local transcription |
|
||||
| `WHISPER_HOST_PORT` | Host port published by Compose (default `8090`) |
|
||||
| `TRANSCRIPTION_TIMEOUT` | Job/HTTP timeout in seconds (default `600`) |
|
||||
| `DB_QUEUE_RETRY_AFTER` | Database queue retry window; must exceed `TRANSCRIPTION_TIMEOUT` (default `660`) |
|
||||
| `QUEUE_CONNECTION` | Use `database` (default) so transcription runs in the background |
|
||||
|
||||
|
||||
Finished transcripts are stored on the recording (`transcript` column) and are included in the recordings search box (title, artist, album, filename, and transcript).
|
||||
|
||||
Ensure `APP_URL` matches how you access the app (default `http://localhost:8000`).
|
||||
|
||||
## Running locally
|
||||
|
||||
Start Whisper, then the app stack:
|
||||
## Native PHP development (optional)
|
||||
|
||||
```bash
|
||||
docker compose up -d whisper
|
||||
composer run dev
|
||||
```
|
||||
|
||||
Or separately:
|
||||
|
||||
```bash
|
||||
docker compose up -d whisper
|
||||
php artisan serve
|
||||
composer setup
|
||||
docker compose up -d whisper reverb
|
||||
# In separate terminals:
|
||||
php artisan serve --port=8000
|
||||
php artisan queue:work
|
||||
php artisan reverb:start
|
||||
npm run dev
|
||||
```
|
||||
|
||||
Open [http://localhost:8000/recordings](http://localhost:8000/recordings).
|
||||
Set `APP_URL=http://localhost:8000`, `LOCAL_WHISPER_URL=http://127.0.0.1:8090/v1`, and Reverb `REVERB_HOST=localhost` / `REVERB_PORT=8080` (match `VITE_REVERB_*`).
|
||||
|
||||
Transcription jobs are queued — keep a queue worker running or jobs will stay pending.
|
||||
## Configuration
|
||||
|
||||
Finished transcripts are stored on the recording (`transcript` column) and are included in the recordings search box (title, artist, album, filename, and transcript).
|
||||
|
||||
`DB_QUEUE_RETRY_AFTER` must exceed `TRANSCRIPTION_TIMEOUT` so long Whisper jobs are not re-queued mid-run.
|
||||
|
||||
## Usage
|
||||
|
||||
1. **Upload** audio from Recordings → Upload (single file or batch dropzone).
|
||||
2. Transcription queues automatically — keep `php artisan queue:work` running.
|
||||
3. Watch live progress on the recording page (or stop and restart).
|
||||
2. Transcription queues automatically — the Compose `queue` service (or `php artisan queue:work`) must be running.
|
||||
3. Watch live progress on the recordings list or detail page (Reverb); stop and restart anytime.
|
||||
4. Search the list by title, artist, or transcript text.
|
||||
5. If older uploads still show **Queued** with no progress, use **Queue pending transcriptions** on the recordings list.
|
||||
|
||||
|
||||
|
||||
## Tests
|
||||
|
||||
```bash
|
||||
@@ -138,8 +117,6 @@ composer test
|
||||
php artisan test
|
||||
```
|
||||
|
||||
|
||||
|
||||
## License
|
||||
|
||||
MIT
|
||||
MIT
|
||||
|
||||
Reference in New Issue
Block a user