Skip to main content
bluebeezle
Inspiring
June 8, 2016
질문

How to get the starting frame of a layer in a Video Timeline

  • June 8, 2016
  • 3 답변들
  • 1285 조회

I'm looking to perform an action on every layer in a PSD. Unfortunately, these PSDs also have video timelines, so if I cycle through the layers and any of the layers don't exist on the frame that the playhead is currently at, the action won't be performed on those frames

so basically, I need a way to move the playhead to the beginning frame of every layer it performs this action on. anyone know of a way to retrieve that information from a layer?

thanks.

이 주제는 답변이 닫혔습니다.

3 답변

Participating Frequently
November 7, 2018

I see you posted this a few times bluebeezle​  

I have the same question and have found the best answer from r-bin here:  Is there any way to move the time indicator of timeline to first frame of the layer using Photoshop script?

His function steps through the document timeline frame by frame, checks if the Transform property of the layer is enabled.  If not enabled... then go to next frame... As soon as it becomes enabled, then you know you have the start frame of that layer.  The same thing can be applied backwards to find the end frame of the layer.  Its a great trick!   But unfortunately too slow for my needs. So like you, I'm searching for a better way to extract the timeline start frame of a layer.  If you can get that start frame, then you can just run a "goToTime"  function to put the playhead at this point.  MUCH faster.   but...  I can't seem to extract a layer's start time.

Any ideas out there? 

(To put the playhead at a set point in the timeline.)

function goToTime(seconds, frames, framerate) {

        var ref33 = new ActionReference();

        ref33.putProperty( charIDToTypeID('Prpr'), stringIDToTypeID('time') );

        ref33.putClass( stringIDToTypeID('timeline') );

        var desc69 = new ActionDescriptor();

        desc69.putReference( charIDToTypeID('null'), ref33 );

        var desc70 = new ActionDescriptor();

        desc70.putInteger( stringIDToTypeID('seconds'), Seconds );

        desc70.putInteger( stringIDToTypeID('frame'),Frame );

        desc70.putDouble( stringIDToTypeID('frameRate'), framerate );

        desc69.putObject( charIDToTypeID('T   '), stringIDToTypeID('timecode'), desc70 );

        executeAction( charIDToTypeID('setd'), desc69, DialogModes.NO );

}

JJMack
Community Expert
Community Expert
June 8, 2016

Do you want to process just the first layer or all the layers?

For example a script like this one would process all layers in all groups.

try { if(app.documents.length>0){ app.activeDocument.suspendHistory ('Trim Layers', 'TrimLayers();') } }

catch(e) { alert( e + ': on line '  + e.line );}

function TrimLayers() {

  var orig_ruler_units = app.preferences.rulerUnits; // save user ruler units 

  var orig_display_dialogs = app.displayDialogs; // Save users Displat Doalogs

  app.preferences.rulerUnits = Units.PIXELS; // set the ruler units to PIXELS

  app.displayDialogs = DialogModes.NO; // Set Dialogs off

  var doc  = app.activeDocument; // get current document

  var saveactiveLayer = doc.activeLayer; // save activeLayer

  doc.selection.deselect(); // make sure ther is no active selection

  try {processArtLayers(doc);} // Process all Layers

  catch(e) { alert( e + ': on line '  + e.line );} // some unexpected error

  doc.selection.deselect(); // make sure ther is no active selection

  try { doc.activeLayer = saveactiveLayer;} // restore activeLayer

  catch(e) { } // may have been deleted

  app.displayDialogs = orig_display_dialogs; // Reset display dialogs

  app.preferences.rulerUnits = orig_ruler_units; // reset units to original settings

}

///////////////////////////////////////////////////////////////////////////////

// Function: processsArtLayers

// Input: document or layer set

///////////////////////////////////////////////////////////////////////////////

function processArtLayers(obj) {

    for(var i = obj.artLayers.length-1;0<= i; i--) { // for Layers

  try {TrimLayer(obj.artLayers); } // process Art Layer

  catch(e) {} // don't stop on errors

  }

    for(var i=obj.layerSets.length-1;0<=i;i--) { // for Layer sets

  try {processArtLayers(obj.layerSets);} // process Layer Set

  catch(e) {} // don't stop on errors

  }

}

function TrimLayer(layer) {

  // do nothing if layer is background or locked 

  if(layer.isBackgroundLayer || layer.allLocked || layer.pixelsLocked || layer.positionLocked || layer.transparentPixelsLocked ) return;

  //if(layer.protectArtboardAutonest ) return;

  if( layer.kind != LayerKind.NORMAL) return; // do nothing if not normal layer  

  app.activeDocument.activeLayer = layer; // target layer

  var bounds = layer.bounds; // layer bounds 

  var LWidth = bounds[2]-bounds[0]; // layer width 

  var LHeight = bounds[3]-bounds[1]; // layer height

  var Lleft =  bounds[0]; // layer left x

  var Lright =  bounds[2]; // layer right x

  var Ltop =  bounds[1]; // layer top y

  var Lbottom =  bounds[3]; // layer bottom y

  var tlrnc = 20; // tolerance

  i=0.6; // fudge factor

  try {

  MagicWand((Lleft+i), (Ltop+i), tlrnc); // top left

  app.activeDocument.selection.clear(); // clear

  }

  catch(e) {}; // do nothing

  try {

  MagicWand((Lleft+LWidth/2), (Ltop+i), tlrnc); // top middle

  app.activeDocument.selection.clear(); // clear

  }

  catch(e) {};   // do nothing

  try {

  MagicWand((Lright-i), (Ltop+i), tlrnc); // top right

  app.activeDocument.selection.clear(); // clear

  }

  catch(e) {}; // do nothing

  try {

  MagicWand((Lleft+i), (Ltop+LHeight/2), tlrnc); // middle left

  app.activeDocument.selection.clear(); // clear

  }

  catch(e) {}; // do nothing

  try {

  MagicWand((Lright-i), (Ltop+LHeight/2), tlrnc); // middle right

  app.activeDocument.selection.clear(); // clear

  }

  catch(e) {}; // do nothing

  try {

  MagicWand((Lleft+i), (Lbottom-i), tlrnc); // bottom left

  app.activeDocument.selection.clear(); // clear

  }

  catch(e) {};   // do nothing

  try {

  MagicWand((Lleft+LWidth/2), (Lbottom-i), tlrnc); // bottom middle

  app.activeDocument.selection.clear(); // clear

  }

  catch(e) {}; // do nothing

  try {

  MagicWand((Lright-i), (Lbottom-i), tlrnc); // bottom right

  app.activeDocument.selection.clear(); // clear

  }

  catch(e) {};   // do nothing

}

function MagicWand(x,y,tolerance) {

var idsetd = charIDToTypeID( "setd" );

    var desc106 = new ActionDescriptor();

    var idnull = charIDToTypeID( "null" );

        var ref60 = new ActionReference();

        var idChnl = charIDToTypeID( "Chnl" );

        var idfsel = charIDToTypeID( "fsel" );

        ref60.putProperty( idChnl, idfsel );

    desc106.putReference( idnull, ref60 );

    var idT = charIDToTypeID( "T   " );

        var desc107 = new ActionDescriptor();

        var idHrzn = charIDToTypeID( "Hrzn" );

        var idPxl = charIDToTypeID( "#Pxl" );

        desc107.putUnitDouble( idHrzn, idPxl, x );

        var idVrtc = charIDToTypeID( "Vrtc" );

        var idPxl = charIDToTypeID( "#Pxl" );

        desc107.putUnitDouble( idVrtc, idPxl, y );

    var idPnt = charIDToTypeID( "Pnt " );

    desc106.putObject( idT, idPnt, desc107 );

    var idTlrn = charIDToTypeID( "Tlrn" );

    desc106.putInteger( idTlrn, tolerance );

    var idAntA = charIDToTypeID( "AntA" );

    desc106.putBoolean( idAntA, true );

executeAction( idsetd, desc106, DialogModes.NO );

}

JJMack
bluebeezle
bluebeezle작성자
Inspiring
June 9, 2016

all the layers. but I can't process a layer if the layer doesn't exist where the playhead currently is, so I have to move the playhead to each layer's "in" point on the timeline to process them.

I might be mistaken since I don't know the language yet, but I don't see anything in the code you posted that would accommodate that timeline issue.

JJMack
Community Expert
Community Expert
June 9, 2016

If you have the Scriptlistener Plug-in installed see if anything get recorded when you step the play head to the next frame, If it does you may be able to create a function to step the play head.  I would not expect there would be any DOM interface for any Photoshop Video. Searching Photoshop Scripting documentation for Timeline returns no hits.

I just tried in a frame animation I clicked the next frame icon three in the timeline the same thing was recorded for each. I would put the execute in a try catch  in case it fails or come the the end of the animation however it may wrap so you will most likely need to know how many frames you have.  Also a frame can be a composite of several layers. If the is the case you may need to process the visible layers for each frame and you may also neet to targer those layers. You may need position the head the frame 1 before executing the script.  You know more about your document configuration then I do.  You may need the program that knowledge into your script.  You did not show what your timeline looks like. The scriplistener code for the three next frame clicks.

 

// =======================================================

var idanimationFrameActivate = stringIDToTypeID( "animationFrameActivate" );

    var desc4 = new ActionDescriptor();

    var idnull = charIDToTypeID( "null" );

        var ref1 = new ActionReference();

        var idanimationFrameClass = stringIDToTypeID( "animationFrameClass" );

        var idOrdn = charIDToTypeID( "Ordn" );

        var idNxt = charIDToTypeID( "Nxt " );

        ref1.putEnumerated( idanimationFrameClass, idOrdn, idNxt );

    desc4.putReference( idnull, ref1 );

executeAction( idanimationFrameActivate, desc4, DialogModes.NO );

// =======================================================

var idanimationFrameActivate = stringIDToTypeID( "animationFrameActivate" );

    var desc5 = new ActionDescriptor();

    var idnull = charIDToTypeID( "null" );

        var ref2 = new ActionReference();

        var idanimationFrameClass = stringIDToTypeID( "animationFrameClass" );

        var idOrdn = charIDToTypeID( "Ordn" );

        var idNxt = charIDToTypeID( "Nxt " );

        ref2.putEnumerated( idanimationFrameClass, idOrdn, idNxt );

    desc5.putReference( idnull, ref2 );

executeAction( idanimationFrameActivate, desc5, DialogModes.NO );

// =======================================================

var idanimationFrameActivate = stringIDToTypeID( "animationFrameActivate" );

    var desc6 = new ActionDescriptor();

    var idnull = charIDToTypeID( "null" );

        var ref3 = new ActionReference();

        var idanimationFrameClass = stringIDToTypeID( "animationFrameClass" );

        var idOrdn = charIDToTypeID( "Ordn" );

        var idNxt = charIDToTypeID( "Nxt " );

        ref3.putEnumerated( idanimationFrameClass, idOrdn, idNxt );

    desc6.putReference( idnull, ref3 );

executeAction( idanimationFrameActivate, desc6, DialogModes.NO );

JJMack
JJMack
Community Expert
Community Expert
June 8, 2016

To do what you want to do I believe you would need  to flatten your video layer into frame layers and process all the document layer through your action,  Photoshop only supports up to 8,000 layers so you could process videos that are less the 6 minuets in length. I believe you will also loose the Audio.  If you really want to process video you should us video editing applications like Adobe Premier Pro and After Effects.   Photoshop only supports some basic video editing features like trim, splice, transition and can add title and credit  frames.. I think you can also convert a video layer into a smart object layer and use smart filters on the video.

JJMack
bluebeezle
bluebeezle작성자
Inspiring
June 8, 2016

none of the layers are video groups - they're all just layers that exist during various parts of the timeline, inside normal layer groups. no audio involved, either. what code do I need to get the start frame of each layer?