chore: checkpoint before release v0.2.10-rc.6

This commit is contained in:
2026-03-22 19:23:43 -04:00
parent 4df4361f89
commit 6fcd656668
35 changed files with 328 additions and 42 deletions

View File

@@ -2,6 +2,27 @@
All notable changes to this project will be documented in this file.
## [v0.2.10-rc.5] - 2026-03-22
### Runtime & Queue Control
- Engine runtime modes now support `background`, `balanced`, and `throughput`, with manual concurrency overrides and drain/resume controls exposed through the API and dashboard header.
- Engine status reporting now includes pause source, drain state, concurrent-limit metadata, and mode visibility for troubleshooting.
- Queue management continued to harden with safer active-job controls, clearer failure surfacing, and better per-job operational feedback.
### Media Processing & Library Health
- VAAPI-first Intel handling, remux planning, subtitle sidecars, and library health issue reporting were expanded across the planner, FFmpeg integration, and dashboard.
- Hardware detection and probe logging were improved to make CPU/GPU backend selection and diagnostics easier to understand.
- Stream rules landed for commentary stripping, audio-language filtering, and keeping only the default audio track when needed.
### Paths, Setup & Docs
- Default config and database paths were normalized around the `alchemist` runtime home, and the repo now ships a `justfile` for common dev, release, Docker, and database workflows.
- The docs site moved onto Starlight and now builds locally with a proper content config, localized collection wiring, and a corrected splash-page schema.
- Release automation now bumps repo-wide version manifests, supports checkpoint commits before release validation, and isolates web-e2e onto a separate port so a local server on `3000` does not block release verification.
### CI/CD & Release Tooling
- Docker publishing is now gated behind successful validation, and local release verification covers Rust checks/tests, frontend verification, docs build, `actionlint`, and the reliability Playwright suite.
- `just update` does not create the release commit, git tag, or push until the full validation gate passes.
## [v0.2.10-rc.2] - 2026-03-21
### Stability & Reliability

2
Cargo.lock generated
View File

@@ -26,7 +26,7 @@ dependencies = [
[[package]]
name = "alchemist"
version = "0.2.10-rc.4"
version = "0.2.10-rc.5"
dependencies = [
"anyhow",
"argon2",

View File

@@ -1,6 +1,6 @@
[package]
name = "alchemist"
version = "0.2.10-rc.4"
version = "0.2.10-rc.5"
edition = "2021"
license = "GPL-3.0"
build = "build.rs"

View File

@@ -27,9 +27,11 @@ Everything is visible in the web dashboard. You can see what is running, what wa
- Get a ping when work finishes through Discord, Gotify, or a webhook.
- Keep heavy jobs out of the way with a scheduler for off-peak hours.
- Push urgent files to the front with the priority queue.
- Switch the engine between background, balanced, and throughput modes without restarting the app.
- Let hardware acceleration happen automatically on NVIDIA, Intel, AMD, or Apple, with CPU fallback when needed.
- Preserve HDR metadata or tonemap to SDR depending on what you need.
- Add folders once and let watch folders keep monitoring them automatically.
- Shape audio output with stream rules for commentary stripping, language filtering, and default-track retention.
## Hardware Support

View File

@@ -1 +1 @@
0.2.10-rc.4
0.2.10-rc.5

File diff suppressed because one or more lines are too long

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -1 +1 @@
{"version":"1.4.0","languages":{"en":{"hash":"en_58404df84f","wasm":"en","page_count":25}},"include_characters":["_","‿","⁀","⁔","︳","︴","","","","_"]}
{"version":"1.4.0","languages":{"en":{"hash":"en_4719f7e2e7","wasm":"en","page_count":25}},"include_characters":["_","‿","⁀","⁔","︳","︴","","","","_"]}

Binary file not shown.

Binary file not shown.

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -1,6 +1,6 @@
{
"name": "alchemist-docs",
"version": "0.2.10-rc.4",
"version": "0.2.10-rc.5",
"private": true,
"scripts": {
"dev": "astro dev",

View File

@@ -58,14 +58,41 @@ The front-end will automatically "talk" to the back-end running on port 3000.
We value high-quality, tested code. Please ensure your changes don't break existing functionality.
```bash
# Run all Rust tests
cargo test
# Run the main repo validation gate
just check
# Run front-end linting
# Run the Rust test suite
just test
# Run the frontend verification gate
cd web
bun run lint
bun run verify
# Build the docs site
cd ../docs
bun install --frozen-lockfile
bun run build
# Run the reliability Playwright suite on an isolated test port
cd ../web-e2e
ALCHEMIST_E2E_PORT=4173 bun install --frozen-lockfile
ALCHEMIST_E2E_PORT=4173 bun run test:reliability
```
## Release Workflow
The repo ships a `justfile` for release preparation:
```bash
# Rewrite version files only
just bump 0.2.10-rc.5
# Full guarded release flow
just update v0.2.10-rc.5
```
`just update` can checkpoint dirty local work, validates the repo before release, blocks behind/diverged remote state, and only creates the release commit/tag/push after the full validation gate passes.
## UI Development
The `web/` folder uses **Tailwind CSS** for styling and **Lucide React** for icons. If you add new components, please follow the existing patterns in `web/src/components/ui/`.

View File

@@ -9,6 +9,9 @@ All API endpoints require authentication via Bearer token, except for:
- `/api/health` - Health check (returns status, version, uptime)
- `/api/ready` - Readiness check (returns database status)
Most browser clients use the session cookie returned by `/api/auth/login`.
Bearer auth is still accepted by the backend, but the web UI authenticates with the session cookie automatically.
## Authentication
To use the API, you first need to log in to get a token.
@@ -22,10 +25,7 @@ Content-Type: application/json
"password": "secret"
}
# Response: {"token": "abc123..."}
# Use token in subsequent requests
Authorization: Bearer abc123...
# Response: 200 OK + Set-Cookie: alchemist_session=...
```
## Jobs
@@ -33,7 +33,10 @@ Authorization: Bearer abc123...
Endpoints for managing transcoding jobs.
```bash
# Get all jobs
# Get all jobs (canonical endpoint)
GET /api/jobs
# Legacy alias
GET /api/jobs/table
# Get job stats
@@ -85,10 +88,37 @@ POST /api/engine/pause
# Resume processing
POST /api/engine/resume
# Drain processing (finish active jobs, stop starting new ones)
POST /api/engine/drain
# Cancel drain and resume normal scheduling
POST /api/engine/stop-drain
# Get engine status
GET /api/engine/status
# Inspect engine mode and computed concurrency limits
GET /api/engine/mode
# Set engine mode and optional overrides
POST /api/engine/mode
Content-Type: application/json
{
"mode": "balanced",
"concurrent_jobs_override": 2,
"threads_override": 0
}
```
`/api/engine/status` now returns richer runtime state including:
- `status`: `running`, `paused`, or `draining`
- `manual_paused`
- `scheduler_paused`
- `draining`
- `mode`
- `concurrent_limit`
- `is_manual_override`
## Settings
Endpoints for managing application configuration.
@@ -116,6 +146,25 @@ Content-Type: application/json
}
```
Additional operational endpoints worth knowing:
```bash
# Runtime hardware details
GET /api/system/hardware
# Hardware probe log
GET /api/system/hardware/probe-log
# Library health overview
GET /api/library/health
# Start a library health scan
POST /api/library/health/scan
# List detected health issues
GET /api/library/health/issues
```
## Server-Sent Events
Real-time updates are provided via a Server-Sent Events (SSE) stream.

View File

@@ -5,6 +5,43 @@ description: History of changes and releases for Alchemist.
All notable changes to Alchemist will be documented on this page.
## v0.2.10-rc.5
### Runtime & Queue Control
- ✅ Added engine runtime modes (`background`, `balanced`, `throughput`) with manual overrides and drain/resume controls.
- ✅ Expanded engine status reporting so the UI can distinguish manual pause, scheduler pause, drain state, and effective concurrency.
### Media Processing & Diagnostics
- ✅ Improved VAAPI-first Intel handling, remux planning, subtitle sidecars, and library health issue reporting.
- ✅ Expanded hardware detection and probe logging for clearer CPU/GPU backend diagnostics.
- ✅ Added stream rules for commentary stripping, language filtering, and default-audio retention.
### Tooling & Docs
- ✅ Normalized runtime config/database paths under the `alchemist` home and added a richer `justfile` workflow for development and releases.
- ✅ Migrated docs onto a working Starlight content configuration and refreshed the local docs build path.
- ✅ Hardened local release validation so Docker/tag pushes happen only after all checks pass, including docs and isolated web-e2e runs.
## v0.2.10-rc.2
### Stability & Reliability
- ✅ VMAF quality gating, retry backoff, orphaned temp cleanup, log retention, and auth session cleanup landed together.
- ✅ Resource endpoint caching reduced repeated OS probes from multiple open browser tabs.
### New Features
- ✅ Per-library profiles, the savings dashboard, Library Doctor, and `/api/jobs` as the canonical jobs endpoint.
### Beginner Experience
- ✅ Plain-English skip reasons, first-run redirect handling, setup wizard explanations, and telemetry opt-in.
### Job Management
- ✅ Dedicated skipped/archived tabs and richer sorting controls in the job list.
### UI & Design
- ✅ DM Sans typography, a simplified sidebar active state, tighter radius scale, and a more composable setup wizard.
### Infrastructure
- ✅ Cached Rust/TS/frontend CI, multi-arch Docker images, tarball release assets with checksums, and a pinned-FFmpeg Dockerfile.
## v0.2.10-rc.1
### Safety & Reliability

View File

@@ -5,7 +5,7 @@ description: Detailed reference for Alchemist configuration settings.
This page explains all the settings available in Alchemist. You can change these to control how Alchemist handles your video files and how it uses your computer's hardware.
Alchemist uses a YAML configuration file (usually `config.yaml` in your `/config` volume). Below is a complete list of all supported fields.
Alchemist uses a TOML configuration file, usually `config.toml`. On Linux and macOS the default path is `~/.config/alchemist/config.toml` unless you override it with `ALCHEMIST_CONFIG_PATH`.
## Transcoding Settings (`transcode`)
@@ -19,7 +19,7 @@ Settings that control the actual video conversion process.
| `concurrent_jobs` | integer | `1` | How many transcodes to run at once. | `2` |
| `threads` | integer | `0` | CPU threads per job. `0` means automatic. | `4` |
| `quality_profile` | string | `Balanced` | Speed vs. Quality tradeoff. | `Quality`, `Balanced`, `Speed` |
| `output_codec` | string | `hevc` | The target video format. | `av1`, `hevc`, `h264` |
| `output_codec` | string | `av1` | The target video format. | `av1`, `hevc`, `h264` |
| `allow_fallback` | boolean | `true` | If hardware encoding fails, fall back to software. | `false` |
| `hdr_mode` | string | `preserve` | How to handle HDR video. | `preserve`, `tonemap` |
| `tonemap_algorithm` | string | `hable` | Algorithm for HDR to SDR conversion. | `mobius`, `reinhard`, `clip` |
@@ -28,13 +28,23 @@ Settings that control the actual video conversion process.
| `subtitle_mode` | string | `copy` | How to handle subtitles. | `copy`, `burn`, `none`, `extract` |
| `vmaf_min_score` | float | (Optional) | Minimum VMAF score to accept a transcode. | `93.0` |
### Stream Rules (`transcode.stream_rules`)
Rules for pruning or keeping audio tracks before the output is written.
| Field | Type | Default | Description | Example |
| :--- | :--- | :--- | :--- | :--- |
| `strip_audio_by_title` | list | `[]` | Remove audio tracks whose title contains one of these strings. | `["commentary", "director"]` |
| `keep_audio_languages` | list | `[]` | Keep only audio tracks whose language tag matches these ISO 639-2 codes. | `["eng", "jpn"]` |
| `keep_only_default_audio` | boolean | `false` | Keep only the default audio track unless language rules override it. | `true` |
## Hardware Settings (`hardware`)
Settings for graphics cards and CPU encoding.
| Field | Type | Default | Description | Example |
| :--- | :--- | :--- | :--- | :--- |
| `preferred_vendor` | string | `cpu` | The GPU brand you want to use. | `nvidia`, `intel`, `amd`, `apple` |
| `preferred_vendor` | string | (Auto) | Preferred hardware backend if you want to pin one. | `nvidia`, `intel`, `amd`, `apple`, `cpu` |
| `device_path` | string | (Optional) | Path to the GPU device file. | `/dev/dri/renderD128` |
| `allow_cpu_fallback` | boolean | `true` | Fall back to CPU if no GPU is found. | `false` |
| `cpu_preset` | string | `medium` | Software encoding speed/quality preset. | `slow`, `fast`, `faster` |
@@ -47,7 +57,7 @@ Settings for finding and watching your video files.
| Field | Type | Default | Description | Example |
| :--- | :--- | :--- | :--- | :--- |
| `directories` | list | `[]` | List of folders to scan for videos. | `["/media/movies", "/media/tv"]` |
| `watch_enabled` | boolean | `true` | Watch for new files in real-time. | `false` |
| `watch_enabled` | boolean | `false` | Watch configured library directories in real-time. | `true` |
| `extra_watch_dirs` | list | `[]` | Additional folders to watch with options. | `[{ "path": "/media/downloads", "is_recursive": true }]` |
## Notification Settings (`notifications`)
@@ -98,3 +108,4 @@ General application settings.
| `monitoring_poll_interval` | float | `2.0` | How often to refresh system stats (sec). | `5.0` |
| `enable_telemetry` | boolean | `false` | Send anonymous usage data to help us. | `true` |
| `log_retention_days` | integer | `30` | How long to keep log files on disk. | `7` |
| `engine_mode` | string | `balanced` | Runtime engine mode used by the dashboard/API controls. | `background`, `balanced`, `throughput` |

View File

@@ -271,7 +271,7 @@ update NEW_VERSION:
echo "── Docs build ──"; \
(cd docs && bun install --frozen-lockfile && bun run build); \
echo "── E2E reliability ──"; \
(cd web-e2e && bun install --frozen-lockfile && bun run test:reliability); \
(cd web-e2e && bun install --frozen-lockfile && ALCHEMIST_E2E_PORT=4173 bun run test:reliability); \
mapfile -t PACKAGE_FILES < <(git ls-files -- 'package.json' '*/package.json'); \
mapfile -t CHANGED_TRACKED < <(git diff --name-only --); \
if [ "${#CHANGED_TRACKED[@]}" -eq 0 ]; then \

View File

@@ -162,7 +162,17 @@ pub async fn run_server(args: RunServerArgs) -> Result<()> {
let app = app_router(state);
let addr = "0.0.0.0:3000";
let port = std::env::var("ALCHEMIST_SERVER_PORT")
.ok()
.filter(|value| !value.trim().is_empty())
.map(|value| {
value.trim().parse::<u16>().map_err(|_| {
AlchemistError::Config("ALCHEMIST_SERVER_PORT must be a valid u16".to_string())
})
})
.transpose()?
.unwrap_or(3000);
let addr = format!("0.0.0.0:{port}");
info!("listening on http://{}", addr);
let listener = tokio::net::TcpListener::bind(&addr)
.await

View File

@@ -2,6 +2,7 @@ import { request, type FullConfig } from "@playwright/test";
import fs from "node:fs/promises";
import {
AUTH_STATE_PATH,
BASE_URL,
MEDIA_DIR,
TEST_PASSWORD,
TEST_USERNAME,
@@ -15,7 +16,7 @@ async function waitForSetupStatus(maxMs = 30_000): Promise<void> {
const startedAt = Date.now();
while (Date.now() - startedAt < maxMs) {
try {
const api = await request.newContext({ baseURL: "http://127.0.0.1:3000" });
const api = await request.newContext({ baseURL: BASE_URL });
const response = await api.get("/api/setup/status");
await api.dispose();
if (response.ok()) {
@@ -33,7 +34,7 @@ async function globalSetup(_config: FullConfig): Promise<void> {
await fs.mkdir(MEDIA_DIR, { recursive: true });
await waitForSetupStatus();
const api = await request.newContext({ baseURL: "http://127.0.0.1:3000" });
const api = await request.newContext({ baseURL: BASE_URL });
const statusResponse = await api.get("/api/setup/status");
if (!statusResponse.ok()) {

View File

@@ -1,6 +1,6 @@
{
"name": "alchemist-web-e2e",
"version": "0.2.10-rc.4",
"version": "0.2.10-rc.5",
"private": true,
"packageManager": "bun@1",
"type": "module",

View File

@@ -1,5 +1,5 @@
import { defineConfig } from "@playwright/test";
import { CONFIG_PATH, DB_PATH } from "./testConfig";
import { BASE_URL, CONFIG_PATH, DB_PATH, PORT } from "./testConfig";
export default defineConfig({
testDir: "./tests",
@@ -13,7 +13,7 @@ export default defineConfig({
reporter: "list",
globalSetup: "./global-setup.ts",
use: {
baseURL: "http://127.0.0.1:3000",
baseURL: BASE_URL,
headless: true,
trace: "retain-on-failure",
screenshot: "only-on-failure",
@@ -37,13 +37,14 @@ export default defineConfig({
],
webServer: {
command: "sh -c 'mkdir -p .runtime/media && cargo run --manifest-path ../Cargo.toml -- --reset-auth'",
url: "http://127.0.0.1:3000/api/health",
url: `${BASE_URL}/api/health`,
reuseExistingServer: false,
timeout: 120_000,
env: {
ALCHEMIST_CONFIG_PATH: CONFIG_PATH,
ALCHEMIST_DB_PATH: DB_PATH,
ALCHEMIST_CONFIG_MUTABLE: "true",
ALCHEMIST_SERVER_PORT: String(PORT),
RUST_LOG: "warn",
},
},

View File

@@ -6,5 +6,15 @@ export const AUTH_STATE_PATH = path.join(RUNTIME_DIR, "auth-state.json");
export const CONFIG_PATH = path.join(RUNTIME_DIR, "config.toml");
export const DB_PATH = path.join(RUNTIME_DIR, "alchemist.db");
const rawPort = process.env.ALCHEMIST_E2E_PORT ?? "3000";
const parsedPort = Number.parseInt(rawPort, 10);
if (!Number.isInteger(parsedPort) || parsedPort <= 0 || parsedPort > 65535) {
throw new Error(`ALCHEMIST_E2E_PORT must be a valid port, received "${rawPort}"`);
}
export const PORT = parsedPort;
export const BASE_URL = `http://127.0.0.1:${PORT}`;
export const TEST_USERNAME = "playwright";
export const TEST_PASSWORD = "playwright-password";

View File

@@ -1,6 +1,6 @@
{
"name": "alchemist-web",
"version": "0.2.10-rc.4",
"version": "0.2.10-rc.5",
"private": true,
"packageManager": "bun@1",
"type": "module",