in03 / proxima

Transcode source media directly from DaVinci Resolve using multiple machines for encoding. Great for creating proxies quickly.
MIT License
53 stars 3 forks source link

Add the Resolve API media pool item object so we can call it directly to link #72

Closed github-actions[bot] closed 2 years ago

github-actions[bot] commented 2 years ago

Add the Resolve API media pool item object so we can call it directly to link

source_metadata.update({'media_pool_item_object':media_pool_item})

https://github.com/in03/Resolve-Proxy-Encoder/blob/7a37143ef2824dc1064c40b4db9cc5d9c7efe1e1/resolve_proxy_encoder/queuer/resolve.py#L268


import imp
import logging
import os
import sys

from app.utils import core
from settings.manager import SettingsManager

config = SettingsManager()

core.install_rich_tracebacks()
logger = logging.getLogger(__name__)

class ResolveObjects:
    def __init__(self):
        self._populate_variables()

    def _get_resolve(self):

        ext = ".so"
        if sys.platform.startswith("darwin"):
            path = "/Applications/DaVinci Resolve/DaVinci Resolve.app/Contents/Libraries/Fusion/"
        elif sys.platform.startswith("win") or sys.platform.startswith("cygwin"):
            path = "C:\\Program Files\\Blackmagic Design\\DaVinci Resolve\\"
            ext = ".dll"
        elif sys.platform.startswith("linux"):
            path = "/opt/resolve/libs/Fusion/"
        else:
            raise Exception("Unsupported system! " + sys.platform)

        bmd = imp.load_dynamic("fusionscript", path + "fusionscript" + ext)
        resolve = bmd.scriptapp("Resolve")

        if not resolve:
            return None

        try:
            sys.modules[__name__] = resolve
        except ImportError:
            return None

        return resolve

    def _populate_variables(self):

        try:

            self.resolve = self._get_resolve()
            if self.resolve is None:
                raise TypeError

        except:

            logger.warning(
                "[red] :warning: Couldn't access the Resolve Python API. Is DaVinci Resolve running?[/]"
            )
            core.app_exit(1, -1)

        try:

            self.project = self.resolve.GetProjectManager().GetCurrentProject()
            if self.project is None:
                raise TypeError

        except:

            logger.warning(
                "[red] :warning: Couldn't get current project. Is a project open in Resolve?[/]"
            )
            core.app_exit(1, -1)

        try:

            self.timeline = self.project.GetCurrentTimeline()
            if self.timeline is None:
                raise TypeError
        except:

            logger.warning(
                "[red] :warning: Couldn't get current timeline. Is a timeline open in Resolve?[/]"
            )
            core.app_exit(1, -1)

        try:

            self.media_pool = self.project.GetMediaPool()
            if self.media_pool is None:
                raise TypeError

        except:

            logger.warning("[red] :warning: Couldn't get Resolve's media pool.[/]")
            core.app_exit(1, -1)

def get_video_track_items(timeline):
    """Get all video track items from the provided timeline"""

    all_track_items = []

    # Get count of tracks (index) in active timeline
    track_len = timeline.GetTrackCount("video")
    print(f"[green]Video track count: {track_len}[/]")

    # For each track in timeline (using index)
    for i in range(1, track_len + 1):

        # Get items
        track_items = timeline.GetItemListInTrack("video", i)

        if track_items is None:
            print(f"[yellow]No items found in track {i}[/]")
            continue

        else:
            all_track_items.append(track_items)

    return all_track_items

def get_media_pool_items(track_items):
    """Return media pool items for all track items"""

    all_media_pool_items = []

    for track in track_items:
        for item in track:
            media_item = item.GetMediaPoolItem()
            all_media_pool_items.append(media_item)

    return all_media_pool_items

def get_resolve_timelines(project, active_timeline_first=True):
    """Return a list of all Resolve timeline objects in current project."""

    timelines = []

    timeline_len = project.GetTimelineCount()
    if timeline_len > 0:

        for i in range(1, timeline_len + 1):
            timeline = project.GetTimelineByIndex(i)
            timelines.append(timeline)

        if active_timeline_first:
            active = project.GetCurrentTimeline().GetName()  # Get active timeline
            timeline_names = [x.GetName() for x in timelines]
            active_i = timeline_names.index(
                active
            )  # It's already in the list, find it's index
            timelines.insert(
                0, timelines.pop(active_i)
            )  # Move it to the front, indexing should be the same as name list
    else:
        return False

    return timelines

def get_source_metadata(media_pool_items):
    """Return source metadata for each media pool item that passes configured criteria.

    each media pool item must meet the following criteria:
        - return valid clip properties (needed for encoding, internal track items don't have them)
        - whitelisted extension (e.g, BRAW performs fine without proxies)
        - whitelisted framerate (optional) FFmpeg should handle most

    Args:
        - media_pool_items: list of Resolve API media pool items

    Returns:
        - filtered_metadata: a list of dictionaries containing all valid media and its Resolve-given properties.

    Raises:
        - none

    """

    # Lists
    filtered_metadata = []

    # Prevent filtering same media item multiple times
    # since multiple clips can have same source media
    seen_item = []

    for media_pool_item in media_pool_items:

        # Check media pool item is valid, get UUID
        try:

            mpi_uuid = str(media_pool_item).split("UUID:")[1].split("]")[0]

        except:

            logger.debug(
                f"[magenta]Media Pool Item: 'None'[/]\n"
                + f"[yellow]Invalid item: has no UUID[/]\n",
                extra={"markup": True},
            )
            continue

        if str(media_pool_item) in seen_item:

            logger.debug(
                f"[magenta]Media Pool Item: {mpi_uuid}[/]\n"
                + "[yellow]Already seen media pool item. Skipping...[/]\n",
                extra={"markup": True},
            )
            continue

        else:

            # Add first encounter to list for comparison
            seen_item.append(str(media_pool_item))

        # Check media pool item has clip properties
        if not hasattr(media_pool_item, "GetClipProperty()"):

            logger.debug(
                f"[magenta]Media Pool Item: {mpi_uuid}[/]\n"
                + "[yellow]Media pool item has no clip properties. Skipping...[/]\n",
                extra={"markup": True},
            )
            continue

        # Get source metadata, path, extension
        source_metadata = media_pool_item.GetClipProperty()
        source_path = source_metadata["File Path"]
        source_ext = os.path.splitext(source_path)[1].lower()

        # Might still get media that has clip properties, but empty attributes
        # Should only be internally generated media that returns this way
        if source_path is "":

            logger.debug(
                f"[magenta]Media Pool Item: {mpi_uuid}[/]\n"
                + f"clip properties did not return a valid filepath. Skipping...\n",
                extra={"markup": True},
            )
            continue

        # Filter extension
        if source_ext not in config["filters"]["extension_whitelist"]:

            logger.warning(
                f"[magenta]Media Pool Item: {mpi_uuid}[/]\n"
                + f"[yellow]Ignoring non-whitelisted file extension: '{source_ext}'[/]\n"
                + f"from '{source_metadata['File Path']}'[/]\n",
                extra={"markup": True},
            )
            continue

        # Filter framerate
        source_framerate = source_metadata["FPS"]
        if config["filters"]["use_framerate_whitelist"] == True:
            if source_framerate not in config["filters"]["framerate_whitelist"]:

                logger.warning(
                    f"[magenta]Media Pool Item: {mpi_uuid}[/]\n"
                    + f"[yellow]Ignoring non-whitelisted framerate: '{source_framerate}'[/] "
                    + f"from '{source_metadata['File Path']}' [/]\n",
                    extra={"markup": True},
                )
                continue

        # TODO: Add the Resolve API media pool item object so we can call it directly to link
        # source_metadata.update({'media_pool_item_object':media_pool_item})
        filtered_metadata.append(source_metadata)

    print(f"[green]Total queuable clips on timeline: {len(filtered_metadata)}[/]")

    return filtered_metadata

724d74d1a186c7ce8a2fee44eb7c7e281565352a

github-actions[bot] commented 2 years ago

Closed in 4eaaf02ec3f66143f5d871d81ebe332d94b537c8