Files
alchemist/docs/openapi.yaml
bybrooklyn f31dd23230 feat: comprehensive codebase improvements from audit
Architecture:
- Split server.rs (4,727 LOC) into 11 focused modules
- Add typed EventChannels (jobs/config/system) with appropriate capacities
- Add database query timeouts (5s on critical queries)
- Add graceful shutdown with signal handling

API:
- Add API versioning (/api/v1/) with backwards-compatible aliases
- Add X-Request-Id header for request tracing
- Create OpenAPI spec (docs/openapi.yaml)

Security:
- Add security headers middleware (CSP, X-Frame-Options, etc.)
- Add HSTS header (config-gated via https_only setting)
- Add config file permission check on Unix
- Fix path traversal vulnerability in file browser
- Add symlink detection in file browser

Frontend:
- Handle SSE lagged events with toast notification
- Clean up banned CSS patterns in components
- Add warning toast variant

Testing & Docs:
- Add FFmpeg integration tests with fixtures
- Expand documentation site (9 new pages)
- Pin MSRV to 1.85 in Cargo.toml

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-03-27 07:52:28 -04:00

2379 lines
61 KiB
YAML

openapi: 3.0.3
info:
title: Alchemist API
description: |
REST API for Alchemist - a self-hosted media transcoding pipeline.
## Authentication
Most endpoints require authentication. Obtain a session token via `/auth/login` and then include it as:
- **Cookie**: `alchemist_session=<token>`
- **Header**: `Authorization: Bearer <token>`
## Rate Limiting
- **Login attempts**: 10 per second
- **Global API**: 120 requests per minute
## Versioning
The API is available at both `/api/v1/*` (versioned) and `/api/*` (backwards-compatible alias).
version: 1.0.0
contact:
name: Alchemist
url: https://github.com/alchemist-dev/alchemist
license:
name: MIT
servers:
- url: /api/v1
description: Versioned API (recommended)
- url: /api
description: Backwards-compatible alias
tags:
- name: Auth
description: Authentication and session management
- name: Jobs
description: Transcoding job management
- name: Stats
description: Statistics and metrics
- name: Logs
description: System and job logging
- name: Events
description: Server-sent events for real-time updates
- name: Engine
description: Transcoding engine control
- name: Settings
description: Configuration management
- name: Scan
description: Library scanning operations
- name: Profiles
description: Encoding profile management
- name: Library
description: Library health and management
- name: System
description: System information and health checks
- name: Filesystem
description: Filesystem browsing
- name: Setup
description: Initial setup wizard
paths:
# ============================================================================
# AUTH ENDPOINTS
# ============================================================================
/auth/login:
post:
tags: [Auth]
summary: Authenticate user
description: Login with username and password to obtain a session token.
operationId: login
security: []
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/LoginRequest'
responses:
'200':
description: Login successful
headers:
Set-Cookie:
schema:
type: string
example: alchemist_session=abc123; HttpOnly; SameSite=Lax; Max-Age=2592000
content:
application/json:
schema:
$ref: '#/components/schemas/StatusResponse'
'401':
description: Invalid credentials
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
'429':
description: Too many login attempts
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
/auth/logout:
post:
tags: [Auth]
summary: Logout and invalidate session
operationId: logout
responses:
'200':
description: Logout successful
content:
application/json:
schema:
$ref: '#/components/schemas/StatusResponse'
# ============================================================================
# JOBS ENDPOINTS
# ============================================================================
/jobs:
get:
tags: [Jobs]
summary: List jobs with filtering and pagination
operationId: listJobs
parameters:
- name: limit
in: query
schema:
type: integer
minimum: 1
maximum: 200
default: 50
- name: page
in: query
schema:
type: integer
minimum: 1
default: 1
- name: status
in: query
description: Comma-separated list of statuses
schema:
type: string
example: Queued,Encoding,Completed
- name: search
in: query
description: Search in job paths/names
schema:
type: string
- name: sort_by
in: query
schema:
type: string
- name: sort_desc
in: query
schema:
type: boolean
default: false
- name: archived
in: query
schema:
type: string
enum: ['true', 'false']
default: 'false'
responses:
'200':
description: List of jobs
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/Job'
/jobs/table:
get:
tags: [Jobs]
summary: List jobs (alias for /jobs)
operationId: listJobsTable
parameters:
- $ref: '#/components/parameters/LimitParam'
- $ref: '#/components/parameters/PageParam'
- name: status
in: query
schema:
type: string
- name: search
in: query
schema:
type: string
- name: sort_by
in: query
schema:
type: string
- name: sort_desc
in: query
schema:
type: boolean
- name: archived
in: query
schema:
type: string
enum: ['true', 'false']
responses:
'200':
description: List of jobs
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/Job'
/jobs/batch:
post:
tags: [Jobs]
summary: Perform batch operation on jobs
operationId: batchJobs
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/BatchJobRequest'
responses:
'200':
description: Batch operation completed
content:
application/json:
schema:
$ref: '#/components/schemas/CountResponse'
'409':
description: Conflict - some jobs are active
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
/jobs/{id}/cancel:
post:
tags: [Jobs]
summary: Cancel a job
operationId: cancelJob
parameters:
- $ref: '#/components/parameters/JobIdParam'
responses:
'200':
description: Job cancelled
content:
application/json:
schema:
$ref: '#/components/schemas/StatusResponse'
'404':
description: Job not found
'409':
description: Job cannot be cancelled
/jobs/{id}/restart:
post:
tags: [Jobs]
summary: Restart a completed or failed job
operationId: restartJob
parameters:
- $ref: '#/components/parameters/JobIdParam'
responses:
'200':
description: Job restarted
content:
application/json:
schema:
$ref: '#/components/schemas/StatusResponse'
'404':
description: Job not found
'409':
description: Job is currently active
/jobs/{id}/delete:
post:
tags: [Jobs]
summary: Delete a job
operationId: deleteJob
parameters:
- $ref: '#/components/parameters/JobIdParam'
responses:
'200':
description: Job deleted
content:
application/json:
schema:
$ref: '#/components/schemas/StatusResponse'
'404':
description: Job not found
'409':
description: Job is currently active
/jobs/{id}/priority:
post:
tags: [Jobs]
summary: Update job priority
operationId: setJobPriority
parameters:
- $ref: '#/components/parameters/JobIdParam'
requestBody:
required: true
content:
application/json:
schema:
type: object
required: [priority]
properties:
priority:
type: integer
responses:
'200':
description: Priority updated
content:
application/json:
schema:
type: object
properties:
id:
type: integer
priority:
type: integer
/jobs/{id}/details:
get:
tags: [Jobs]
summary: Get detailed job information
operationId: getJobDetails
parameters:
- $ref: '#/components/parameters/JobIdParam'
responses:
'200':
description: Job details
content:
application/json:
schema:
$ref: '#/components/schemas/JobDetails'
'404':
description: Job not found
/jobs/restart-failed:
post:
tags: [Jobs]
summary: Restart all failed/cancelled jobs
operationId: restartFailedJobs
responses:
'200':
description: Jobs queued for retry
content:
application/json:
schema:
type: object
properties:
count:
type: integer
message:
type: string
/jobs/clear-completed:
post:
tags: [Jobs]
summary: Clear completed jobs from queue
description: Preserves historical stats while removing completed jobs from the active queue.
operationId: clearCompletedJobs
responses:
'200':
description: Jobs cleared
content:
application/json:
schema:
type: object
properties:
count:
type: integer
message:
type: string
# ============================================================================
# STATS ENDPOINTS
# ============================================================================
/stats:
get:
tags: [Stats]
summary: Get current job statistics
operationId: getStats
responses:
'200':
description: Current statistics
content:
application/json:
schema:
$ref: '#/components/schemas/Stats'
/stats/aggregated:
get:
tags: [Stats]
summary: Get aggregated statistics
operationId: getAggregatedStats
responses:
'200':
description: Aggregated statistics
content:
application/json:
schema:
$ref: '#/components/schemas/AggregatedStats'
/stats/daily:
get:
tags: [Stats]
summary: Get daily statistics for last 30 days
operationId: getDailyStats
responses:
'200':
description: Daily statistics
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/DailyStats'
/stats/detailed:
get:
tags: [Stats]
summary: Get detailed encode statistics
operationId: getDetailedStats
responses:
'200':
description: Detailed encode statistics (last 50 jobs)
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/DetailedStats'
/stats/savings:
get:
tags: [Stats]
summary: Get storage savings summary
operationId: getSavingsStats
responses:
'200':
description: Storage savings breakdown
content:
application/json:
schema:
$ref: '#/components/schemas/SavingsStats'
# ============================================================================
# LOGS ENDPOINTS
# ============================================================================
/logs/history:
get:
tags: [Logs]
summary: Get system logs with pagination
operationId: getLogHistory
parameters:
- $ref: '#/components/parameters/PageParam'
- $ref: '#/components/parameters/LimitParam'
responses:
'200':
description: Log entries
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/LogEntry'
/logs:
delete:
tags: [Logs]
summary: Clear all system logs
operationId: clearLogs
responses:
'200':
description: Logs cleared
# ============================================================================
# EVENTS ENDPOINT (SSE)
# ============================================================================
/events:
get:
tags: [Events]
summary: Subscribe to real-time events
description: |
Server-Sent Events stream for real-time updates.
## Event Types
- **log**: System/job log entry
- **progress**: Job encoding progress
- **status**: Job status change
- **decision**: Transcoder decision
- **lagged**: Client fell behind event stream
operationId: subscribeEvents
responses:
'200':
description: SSE event stream
content:
text/event-stream:
schema:
type: string
description: |
Events are sent in SSE format:
```
event: log
data: {"level":"info","job_id":123,"message":"Starting encode"}
event: progress
data: {"job_id":123,"percentage":45,"time":"00:15:30"}
```
# ============================================================================
# ENGINE ENDPOINTS
# ============================================================================
/engine/status:
get:
tags: [Engine]
summary: Get engine status
operationId: getEngineStatus
responses:
'200':
description: Engine status
content:
application/json:
schema:
$ref: '#/components/schemas/EngineStatus'
/engine/mode:
get:
tags: [Engine]
summary: Get engine mode configuration
operationId: getEngineMode
responses:
'200':
description: Engine mode details
content:
application/json:
schema:
$ref: '#/components/schemas/EngineModeResponse'
post:
tags: [Engine]
summary: Set engine mode
operationId: setEngineMode
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/EngineModeRequest'
responses:
'200':
description: Mode updated
content:
application/json:
schema:
type: object
properties:
status:
type: string
mode:
$ref: '#/components/schemas/EngineMode'
concurrent_limit:
type: integer
is_manual_override:
type: boolean
/engine/pause:
post:
tags: [Engine]
summary: Pause the encoding engine
operationId: pauseEngine
responses:
'200':
description: Engine paused
content:
application/json:
schema:
type: object
properties:
status:
type: string
example: paused
/engine/resume:
post:
tags: [Engine]
summary: Resume the encoding engine
operationId: resumeEngine
responses:
'200':
description: Engine resumed
content:
application/json:
schema:
type: object
properties:
status:
type: string
example: running
/engine/drain:
post:
tags: [Engine]
summary: Drain the job queue
description: Stop accepting new jobs while finishing active ones.
operationId: drainEngine
responses:
'200':
description: Engine draining
content:
application/json:
schema:
type: object
properties:
status:
type: string
example: draining
/engine/stop-drain:
post:
tags: [Engine]
summary: Stop draining and resume normal operation
operationId: stopDrain
responses:
'200':
description: Drain stopped
content:
application/json:
schema:
type: object
properties:
status:
type: string
example: running
# ============================================================================
# SETTINGS ENDPOINTS
# ============================================================================
/settings/transcode:
get:
tags: [Settings]
summary: Get transcoding settings
operationId: getTranscodeSettings
responses:
'200':
description: Transcoding settings
content:
application/json:
schema:
$ref: '#/components/schemas/TranscodeSettings'
post:
tags: [Settings]
summary: Update transcoding settings
operationId: updateTranscodeSettings
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/TranscodeSettings'
responses:
'200':
description: Settings updated
content:
application/json:
schema:
$ref: '#/components/schemas/TranscodeSettings'
'400':
description: Invalid settings
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
/settings/system:
get:
tags: [Settings]
summary: Get system settings
operationId: getSystemSettings
responses:
'200':
description: System settings
content:
application/json:
schema:
$ref: '#/components/schemas/SystemSettings'
post:
tags: [Settings]
summary: Update system settings
operationId: updateSystemSettings
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/SystemSettings'
responses:
'200':
description: Settings updated
content:
application/json:
schema:
$ref: '#/components/schemas/SystemSettings'
/settings/hardware:
get:
tags: [Settings]
summary: Get hardware acceleration settings
operationId: getHardwareSettings
responses:
'200':
description: Hardware settings
content:
application/json:
schema:
$ref: '#/components/schemas/HardwareSettings'
post:
tags: [Settings]
summary: Update hardware acceleration settings
operationId: updateHardwareSettings
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/HardwareSettings'
responses:
'200':
description: Settings updated
content:
application/json:
schema:
$ref: '#/components/schemas/HardwareSettings'
/settings/bundle:
get:
tags: [Settings]
summary: Get entire configuration bundle
operationId: getConfigBundle
responses:
'200':
description: Full configuration
content:
application/json:
schema:
$ref: '#/components/schemas/ConfigBundle'
put:
tags: [Settings]
summary: Update entire configuration bundle
operationId: updateConfigBundle
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/ConfigBundle'
responses:
'200':
description: Configuration updated
content:
application/json:
schema:
$ref: '#/components/schemas/ConfigBundle'
/settings/config:
get:
tags: [Settings]
summary: Get raw TOML configuration
operationId: getRawConfig
responses:
'200':
description: Raw configuration
content:
application/json:
schema:
type: object
properties:
raw_toml:
type: string
normalized:
type: object
put:
tags: [Settings]
summary: Update raw TOML configuration
operationId: updateRawConfig
requestBody:
required: true
content:
application/json:
schema:
type: object
required: [raw_toml]
properties:
raw_toml:
type: string
responses:
'200':
description: Configuration updated
content:
application/json:
schema:
type: object
properties:
raw_toml:
type: string
normalized:
type: object
/settings/files:
get:
tags: [Settings]
summary: Get file settings
operationId: getFileSettings
responses:
'200':
description: File settings
content:
application/json:
schema:
$ref: '#/components/schemas/FileSettings'
post:
tags: [Settings]
summary: Update file settings
operationId: updateFileSettings
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/FileSettings'
responses:
'200':
description: Settings updated
content:
application/json:
schema:
$ref: '#/components/schemas/FileSettings'
/settings/preferences/{key}:
get:
tags: [Settings]
summary: Get user preference
operationId: getPreference
parameters:
- name: key
in: path
required: true
schema:
type: string
responses:
'200':
description: Preference value
content:
application/json:
schema:
type: object
properties:
key:
type: string
value:
type: string
'404':
description: Preference not found
post:
tags: [Settings]
summary: Set user preference
operationId: setPreference
parameters:
- name: key
in: path
required: true
schema:
type: string
requestBody:
required: true
content:
application/json:
schema:
type: object
required: [value]
properties:
value:
type: string
responses:
'200':
description: Preference saved
content:
application/json:
schema:
type: object
properties:
key:
type: string
value:
type: string
/settings/watch-dirs:
get:
tags: [Settings]
summary: List watch directories
operationId: listWatchDirs
responses:
'200':
description: Watch directories
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/WatchDir'
post:
tags: [Settings]
summary: Add watch directory
operationId: addWatchDir
requestBody:
required: true
content:
application/json:
schema:
type: object
required: [path]
properties:
path:
type: string
is_recursive:
type: boolean
default: true
responses:
'201':
description: Watch directory added
content:
application/json:
schema:
$ref: '#/components/schemas/WatchDir'
/settings/watch-dirs/{id}:
delete:
tags: [Settings]
summary: Remove watch directory
operationId: removeWatchDir
parameters:
- name: id
in: path
required: true
schema:
type: integer
responses:
'200':
description: Watch directory removed
'404':
description: Watch directory not found
/settings/notifications:
get:
tags: [Settings]
summary: List notification targets
operationId: listNotificationTargets
responses:
'200':
description: Notification targets
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/NotificationTarget'
post:
tags: [Settings]
summary: Add notification target
operationId: addNotificationTarget
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/NotificationTargetInput'
responses:
'201':
description: Notification target added
content:
application/json:
schema:
$ref: '#/components/schemas/NotificationTarget'
/settings/notifications/{id}:
delete:
tags: [Settings]
summary: Remove notification target
operationId: removeNotificationTarget
parameters:
- name: id
in: path
required: true
schema:
type: integer
responses:
'200':
description: Notification target removed
'404':
description: Notification target not found
/settings/notifications/test:
post:
tags: [Settings]
summary: Send test notification
operationId: testNotification
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/NotificationTargetInput'
responses:
'200':
description: Test notification sent
'400':
description: Invalid notification configuration
/settings/schedule:
get:
tags: [Settings]
summary: List schedule windows
operationId: listScheduleWindows
responses:
'200':
description: Schedule windows
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/ScheduleWindow'
post:
tags: [Settings]
summary: Add schedule window
operationId: addScheduleWindow
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/ScheduleWindowInput'
responses:
'201':
description: Schedule window added
content:
application/json:
schema:
$ref: '#/components/schemas/ScheduleWindow'
/settings/schedule/{id}:
delete:
tags: [Settings]
summary: Remove schedule window
operationId: removeScheduleWindow
parameters:
- name: id
in: path
required: true
schema:
type: integer
responses:
'200':
description: Schedule window removed
'404':
description: Schedule window not found
# ============================================================================
# SCAN ENDPOINTS
# ============================================================================
/scan:
post:
tags: [Scan]
summary: Trigger library scan (legacy)
operationId: triggerScanLegacy
responses:
'200':
description: Scan completed
'202':
description: Scan started
/scan/start:
post:
tags: [Scan]
summary: Start library scan
operationId: startScan
responses:
'202':
description: Scan started
content:
application/json:
schema:
type: object
properties:
status:
type: string
example: accepted
/scan/status:
get:
tags: [Scan]
summary: Get scan status
operationId: getScanStatus
responses:
'200':
description: Scan status
content:
application/json:
schema:
$ref: '#/components/schemas/ScanStatus'
# ============================================================================
# PROFILES ENDPOINTS
# ============================================================================
/profiles/presets:
get:
tags: [Profiles]
summary: Get built-in profile presets
operationId: getProfilePresets
responses:
'200':
description: Built-in presets
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/Profile'
/profiles:
get:
tags: [Profiles]
summary: List all profiles
operationId: listProfiles
responses:
'200':
description: All profiles
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/Profile'
post:
tags: [Profiles]
summary: Create profile
operationId: createProfile
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/ProfileInput'
responses:
'201':
description: Profile created
content:
application/json:
schema:
$ref: '#/components/schemas/Profile'
/profiles/{id}:
put:
tags: [Profiles]
summary: Update profile
operationId: updateProfile
parameters:
- name: id
in: path
required: true
schema:
type: integer
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/ProfileInput'
responses:
'200':
description: Profile updated
content:
application/json:
schema:
$ref: '#/components/schemas/Profile'
'404':
description: Profile not found
delete:
tags: [Profiles]
summary: Delete profile
operationId: deleteProfile
parameters:
- name: id
in: path
required: true
schema:
type: integer
responses:
'200':
description: Profile deleted
'404':
description: Profile not found
'409':
description: Profile is in use
/watch-dirs/{id}/profile:
patch:
tags: [Profiles]
summary: Assign profile to watch directory
operationId: assignWatchDirProfile
parameters:
- name: id
in: path
required: true
schema:
type: integer
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
profile_id:
type: integer
nullable: true
description: Profile ID to assign, or null to unassign
responses:
'200':
description: Profile assigned
'404':
description: Watch directory not found
# ============================================================================
# LIBRARY HEALTH ENDPOINTS
# ============================================================================
/library/health:
get:
tags: [Library]
summary: Get library health summary
operationId: getLibraryHealth
responses:
'200':
description: Health summary
content:
application/json:
schema:
$ref: '#/components/schemas/LibraryHealth'
/library/health/issues:
get:
tags: [Library]
summary: Get jobs with health issues
operationId: getLibraryHealthIssues
responses:
'200':
description: Jobs with issues
content:
application/json:
schema:
type: array
items:
type: object
properties:
job:
$ref: '#/components/schemas/Job'
report:
type: object
properties:
issues:
type: array
items:
type: object
properties:
type:
type: string
enum: [corruption, stream_issue]
severity:
type: string
enum: [critical, warning]
description:
type: string
/library/health/scan:
post:
tags: [Library]
summary: Start library health scan
operationId: startHealthScan
responses:
'202':
description: Health scan started
content:
application/json:
schema:
type: object
properties:
status:
type: string
example: accepted
/library/health/scan/{id}:
post:
tags: [Library]
summary: Rescan specific job for health issues
operationId: rescanJobHealth
parameters:
- $ref: '#/components/parameters/JobIdParam'
responses:
'200':
description: Health scan result
content:
application/json:
schema:
type: object
properties:
job_id:
type: integer
issue_found:
type: boolean
# ============================================================================
# SYSTEM ENDPOINTS
# ============================================================================
/health:
get:
tags: [System]
summary: Health check
operationId: healthCheck
security: []
responses:
'200':
description: Service is healthy
content:
application/json:
schema:
$ref: '#/components/schemas/HealthResponse'
/ready:
get:
tags: [System]
summary: Readiness probe
description: Checks database connectivity
operationId: readinessProbe
security: []
responses:
'200':
description: Service is ready
content:
application/json:
schema:
type: object
properties:
ready:
type: boolean
example: true
'503':
description: Service not ready
content:
application/json:
schema:
type: object
properties:
ready:
type: boolean
example: false
reason:
type: string
/system/resources:
get:
tags: [System]
summary: Get system resource usage
description: Returns real-time CPU, memory, disk usage. Cached for 500ms.
operationId: getSystemResources
responses:
'200':
description: System resources
content:
application/json:
schema:
$ref: '#/components/schemas/SystemResources'
/system/hardware:
get:
tags: [System]
summary: Detect available hardware
description: Detects GPUs and hardware encoders. Public during setup.
operationId: detectHardware
security: []
responses:
'200':
description: Hardware detection results
content:
application/json:
schema:
$ref: '#/components/schemas/HardwareInfo'
/system/version:
get:
tags: [System]
summary: Get version information
operationId: getVersion
responses:
'200':
description: Version info
content:
application/json:
schema:
type: object
properties:
version:
type: string
example: 1.2.3
commit:
type: string
build_date:
type: string
/system/ffmpeg:
get:
tags: [System]
summary: Get FFmpeg information
operationId: getFfmpegInfo
responses:
'200':
description: FFmpeg info
content:
application/json:
schema:
type: object
properties:
version:
type: string
path:
type: string
encoders:
type: array
items:
type: string
decoders:
type: array
items:
type: string
# ============================================================================
# FILESYSTEM ENDPOINTS
# ============================================================================
/fs/browse:
get:
tags: [Filesystem]
summary: Browse filesystem
operationId: browseFilesystem
parameters:
- name: path
in: query
schema:
type: string
default: /
responses:
'200':
description: Directory listing
content:
application/json:
schema:
$ref: '#/components/schemas/DirectoryListing'
/fs/recommendations:
get:
tags: [Filesystem]
summary: Get recommended directories
operationId: getDirectoryRecommendations
security: []
responses:
'200':
description: Recommended directories
content:
application/json:
schema:
type: object
properties:
input_directories:
type: array
items:
type: object
properties:
name:
type: string
path:
type: string
output_directory:
type: string
config_directory:
type: string
/fs/preview:
post:
tags: [Filesystem]
summary: Preview selected folders
operationId: previewFolders
security: []
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
input_paths:
type: array
items:
type: string
output_path:
type: string
config_path:
type: string
responses:
'200':
description: Folder preview
content:
application/json:
schema:
type: object
properties:
valid:
type: boolean
issues:
type: array
items:
type: string
space_available_gb:
type: number
estimated_library_size:
type: integer
# ============================================================================
# SETUP ENDPOINTS
# ============================================================================
/setup/status:
get:
tags: [Setup]
summary: Check if setup is required
operationId: getSetupStatus
security: []
responses:
'200':
description: Setup status
content:
application/json:
schema:
type: object
properties:
setup_required:
type: boolean
enable_telemetry:
type: boolean
config_mutable:
type: boolean
/setup/complete:
post:
tags: [Setup]
summary: Complete initial setup
operationId: completeSetup
security: []
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/SetupRequest'
responses:
'200':
description: Setup completed
content:
application/json:
schema:
type: object
properties:
status:
type: string
message:
type: string
'400':
description: Validation errors
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
# ============================================================================
# UI PREFERENCES ENDPOINTS
# ============================================================================
/ui/preferences:
get:
tags: [Settings]
summary: Get UI preferences
operationId: getUiPreferences
responses:
'200':
description: UI preferences
content:
application/json:
schema:
type: object
additionalProperties: true
post:
tags: [Settings]
summary: Update UI preferences
operationId: updateUiPreferences
requestBody:
required: true
content:
application/json:
schema:
type: object
additionalProperties: true
responses:
'200':
description: Preferences updated
content:
application/json:
schema:
type: object
additionalProperties: true
# ==============================================================================
# COMPONENTS
# ==============================================================================
components:
securitySchemes:
bearerAuth:
type: http
scheme: bearer
cookieAuth:
type: apiKey
in: cookie
name: alchemist_session
parameters:
JobIdParam:
name: id
in: path
required: true
schema:
type: integer
LimitParam:
name: limit
in: query
schema:
type: integer
minimum: 1
maximum: 200
default: 50
PageParam:
name: page
in: query
schema:
type: integer
minimum: 1
default: 1
schemas:
# --------------------------------------------------------------------------
# Common Responses
# --------------------------------------------------------------------------
StatusResponse:
type: object
properties:
status:
type: string
example: ok
ErrorResponse:
type: object
properties:
error:
type: string
message:
type: string
details:
type: object
CountResponse:
type: object
properties:
count:
type: integer
# --------------------------------------------------------------------------
# Auth
# --------------------------------------------------------------------------
LoginRequest:
type: object
required: [username, password]
properties:
username:
type: string
password:
type: string
format: password
# --------------------------------------------------------------------------
# Jobs
# --------------------------------------------------------------------------
Job:
type: object
properties:
id:
type: integer
input_path:
type: string
output_path:
type: string
nullable: true
status:
$ref: '#/components/schemas/JobStatus'
priority:
type: integer
default: 0
created_at:
type: string
format: date-time
updated_at:
type: string
format: date-time
archived:
type: boolean
default: false
JobStatus:
type: string
enum:
- Queued
- Analyzing
- Encoding
- Remuxing
- Resuming
- Completed
- Failed
- Cancelled
- Archived
JobDetails:
type: object
properties:
job:
$ref: '#/components/schemas/Job'
metadata:
type: object
nullable: true
description: FFmpeg metadata if available
encode_stats:
type: object
nullable: true
description: Detailed encode statistics
job_logs:
type: array
items:
$ref: '#/components/schemas/LogEntry'
job_failure_summary:
type: string
nullable: true
BatchJobRequest:
type: object
required: [action, ids]
properties:
action:
type: string
enum: [cancel, delete, restart]
ids:
type: array
items:
type: integer
# --------------------------------------------------------------------------
# Stats
# --------------------------------------------------------------------------
Stats:
type: object
properties:
total:
type: integer
completed:
type: integer
active:
type: integer
failed:
type: integer
concurrent_limit:
type: integer
AggregatedStats:
type: object
properties:
total_input_bytes:
type: integer
format: int64
total_output_bytes:
type: integer
format: int64
total_savings_bytes:
type: integer
format: int64
total_time_seconds:
type: integer
total_jobs:
type: integer
avg_vmaf:
type: number
nullable: true
DailyStats:
type: object
properties:
date:
type: string
format: date
jobs_completed:
type: integer
total_input_bytes:
type: integer
format: int64
total_output_bytes:
type: integer
format: int64
total_time_seconds:
type: integer
DetailedStats:
type: object
properties:
job_id:
type: integer
input_size:
type: integer
format: int64
output_size:
type: integer
format: int64
encode_time_seconds:
type: integer
vmaf_score:
type: number
nullable: true
codec:
type: string
quality:
type: string
SavingsStats:
type: object
properties:
total_saved_bytes:
type: integer
format: int64
by_codec:
type: object
additionalProperties:
type: object
properties:
count:
type: integer
saved_bytes:
type: integer
format: int64
by_quality:
type: object
additionalProperties:
type: object
properties:
count:
type: integer
saved_bytes:
type: integer
format: int64
# --------------------------------------------------------------------------
# Logs
# --------------------------------------------------------------------------
LogEntry:
type: object
properties:
id:
type: integer
level:
type: string
enum: [info, warn, error]
job_id:
type: integer
nullable: true
message:
type: string
timestamp:
type: string
format: date-time
# --------------------------------------------------------------------------
# Engine
# --------------------------------------------------------------------------
EngineStatus:
type: object
properties:
status:
type: string
enum: [running, paused, draining]
manual_paused:
type: boolean
scheduler_paused:
type: boolean
draining:
type: boolean
mode:
$ref: '#/components/schemas/EngineMode'
concurrent_limit:
type: integer
is_manual_override:
type: boolean
EngineMode:
type: string
enum: [Background, Balanced, Throughput]
EngineModeResponse:
type: object
properties:
mode:
$ref: '#/components/schemas/EngineMode'
is_manual_override:
type: boolean
concurrent_limit:
type: integer
cpu_count:
type: integer
computed_limits:
type: object
properties:
background:
type: integer
balanced:
type: integer
throughput:
type: integer
EngineModeRequest:
type: object
required: [mode]
properties:
mode:
$ref: '#/components/schemas/EngineMode'
concurrent_jobs_override:
type: integer
nullable: true
threads_override:
type: integer
nullable: true
# --------------------------------------------------------------------------
# Settings
# --------------------------------------------------------------------------
TranscodeSettings:
type: object
properties:
concurrent_jobs:
type: integer
minimum: 1
size_reduction_threshold:
type: number
minimum: 0
maximum: 1
min_bpp_threshold:
type: number
min_file_size_mb:
type: integer
output_codec:
type: string
enum: [h265, vp9, av1]
quality_profile:
type: string
enum: [high, medium, low]
threads:
type: integer
minimum: 0
maximum: 512
description: 0 = auto
allow_fallback:
type: boolean
hdr_mode:
type: string
enum: [tone-map, passthrough, strip]
tonemap_algorithm:
type: string
enum: [linear, gamma, bt2390]
tonemap_peak:
type: number
minimum: 50
maximum: 1000
tonemap_desat:
type: number
subtitle_mode:
type: string
enum: [none, passthrough, burn]
stream_rules:
type: object
description: Stream filtering rules
SystemSettings:
type: object
properties:
monitoring_poll_interval:
type: number
minimum: 0.5
maximum: 10
enable_telemetry:
type: boolean
watch_enabled:
type: boolean
HardwareSettings:
type: object
properties:
allow_cpu_fallback:
type: boolean
allow_cpu_encoding:
type: boolean
cpu_preset:
type: string
enum: [slow, medium, fast, faster]
preferred_vendor:
type: string
enum: [nvidia, intel, amd]
nullable: true
device_path:
type: string
nullable: true
FileSettings:
type: object
additionalProperties: true
ConfigBundle:
type: object
description: Complete configuration including all settings sections
additionalProperties: true
# --------------------------------------------------------------------------
# Watch Directories
# --------------------------------------------------------------------------
WatchDir:
type: object
properties:
id:
type: integer
path:
type: string
is_recursive:
type: boolean
profile_id:
type: integer
nullable: true
created_at:
type: string
format: date-time
updated_at:
type: string
format: date-time
# --------------------------------------------------------------------------
# Notifications
# --------------------------------------------------------------------------
NotificationTarget:
type: object
properties:
id:
type: integer
name:
type: string
target_type:
type: string
enum: [webhook, discord, gotify]
endpoint_url:
type: string
auth_token:
type: string
nullable: true
events:
type: string
description: JSON array of event types
enabled:
type: boolean
created_at:
type: string
format: date-time
NotificationTargetInput:
type: object
required: [name, target_type, endpoint_url]
properties:
name:
type: string
target_type:
type: string
enum: [webhook, discord, gotify]
endpoint_url:
type: string
format: uri
auth_token:
type: string
events:
type: array
items:
type: string
enabled:
type: boolean
default: true
# --------------------------------------------------------------------------
# Schedule
# --------------------------------------------------------------------------
ScheduleWindow:
type: object
properties:
id:
type: integer
start_time:
type: string
example: '09:00'
end_time:
type: string
example: '17:00'
days_of_week:
type: string
description: JSON array of day numbers (0=Sunday, 6=Saturday)
enabled:
type: boolean
created_at:
type: string
format: date-time
updated_at:
type: string
format: date-time
ScheduleWindowInput:
type: object
required: [start_time, end_time, days_of_week]
properties:
start_time:
type: string
example: '09:00'
end_time:
type: string
example: '17:00'
days_of_week:
type: array
items:
type: integer
minimum: 0
maximum: 6
enabled:
type: boolean
default: true
# --------------------------------------------------------------------------
# Profiles
# --------------------------------------------------------------------------
Profile:
type: object
properties:
id:
type: integer
name:
type: string
preset:
type: string
codec:
type: string
quality_profile:
type: string
hdr_mode:
type: string
audio_mode:
type: string
crf_override:
type: integer
nullable: true
notes:
type: string
nullable: true
builtin:
type: boolean
ProfileInput:
type: object
required: [name]
properties:
name:
type: string
preset:
type: string
codec:
type: string
quality_profile:
type: string
hdr_mode:
type: string
audio_mode:
type: string
crf_override:
type: integer
notes:
type: string
# --------------------------------------------------------------------------
# Library Health
# --------------------------------------------------------------------------
LibraryHealth:
type: object
properties:
files_checked:
type: integer
issues_found:
type: integer
last_scan_time:
type: string
format: date-time
nullable: true
status:
type: string
enum: [healthy, issues_detected]
# --------------------------------------------------------------------------
# Scan
# --------------------------------------------------------------------------
ScanStatus:
type: object
properties:
scanning:
type: boolean
files_scanned:
type: integer
files_queued:
type: integer
start_time:
type: string
format: date-time
nullable: true
elapsed_seconds:
type: integer
# --------------------------------------------------------------------------
# System
# --------------------------------------------------------------------------
HealthResponse:
type: object
properties:
status:
type: string
example: ok
version:
type: string
uptime:
type: string
example: 48h 30m 15s
uptime_seconds:
type: integer
SystemResources:
type: object
properties:
cpu_usage_percent:
type: number
memory_used_bytes:
type: integer
format: int64
memory_total_bytes:
type: integer
format: int64
disk_used_bytes:
type: integer
format: int64
disk_total_bytes:
type: integer
format: int64
gpu_usage_percent:
type: number
nullable: true
gpu_memory_used_bytes:
type: integer
format: int64
nullable: true
HardwareInfo:
type: object
properties:
gpus:
type: array
items:
type: object
properties:
name:
type: string
vendor:
type: string
device_path:
type: string
encoders:
type: array
items:
type: string
cpu_cores:
type: integer
# --------------------------------------------------------------------------
# Filesystem
# --------------------------------------------------------------------------
DirectoryListing:
type: object
properties:
path:
type: string
parent:
type: string
nullable: true
directories:
type: array
items:
type: object
properties:
name:
type: string
path:
type: string
files:
type: array
items:
type: object
properties:
name:
type: string
path:
type: string
size:
type: integer
format: int64
# --------------------------------------------------------------------------
# Setup
# --------------------------------------------------------------------------
SetupRequest:
type: object
required: [username, password, input_directories, output_directory]
properties:
username:
type: string
minLength: 1
password:
type: string
format: password
minLength: 8
input_directories:
type: array
items:
type: string
minItems: 1
output_directory:
type: string
enable_telemetry:
type: boolean
default: false
security:
- bearerAuth: []
- cookieAuth: []