Some additional questions.
- How would you decide which published subsets to include for a specific task?
- Would you always want ALL reviews of all the tasks, or just recent ones? If so, how would you decide?
- What’s the reason you want a merged video and not just a quickly viewable “playlist” of recent versions (or even move them into e.g. a review folder, or upload them to a review tool of your choice like in a production tracker like Kitsu, Ftrack or Shotgrid - which is what OpenPype can already do?)
Below are some potential examples working towards what you are looking for from a code perspective. Maybe of interest to some.
Retrieve latest representations and concatenate files with FFMPEG
Here’s an example API logic of how to potentially retrieve the relevant review files.
import tempfile
from openpype.client import get_subsets, get_last_versions, get_representations
from openpype.pipeline import legacy_io, get_representation_path
from openpype.lib import (
get_ffmpeg_tool_path,
run_subprocess,
)
def simple_concat_videos(paths, destination):
"""Combine multiple videos with same codec into one file"""
ffmpeg_path = get_ffmpeg_tool_path("ffmpeg")
with tempfile.NamedTemporaryFile("w", suffix=".txt", delete=False) as f:
for path in paths:
path = path.replace("\\", "/")
f.write(f"file '{path}'\n")
filelist = f.name.replace("\\", "/")
# Simple FFMEG concatenation like this assumes the input video files
# have the same codecs + resolutions otherwise the output might not
# look the way you'd expect them to. There is no handling of different
# resolutions, FPS, codecs, etc.
args = [
ffmpeg_path,
"-f", "concat",
"-safe", "0",
"-i", filelist,
"-c", "copy",
destination,
]
run_subprocess(args)
project_name = legacy_io.active_project()
# This query could be optimized if you know the exact subset names, e.g. `reviewMain` or alike
# since you can pass the `subset_names` argument
subsets = get_subsets(project_name)
subset_ids = {subset["_id"] for subset in subsets}
last_versions_by_subset_id = get_last_versions(project_name, subset_ids=subset_ids)
versions = list(last_versions_by_subset_id.values())
version_ids = {version["_id"] for version in versions}
representations = list(get_representations(project_name, version_ids=version_ids, representation_names=["h264_png"]))
# Filter representations by task name context
#task = "modeling"
#representations = [repre for repre in representations if repre["context"]["task"]["name"] == task]
# Get the paths to the h264_png representations
paths = [get_representation_path(repre) for repre in representations]
destination = "E:/test.mp4"
if len(paths) > 1:
print(f"Concatenating videos:")
for path in paths:
print(f"- {path}")
print(f"Writing to: {destination}")
simple_concat_videos(paths, destination)
Disclaimer: this is using the openpype.client
api which is currently focused to be work for both OpenPype and AYON and since it’s still targeted to work for both these queries might not be as optimal as you’d like. If e.g. focusing solely on OpenPype with MongoDB I’d have likely made very different queries directly to the representations to solely get the latest representations based on the task instead of filtering them in Python like this logic does - since this is now basically getting ALL subsets and ALL latest versions.
A part of the potentially tricky logic to merging just any files together is the fact that they might mismatch in FPS, codecs, resolutions, etc. and you are basically working your way through a myriad of challenges with encoding that correctly. I might just recommend taking this logic to instead create some sort of “playlist” from the paths to open them in a media player of choice which can be launched with a playlist. The benefits would be:
- You’re actually viewing the source files, basically ensuring what you see is what you get
- No need to encode/re-encode, so less delay to view the video file.
- Within the video player you can then directly trace back to the source file too
- You can easily re-order the playblast or alike with the video player of your choice (if the player supports that).
Retrieve latest representations and load them in VLC as playlist
Here’s a relatively similar example like before but now instead of creating a merged video file it will load them into VLC in one go - you’ll then have a ready to go VLC playlist to watch. You’ll just need the command line flags to do that for the video editor of your choice and you’re good to go.
from openpype.client import get_subsets, get_last_versions, get_representations
from openpype.pipeline import legacy_io, get_representation_path
import subprocess
def get_latest_representation_paths(project_name,
subset_names=None,
task_names=None,
representation_names=None):
subsets = get_subsets(project_name, subset_names=subset_names, fields=["_id"])
subset_ids = {subset["_id"] for subset in subsets}
last_versions_by_subset_id = get_last_versions(project_name, subset_ids=subset_ids, fields=["_id"])
versions = list(last_versions_by_subset_id.values())
version_ids = {version["_id"] for version in versions}
representations = list(get_representations(project_name, version_ids=version_ids, representation_names=["h264_png"]))
# Filter representations by task name context
if task_names:
representations = [repre for repre in representations if repre["context"]["task"]["name"] in task_names]
# Get the paths to the h264_png representations
paths = [get_representation_path(repre) for repre in representations]
return paths
project_name = legacy_io.active_project()
paths = get_latest_representation_paths(project_name, representation_names=["h264_png"])
# Open multiple videos in VLC
VLC_PATH = r"C:\Program Files\VideoLAN\VLC\vlc.exe"
subprocess.Popen([VLC_PATH] + paths)