# Directus Flow Setup Guide - YouTube Thumbnail Automation ## Overview This guide sets up a Directus Flow to automatically extract YouTube thumbnails when `media_items` are created or updated with `type` = `youtube_video` or `youtube`. ## Prerequisites 1. Access to your Directus admin at `https://enias.zeabur.app/admin` 2. Admin permissions to create flows and modify collections ## Step 1: Add youtube_thumbnail Field to media_items Collection 1. Go to **Settings** → **Data Model** → **media_items** 2. Click **Create Field** 3. Configure the field: - **Field Name**: `youtube_thumbnail` - **Type**: File - **Interface**: File (Image) - **Display Template**: Show thumbnail preview in layouts - **Required**: No - **Default**: null ## Step 2: Create the YouTube Automation Flow ### 2.1 Create New Flow 1. Go to **Settings** → **Flows** 2. Click **Create Flow** 3. Configure basic settings: - **Name**: "YouTube Thumbnail Auto-Population" - **Status**: Active - **Icon**: smart_display (or video icon) - **Color**: #FF0000 (YouTube red) - **Description**: "Automatically extract and populate YouTube thumbnails" ### 2.2 Configure Trigger 1. Click **Add Trigger** 2. Select **Event Hook** trigger 3. Configure trigger: - **Scope**: Items - **Actions**: Update, Create - **Collections**: media_items ### 2.3 Add Filter Condition In the trigger configuration, add a filter condition: ```json { "$and": [ { "$or": [ { "type": { "_eq": "youtube_video" } }, { "type": { "_eq": "youtube" } } ] }, { "url": { "_nnull": true } } ] } ``` ### 2.4 Create Operations #### Operation 1: Extract Video ID (Run Script) 1. Add **Run Script** operation 2. Name: "Extract YouTube Video ID" 3. Code: ```javascript // Extract YouTube video ID from URL function extractYouTubeId(url) { if (!url) return null; // Handle different YouTube URL formats const patterns = [ /(?:youtube\.com\/watch\?v=)([a-zA-Z0-9_-]{11})/, /(?:youtu\.be\/)([a-zA-Z0-9_-]{11})/, /(?:youtube\.com\/embed\/)([a-zA-Z0-9_-]{11})/, /(?:youtube\.com\/v\/)([a-zA-Z0-9_-]{11})/ ]; for (const pattern of patterns) { const match = url.match(pattern); if (match) return match[1]; } return null; } const videoId = extractYouTubeId($trigger.payload.url); if (!videoId) { throw new Error('Could not extract YouTube video ID from URL: ' + $trigger.payload.url); } // Generate thumbnail URLs with quality fallback const thumbnailUrls = [ `https://img.youtube.com/vi/${videoId}/maxresdefault.jpg`, `https://img.youtube.com/vi/${videoId}/hqdefault.jpg`, `https://img.youtube.com/vi/${videoId}/mqdefault.jpg`, `https://img.youtube.com/vi/${videoId}/default.jpg` ]; module.exports = { video_id: videoId, thumbnail_urls: thumbnailUrls }; ``` #### Operation 2: Download & Upload Thumbnail (Run Script) 1. Add **Run Script** operation 2. Name: "Download and Upload Thumbnail" 3. Code: ```javascript const axios = require('axios'); // Download thumbnail and upload to Directus async function downloadAndUploadThumbnail(thumbnailUrls, videoId) { // Try each thumbnail URL until we find one that works for (const url of thumbnailUrls) { try { // Download thumbnail const response = await axios.get(url, { responseType: 'arraybuffer', timeout: 10000, headers: { 'User-Agent': 'Mozilla/5.0 (compatible; DirectusBot/1.0)' } }); if (response.status === 200 && response.data.length > 1000) { // Ensure it's a valid image // Upload to Directus files collection const fileData = { title: `YouTube Thumbnail - ${videoId}`, filename_download: `youtube_${videoId}.jpg`, type: 'image/jpeg', storage: 'local', data: Buffer.from(response.data).toString('base64') }; const uploadResult = await $directus.files.createOne(fileData); return uploadResult.id; } } catch (error) { console.log(`Failed to download from ${url}:`, error.message); continue; // Try next URL } } throw new Error('Failed to download thumbnail from any source'); } const fileId = await downloadAndUploadThumbnail( $last.thumbnail_urls, $last.video_id ); module.exports = { thumbnail_file_id: fileId }; ``` #### Operation 3: Update Media Item (Update Data) 1. Add **Update Data** operation 2. Configure: - **Collection**: media_items - **Key**: `{{$trigger.key}}` - **Payload**: ```json { "youtube_thumbnail": "{{$last.thumbnail_file_id}}" } ``` ## Step 3: Test the Flow ### Test Data Create or update a `media_items` record with: - `type`: "youtube_video" - `url`: "https://www.youtube.com/watch?v=dQw4w9WgXcQ" ### Expected Result The Flow should automatically: 1. Extract video ID: `dQw4w9WgXcQ` 2. Download the best available thumbnail 3. Upload it to Directus files 4. Update the `youtube_thumbnail` field with the file reference ## Step 4: Verify Integration 1. Check **Activity** tab for flow execution logs 2. Verify `youtube_thumbnail` field is populated 3. Confirm thumbnail displays in collection layouts 4. Test with different YouTube URL formats ## Troubleshooting ### Common Issues 1. **Flow not triggering**: Check filter conditions and collection name 2. **Thumbnail download fails**: YouTube may block requests - add retry logic 3. **File upload fails**: Check Directus storage configuration 4. **Permission errors**: Ensure flow has file creation permissions ### Debug Tips 1. Enable flow logging in Settings 2. Check Activity logs for error details 3. Test operations individually 4. Verify field permissions and data types ## Alternative Approach: HTTP Request Operation If the Run Script approach doesn't work, you can use HTTP Request operations: 1. **HTTP Request** to download thumbnail 2. **HTTP Request** to upload to Directus files API 3. **Update Data** to link the file This approach uses the REST API instead of the JavaScript SDK.