222 lines
8.1 KiB
Python
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) |