- PHP 99.8%
- Dockerfile 0.1%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
| .devcontainer | ||
| .idea | ||
| bin | ||
| config | ||
| docker | ||
| docs | ||
| migrations | ||
| public | ||
| src | ||
| tests | ||
| .dockerignore | ||
| .editorconfig | ||
| .env | ||
| .env.dev | ||
| .env.docker | ||
| .env.docker.local.example | ||
| .env.test | ||
| .gitignore | ||
| .php-cs-fixer.dist.php | ||
| AGENTS.md | ||
| composer.json | ||
| composer.lock | ||
| deptrac.yaml | ||
| infection.json5 | ||
| php.ini | ||
| phpstan.dist.neon | ||
| phpunit.dist.xml | ||
| README.md | ||
| symfony.lock | ||
SymfonyAudioBackend
This project is an audio library backend for managing and streaming music, playlists, podcasts, and background sounds. It handles audio uploads, metadata extraction, Opus compression, podcast downloads, and artwork generation.
This server is meant to be used with a dedicated administration application for managing the media on the server and with OpenSubsonic-compatible clients (e.g., Feishin, Tempus, Symfonium) for consuming the media.
Technologies
| Area | Main technologies |
|---|---|
| Application | PHP 8.5+, Symfony 8.1, Composer |
| Persistence | Doctrine ORM 3, Doctrine Migrations, MariaDB 12 |
| Background work | Symfony Messenger and Scheduler, LavinMQ message queues |
| Caching and coordination | Valkey 9, Symfony Cache and Lock |
| API contracts | Symfony Serializer, ObjectMapper, and Validator; NelmioApiDocBundle and swagger-php for OpenAPI documentation |
| Media processing | FFmpeg for Opus compression, ImageMagick for thumbnails, PHP parsers for FLAC and Opus metadata |
| External systems | MusicBrainz and Cover Art Archive for release information and artwork; podcast RSS feeds; optional LocalAI or OpenAI image generation through Symfony AI |
| Runtime and development | Docker/Podman Compose, devcontainers; FrankenPHP and Caddy in the production stack |
Application architecture
The application is internally split into multiple layers with distinct responsibilities.
| Layer | Responsibility |
|---|---|
| Controller | Controllers expose HTTP endpoints. Each endpoint triggers an application process or returns data from a DisplayRepository |
| DisplayRepository | DisplayRepositories provide read-only access to the database and return the data as use-case specific DTOs. |
| Process | Processes represent, usually multi-step, operations that change the application state. Process classes use Services and Repositories in order to carry out a specific task while coordinating database transactions and external locks. |
| Service | Services provide functions to change the application state or access external systems. Services are only responsible for providing an abstract interface to the system behind them. |
| Repository | Repositories provide read-write access to the database and return data as Doctrine entities. |
| MessageHandler | Message handlers process messages from the MessageQueue and trigger application processes. |
| EventListener | Event listeners react to domain events and trigger synchronous or asynchronous actions via the MessageQueue or the Services. |
| Utility | Utility classes provide reusable functions to the rest of the application. Utilities never change the state of the application. |
The application uses multiple worker processes to handle async tasks. Multiple queues are used to avoid that short-running tasks (e.g., processing the outbox, removing old files) are blocked by long-running tasks (e.g., compressing media files, updating podcasts and thumbnails).
| Worker | Purpose |
|---|---|
| Async | Processes messages that can be processed quickly (e.g. processing the outbox, removing old files) |
| Compression | Converts media files to OPUS files. |
| Podcast | Updates the stored podcasts by checking their current RSS feeds, downloading new episodes and clean-up old episodes. |
| Thumbnail | Generates thumbnails for entities (album, artist, genre, playlist). |
The documentation index contains architecture and data-model diagrams, plus guides to individual processes.
The API of the application is split into two parts:
- The
/apiendpoints serve the dedicated administration application. - The
/restendpoints provide partial OpenSubsonic compatibility for audio clients.
See the API guide for supported endpoints and compatibility limitations.
Code quality and testing tools
The project uses the following tools to ensure correctness, code quality, and coding style consistency:
| Tool | Purpose |
|---|---|
| PHPUnit | Automated tests |
| Infection | Mutation testing |
| PHPStan | Static analysis |
| Deptrac | Architecture dependency checks |
| PHP-CS-Fixer | Automated formatting |
| NelmioApiDocBundle | API documentation |
| SPX | Request profiling |
Development with devcontainers
The application was developed using devcontainers to provide a reproducible development environment. The devcontainer provides an environment with all required PHP dependencies and external systems (e.g., MariaDB, Valkey, LavinMQ).
To use the devcontainer, Docker or Podman as well as a compatible IDE (e.g. PHPStorm, VSCode) are required.
The provided devcontainer configuration was tested with PHPStorm and Podman. Different environments might require changes to the configuration.
If the provided devcontainer configuration was used to load this project, the following steps have to be executed inside the container to run the application:
- Install Composer dependencies
composer install
- Run the migrations
php bin/console doctrine:migrations:migrate --no-interaction
- Run the development server
composer run dev
- Run the background worker
php bin/console messenger:consume async compression podcast thumbnail scheduler_default
The API is available at http://localhost:8000 through the forwarded port.
The application is able to generate thumbnails for artists, genres and playlists with AI.
This option is disabled by default. It can be enabled by setting the environment variable APP_AI_GENERATE_THUMBNAILS to true in .env.dev.local.
The thumbnails can be generated either by a LocalAI instance or through OpenAI.
To enable local generation, the variable APP_AI_USE_LOCAL_AI has to be set to true and the variable APP_LOCALAI_BASE_URL has to be set to the URL of the LocalAI instance. The LocalAI instance has to have the model qwen-image-2.1-q4_k-ggml available.
To use OpenAI for thumbnail generation, a valid OpenAI API key has to be provided in the variable APP_OPENAI_API_KEY in .env.dev.local.
Production setup with Docker/Podman compose
The application is designed to be run with Docker or Podman compose. The included "compose.yaml" contains all the necessary services to run the application.
The steps required to set up the docker/podman environment are documented in the docker README.
AI-assisted development
The OpenSubsonic endpoints, OpenAPI documentation, and audio file parsing logic were implemented using an AI-assisted workflow.