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:
@@ -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,11 +123,11 @@ 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):
|
||||||
@@ -133,57 +135,91 @@ def convert_mp4_to_mov(input_file: str, output_file: str) -> bool:
|
|||||||
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)
|
||||||
|
print(f" ProRes failed, trying DNxHD for {os.path.basename(input_file)}")
|
||||||
|
cmd_dnx = [
|
||||||
|
"ffmpeg",
|
||||||
|
"-i",
|
||||||
|
input_file,
|
||||||
|
"-c:v",
|
||||||
|
"dnxhd",
|
||||||
|
"-b:v",
|
||||||
|
"120M", # DNxHD 120 Mbps (1080p)
|
||||||
|
"-c:a",
|
||||||
|
"pcm_s16le",
|
||||||
|
"-y",
|
||||||
|
output_file,
|
||||||
|
]
|
||||||
|
try:
|
||||||
|
result = subprocess.run(cmd_dnx, capture_output=True, text=True, timeout=3600)
|
||||||
|
if result.returncode == 0:
|
||||||
|
print(
|
||||||
|
f" Converted (DNxHD): {os.path.basename(input_file)} -> {os.path.basename(output_file)}"
|
||||||
|
)
|
||||||
|
return True
|
||||||
|
except subprocess.TimeoutExpired:
|
||||||
|
print(f" Timeout converting {input_file}", file=sys.stderr)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# Fallback to high-quality encoding
|
# 3. Last resort: H.264 in MOV (re-encoded, not copy)
|
||||||
print(f" Copy codecs not compatible, using high-quality encoding for {os.path.basename(input_file)}")
|
print(f" DNxHD failed, using H.264 in MOV for {os.path.basename(input_file)}")
|
||||||
cmd_encode = [
|
cmd_h264 = [
|
||||||
'ffmpeg', '-i', input_file,
|
"ffmpeg",
|
||||||
'-c:v', 'libx264',
|
"-i",
|
||||||
'-crf', '18', # High quality (lower = better quality)
|
input_file,
|
||||||
'-preset', 'slow', # Better compression
|
"-c:v",
|
||||||
'-c:a', 'copy', # Copy audio
|
"libx264",
|
||||||
'-y',
|
"-crf",
|
||||||
output_file
|
"18",
|
||||||
|
"-preset",
|
||||||
|
"slow",
|
||||||
|
"-pix_fmt",
|
||||||
|
"yuv420p", # Maximum compatibility
|
||||||
|
"-c:a",
|
||||||
|
"pcm_s16le",
|
||||||
|
"-y",
|
||||||
|
output_file,
|
||||||
]
|
]
|
||||||
|
|
||||||
try:
|
try:
|
||||||
result = subprocess.run(
|
result = subprocess.run(cmd_h264, 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 (H.264 MOV): {os.path.basename(input_file)} -> {os.path.basename(output_file)}"
|
||||||
|
)
|
||||||
return True
|
return True
|
||||||
else:
|
|
||||||
print(f" Error converting {input_file}: {result.stderr}", file=sys.stderr)
|
print(f" Error converting {input_file}: {result.stderr}", file=sys.stderr)
|
||||||
return False
|
return False
|
||||||
except subprocess.TimeoutExpired:
|
except subprocess.TimeoutExpired:
|
||||||
|
|||||||
Reference in New Issue
Block a user