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,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
|
||||||
|
|||||||
Reference in New Issue
Block a user