Replace multiple footage Items with Placeholder

What type of scripts do you need?

Moderator: byronnash

Post Reply
tergy
Posts: 7
Joined: May 14th, 2008, 12:39 pm

Hi,
Im working on a project that involve a repetitive task and I will be on that project for 2 more months.
There is no way to select multiple footage and perform a replace with placeholder for each. Only one at a time.

The Problem
I select footage items in the project window. Right-click. Replace with / PlaceHolder.
Then I need to change the resolution and frame rate. I leave the name and duration as is. And I do this 30 times a day.
A kind of batch replace with placeholder script would be helpful. Does a script already exist? My research have been unsuccessful.

The Story
This is part of a proxy workflow with multiple 3D passes. My project will be deliver in UHD 2160p 60fps. My after effects is set to final specs, but all 3D will be render at HD 1080p 30fps until final approval.
So I import all my 1080p 30fps 3D passes (thanks to Immigration script!), replace each pass with a place holder (keeping the name and duration of the original footage) changing resolution and frame rate to 2160p 60fps. This is tedious. Then relink each 1080p pass as proxy.
Looking for a way to streamline this.

Could someone help. Thanks a ton.

Last edited by tergy on April 7th, 2020, 6:01 pm, edited 1 time in total.
tergy
Posts: 7
Joined: May 14th, 2008, 12:39 pm

Ok, so this is my first attempt at scripting and this is mainly copy and paste from everywhere.
I still getting error for the variables item.name and item.duration, and even when i try some dummy name and duration... the script doesnt do anything after that.
Could someone help me debug this.
thanks.

Code: Select all

/// This script replace multiple footage item with a placeholder. Well... not yet!

/// project variables
var project = app.project;
var item = project.item.selected;

// placeholder variables and predefined values
var name = item.name;
var width = 3840;
var height = 2160;
var frameRate = 30;
var duration = item.duration;

// function to replace with placeholder
function(){
    app.beginUndoGroup("replace with place holder");
    
        for(var i = 1; i <= project.numItems; i++){
            if(project.item(i) instanceof FootageItem){
                project.item(i).replaceWithPlaceholder(name, width, height, frameRate, duration);
                }
            }

    app.endUndoGroup();
    alert("Successfully replace");
    }
    
User avatar
alexmunteanu
Posts: 8
Joined: April 11th, 2020, 9:27 pm

Hey,

The first issue is that `selected` property returns a Boolean. So it basically tells you if the specified item is selected or not.
Second: `app.project.item.selected` does not exist. You need to specify an item. E.g.: `app.project.item(1).selected`.
Third:you're using an anonymous function, but in this case you'll need to autorun it. E.g.: (function () { ... })()
Fourth (optional): Solids are also FootageItems, so you'll have to ignore them if the user accidentally selected solids too.

Here's a working sample (you can also select a bin and all Footage items inside it will be replaced, even if they are in other sub-bins):

Code: Select all


function runReplaceWithPlaceHolder ()
{
  if (app.project.selection.length > 0) // Process only if user selected some Project Panel items
  {
    app.beginUndoGroup('Replace with placeholder')

    var replaceCount = replaceWith_UHD_30fps_placeholder(app.project.selection);

    app.endUndoGroup();

    alert('Successfully replaced ' + replaceCount + ' items.', 'Done!', false);
  }
  else
  {
    alert('You must selected at least one Project Panel item', 'No items selected.', false);
  }
}

function replaceWith_UHD_30fps_placeholder (itemsArr, count)
{
  var
  curItem,
  /*
  replaceWithPlaceholder() requires parameter 1 (item name) to have 31 characters after the conversion
  So, we'll save each item's name and id so we can rename each of the placeholders afterwards
  */
  savedItems = {
    "names": [],
    "ids": []
  };

  if (typeof count === 'undefined') count = 0;

  for (var i = 0, iLen = itemsArr.length; i < iLen; i++)
  {
    curItem = itemsArr[i];

    if (curItem instanceof FootageItem && !(curItem.mainSource instanceof SolidSource))
    {
      count++;

      savedItems.names.push(curItem.name);
      savedItems.ids.push(curItem.id);

      curItem.replaceWithPlaceholder(curItem.name.substr(0,31), 3840, 2160, 30, curItem.duration);
    }
    else if (curItem instanceof FolderItem)
    {
      var binItems = [];

      for (var k = 1, kLen = curItem.numItems; k <= kLen; k++) binItems.push(curItem.item(k));

      count += replaceWith_UHD_30fps_placeholder(binItems, count);
    }
  }

  // Add initial items names
  for (var i = 0, iLen = savedItems.ids.length; i < iLen; i++)
  {
    app.project.itemByID(savedItems.ids[i]).name = savedItems.names[i]; // itemByID() introduced in CC2014
  }

  // Dump variables
  savedItems.names = [];
  savedItems.ids = [];
  curItem = null;

  return count;
}

runReplaceWithPlaceHolder();

You can also create a ScriptUI with a button, and dock it to your AE interface, so you don't have to manually run the script each time.
Let me know if you'll need help with re-importing the final renders.

Cheers!

tergy
Posts: 7
Joined: May 14th, 2008, 12:39 pm

Wow Alex. This is not novice.
I came up with something that kind do the job and that I could almost read entirely. I also added the function to swap the mainSouce to the Proxy in the same Go. But your script take care of several problems. One I had with the 31 characters limit; which is not uncommon with 3D passes. I try to insert the setProxy function into your script but I coud not even find what was the item selected variable to get the .file, if that make sense ?? I also now face the problem to make it work with either a file (setProxy) and a fileSequence (setProxyWithSequence).

This was my attempt... which kindda work ?? Please don't laugh!

Code: Select all

/// User Input values
            var width = parseFloat(prompt("Placeholder WIDTH ?"));
            var height = parseFloat(prompt("Placeholder HEIGHT ?"));
            var frameRate = parseFloat(prompt("Placeholder FRAME RATE ?"));

// Swap to proxy and replace with placeholder function
    app.beginUndoGroup("swap to proxy and replace with placeholder ");
               
    for (var i = 1; i <= app.project.numItems; i++){
        if (app.project.item(i).selected){
                    
            var name = app.project.item(i).name;
            var duration = app.project.item(i).duration;
            var file = app.project.item(i).file;
            
            app.project.item(i).setProxyWithSequence(file,false);
            app.project.item(i).replaceWithPlaceholder(name,width,height,frameRate,duration);

    }

    app.endUndoGroup();
    }

alert("Successfully replace with placeholders");
User avatar
alexmunteanu
Posts: 8
Joined: April 11th, 2020, 9:27 pm

I've made a few more changes besides the proxy part:

  • corrected/ improved item check

  • added the option to use the defaults w-3840, h-2160, fps-30 or insert new values

  • it can handle both file sequences and video files

Here you go (also attached):

Code: Select all


function runReplaceWithPlaceHolder ()
{
  if (app.project.selection.length > 0) // Process only if user selected some Project Panel items
  {
    app.beginUndoGroup('Swap to proxy and replace with placeholder');

    var
    options = {},
    replaceCount,
    confirmCustomOptions = confirm('Do you want to use other settings for the placeholders?\n\nDefaults:\n- width: 3840\n- height: 2160\n- fps: 30');

    if (confirmCustomOptions)
    {
      options.width = prompt('Width (leave blank for the default value):', '', 'Placeholder options');
      options.height = prompt('Height (leave blank for the default value):', '', 'Placeholder options');
      options.fps = prompt('Fps (leave blank for the default value):', '', 'Placeholder options');
    }

    replaceCount = replaceWithPlaceholderRecursive(app.project.selection, options);

    app.endUndoGroup();

    alert('Successfully replaced ' + replaceCount + ' items.', 'Done!', false);
  }
  else
  {
    alert('You must selected at least one Project Panel item', 'No items selected.', false);
  }
}

function replaceWithPlaceholderRecursive (itemsArr, options, count)
{
  var
  curItem,
  /*
  replaceWithPlaceholder() requires parameter 1 (item name) to have 31 characters after the conversion
  So, we'll save each item's name and id so we can rename each of the placeholders afterwards
  */
  savedItems = {
    "names": [],
    "ids": []
  };

  // Some `options` argument checks
  if (typeof options !== 'object' || options == null)
  {
    options = {
      "width": 3840,
      "height": 2160,
      "fps": 30
    };
  }
  else
  {
    if (!options.hasOwnProperty('width') || isNaN(parseInt(options.width))) options.width = 3840;
    if (!options.hasOwnProperty('height') || isNaN(parseInt(options.height))) options.height = 2160;
    if (!options.hasOwnProperty('fps') || isNaN(parseFloat(options.fps)) || options.fps < 1) options.fps = 30;
  }

  if (typeof count === 'undefined') count = 0;

  for (var i = 0, iLen = itemsArr.length; i < iLen; i++)
  {
    curItem = itemsArr[i];

    if (curItem instanceof FootageItem && curItem.mainSource instanceof FileSource && !(curItem.mainSource instanceof PlaceholderSource) && curItem.hasVideo)
    {
      count++;

      savedItems.names.push(curItem.name);
      savedItems.ids.push(curItem.id);

      if (isFileSequence(curItem)) curItem.setProxyWithSequence(curItem.mainSource.file, false);
      else curItem.setProxy(curItem.mainSource.file);

      curItem.replaceWithPlaceholder(curItem.name.substr(0, 31), parseInt(options.width), parseInt(options.height), options.fps, curItem.duration);
    }
    else if (curItem instanceof FolderItem)
    {
      var binItems = [];

      for (var k = 1, kLen = curItem.numItems; k <= kLen; k++) binItems.push(curItem.item(k));

      count += replaceWithPlaceholderRecursive(binItems, options, count);
    }
  }

  // Add initial items names
  for (var i = 0, iLen = savedItems.ids.length; i < iLen; i++)
  {
    app.project.itemByID(savedItems.ids[i]).name = savedItems.names[i]; // itemByID() introduced in CC2014
  }

  // Dump variables
  savedItems.names = [];
  savedItems.ids = [];
  curItem = null;

  return count;
}

function isFileSequence (item)
{
  if (item instanceof FootageItem && item.mainSource instanceof FileSource && !(item.mainSource.isStill) && item.hasVideo)
  {
    var extname = item.mainSource.file.fsName.split('.').pop();

    return extname.match(new RegExp("(ai|bmp|bw|cin|cr2|crw|dcr|dng|dib|dpx|eps|erf|exr|gif|hdr|ico|icb|iff|jpe|jpeg|jpg|mos|mrw|nef|orf|pbm|pef|pct|pcx|pdf|pic|pict|png|ps|psd|pxr|raf|raw|rgb|rgbe|rla|rle|rpf|sgi|srf|tdi|tga|tif|tiff|vda|vst|x3f|xyze)", "i")) !== null;
  }

  return false;
}

runReplaceWithPlaceHolder();

Attachments
replaceWithPlaceholder_v02.jsx
(3.77 KiB) Downloaded 637 times
User avatar
alexmunteanu
Posts: 8
Joined: April 11th, 2020, 9:27 pm

P.S.: You don't have to check all the project items. app.project.selection gives you an array of all the selected items. In bigger projects your script will be slower.

tergy
Posts: 7
Joined: May 14th, 2008, 12:39 pm

Hey Alex. Thank you so much. This is rock solid. This is now part of my UI.
Playing with your script, I might have found something... Once you undo, you cannot reRun the script. It will replace 0 footage. I dont know? Might be a limitation. The last function seems out of the Undo group...
Either way, I send you all my love and some Lysol.
Stay home, stay safe!

User avatar
alexmunteanu
Posts: 8
Joined: April 11th, 2020, 9:27 pm

Yes... the previously replaced items will remain as a placeholder somehow, after an Undo.
Will check into that a bit later and send you an updated script.

Cheers! Take care!

User avatar
alexmunteanu
Posts: 8
Joined: April 11th, 2020, 9:27 pm

In you specific case there is a workaround.
replaceWithPlaceholder() method changes the mainSource property and it seems that an Undo, even if apparently you can see the correct file, still leaves mainSource property of type PlaceholderSource.

To avoid this, I simply replaced the item with a solid. :)

Here's the full script (also attached):

Code: Select all


function runReplaceWithPlaceHolder ()
{
  if (app.project.selection.length > 0) // Process only if user selected some Project Panel items
  {
    app.beginUndoGroup('Swap to proxy and replace with placeholder');

    var
    options = {},
    replaceCount,
    confirmCustomOptions = confirm('Do you want to use other settings for the placeholders?\n\nDefaults:\n- width: 3840\n- height: 2160\n- fps: 30');

    if (confirmCustomOptions)
    {
      options.width = prompt('Width (leave blank for the default value):', '', 'Placeholder options');
      options.height = prompt('Height (leave blank for the default value):', '', 'Placeholder options');
      options.fps = prompt('Fps (leave blank for the default value):', '', 'Placeholder options');
    }

    replaceCount = replaceWithPlaceholderRecursive(app.project.selection, options);

    app.endUndoGroup();

    alert('Successfully replaced ' + replaceCount + ' item' + String(replaceCount > 1 ? 's' : '') + '.', 'Done!', false);
  }
  else
  {
    alert('You must selected at least one Project Panel item', 'No items selected.', false);
  }
}

function replaceWithPlaceholderRecursive (itemsArr, options, count)
{
  var
  curItem,
  /*
  replaceWithPlaceholder() requires parameter 1 (item name) to have 31 characters after the conversion
  So, we'll save each item's name and id so we can rename each of the placeholders afterwards
  */
  savedItems = {
    "names": [],
    "ids": []
  };

  // Some `options` argument checks
  if (typeof options !== 'object' || options == null)
  {
    options = {
      "width": 3840,
      "height": 2160,
      "fps": 30
    };
  }
  else
  {
    if (!options.hasOwnProperty('width') || isNaN(parseInt(options.width))) options.width = 3840;
    if (!options.hasOwnProperty('height') || isNaN(parseInt(options.height))) options.height = 2160;
    if (!options.hasOwnProperty('fps') || isNaN(parseFloat(options.fps)) || options.fps < 1) options.fps = 30;
  }

  if (typeof count === 'undefined') count = 0;

  for (var i = 0, iLen = itemsArr.length; i < iLen; i++)
  {
    curItem = itemsArr[i];

    if (curItem instanceof FootageItem && curItem.mainSource instanceof FileSource && !(curItem.mainSource instanceof PlaceholderSource) && curItem.hasVideo)
    {
      count++;

      savedItems.names.push(curItem.name);
      savedItems.ids.push(curItem.id);

      if (isFileSequence(curItem)) curItem.setProxyWithSequence(curItem.mainSource.file, false);
      else curItem.setProxy(curItem.mainSource.file);

      // curItem.replaceWithPlaceholder(curItem.name.substr(0, 31), parseInt(options.width), parseInt(options.height), options.fps, curItem.duration);
      curItem.replaceWithSolid([0.21167242527008, 0.80082058906555, 0.87058824300766], curItem.name.substr(0, 31), parseInt(options.width), parseInt(options.height), curItem.pixelAspect);
    }
    else if (curItem instanceof FolderItem)
    {
      var binItems = [];

      for (var k = 1, kLen = curItem.numItems; k <= kLen; k++) binItems.push(curItem.item(k));

      count += replaceWithPlaceholderRecursive(binItems, options, count);
    }
  }

  // Add initial items names
  for (var i = 0, iLen = savedItems.ids.length; i < iLen; i++)
  {
    app.project.itemByID(savedItems.ids[i]).name = savedItems.names[i]; // itemByID() introduced in CC2014
  }

  // Dump variables
  savedItems.names = [];
  savedItems.ids = [];
  curItem = null;

  return count;
}

function isFileSequence (item)
{
  if (item instanceof FootageItem && item.mainSource instanceof FileSource && !(item.mainSource.isStill) && item.hasVideo)
  {
    var extname = item.mainSource.file.fsName.split('.').pop();

    return extname.match(new RegExp("(ai|bmp|bw|cin|cr2|crw|dcr|dng|dib|dpx|eps|erf|exr|gif|hdr|ico|icb|iff|jpe|jpeg|jpg|mos|mrw|nef|orf|pbm|pef|pct|pcx|pdf|pic|pict|png|ps|psd|pxr|raf|raw|rgb|rgbe|rla|rle|rpf|sgi|srf|tdi|tga|tif|tiff|vda|vst|x3f|xyze)", "i")) !== null;
  }

  return false;
}

runReplaceWithPlaceHolder();

Cheers!

Attachments
replaceWithPlaceholder_v03.jsx
(4 KiB) Downloaded 629 times
tergy
Posts: 7
Joined: May 14th, 2008, 12:39 pm

Tested in production for 2 days.
Flawless.
Cheers Alex!

User avatar
alexmunteanu
Posts: 8
Joined: April 11th, 2020, 9:27 pm

Glad I could help!

Post Reply