This one is a real scripting zinger.
I have a macro that deletes all fades and heals separation. Easy enough. But what I would really like is a macro that determines whether a fade is a separation or an actual edit, and if it is an edit then it leaves the original fade as-is -- if it is a separation, it deletes the fade and heals the separation.
Note: I know how to batch create fades but I'm trying to keep the original fades where heal separation isn't healing a separation.
A little context: I am a music editor and I often get AAFs from picture editors with cuts all over the music tracks. However, 80% of these aren't actual music edits -- they are inadvertent and unnecessary cuts made while they were editing the video. Nonetheless, a lot of picture editors will auto-apply fades across their music regions, which means the AAF I'm getting is riddled with "fake" fades/edits. So I end up going through each fade, one-by-one, deleting the fade and healing separation (which unites the clips again if it's not a true edit). When I come upon a clip in which the heal separation does not unite the clips, I know that this was an actual music edit performed by the picture editor, and I know to leave the fade alone for now.
I'd like to automate this process for an entire selection.
The psuedocode for the macro I'd like is something like this:
const fades = selectAllFadesFromSelectionAndMakeArray()
fades.forEach(fade => {
if (batch fade dialog is 'fade in' || 'fade out') {
break
} else {
deletefade()
healseparation()
if (a cut still remains i.e. there are still two separate clips where fade once existed) {
undo deletefade
}
}
}
I realize creating an array of fades and looping through them might be nearly impossible, and that determining whether a deleted fade leaves two clips or one might also be impossible, but I wanted to check in here. There might be other methods for achieving this as well -- like shift+tabbing through each object, determining whether it is a fade or a clip (is "delete fades" menu option available), if it is a fade then delete fade, etc etc
- OOwen Granich-Young @Owen_Granich_Young
Hey @AMPM101 seemed like a fun thing to give a whirl, here you go.
/// Menu Click Items const deleteFade = () => sf.ui.proTools.menuClick({ menuPath: [`Edit`, `Fades`, `Delete`] }) const healSeperation = () => sf.ui.proTools.menuClick({ menuPath: [`Edit`, `Heal Separation`] }) const undoFadeDelete = () => sf.ui.proTools.menuClick({ menuPath: [`Edit`, `Undo Delete`] }) /// Main Function function bulkHealFades() { deleteFade(); if (!sf.ui.proTools.getMenuItem(`Edit`, `Heal Separation`).isEnabled) { undoFadeDelete(); } else { healSeperation(); } } // Do to all Clips sf.ui.proTools.clipDoForEachSelectedClip({ action: bulkHealFades });
SO this script is 89% there, there's at least one funky thing I've found that I'm not sure how to fix if there's not a FADE IN on the start of any of the clips the script will fail. Hoping someone can chime in suggesting an easy fix. Here it is in action.
Ran it on a AAF piece of music, you were not kidding how many times a picture editor does that!
Bests,
OwenONCE AGAIN : Script will Fail if there's no FADE IN on any of the clips selected.
- AAMPM101 @AMPM101
Incredible! Such quick work!
I'm gonna see if I can tackle the fade-in issue. Obviously the problem is that we're trying to undoFadeDelete when Undo Delete doesn't exist in the undo menu because you haven't deleted a fade (since a fade didn't exist). And it seems rather difficult to simply add a temporary fade-in and immediately delete it.
The only other thing is I get an error upon completion saying it can't select next full clip inside selection.
I'll try to work on it from here. Thanks again!
- OOwen Granich-Young @Owen_Granich_Young
Had an aHa! Pretty sure this fixes it :
/// Menu Click Items const deleteFade = () => sf.ui.proTools.menuClick({ menuPath: [`Edit`, `Fades`, `Delete`] }) const healSeperation = () => sf.ui.proTools.menuClick({ menuPath: [`Edit`, `Heal Separation`] }) const undoFadeDelete = () => sf.ui.proTools.menuClick({ menuPath: [`Edit`, `Undo Delete`] }) /// Main Function function bulkHealFades() { deleteFade(); if (!sf.ui.proTools.getMenuItem(`Edit`, `Heal Separation`).isEnabled) { if (sf.ui.proTools.getMenuItem(`Edit`, `Undo Delete`).exists) { undoFadeDelete(); } } else { healSeperation(); } } // Do to all Clips sf.ui.proTools.clipDoForEachSelectedClip({ action: bulkHealFades });
- AAMPM101 @AMPM101
Yep I believe this works - my only hesitation with this is that I think if I had just deleted a separate fade prior to running this script, it will undo that action instead of proceeding as expected.
- OOwen Granich-Young @Owen_Granich_Young
Ha, yep, I imagine that would happen lol.
- OOwen Granich-Young @Owen_Granich_Young
Updated with a hack. Edit Copy first :P
/// Menu Click Items const deleteFade = () => sf.ui.proTools.menuClick({ menuPath: [`Edit`, `Fades`, `Delete`] }) const healSeperation = () => sf.ui.proTools.menuClick({ menuPath: [`Edit`, `Heal Separation`] }) const undoFadeDelete = () => sf.ui.proTools.menuClick({ menuPath: [`Edit`, `Undo Delete`] }) const copy = () => sf.ui.proTools.menuClick({ menuPath: [`Edit`, `Copy`] }) /// For Each Clip Action function bulkHealFades() { deleteFade(); if (!sf.ui.proTools.getMenuItem(`Edit`, `Heal Separation`).isEnabled) { if (sf.ui.proTools.getMenuItem(`Edit`, `Undo Delete`).exists) { undoFadeDelete() } } else { healSeperation(); } } function main() { // Hack to Clear the Undo copy(); // Do to all Clips sf.ui.proTools.clipDoForEachSelectedClip({ action: bulkHealFades }); } main();
- AAMPM101 @AMPM101
Yesss thank you so much, Owen!
- OOwen Granich-Young @Owen_Granich_Young
Hey hey long time, I've been doing some music editing this week and built this quicky to help myself since I see a lot of times the editors are doing these moves to set vol automations. This is just the 'one off' button but I'm sure you could put it into the above package too. It's pretty nifty to not have to deal with the cilp gain auto after.
if (sf.ui.proTools.getMenuItem("Edit", "Fades", "Delete").exists) { sf.ui.proTools.menuClick({ menuPath: ["Edit", "Fades", "Delete"], }); sf.keyboard.press({ keys: "n", repetitions: 2, fast: true, }); } sf.ui.proTools.menuClick({ menuPath: ["Edit", "Heal Separation"], }); sf.ui.proTools.menuClick({ menuPath: ["Edit","Cut Special","Clip Gain"], }); sf.ui.proTools.clipGainCreateBoundaryBreakpoints();
Take it for a spin, should be easy enough to drop it into the above script too.
- OOwen Granich-Young @Owen_Granich_Young
Of course as soon as I post this I go ... but it could be better.
/// Menu Click Items const deleteFade = () => sf.ui.proTools.menuClick({ menuPath: [`Edit`, `Fades`, `Delete`] }) const healSeperation = () => sf.ui.proTools.menuClick({ menuPath: [`Edit`, `Heal Separation`] }) const undoFadeDelete = () => sf.ui.proTools.menuClick({ menuPath: [`Edit`, `Undo Delete`] }) const cutClipGain = () => sf.ui.proTools.menuClick({ menuPath: ["Edit", "Cut Special", "Clip Gain"], }); /// Main Function function tryToHeal() { if (sf.ui.proTools.getMenuItem("Edit", "Fades", "Delete").exists) { sf.ui.proTools.menuClick({ menuPath: ["Edit", "Fades", "Delete"], }); sf.keyboard.press({ keys: "n", repetitions: 2, fast: true, }); } if (!sf.ui.proTools.getMenuItem(`Edit`, `Heal Separation`).isEnabled) { undoFadeDelete(); } else { healSeperation(); cutClipGain(); sf.ui.proTools.clipGainCreateBoundaryBreakpoints(); } } tryToHeal()
Chris Shaw @Chris_Shaw2024-06-12 19:46:19.072Z2024-06-12 20:35:56.117Z
This is pretty cool.
The only thing I noticed is that you declare but never use thedeleteFade()
function.Replace lines 12-14 with it.
You should also put lines 2-5 inside the
tryToHeal()
function so that it's all self contained and re-useable (assuming that this wasn't copied from a much larger script).To make it more towards @Kitch 's concept of reading a script like a table of contents I refactored it more:
function tryToHeal() { /// Menu Click Items const deleteFades = () => sf.ui.proTools.menuClick({ menuPath: [`Edit`, `Fades`, `Delete`] }) const healSeperation = () => sf.ui.proTools.menuClick({ menuPath: [`Edit`, `Heal Separation`] }) const undoFadeDelete = () => sf.ui.proTools.menuClick({ menuPath: [`Edit`, `Undo Delete`] }) const cutClipGain = () => sf.ui.proTools.menuClick({ menuPath: ["Edit", "Cut Special", "Clip Gain"], }); // Get menu states const fadesExist = sf.ui.proTools.getMenuItem("Edit", "Fades", "Delete").exists; const cantHealSeparation = () => !sf.ui.proTools.getMenuItem(`Edit`, `Heal Separation`).isEnabled; function refreshPtMenus() { sf.keyboard.press({ keys: "n", repetitions: 2, fast: true, }) } // Main if (fadesExist) { deleteFades() refreshPtMenus() } if (cantHealSeparation()) { undoFadeDelete(); } else { healSeperation(); cutClipGain(); sf.ui.proTools.clipGainCreateBoundaryBreakpoints(); } } tryToHeal()
- OOwen Granich-Young @Owen_Granich_Young
haha good catch, i was just moving fast and comping the two scripts together lol.
although I'm not sure you can get the menu state on heal separation at the beginning, you might have to get it after delete fades... ohhhhh i see you made it a active one there COOOOOOL Never thought of that.