10. The application¶
How VeeJay is built: one program, one database file, and a small number of well-chosen parts.
10.1 The big picture¶
VeeJay is built to be stable, easy to install and easy to understand. Speed is not the deciding factor, because VeeJay spends most of its time waiting for the network and for downloads. What matters more is that it is correct, that it recovers from interruptions, and that it is simple to run.
VeeJay is written from scratch. It borrows ideas from other projects, but it does not build on the code of any of them.
| Part | What it is |
|---|---|
| The VeeJay program | One program that holds the web interface, the API and the background worker. It is written in Python. |
| The library database | A SQLite file with the facts about every artist, song and video, described in 9. The database. |
| The state file | A second, small SQLite file that holds the queue of jobs and their history. |
| The SoulSeek client (slskd) | A bundled program that VeeJay controls through its API. |
| ffmpeg and yt-dlp | Bundled tools that VeeJay starts as separate programs to convert audio and download video. |
| The outside services | YouTube, MusicBrainz, Wikipedia, Wikidata, AcoustID, LRCLIB, the Cover Art Archive, Lidarr, Discord, and the platforms whose playlists VeeJay keeps in step. Each is reached through its own adapter. |
How the parts fit.
- You vote on a video, or change a playlist, on a platform.
- The program notices, puts the song in its queue, and works through the steps: look it up, verify who the artist is, download the video, find the audio, verify it, convert it, tag it, name it, write the NFO and file everything.
- The interface shows it live, so you can see what is happening and answer anything VeeJay needs from you.
- The database is the source. Every file name, tag and NFO file is written from it.
10.2 The back end¶
The back end is written in Python, using the FastAPI framework. Python is chosen mainly for tag writing: its tag library, mutagen, is the one used by MusicBrainz Picard, and it handles the custom tags that VeeJay writes into FLAC, ALAC and M4V files. The tools VeeJay depends on, yt-dlp among them, are Python tools too. The tag writing sits behind an interface, so it can be replaced later if there is ever a reason to.
One program. The web interface, the API and the background worker run together in a single program, organized into separate modules. This keeps VeeJay simple to install and run, and means only one program writes to the database. The heavy work happens outside it: yt-dlp and ffmpeg are started as separate programs, so the VeeJay program stays light.
The outside services. Each service has its own rules, limits and ways of failing, so each is reached through its own adapter, behind a clear interface, and the rest of VeeJay never talks to a service directly.
- Each adapter handles its own request limits, retries and caching. The MusicBrainz adapter, for example, keeps to about one request a second.
- Services of the same kind share one interface. All lyrics providers use the same one, so adding a provider means adding an adapter.
- When a service changes, only its adapter changes.
- A source that has a second route is switched at once when the first stops answering.
The storage layer. The code reaches the database through one storage layer, built on SQLAlchemy, with migrations by Alembic. It owns the SQL, it is the only code that opens the database, and it opens it the same way every time, as described in 9.4.
10.3 The front end¶
The front end is a web app built with React 19, TypeScript, Vite and Tailwind. VeeJay's own program serves it, so nothing separate has to run. To keep it stable:
- Few libraries, with exact versions pinned, so updates happen only when VeeJay chooses.
- Strict TypeScript.
- One way to load data, and no extra state libraries.
- A generated link to the back end. FastAPI describes its API in a standard format, and the TypeScript code that calls it is generated from that description. If the back end changes a field, the front end stops building instead of failing in front of you.
- Automatic screen tests, described in 10.7.
The screens.
| Screen | What it is for |
|---|---|
| Dashboard | The library at a glance: what just arrived, what is downloading, and which services are connected. |
| Activity | What is happening now and what finished: new likes, downloads and their progress, failures. |
| Needs attention | What VeeJay needs from you: a like it could not classify, a song on the Needs audio list, an unverified Primary, and the review items. Each can be answered in a click. |
| Library and Artists | Browsing songs, videos and artists with thumbnails, with filters and bulk editing of many rows at once. |
| Settings | The choices from 10.5, the update and log options, and the controls for backups and restore. |
| Setup | The first-run steps: the library folder, FLAC or ALAC, the sign-ins, the backup location and the existing likes. |
Staying up to date. The interface updates itself as things happen, with no refresh. The server pushes changes to the page over Server-Sent Events, a connection that carries updates in one direction only. Everything a person does, such as answering a question, goes through ordinary requests. Some reverse proxies buffer the stream and delay updates until their settings are changed, and the documentation says how. The front end can also be installed as a web app, and it receives push notifications.
10.4 Background work and recovery¶
One like sets off a long chain of work, and a large import of existing likes can mean thousands of them over days. Restarts, updates, power cuts and network failures will interrupt it, so VeeJay saves its place.
- A persistent queue. Each job is saved in the state file before it starts, and each step is recorded: looked up, video downloaded, audio found, verified, tagged, filed.
- Resuming. After a restart, VeeJay resumes from the last completed step of each job.
- Safe files. A file is written under a temporary name and renamed only when it is complete, so a half-written file never appears in the library. Leftover partial files are cleaned up at start.
- Safe to repeat. Every step can be run twice without harm, in case a step finished but its record did not.
- Retries. A failed step is retried after a delay, up to a limit. After that, the song goes to Needs attention.
- Time limits. Each step has one, so a job cannot stay stuck.
- Limits. The queue respects YouTube's daily quota and each service's request limits, and a large import of existing likes runs slowly in the background.
10.5 Settings, secrets and backups¶
Settings and secrets are kept apart.
- Settings are choices: the library folder, FLAC or ALAC, the lyrics providers, the address of Lidarr and the notification options. They live in one file in VeeJay's data folder.
- Secrets are sign-ins and keys: the YouTube sign-in, the SoulSeek login, the keys for services such as AcoustID, Genius and Lidarr, and a Discord webhook. They live in a separate protected file in the same folder, readable only by VeeJay. They are never kept in the database, are shown masked in the app, and are never written to logs.
The setup screen writes both files, so nobody edits them by hand. Because the secrets file is protected by its permissions, anyone who can read the data folder can read the secrets.
Backups. VeeJay backs up the database and the settings automatically. It does not back up the media files, which are large and which people already protect with their own tools, and it does not back up the secrets.
- Schedule. One backup a day, keeping the last 7 daily copies and the last 4 weekly copies.
- Before a bulk change. A snapshot is taken before any change that touches many rows or files, such as adopting existing files or running a migration, and kept for 30 days.
- Restore. A Restore button brings back any backup. Before it restores, the app says plainly that anything done since that backup will be lost.
- Where they go. At setup VeeJay asks whether you have iCloud or OneDrive. If you do, backups go there, in
VeeJay/backups, so they survive a failed disk. If you do not, they go to abackupsfolder inside VeeJay's own data folder, and the app says they would not survive a failed disk. In a container, the synced folder has to be mapped into it.
After a restore on a new machine, the secrets are entered again, including signing in to the platforms.
Caches. VeeJay remembers lookups so it does not repeat them, in a cache folder apart from the database. The cache can be deleted at any time and is rebuilt, and it is never backed up.
| What is remembered | For how long |
|---|---|
| YouTube video details | 7 days |
| Lookups from MusicBrainz and Wikipedia | 30 days |
| "Not found" results | 30 days, then VeeJay tries again |
10.6 Signing in and the API¶
VeeJay holds your secrets and can move files, so a login is required by default.
- First run. Setup creates an admin account.
- Local network. A setting lets you turn the login off for addresses on your own network only.
- Reverse proxies. Behind a reverse proxy, every request can look as if it comes from the local network, which would let strangers in. VeeJay trusts forwarded addresses only from proxies you name, and the documentation warns about this.
- Resetting a password. VeeJay has no email, so a password is reset with a command on the server.
The API. VeeJay has a documented, versioned API, secured by an API key. The interface uses the same API, so it is built once and tested constantly. The documentation is generated from the code. Other programs and scripts can use the API to read what VeeJay is doing and to control it, and changes to it are versioned so they do not break other people's scripts.
Updating the bundled tools. yt-dlp, ffmpeg and the SoulSeek client update automatically on a schedule. Every update is tested before it takes over, and VeeJay keeps the previous version and rolls back to it if the test fails. In a container, the updated tools live in a folder that survives when the container is replaced.
Logs and problem reports. Log files are written to VeeJay's data folder and rotated by size and age, with secrets removed before anything is written. A Logs screen shows the log, filtered by level and followed live. A support bundle can be downloaded with one button: the recent logs, the version information and the settings, with secrets removed. VeeJay does not send problem reports anywhere by itself.
10.7 Testing and quality¶
VeeJay renames and moves files and rewrites tags in a library. A bug can damage files or send a song to the wrong place, so testing is how the project stays stable. All tests run automatically on every change to the code, and a change that fails them is not accepted.
| Layer | What it tests |
|---|---|
| Rule tests | Every database rule: the wrong value is refused, as described in 9.6. |
| Unit tests | The rules for file names, the Primary, tags and fallbacks. |
| Service tests | Each adapter against saved responses from its service, so they run without the internet. |
| File tests | Writing the tags into small sample FLAC, ALAC and M4V files and reading them back, including the custom tags. |
| Screen tests | The real interface, driven in a browser. |
| Live checks | A small set of checks that run on a schedule against the real services, such as a test download that tells us when yt-dlp stops working. |
Test audio and video are generated for the purpose and are never real songs, so there is no copyright question. Saved responses go out of date and are refreshed when a service changes, and a live check can fail for reasons nobody controls, such as a service being down, so its result is read with judgment.