Last active
April 18, 2025 06:28
-
-
Save obar1/e41740fa61f84b5e627deda6c23a212e to your computer and use it in GitHub Desktop.
Revisions
-
obar1 revised this gist
Apr 18, 2025 . 1 changed file with 57 additions and 5 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -31,9 +31,9 @@ def save_video_db(video_list): json.dump(video_list, f, indent=4) def download_youtube_video(url, output_path): """Download a YouTube video as MP4 and return video info including tags and subtitles.""" try: # Configure yt-dlp options for browser-compatible MP4 and subtitles ydl_opts = { 'format': 'bestvideo[vcodec~="^avc1"][ext=mp4]+bestaudio[acodec~="mp4a"]/best[ext=mp4]', # H.264 + AAC 'outtmpl': os.path.join(output_path, '%(title)s.%(ext)s'), @@ -42,6 +42,10 @@ def download_youtube_video(url, output_path): 'key': 'FFmpegVideoConvertor', 'preferedformat': 'mp4', # Ensure MP4 with H.264/AAC }], 'writesubtitles': True, # Download subtitles if available 'writeautomaticsub': True, # Download automatic subtitles 'subtitleslangs': ['en'], # Prefer English subtitles 'subtitlesformat': 'vtt', # WebVTT format for browser compatibility } # Download the video @@ -50,20 +54,37 @@ def download_youtube_video(url, output_path): video_title = info.get('title', 'Untitled Video') raw_filename = ydl.prepare_filename(info).split(os.sep)[-1] video_filename = sanitize_filename(raw_filename) # Get tags tags = info.get('tags', []) or [] if not tags: tags = ['No tags available'] # Get subtitles subtitles = None subtitle_file = os.path.join(output_path, f"{sanitize_filename(info.get('title', 'video'))}.en.vtt") if os.path.exists(subtitle_file): with open(subtitle_file, 'r', encoding='utf-8') as f: subtitles = f.read() else: subtitles = "No subtitles available" print(f"Downloading: {video_title}") print(f"Download completed! Video saved to {output_path}") return { 'title': video_title, 'filename': video_filename, 'path': os.path.join(output_path, video_filename), 'tags': tags, 'subtitles': subtitles } except Exception as e: print(f"An error occurred during download: {str(e)}") return None def generate_index_html(video_list): """Generate or update the index.html file with video list, players, tags, and subtitles.""" html_content = """ <!DOCTYPE html> <html lang="en"> @@ -72,6 +93,24 @@ def generate_index_html(video_list): <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>YouTube Video Downloads</title> <script src="https://cdn.tailwindcss.com"></script> <style> .subtitles { max-height: 200px; overflow-y: auto; background-color: #f8f8f8; padding: 1rem; border-radius: 0.5rem; white-space: pre-wrap; } .tag { display: inline-block; background-color: #e5e7eb; padding: 0.25rem 0.5rem; border-radius: 0.25rem; margin-right: 0.5rem; margin-bottom: 0.5rem; } </style> </head> <body class="bg-gray-100 font-sans"> <div class="container mx-auto p-4"> @@ -81,15 +120,28 @@ def generate_index_html(video_list): for video in video_list: escaped_title = html.escape(video['title']) video_path = os.path.join('videos', os.path.basename(video['filename'])).replace('\\', '/') tags = video.get('tags', ['No tags available']) subtitles = html.escape(video.get('subtitles', 'No subtitles available')) subtitle_track = os.path.join('videos', f"{sanitize_filename(video['title'])}.en.vtt").replace('\\', '/') # Generate tags HTML tags_html = ''.join(f'<span class="tag">{html.escape(tag)}</span>' for tag in tags) html_content += f""" <div class="bg-white p-4 rounded-lg shadow-md"> <h2 class="text-xl font-semibold mb-2">{escaped_title}</h2> <video controls class="w-full max-w-2xl mx-auto rounded" style="aspect-ratio: 16/9;"> <source src="{video_path}" type="video/mp4"> <track kind="subtitles" src="{subtitle_track}" srclang="en" label="English" default> Your browser does not support the video tag, or the video file may be inaccessible. Ensure the file exists at '{video_path}' and is in a compatible MP4 format (H.264/AAC). </video> <div class="mt-4"> <h3 class="text-lg font-medium mb-2">Tags</h3> <div class="mb-4">{tags_html}</div> <h3 class="text-lg font-medium mb-2">Subtitles</h3> <div class="subtitles">{subtitles}</div> </div> <a href="{video_path}" class="text-blue-500 hover:underline mt-2 inline-block" download>Download Video</a> </div> """ -
obar1 created this gist
Apr 18, 2025 .There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,153 @@ from yt_dlp import YoutubeDL import os import json import html import re import http.server import socketserver import threading # Directory to store videos VIDEO_DIR = "videos" # JSON file to track downloaded videos VIDEO_DB = "videos.json" # HTML file for the webpage INDEX_HTML = "index.html" def sanitize_filename(filename): """Remove or replace invalid characters for filenames.""" return re.sub(r'[<>:"/\\|?*]', '_', filename) def load_video_db(): """Load the video database from JSON file.""" if os.path.exists(VIDEO_DB): with open(VIDEO_DB, 'r') as f: return json.load(f) return [] def save_video_db(video_list): """Save the video database to JSON file.""" with open(VIDEO_DB, 'w') as f: json.dump(video_list, f, indent=4) def download_youtube_video(url, output_path): """Download a YouTube video as MP4 and return video info.""" try: # Configure yt-dlp options for browser-compatible MP4 ydl_opts = { 'format': 'bestvideo[vcodec~="^avc1"][ext=mp4]+bestaudio[acodec~="mp4a"]/best[ext=mp4]', # H.264 + AAC 'outtmpl': os.path.join(output_path, '%(title)s.%(ext)s'), 'merge_output_format': 'mp4', 'postprocessors': [{ 'key': 'FFmpegVideoConvertor', 'preferedformat': 'mp4', # Ensure MP4 with H.264/AAC }], } # Download the video with YoutubeDL(ydl_opts) as ydl: info = ydl.extract_info(url, download=True) video_title = info.get('title', 'Untitled Video') raw_filename = ydl.prepare_filename(info).split(os.sep)[-1] video_filename = sanitize_filename(raw_filename) print(f"Downloading: {video_title}") print(f"Download completed! Video saved to {output_path}") return { 'title': video_title, 'filename': video_filename, 'path': os.path.join(output_path, video_filename) } except Exception as e: print(f"An error occurred during download: {str(e)}") return None def generate_index_html(video_list): """Generate or update the index.html file with video list and players.""" html_content = """ <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>YouTube Video Downloads</title> <script src="https://cdn.tailwindcss.com"></script> </head> <body class="bg-gray-100 font-sans"> <div class="container mx-auto p-4"> <h1 class="text-3xl font-bold mb-6 text-center">Downloaded YouTube Videos</h1> <div class="grid gap-6"> """ for video in video_list: escaped_title = html.escape(video['title']) # Use relative path for HTML (videos/ is relative to index.html) video_path = os.path.join('videos', os.path.basename(video['filename'])).replace('\\', '/') html_content += f""" <div class="bg-white p-4 rounded-lg shadow-md"> <h2 class="text-xl font-semibold mb-2">{escaped_title}</h2> <video controls class="w-full max-w-2xl mx-auto rounded" style="aspect-ratio: 16/9;"> <source src="{video_path}" type="video/mp4"> Your browser does not support the video tag, or the video file may be inaccessible. Ensure the file exists at '{video_path}' and is in a compatible MP4 format (H.264/AAC). </video> <a href="{video_path}" class="text-blue-500 hover:underline mt-2 inline-block" download>Download Video</a> </div> """ html_content += """ </div> </div> </body> </html> """ with open(INDEX_HTML, 'w') as f: f.write(html_content) print(f"Updated {INDEX_HTML} with {len(video_list)} videos.") def start_local_server(port=8123): """Start a simple HTTP server to serve the webpage.""" class Handler(http.server.SimpleHTTPRequestHandler): def __init__(self, *args, **kwargs): super().__init__(*args, directory=os.getcwd(), **kwargs) with socketserver.TCPServer(("", port), Handler) as httpd: print(f"Serving at http://localhost:{port}/{INDEX_HTML}") print("Press Ctrl+C to stop the server.") httpd.serve_forever() if __name__ == "__main__": # Create video directory if it doesn't exist if not os.path.exists(VIDEO_DIR): os.makedirs(VIDEO_DIR) # Load existing video database video_list = load_video_db() # Get user input video_url = input("Enter YouTube video URL: ") if video_url: save_path = VIDEO_DIR # Download the video video_info = download_youtube_video(video_url, save_path) if video_info: # Check if video already exists in the database if not any(v['filename'] == video_info['filename'] for v in video_list): video_list.append(video_info) save_video_db(video_list) else: print(f"Video '{video_info['title']}' is already in the database.") # Generate or update the index.html generate_index_html(video_list) # Prompt to start a local server start_server = input("Would you like to start a local server to view the webpage? (y/n): ").lower() == 'y' if start_server: server_thread = threading.Thread(target=start_local_server, daemon=True) server_thread.start() input("Press Enter to stop the server...\n") else: print("Failed to download the video. Webpage not updated.")