Update yt-dlp option config for download best quality video with pc/phone support
This commit is contained in:
@@ -8,10 +8,144 @@ import asyncio
|
|||||||
import threading
|
import threading
|
||||||
import logging
|
import logging
|
||||||
import time
|
import time
|
||||||
|
import shutil
|
||||||
|
import json
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
async def fix_video_aspect_ratio(video_path: str) -> Optional[str]:
|
||||||
|
"""
|
||||||
|
Fix video aspect ratio metadata for mobile compatibility
|
||||||
|
|
||||||
|
This function ensures that video has correct aspect ratio metadata
|
||||||
|
so it displays correctly on mobile devices (not as square)
|
||||||
|
|
||||||
|
Args:
|
||||||
|
video_path: Path to video file
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Path to fixed video file (same file if fixed in place, or new file)
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
# Check if ffmpeg is available
|
||||||
|
if not shutil.which('ffmpeg'):
|
||||||
|
logger.warning("ffmpeg not found, skipping aspect ratio fix")
|
||||||
|
return None
|
||||||
|
|
||||||
|
# Check if ffprobe is available
|
||||||
|
if not shutil.which('ffprobe'):
|
||||||
|
logger.warning("ffprobe not found, skipping aspect ratio fix")
|
||||||
|
return None
|
||||||
|
|
||||||
|
video_file = Path(video_path)
|
||||||
|
if not video_file.exists():
|
||||||
|
logger.warning(f"Video file not found: {video_path}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
# Get video information to check aspect ratio
|
||||||
|
probe_cmd = [
|
||||||
|
'ffprobe', '-v', 'error',
|
||||||
|
'-select_streams', 'v:0',
|
||||||
|
'-show_entries', 'stream=width,height,display_aspect_ratio,sample_aspect_ratio',
|
||||||
|
'-of', 'json',
|
||||||
|
str(video_path)
|
||||||
|
]
|
||||||
|
|
||||||
|
proc = await asyncio.create_subprocess_exec(
|
||||||
|
*probe_cmd,
|
||||||
|
stdout=asyncio.subprocess.PIPE,
|
||||||
|
stderr=asyncio.subprocess.PIPE
|
||||||
|
)
|
||||||
|
stdout, stderr = await proc.communicate()
|
||||||
|
|
||||||
|
if proc.returncode != 0:
|
||||||
|
logger.warning(f"Failed to probe video: {stderr.decode()}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
try:
|
||||||
|
probe_data = json.loads(stdout.decode())
|
||||||
|
streams = probe_data.get('streams', [])
|
||||||
|
if not streams:
|
||||||
|
logger.warning("No video streams found")
|
||||||
|
return None
|
||||||
|
|
||||||
|
stream = streams[0]
|
||||||
|
width = stream.get('width')
|
||||||
|
height = stream.get('height')
|
||||||
|
dar = stream.get('display_aspect_ratio') # Display Aspect Ratio
|
||||||
|
sar = stream.get('sample_aspect_ratio') # Sample Aspect Ratio
|
||||||
|
|
||||||
|
if not width or not height:
|
||||||
|
logger.warning("Could not get video dimensions")
|
||||||
|
return None
|
||||||
|
|
||||||
|
# Calculate expected aspect ratio
|
||||||
|
expected_dar = f"{width}:{height}"
|
||||||
|
|
||||||
|
# If aspect ratio metadata is missing or incorrect, fix it
|
||||||
|
needs_fix = False
|
||||||
|
if not dar or dar == 'N/A':
|
||||||
|
needs_fix = True
|
||||||
|
logger.info(f"Video missing aspect ratio metadata (DAR), will fix to {expected_dar}")
|
||||||
|
elif dar != expected_dar:
|
||||||
|
# Check if the difference is significant
|
||||||
|
try:
|
||||||
|
dar_parts = dar.split(':')
|
||||||
|
if len(dar_parts) == 2:
|
||||||
|
dar_ratio = float(dar_parts[0]) / float(dar_parts[1])
|
||||||
|
expected_ratio = width / height
|
||||||
|
if abs(dar_ratio - expected_ratio) > 0.01: # More than 1% difference
|
||||||
|
needs_fix = True
|
||||||
|
logger.info(f"Video has incorrect aspect ratio {dar}, will fix to {expected_dar}")
|
||||||
|
except (ValueError, ZeroDivisionError):
|
||||||
|
needs_fix = True
|
||||||
|
|
||||||
|
if not needs_fix:
|
||||||
|
logger.debug(f"Video aspect ratio is correct: {dar} (dimensions: {width}x{height})")
|
||||||
|
return None
|
||||||
|
|
||||||
|
# Fix aspect ratio by remuxing with correct metadata
|
||||||
|
# Use temp file to avoid corruption if process fails
|
||||||
|
temp_file = video_file.with_suffix('.tmp' + video_file.suffix)
|
||||||
|
|
||||||
|
fix_cmd = [
|
||||||
|
'ffmpeg', '-i', str(video_path),
|
||||||
|
'-c', 'copy', # Copy streams without re-encoding
|
||||||
|
'-aspect', expected_dar, # Set correct aspect ratio
|
||||||
|
'-movflags', '+faststart', # Optimize for streaming
|
||||||
|
'-y', # Overwrite
|
||||||
|
str(temp_file)
|
||||||
|
]
|
||||||
|
|
||||||
|
proc = await asyncio.create_subprocess_exec(
|
||||||
|
*fix_cmd,
|
||||||
|
stdout=asyncio.subprocess.PIPE,
|
||||||
|
stderr=asyncio.subprocess.PIPE
|
||||||
|
)
|
||||||
|
await proc.communicate()
|
||||||
|
|
||||||
|
if proc.returncode == 0 and temp_file.exists():
|
||||||
|
# Replace original file with fixed one
|
||||||
|
video_file.unlink()
|
||||||
|
temp_file.rename(video_file)
|
||||||
|
logger.info(f"Video aspect ratio fixed: {video_path} (DAR: {expected_dar})")
|
||||||
|
return str(video_file)
|
||||||
|
else:
|
||||||
|
logger.warning(f"Failed to fix aspect ratio, keeping original file")
|
||||||
|
if temp_file.exists():
|
||||||
|
temp_file.unlink()
|
||||||
|
return None
|
||||||
|
|
||||||
|
except json.JSONDecodeError as e:
|
||||||
|
logger.warning(f"Failed to parse ffprobe output: {e}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error fixing video aspect ratio: {e}", exc_info=True)
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
def create_progress_hook(progress_callback: Optional[Callable] = None, event_loop=None, cancel_event: Optional[threading.Event] = None, last_update_time: list = None):
|
def create_progress_hook(progress_callback: Optional[Callable] = None, event_loop=None, cancel_event: Optional[threading.Event] = None, last_update_time: list = None):
|
||||||
"""
|
"""
|
||||||
Create progress hook for tracking download progress
|
Create progress hook for tracking download progress
|
||||||
@@ -165,8 +299,15 @@ async def download_media(
|
|||||||
'merge_output_format': 'mp4',
|
'merge_output_format': 'mp4',
|
||||||
# FFmpeg options for merging to ensure compatibility
|
# FFmpeg options for merging to ensure compatibility
|
||||||
# Copy streams when possible (no re-encoding), only encode if necessary
|
# Copy streams when possible (no re-encoding), only encode if necessary
|
||||||
|
# Add proper metadata for mobile compatibility
|
||||||
'postprocessor_args': {
|
'postprocessor_args': {
|
||||||
'ffmpeg': ['-c:v', 'copy', '-c:a', 'aac', '-movflags', '+faststart']
|
'ffmpeg': [
|
||||||
|
'-c:v', 'copy',
|
||||||
|
'-c:a', 'aac',
|
||||||
|
'-movflags', '+faststart',
|
||||||
|
'-aspect', 'auto', # Preserve aspect ratio
|
||||||
|
'-metadata:s:v:0', 'rotate=0', # Ensure no rotation metadata issues
|
||||||
|
]
|
||||||
},
|
},
|
||||||
# Don't prefer free formats (they may be lower quality)
|
# Don't prefer free formats (they may be lower quality)
|
||||||
'prefer_free_formats': False,
|
'prefer_free_formats': False,
|
||||||
@@ -330,12 +471,20 @@ async def download_media(
|
|||||||
if file_path.exists():
|
if file_path.exists():
|
||||||
file_size = file_path.stat().st_size
|
file_size = file_path.stat().st_size
|
||||||
logger.info(f"File found: {file_path}, size: {file_size / (1024*1024):.2f} MB")
|
logger.info(f"File found: {file_path}, size: {file_size / (1024*1024):.2f} MB")
|
||||||
|
|
||||||
|
# Post-process video to fix aspect ratio for mobile compatibility
|
||||||
|
if ext.lower() in ['mp4', 'mov', 'avi', 'mkv', 'webm']:
|
||||||
|
fixed_file_path = await fix_video_aspect_ratio(str(file_path))
|
||||||
|
if fixed_file_path:
|
||||||
|
file_path = Path(fixed_file_path)
|
||||||
|
logger.info(f"Video aspect ratio fixed: {file_path}")
|
||||||
|
|
||||||
return {
|
return {
|
||||||
'file_path': str(file_path),
|
'file_path': str(file_path),
|
||||||
'title': title,
|
'title': title,
|
||||||
'duration': info.get('duration'),
|
'duration': info.get('duration'),
|
||||||
'thumbnail': info.get('thumbnail'),
|
'thumbnail': info.get('thumbnail'),
|
||||||
'size': file_size
|
'size': file_path.stat().st_size
|
||||||
}
|
}
|
||||||
else:
|
else:
|
||||||
# Output list of all files in directory for debugging
|
# Output list of all files in directory for debugging
|
||||||
|
|||||||
Reference in New Issue
Block a user