[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"marketplace-site-banners":3,"github-stars":9,"directus-extension-transcode-video-hls-secure":115},[4],{"id":5,"icon":6,"content":7,"link":8,"include_paths":6,"exclude_paths":6},"5cf2f5f6-aecf-4395-8c23-69de326fdf02",null,"Directus V12 is here","https:\u002F\u002Fdirectus.com\u002Fresources\u002Fv12-built-for-the-whole-team",{"id":10,"node_id":11,"name":12,"full_name":13,"private":14,"owner":15,"html_url":33,"description":34,"fork":14,"url":35,"forks_url":36,"keys_url":37,"collaborators_url":38,"teams_url":39,"hooks_url":40,"issue_events_url":41,"events_url":42,"assignees_url":43,"branches_url":44,"tags_url":45,"blobs_url":46,"git_tags_url":47,"git_refs_url":48,"trees_url":49,"statuses_url":50,"languages_url":51,"stargazers_url":52,"contributors_url":53,"subscribers_url":54,"subscription_url":55,"commits_url":56,"git_commits_url":57,"comments_url":58,"issue_comment_url":59,"contents_url":60,"compare_url":61,"merges_url":62,"archive_url":63,"downloads_url":64,"issues_url":65,"pulls_url":66,"milestones_url":67,"notifications_url":68,"labels_url":69,"releases_url":70,"deployments_url":71,"created_at":72,"updated_at":73,"pushed_at":74,"git_url":75,"ssh_url":76,"clone_url":77,"svn_url":33,"homepage":78,"size":79,"stargazers_count":80,"watchers_count":80,"language":81,"has_issues":82,"has_projects":82,"has_downloads":82,"has_wiki":14,"has_pages":14,"has_discussions":82,"forks_count":83,"mirror_url":6,"archived":14,"disabled":14,"open_issues_count":84,"license":85,"allow_forking":82,"is_template":14,"web_commit_signoff_required":14,"has_pull_requests":82,"pull_request_creation_policy":90,"topics":91,"visibility":32,"forks":83,"open_issues":84,"watchers":80,"default_branch":111,"temp_clone_token":6,"custom_properties":112,"organization":113,"network_count":83,"subscribers_count":114},7122594,"MDEwOlJlcG9zaXRvcnk3MTIyNTk0","directus","directus\u002Fdirectus",false,{"login":12,"id":16,"node_id":17,"avatar_url":18,"gravatar_id":19,"url":20,"html_url":21,"followers_url":22,"following_url":23,"gists_url":24,"starred_url":25,"subscriptions_url":26,"organizations_url":27,"repos_url":28,"events_url":29,"received_events_url":30,"type":31,"user_view_type":32,"site_admin":14},15967950,"MDEyOk9yZ2FuaXphdGlvbjE1OTY3OTUw","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F15967950?v=4","","https:\u002F\u002Fapi.github.com\u002Fusers\u002Fdirectus","https:\u002F\u002Fgithub.com\u002Fdirectus","https:\u002F\u002Fapi.github.com\u002Fusers\u002Fdirectus\u002Ffollowers","https:\u002F\u002Fapi.github.com\u002Fusers\u002Fdirectus\u002Ffollowing{\u002Fother_user}","https:\u002F\u002Fapi.github.com\u002Fusers\u002Fdirectus\u002Fgists{\u002Fgist_id}","https:\u002F\u002Fapi.github.com\u002Fusers\u002Fdirectus\u002Fstarred{\u002Fowner}{\u002Frepo}","https:\u002F\u002Fapi.github.com\u002Fusers\u002Fdirectus\u002Fsubscriptions","https:\u002F\u002Fapi.github.com\u002Fusers\u002Fdirectus\u002Forgs","https:\u002F\u002Fapi.github.com\u002Fusers\u002Fdirectus\u002Frepos","https:\u002F\u002Fapi.github.com\u002Fusers\u002Fdirectus\u002Fevents{\u002Fprivacy}","https:\u002F\u002Fapi.github.com\u002Fusers\u002Fdirectus\u002Freceived_events","Organization","public","https:\u002F\u002Fgithub.com\u002Fdirectus\u002Fdirectus","The flexible backend for all your projects 🐰 Turn your DB into a headless CMS, admin panels, or apps with a custom UI, instant APIs, auth & more.","https:\u002F\u002Fapi.github.com\u002Frepos\u002Fdirectus\u002Fdirectus","https:\u002F\u002Fapi.github.com\u002Frepos\u002Fdirectus\u002Fdirectus\u002Fforks","https:\u002F\u002Fapi.github.com\u002Frepos\u002Fdirectus\u002Fdirectus\u002Fkeys{\u002Fkey_id}","https:\u002F\u002Fapi.github.com\u002Frepos\u002Fdirectus\u002Fdirectus\u002Fcollaborators{\u002Fcollaborator}","https:\u002F\u002Fapi.github.com\u002Frepos\u002Fdirectus\u002Fdirectus\u002Fteams","https:\u002F\u002Fapi.github.com\u002Frepos\u002Fdirectus\u002Fdirectus\u002Fhooks","https:\u002F\u002Fapi.github.com\u002Frepos\u002Fdirectus\u002Fdirectus\u002Fissues\u002Fevents{\u002Fnumber}","https:\u002F\u002Fapi.github.com\u002Frepos\u002Fdirectus\u002Fdirectus\u002Fevents","https:\u002F\u002Fapi.github.com\u002Frepos\u002Fdirectus\u002Fdirectus\u002Fassignees{\u002Fuser}","https:\u002F\u002Fapi.github.com\u002Frepos\u002Fdirectus\u002Fdirectus\u002Fbranches{\u002Fbranch}","https:\u002F\u002Fapi.github.com\u002Frepos\u002Fdirectus\u002Fdirectus\u002Ftags","https:\u002F\u002Fapi.github.com\u002Frepos\u002Fdirectus\u002Fdirectus\u002Fgit\u002Fblobs{\u002Fsha}","https:\u002F\u002Fapi.github.com\u002Frepos\u002Fdirectus\u002Fdirectus\u002Fgit\u002Ftags{\u002Fsha}","https:\u002F\u002Fapi.github.com\u002Frepos\u002Fdirectus\u002Fdirectus\u002Fgit\u002Frefs{\u002Fsha}","https:\u002F\u002Fapi.github.com\u002Frepos\u002Fdirectus\u002Fdirectus\u002Fgit\u002Ftrees{\u002Fsha}","https:\u002F\u002Fapi.github.com\u002Frepos\u002Fdirectus\u002Fdirectus\u002Fstatuses\u002F{sha}","https:\u002F\u002Fapi.github.com\u002Frepos\u002Fdirectus\u002Fdirectus\u002Flanguages","https:\u002F\u002Fapi.github.com\u002Frepos\u002Fdirectus\u002Fdirectus\u002Fstargazers","https:\u002F\u002Fapi.github.com\u002Frepos\u002Fdirectus\u002Fdirectus\u002Fcontributors","https:\u002F\u002Fapi.github.com\u002Frepos\u002Fdirectus\u002Fdirectus\u002Fsubscribers","https:\u002F\u002Fapi.github.com\u002Frepos\u002Fdirectus\u002Fdirectus\u002Fsubscription","https:\u002F\u002Fapi.github.com\u002Frepos\u002Fdirectus\u002Fdirectus\u002Fcommits{\u002Fsha}","https:\u002F\u002Fapi.github.com\u002Frepos\u002Fdirectus\u002Fdirectus\u002Fgit\u002Fcommits{\u002Fsha}","https:\u002F\u002Fapi.github.com\u002Frepos\u002Fdirectus\u002Fdirectus\u002Fcomments{\u002Fnumber}","https:\u002F\u002Fapi.github.com\u002Frepos\u002Fdirectus\u002Fdirectus\u002Fissues\u002Fcomments{\u002Fnumber}","https:\u002F\u002Fapi.github.com\u002Frepos\u002Fdirectus\u002Fdirectus\u002Fcontents\u002F{+path}","https:\u002F\u002Fapi.github.com\u002Frepos\u002Fdirectus\u002Fdirectus\u002Fcompare\u002F{base}...{head}","https:\u002F\u002Fapi.github.com\u002Frepos\u002Fdirectus\u002Fdirectus\u002Fmerges","https:\u002F\u002Fapi.github.com\u002Frepos\u002Fdirectus\u002Fdirectus\u002F{archive_format}{\u002Fref}","https:\u002F\u002Fapi.github.com\u002Frepos\u002Fdirectus\u002Fdirectus\u002Fdownloads","https:\u002F\u002Fapi.github.com\u002Frepos\u002Fdirectus\u002Fdirectus\u002Fissues{\u002Fnumber}","https:\u002F\u002Fapi.github.com\u002Frepos\u002Fdirectus\u002Fdirectus\u002Fpulls{\u002Fnumber}","https:\u002F\u002Fapi.github.com\u002Frepos\u002Fdirectus\u002Fdirectus\u002Fmilestones{\u002Fnumber}","https:\u002F\u002Fapi.github.com\u002Frepos\u002Fdirectus\u002Fdirectus\u002Fnotifications{?since,all,participating}","https:\u002F\u002Fapi.github.com\u002Frepos\u002Fdirectus\u002Fdirectus\u002Flabels{\u002Fname}","https:\u002F\u002Fapi.github.com\u002Frepos\u002Fdirectus\u002Fdirectus\u002Freleases{\u002Fid}","https:\u002F\u002Fapi.github.com\u002Frepos\u002Fdirectus\u002Fdirectus\u002Fdeployments","2012-12-12T01:35:36Z","2026-07-12T17:19:36Z","2026-07-11T00:07:00Z","git:\u002F\u002Fgithub.com\u002Fdirectus\u002Fdirectus.git","git@github.com:directus\u002Fdirectus.git","https:\u002F\u002Fgithub.com\u002Fdirectus\u002Fdirectus.git","https:\u002F\u002Fdirectus.com",445838,36463,"TypeScript",true,4837,405,{"key":86,"name":87,"spdx_id":88,"url":6,"node_id":89},"other","Other","NOASSERTION","MDc6TGljZW5zZTA=","all",[92,93,94,95,96,97,12,98,99,100,101,102,103,104,105,106,107,108,109,110],"api","app","cms","composable","data-visualization","database","graphql","headless-cms","javascript","mariadb","mssql","mysql","no-code","node","postgresql","sql","sqlite","typescript","vue","main",{},{"login":12,"id":16,"node_id":17,"avatar_url":18,"gravatar_id":19,"url":20,"html_url":21,"followers_url":22,"following_url":23,"gists_url":24,"starred_url":25,"subscriptions_url":26,"organizations_url":27,"repos_url":28,"events_url":29,"received_events_url":30,"type":31,"user_view_type":32,"site_admin":14},327,{"name":116,"description":117,"verified":14,"id":118,"readme":119,"type":120,"last_updated":121,"host_version":122,"downloads":123,"total_downloads":202,"sandbox":14,"license":203,"publisher":204,"versions":207,"formatted_name":307,"formatted_readme":308,"formatted_description":117,"featured_image":309,"images":310,"repository_url":212,"recent_downloads_7_days":314,"recent_downloads_30_days":315},"directus-extension-transcode-video-hls-secure","Transcode uploaded videos to adaptive HLS streams with multiple quality levels (240p, 480p, 720p, 1080p, 4K)","bd77a8c0-de20-41b9-bc40-30e5da74fd5b","# Transcode Video Operation\n\nA Directus custom operation that transcodes uploaded videos to adaptive HLS streams with multiple quality levels for optimal playback across different devices and network conditions.\n\n\u003Cimg alt=\"screenshot_operation\" src=\"https:\u002F\u002Fraw.githubusercontent.com\u002Fdomdus\u002Fdirectus-extension-transcode-video-operation\u002Fmain\u002Fdocs\u002Fscreenshot_operation.png\" \u002F>\n\n## Overview\n\nThis extension adds a custom operation to Directus Flows that automatically transcodes video files into HLS format with multiple quality levels (240p, 480p, 720p, 1080p, 4K). The operation creates adaptive streaming playlists, extracts a thumbnail, and organizes all transcoded assets in Directus folders.\n\n## Features\n\n- **Adaptive HLS Streaming**: Transcodes videos to HLS format with multiple quality levels\n- **HLS AES-128 Encryption**: Automatically secures every video with a unique, randomly generated 16-byte encryption key\n- **Smart Quality Selection**: Automatically prevents upscaling - only transcodes qualities equal to or lower than source resolution\n- **Multiple Quality Levels**: Supports 240p, 480p, 720p, 1080p, and 2160p (4K)\n- **Automatic Thumbnail Extraction**: Extracts thumbnail image at 1 second from video\n- **High Bit Depth Support**: Automatically detects and converts 10-bit videos to 8-bit for maximum compatibility\n- **Folder Organization**: Automatically creates and organizes transcoded files in Directus folders\n- **Video Metadata Extraction**: Extracts dimensions, duration, and orientation information\n- **Cloud Storage Support**: Works with local storage, S3, GCS, Azure, and other cloud storage adapters\n  - Automatically downloads source files from cloud storage for processing\n  - Uploads transcoded files to specified storage location\n  - Cleans up temporary local files when using cloud storage\n- **Flexible Storage Configuration**: Choose where transcoded files are stored:\n  - Environment default (first configured storage)\n  - Same as source file\n  - Custom storage location\n \n\n## Requirements\n\n- **HLS Capable Video Player**: The default HTML5 player won't play it. You need a player with HLS streaming support. For Directus Data Studio you can install the [Streaming Video Player extension](#integration-with-streaming-video-player)\n- **FFmpeg**: FFmpeg must be installed and available in the system PATH\n  - Installation: `apt-get install ffmpeg` (Debian\u002FUbuntu) or `brew install ffmpeg` (macOS)\n  - Verify: `ffmpeg -version` and `ffprobe -version`\n  - **Docker**: For Docker deployments (https:\u002F\u002Fdirectus.io\u002Fdocs\u002Fself-hosting\u002Fdeploying#docker-compose-examples):\n    1. Add build configuration to your `docker-compose.yml`:\n       ```yaml\n       directus:\n         build:\n           context: .\n           dockerfile: Dockerfile\n       ```\n    2. Create a `Dockerfile` in the same directory with:\n       ```dockerfile\n       FROM directus\u002Fdirectus:11.13.2\n       \n       USER root\n       RUN apk add --no-cache ffmpeg\n       RUN ffmpeg -version && ffprobe -version\n       USER node\n       ```\n    3. Rebuild and restart your containers:\n       ```bash\n       docker compose up --build -d\n       ```\n\n## Installation\n\n### Via Directus Marketplace\n\n1. Open your Directus project\n2. Navigate to **Settings** → **Extensions**\n3. Click **Browse Marketplace**\n4. Search for \"Transcode Video Operation\"\n5. Click **Install**\n\n### Manual Installation\n\n1. Install package\n\n```bash\nnpm install directus-extension-transcode-video-operation\n```\n\n2. Build the extension:\n```bash\nnpm run build\n```\n\n3. Copy the `dist` folder to your Directus extensions directory:\n```\ndirectus\u002Fextensions\u002Fdirectus-extension-transcode-video-operation\u002F\n```\n\n4. Restart your Directus instance\n\n## Usage\n\n### Setting Up a Flow\n\n1. Navigate to **Settings** → **Flows**\n2. Create a new flow or edit an existing one\n3. Add a trigger (e.g., **Event Hook** for file uploads)\n4. Add the **Transcode Video Operation**\n5. Configure the operation parameters (see Configuration below)\n6. Save and activate the flow\n\n\u003Cimg width=\"600px\" alt=\"screenshot_flow_upload\" src=\"https:\u002F\u002Fraw.githubusercontent.com\u002Fdomdus\u002Fdirectus-extension-transcode-video-operation\u002Fmain\u002Fdocs\u002Fscreenshot_flow_upload.png\" \u002F>\n\n### Operation Parameters\n\n- **File** (required): The Directus file to transcode\n  - Accepts: File UUID (string) or Directus File Object\n  - Use `{{ $last }}` to reference the file from a previous operation\n  - Example: `{{ $trigger.body.key }}` for event hook triggers\n  - Example: `{{ $last.video }}` for file object from collection\n  \n- **Folder** (optional): The Directus folder where transcoded files will be stored\n  - Uses Directus folder selector interface with create capabilities\n  - If not provided, a new folder will be created automatically\n  - A subfolder with the video filename will be created automatically\n  - All transcoded segments, playlists, encryption keys, and thumbnails will be stored in this folder structure\n\n- **Key Base URL** (optional): The public URL prefix for the encryption key file\n  - Use this if you are serving keys via a separate secure endpoint or CDN\n  - Example: `https:\u002F\u002Fkeys.yoursite.com\u002Fvideo-keys`\n  - If provided, the `.m3u8` playlist will reference the key as `https:\u002F\u002Fkeys.yoursite.com\u002Fvideo-keys\u002Ffilename.key`\n  - If left blank (default), a relative path `filename.key` will be used, which will run against Directus `\u002Fassets` endpoint if `Playlist Reference Type` is set to `id`.\n\n- **Quality Levels** (optional, default: all qualities): Select which quality levels to transcode\n  - Available: 240p, 480p, 720p, 1080p, 2160p (4K)\n  - Only qualities equal to or lower than source resolution will be transcoded (no upscaling)\n  - Example: A 1080p source video will only transcode 240p, 480p, 720p, and 1080p (4K will be skipped)\n\n- **Storage Adapter** (optional, default: `Environment Configuration (First One)`): Where transcoded files should be stored\n  - **Environment Configuration (First One)**: Uses the first configured storage location from environment variables\n  - **Same as Source File**: Stores transcoded files in the same storage location as the source file\n  - **Other**: Allows specifying a custom storage location name (must match one of your configured `STORAGE_LOCATIONS`)\n\n- **Target Storage Location** (optional): Custom storage location name\n  - Only visible when \"Storage Adapter\" is set to \"Other\"\n  - Must match one of your configured storage locations (e.g., `local`, `s3`, `gcs`)\n  - Example: If you have `STORAGE_LOCATIONS=\"local,s3\"`, you can specify `s3` here\n\n- **Thread Count** (optional, default: `1`): Number of CPU threads to use for encoding\n  - `1` = Single-threaded encoding (default, most compatible)\n  - `2+` = Use specific number of threads (e.g., `4` for 4 threads)\n  - `0` = Use all available CPU cores (fastest, but may impact system performance)\n  - Note: More threads = faster encoding, but higher CPU usage\n\n- **Process Priority** (optional, default: `19`): CPU priority (nice value) for transcoding process\n  - Range: 0 (default\u002Fhighest priority) to 19 (lowest priority)\n  - Lower values = higher CPU priority (may impact system performance)\n  - Higher values = lower CPU priority (better for background processing)\n  - Recommended: Use `19` when transcoding might impact system performance\n  - **Note**: Only works on Unix-like systems (Linux, macOS). On Windows, this setting is ignored with a warning.\n\n- **Playlist Reference Type** (optional, default: `id`): How playlists should reference segments\n  - **`id`** (default): Uses Directus file IDs - playlists reference segments as `\u002Fassets\u002F:file_id` to run against \u002Fassets endpoint\n  - **`filename_disk`**: Uses original filenames - playlists reference segments by filename (useful for custom streaming servers)\n\n### Example Flow Configuration (Collection Item Trigger)\n\n**Trigger**: Manual trigger\n- **Collections**: `your_collection`\n- **Asynchronous**: `enabled`\n\n**Read Data**: Read `your_collection` with video field query:\n- **IDs**: `{{$trigger.body.keys[0]}}`\n- **Query**:\n```json\n{\n    \"fields\": \"*,video.*\"\n}\n```\n\n **Transcode Video Operation**: \n - **File**: `{{ $last.video }}`\n- **Folder**: `{{ $last.video.folder }}` (or select\u002Fcreate via folder picker)\n- **Quality Levels**: `[\"240p\", \"480p\", \"720p\", \"1080p\"]`\n- **Storage Adapter**: `Same as Source File` (or `Environment Configuration (First One)`, or `Other` with custom storage)\n- **Thread Count**: `1` (or `0` for all cores, `4` for 4 threads, etc.)\n- **Process Priority**: `19` (or `0` for higher priority)\n- **Playlist Reference Type**: `id`\n\n**Update Data**: Update `your_collection` with payload:\n```json\n{\n    \"stream_link\": \"\u002Fassets\u002F{{$last.master.id}}\",\n    \"image\": \"{{$last.metadata.thumbnail}}\"\n}\n```\n\n\u003Cimg width=\"600px\" alt=\"screenshot_flow_collection\" src=\"https:\u002F\u002Fraw.githubusercontent.com\u002Fdomdus\u002Fdirectus-extension-transcode-video-operation\u002Fmain\u002Fdocs\u002Fscreenshot_flow_collection.png\" \u002F>\n\n*Collection Flow Example (sets stream link field in directus file)*\n\n## Output Structure\n\nThe operation creates the following files in the specified virtual folder:\n\n\u003Cimg width=\"600px\" alt=\"screenshot_file_lib\" src=\"https:\u002F\u002Fraw.githubusercontent.com\u002Fdomdus\u002Fdirectus-extension-transcode-video-operation\u002Fmain\u002Fdocs\u002Fscreenshot_file_lib.png\" \u002F>\n\n```\nfolder\u002F\n  └── video-filename\u002F\n      ├── video-filename_240p.m3u8          # 240p quality playlist\n      ├── video-filename_240p_000.ts        # 240p segments\n      ├── video-filename_480p.m3u8          # 480p quality playlist\n      ├── video-filename_720p.m3u8          # 720p quality playlist\n      ├── video-filename_1080p.m3u8         # 1080p quality playlist\n      ├── video-filename_playlist.m3u8      # Master playlist (references all qualities)\n      ├── video-filename.key                # AES-128 binary encryption key\n      └── video-filename_thumb.jpg          # Thumbnail image\n```\n\n### Operation Response\n\nThe operation returns a JSON object with:\n\n```json\n{\n  \"master\": {\n    \"id\": \"file-uuid\",\n    \"filename_disk\": \"video-filename_playlist.m3u8\"\n  },\n  \"metadata\": {\n    \"availableQualities\": [240, 480, 720, 1080],\n    \"dimensions\": {\n      \"width\": 1920,\n      \"height\": 1080,\n      \"isVertical\": false\n    },\n    \"duration\": 125000,\n    \"thumbnail\": \"thumbnail-file-uuid\"\n  },\n  \"files\": [\n    {\n      \"filename_disk\": \"video-filename_240p.m3u8\",\n      \"id\": \"file-uuid-1\"\n    },\n    {\n      \"filename_disk\": \"video-filename_thumb.jpg\",\n      \"id\": \"file-uuid-2\"\n    }\n    \u002F\u002F ... more files\n  ]\n}\n```\n\n## How It Works\n\n1. **File Input Processing**: \n   - If file is a UUID string, fetches the full file object from Directus\n   - If file is already a file object, uses it directly\n2. **Source File Handling**:\n   - **Local Storage**: Uses file directly from disk\n   - **Cloud Storage**: Downloads source file to temporary location for processing\n3. **Storage Configuration**: Determines target storage location based on user selection\n   - Resolves storage driver (local vs. cloud) from environment configuration\n4. **File Validation**: Checks that the input file exists and is accessible\n5. **Metadata Extraction**: Uses `ffprobe` to get video dimensions, duration, and bit depth\n6. **Key Generation**: Generates a unique 16-byte random key for AES-128 encryption\n7. **Quality Filtering**: Filters out quality levels that would require upscaling\n8. **Bit Depth Detection**: Detects 10-bit videos and adds pixel format conversion\n9. **Transcoding**: Uses `ffmpeg` to transcode each quality level sequentially\n    - Applies AES-128 encryption using the generated key and a temporary `.keyinfo` file\n    - Creates HLS segments (`.ts` files) and quality playlists (`.m3u8`)\n    - Uses H.264 codec with optimized settings for each quality\n    - Applies process priority (nice value) on Unix-like systems\n10. **Master Playlist**: Generates master playlist with bandwidth and resolution metadata\n11. **Thumbnail Extraction**: Extracts thumbnail at 1 second mark (if not already exists)\n12. **Folder Creation**: Creates Directus virtual folder structure for organization\n13. **File Upload**: \n    - Checks for existing files to prevent duplicates\n    - For cloud storage: Uploads files and cleans up local copies\n    - For local storage: Files remain on disk\n13. **Cleanup**: \n    - Removes temporary downloaded source file (if from cloud storage)\n    - Removes local transcoded files (if target storage is cloud)\n\n## Technical Details\n\n### Encoding Settings\n\n- **Codec**: H.264 (libx264)\n- **Profile**: Main (maximum compatibility)\n- **Audio**: AAC, 48kHz\n- **Segment Duration**: 4 seconds\n- **Playlist Type**: VOD (Video on Demand)\n- **CRF**: 20 (constant rate factor for quality)\n- **Process Priority**: Configurable nice value (0-19) for CPU priority control\n\n### Storage Handling\n\n- **Local Storage**: Files are created directly on disk and registered in Directus\n- **Cloud Storage**: \n  - Source files are downloaded via HTTP to temporary location for FFmpeg processing\n  - Transcoded files are uploaded to cloud storage via Directus FilesService\n  - Temporary local files are automatically cleaned up after upload\n- **Storage Detection**: Automatically detects storage driver type from environment configuration\n\n### Quality Bitrates\n\n- **240p**: 400 kbps video, 64 kbps audio\n- **480p**: 1400 kbps video, 128 kbps audio\n- **720p**: 2800 kbps video, 128 kbps audio\n- **1080p**: 5000 kbps video, 192 kbps audio\n- **2160p (4K)**: 20000 kbps video, 192 kbps audio\n\n### Upscaling Prevention\n\nThe operation automatically detects the source video resolution and only transcodes quality levels that are equal to or lower than the source. For example:\n- **1080p source**: Only transcodes 240p, 480p, 720p, 1080p (skips 4K)\n- **720p source**: Only transcodes 240p, 480p, 720p (skips 1080p and 4K)\n- **4K source**: Transcodes all available quality levels\n\n## Environment Variables\n\nThe operation requires several standard Directus environment variables to be correctly configured:\n\n- `STORAGE_LOCATIONS`: Needed to resolve all available storage locations.\n- `STORAGE_[LOCATION]_DRIVER`: Needed to distinguish between local and cloud storage.\n- `STORAGE_[LOCATION]_ROOT`: Required for local storage path resolution.\n- `PUBLIC_URL`: Used to construct the internal download URL when source files are on cloud storage.\n- `HOST` and `PORT`: Used as fallbacks if `PUBLIC_URL` is not defined.\n\n## Security Considerations\n\n> [!IMPORTANT]\n> The HLS AES-128 encryption key is stored in the same Directus folder as the video segments by default. To restrict unauthorized access to the encryption key, you should either:\n> 1. Set folder permissions in Directus to restrict visibility.\n> 2. Use a custom `Key Base URL` to point to a secure key server.\n> 3. Use a storage adapter that supports signed URLs or custom access control.\n\n## Integration with Streaming Video Player\n\nThis operation works seamlessly with the [Streaming Video Player](https:\u002F\u002Fgithub.com\u002Fdomdus\u002Fdirectus-extension-streaming-video-player) extension (available in Directus Marketplace):\n\n1. Transcode videos using this operation\n2. Store the master playlist reference in a string field\n3. Use the Streaming Video Player interface to play the HLS stream\n\n## License\n\nMIT\n\n","operation","2026-07-11T23:35:32.226Z","^11.0.0",[124,127,130,133,136,139,142,145,148,151,154,157,159,162,165,167,169,172,174,176,179,181,184,186,188,190,192,194,196,199],{"date":125,"count":126},"2026-06-12",6,{"date":128,"count":129},"2026-06-13",26,{"date":131,"count":132},"2026-06-14",9,{"date":134,"count":135},"2026-06-15",14,{"date":137,"count":138},"2026-06-16",24,{"date":140,"count":141},"2026-06-17",16,{"date":143,"count":144},"2026-06-18",12,{"date":146,"count":147},"2026-06-19",19,{"date":149,"count":150},"2026-06-20",25,{"date":152,"count":153},"2026-06-21",28,{"date":155,"count":156},"2026-06-22",4,{"date":158,"count":132},"2026-06-23",{"date":160,"count":161},"2026-06-24",13,{"date":163,"count":164},"2026-06-25",0,{"date":166,"count":132},"2026-06-26",{"date":168,"count":135},"2026-06-27",{"date":170,"count":171},"2026-06-28",7,{"date":173,"count":171},"2026-06-29",{"date":175,"count":156},"2026-06-30",{"date":177,"count":178},"2026-07-01",15,{"date":180,"count":144},"2026-07-02",{"date":182,"count":183},"2026-07-03",8,{"date":185,"count":164},"2026-07-04",{"date":187,"count":164},"2026-07-05",{"date":189,"count":156},"2026-07-06",{"date":191,"count":164},"2026-07-07",{"date":193,"count":183},"2026-07-08",{"date":195,"count":126},"2026-07-09",{"date":197,"count":198},"2026-07-10",10,{"date":200,"count":201},"2026-07-11",297,2072,"MIT",{"username":205,"verified":14,"id":206,"github_username":6,"github_name":6,"github_blog":6,"github_bio":6,"github_location":6,"github_company":6,"github_avatar_url":6},"ymys","043f57c7-3cc7-493c-98f4-c435b8370896",[208,220,228,236,244,252,259,267,275,283,291,299],{"version":209,"package":118,"publisher":206,"type":120,"host_version":122,"publish_date":210,"verified":14,"id":211,"url_repository":212,"url_homepage":213,"url_bugs":214,"file_count":215,"unpacked_size":216,"sandbox":14,"sandbox_requested_scopes":6,"license":203,"bundled":217,"maintainers":218},"2.4.0","2026-05-29T07:37:48.477Z","0a9f2778-b1ae-4544-97ec-682057c333ed","https:\u002F\u002Fgithub.com\u002Fymys\u002Fdirectus-extension-transcode-video-HLS-secure.git","https:\u002F\u002Fgithub.com\u002Fymys\u002Fdirectus-extension-transcode-video-HLS-secure#readme","https:\u002F\u002Fgithub.com\u002Fymys\u002Fdirectus-extension-transcode-video-HLS-secure\u002Fissues",5,52867,[],[219],14503,{"version":221,"package":118,"publisher":206,"type":120,"host_version":122,"publish_date":222,"verified":14,"id":223,"url_repository":212,"url_homepage":213,"url_bugs":214,"file_count":215,"unpacked_size":224,"sandbox":14,"sandbox_requested_scopes":6,"license":203,"bundled":225,"maintainers":226},"3.4.0","2026-03-31T02:53:47.869Z","0e0c424d-6279-4536-821a-4722637220e1",988200,[],[227],14080,{"version":229,"package":118,"publisher":206,"type":120,"host_version":122,"publish_date":230,"verified":14,"id":231,"url_repository":212,"url_homepage":213,"url_bugs":214,"file_count":215,"unpacked_size":232,"sandbox":14,"sandbox_requested_scopes":6,"license":203,"bundled":233,"maintainers":234},"2.3.0","2026-03-31T01:35:42.750Z","15829531-3494-451c-8dd4-30560ba02076",46015,[],[235],14079,{"version":237,"package":118,"publisher":206,"type":120,"host_version":122,"publish_date":238,"verified":14,"id":239,"url_repository":212,"url_homepage":213,"url_bugs":214,"file_count":126,"unpacked_size":240,"sandbox":14,"sandbox_requested_scopes":6,"license":203,"bundled":241,"maintainers":242},"2.9.0","2026-06-07T11:45:22.941Z","25c400a8-4902-4126-8af0-965adb636136",934986,[],[243],14567,{"version":245,"package":118,"publisher":206,"type":120,"host_version":122,"publish_date":246,"verified":14,"id":247,"url_repository":212,"url_homepage":213,"url_bugs":214,"file_count":126,"unpacked_size":248,"sandbox":14,"sandbox_requested_scopes":6,"license":203,"bundled":249,"maintainers":250},"2.10.0","2026-06-07T18:44:06.666Z","613dbc87-d363-4184-a36e-7b9fc7d050e8",935734,[],[251],14568,{"version":253,"package":118,"publisher":206,"type":120,"host_version":122,"publish_date":121,"verified":14,"id":254,"url_repository":212,"url_homepage":213,"url_bugs":214,"file_count":126,"unpacked_size":255,"sandbox":14,"sandbox_requested_scopes":6,"license":203,"bundled":256,"maintainers":257},"3.13.0","736dda78-4b17-4082-bd70-4166b7e61272",936419,[],[258],14685,{"version":260,"package":118,"publisher":206,"type":120,"host_version":122,"publish_date":261,"verified":14,"id":262,"url_repository":212,"url_homepage":213,"url_bugs":214,"file_count":215,"unpacked_size":263,"sandbox":14,"sandbox_requested_scopes":6,"license":203,"bundled":264,"maintainers":265},"2.7.0","2026-06-04T05:08:26.824Z","76f4b919-c627-4b3d-9788-4d1b6a910f9a",60646,[],[266],14546,{"version":268,"package":118,"publisher":206,"type":120,"host_version":122,"publish_date":269,"verified":14,"id":270,"url_repository":212,"url_homepage":213,"url_bugs":214,"file_count":126,"unpacked_size":271,"sandbox":14,"sandbox_requested_scopes":6,"license":203,"bundled":272,"maintainers":273},"3.12.0","2026-07-11T22:41:37.088Z","8041856d-885a-4f10-96d8-794bd796b09c",936442,[],[274],14684,{"version":276,"package":118,"publisher":206,"type":120,"host_version":122,"publish_date":277,"verified":14,"id":278,"url_repository":212,"url_homepage":213,"url_bugs":214,"file_count":215,"unpacked_size":279,"sandbox":14,"sandbox_requested_scopes":6,"license":203,"bundled":280,"maintainers":281},"2.6.0","2026-06-03T15:22:04.748Z","8911ee4f-89c9-4244-9fec-fcc424592e7d",60124,[],[282],14544,{"version":284,"package":118,"publisher":206,"type":120,"host_version":122,"publish_date":285,"verified":14,"id":286,"url_repository":212,"url_homepage":213,"url_bugs":214,"file_count":126,"unpacked_size":287,"sandbox":14,"sandbox_requested_scopes":6,"license":203,"bundled":288,"maintainers":289},"3.11.0","2026-06-09T12:42:39.531Z","89fb9a1d-fdab-4275-8a5b-c68b93efa6b8",936447,[],[290],14574,{"version":292,"package":118,"publisher":206,"type":120,"host_version":122,"publish_date":293,"verified":14,"id":294,"url_repository":212,"url_homepage":213,"url_bugs":214,"file_count":126,"unpacked_size":295,"sandbox":14,"sandbox_requested_scopes":6,"license":203,"bundled":296,"maintainers":297},"2.8.0","2026-06-05T12:58:57.827Z","bac59447-8c28-421f-840e-82d93f745452",929822,[],[298],14562,{"version":300,"package":118,"publisher":206,"type":120,"host_version":122,"publish_date":301,"verified":14,"id":302,"url_repository":212,"url_homepage":213,"url_bugs":214,"file_count":215,"unpacked_size":303,"sandbox":14,"sandbox_requested_scopes":6,"license":203,"bundled":304,"maintainers":305},"2.5.0","2026-05-29T14:05:07.456Z","dbf3aca9-a752-42a6-bf61-e22f26a33c28",53009,[],[306],14504,"Transcode Video Hls Secure","\u003Ch1>Transcode Video Operation\u003C\u002Fh1>\n\u003Cp>A Directus custom operation that transcodes uploaded videos to adaptive HLS streams with multiple quality levels for optimal playback across different devices and network conditions.\u003C\u002Fp>\n\u003Cimg alt=\"screenshot_operation\" src=\"https:\u002F\u002Fraw.githubusercontent.com\u002Fdomdus\u002Fdirectus-extension-transcode-video-operation\u002Fmain\u002Fdocs\u002Fscreenshot_operation.png\">\n\u003Ch2>Overview\u003C\u002Fh2>\n\u003Cp>This extension adds a custom operation to Directus Flows that automatically transcodes video files into HLS format with multiple quality levels (240p, 480p, 720p, 1080p, 4K). The operation creates adaptive streaming playlists, extracts a thumbnail, and organizes all transcoded assets in Directus folders.\u003C\u002Fp>\n\u003Ch2>Features\u003C\u002Fh2>\n\u003Cul>\n\u003Cli>\u003Cstrong>Adaptive HLS Streaming\u003C\u002Fstrong>: Transcodes videos to HLS format with multiple quality levels\u003C\u002Fli>\n\u003Cli>\u003Cstrong>HLS AES-128 Encryption\u003C\u002Fstrong>: Automatically secures every video with a unique, randomly generated 16-byte encryption key\u003C\u002Fli>\n\u003Cli>\u003Cstrong>Smart Quality Selection\u003C\u002Fstrong>: Automatically prevents upscaling - only transcodes qualities equal to or lower than source resolution\u003C\u002Fli>\n\u003Cli>\u003Cstrong>Multiple Quality Levels\u003C\u002Fstrong>: Supports 240p, 480p, 720p, 1080p, and 2160p (4K)\u003C\u002Fli>\n\u003Cli>\u003Cstrong>Automatic Thumbnail Extraction\u003C\u002Fstrong>: Extracts thumbnail image at 1 second from video\u003C\u002Fli>\n\u003Cli>\u003Cstrong>High Bit Depth Support\u003C\u002Fstrong>: Automatically detects and converts 10-bit videos to 8-bit for maximum compatibility\u003C\u002Fli>\n\u003Cli>\u003Cstrong>Folder Organization\u003C\u002Fstrong>: Automatically creates and organizes transcoded files in Directus folders\u003C\u002Fli>\n\u003Cli>\u003Cstrong>Video Metadata Extraction\u003C\u002Fstrong>: Extracts dimensions, duration, and orientation information\u003C\u002Fli>\n\u003Cli>\u003Cstrong>Cloud Storage Support\u003C\u002Fstrong>: Works with local storage, S3, GCS, Azure, and other cloud storage adapters\n\u003Cul>\n\u003Cli>Automatically downloads source files from cloud storage for processing\u003C\u002Fli>\n\u003Cli>Uploads transcoded files to specified storage location\u003C\u002Fli>\n\u003Cli>Cleans up temporary local files when using cloud storage\u003C\u002Fli>\n\u003C\u002Ful>\n\u003C\u002Fli>\n\u003Cli>\u003Cstrong>Flexible Storage Configuration\u003C\u002Fstrong>: Choose where transcoded files are stored:\n\u003Cul>\n\u003Cli>Environment default (first configured storage)\u003C\u002Fli>\n\u003Cli>Same as source file\u003C\u002Fli>\n\u003Cli>Custom storage location\u003C\u002Fli>\n\u003C\u002Ful>\n\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Ch2>Requirements\u003C\u002Fh2>\n\u003Cul>\n\u003Cli>\u003Cstrong>HLS Capable Video Player\u003C\u002Fstrong>: The default HTML5 player won't play it. You need a player with HLS streaming support. For Directus Data Studio you can install the \u003Ca href=\"#integration-with-streaming-video-player\">Streaming Video Player extension\u003C\u002Fa>\u003C\u002Fli>\n\u003Cli>\u003Cstrong>FFmpeg\u003C\u002Fstrong>: FFmpeg must be installed and available in the system PATH\n\u003Cul>\n\u003Cli>Installation: \u003Ccode>apt-get install ffmpeg\u003C\u002Fcode> (Debian\u002FUbuntu) or \u003Ccode>brew install ffmpeg\u003C\u002Fcode> (macOS)\u003C\u002Fli>\n\u003Cli>Verify: \u003Ccode>ffmpeg -version\u003C\u002Fcode> and \u003Ccode>ffprobe -version\u003C\u002Fcode>\u003C\u002Fli>\n\u003Cli>\u003Cstrong>Docker\u003C\u002Fstrong>: For Docker deployments (\u003Ca href=\"https:\u002F\u002Fdirectus.io\u002Fdocs\u002Fself-hosting\u002Fdeploying#docker-compose-examples\">https:\u002F\u002Fdirectus.io\u002Fdocs\u002Fself-hosting\u002Fdeploying#docker-compose-examples\u003C\u002Fa>):\n\u003Col>\n\u003Cli>Add build configuration to your \u003Ccode>docker-compose.yml\u003C\u002Fcode>:\n\u003Cpre>\u003Ccode class=\"language-yaml\">directus:  build:    context: .    dockerfile: Dockerfile\n\u003C\u002Fcode>\u003C\u002Fpre>\u003C\u002Fli>\n\u003Cli>Create a \u003Ccode>Dockerfile\u003C\u002Fcode> in the same directory with:\n\u003Cpre>\u003Ccode class=\"language-dockerfile\">FROM directus\u002Fdirectus:11.13.2USER rootRUN apk add --no-cache ffmpegRUN ffmpeg -version &amp;&amp; ffprobe -versionUSER node\n\u003C\u002Fcode>\u003C\u002Fpre>\u003C\u002Fli>\n\u003Cli>Rebuild and restart your containers:\n\u003Cpre>\u003Ccode class=\"language-bash\">docker compose up --build -d\n\u003C\u002Fcode>\u003C\u002Fpre>\u003C\u002Fli>\n\u003C\u002Fol>\n\u003C\u002Fli>\n\u003C\u002Ful>\n\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Ch2>Installation\u003C\u002Fh2>\n\u003Ch3>Via Directus Marketplace\u003C\u002Fh3>\n\u003Col>\n\u003Cli>Open your Directus project\u003C\u002Fli>\n\u003Cli>Navigate to \u003Cstrong>Settings\u003C\u002Fstrong> → \u003Cstrong>Extensions\u003C\u002Fstrong>\u003C\u002Fli>\n\u003Cli>Click \u003Cstrong>Browse Marketplace\u003C\u002Fstrong>\u003C\u002Fli>\n\u003Cli>Search for \"Transcode Video Operation\"\u003C\u002Fli>\n\u003Cli>Click \u003Cstrong>Install\u003C\u002Fstrong>\u003C\u002Fli>\n\u003C\u002Fol>\n\u003Ch3>Manual Installation\u003C\u002Fh3>\n\u003Col>\n\u003Cli>Install package\u003C\u002Fli>\n\u003C\u002Fol>\n\u003Cpre>\u003Ccode class=\"language-bash\">npm install directus-extension-transcode-video-operation\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Col>\n\u003Cli>Build the extension:\u003C\u002Fli>\n\u003C\u002Fol>\n\u003Cpre>\u003Ccode class=\"language-bash\">npm run build\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Col>\n\u003Cli>Copy the \u003Ccode>dist\u003C\u002Fcode> folder to your Directus extensions directory:\u003C\u002Fli>\n\u003C\u002Fol>\n\u003Cpre>\u003Ccode>directus\u002Fextensions\u002Fdirectus-extension-transcode-video-operation\u002F\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Col>\n\u003Cli>Restart your Directus instance\u003C\u002Fli>\n\u003C\u002Fol>\n\u003Ch2>Usage\u003C\u002Fh2>\n\u003Ch3>Setting Up a Flow\u003C\u002Fh3>\n\u003Col>\n\u003Cli>Navigate to \u003Cstrong>Settings\u003C\u002Fstrong> → \u003Cstrong>Flows\u003C\u002Fstrong>\u003C\u002Fli>\n\u003Cli>Create a new flow or edit an existing one\u003C\u002Fli>\n\u003Cli>Add a trigger (e.g., \u003Cstrong>Event Hook\u003C\u002Fstrong> for file uploads)\u003C\u002Fli>\n\u003Cli>Add the \u003Cstrong>Transcode Video Operation\u003C\u002Fstrong>\u003C\u002Fli>\n\u003Cli>Configure the operation parameters (see Configuration below)\u003C\u002Fli>\n\u003Cli>Save and activate the flow\u003C\u002Fli>\n\u003C\u002Fol>\n\u003Cimg alt=\"screenshot_flow_upload\" src=\"https:\u002F\u002Fraw.githubusercontent.com\u002Fdomdus\u002Fdirectus-extension-transcode-video-operation\u002Fmain\u002Fdocs\u002Fscreenshot_flow_upload.png\">\n\u003Ch3>Operation Parameters\u003C\u002Fh3>\n\u003Cul>\n\u003Cli>\n\u003Cp>\u003Cstrong>File\u003C\u002Fstrong> (required): The Directus file to transcode\u003C\u002Fp>\n\u003Cul>\n\u003Cli>Accepts: File UUID (string) or Directus File Object\u003C\u002Fli>\n\u003Cli>Use \u003Ccode>{{ $last }}\u003C\u002Fcode> to reference the file from a previous operation\u003C\u002Fli>\n\u003Cli>Example: \u003Ccode>{{ $trigger.body.key }}\u003C\u002Fcode> for event hook triggers\u003C\u002Fli>\n\u003Cli>Example: \u003Ccode>{{ $last.video }}\u003C\u002Fcode> for file object from collection\u003C\u002Fli>\n\u003C\u002Ful>\n\u003C\u002Fli>\n\u003Cli>\n\u003Cp>\u003Cstrong>Folder\u003C\u002Fstrong> (optional): The Directus folder where transcoded files will be stored\u003C\u002Fp>\n\u003Cul>\n\u003Cli>Uses Directus folder selector interface with create capabilities\u003C\u002Fli>\n\u003Cli>If not provided, a new folder will be created automatically\u003C\u002Fli>\n\u003Cli>A subfolder with the video filename will be created automatically\u003C\u002Fli>\n\u003Cli>All transcoded segments, playlists, encryption keys, and thumbnails will be stored in this folder structure\u003C\u002Fli>\n\u003C\u002Ful>\n\u003C\u002Fli>\n\u003Cli>\n\u003Cp>\u003Cstrong>Key Base URL\u003C\u002Fstrong> (optional): The public URL prefix for the encryption key file\u003C\u002Fp>\n\u003Cul>\n\u003Cli>Use this if you are serving keys via a separate secure endpoint or CDN\u003C\u002Fli>\n\u003Cli>Example: \u003Ccode>https:\u002F\u002Fkeys.yoursite.com\u002Fvideo-keys\u003C\u002Fcode>\u003C\u002Fli>\n\u003Cli>If provided, the \u003Ccode>.m3u8\u003C\u002Fcode> playlist will reference the key as \u003Ccode>https:\u002F\u002Fkeys.yoursite.com\u002Fvideo-keys\u002Ffilename.key\u003C\u002Fcode>\u003C\u002Fli>\n\u003Cli>If left blank (default), a relative path \u003Ccode>filename.key\u003C\u002Fcode> will be used, which will run against Directus \u003Ccode>\u002Fassets\u003C\u002Fcode> endpoint if \u003Ccode>Playlist Reference Type\u003C\u002Fcode> is set to \u003Ccode>id\u003C\u002Fcode>.\u003C\u002Fli>\n\u003C\u002Ful>\n\u003C\u002Fli>\n\u003Cli>\n\u003Cp>\u003Cstrong>Quality Levels\u003C\u002Fstrong> (optional, default: all qualities): Select which quality levels to transcode\u003C\u002Fp>\n\u003Cul>\n\u003Cli>Available: 240p, 480p, 720p, 1080p, 2160p (4K)\u003C\u002Fli>\n\u003Cli>Only qualities equal to or lower than source resolution will be transcoded (no upscaling)\u003C\u002Fli>\n\u003Cli>Example: A 1080p source video will only transcode 240p, 480p, 720p, and 1080p (4K will be skipped)\u003C\u002Fli>\n\u003C\u002Ful>\n\u003C\u002Fli>\n\u003Cli>\n\u003Cp>\u003Cstrong>Storage Adapter\u003C\u002Fstrong> (optional, default: \u003Ccode>Environment Configuration (First One)\u003C\u002Fcode>): Where transcoded files should be stored\u003C\u002Fp>\n\u003Cul>\n\u003Cli>\u003Cstrong>Environment Configuration (First One)\u003C\u002Fstrong>: Uses the first configured storage location from environment variables\u003C\u002Fli>\n\u003Cli>\u003Cstrong>Same as Source File\u003C\u002Fstrong>: Stores transcoded files in the same storage location as the source file\u003C\u002Fli>\n\u003Cli>\u003Cstrong>Other\u003C\u002Fstrong>: Allows specifying a custom storage location name (must match one of your configured \u003Ccode>STORAGE_LOCATIONS\u003C\u002Fcode>)\u003C\u002Fli>\n\u003C\u002Ful>\n\u003C\u002Fli>\n\u003Cli>\n\u003Cp>\u003Cstrong>Target Storage Location\u003C\u002Fstrong> (optional): Custom storage location name\u003C\u002Fp>\n\u003Cul>\n\u003Cli>Only visible when \"Storage Adapter\" is set to \"Other\"\u003C\u002Fli>\n\u003Cli>Must match one of your configured storage locations (e.g., \u003Ccode>local\u003C\u002Fcode>, \u003Ccode>s3\u003C\u002Fcode>, \u003Ccode>gcs\u003C\u002Fcode>)\u003C\u002Fli>\n\u003Cli>Example: If you have \u003Ccode>STORAGE_LOCATIONS=\"local,s3\"\u003C\u002Fcode>, you can specify \u003Ccode>s3\u003C\u002Fcode> here\u003C\u002Fli>\n\u003C\u002Ful>\n\u003C\u002Fli>\n\u003Cli>\n\u003Cp>\u003Cstrong>Thread Count\u003C\u002Fstrong> (optional, default: \u003Ccode>1\u003C\u002Fcode>): Number of CPU threads to use for encoding\u003C\u002Fp>\n\u003Cul>\n\u003Cli>\u003Ccode>1\u003C\u002Fcode> = Single-threaded encoding (default, most compatible)\u003C\u002Fli>\n\u003Cli>\u003Ccode>2+\u003C\u002Fcode> = Use specific number of threads (e.g., \u003Ccode>4\u003C\u002Fcode> for 4 threads)\u003C\u002Fli>\n\u003Cli>\u003Ccode>0\u003C\u002Fcode> = Use all available CPU cores (fastest, but may impact system performance)\u003C\u002Fli>\n\u003Cli>Note: More threads = faster encoding, but higher CPU usage\u003C\u002Fli>\n\u003C\u002Ful>\n\u003C\u002Fli>\n\u003Cli>\n\u003Cp>\u003Cstrong>Process Priority\u003C\u002Fstrong> (optional, default: \u003Ccode>19\u003C\u002Fcode>): CPU priority (nice value) for transcoding process\u003C\u002Fp>\n\u003Cul>\n\u003Cli>Range: 0 (default\u002Fhighest priority) to 19 (lowest priority)\u003C\u002Fli>\n\u003Cli>Lower values = higher CPU priority (may impact system performance)\u003C\u002Fli>\n\u003Cli>Higher values = lower CPU priority (better for background processing)\u003C\u002Fli>\n\u003Cli>Recommended: Use \u003Ccode>19\u003C\u002Fcode> when transcoding might impact system performance\u003C\u002Fli>\n\u003Cli>\u003Cstrong>Note\u003C\u002Fstrong>: Only works on Unix-like systems (Linux, macOS). On Windows, this setting is ignored with a warning.\u003C\u002Fli>\n\u003C\u002Ful>\n\u003C\u002Fli>\n\u003Cli>\n\u003Cp>\u003Cstrong>Playlist Reference Type\u003C\u002Fstrong> (optional, default: \u003Ccode>id\u003C\u002Fcode>): How playlists should reference segments\u003C\u002Fp>\n\u003Cul>\n\u003Cli>\u003Cstrong>\u003Ccode>id\u003C\u002Fcode>\u003C\u002Fstrong> (default): Uses Directus file IDs - playlists reference segments as \u003Ccode>\u002Fassets\u002F:file_id\u003C\u002Fcode> to run against \u002Fassets endpoint\u003C\u002Fli>\n\u003Cli>\u003Cstrong>\u003Ccode>filename_disk\u003C\u002Fcode>\u003C\u002Fstrong>: Uses original filenames - playlists reference segments by filename (useful for custom streaming servers)\u003C\u002Fli>\n\u003C\u002Ful>\n\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Ch3>Example Flow Configuration (Collection Item Trigger)\u003C\u002Fh3>\n\u003Cp>\u003Cstrong>Trigger\u003C\u002Fstrong>: Manual trigger\u003C\u002Fp>\n\u003Cul>\n\u003Cli>\u003Cstrong>Collections\u003C\u002Fstrong>: \u003Ccode>your_collection\u003C\u002Fcode>\u003C\u002Fli>\n\u003Cli>\u003Cstrong>Asynchronous\u003C\u002Fstrong>: \u003Ccode>enabled\u003C\u002Fcode>\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Cp>\u003Cstrong>Read Data\u003C\u002Fstrong>: Read \u003Ccode>your_collection\u003C\u002Fcode> with video field query:\u003C\u002Fp>\n\u003Cul>\n\u003Cli>\u003Cstrong>IDs\u003C\u002Fstrong>: \u003Ccode>{{$trigger.body.keys[0]}}\u003C\u002Fcode>\u003C\u002Fli>\n\u003Cli>\u003Cstrong>Query\u003C\u002Fstrong>:\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Cpre>\u003Ccode class=\"language-json\">{\n    \"fields\": \"*,video.*\"\n}\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>\u003Cstrong>Transcode Video Operation\u003C\u002Fstrong>:\u003C\u002Fp>\n\u003Cul>\n\u003Cli>\u003Cstrong>File\u003C\u002Fstrong>: \u003Ccode>{{ $last.video }}\u003C\u002Fcode>\u003C\u002Fli>\n\u003Cli>\u003Cstrong>Folder\u003C\u002Fstrong>: \u003Ccode>{{ $last.video.folder }}\u003C\u002Fcode> (or select\u002Fcreate via folder picker)\u003C\u002Fli>\n\u003Cli>\u003Cstrong>Quality Levels\u003C\u002Fstrong>: \u003Ccode>[\"240p\", \"480p\", \"720p\", \"1080p\"]\u003C\u002Fcode>\u003C\u002Fli>\n\u003Cli>\u003Cstrong>Storage Adapter\u003C\u002Fstrong>: \u003Ccode>Same as Source File\u003C\u002Fcode> (or \u003Ccode>Environment Configuration (First One)\u003C\u002Fcode>, or \u003Ccode>Other\u003C\u002Fcode> with custom storage)\u003C\u002Fli>\n\u003Cli>\u003Cstrong>Thread Count\u003C\u002Fstrong>: \u003Ccode>1\u003C\u002Fcode> (or \u003Ccode>0\u003C\u002Fcode> for all cores, \u003Ccode>4\u003C\u002Fcode> for 4 threads, etc.)\u003C\u002Fli>\n\u003Cli>\u003Cstrong>Process Priority\u003C\u002Fstrong>: \u003Ccode>19\u003C\u002Fcode> (or \u003Ccode>0\u003C\u002Fcode> for higher priority)\u003C\u002Fli>\n\u003Cli>\u003Cstrong>Playlist Reference Type\u003C\u002Fstrong>: \u003Ccode>id\u003C\u002Fcode>\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Cp>\u003Cstrong>Update Data\u003C\u002Fstrong>: Update \u003Ccode>your_collection\u003C\u002Fcode> with payload:\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-json\">{\n    \"stream_link\": \"\u002Fassets\u002F{{$last.master.id}}\",\n    \"image\": \"{{$last.metadata.thumbnail}}\"\n}\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cimg alt=\"screenshot_flow_collection\" src=\"https:\u002F\u002Fraw.githubusercontent.com\u002Fdomdus\u002Fdirectus-extension-transcode-video-operation\u002Fmain\u002Fdocs\u002Fscreenshot_flow_collection.png\">\n\u003Cp>\u003Cem>Collection Flow Example (sets stream link field in directus file)\u003C\u002Fem>\u003C\u002Fp>\n\u003Ch2>Output Structure\u003C\u002Fh2>\n\u003Cp>The operation creates the following files in the specified virtual folder:\u003C\u002Fp>\n\u003Cimg alt=\"screenshot_file_lib\" src=\"https:\u002F\u002Fraw.githubusercontent.com\u002Fdomdus\u002Fdirectus-extension-transcode-video-operation\u002Fmain\u002Fdocs\u002Fscreenshot_file_lib.png\">\n\u003Cpre>\u003Ccode>folder\u002F\n  └── video-filename\u002F\n      ├── video-filename_240p.m3u8          # 240p quality playlist\n      ├── video-filename_240p_000.ts        # 240p segments\n      ├── video-filename_480p.m3u8          # 480p quality playlist\n      ├── video-filename_720p.m3u8          # 720p quality playlist\n      ├── video-filename_1080p.m3u8         # 1080p quality playlist\n      ├── video-filename_playlist.m3u8      # Master playlist (references all qualities)\n      ├── video-filename.key                # AES-128 binary encryption key\n      └── video-filename_thumb.jpg          # Thumbnail image\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Ch3>Operation Response\u003C\u002Fh3>\n\u003Cp>The operation returns a JSON object with:\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-json\">{\n  \"master\": {\n    \"id\": \"file-uuid\",\n    \"filename_disk\": \"video-filename_playlist.m3u8\"\n  },\n  \"metadata\": {\n    \"availableQualities\": [240, 480, 720, 1080],\n    \"dimensions\": {\n      \"width\": 1920,\n      \"height\": 1080,\n      \"isVertical\": false\n    },\n    \"duration\": 125000,\n    \"thumbnail\": \"thumbnail-file-uuid\"\n  },\n  \"files\": [\n    {\n      \"filename_disk\": \"video-filename_240p.m3u8\",\n      \"id\": \"file-uuid-1\"\n    },\n    {\n      \"filename_disk\": \"video-filename_thumb.jpg\",\n      \"id\": \"file-uuid-2\"\n    }\n    \u002F\u002F ... more files\n  ]\n}\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Ch2>How It Works\u003C\u002Fh2>\n\u003Col>\n\u003Cli>\u003Cstrong>File Input Processing\u003C\u002Fstrong>:\n\u003Cul>\n\u003Cli>If file is a UUID string, fetches the full file object from Directus\u003C\u002Fli>\n\u003Cli>If file is already a file object, uses it directly\u003C\u002Fli>\n\u003C\u002Ful>\n\u003C\u002Fli>\n\u003Cli>\u003Cstrong>Source File Handling\u003C\u002Fstrong>:\n\u003Cul>\n\u003Cli>\u003Cstrong>Local Storage\u003C\u002Fstrong>: Uses file directly from disk\u003C\u002Fli>\n\u003Cli>\u003Cstrong>Cloud Storage\u003C\u002Fstrong>: Downloads source file to temporary location for processing\u003C\u002Fli>\n\u003C\u002Ful>\n\u003C\u002Fli>\n\u003Cli>\u003Cstrong>Storage Configuration\u003C\u002Fstrong>: Determines target storage location based on user selection\n\u003Cul>\n\u003Cli>Resolves storage driver (local vs. cloud) from environment configuration\u003C\u002Fli>\n\u003C\u002Ful>\n\u003C\u002Fli>\n\u003Cli>\u003Cstrong>File Validation\u003C\u002Fstrong>: Checks that the input file exists and is accessible\u003C\u002Fli>\n\u003Cli>\u003Cstrong>Metadata Extraction\u003C\u002Fstrong>: Uses \u003Ccode>ffprobe\u003C\u002Fcode> to get video dimensions, duration, and bit depth\u003C\u002Fli>\n\u003Cli>\u003Cstrong>Key Generation\u003C\u002Fstrong>: Generates a unique 16-byte random key for AES-128 encryption\u003C\u002Fli>\n\u003Cli>\u003Cstrong>Quality Filtering\u003C\u002Fstrong>: Filters out quality levels that would require upscaling\u003C\u002Fli>\n\u003Cli>\u003Cstrong>Bit Depth Detection\u003C\u002Fstrong>: Detects 10-bit videos and adds pixel format conversion\u003C\u002Fli>\n\u003Cli>\u003Cstrong>Transcoding\u003C\u002Fstrong>: Uses \u003Ccode>ffmpeg\u003C\u002Fcode> to transcode each quality level sequentially\n\u003Cul>\n\u003Cli>Applies AES-128 encryption using the generated key and a temporary \u003Ccode>.keyinfo\u003C\u002Fcode> file\u003C\u002Fli>\n\u003Cli>Creates HLS segments (\u003Ccode>.ts\u003C\u002Fcode> files) and quality playlists (\u003Ccode>.m3u8\u003C\u002Fcode>)\u003C\u002Fli>\n\u003Cli>Uses H.264 codec with optimized settings for each quality\u003C\u002Fli>\n\u003Cli>Applies process priority (nice value) on Unix-like systems\u003C\u002Fli>\n\u003C\u002Ful>\n\u003C\u002Fli>\n\u003Cli>\u003Cstrong>Master Playlist\u003C\u002Fstrong>: Generates master playlist with bandwidth and resolution metadata\u003C\u002Fli>\n\u003Cli>\u003Cstrong>Thumbnail Extraction\u003C\u002Fstrong>: Extracts thumbnail at 1 second mark (if not already exists)\u003C\u002Fli>\n\u003Cli>\u003Cstrong>Folder Creation\u003C\u002Fstrong>: Creates Directus virtual folder structure for organization\u003C\u002Fli>\n\u003Cli>\u003Cstrong>File Upload\u003C\u002Fstrong>:\n\u003Cul>\n\u003Cli>Checks for existing files to prevent duplicates\u003C\u002Fli>\n\u003Cli>For cloud storage: Uploads files and cleans up local copies\u003C\u002Fli>\n\u003Cli>For local storage: Files remain on disk\u003C\u002Fli>\n\u003C\u002Ful>\n\u003C\u002Fli>\n\u003Cli>\u003Cstrong>Cleanup\u003C\u002Fstrong>:\n\u003Cul>\n\u003Cli>Removes temporary downloaded source file (if from cloud storage)\u003C\u002Fli>\n\u003Cli>Removes local transcoded files (if target storage is cloud)\u003C\u002Fli>\n\u003C\u002Ful>\n\u003C\u002Fli>\n\u003C\u002Fol>\n\u003Ch2>Technical Details\u003C\u002Fh2>\n\u003Ch3>Encoding Settings\u003C\u002Fh3>\n\u003Cul>\n\u003Cli>\u003Cstrong>Codec\u003C\u002Fstrong>: H.264 (libx264)\u003C\u002Fli>\n\u003Cli>\u003Cstrong>Profile\u003C\u002Fstrong>: Main (maximum compatibility)\u003C\u002Fli>\n\u003Cli>\u003Cstrong>Audio\u003C\u002Fstrong>: AAC, 48kHz\u003C\u002Fli>\n\u003Cli>\u003Cstrong>Segment Duration\u003C\u002Fstrong>: 4 seconds\u003C\u002Fli>\n\u003Cli>\u003Cstrong>Playlist Type\u003C\u002Fstrong>: VOD (Video on Demand)\u003C\u002Fli>\n\u003Cli>\u003Cstrong>CRF\u003C\u002Fstrong>: 20 (constant rate factor for quality)\u003C\u002Fli>\n\u003Cli>\u003Cstrong>Process Priority\u003C\u002Fstrong>: Configurable nice value (0-19) for CPU priority control\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Ch3>Storage Handling\u003C\u002Fh3>\n\u003Cul>\n\u003Cli>\u003Cstrong>Local Storage\u003C\u002Fstrong>: Files are created directly on disk and registered in Directus\u003C\u002Fli>\n\u003Cli>\u003Cstrong>Cloud Storage\u003C\u002Fstrong>:\n\u003Cul>\n\u003Cli>Source files are downloaded via HTTP to temporary location for FFmpeg processing\u003C\u002Fli>\n\u003Cli>Transcoded files are uploaded to cloud storage via Directus FilesService\u003C\u002Fli>\n\u003Cli>Temporary local files are automatically cleaned up after upload\u003C\u002Fli>\n\u003C\u002Ful>\n\u003C\u002Fli>\n\u003Cli>\u003Cstrong>Storage Detection\u003C\u002Fstrong>: Automatically detects storage driver type from environment configuration\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Ch3>Quality Bitrates\u003C\u002Fh3>\n\u003Cul>\n\u003Cli>\u003Cstrong>240p\u003C\u002Fstrong>: 400 kbps video, 64 kbps audio\u003C\u002Fli>\n\u003Cli>\u003Cstrong>480p\u003C\u002Fstrong>: 1400 kbps video, 128 kbps audio\u003C\u002Fli>\n\u003Cli>\u003Cstrong>720p\u003C\u002Fstrong>: 2800 kbps video, 128 kbps audio\u003C\u002Fli>\n\u003Cli>\u003Cstrong>1080p\u003C\u002Fstrong>: 5000 kbps video, 192 kbps audio\u003C\u002Fli>\n\u003Cli>\u003Cstrong>2160p (4K)\u003C\u002Fstrong>: 20000 kbps video, 192 kbps audio\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Ch3>Upscaling Prevention\u003C\u002Fh3>\n\u003Cp>The operation automatically detects the source video resolution and only transcodes quality levels that are equal to or lower than the source. For example:\u003C\u002Fp>\n\u003Cul>\n\u003Cli>\u003Cstrong>1080p source\u003C\u002Fstrong>: Only transcodes 240p, 480p, 720p, 1080p (skips 4K)\u003C\u002Fli>\n\u003Cli>\u003Cstrong>720p source\u003C\u002Fstrong>: Only transcodes 240p, 480p, 720p (skips 1080p and 4K)\u003C\u002Fli>\n\u003Cli>\u003Cstrong>4K source\u003C\u002Fstrong>: Transcodes all available quality levels\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Ch2>Environment Variables\u003C\u002Fh2>\n\u003Cp>The operation requires several standard Directus environment variables to be correctly configured:\u003C\u002Fp>\n\u003Cul>\n\u003Cli>\u003Ccode>STORAGE_LOCATIONS\u003C\u002Fcode>: Needed to resolve all available storage locations.\u003C\u002Fli>\n\u003Cli>\u003Ccode>STORAGE_[LOCATION]_DRIVER\u003C\u002Fcode>: Needed to distinguish between local and cloud storage.\u003C\u002Fli>\n\u003Cli>\u003Ccode>STORAGE_[LOCATION]_ROOT\u003C\u002Fcode>: Required for local storage path resolution.\u003C\u002Fli>\n\u003Cli>\u003Ccode>PUBLIC_URL\u003C\u002Fcode>: Used to construct the internal download URL when source files are on cloud storage.\u003C\u002Fli>\n\u003Cli>\u003Ccode>HOST\u003C\u002Fcode> and \u003Ccode>PORT\u003C\u002Fcode>: Used as fallbacks if \u003Ccode>PUBLIC_URL\u003C\u002Fcode> is not defined.\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Ch2>Security Considerations\u003C\u002Fh2>\n\u003Cblockquote>\n\u003Cp>[!IMPORTANT]\nThe HLS AES-128 encryption key is stored in the same Directus folder as the video segments by default. To restrict unauthorized access to the encryption key, you should either:\u003C\u002Fp>\n\u003Col>\n\u003Cli>Set folder permissions in Directus to restrict visibility.\u003C\u002Fli>\n\u003Cli>Use a custom \u003Ccode>Key Base URL\u003C\u002Fcode> to point to a secure key server.\u003C\u002Fli>\n\u003Cli>Use a storage adapter that supports signed URLs or custom access control.\u003C\u002Fli>\n\u003C\u002Fol>\n\u003C\u002Fblockquote>\n\u003Ch2>Integration with Streaming Video Player\u003C\u002Fh2>\n\u003Cp>This operation works seamlessly with the \u003Ca href=\"https:\u002F\u002Fgithub.com\u002Fdomdus\u002Fdirectus-extension-streaming-video-player\">Streaming Video Player\u003C\u002Fa> extension (available in Directus Marketplace):\u003C\u002Fp>\n\u003Col>\n\u003Cli>Transcode videos using this operation\u003C\u002Fli>\n\u003Cli>Store the master playlist reference in a string field\u003C\u002Fli>\n\u003Cli>Use the Streaming Video Player interface to play the HLS stream\u003C\u002Fli>\n\u003C\u002Fol>\n\u003Ch2>License\u003C\u002Fh2>\n\u003Cp>MIT\u003C\u002Fp>\n","https:\u002F\u002Fraw.githubusercontent.com\u002Fdomdus\u002Fdirectus-extension-transcode-video-operation\u002Fmain\u002Fdocs\u002Fscreenshot_operation.png",[309,311,312,313],"https:\u002F\u002Fraw.githubusercontent.com\u002Fdomdus\u002Fdirectus-extension-transcode-video-operation\u002Fmain\u002Fdocs\u002Fscreenshot_flow_upload.png","https:\u002F\u002Fraw.githubusercontent.com\u002Fdomdus\u002Fdirectus-extension-transcode-video-operation\u002Fmain\u002Fdocs\u002Fscreenshot_flow_collection.png","https:\u002F\u002Fraw.githubusercontent.com\u002Fdomdus\u002Fdirectus-extension-transcode-video-operation\u002Fmain\u002Fdocs\u002Fscreenshot_file_lib.png",325,600]