[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"github-stars":3,"marketplace-site-banners":110,"directus-extension-transcode-video-hls-secure":116},{"id":4,"node_id":5,"name":6,"full_name":7,"private":8,"owner":9,"html_url":27,"description":28,"fork":8,"url":29,"forks_url":30,"keys_url":31,"collaborators_url":32,"teams_url":33,"hooks_url":34,"issue_events_url":35,"events_url":36,"assignees_url":37,"branches_url":38,"tags_url":39,"blobs_url":40,"git_tags_url":41,"git_refs_url":42,"trees_url":43,"statuses_url":44,"languages_url":45,"stargazers_url":46,"contributors_url":47,"subscribers_url":48,"subscription_url":49,"commits_url":50,"git_commits_url":51,"comments_url":52,"issue_comment_url":53,"contents_url":54,"compare_url":55,"merges_url":56,"archive_url":57,"downloads_url":58,"issues_url":59,"pulls_url":60,"milestones_url":61,"notifications_url":62,"labels_url":63,"releases_url":64,"deployments_url":65,"created_at":66,"updated_at":67,"pushed_at":68,"git_url":69,"ssh_url":70,"clone_url":71,"svn_url":27,"homepage":72,"size":73,"stargazers_count":74,"watchers_count":74,"language":75,"has_issues":76,"has_projects":76,"has_downloads":76,"has_wiki":8,"has_pages":8,"has_discussions":76,"forks_count":77,"mirror_url":78,"archived":8,"disabled":8,"open_issues_count":79,"license":80,"allow_forking":76,"is_template":8,"web_commit_signoff_required":8,"has_pull_requests":76,"pull_request_creation_policy":85,"topics":86,"visibility":26,"forks":77,"open_issues":79,"watchers":74,"default_branch":106,"temp_clone_token":78,"custom_properties":107,"organization":108,"network_count":77,"subscribers_count":109},7122594,"MDEwOlJlcG9zaXRvcnk3MTIyNTk0","directus","directus\u002Fdirectus",false,{"login":6,"id":10,"node_id":11,"avatar_url":12,"gravatar_id":13,"url":14,"html_url":15,"followers_url":16,"following_url":17,"gists_url":18,"starred_url":19,"subscriptions_url":20,"organizations_url":21,"repos_url":22,"events_url":23,"received_events_url":24,"type":25,"user_view_type":26,"site_admin":8},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-06-01T20:57:39Z","2026-06-01T21:41:46Z","git:\u002F\u002Fgithub.com\u002Fdirectus\u002Fdirectus.git","git@github.com:directus\u002Fdirectus.git","https:\u002F\u002Fgithub.com\u002Fdirectus\u002Fdirectus.git","https:\u002F\u002Fdirectus.io",442985,36031,"TypeScript",true,4790,null,402,{"key":81,"name":82,"spdx_id":83,"url":78,"node_id":84},"other","Other","NOASSERTION","MDc6TGljZW5zZTA=","all",[87,88,89,90,91,92,6,93,94,95,96,97,98,99,100,101,102,103,104,105],"api","app","cms","composable","data-visualization","database","graphql","headless-cms","javascript","mariadb","mssql","mysql","no-code","node","postgresql","sql","sqlite","typescript","vue","main",{},{"login":6,"id":10,"node_id":11,"avatar_url":12,"gravatar_id":13,"url":14,"html_url":15,"followers_url":16,"following_url":17,"gists_url":18,"starred_url":19,"subscriptions_url":20,"organizations_url":21,"repos_url":22,"events_url":23,"received_events_url":24,"type":25,"user_view_type":26,"site_admin":8},327,[111],{"id":112,"icon":113,"content":114,"link":115,"include_paths":78,"exclude_paths":78},"812bf73d-ebfb-4246-9538-937a09a0c795","connected_tv","Learn more about our native MCP","https:\u002F\u002Fdirectus.io\u002Fmcp",{"name":117,"description":118,"verified":8,"id":119,"readme":120,"type":121,"last_updated":122,"host_version":123,"downloads":124,"total_downloads":130,"sandbox":8,"license":191,"publisher":192,"versions":195,"formatted_name":231,"formatted_readme":232,"formatted_description":118,"featured_image":233,"images":234,"repository_url":200,"recent_downloads_7_days":238,"recent_downloads_30_days":239},"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-05-29T14:05:07.456Z","^11.0.0",[125,128,131,133,135,137,139,141,143,145,147,149,151,153,155,157,159,161,164,166,168,170,172,174,177,179,181,183,186,188],{"date":126,"count":127},"2026-05-02",2,{"date":129,"count":130},"2026-05-03",0,{"date":132,"count":130},"2026-05-04",{"date":134,"count":130},"2026-05-05",{"date":136,"count":130},"2026-05-06",{"date":138,"count":130},"2026-05-07",{"date":140,"count":130},"2026-05-08",{"date":142,"count":130},"2026-05-09",{"date":144,"count":130},"2026-05-10",{"date":146,"count":130},"2026-05-11",{"date":148,"count":127},"2026-05-12",{"date":150,"count":127},"2026-05-13",{"date":152,"count":127},"2026-05-14",{"date":154,"count":130},"2026-05-15",{"date":156,"count":130},"2026-05-16",{"date":158,"count":130},"2026-05-17",{"date":160,"count":130},"2026-05-18",{"date":162,"count":163},"2026-05-19",1,{"date":165,"count":163},"2026-05-20",{"date":167,"count":130},"2026-05-21",{"date":169,"count":127},"2026-05-22",{"date":171,"count":130},"2026-05-23",{"date":173,"count":163},"2026-05-24",{"date":175,"count":176},"2026-05-25",3,{"date":178,"count":130},"2026-05-26",{"date":180,"count":163},"2026-05-27",{"date":182,"count":130},"2026-05-28",{"date":184,"count":185},"2026-05-29",267,{"date":187,"count":130},"2026-05-30",{"date":189,"count":190},"2026-05-31",4,"MIT",{"username":193,"verified":8,"id":194,"github_username":78,"github_name":78,"github_blog":78,"github_bio":78,"github_location":78,"github_company":78,"github_avatar_url":78},"ymys","043f57c7-3cc7-493c-98f4-c435b8370896",[196,208,216,224],{"version":197,"package":119,"publisher":194,"type":121,"host_version":123,"publish_date":198,"verified":8,"id":199,"url_repository":200,"url_homepage":201,"url_bugs":202,"file_count":203,"unpacked_size":204,"sandbox":8,"sandbox_requested_scopes":78,"license":191,"bundled":205,"maintainers":206},"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,[],[207],14503,{"version":209,"package":119,"publisher":194,"type":121,"host_version":123,"publish_date":210,"verified":8,"id":211,"url_repository":200,"url_homepage":201,"url_bugs":202,"file_count":203,"unpacked_size":212,"sandbox":8,"sandbox_requested_scopes":78,"license":191,"bundled":213,"maintainers":214},"3.4.0","2026-03-31T02:53:47.869Z","0e0c424d-6279-4536-821a-4722637220e1",988200,[],[215],14080,{"version":217,"package":119,"publisher":194,"type":121,"host_version":123,"publish_date":218,"verified":8,"id":219,"url_repository":200,"url_homepage":201,"url_bugs":202,"file_count":203,"unpacked_size":220,"sandbox":8,"sandbox_requested_scopes":78,"license":191,"bundled":221,"maintainers":222},"2.3.0","2026-03-31T01:35:42.750Z","15829531-3494-451c-8dd4-30560ba02076",46015,[],[223],14079,{"version":225,"package":119,"publisher":194,"type":121,"host_version":123,"publish_date":122,"verified":8,"id":226,"url_repository":200,"url_homepage":201,"url_bugs":202,"file_count":203,"unpacked_size":227,"sandbox":8,"sandbox_requested_scopes":78,"license":191,"bundled":228,"maintainers":229},"2.5.0","dbf3aca9-a752-42a6-bf61-e22f26a33c28",53009,[],[230],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",[233,235,236,237],"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",272,286]