youtube-automation/DIRECTUS_FLOW_SETUP.md

6.0 KiB

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 SettingsData Modelmedia_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 SettingsFlows
  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:

{
  "$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:
// 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:
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:
      {
        "youtube_thumbnail": "{{$last.thumbnail_file_id}}"
      }
      

Step 3: Test the Flow

Test Data

Create or update a media_items record with:

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.