Murad Jabir 1 рік тому
батько
коміт
08241108c6
1 змінених файлів з 146 додано та 19 видалено
  1. 146 19
      main.py

+ 146 - 19
main.py

@@ -108,23 +108,110 @@ def get_popular_videos(youtube, region_code):
     return popular_videos
 
 def download_video(video_id, output_path):
-    """Download a YouTube video prioritizing 4K non-AV1 formats, with fallback options."""
+    """Download a YouTube video with quality priority - downloads 4K VP9/AV1 if needed for conversion."""
     try:
+        # Quality-priority format selection:
+        # 1. First try: Apple TV compatible codecs at any resolution
+        # 2. Fallback: Download highest quality available (including 4K VP9/AV1 for conversion)
+        # 3. Last resort: Best available format
         command = [
             "yt-dlp",
-            "-f", "bv[height=2160][vcodec!*=av1]+ba / bv[height<=1080]+ba / best",
+            "-f", "(bv[vcodec~='^(avc|h264|hevc|h265)'] / bv[height>=2160] / bv[height>=1080] / best) + (ba[acodec~='^(aac|mp4a)'] / ba)",
             "--merge-output-format", "mp4",
             "--no-mtime",
             f"https://www.youtube.com/watch?v={video_id}",
             "-o", os.path.join(output_path, "%(title)s.%(ext)s")
         ]
         subprocess.run(command, check=True)
-        print(f"Successfully downloaded video {video_id}")
+        print(f"Successfully downloaded video {video_id} (Quality priority - will convert VP9/AV1 if needed)")
     except subprocess.CalledProcessError as e:
         print(f"Error downloading video {video_id}: {e}")
         return False
     return True
 
+def convert_to_apple_tv_compatible(input_file):
+    """Convert a video to Apple TV 4K compatible format, optimized by resolution."""
+    base_name = os.path.splitext(input_file)[0]
+    mp4_file = base_name + ".mp4"
+    
+    print(f"Converting {input_file} to Apple TV compatible format...")
+    
+    # Detect video properties to optimize conversion
+    try:
+        import json
+        probe_cmd = [
+            "ffprobe", "-v", "quiet", "-print_format", "json", 
+            "-show_format", "-show_streams", input_file
+        ]
+        probe_result = subprocess.run(probe_cmd, capture_output=True, text=True, check=True)
+        video_info = json.loads(probe_result.stdout)
+        
+        # Find video stream
+        video_stream = next((s for s in video_info['streams'] if s['codec_type'] == 'video'), None)
+        is_4k = video_stream and int(video_stream.get('height', 0)) >= 2160
+        is_high_res = video_stream and int(video_stream.get('height', 0)) >= 1080
+        
+        print(f"Video resolution: {video_stream.get('height', 'unknown')}p")
+        
+    except (subprocess.CalledProcessError, json.JSONDecodeError, Exception) as e:
+        print(f"Could not probe video properties: {e}, using default settings")
+        is_4k = False
+        is_high_res = True
+    
+    # Apple TV 4K optimized encoding based on source quality:
+    if is_4k:
+        # 4K content: Use H.265 for better compression at high resolution
+        codec_args = [
+            "-c:v", "libx265",          # H.265 for 4K (better compression)
+            "-preset", "medium",        # Better compression for 4K
+            "-crf", "20",               # Higher quality for 4K content
+            "-level", "5.1",            # Level 5.1 supports 4K
+            "-pix_fmt", "yuv420p",      # Ensure compatibility
+        ]
+        print("Using H.265 encoding for 4K content")
+    elif is_high_res:
+        # 1080p content: Use H.264 for maximum compatibility
+        codec_args = [
+            "-c:v", "libx264",          # H.264 for 1080p (max compatibility)
+            "-profile:v", "high",       # High profile for better quality
+            "-preset", "fast",          # Faster encoding for 1080p
+            "-crf", "21",               # Good quality for 1080p
+            "-level", "4.1",            # Level 4.1 supports 1080p at high bitrates
+        ]
+        print("Using H.264 encoding for 1080p content")
+    else:
+        # Lower resolution: Use H.264 with faster settings
+        codec_args = [
+            "-c:v", "libx264",          # H.264 for lower res
+            "-profile:v", "main",       # Main profile for lower res
+            "-preset", "fast",          # Fast encoding
+            "-crf", "23",               # Standard quality
+            "-level", "3.1",            # Level 3.1 for lower res
+        ]
+        print("Using H.264 encoding for lower resolution content")
+    
+    command = [
+        "ffmpeg", "-i", input_file,
+        *codec_args,
+        "-c:a", "aac",              # AAC audio codec
+        "-b:a", "192k",             # Higher audio bitrate for better quality
+        "-movflags", "+faststart",  # Optimize for streaming/quick playback
+        "-y",                       # Overwrite output file if exists
+        mp4_file
+    ]
+    
+    try:
+        print("Starting conversion... This may take a while for 4K content.")
+        subprocess.run(command, check=True)
+        print(f"Conversion completed: {mp4_file}")
+        if os.path.exists(mp4_file):
+            os.remove(input_file)
+            print(f"Original file deleted: {input_file}")
+        return True
+    except subprocess.CalledProcessError as e:
+        print(f"Conversion failed for {input_file}: {e}")
+        return False
+
 # Temporarily commented out video conversion functionality
 # def convert_to_mp4(input_file):
 #     """Convert a video to mp4 and delete the original file."""
@@ -226,14 +313,34 @@ def main():
                 print(f"Skipping video {video['video_id']} due to download error.")
                 continue  # Skip this video if download failed
             
-            # Temporarily comment out conversion, but keep it in the code for future use
-            # video_file = next(
-            #     (f for f in os.listdir(video_dir) if not f.endswith(".mp4")),
-            #     None
-            # )
-            # if video_file:
-            #     input_path = os.path.join(video_dir, video_file)
-            #     threading.Thread(target=convert_to_mp4, args=(input_path,)).start()
+            # Check if conversion is needed (VP9/AV1 codecs or non-MP4 files)
+            needs_conversion = False
+            conversion_file = None
+            
+            for file in os.listdir(video_dir):
+                if file.endswith(('.mp4', '.mkv', '.webm', '.m4v')):
+                    file_path = os.path.join(video_dir, file)
+                    try:
+                        # Quick check for problematic codecs
+                        probe_cmd = ["ffprobe", "-v", "quiet", "-select_streams", "v:0", 
+                                   "-show_entries", "stream=codec_name", "-of", "csv=p=0", file_path]
+                        result = subprocess.run(probe_cmd, capture_output=True, text=True, check=True)
+                        codec = result.stdout.strip().lower()
+                        
+                        if codec in ['vp9', 'av01', 'av1'] or not file.endswith('.mp4'):
+                            needs_conversion = True
+                            conversion_file = file_path
+                            print(f"Found {codec} codec - will convert for Apple TV compatibility")
+                            break
+                    except subprocess.CalledProcessError:
+                        # If we can't probe, assume it might need conversion if not MP4
+                        if not file.endswith('.mp4'):
+                            needs_conversion = True
+                            conversion_file = file_path
+                            break
+            
+            if needs_conversion and conversion_file:
+                threading.Thread(target=convert_to_apple_tv_compatible, args=(conversion_file,)).start()
             
             # Create the NFO file
             nfo_file_path = os.path.join(video_dir, "movie.nfo")
@@ -263,14 +370,34 @@ def main():
                 print(f"Skipping popular video {video['video_id']} due to download error.")
                 continue  # Skip this video if download failed
             
-            # Temporarily comment out conversion, but keep it in the code for future use
-            # video_file = next(
-            #     (f for f in os.listdir(video_dir) if not f.endswith(".mp4")),
-            #     None
-            # )
-            # if video_file:
-            #     input_path = os.path.join(video_dir, video_file)
-            #     threading.Thread(target=convert_to_mp4, args=(input_path,)).start()
+            # Check if conversion is needed (VP9/AV1 codecs or non-MP4 files)
+            needs_conversion = False
+            conversion_file = None
+            
+            for file in os.listdir(video_dir):
+                if file.endswith(('.mp4', '.mkv', '.webm', '.m4v')):
+                    file_path = os.path.join(video_dir, file)
+                    try:
+                        # Quick check for problematic codecs
+                        probe_cmd = ["ffprobe", "-v", "quiet", "-select_streams", "v:0", 
+                                   "-show_entries", "stream=codec_name", "-of", "csv=p=0", file_path]
+                        result = subprocess.run(probe_cmd, capture_output=True, text=True, check=True)
+                        codec = result.stdout.strip().lower()
+                        
+                        if codec in ['vp9', 'av01', 'av1'] or not file.endswith('.mp4'):
+                            needs_conversion = True
+                            conversion_file = file_path
+                            print(f"Found {codec} codec - will convert for Apple TV compatibility")
+                            break
+                    except subprocess.CalledProcessError:
+                        # If we can't probe, assume it might need conversion if not MP4
+                        if not file.endswith('.mp4'):
+                            needs_conversion = True
+                            conversion_file = file_path
+                            break
+            
+            if needs_conversion and conversion_file:
+                threading.Thread(target=convert_to_apple_tv_compatible, args=(conversion_file,)).start()
             
             # Create the NFO file
             nfo_file_path = os.path.join(video_dir, "movie.nfo")