Refactor MP4 to MOV conversion in premiere_to_resolve.py to ensure compatibility with DaVinci Resolve on Linux. The script now re-encodes to ProRes 422 HQ, with fallbacks to DNxHD and H.264, improving reliability and quality of output files. Update error handling for ffmpeg availability and conversion timeouts.

This commit is contained in:
Danilo Reyes
2026-01-29 12:51:41 -06:00
parent 61858c650d
commit f7b65d0b8f

View File

@@ -3,7 +3,9 @@
Premiere to Resolve XML Converter Premiere to Resolve XML Converter
Processes Adobe Premiere Pro XML exports to make them compatible with DaVinci Resolve on Linux. Processes Adobe Premiere Pro XML exports to make them compatible with DaVinci Resolve on Linux.
Updates file paths and optionally converts MP4 files to MOV format. Updates file paths and optionally converts MP4 files to MOV by re-encoding to Resolve-friendly
codecs (ProRes 422 HQ, DNxHD, or H.264 in MOV). Stream copy is not used so Resolve can read
the resulting files on Linux.
""" """
import os import os
@@ -121,71 +123,105 @@ def find_local_files(video_refs: Set[Tuple[str, str]], directory: str) -> Dict[s
def convert_mp4_to_mov(input_file: str, output_file: str) -> bool: def convert_mp4_to_mov(input_file: str, output_file: str) -> bool:
""" """
Convert MP4 to MOV using ffmpeg with minimal quality loss. Convert MP4 to MOV by re-encoding to a codec DaVinci Resolve on Linux supports.
Strategy: Resolve on Linux does not reliably support H.264-in-MP4 or stream-copied
1. Try copy codecs first (lossless if compatible) codecs in MOV. We always re-encode to ProRes 422 HQ (decode-only on Linux
2. Fallback to high-quality encoding but fully supported for import), with fallback to DNxHD or H.264 in MOV.
""" """
# Check if output file already exists # Check if output file already exists
if os.path.exists(output_file): if os.path.exists(output_file):
response = input(f"Output file {output_file} already exists. Overwrite? (y/n): ") response = input(f"Output file {output_file} already exists. Overwrite? (y/n): ")
if response.lower() != 'y': if response.lower() != 'y':
return False return False
# Try copy codecs first (lossless if compatible) try:
cmd_copy = [ subprocess.run(["ffmpeg", "-version"], capture_output=True, timeout=5)
'ffmpeg', '-i', input_file, except (FileNotFoundError, subprocess.TimeoutExpired):
'-c:v', 'copy', print("Error: ffmpeg not found. Please install ffmpeg.", file=sys.stderr)
'-c:a', 'copy', return False
'-y', # Overwrite output file
output_file # 1. Prefer ProRes 422 HQ well supported for import in Resolve on Linux
cmd_prores = [
"ffmpeg",
"-i",
input_file,
"-c:v",
"prores",
"-profile:v",
"2", # ProRes 422 HQ
"-c:a",
"pcm_s16le", # Uncompressed audio, always supported
"-y",
output_file,
] ]
try: try:
result = subprocess.run( result = subprocess.run(
cmd_copy, cmd_prores, capture_output=True, text=True, timeout=3600
capture_output=True,
text=True,
timeout=3600 # 1 hour timeout
) )
if result.returncode == 0: if result.returncode == 0:
print(f" Converted (copy codecs): {os.path.basename(input_file)} -> {os.path.basename(output_file)}") print(
f" Converted (ProRes 422 HQ): {os.path.basename(input_file)} -> {os.path.basename(output_file)}"
)
return True return True
except subprocess.TimeoutExpired: except subprocess.TimeoutExpired:
print(f" Timeout converting {input_file}", file=sys.stderr) print(f" Timeout converting {input_file}", file=sys.stderr)
return False return False
except FileNotFoundError:
print("Error: ffmpeg not found. Please install ffmpeg.", file=sys.stderr) # 2. Fallback: DNxHD 120 (1080p-friendly, full encode/decode support on Resolve Linux)
return False print(f" ProRes failed, trying DNxHD for {os.path.basename(input_file)}")
cmd_dnx = [
# Fallback to high-quality encoding "ffmpeg",
print(f" Copy codecs not compatible, using high-quality encoding for {os.path.basename(input_file)}") "-i",
cmd_encode = [ input_file,
'ffmpeg', '-i', input_file, "-c:v",
'-c:v', 'libx264', "dnxhd",
'-crf', '18', # High quality (lower = better quality) "-b:v",
'-preset', 'slow', # Better compression "120M", # DNxHD 120 Mbps (1080p)
'-c:a', 'copy', # Copy audio "-c:a",
'-y', "pcm_s16le",
output_file "-y",
output_file,
] ]
try: try:
result = subprocess.run( result = subprocess.run(cmd_dnx, capture_output=True, text=True, timeout=3600)
cmd_encode,
capture_output=True,
text=True,
timeout=3600
)
if result.returncode == 0: if result.returncode == 0:
print(f" Converted (encoded): {os.path.basename(input_file)} -> {os.path.basename(output_file)}") print(
f" Converted (DNxHD): {os.path.basename(input_file)} -> {os.path.basename(output_file)}"
)
return True return True
else: except subprocess.TimeoutExpired:
print(f" Error converting {input_file}: {result.stderr}", file=sys.stderr) print(f" Timeout converting {input_file}", file=sys.stderr)
return False return False
# 3. Last resort: H.264 in MOV (re-encoded, not copy)
print(f" DNxHD failed, using H.264 in MOV for {os.path.basename(input_file)}")
cmd_h264 = [
"ffmpeg",
"-i",
input_file,
"-c:v",
"libx264",
"-crf",
"18",
"-preset",
"slow",
"-pix_fmt",
"yuv420p", # Maximum compatibility
"-c:a",
"pcm_s16le",
"-y",
output_file,
]
try:
result = subprocess.run(cmd_h264, capture_output=True, text=True, timeout=3600)
if result.returncode == 0:
print(
f" Converted (H.264 MOV): {os.path.basename(input_file)} -> {os.path.basename(output_file)}"
)
return True
print(f" Error converting {input_file}: {result.stderr}", file=sys.stderr)
return False
except subprocess.TimeoutExpired: except subprocess.TimeoutExpired:
print(f" Timeout converting {input_file}", file=sys.stderr) print(f" Timeout converting {input_file}", file=sys.stderr)
return False return False