youtube-automation/microsite/app.py

222 lines
8.1 KiB
Python

#!/usr/bin/env python3
"""
YouTube to Directus Microsite
Simple Flask app to add YouTube videos to Directus media_items
"""
import sys
import os
import re
import json
import logging
from datetime import datetime
from urllib.parse import urlparse, parse_qs
from flask import Flask, render_template, request, jsonify, redirect, url_for
from flask_cors import CORS
# Add parent directories to path for imports
import os
parent_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
src_dir = os.path.join(parent_dir, 'src')
sys.path.insert(0, parent_dir)
sys.path.insert(0, src_dir)
from config import DIRECTUS_TOKEN, DIRECTUS_URL
from directus_client import DirectusClient
from youtube_processor import YouTubeProcessor
from youtube_metadata import YouTubeMetadataExtractor
app = Flask(__name__)
app.secret_key = 'youtube-directus-microsite-secret-key'
# Enable CORS for all routes
CORS(app, origins='*')
# Configure logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
# Initialize services
directus_client = DirectusClient()
youtube_processor = YouTubeProcessor()
metadata_extractor = YouTubeMetadataExtractor()
def get_smart_video_info(url):
"""Get comprehensive video information using smart metadata extraction"""
try:
metadata = metadata_extractor.get_video_metadata(url)
return metadata
except Exception as e:
logger.error(f"Error extracting video metadata: {e}")
return None
@app.route('/')
def index():
"""Main page with YouTube URL form"""
return render_template('index.html')
@app.route('/add_video', methods=['POST'])
def add_video():
"""Process YouTube URL and add to Directus with smart metadata extraction"""
try:
youtube_url = request.form.get('youtube_url', '').strip()
custom_title = request.form.get('custom_title', '').strip()
if not youtube_url:
return jsonify({'error': 'YouTube URL is required'}), 400
# Extract comprehensive video metadata
logger.info(f"Extracting metadata for URL: {youtube_url}")
video_info = get_smart_video_info(youtube_url)
if not video_info:
return jsonify({'error': 'Invalid YouTube URL or failed to extract metadata'}), 400
# Use custom title if provided, otherwise use extracted title
if custom_title:
video_info['title'] = custom_title
logger.info(f"Processing video: {video_info['video_id']} - {video_info['title']}")
# Parse upload date to proper format if available
published_date = None
if video_info.get('upload_date'):
try:
# Try to parse ISO format date
from dateutil import parser
parsed_date = parser.parse(video_info['upload_date'])
published_date = parsed_date.strftime('%Y-%m-%d')
except:
published_date = None
# Create metadata JSON with all extra YouTube data
metadata_json = {
'duration': video_info.get('duration'),
'duration_formatted': video_info.get('duration_formatted'),
'view_count': video_info.get('view_count'),
'view_count_formatted': video_info.get('view_count_formatted'),
'like_count': video_info.get('like_count'),
'like_count_formatted': video_info.get('like_count_formatted'),
'comment_count': video_info.get('comment_count'),
'comment_count_formatted': video_info.get('comment_count_formatted'),
'channel_id': video_info.get('channel_id'),
'category': video_info.get('category'),
'keywords': video_info.get('keywords', []),
'is_live': video_info.get('is_live', False),
'is_family_safe': video_info.get('is_family_safe', True),
'original_upload_date': video_info.get('upload_date'),
'last_scraped': datetime.now().isoformat()
}
# Create media item data with correct field mappings
media_item_data = {
'title': video_info['title'],
'url': video_info['canonical_url'],
'type': video_info['type'],
'description': video_info.get('description', ''),
'external_id': video_info['video_id'], # Store YouTube video ID
'author': video_info.get('channel_name', ''), # Channel name in author field
'source_platform': 'YouTube', # Always YouTube for this microsite
'metadata': metadata_json, # All extra data in JSON field
'status': 'published'
}
# Add published date if we successfully parsed it
if published_date:
media_item_data['published_date'] = published_date
# Add to Directus
item_id = directus_client.create_media_item(media_item_data)
if item_id:
logger.info(f"Successfully created media item: {item_id}")
# Try to download and add thumbnail immediately
thumbnail_success = False
try:
thumbnail_data, filename = youtube_processor.download_best_thumbnail(video_info['video_id'])
if thumbnail_data and filename:
file_id = directus_client.upload_file(
thumbnail_data,
filename,
title=f"YouTube Thumbnail - {video_info['video_id']}"
)
if file_id:
thumbnail_success = directus_client.update_media_item_thumbnail(item_id, file_id)
except Exception as e:
logger.warning(f"Failed to add thumbnail immediately: {e}")
# Prepare response with rich metadata
response_data = {
'success': True,
'item_id': item_id,
'video_id': video_info['video_id'],
'title': video_info['title'],
'description': video_info.get('description', '')[:200] + ('...' if len(video_info.get('description', '')) > 200 else ''),
'channel_name': video_info.get('channel_name', ''),
'duration_formatted': video_info.get('duration_formatted', ''),
'view_count_formatted': video_info.get('view_count_formatted', ''),
'thumbnail_added': thumbnail_success,
'message': f"Successfully added: {video_info['title']}"
}
return jsonify(response_data)
else:
return jsonify({'error': 'Failed to create media item in Directus'}), 500
except Exception as e:
logger.error(f"Error processing video: {e}")
return jsonify({'error': f'Processing error: {str(e)}'}), 500
@app.route('/preview')
def preview():
"""Preview YouTube video information before adding"""
youtube_url = request.args.get('url', '').strip()
if not youtube_url:
return jsonify({'error': 'URL parameter required'}), 400
video_info = get_smart_video_info(youtube_url)
if not video_info:
return jsonify({'error': 'Invalid YouTube URL or failed to extract metadata'}), 400
return jsonify({
'success': True,
'video_info': video_info
})
@app.route('/status')
def status():
"""Check service status"""
try:
# Test Directus connection
test_items = directus_client.get_unprocessed_youtube_items(limit=1)
directus_status = 'connected'
except Exception as e:
directus_status = f'error: {str(e)}'
return jsonify({
'service': 'YouTube to Directus Microsite',
'status': 'running',
'directus': directus_status,
'directus_url': DIRECTUS_URL,
'timestamp': datetime.now().isoformat()
})
if __name__ == '__main__':
print("🎬 YouTube to Directus Microsite")
print("================================")
print(f"Directus URL: {DIRECTUS_URL}")
print(f"Has Token: {'Yes' if DIRECTUS_TOKEN else 'No'}")
print("")
print("Starting Flask server on http://localhost:5001")
app.run(host='0.0.0.0', port=5001, debug=True)