Your personal media companion, built with Phoenix LiveView
A modern, self-hosted media management platform for tracking, organizing, and monitoring your media library.
Warning
Mydia is still in version 0.x.x and is subject to major changes from version to version. Feedback is welcome! Expect bugs and please open issues or feature requests.
- πΊ Unified Media Management β Track both movies and TV shows with rich metadata from TMDB/TVDB
- π€ Automated Downloads β Background search and download with quality profiles and smart release ranking
- β¬οΈ Download Clients β qBittorrent, Transmission, SABnzbd, and NZBGet support
- π Indexer Integration β Search via Prowlarr and Jackett for finding releases
- π Built-in Indexer Library β Native Cardigann support (experimental, limited testing)
- π₯ Multi-User System β Built-in admin/guest roles with request approval workflow
- π SSO Support β Local authentication plus OIDC/OpenID Connect integration
- π Release Calendar β Track upcoming releases and monitor episodes
- π¨ Modern Real-Time UI β Phoenix LiveView with instant updates and responsive design
| Feature | Mydia | Radarr | Sonarr |
|---|---|---|---|
| Media Types | Movies + TV Shows | Movies only | TV Shows only |
| Built-in Indexers | π§ͺ Cardigann (experimental) | β Requires Prowlarr/Jackett | β Requires Prowlarr/Jackett |
| Multi-User & Requests | β Built-in (admin/guest roles) | β Requires Ombi/Overseerr | β Requires Ombi/Overseerr |
| Authentication | Local + OIDC/SSO built-in | Local only | Local only |
| Library Management | β | β | β |
| Download Automation | β | β | β |
| Quality Profiles | β | β Advanced | β Advanced |
| Custom Formats | β³ Planned | β | β |
| Automatic Upgrades | β³ Planned | β | β |
| Media Server Integration | β³ Planned | β Plex/Kodi/Jellyfin | β Plex/Kodi/Jellyfin |
| List Import | β³ Planned | β | β |
| Native Playback | π§ͺ Experimental | β | β |
| Technology | Elixir/Phoenix LiveView | .NET/React | .NET/React |
| Maturity | Early development | Production-ready | Production-ready |
Legend: β Available | β³ Planned | π§ͺ Experimental | β Not Available
Choose Mydia for: Unified movies+TV management, built-in multi-user support, modern real-time UI, native SSO
Choose Radarr/Sonarr for: Mature ecosystem, advanced custom formats, comprehensive automation, wider integrations
Note
Quality profile presets are still in testing. Feedback and suggestions are welcome in GitHub Issues.
Mydia includes 8 built-in profiles (SD through Remux-2160p) plus a preset gallery with 23 one-click imports:
- TRaSH Guides β Community-vetted profiles for HD/UHD Bluray, WEB, and Remux
- Profilarr/Dictionarry β Quality, Balanced, Efficient, Compact, and Remux tiers for 720p/1080p/2160p
- Storage & Use-Case β Profiles optimized for storage constraints, streaming, or mobile
Profiles support resolution filtering, file size limits, source/codec/audio preferences, HDR format preferences, and upgrade rules. Releases are automatically scored and ranked based on your profile settings.
Multi-platform images are available for the following architectures:
| Architecture | Available | Tag |
|---|---|---|
| x86-64 | β | amd64-latest |
| arm64 | β | arm64-latest |
The multi-arch image ghcr.io/getmydia/mydia:latest will automatically pull the correct image for your architecture.
Database Variants:
ghcr.io/getmydia/mydia:latest- SQLite (default)ghcr.io/getmydia/mydia:latest-pg- PostgreSQL
Both variants are available for all supported architectures. See PostgreSQL Support for details.
- Generate required secrets:
# Generate SECRET_KEY_BASE
openssl rand -base64 48
# Generate GUARDIAN_SECRET_KEY
openssl rand -base64 48- Set up your container using Docker Compose (recommended) or Docker CLI
- Access the web interface at
http://your-server:4000 - On first visit, you'll be guided through creating the initial admin user
- Configure download clients and indexers in the Admin section
Here are some example snippets to help you get started creating a container.
---
services:
mydia:
image: ghcr.io/getmydia/mydia:latest
container_name: mydia
environment:
- PUID=1000
- PGID=1000
- TZ=America/New_York
- SECRET_KEY_BASE=your-secret-key-base-here # Required: generate with openssl rand -base64 48
- GUARDIAN_SECRET_KEY=your-guardian-secret-key-here # Required: generate with openssl rand -base64 48
- PHX_HOST=localhost # Change to your domain
- PORT=4000
- MOVIES_PATH=/media/library/movies
- TV_PATH=/media/library/tv
volumes:
- /path/to/mydia/config:/config
- /path/to/your/media:/media # Single mount enables hardlinks between downloads and libraries
ports:
- 4000:4000
restart: unless-stoppedNote: This example uses a single
/mediamount to enable hardlink support. Organize your host directory with subdirectories like/path/to/your/media/downloads,/path/to/your/media/library/movies, and/path/to/your/media/library/tv. Configure your download client to save to/media/downloads.
docker run -d \
--name=mydia \
-e PUID=1000 \
-e PGID=1000 \
-e TZ=America/New_York \
-e SECRET_KEY_BASE=your-secret-key-base-here \
-e GUARDIAN_SECRET_KEY=your-guardian-secret-key-here \
-e PHX_HOST=localhost \
-e PORT=4000 \
-e MOVIES_PATH=/media/library/movies \
-e TV_PATH=/media/library/tv \
-p 4000:4000 \
-v /path/to/mydia/config:/config \
-v /path/to/your/media:/media \
--restart unless-stopped \
ghcr.io/getmydia/mydia:latestNote: This example uses a single
/mediamount to enable hardlink support. Configure your download client to save to/media/downloads.
Container images are configured using parameters passed at runtime. These parameters are separated by a colon and indicate external:internal respectively.
| Parameter | Function |
|---|---|
4000:4000 |
Web interface |
| Env | Function |
|---|---|
PUID=1000 |
User ID for file permissions - see User / Group Identifiers below |
PGID=1000 |
Group ID for file permissions - see User / Group Identifiers below |
TZ=UTC |
Timezone (e.g., America/New_York) |
SECRET_KEY_BASE |
Required - Phoenix secret key (generate with: openssl rand -base64 48) |
GUARDIAN_SECRET_KEY |
Required - JWT signing key (generate with: openssl rand -base64 48) |
PHX_HOST=localhost |
Public hostname for the application |
PORT=4000 |
Web server port |
MOVIES_PATH=/media/movies |
Movies directory path |
TV_PATH=/media/tv |
TV shows directory path |
See the Environment Variables Reference section below for complete configuration options including download clients, indexers, and authentication.
| Volume | Function |
|---|---|
/config |
Application data, database, and configuration files |
/media/movies |
Movies library location |
/media/tv |
TV shows library location |
/media/downloads |
Download client output directory (optional) |
π‘ Hardlink Support for Efficient Storage
For optimal storage efficiency, mydia uses hardlinks by default when importing media. Hardlinks allow the same file to appear in both your download folder and library folder without consuming additional disk space.
To enable hardlinks, ensure your downloads and library directories are on the same filesystem by using a single parent volume mount:
volumes: - /path/to/mydia/config:/config - /path/to/your/media:/media # Single mount for downloads AND librariesThen organize your host directory structure like:
/path/to/your/media/ βββ downloads/ # Download client output βββ library/ β βββ movies/ # Movies library β βββ tv/ # TV libraryAnd configure environment variables:
environment: - MOVIES_PATH=/media/library/movies - TV_PATH=/media/library/tvConfigure your download client (qBittorrent, Transmission, etc.) to save files to
/media/downloads.Benefits:
- Instant file operations (no data copying)
- Zero duplicate storage space
- Files remain seeding in your download client while available in your library
Note: If your downloads and libraries must be on different filesystems, mydia will automatically fall back to copying files. This works fine but uses more storage space and takes longer.
Mydia uses relative path storage for media files, which provides several benefits:
- Flexible Library Relocation: You can change your library root paths (e.g., from
/mnt/media/Moviesto/new/storage/Movies) without breaking existing file references - Path Independence: Media file records are stored relative to their library path, making the database portable
- Automatic Migration: When you update library paths via the Admin UI, Mydia validates that existing files are accessible at the new location before making changes
- Seamless Upgrades: The migration from absolute to relative paths happens automatically on first startup after upgrade
Library paths can be configured via:
- Environment variables (
MOVIES_PATH,TV_PATH) - Highest priority - Admin UI (Database settings)
- YAML configuration file (
config/config.yml) - Schema defaults - Lowest priority
Changes to library paths are validated against your existing media files to prevent broken references. If you need to relocate your library, update the environment variables or Admin UI settings, and Mydia will verify file accessibility before applying the changes.
When using volumes (-v flags), permissions issues can arise between the host and container. To avoid this, specify the user PUID and group PGID to ensure files created by the container are owned by your user.
Finding your IDs:
id your_userExample output: uid=1000(your_user) gid=1000(your_user)
Use these values for PUID and PGID in your container configuration.
Conflict Handling:
The entrypoint script automatically handles conflicts when the specified PUID/PGID is already in use by another user or group in the container. For example, GID 100 is typically used by the "users" group in Alpine Linux. The container will automatically resolve these conflicts by removing the conflicting user/group and applying your specified IDs.
Common scenarios:
- Using
PGID=100(conflicts with "users" group) - automatically handled β - Using
PUID=99(conflicts with "nobody" user on some systems) - automatically handled β - Any custom UID/GID that matches your host system - automatically handled β
The container logs will show which conflicts were resolved during startup.
docker compose pull
docker compose up -ddocker stop mydia
docker rm mydia
docker pull ghcr.io/getmydia/mydia:latest
# Run your docker run command againNote: Migrations run automatically on startup. Your data in /config is preserved across updates.
Mydia automatically creates database backups before running migrations to ensure your data is safe during updates:
- Automatic Backups: When migrations are pending, a timestamped backup is created before running them
- Backup Location: Stored alongside the database file (e.g.,
/config/mydia_dev_backup_YYYYMMDD_HHMMSS.db) - Automatic Cleanup: Only the 10 most recent backups are kept to save disk space
- Manual Restore: If needed, you can restore from a backup by:
- Stop the container
- Replace the database file with your chosen backup
- Restart the container
To disable automatic backups (not recommended):
# In docker-compose.yml or docker run command
environment:
- SKIP_BACKUPS=trueFor more details about backup and restore procedures, see docs/deployment/DEPLOYMENT.md.
Mydia provides separate Docker images for PostgreSQL users who prefer a more traditional database backend:
| Image Tag | Database | Use Case |
|---|---|---|
latest |
SQLite | Default, simpler setup, single-file database |
latest-pg |
PostgreSQL | Scalability, existing PostgreSQL infrastructure |
Quick Start with PostgreSQL:
# docker-compose.postgres.yml (simplified example)
services:
postgres:
image: postgres:16-alpine
environment:
POSTGRES_USER: mydia
POSTGRES_PASSWORD: changeme
POSTGRES_DB: mydia
volumes:
- postgres_data:/var/lib/postgresql/data
mydia:
image: ghcr.io/getmydia/mydia:latest-pg # PostgreSQL variant
depends_on:
postgres:
condition: service_healthy
environment:
DATABASE_TYPE: postgres
DATABASE_HOST: postgres
DATABASE_PORT: 5432
DATABASE_NAME: mydia
DATABASE_USER: mydia
DATABASE_PASSWORD: changeme
# ... other required variables
ports:
- "4000:4000"
volumes:
postgres_data:PostgreSQL Environment Variables:
| Variable | Description | Default |
|---|---|---|
DATABASE_TYPE |
Set to postgres for PostgreSQL |
sqlite |
DATABASE_HOST |
PostgreSQL server hostname | localhost |
DATABASE_PORT |
PostgreSQL server port | 5432 |
DATABASE_NAME |
Database name | mydia |
DATABASE_USER |
Database username | postgres |
DATABASE_PASSWORD |
Database password | - |
POOL_SIZE |
Connection pool size | 10 |
A complete example configuration is available in docker-compose.postgres.yml.
Important Notes:
- The database adapter is compiled into the image, so
latestandlatest-pgare not interchangeable at runtime - Use the correct image variant for your database choice
- Both variants receive the same feature updates and version numbers
- PostgreSQL images use
-pgsuffix (e.g.,1.0.0-pg,latest-pg,beta-pg)
Beta releases are available for testing new features before they are released to stable. Beta releases:
- Are tagged with
betainstead oflatest - May contain experimental features or breaking changes
- Are not recommended for production use
To use a beta release:
# Pull the latest beta release
docker pull ghcr.io/getmydia/mydia:beta
# Or use a specific beta version
docker pull ghcr.io/getmydia/mydia:1.2.3-beta.1Update your Docker Compose configuration:
services:
mydia:
image: ghcr.io/getmydia/mydia:beta # Use beta tag instead of latest
# ... rest of configurationSee DEPLOYMENT.md for advanced deployment topics.
| Variable | Description | Example |
|---|---|---|
SECRET_KEY_BASE |
Phoenix secret key for cookies/sessions | Generate with: openssl rand -base64 48 |
GUARDIAN_SECRET_KEY |
JWT signing key for authentication | Generate with: openssl rand -base64 48 |
| Variable | Description | Default |
|---|---|---|
PUID |
User ID for file permissions | 1000 |
PGID |
Group ID for file permissions | 1000 |
TZ |
Timezone (e.g., America/New_York, Europe/London) |
UTC |
DATABASE_PATH |
Path to SQLite database file | /config/mydia.db |
| Variable | Description | Default |
|---|---|---|
PHX_HOST |
Public hostname for the application | localhost |
PORT |
Web server port | 4000 |
HOST |
Server binding address | 0.0.0.0 |
URL_SCHEME |
URL scheme for external links (http/https) | http |
PHX_CHECK_ORIGIN |
WebSocket origin checking. Set to false to allow all origins (useful for IP-based access), or comma-separated list of allowed origins |
Allows PHX_HOST with any scheme |
| Variable | Description | Default |
|---|---|---|
MOVIES_PATH |
Movies directory path | /media/movies |
TV_PATH |
TV shows directory path | /media/tv |
MEDIA_SCAN_INTERVAL_HOURS |
Hours between library scans | 1 |
Configure additional library paths using numbered environment variables (<N> = 1, 2, 3, etc.):
| Variable Pattern | Description | Example |
|---|---|---|
LIBRARY_PATH_<N>_PATH |
Directory path | /media/music |
LIBRARY_PATH_<N>_TYPE |
Library type (movies, series, mixed, music, books, adult) | music |
LIBRARY_PATH_<N>_MONITORED |
Enable monitoring | true |
LIBRARY_PATH_<N>_SCAN_INTERVAL |
Scan interval in seconds | 3600 |
LIBRARY_PATH_<N>_QUALITY_PROFILE_ID |
Quality profile ID | 1 |
Example for music and books libraries:
LIBRARY_PATH_1_PATH=/media/music
LIBRARY_PATH_1_TYPE=music
LIBRARY_PATH_2_PATH=/media/books
LIBRARY_PATH_2_TYPE=booksNote: Library paths configured via environment variables are registered in the database on startup. If you later remove the environment variables, the libraries remain in the database as disabled entries to maintain data consistency. Currently there is no way to remove these entries via the Admin UIβthis will be improved in a future release.
| Variable | Description | Default |
|---|---|---|
LOCAL_AUTH_ENABLED |
Enable local username/password auth | true |
OIDC_ENABLED |
Enable OIDC/OpenID Connect auth | false |
OIDC_DISCOVERY_DOCUMENT_URI |
OIDC discovery endpoint URL | - |
OIDC_CLIENT_ID |
OIDC client ID | - |
OIDC_CLIENT_SECRET |
OIDC client secret | - |
OIDC_REDIRECT_URI |
OIDC callback URL | Auto-computed |
OIDC_SCOPES |
Space-separated scope list | openid profile email |
OIDC Provider Compatibility:
Mydia uses standard OAuth2 authentication that works with minimal provider configuration - just set client_id, client_secret, and redirect_uris in your provider. No need to configure:
token_endpoint_auth_methodsettingsresponse_modeslists- JWT-based authentication methods
- PAR (Pushed Authorization Request) settings
This should work with any standard OIDC provider including Keycloak, Authelia, Auth0, Okta, Azure AD, and Google.
See docs/OIDC_TESTING.md for detailed setup instructions and provider examples.
User Roles:
- Admin: Full access to all features including media management, downloads, configuration, and request approval
- Guest: Can browse media library and submit requests for new content that require admin approval
OIDC Auto-Promotion: The first user to log in via OIDC is automatically promoted to admin role. Subsequent OIDC users are assigned the guest role by default.
First-Time Setup:
When you first access Mydia, you'll be guided through a setup flow to create the initial admin user. You can either:
- Set a custom password of your choice
- Generate a secure random password that will be displayed once
After the admin user is created, you'll be automatically logged in and can begin configuring your media library.
β οΈ EXPERIMENTAL FEATURES WARNINGThe features controlled by these flags are very early in development and are unstable or may not work at all. Do not enable these features unless you understand the risks and are comfortable with potential issues, bugs, or breaking changes. These features are opt-in and disabled by default for good reason.
| Variable | Description | Default |
|---|---|---|
ENABLE_PLAYBACK |
Enable media playback controls and HLS streaming | false |
ENABLE_CARDIGANN |
Enable native Cardigann indexer support (hundreds of indexers without Prowlarr/Jackett) | true |
ENABLE_SUBTITLES |
Enable subtitle download and management | false |
π CARDIGANN INDEXERS NOTE
Cardigann indexer support is highly experimental. Only a limited number of indexers have been tested. You may encounter issues with untested indexers - if you do, please report them as GitHub issues to help improve compatibility. Set
ENABLE_CARDIGANN=falseto disable if needed.
Configure multiple download clients using numbered environment variables (<N> = 1, 2, 3, etc.):
| Variable Pattern | Description | Example |
|---|---|---|
DOWNLOAD_CLIENT_<N>_NAME |
Client display name | qBittorrent |
DOWNLOAD_CLIENT_<N>_TYPE |
Client type (qbittorrent, transmission, sabnzbd, nzbget, http) | qbittorrent |
DOWNLOAD_CLIENT_<N>_ENABLED |
Enable this client | true |
DOWNLOAD_CLIENT_<N>_PRIORITY |
Client priority (higher = preferred) | 1 |
DOWNLOAD_CLIENT_<N>_HOST |
Client hostname or IP | qbittorrent |
DOWNLOAD_CLIENT_<N>_PORT |
Client port | 8080 |
DOWNLOAD_CLIENT_<N>_USE_SSL |
Use SSL/TLS connection | false |
DOWNLOAD_CLIENT_<N>_USERNAME |
Authentication username (Transmission, qBittorrent, NZBGet) | - |
DOWNLOAD_CLIENT_<N>_PASSWORD |
Authentication password (Transmission, qBittorrent, NZBGet) | - |
DOWNLOAD_CLIENT_<N>_API_KEY |
API key (SABnzbd) | - |
DOWNLOAD_CLIENT_<N>_CATEGORY |
Default download category | - |
DOWNLOAD_CLIENT_<N>_DOWNLOAD_DIRECTORY |
Download output directory | - |
Supported Download Clients:
- Torrent Clients: qBittorrent, Transmission
- Usenet Clients: SABnzbd, NZBGet
Example configurations:
# qBittorrent
DOWNLOAD_CLIENT_1_NAME=qBittorrent
DOWNLOAD_CLIENT_1_TYPE=qbittorrent
DOWNLOAD_CLIENT_1_HOST=qbittorrent
DOWNLOAD_CLIENT_1_PORT=8080
DOWNLOAD_CLIENT_1_USERNAME=admin
DOWNLOAD_CLIENT_1_PASSWORD=adminpass
# Transmission
DOWNLOAD_CLIENT_2_NAME=Transmission
DOWNLOAD_CLIENT_2_TYPE=transmission
DOWNLOAD_CLIENT_2_HOST=transmission
DOWNLOAD_CLIENT_2_PORT=9091
DOWNLOAD_CLIENT_2_USERNAME=admin
DOWNLOAD_CLIENT_2_PASSWORD=adminpass
# SABnzbd (Usenet)
DOWNLOAD_CLIENT_3_NAME=SABnzbd
DOWNLOAD_CLIENT_3_TYPE=sabnzbd
DOWNLOAD_CLIENT_3_HOST=sabnzbd
DOWNLOAD_CLIENT_3_PORT=8080
DOWNLOAD_CLIENT_3_API_KEY=your-sabnzbd-api-key
# NZBGet (Usenet)
DOWNLOAD_CLIENT_4_NAME=NZBGet
DOWNLOAD_CLIENT_4_TYPE=nzbget
DOWNLOAD_CLIENT_4_HOST=nzbget
DOWNLOAD_CLIENT_4_PORT=6789
DOWNLOAD_CLIENT_4_USERNAME=nzbget
DOWNLOAD_CLIENT_4_PASSWORD=tegbzn6789Configure multiple indexers using numbered environment variables (<N> = 1, 2, 3, etc.):
| Variable Pattern | Description | Example |
|---|---|---|
INDEXER_<N>_NAME |
Indexer display name | Prowlarr |
INDEXER_<N>_TYPE |
Indexer type (prowlarr, jackett, public) | prowlarr |
INDEXER_<N>_ENABLED |
Enable this indexer | true |
INDEXER_<N>_PRIORITY |
Indexer priority (higher = preferred) | 1 |
INDEXER_<N>_BASE_URL |
Indexer base URL | http://prowlarr:9696 |
INDEXER_<N>_API_KEY |
Indexer API key | - |
INDEXER_<N>_INDEXER_IDS |
Comma-separated indexer IDs | 1,2,3 |
INDEXER_<N>_CATEGORIES |
Comma-separated categories | movies,tv |
INDEXER_<N>_RATE_LIMIT |
API rate limit (requests/sec) | - |
Example for Prowlarr:
INDEXER_1_NAME=Prowlarr
INDEXER_1_TYPE=prowlarr
INDEXER_1_BASE_URL=http://prowlarr:9696
INDEXER_1_API_KEY=your-prowlarr-api-key-hereExample for Jackett:
INDEXER_2_NAME=Jackett
INDEXER_2_TYPE=jackett
INDEXER_2_BASE_URL=http://jackett:9117
INDEXER_2_API_KEY=your-jackett-api-key-here| Variable | Description | Default |
|---|---|---|
LOG_LEVEL |
Application log level (debug, info, warning, error) | info |
Configuration is loaded in this order (highest to lowest priority):
- Environment Variables - Override everything
- Database Settings - Configured via Admin UI
- YAML File - From
config/config.yml - Schema Defaults - Built-in defaults
With Docker (Recommended):
# Start everything
./dev up -d
# Run migrations
./dev mix ecto.migrate
# View at http://localhost:4000
# Check logs for the auto-generated admin password:
./dev logs | grep "DEFAULT ADMIN USER CREATED" -A 10See all commands with ./dev
Without Docker:
mix setup
mix phx.serverVisit localhost:4000
With Nix:
# Enter development shell
nix develop
# First-time setup
mix deps.get
mix ecto.setup
# Start server
mix phx.serverThe Nix development shell provides Elixir, Erlang, Node.js, SQLite, FFmpeg, and all required build tools.
See docs/nix.md for full Nix development and NixOS deployment documentation.
Mydia provides a NixOS module for declarative deployment:
# flake.nix
{
inputs.mydia.url = "github:getmydia/mydia";
outputs = { self, nixpkgs, mydia }: {
nixosConfigurations.myserver = nixpkgs.lib.nixosSystem {
modules = [ mydia.nixosModules.default ./configuration.nix ];
};
};
}
# configuration.nix
{
services.mydia = {
enable = true;
host = "mydia.example.com";
secretKeyBaseFile = "/run/secrets/mydia/secret_key_base";
mediaLibraries = [ "/mnt/media/movies" "/mnt/media/tv" ];
};
}The module supports OIDC authentication, download clients, FlareSolverr, and systemd security hardening.
See docs/nix.md for complete configuration options and examples.
All pull requests and commits to the main branch automatically run:
- β Code compilation with warnings as errors
- β Code formatting checks
- β Static analysis with Credo
- β Full test suite (ExUnit + LiveView tests)
- β End-to-end tests (Playwright)
- β Docker build verification
Run these checks locally before committing:
mix precommitMydia uses Playwright for comprehensive browser-based testing of complete user workflows, JavaScript interactions, and real-time features.
Quick Start:
# Install dependencies
cd assets
npm install
# Run E2E tests (Chromium only, fast)
npm run test:e2e
# Run with UI for debugging
npm run test:e2e:ui
# Run in headed mode (see browser)
npm run test:e2e -- --headedWhat's Tested:
- β Authentication flows (local + OIDC)
- β LiveView real-time updates
- β JavaScript/Alpine.js interactions
- β Complete user workflows
- β Cross-browser compatibility
CI Integration:
E2E tests run automatically in GitHub Actions on every PR and push. Tests use a Docker Compose environment with mock services (OAuth2, Prowlarr, qBittorrent) for fast, reliable testing.
Writing Tests:
import { test, expect } from "@playwright/test";
import { loginAsAdmin } from "../helpers/auth";
import { assertFlashMessage } from "../helpers/liveview";
test("admin can update settings", async ({ page }) => {
await loginAsAdmin(page);
await page.goto("/admin/settings");
await page.fill('input[name="setting"]', "value");
await page.click('button[type="submit"]');
await assertFlashMessage(page, "success", "Settings saved");
});Full Documentation: See docs/testing/e2e.md for complete guide on running, writing, and debugging E2E tests.
Automatic pre-commit hooks are available to catch formatting issues before they reach CI:
# Install hooks (one-time setup)
./scripts/install-git-hooks.shThe pre-commit hook will automatically run mix format --check-formatted before each commit. If formatting issues are found, the commit will be blocked with instructions to fix them.
To bypass the hook in exceptional cases (not recommended):
git commit --no-verifyCreate compose.override.yml to add services like Transmission, Prowlarr, Jackett, or custom configurations:
cp compose.override.yml.example compose.override.yml
# Edit and uncomment services you need
./dev up -dCapture automated screenshots for documentation:
./take-screenshotsSee assets/SCREENSHOTS.md for configuration options.
Caution
Music, Books, and Adult libraries are highly experimental with minimal functionality. They support basic library scanning and browsing onlyβno metadata fetching, download automation, or quality profiles. Expect bugs and incomplete features.
| Library Type | Status | What Works | What Doesn't |
|---|---|---|---|
| Music | π§ͺ Experimental | File scanning, artist/album/track browsing | Metadata enrichment, downloads |
| Books | π§ͺ Experimental | File scanning, author/book browsing | Metadata enrichment, downloads |
| Adult | π§ͺ Experimental | File scanning, basic browsing | Metadata enrichment, downloads |
Configure library paths via Admin UI or environment variables (see Library Path Configuration).
- Phoenix 1.8 + LiveView
- Ecto + SQLite/PostgreSQL
- Oban (background jobs)
- Tailwind CSS + DaisyUI
- Req (HTTP client)
Mydia implements TRaSH Guides-compatible filename naming to preserve quality metadata and prevent download loops. Core features like title, year, quality (source/resolution), PROPER/REPACK flags, codec, and release group are fully implemented. Partial support exists for HDR detection (basic detection without format distinction) and audio codecs (codec without channel info). Missing features include edition tags, custom formats, 3D indicators, audio channels, specific HDR formats, and episode title truncation.
| Feature | Movies | TV Shows | Notes |
|---|---|---|---|
| Implemented | Title, Year, Quality, Codec, Release Group, PROPER/REPACK | Title, Year, S##E##, Episode Title, Quality, Codec, Release Group, PROPER/REPACK | Prevents duplicate downloads |
| Partial | HDR (basic), Audio codec | HDR (basic), Audio codec | No format distinction or channel info |
| Missing | Edition tags, Custom formats, 3D, Audio channels | Custom formats, Audio channels, Title truncation | Planned enhancements |
Example output: The Matrix (1999) [BluRay-1080p][DTS][x264]-GROUP.mkv β’ Breaking Bad (2008) - S01E01 - Pilot [BluRay-1080p][x264]-GROUP.mkv
Built with Elixir & Phoenix