mirror of
https://github.com/ynput/ayon-core.git
synced 2025-12-24 12:54:40 +01:00
Merge pull request #1250 from ynput/bugfix/AY-7730_thumbnail-creation-for-single-frame-video_1249
Improves thumbnail extraction reliability
This commit is contained in:
commit
0d8430188a
1 changed files with 67 additions and 17 deletions
|
|
@ -506,24 +506,36 @@ class ExtractThumbnail(pyblish.api.InstancePlugin):
|
||||||
# Set video input attributes
|
# Set video input attributes
|
||||||
max_int = str(2147483647)
|
max_int = str(2147483647)
|
||||||
video_data = get_ffprobe_data(video_file_path, logger=self.log)
|
video_data = get_ffprobe_data(video_file_path, logger=self.log)
|
||||||
# Use duration of the individual streams since it is returned with
|
|
||||||
# higher decimal precision than 'format.duration'. We need this
|
|
||||||
# more precise value for calculating the correct amount of frames
|
|
||||||
# for higher FPS ranges or decimal ranges, e.g. 29.97 FPS
|
|
||||||
duration = max(
|
|
||||||
float(stream.get("duration", 0))
|
|
||||||
for stream in video_data["streams"]
|
|
||||||
if stream.get("codec_type") == "video"
|
|
||||||
)
|
|
||||||
|
|
||||||
cmd_args = [
|
# Get duration or use a safe default (single frame)
|
||||||
"-y",
|
duration = 0
|
||||||
"-ss", str(duration * self.duration_split),
|
for stream in video_data["streams"]:
|
||||||
|
if stream.get("codec_type") == "video":
|
||||||
|
stream_duration = float(stream.get("duration", 0))
|
||||||
|
if stream_duration > duration:
|
||||||
|
duration = stream_duration
|
||||||
|
|
||||||
|
# For very short videos, just use the first frame
|
||||||
|
# Calculate seek position safely
|
||||||
|
seek_position = 0.0
|
||||||
|
# Only use timestamp calculation for videos longer than 0.1 seconds
|
||||||
|
if duration > 0.1:
|
||||||
|
seek_position = duration * self.duration_split
|
||||||
|
|
||||||
|
# Build command args
|
||||||
|
cmd_args = []
|
||||||
|
if seek_position > 0.0:
|
||||||
|
cmd_args.extend(["--ss", str(seek_position)])
|
||||||
|
|
||||||
|
# Add generic ffmpeg commands
|
||||||
|
cmd_args.extend([
|
||||||
"-i", video_file_path,
|
"-i", video_file_path,
|
||||||
"-analyzeduration", max_int,
|
"-analyzeduration", max_int,
|
||||||
"-probesize", max_int,
|
"-probesize", max_int,
|
||||||
"-frames:v", "1"
|
"-y",
|
||||||
]
|
"-frames:v", "1",
|
||||||
|
output_thumb_file_path
|
||||||
|
])
|
||||||
|
|
||||||
# add output file path
|
# add output file path
|
||||||
cmd_args.append(output_thumb_file_path)
|
cmd_args.append(output_thumb_file_path)
|
||||||
|
|
@ -537,15 +549,53 @@ class ExtractThumbnail(pyblish.api.InstancePlugin):
|
||||||
# run subprocess
|
# run subprocess
|
||||||
self.log.debug("Executing: {}".format(" ".join(cmd)))
|
self.log.debug("Executing: {}".format(" ".join(cmd)))
|
||||||
run_subprocess(cmd, logger=self.log)
|
run_subprocess(cmd, logger=self.log)
|
||||||
self.log.debug(
|
|
||||||
"Thumbnail created: {}".format(output_thumb_file_path))
|
# Verify the output file was created
|
||||||
return output_thumb_file_path
|
if (
|
||||||
|
os.path.exists(output_thumb_file_path)
|
||||||
|
and os.path.getsize(output_thumb_file_path) > 0
|
||||||
|
):
|
||||||
|
self.log.debug(
|
||||||
|
"Thumbnail created: {}".format(output_thumb_file_path))
|
||||||
|
return output_thumb_file_path
|
||||||
|
self.log.warning("Output file was not created or is empty")
|
||||||
|
|
||||||
|
# Try to create thumbnail without offset
|
||||||
|
# - skip if offset did not happen
|
||||||
|
if "-ss" not in cmd_args:
|
||||||
|
return None
|
||||||
|
|
||||||
|
self.log.debug("Trying fallback without offset")
|
||||||
|
# Remove -ss and its value
|
||||||
|
ss_index = cmd_args.index("-ss")
|
||||||
|
cmd_args.pop(ss_index) # Remove -ss
|
||||||
|
cmd_args.pop(ss_index) # Remove the timestamp value
|
||||||
|
|
||||||
|
# Create new command and try again
|
||||||
|
cmd = get_ffmpeg_tool_args("ffmpeg", *cmd_args)
|
||||||
|
self.log.debug("Fallback command: {}".format(" ".join(cmd)))
|
||||||
|
run_subprocess(cmd, logger=self.log)
|
||||||
|
|
||||||
|
if (
|
||||||
|
os.path.exists(output_thumb_file_path)
|
||||||
|
and os.path.getsize(output_thumb_file_path) > 0
|
||||||
|
):
|
||||||
|
self.log.debug("Fallback thumbnail created")
|
||||||
|
return output_thumb_file_path
|
||||||
|
return None
|
||||||
except RuntimeError as error:
|
except RuntimeError as error:
|
||||||
self.log.warning(
|
self.log.warning(
|
||||||
"Failed intermediate thumb source using ffmpeg: {}".format(
|
"Failed intermediate thumb source using ffmpeg: {}".format(
|
||||||
error)
|
error)
|
||||||
)
|
)
|
||||||
return None
|
return None
|
||||||
|
finally:
|
||||||
|
# Remove output file if is empty
|
||||||
|
if (
|
||||||
|
os.path.exists(output_thumb_file_path)
|
||||||
|
and os.path.getsize(output_thumb_file_path) == 0
|
||||||
|
):
|
||||||
|
os.remove(output_thumb_file_path)
|
||||||
|
|
||||||
def _get_resolution_arg(
|
def _get_resolution_arg(
|
||||||
self,
|
self,
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue