Import Session Data - wait for 'restoring tracks' progress bar
Hi,
I have a script designed to import Pro Tools sessions one by one into a master session. I use the SDK command to do this, however, the script does not seem to wait for each import to complete before moving to the next, meaning the sessions often get imported in the wrong order.
I would like to wait until the 'restoring tracks' progress bar appears and then disappears, but I can't seem to pick this UI element no matter what I do. Does anyone know how I can wait for this (shown below) to appear then disappear?

Thanks,
Fergus
- Kitch Membery @Kitch2025-01-28 22:24:23.154Z
To have SoundFlow wait for these kinds of windows in Pro Tools, we use the "Wait for No Modals" method. Like this...
sf.ui.proTools.waitForNoModals();
Sometimes, however if the modals window take a moment to appear, you may need a small wait before the "Wait for No Modals" method. Like this...
sf.wait({intervalMs:200}); sf.ui.proTools.waitForNoModals();
I hope that helps. :-)
Pinewood Studios @Pinewood_Studios
Hey Kitch,
Thanks for confirming. In this case, the modal window doesn't seem to pop up until after the entire script has run, with up to 6 sf.app.proTools.importSessionData() commands already called (confirmed with logging). So it'd have to be quite a long wait I imagine.
Out of interest, are there any plans at all to implement a 'waitForModals()' method? That'd help in this exact scenario, at the least.
Best wishes, and thanks for your response.
Fergus
Kitch Membery @Kitch2025-01-29 18:14:20.480Z
Hi Fergus,
Can you paste the script here in the thread so I can troubleshoot the issue further?
Thanks in advance. :-)
Pinewood Studios @Pinewood_Studios
Of course. No judgement please... very much a WIP, ha!
// PRO TOOLS START SCRIPT if (!sf.ui.proTools.isRunning) throw 'Pro Tools is not running'; sf.ui.proTools.appActivateMainWindow(); const enableLogging = "YES" sf.interaction.displayDialog({ prompt: "This script will search the directory above the one containing currently open session, looking for reels 2 onwards. You should already have open a session containing reel 1 open. If you don't, please do this then re-run this script", buttons: ["Proceed","Cancel"], defaultButton: "Proceed", }); let numReels = Number(prompt("How many reels are there?")); if (isNaN(numReels)) throw 0; const currentSessionPath = sf.app.proTools.getSessionPath().sessionPath.replace(/:/g, "/"); //const currentSessionDirectory = currentSessionPath.substring(0, currentSessionPath.lastIndexOf('/')); const mainDirectory = currentSessionPath.substring(0, currentSessionPath.lastIndexOf('/', currentSessionPath.lastIndexOf('/') - 1)); const possibleDirectories = sf.file.directoryGetDirectories({path: mainDirectory}).paths if(enableLogging.toLowerCase() === "yes"){ log(possibleDirectories); } let sessionToImportPath; let allSessionPaths; let firstMatchingReelSessionPath; let directoryToTest; const possibleDirectoryNames = [ "R", "R0", "REEL", "R-", "R_", "R-0", "R_0" ]; for (let index = 2 ; index<numReels+1; index++ ){ for (const directoryName of possibleDirectoryNames) { if(enableLogging.toLowerCase() === "yes"){ log(`Trying combination: ${directoryName}${index}`); }; //sf.wait({intervalMs: 100}); directoryToTest = possibleDirectories.filter(directory => directory.includes(`${directoryName}${index}`))[0] if(!directoryToTest){ if(enableLogging.toLowerCase() === "yes"){ log(`No possibilities found for combination ${directoryName}${index}`); } //Move to next possibility continue; } //log("Testing " + directoryToTest); if (sf.file.directoryExists({path: directoryToTest}).exists){ if(enableLogging.toLowerCase() === "yes"){ log("Successfully found: " + directoryToTest) } sessionToImportPath = directoryToTest; break; } } allSessionPaths = sf.file.directoryGetFiles({ path: sessionToImportPath, searchPattern: '*.ptx' }).paths; firstMatchingReelSessionPath = allSessionPaths[0]; if (!firstMatchingReelSessionPath) throw `No session found in directory: ${sessionToImportPath}`; if(enableLogging.toLowerCase() === "yes"){ log(`Importing: ${firstMatchingReelSessionPath}`); } sf.app.proTools.importSessionData({ sessionPath: firstMatchingReelSessionPath, audioMediaOptions: "LinkToSourceAudio", audioHandleSize: 2000, videoMediaOptions: "LinkToSourceVideo", trackMatchOptions: "MatchTracks", playlistOptions: "ImportOverlayNewOnExistingPlaylists", importClipGain: true, importClipsAndMedia: true, importVolumeAutomation: false, timecodeMappingUnits: "MaintainAbsoluteTimeCodeValues", timecodeMappingStartTime: "00:00:00:00", adjustSessionStartTimeToMatchSource: false, }); //WE NEED TO WAIT HERE FOR THE FIRST REEL TO IMPORT BEFORE MOVING ON //WAIT FOR PROGRESS BAR TO GO AWAY sf.ui.proTools.waitForNoModals(); if(enableLogging.toLowerCase() === "yes"){ log("All Modals are closed"); } }
Kitch Membery @Kitch2025-01-30 19:29:44.142Z
Hi Fergus,
Did you try it with a small wait after the import and before the
sf.ui.proTools.waitForNoModals()
method?This may require a little setup on my side to test, so I'll endeavor to look into it early next week. :-)
Pinewood Studios @Pinewood_Studios
I didn't try that yet no, sorry! I'll try and get round to it next week.
If you're going to set up to test this (and I really appreciate it!) it's worth noting that it seems to work fine on smaller sessions. But bigger sessions including automation, lots of tracks, files etc, seem to import in the wrong order.
Thanks again!
Fergus
Pinewood Studios @Pinewood_Studios
Hey again Kitch, I got to test this today after all.
A 1000ms sf.wait does actually seem to help things! Obviously the wait time is a bit of a guessing game... I do wonder if a waitForModals() method would be a bit more robust. Would love to know your thoughts.. I'm no developer!
Fergus
Kitch Membery @Kitch2025-01-31 22:28:45.581Z
Hi Fergus?
I'll dig into this next week.
My thought is that the script might require an
sf.waitFor()
method with a custom callback to see when the action has been completed.Is there something that happens to the session that proves the import has been successful? Working this out will be the key to finding a robust solution rather than an arbitrary wait.
Pinewood Studios @Pinewood_Studios
I suppose given the purpose of this specific script, we know the import has been successful if there are additional clips on the timeline... or perhaps additional automation nodes if there are no clips to import. It's a difficult one I guess...
It is worth nothing though, that so far (and I've run the script a lot today) the 1 second arbitrary wait, followed by the waitForNoModals() method is flawless, so I really appreciate that tip from you.
Fergus
// PRO TOOLS START SCRIPT if (!sf.ui.proTools.isRunning) throw 'Pro Tools is not running'; sf.ui.proTools.appActivateMainWindow(); const enableLogging = "NO" sf.interaction.displayDialog({ prompt: "This script will search the directory above the one containing currently open session, looking for reels 2 onwards. You should already have open a session containing reel 1 open. If you don't, please do this then re-run this script", buttons: ["Proceed","Cancel"], defaultButton: "Proceed", }); let numReels = Number(prompt("How many reels are there?")); if (isNaN(numReels)) throw 0; const currentSessionPath = sf.app.proTools.getSessionPath().sessionPath.replace(/:/g, "/"); //const currentSessionDirectory = currentSessionPath.substring(0, currentSessionPath.lastIndexOf('/')); const mainDirectory = currentSessionPath.substring(0, currentSessionPath.lastIndexOf('/', currentSessionPath.lastIndexOf('/') - 1)); const possibleDirectories = sf.file.directoryGetDirectories({path: mainDirectory}).paths if(enableLogging.toLowerCase() === "yes"){ log(possibleDirectories); } let sessionToImportPath; let allSessionPaths; let firstMatchingReelSessionPath; let directoryToTest; const possibleDirectoryNames = [ "R", "R0", "REEL", "R-", "R_", "R-0", "R_0" ]; for (let index = 2 ; index<numReels+1; index++ ){ if(enableLogging.toLowerCase() === "yes"){ log("Resetting Variables.."); } //Reset the paths sessionToImportPath = null firstMatchingReelSessionPath = null allSessionPaths = null for (const directoryName of possibleDirectoryNames) { if(enableLogging.toLowerCase() === "yes"){ log(`Trying combination: ${directoryName}${index}`); }; //sf.wait({intervalMs: 100}); directoryToTest = possibleDirectories.filter(directory => directory.includes(`${directoryName}${index}`))[0] if(!directoryToTest){ if(enableLogging.toLowerCase() === "yes"){ log(`No possibilities found for combination ${directoryName}${index}`); } //Move to next possibility continue; } //log("Testing " + directoryToTest); if (sf.file.directoryExists({path: directoryToTest}).exists){ if(enableLogging.toLowerCase() === "yes"){ log("Successfully found: " + directoryToTest) } sessionToImportPath = directoryToTest; break; } } //If we found a folder path, we then search for the .ptx file within it. //If none or multiple .ptx sessions are found within, then we ask the //user to select the right one if(sessionToImportPath){ allSessionPaths = sf.file.directoryGetFiles({ path: sessionToImportPath, searchPattern: '*.ptx' }).paths; if(allSessionPaths.length > 1){ if(enableLogging.toLowerCase() === "yes"){ log(`Multiple .ptx sessions were found for Reel ${index}. Prompting for manual selection.`); } firstMatchingReelSessionPath = sf.interaction.selectFile({ allowedFileTypes: ["ptx"], prompt: `Multiple ptx sessions found for Reel ${index}. Please select the .ptx file for this reel.`, defaultLocation: sessionToImportPath }).path; } else if(allSessionPaths.length === 0){ if(enableLogging.toLowerCase() === "yes"){ log(`No .ptx sessions were found for Reel ${index}. Prompting for manual selection.`); } firstMatchingReelSessionPath = sf.interaction.selectFile({ allowedFileTypes: ["ptx"], prompt: `No ptx sessions found for Reel ${index}. Please select the .ptx file for this reel.`, defaultLocation: sessionToImportPath }).path; } else { if(enableLogging.toLowerCase() === "yes"){ log(`One .ptx session was found for Reel ${index}.`); } firstMatchingReelSessionPath = allSessionPaths[0]; } } else { if(enableLogging.toLowerCase() === "yes"){ log(`No folder was found for Reel ${index}. Prompting for manual selection.`); } firstMatchingReelSessionPath = sf.interaction.selectFile({ allowedFileTypes: ["ptx"], prompt: `Reel ${index} folder not found. Please select the .ptx file for this reel.` }).path; } if(enableLogging.toLowerCase() === "yes"){ log(`Importing: ${firstMatchingReelSessionPath}`); } sf.app.proTools.importSessionData({ sessionPath: firstMatchingReelSessionPath, audioMediaOptions: "LinkToSourceAudio", audioHandleSize: 2000, videoMediaOptions: "LinkToSourceVideo", trackMatchOptions: "MatchTracks", playlistOptions: "ImportOverlayNewOnExistingPlaylists", importClipGain: true, importClipsAndMedia: true, importVolumeAutomation: false, timecodeMappingUnits: "MaintainAbsoluteTimeCodeValues", timecodeMappingStartTime: "00:00:00:00", adjustSessionStartTimeToMatchSource: false, }); //WE NEED TO WAIT HERE FOR THE FIRST REEL TO IMPORT BEFORE MOVING ON sf.wait({intervalMs: 1000}); //WAIT FOR PROGRESS BAR TO GO AWAY sf.ui.proTools.waitForNoModals(); if(enableLogging.toLowerCase() === "yes"){ log("All Modals are closed"); } }
Kitch Membery @Kitch2025-01-31 22:48:45.554Z
When you run the
importSessionData
does the import happen all in one go? Or does it do it track by track/clip by clip etc?If it happens in one go, you could have the
sf.waitFor()
callback check the track count or clip count, and when it changes move on.If you could upload a screen recording of the script running with the
sf.wait({intervalMs: 1000});
, that would be great.Pinewood Studios @Pinewood_Studios
Hi Kitch,
No problem at all. I will run the script when I next have the chance and send the video.
Fergus
Pinewood Studios @Pinewood_Studios
Hi Kitch. Video of correctly functioning of script is here:
Kitch Membery @Kitch2025-02-04 20:03:24.950Z
Thanks, Fergus.
The video was helpful... Are you by chance around to jump on a quick Zoom call?
Pinewood Studios @Pinewood_Studios
I am yes, feel free to send an invite to fergus.pateman@pinewoodgroup.com.
Fergus
Kitch Membery @Kitch2025-02-04 20:14:34.504Z
Email sent :-)
- In reply toPinewood_Studios⬆:
Kitch Membery @Kitch2025-02-04 20:20:59.256Z
Hi Fergus, did you get the Zoom link I emailed?
Pinewood Studios @Pinewood_Studios
I did not.. could you possibly re-send? Or alternatively I can call you.
Fergus
Kitch Membery @Kitch2025-02-04 20:25:24.277Z
I'll resend it right now.
Pinewood Studios @Pinewood_Studios
Nothing yet. Could you try fergus.pateman@gmail.com instead? Might be getting caught up in our company filter.
Fergus
Kitch Membery @Kitch2025-02-04 20:31:34.172Z
Sent :-)
- In reply toPinewood_Studios⬆:
Kitch Membery @Kitch2025-02-04 20:29:30.924Z
If you did not receive the link, please send me an email at support@soundflow.org. :-)
- In reply toPinewood_Studios⬆:
Kitch Membery @Kitch2025-02-04 21:28:40.666Z
Hi Fergus,
As we spoke about on our call, here is a possible solution using the
sf.waitFor()
method to wait for new clips to be added to the session, this will hopefully prove that each import has finished.// Make sure the Clips List is open const proTools = sf.ui.proTools; const mainWindow = proTools.mainWindow; const clipsList = mainWindow.clipListView; const rows = clipsList.childrenByRole("AXRow"); const originalClipCount = rows.invalidate().length; // Main code to import here here sf.waitFor({ callback: () => rows.invalidate().length > originalClipCount, timeout: 10000, });
Let me know if it works for you. :-)