Rendering Text From Metadata Tags

User avatar
AndrewHazelden
Fusius Of Borg
Posts: 2598
Joined: 10 years ago
Location: West Dover, Nova Scotia, Canada
Has thanked: 2 times
Been thanked: 7 times
Contact:

Rendering Text From Metadata Tags

#1

Unread post by AndrewHazelden »

TextFromMetadata-viewer.png
This example shows how a Text+ node can be made to wirelesssly render metadata information from a Loader node in your composite. You can change the data source on the Text+ node by dragging and dropping a new Loader node onto the Text+ node's UserControl based "MetadataSource" attribute (that is visible on the Text Tab).
TextFromMetadata-drag-and-drop-connection.png
Download the Comp:
TextFromMetadata.comp
The Resize and Crop nodes are added to this example so the Loader node based images will be automatically sized and centered to fit in the view as you load in different pictures while testing the metadata tags.
TextFromMetadata-flow.png
How does this process work?

This example works because the Text+ node has a custom a UserControl input pasted on the end of the the node's code block named "MetadataSource":

Code: Select all

UserControls = ordered() {
  MetadataSource = {
    LINKID_DataType = "Image",
    INP_Default = 0,
    IC_ControlPage = 1,
    INPID_InputControl = "ImageControl",
    LINKS_Name = "MetadataSource",
  }
}
The Text+ Styled Text field then has an expression added that reads the wireless source connection and looks up a specific metadata tag. In this case it is a "Filename" Metadata attribute but you could change the expression to print out any other tag you want to display.

Expression:

Code: Select all

Text(self:GetSourceTool("MetadataSource").Output.Metadata.Filename)
If you wanted to print out the connected node name (like "Loader1") you could change the expression to:

Code: Select all

Text(self:GetSourceTool("MetadataSource").Name)
In the Viewer window you can click on the button labeled "SubV" and select the "Metadata" option in the menu to display a viewer window based overlay that shows of all the Metadata tags that are embedded in the current image. Any of these tags could be accessed from the Text+ node's Styled Text Expression.
fusion-viewer-subv-metadata.png
This workflow was inspired by Kristof's "Custom MainInputs" thread.
You do not have the required permissions to view the files attached to this post.

User avatar
AndrewHazelden
Fusius Of Borg
Posts: 2598
Joined: 10 years ago
Location: West Dover, Nova Scotia, Canada
Has thanked: 2 times
Been thanked: 7 times
Contact:

Re: Rendering Text From Metadata Tags

#2

Unread post by AndrewHazelden »

This example builds on the technique from the previous post to show how you can overlay EXIF photographic metadata over the image. It depends on your imagery actually having EXIF metadata tags so you don't get an error in the Console tab.
fusion-viewer-metadata.png
Downloads:

Here is a Fusion comp example and a sample photo. If you save the two assets to the same folder, when you open the comp in Fusion the photo is automatically loaded in to the comp file using the relative Pathmap address of "Comp:/Two Chicken Feet Walking on a Keyboard.jpg".
TextFromPhotoMetadata.comp
Two Chicken Feet Walking on a Keyboard.jpg
The Styled Text Field then uses an expression added that reads the source connection and looks up a specific metadata tag. In this case it is the standard EXIF Metadata attributes but you could change it to print out any attribute you want to display.

Code: Select all

Text("[Camera] " .. self:GetSourceTool("MetadataSource").Output.Metadata.Camera.Make ..  " " .. self:GetSourceTool("MetadataSource").Output.Metadata.Camera.Model .. "\n" ..
"[ISO] " .. self:GetSourceTool("MetadataSource").Output.Metadata.Camera.Exif.PhotographicSensitivity .. "\n" ..
"[Focal Length] " .. self:GetSourceTool("MetadataSource").Output.Metadata.Camera.Exif.FocalLength .. " mm\n" ..
"[Shutter Speed] 1/" .. 1/self:GetSourceTool("MetadataSource").Output.Metadata.Camera.Exif.ExposureTime .. " th of a second\n" ..
"[F-Stop] " .. self:GetSourceTool("MetadataSource").Output.Metadata.Camera.Exif.FNumber .. "\n" )
With the sample image this will generate a text output of:
[Camera] SONY ILCE-7SM2
[ISO] 1600
[Focal Length] 0 mm
[Shutter Speed] 1/45 th of a second
[F-Stop] 0
Note: The F-Stop and FocalLength EXIF attributes are empty in this sample image since the photo was taken using a lens that was connected to a Sony A7SII camera with a purely mechanical lens adapter with no electronic lens data connections.

The Text+ node's image width and height settings are configured using these expressions:

Width:

Code: Select all

self:GetSourceTool("MetadataSource").Output.OriginalWidth
Height:

Code: Select all

self:GetSourceTool("MetadataSource").Output.OriginalHeight
fusion-auto-image-size.png
Note: You may need to nudge the Text+ node's Layout Tab > Center X/Y placement controls when you load your own image into the comp.
You do not have the required permissions to view the files attached to this post.

User avatar
SecondMan
Site Admin
Posts: 7924
Joined: 10 years ago
Location: Vancouver, Canada
Mood:
Has thanked: 78 times
Been thanked: 72 times
Contact:

Re: Rendering Text From Metadata Tags

#3

Unread post by SecondMan »

You write fantastic posts Andrew, thank you!

User avatar
AndrewHazelden
Fusius Of Borg
Posts: 2598
Joined: 10 years ago
Location: West Dover, Nova Scotia, Canada
Has thanked: 2 times
Been thanked: 7 times
Contact:

Re: Rendering Text From Metadata Tags

#4

Unread post by AndrewHazelden »

SecondMan wrote:You write fantastic posts Andrew, thank you!
Thanks SecondMan! That means a lot to me coming from you. :)

User avatar
jun
Posts: 2
Joined: 10 years ago

Re: Rendering Text From Metadata Tags

#5

Unread post by jun »

Is it possible to extract data from a string with slashes and spaces in the metadata?

For example: mxf/Camera Roll Angle = 1.2000000

Most of the metadata in SonyVenice uses "mxf/" and spaces, and I am having trouble extracting the text strings.
Any help would be appreciated.
VENICE_metadata.jpg
You do not have the required permissions to view the files attached to this post.

User avatar
AndrewHazelden
Fusius Of Borg
Posts: 2598
Joined: 10 years ago
Location: West Dover, Nova Scotia, Canada
Has thanked: 2 times
Been thanked: 7 times
Contact:

Re: Rendering Text From Metadata Tags

#6

Unread post by AndrewHazelden »

Hi @jun.
jun wrote: 3 years agoIs it possible to extract data from a string with slashes and spaces in the metadata?
A solution to your issue of accessing metadata records with separator characters in the attribute name was posted by @Midgardsormr in the following WSL thread:

Truncated metadata in viewer
Re: Truncated metadata in viewer

User avatar
AndrewHazelden
Fusius Of Borg
Posts: 2598
Joined: 10 years ago
Location: West Dover, Nova Scotia, Canada
Has thanked: 2 times
Been thanked: 7 times
Contact:

Re: Rendering Text From Metadata Tags

#7

Unread post by AndrewHazelden »

Accessing Resolve Media Pool Metadata in the Fusion Page

This weekend I was asked by a friend in the immersive sector to explain how a Resolve Fusion page user could access Resolve Media Pool based metadata records. Their goal was to create custom burnin titles using Fusion page Text+ nodes, then to reformat the "flat" titles into a 360VR image projection. This would make it possible to quickly overlay important data over the footage when exporting intermediate files for the rest of their team's post-production staff to use.

My initial commentary before showing one approach to do this, is to say that life in the Fusion page would be so much more fulfilling, and a more rewarding experience overall if Resolve Tokens worked in a Text+ node's expression field. Also, I think intool scripts and simple expressions having read-only (thread-safe) access to Resolve API zones like Media Pool Items would be super-powerful, too.

In fuses, for example, we can access the following values without needing to make a new connection to Fusion() / fusion.CurrentComp:
  • self.Comp
  • self.Comp:MapPath()
  • self.Comp:Execute()
  • self.Comp.Filename
  • self.Comp.Name
  • self.Comp.RenderStart
  • self.Comp.RenderEnd
  • self.Comp.GlobalStart
  • self.Comp.GlobalEnd
Conceptually, if there was a "self.MediaPool:" approach that was accessible in fuses, intool scripts, and simple expressions, life would be better for Resolve based TDs.

If this was possible, one would be able to more easily check the MediaPool with Lua Patterns to look for assets by name. Then a MediaIn node MediaID tag could be relinked to use these revised assets in Title Templates, Effects Templates, etc.

This way you could make title templates that use movie files with a MediaIn node, vs going over to PathMapped image sequences in Loader nodes... just to avoid issues that happen when importing a pre-existing .setting file (that holds a MediaIn node) into a new Resolve project database where the MediaID tag for the MediaPool item won't lineup with the asset inside the Macro.

Connecting to the Media Pool

In the mean time let's explore one approach to pass Media Pool item metadata to a Title+ node. We are going to use a DIY Lua script technique that places a copy of the current Media Pool metadata Lua table info into the "Comments" record of a MediaIn node in the Fusion page.

Image

Then we use a UserControls entry added to the Text+ node to receive the connection information from the MediaIn node, done wirelessly, via the magic of a INPID_InputControl = "ImageControl",. This is how the MediaIn node's Comments records will be accessible in the Text+ node's StyledText field expression.

Image

The super-lazy way to access the MediaIn node comments in a Text+ node StyledText expression would be:
MediaIn1.Comments


If you want to add the MediaIn node comments content to a Text+ node reliably you can use a StyledText expression of:
Text(self:GetSourceTool("MetadataSource").Comments.Value)

or you can use the following StyledText expression to access a specific record directly from the Lua table data and print it in the text+ node using:
:local tbl = bmd.readstring(self:GetSourceTool("MetadataSource").Comments.Value); return Text(tbl.Property["Clip Name"])

The simple expression above pulls the data from a WirelessLink "Drag and Drop" like UserControls entry added to the Text+ node in a text editor using:

Code: Select all

UserControls = ordered() {
    MetadataSource = {
        LINKID_DataType = "Image",
        LINKS_Name = "MetadataSource",
        IC_ControlPage = 1,
        INPID_InputControl = "ImageControl",
        INP_Default = 0,
    }
}

Copy/Paste-able Example Node Setup

Image

Here is a macro .settings snippet that has a MediaIn node with a pre-filled Comments entry, along with a Text+ node that has the UserControls field appended:

"MediaIn node with comments and Text+ node.setting"

Code: Select all

{
	Tools = ordered() {
		Text = TextPlus {
			CtrlWZoom = false,
			NameSet = true,
			CustomData = {
				Path = {
					Map = {
						["Setting:"] = "Previews:/KartaVR/"
					}
				},
			},
			Inputs = {
				GlobalOut = Input { Value = 119, },
				Width = Input { Value = 1920, },
				Height = Input { Value = 1080, },
				["Gamut.SLogVersion"] = Input { Value = FuID { "SLog2" }, },
				Center = Input { Value = { 0.5, 0.0977223427331887 }, },
				StyledText = Input {
					Value = "",
					Expression = ":local tbl = bmd.readstring(self:GetSourceTool(\"MetadataSource\").Comments.Value); return Text(tbl.Property[\"Clip Name\"])",
				},
				Font = Input { Value = "Open Sans", },
				Style = Input { Value = "Bold", },
				Size = Input { Value = 0.0398550724637681, },
				VerticalJustificationNew = Input { Value = 3, },
				HorizontalJustificationNew = Input { Value = 3, },
				MetadataSource = Input {
					SourceOp = "MediaIn1",
					Source = "Output",
				},
			},
			ViewInfo = OperatorInfo { Pos = { 484.333, 150.924 } },
			UserControls = ordered() {
				MetadataSource = {
					LINKID_DataType = "Image",
					LINKS_Name = "MetadataSource",
					IC_ControlPage = 1,
					INPID_InputControl = "ImageControl",
					INP_Default = 0,
				}
			}
		},
		MediaIn1 = MediaIn {
			CustomData = {
				MediaProps = {
					MEDIA_FORMAT_TYPE = "JPEG",
					MEDIA_HEIGHT = 2048,
					MEDIA_IS_SOURCE_RES = true,
					MEDIA_LAYER_DESC = {
					},
					MEDIA_MARK_IN = 0,
					MEDIA_MARK_OUT = 0,
					MEDIA_NAME = "angular360degree.jpg",
					MEDIA_NUM_FRAMES = 1,
					MEDIA_NUM_LAYERS = 0,
					MEDIA_PAR = 1,
					MEDIA_PATH = "/Users/vfx/Reactor/Deploy/Macros/KartaVR/Images/angular360degree.jpg",
					MEDIA_SRC_FRAME_RATE = 24,
					MEDIA_START_FRAME = 0,
					MEDIA_WIDTH = 2048
				},
			},
			Inputs = {
				GlobalOut = Input { Value = 119, },
				MediaID = Input { Value = "66eb91ea-0b56-40b5-a7d1-39e3764530a1", },
				AudioTrack = Input { Value = FuID { "Timeline Audio" }, },
				Layer = Input {
					Value = Text {
					},
				},
				ClipTimeEnd = Input { Value = 0, },
				HoldLastFrame = Input { Value = 119, },
				["Gamut.SLogVersion"] = Input { Value = FuID { "SLog2" }, },
				Comments = Input { Value = "{\n	MediaProps = {\n		MEDIA_PAR = 1,\n		MEDIA_PATH = \"/Users/vfx/Reactor/Deploy/Macros/KartaVR/Images/angular360degree.jpg\",\n		MEDIA_SRC_FRAME_RATE = 24,\n		MEDIA_START_FRAME = 0,\n		MEDIA_WIDTH = 2048,\n		MEDIA_FORMAT_TYPE = \"JPEG\",\n		MEDIA_HEIGHT = 2048,\n		MEDIA_IS_SOURCE_RES = true,\n		MEDIA_LAYER_DESC = {\n		},\n		MEDIA_MARK_IN = 0,\n		MEDIA_MARK_OUT = 0,\n		MEDIA_NAME = \"angular360degree.jpg\",\n		MEDIA_NUM_FRAMES = 1,\n		MEDIA_NUM_LAYERS = 0\n	},\n	NodeName = \"MediaIn1\",\n	MediaID = \"66eb91ea-0b56-40b5-a7d1-39e3764530a1\",\n	Property = {\n		[\"Good Take\"] = \"\",\n		[\"H-FLIP\"] = \"Off\",\n		IDT = \"\",\n		[\"Input Color Space\"] = \"Project\",\n		[\"Input LUT\"] = \"\",\n		[\"Input Sizing Preset\"] = \"None\",\n		End = \"360\",\n		[\"Offline Reference\"] = \"\",\n		[\"Online Status\"] = \"Online\",\n		Out = \"\",\n		PAR = \"Square\",\n		[\"Proxy Media Path\"] = \"\",\n		[\"Reel Name\"] = \"\",\n		Resolution = \"2048x2048\",\n		[\"Roll/Card\"] = \"\",\n		[\"S3D Sync\"] = \"\",\n		[\"Sample Rate\"] = \"\",\n		Scene = \"\",\n		Shot = \"\",\n		In = \"\",\n		[\"Start KeyKode\"] = \"\",\n		[\"Start TC\"] = \"00:00:00:00\",\n		Proxy = \"None\",\n		[\"Synced Audio\"] = \"\",\n		Take = \"\",\n		Usage = \"0\",\n		[\"V-FLIP\"] = \"Off\",\n		[\"Video Codec\"] = \"JPEG\",\n		[\"Alpha mode\"] = \"None\",\n		[\"Super Scale\"] = 1,\n		Comments = \"\",\n		Type = \"Still\",\n		Start = \"360\",\n		[\"Drop frame\"] = \"0\",\n		[\"Slate TC\"] = \"00:00:00:00\",\n		[\"Enable Deinterlacing\"] = \"0\",\n		Sharpness = \"\",\n		[\"Audio Bit Depth\"] = \"\",\n		[\"Audio Ch\"] = \"0\",\n		[\"Audio Codec\"] = \"\",\n		[\"Audio Offset\"] = \"\",\n		[\"Bit Depth\"] = \"8\",\n		[\"Camera #\"] = \"\",\n		[\"Clip Color\"] = \"\",\n		[\"Clip Name\"] = \"angular360degree.jpg\",\n		Angle = \"\",\n		[\"Data Level\"] = \"Auto\",\n		[\"Date Added\"] = \"Sat Oct 1 2022 23:18:29\",\n		[\"Date Created\"] = \"Tue Aug 23 2022 00:23:35\",\n		[\"Date Modified\"] = \"Tue Aug 23 00:23:35 2022\",\n		Description = \"\",\n		Duration = \"00:00:00:01\",\n		[\"Noise Reduction\"] = \"\",\n		[\"End TC\"] = \"00:00:00:01\",\n		FPS = 24,\n		[\"Field Dominance\"] = \"Auto\",\n		[\"File Name\"] = \"angular360degree.jpg\",\n		[\"File Path\"] = \"/Users/vfx/Reactor/Deploy/Macros/KartaVR/Images/angular360degree.jpg\",\n		Flags = \"\",\n		Format = \"JPEG\",\n		Frames = \"1\",\n		Keyword = \"\"\n	}\n}", },
				FrameRenderScriptNest = Input { Value = 0, },
			},
			ViewInfo = OperatorInfo { Pos = { 328, 150.394 } },
		}
	}
}

WIP Example Lua Comp Script

This comp script takes the currently selected (and active) MediaIn node from a Resolve Studio based Fusion page composite and looks for its metadata records in the active Resolve Media Pool bin "folder".

"Find Selected MediaID in Media Pool.lua" Comp Script:

Code: Select all

--[[--
Find Selected MediaID in Media Pool.lua - 2022-10-02 02.00 AM
By Andrew Hazelden <andrew@andrewhazelden.com>

Overview:
This comp script takes the currently selected (and active) MediaIn node from a Resolve Studio based Fusion page composite and looks for its metadata records in the active Resolve Media Pool bin "folder". The metadata records are then used as the contents in the MediaIn node's Comments field.

The resulting output in the Comments field on a MediaIn node looks like this:
{
	NodeName = "MediaIn5",
	MediaID = "9769be21-8a07-4786-a280-06bb78943d91",
	Property = {
		IDT = "",
		["Input Color Space"] = "Project",
		["Input LUT"] = "",
		["Input Sizing Preset"] = "None",
		Keyword = "",
		In = "",
		["Online Status"] = "Online",
		Out = "",
		PAR = "Square",
		["Proxy Media Path"] = "",
		["Reel Name"] = "",
		Resolution = "2048x2048",
		["Roll/Card"] = "",
		["S3D Sync"] = "",
		["Sample Rate"] = "",
		Scene = "",
		Shot = "",
		["Slate TC"] = "00:00:00:00",
		["Start KeyKode"] = "",
		["Start TC"] = "00:00:00:00",
		Angle = "",
		["Synced Audio"] = "",
		Take = "",
		Usage = "0",
		["V-FLIP"] = "Off",
		["Video Codec"] = "JPEG",
		["Alpha mode"] = "None",
		["Noise Reduction"] = "",
		Type = "Still",
		Start = "2",
		Sharpness = "",
		["Drop frame"] = "0",
		Proxy = "None",
		["Enable Deinterlacing"] = "0",
		["Offline Reference"] = "",
		["Audio Bit Depth"] = "",
		["Audio Ch"] = "0",
		["Audio Codec"] = "",
		["Audio Offset"] = "",
		["Bit Depth"] = "8",
		["Camera #"] = "",
		["Clip Color"] = "",
		["Clip Name"] = "fulldome_2K.jpg",
		["Super Scale"] = 1,
		["Data Level"] = "Auto",
		Comments = "",
		["Date Created"] = "Tue Aug 23 2022 00:23:45",
		["Date Modified"] = "Tue Aug 23 00:23:45 2022",
		Description = "",
		Duration = "00:00:00:01",
		End = "2",
		["End TC"] = "00:00:00:01",
		FPS = 24,
		["Field Dominance"] = "Auto",
		["File Name"] = "fulldome_2K.jpg",
		["File Path"] = "/Users/vfx/Reactor/Deploy/Macros/KartaVR/Images/fulldome_2K.jpg",
		Flags = "",
		Format = "JPEG",
		Frames = "1",
		["Date Added"] = "Sat Oct 1 2022 23:18:29",
		["Good Take"] = "",
		["H-FLIP"] = "Off"
	},
	MediaProps = { MEDIA_NUM_LAYERS = 0, MEDIA_FORMAT_TYPE = "JPEG", MEDIA_PATH = "/Users/vfx/Reactor/Deploy/Macros/KartaVR/Images/fulldome_2K.jpg", MEDIA_LAYER_DESC = {
		}, MEDIA_NAME = "fulldome_2K.jpg", MEDIA_HEIGHT = 2048, MEDIA_START_FRAME = 0, MEDIA_IS_SOURCE_RES = true, MEDIA_SRC_FRAME_RATE = 24, MEDIA_MARK_OUT = 0, MEDIA_WIDTH = 2048, MEDIA_NUM_FRAMES = 1, MEDIA_MARK_IN = 0, MEDIA_PAR = 1 }
}

* * *

Tip: You could store the output from running this type of comp script in a CustomData record in the "comp" or "tool" scope if you didn't want to use the comment field on the MediaIn node.

Note: If one wanted to revise this script, it would be possible to expand the searching scope of the Media Pool, to start at the root folder and then recursively look in all-subfolders using "folder:GetSubFolderList()".

* * *

The super-lazy way to access the MediaIn node comments in a Text+ node StyledText expression would be:
MediaIn1.Comments


If you want to add the MediaIn node comments content to a Text+ node reliably you can use a StyledText expression of:
Text(self:GetSourceTool("MetadataSource").Comments.Value)

or you can use the following StyledText expression to access a specific record directly from the Lua table data and print it in the text+ node using:

:local tbl = bmd.readstring(self:GetSourceTool("MetadataSource").Comments.Value); return Text(tbl.Property["Clip Name"])

This simple expression above pulls the data from a WirelessLink "Drag and Drop" like UserControls entry added to the Text+ node in a text editor using:
UserControls = ordered() {
    MetadataSource = {
        LINKID_DataType = "Image",
        LINKS_Name = "MetadataSource",
        IC_ControlPage = 1,
        INPID_InputControl = "ImageControl",
        INP_Default = 0,
    }
}

For further reading about this idea check out:
WSL | Rendering Text From Metadata Tags
https://www.steakunderwater.com/wesuckless/viewtopic.php?p=10922#p10922


--]]--


function GetMediaID()
    -- Grab the selected MediaIn node's MediaID tag
    local id
    local tool = comp.ActiveTool
    local name
    if tool and tool:GetAttrs() and tool:GetAttrs().TOOLS_RegID == "MediaIn" then
        -- Node Name
        name = tool.Name

        -- Node MediaID
        id = tool["MediaID"][fu.TIME_UNDEFINED]
        -- dump(nodeID)
    else
        print("[Error] Select a MediaIn node.")
    end

    return id, name, tool
end

function BrowseMediaPool(id)
    -- Look inside the current MediaPool folder for a matching MediaID tag
    local res = Resolve("localhost", 0, bmd.getappuuid())
    if res ~= nil then
        pm = res:GetProjectManager()
        project = pm:GetCurrentProject()
        mp = project:GetMediaPool()
        folder = mp:GetCurrentFolder()

        clips = folder:GetClips()
        for clipIndex, clipValue in ipairs(clips) do
            prop = clipValue:GetClipProperty()
            -- Type: "Still", "Video", "Audio", "Video + Audio", "Geometry", "Generator", "Timeline"
            clipType = prop["Type"]
            -- dump(clipType)
            if clipType == "Still" or clipType == "Video" or clipType == "Video + Audio" then
                clipID = clipValue:GetMediaId()
                -- fileName = prop["File Name"]
                -- dump(fileName)

                -- Check for a mediaID match from the selected MediaIn node in the Fusion page and the MediaPool clip
                if id and clipID and id == clipID then
                    return clipValue
                end
            end
        end
    else
        print("[Error] Could not connect to Resolve session.")
    end
end

function Main()
    -- Grab the selected MediaIn node's MediaID tag
    local nodeID, nodeName, nodeTool = GetMediaID()

    if nodeID then
        -- Look inside the current MediaPool folder for a matching MediaID tag
        local mpClip = BrowseMediaPool(nodeID)
        if mpClip then
            local mpTable = {}
            mpTable.NodeName = (nodeName or "")
            mpTable.MediaID = nodeID
            mpTable.MediaProps = nodeTool:GetData("MediaProps")
            mpTable.Property = mpClip:GetClipProperty()

    --        print("[" .. mpTable.NodeName .. "]")
    --        print("  [MediaID]")
    --        print(mpTable.MediaID)
    --        print("  [Resolve Metadata]")
    --        dump(mpTable.Property)

            -- Write the metadata to the MediaIn node's Comment field:
            nodeTool["Comments"][fu.TIME_UNDEFINED] = bmd.writestring(mpTable)

            -- Auto-switch to the Common control page to display the Comments input content
            nodeTool:ShowControlPage("Common")
        else
            print("[Error] MediaIn Node footage not found in current Media Pool folder.")
        end
    end
    print("[Done]")
end

Main()

Yeah... But Can You Vonk-ify This Workflow For Me?

If you are a fan of the Vonk Data Nodes in Fusion, it is technically possible to use the "vTextExecute" node to dynamically update the Comments fields on MediaIn nodes by running an inline Lua code chunk in the fuse.

This results in the MediaIn Comments data updating in an asynchronous fashion since the fuse uses the "self.Comp:Execute()" function under-the-hood.

Image

I pasted the following Lua script in a Vonk vTextExecute node. When the node is rendered, or displayed in the Viewer window, all of the MediaIn node Comments are automatically refreshed in the comp. For performance tuning, the vTextExcute node could be set to a PassThrough state after you have refreshed the MediaIn nodes.

"Find All MediaID in Media Pool.lua" Comp Script:

Code: Select all

--[[--
Find All MediaID in Media Pool.lua - 2022-10-02 02.00 AM
By Andrew Hazelden <andrew@andrewhazelden.com>

Overview:
This comp script takes all MediaIn nodes from a Resolve Studio based Fusion page composite and looks for their metadata records in the active Resolve Media Pool bin "folder". The metadata records are then used as the contents in the MediaIn node's Comments field.

The resulting output in the Comments field on a MediaIn node looks like this:
{
	NodeName = "MediaIn5",
	MediaID = "9769be21-8a07-4786-a280-06bb78943d91",
	Property = {
		IDT = "",
		["Input Color Space"] = "Project",
		["Input LUT"] = "",
		["Input Sizing Preset"] = "None",
		Keyword = "",
		In = "",
		["Online Status"] = "Online",
		Out = "",
		PAR = "Square",
		["Proxy Media Path"] = "",
		["Reel Name"] = "",
		Resolution = "2048x2048",
		["Roll/Card"] = "",
		["S3D Sync"] = "",
		["Sample Rate"] = "",
		Scene = "",
		Shot = "",
		["Slate TC"] = "00:00:00:00",
		["Start KeyKode"] = "",
		["Start TC"] = "00:00:00:00",
		Angle = "",
		["Synced Audio"] = "",
		Take = "",
		Usage = "0",
		["V-FLIP"] = "Off",
		["Video Codec"] = "JPEG",
		["Alpha mode"] = "None",
		["Noise Reduction"] = "",
		Type = "Still",
		Start = "2",
		Sharpness = "",
		["Drop frame"] = "0",
		Proxy = "None",
		["Enable Deinterlacing"] = "0",
		["Offline Reference"] = "",
		["Audio Bit Depth"] = "",
		["Audio Ch"] = "0",
		["Audio Codec"] = "",
		["Audio Offset"] = "",
		["Bit Depth"] = "8",
		["Camera #"] = "",
		["Clip Color"] = "",
		["Clip Name"] = "fulldome_2K.jpg",
		["Super Scale"] = 1,
		["Data Level"] = "Auto",
		Comments = "",
		["Date Created"] = "Tue Aug 23 2022 00:23:45",
		["Date Modified"] = "Tue Aug 23 00:23:45 2022",
		Description = "",
		Duration = "00:00:00:01",
		End = "2",
		["End TC"] = "00:00:00:01",
		FPS = 24,
		["Field Dominance"] = "Auto",
		["File Name"] = "fulldome_2K.jpg",
		["File Path"] = "/Users/vfx/Reactor/Deploy/Macros/KartaVR/Images/fulldome_2K.jpg",
		Flags = "",
		Format = "JPEG",
		Frames = "1",
		["Date Added"] = "Sat Oct 1 2022 23:18:29",
		["Good Take"] = "",
		["H-FLIP"] = "Off"
	},
	MediaProps = { MEDIA_NUM_LAYERS = 0, MEDIA_FORMAT_TYPE = "JPEG", MEDIA_PATH = "/Users/vfx/Reactor/Deploy/Macros/KartaVR/Images/fulldome_2K.jpg", MEDIA_LAYER_DESC = {
		}, MEDIA_NAME = "fulldome_2K.jpg", MEDIA_HEIGHT = 2048, MEDIA_START_FRAME = 0, MEDIA_IS_SOURCE_RES = true, MEDIA_SRC_FRAME_RATE = 24, MEDIA_MARK_OUT = 0, MEDIA_WIDTH = 2048, MEDIA_NUM_FRAMES = 1, MEDIA_MARK_IN = 0, MEDIA_PAR = 1 }
}

* * *

Tip: You could store the output from running this type of comp script in a CustomData record in the "comp" or "tool" scope if you didn't want to use the comment field on the MediaIn node.

Note: If one wanted to revise this script, it would be possible to expand the searching scope of the Media Pool, to start at the root folder and then recursively look in all-subfolders using "folder:GetSubFolderList()".

* * *

The super-lazy way to access the MediaIn node comments in a Text+ node StyledText expression would be:
MediaIn1.Comments


If you want to add the MediaIn node comments content to a Text+ node reliably you can use a StyledText expression of:
Text(self:GetSourceTool("MetadataSource").Comments.Value)

or you can use the following StyledText expression to access a specific record directly from the Lua table data and print it in the text+ node using:

:local tbl = bmd.readstring(self:GetSourceTool("MetadataSource").Comments.Value); return Text(tbl.Property["Clip Name"])

This simple expression above pulls the data from a WirelessLink "Drag and Drop" like UserControls entry added to the Text+ node in a text editor using:
UserControls = ordered() {
    MetadataSource = {
        LINKID_DataType = "Image",
        LINKS_Name = "MetadataSource",
        IC_ControlPage = 1,
        INPID_InputControl = "ImageControl",
        INP_Default = 0,
    }
}

For further reading about this idea check out:
WSL | Rendering Text From Metadata Tags
https://www.steakunderwater.com/wesuckless/viewtopic.php?p=10922#p10922

--]]--

function GetMediaID(tool)
    -- Grab the MediaIn node's MediaID tag
    local id
    local name
    if tool and tool:GetAttrs() and tool:GetAttrs().TOOLS_RegID == "MediaIn" then
        -- Node Name
        name = tool.Name

        -- Node MediaID
        id = tool["MediaID"][fu.TIME_UNDEFINED]
        -- dump(nodeID)
    else
        print("[Error] Select a MediaIn node.")
    end

    return id, name, tool
end

function BrowseMediaPool(id)
    -- Look inside the current MediaPool folder for a matching MediaID tag
    local res = Resolve("localhost", 0, bmd.getappuuid())
    if res ~= nil then
        pm = res:GetProjectManager()
        project = pm:GetCurrentProject()
        mp = project:GetMediaPool()
        folder = mp:GetCurrentFolder()

        clips = folder:GetClips()
        for clipIndex, clipValue in ipairs(clips) do
            prop = clipValue:GetClipProperty()
            -- Type: "Still", "Video", "Audio", "Video + Audio", "Geometry", "Generator", "Timeline"
            clipType = prop["Type"]
            -- dump(clipType)
            if clipType == "Still" or clipType == "Video" or clipType == "Video + Audio" then
                clipID = clipValue:GetMediaId()
                -- fileName = prop["File Name"]
                -- dump(fileName)

                -- Check for a mediaID match from the selected MediaIn node in the Fusion page and the MediaPool clip
                if id and clipID and id == clipID then
                    return clipValue
                end
            end
        end
    else
        print("[Error] Could not connect to Resolve session.")
    end
end

function Main()
    selectedMediaIn = comp:GetToolList(false, "MediaIn")
    for i, v in ipairs(selectedMediaIn) do
        -- Grab the selected MediaIn node's MediaID tag
        local nodeID, nodeName, nodeTool = GetMediaID(v)
        if nodeID then
            -- Look inside the current MediaPool folder for a matching MediaID tag
            local mpClip = BrowseMediaPool(nodeID)
            if mpClip then
                local mpTable = {}
                mpTable.NodeName = (nodeName or "")
                mpTable.MediaID = nodeID
                mpTable.MediaProps = nodeTool:GetData("MediaProps")
                mpTable.Property = mpClip:GetClipProperty()

               print("[" .. mpTable.NodeName .. "]")
        --        print("  [MediaID]")
        --        print(mpTable.MediaID)
        --        print("  [Resolve Metadata]")
        --        dump(mpTable.Property)

                -- Write the metadata to the MediaIn node's Comment field:
                nodeTool["Comments"][fu.TIME_UNDEFINED] = bmd.writestring(mpTable)

                -- Auto-switch to the Common control page to display the Comments input content
                nodeTool:ShowControlPage("Common")
            else
                print("[" .. tostring(v.Name) .. "] [Error] MediaIn Node footage not found in current Media Pool folder.")
            end
        end
    end
    print("[Done]")
end

Main()

Last edited by AndrewHazelden 2 years ago, edited 2 times in total.

User avatar
Kristof
Fusionator
Posts: 1106
Joined: 10 years ago

Re: Rendering Text From Metadata Tags

#8

Unread post by Kristof »

Oooohhhh... vTextExecute, you say? That's a nice idea! I'll check it out. Thanks @AndrewHazelden.

User avatar
AndrewHazelden
Fusius Of Borg
Posts: 2598
Joined: 10 years ago
Location: West Dover, Nova Scotia, Canada
Has thanked: 2 times
Been thanked: 7 times
Contact:

Re: Rendering Text From Metadata Tags

#9

Unread post by AndrewHazelden »

MediaIn Parameters for Text+ StyledText Expressions

Here are several Resolve MediaIn node parameters you can access directly in a Text+ node's StyledText expression field:

Node Name:
Text(MediaIn1.Name)

MediaIn Media ID:
Text(MediaIn1.MediaID.Value)

MediaIn Clip Name:
Text(MediaIn1.ClipName.Value)

MediaIn Global In:
Text(MediaIn1.GlobalIn)

MediaIn Global Out:
Text(MediaIn1.GlobalOut)

MediaInComments Field:
Text(MediaIn1.Comments.Value)

MediaIn EXR Multi-Part Layer Name:
Text(MediaIn1.Layer.Value)

MediaIn Red Channel Element Name:
Text(MediaIn1.RedName.Value)

MediaIn Green Channel Element Name:
Text(MediaIn1.GreenName.Value)

MediaIn Blue Channel Element Name:
Text(MediaIn1.BlueName.Value)

MediaIn Alpha Channel Element Name:
Text(MediaIn1.AlphaName.Value)

MediaIn Z-Depth Channel Element Name:
Text(MediaIn1.ZName.Value)

MediaIn Extra Channel Element Names:
Text(MediaIn1.CovName.Value)
Text(MediaIn1.ObjIDName.Value)
Text(MediaIn1.MatIDName.Value)
Text(MediaIn1.UName.Value)
Text(MediaIn1.VName.Value)
Text(MediaIn1.XNormName.Value)
Text(MediaIn1.YNormName.Value)
Text(MediaIn1.ZNormName.Value)
Text(MediaIn1.XVelName.Value)
Text(MediaIn1.YVelName.Value)
Text(MediaIn1.XRevVelName.Value)
Text(MediaIn1.YRevVelName.Value)
Text(MediaIn1.XPosName.Value)
Text(MediaIn1.YPosName.Value)
Text(MediaIn1.ZPosName.Value)
Text(MediaIn1.XDispName.Value)
Text(MediaIn1.YDispName.Value)

MediaIn Loop Checkbox State:
Text(MediaIn1.Loop)

MediaIn Reverse Checkbox State:
Text(MediaIn1.Reverse)

User avatar
Chad
Fusionator
Posts: 2044
Joined: 10 years ago
Has thanked: 2 times
Been thanked: 8 times

Re: Rendering Text From Metadata Tags

#10

Unread post by Chad »

Would be nice to extend this to other contexts as well. From the MediaIn or MediaOut I should be able to get the track in Edit, the timeline, in/out points, transitions, effects, etc.. Should be able to pull the RAW settings from Color, too, as well as what ResolveFX tool settings were applied to the clip.

User avatar
renzhezhu1234
Posts: 39
Joined: 2 years ago
Contact:

Re: Rendering Text From Metadata Tags

#11

Unread post by renzhezhu1234 »

Hello! Where is this expressions? I cant see it.....Image
You do not have the required permissions to view the files attached to this post.