Trailers and Extras β
Phase: H.5 Since: 0.14.0
Overview β
This document describes the trailers and extras support in Phlix, including local Trailers/ folder support, -trailer.mkv naming conventions, and TMDB trailer URL integration.
Local Trailer Discovery β
Phlix scans media directories for trailers in two locations:
Same-Level Trailers β
Trailers can be placed at the same level as the main media file:
Movies/
Avatar (2009)/
Avatar (2009).mkv
Avatar (2009)-trailer.mkv β same-level trailerTrailers/ Subfolder β
Trailers can also be placed in a Trailers/ subfolder:
Movies/
Avatar (2009)/
Avatar (2009).mkv
Trailers/
Avatar (2009)-teaser.mkv
Avatar (2009)-official-trailer.mkvNaming Conventions β
The suffix of the trailer file is extracted as the display title:
| Suffix | Display Title |
|---|---|
-trailer | Trailer |
-teaser | Teaser |
-clip | Clip |
-featurette | Featurette |
-behind-the-scenes | Behind the Scenes |
-interview | Interview |
-deleted-scene | Deleted Scene |
Supported File Extensions β
- mkv, mp4, avi, mov, wmv, flv, webm, m4v, mpg, mpeg, ts
API Endpoints β
Get All Extras β
GET /api/v1/media/{id}/extrasReturns all trailers and extras (merged, sorted by type priority).
Response:
{
"extras": [
{
"id": "...",
"media_item_id": "...",
"title": "Official Trailer",
"source": "local",
"url": "file:///path/to/trailer.mkv",
"duration": 120,
"quality": 1080,
"is_local": true,
"file_path": "/path/to/trailer.mkv"
}
],
"count": 1
}Get Trailers Only β
GET /api/v1/media/{id}/trailersReturns only trailers (not other extras).
Get Non-Trailer Extras β
GET /api/v1/media/{id}/extras/otherReturns extras that are not trailers (featurettes, behind the scenes, etc.).
Data Model β
Trailer DTO β
final readonly class Trailer
{
public function __construct(
public string $id,
public string $mediaItemId,
public string $title,
public string $source, // 'local' | 'tmdb'
public string $url,
public int $duration, // seconds
public int $quality, // 480/720/1080/2160
public bool $isLocal,
public string $filePath,
) {}
}Extra DTO β
final readonly class Extra
{
public const TYPE_FEATURETTE = 'featurette';
public const TYPE_BEHIND_THE_SCENES = 'behind_the_scenes';
public const TYPE_INTERVIEW = 'interview';
public const TYPE_CLIP = 'clip';
public const TYPE_DELETED_SCENE = 'deleted_scene';
public const TYPE_TRAILER = 'trailer';
public function __construct(
public string $id,
public string $mediaItemId,
public string $title,
public string $type,
public string $source,
public string $url,
public int $duration,
public int $quality,
public bool $isLocal,
public string $filePath,
) {}
}Database Schema β
The media_extras table stores cached trailer and extra data:
CREATE TABLE media_extras (
id CHAR(36) NOT NULL PRIMARY KEY,
media_item_id CHAR(36) NOT NULL,
title VARCHAR(256) NOT NULL,
extra_type VARCHAR(32) NOT NULL,
source VARCHAR(16) NOT NULL,
url VARCHAR(1024) NOT NULL,
file_path VARCHAR(1024) NULL,
duration INT NOT NULL DEFAULT 0,
quality INT NOT NULL DEFAULT 0,
cached_at DATETIME NOT NULL,
INDEX idx_me_media (media_item_id),
INDEX idx_me_type (extra_type)
);Caching β
Trailer and extra data is cached in the media_extras table with a 24-hour TTL. Cache is refreshed:
- When the TTL expires (on next request)
- When
FolderWatcherdetects changes inTrailers/folders - When
MediaScannerrescans the library
TMDB Integration β
Trailers from TMDB are fetched via the TmdbProvider::getTrailers() method. Local trailers take priority over TMDB trailers with the same title.
TMDB Configuration β
Ensure your config/tmdb.php has a valid API key:
return [
'api_key' => 'your_tmdb_api_key',
// ...
];Folder Watcher Integration β
The FolderWatcher detects changes in:
Trailers/directories- Files with
-trailer,-teaser,-clip,-featurettesuffixes
When detected, it signals that extras should be rescanned for the affected media item.
Architecture β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β ExtrasController β
β GET /api/v1/media/{id}/extras β
β GET /api/v1/media/{id}/trailers β
β GET /api/v1/media/{id}/extras/other β
βββββββββββββββββββββββ¬βββββββββββββββββββββββββββββββββββββββ
β
βΌ
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β TrailerResolver β
β - Merges local + TMDB trailers β
β - Local takes priority over TMDB β
β - Caches results in media_extras (24h TTL) β
ββββββββ¬βββββββββββββββββββ¬βββββββββββββββββββ¬βββββββββββββββββββββ
β β β
βΌ βΌ βΌ
βββββββββββββββ βββββββββββββββ βββββββββββββββββββ
β ExtrasRepo β βTrailerFinderβ β TmdbProvider β
β (cache) β β (scanning) β β (remote) β
βββββββββββββββ βββββββββββββββ βββββββββββββββββββMigration β
Run the migration to create the media_extras table:
php scripts/run-migrations.phpTesting β
Run unit tests:
./vendor/bin/phpunit tests/unit/Media/Extras/Run integration tests:
./vendor/bin/phpunit tests/integration/Media/Extras/