No internet connection
  1. Home
  2. How to

How to copy timecode from text to transpot?

By Nick Norton @notNickNorton
    2022-03-23 17:16:39.821Z

    Hi all, music editor here. I've got a bunch of score files, formatted like this:

    TN 419. 3M2. 01.20.30.21. stereo.wav

    And I'm trying to capture the timecodes and enter them into the transport so I can quickly create a marker for each file.

    I've used key press commands to copy "01.20.30.21" to the clipboard, but when I switch to Pro Tools and have it press * then command V, it doesn't enter. I have a feeling it may have to do with the periods, and that this might require a script rather than key press macros. What do you recommend?

    Solved in post #5, click to view
    • 9 replies
    1. Hey @Nick_Norton1,

      Try this, it will take the contents of the clipboard and replace any “.” with “:”, which should allow you to paste it into the main counter. Put it after the command that copies the timecode to the clipboard.

      const clipboardContents = sf.clipboard.getText().text;
      
      sf.clipboard.setText({
          text: clipboardContents.split('.').join(':')
      }); 
      
      1. Nick Norton @notNickNorton
          2022-03-23 18:39:00.898Z

          Thanks! Time to give Javascript a shot...

          1. Nick Norton @notNickNorton
              2022-03-23 19:59:29.842Z

              Wow did that ever work!

              My full script, in case you see any room for improvement. It also goes back to the finder and gets the cue number and types that in for the marker name.

              
              sf.keyboard.press({
                  keys: "return, right, alt+left, left, left, alt+shift+left, cmd+c, return",
              });
              
              const clipboardContents = sf.clipboard.getText().text;
              
              sf.clipboard.setText({
                  text: clipboardContents.split('.').join(':')
              }); 
              
              sf.ui.proTools.appActivate();
              
              sf.keyboard.press({
                  keys: "numpad multiply",
              });
              
              sf.keyboard.press({
                  keys: "cmd+v",
              });
              
              sf.keyboard.press({
                  keys: "return",
              });
              
              sf.ui.finder.appActivate();
              
              sf.keyboard.press({
                  keys: "return, right, alt+left, alt+left, left, left, alt+shift+left, cmd+c, return",
              });
              
              sf.ui.proTools.appActivate();
              
              sf.keyboard.press({
                  keys: "numpad enter",
              });
              
              sf.keyboard.press({
                  keys: "cmd+v",
              });
              
              sf.keyboard.press({
                  keys: "numpad enter",
              });
              
              sf.ui.finder.appActivate();
              
              sf.keyboard.press({
                  keys: "down",
              });
              
              
              
              1. Raphael Sepulveda @raphaelsepulveda2022-03-24 02:30:33.009Z2022-03-25 01:07:17.707Z

                That's great!

                With SoundFlow, scripts are more robust if we avoid using keyboard shortcuts as much as possible. I've reviewed your script and optimized it for ya!

                Now all you have to do is select as many files as you like on Finder and run this. The script will identify which files within the selection follow your naming convention and filter out the rest. It will then extract the cue number and timecode and create memory locations for those using that data.

                Give it a try and let me know if it works on your end!

                function finderGetSelectedFileNames() {
                    sf.ui.finder.appActivate();
                
                    const selectedPaths = sf.ui.finder.selectedPaths;
                
                    if (!selectedPaths.length) throw 'No files selected in Finder.'
                
                
                    const fileNames = selectedPaths.map(path => path.split('/').filter(Boolean).slice(-1)[0]);
                
                    return { fileNames };
                }
                
                /** @param {string[]} fileNames - Array containing file names.  */
                function getScoreDataFromFileNames(fileNames) {
                    const scoreData = fileNames.reduce((acc, fileName) => {
                        // Check if filename contains both "xMx." and "xx.xx.xx.xx" patterns (x are numbers)
                        const match = fileName.match(/(\dM\d+)|(\d\d\.\d\d\.\d\d\.\d\d)/g);
                
                        if (match && match.length === 2) {
                            // If there's a match, extract data from filename
                            let [cueNumber, timecode] = match;
                
                            // Format timecode by replacing periods with colons
                            timecode = timecode.split('.').join(':');
                
                            acc.push({ cueNumber, timecode });
                        }
                        return acc;
                    }, []);
                
                    if (!scoreData.length) throw 'No file names with format pattern detected.'
                
                    return { scoreData };
                }
                
                /** @param {{ timecode: string }} arg */
                function mainCounterGoToTimecode({ timecode }) {
                    sf.clipboard.setText({ text: timecode });
                
                    sf.ui.proTools.mainWindow.counterDisplay.textFields.whoseTitle.is("Main Counter").first.elementClick();
                    sf.keyboard.press({ keys: "cmd+v, return", });
                }
                
                /** Simple Create Memory Location
                 * @param {{ name: string }} arg
                 */
                function createMemoryLocation({ name }) {
                    const memoryLocationDialog = sf.ui.proTools.newMemoryLocationDialog;
                
                    sf.ui.proTools.appActivateMainWindow();
                
                    sf.keyboard.press({ keys: "numpad enter" });
                    memoryLocationDialog.elementWaitFor();
                
                    memoryLocationDialog.textFields.allItems[1].elementSetTextFieldWithAreaValue({
                        value: name
                    });
                
                    memoryLocationDialog.buttons.whoseTitle.is("OK").first.elementClick();
                    memoryLocationDialog.elementWaitFor({ waitType: "Disappear" });
                }
                
                function main() {
                    const { fileNames } = finderGetSelectedFileNames();
                    const { scoreData } = getScoreDataFromFileNames(fileNames);
                
                    sf.ui.proTools.appActivateMainWindow();
                
                    sf.ui.proTools.mainCounterDoWithValue({
                        targetValue: "Timecode",
                        action: () => {
                            for (const data of scoreData) {
                                mainCounterGoToTimecode({ timecode: data.timecode });
                                createMemoryLocation({ name: data.cueNumber });
                            }
                        }
                    });
                }
                
                main();
                
                Reply3 LikesSolution
                1. Nick Norton @notNickNorton
                    2022-03-24 16:54:04.833Z

                    Damn did that ever work! Thank you!!

                    1. Nick Norton @notNickNorton
                        2022-03-24 17:59:54.730Z

                        This is actually begging two new questions:

                        1. What if the format is slightly different? Is there a way for JS to detect strings of xx.xx.xx.xx and xMx if they, for instance, don't have the dot right next to them, or have a different number of spaces?

                        2. Is there a way to do this from folder names too? Sometimes I get stereo tracks, sometimes I get folders of stems. In that case, question 1 would apply because folders don't have the word "stereo" at the end.

                        Thank you!

                        1. Yeah, I can adjust the algorithm so that if the filename contains a "xMx" and a "xx.xx.xx.xx" (x being numbers) then that will qualify to be put in the session. Do you think that'll be enough? Or are there any other variations? If so, showing me a few examples would help a ton!

                          Oh and yes, the method I'm mentioning here will work with any file including folders since it's just looking for those two specific string patterns.

                          1. Nick Norton @notNickNorton
                              2022-03-25 00:16:53.719Z

                              Sweet! Here are a few examples:

                              SnS.404.5M3.01.40.11.04.Master.wav

                              SS 601. 1M1. 01.02.19.13. stereo.wav

                              TPF 107. 0M1. V2. 01.00.00.00. stereo.wav

                              RHOH 1. 0M15. 01.04.19.19. stereo.wav. (that one has a double digit after the M)

                              1. Perfect, I've updated the code above to handle all these cases!

                                This should work as expected, as long as there aren't cases in which you have an additional combination of words that resemble "xMx" besides the actual cue number. Currently, those would end up getting filtered out.