Hello @samuel_henriques,
I watched your cool macro "Automated Music cue sheet" on Facebook.
This is a macro I've been trying to find out if I could do with SF for a long time and I gave up on it to my knowledge.
If you'd like to share how to do it?
Linked from:
- samuel henriques @samuel_henriques
Hello @Kensuke_Matsui I've been thinking about putting this script on the shop. But between exporting and getting to openOffice, I made a folder action with automator and an applescript to filter the lines of text I need. I guess would be better to be easy to share if this bit was preformed by Soundflow and maybe save it as txt. . This way anyone can place it on the file format they need.
@chrscheuer, if this is something that is possible to do with SoundFlow, and you could help me with it let me know and I'll send the script and the automator thing ? And I guess the script can be reviewed to fix some of my apprentice mistakes.If not, @Kensuke_Matsui bare with me a few days so I can clean it up a bit and write some instructions to set it up.
Thank you
- JJohn Warrin @John_Warrin
Hello @samuel_henriques - I'd gladly pay for a copy of it. This would be so useful.
samuel henriques @samuel_henriques
Unless it's a million bucks, lets see if Kitch can help make this simpler to share on the shop.
and thank you!
samuel henriques @samuel_henriques
Here you go, It's working for me, I guess it still needs a bit of testing and making sure the filter is right.
@Kitch, thank you so much, if you can, please review the code to find the faults./* ///////////////// Auto Music List ///////////////////// This script will: save as.. and append _MUSIC LIST to Session name show all tracks hide inactive tracks select tracks by key word in the comments section of each track, the word can be changed on the "SELECT TRACKS BY WORDS IN COMMENTS" section. clean clip groups delete fades export session info as text to the session folder filtered to include only: session name clip nme, start time, end time, duration of Unmuted clips on channel 1 create clean txt on session folder soundflow will create new spreadsheet and paste (this is setup for my NeoOffice) save as the same name as pro tools session on session folder if you need to use adiferent spreadsheet application you need to change the "WAIT AND PASTE ON NEOOFFICE" section and the "SAVE NEOOFFICE" for your application Close NeoOffice and Pro tools enjoy! */ sf.ui.proTools.appActivate(); sf.ui.proTools.invalidate(); sf.ui.proTools.menuClick({ menuPath: ["File","Save As..."], }); sf.ui.proTools.windows.whoseTitle.is('Save').first.elementWaitFor({ failIfNotFound: true, }); //////////////////////////////// SAVE AS SESSION NAME -OPERATOR NAME +LISTA MUSICA/////////////// var sessionPath = sf.ui.proTools.mainWindow.sessionPath; var sessionName = sessionPath.split('/').slice(-1)[0].split('.').slice(0, -1).join('.'); var newname = sessionName + "_" + "MUSIC LIST"; sf.ui.proTools.windows.whoseTitle.is('Save').first.textFields.first.elementSetTextFieldWithAreaValue({ value: newname, }); sf.ui.proTools.windows.whoseTitle.is('Save').first.buttons.whoseTitle.is('Save').first.elementClick(); sf.ui.proTools.windows.whoseTitle.is('Save').first.elementWaitFor({ waitType: "Disappear", timeout: -1, }); /////////////////////////////////////////////////////SHOW All/HIDE INACTIVE TRACKS///////////////// sf.ui.proTools.menuClick({ menuPath: ["Options","Link Track and Edit Selection"], targetValue: "Enable", }); sf.ui.proTools.menuClick({ menuPath: ["View", "Other Displays", "Track List"], targetValue: "Enable", }); sf.ui.proTools.mainWindow.buttons.whoseTitle.is('Track List pop-up').first.popupMenuSelect({ menuPath: ["Show All Tracks"] }); sf.ui.proTools.mainWindow.buttons.whoseTitle.is('Track List pop-up').first.popupMenuSelect({ menuPath: ["Hide", "Inactive Tracks"] }); //////////////////////////////////SELECT TRACKS BY WORDS IN COMMENTS///////////////////////// // Make sure comments are visible if (!sf.ui.proTools.getMenuItem("View","Edit Window Views","Comments").isMenuChecked) { sf.ui.proTools.menuClick({ menuPath: ["View","Edit Window Views","Comments"], }); } function selecttrackbywordincomments(){ const visibleTracks = sf.ui.proTools.trackGetVisibleTracks().names; var firstTrackFound; let foundTracks = []; // Make sure we're on the Edit window. Required to scroll to first selected track. if (!sf.ui.proTools.getMenuItem("Window", "Edit").isMenuChecked) { sf.ui.proTools.menuClick({ menuPath: ["Window", "Edit"], }) } // Make sure comments are visible if (!sf.ui.proTools.getMenuItem("View", "Edit Window Views", "Comments").isMenuChecked) { sf.ui.proTools.menuClick({ menuPath: ["View", "Edit Window Views", "Comments"], }); } while (true) { // Ask for search query const searchQuery = /// example of search list ("word1 word2 word3") ("MUSICLIST") // Format query to RegEx const searchQueryRegEx = new RegExp(searchQuery.split(" ").join("|").replace(/^/, "\\b(").replace(/$/, ")\\b"), "i"); // Deselect all tracks sf.ui.proTools.trackDeselectAll(); // Search visible tracks for search query for (var i = 0; i < visibleTracks.length; i++) { var trackCommentHasSearch = sf.ui.proTools.trackGetByName({ name: visibleTracks[i] }).track.textFields.first.value.value.match(searchQueryRegEx); if (trackCommentHasSearch) { if (!firstTrackFound) { // Scroll to first track found sf.ui.proTools.trackGetByName({ name: visibleTracks[i], }).track.trackScrollToView(); firstTrackFound = !firstTrackFound; } else { sf.ui.proTools.trackSelectByName({ names: [visibleTracks[i]], deselectOthers: false }); } foundTracks.push(visibleTracks[i]); } } if (foundTracks.length !== 0) { break; } else if (!confirm("No tracks found. Try another search?")) { break; } } } selecttrackbywordincomments() ////////////////////////////////////////// sf.ui.proTools.mainWindow.buttons.whoseTitle.is('Track List pop-up').first.popupMenuSelect({ menuPath: ["Show Only Selected Tracks"], }); ////////////////////////////////////////////////////////////////////////////////////// ////clean fades and clip groups sf.ui.proTools.selectedTrack.popupButtons.whoseTitle.is('Track View selector').first.popupMenuSelect({ menuPath: ["waveform"], isOption: true, }); sf.ui.proTools.menuClick({ menuPath: ["Edit","Select All"], }); sf.ui.proTools.menuClick({ menuPath: ["Clip","Ungroup All"], }); selecttrackbywordincomments(); sf.ui.proTools.menuClick({ menuPath: ["Edit","Select All"], }); sf.ui.proTools.menuClick({ menuPath: ["Edit","Fades","Delete"], }); selecttrackbywordincomments(); //////////////////////////////////////////////////////// ////Close Comments so next open sessions doesn't show them if (sf.ui.proTools.getMenuItem("View","Edit Window Views","Comments").isMenuChecked) { sf.ui.proTools.menuClick({ menuPath: ["View","Edit Window Views","Comments"], }); } //////////////////////////////// EXPORT AS TEXT ////////////////// sf.ui.proTools.menuClick({ menuPath: ["File","Export","Session Info as Text..."], }); sf.wait({ intervalMs: 100, }); sf.ui.proTools.windows.whoseTitle.is('Export Session Text').first.checkBoxes.whoseTitle.is('Include File List').first.checkboxSet({ targetValue: "Disable", }); sf.ui.proTools.windows.whoseTitle.is('Export Session Text').first.checkBoxes.whoseTitle.is('Include Clip List').first.checkboxSet({ targetValue: "Disable", }); sf.ui.proTools.windows.whoseTitle.is('Export Session Text').first.checkBoxes.whoseTitle.is('Include Markers').first.checkboxSet({ targetValue: "Disable", }); sf.ui.proTools.windows.whoseTitle.is('Export Session Text').first.checkBoxes.whoseTitle.is('Include Plug-In List').first.checkboxSet({ targetValue: "Disable", }); sf.ui.proTools.windows.whoseTitle.is('Export Session Text').first.checkBoxes.whoseDescription.is('').allItems[4].checkboxSet({ targetValue: "Enable", }); sf.ui.proTools.windows.whoseTitle.is('Export Session Text').first.radioButtons.whoseDescription.is('').first.checkboxSet({ targetValue: "Disable", }); sf.ui.proTools.windows.whoseTitle.is('Export Session Text').first.radioButtons.whoseTitle.is('Selected Tracks Only').first.checkboxSet({ targetValue: "Enable", }); sf.ui.proTools.windows.whoseTitle.is('Export Session Text').first.checkBoxes.whoseTitle.is('Show Subframes').first.checkboxSet({ targetValue: "Disable", }); sf.ui.proTools.windows.whoseTitle.is('Export Session Text').first.checkBoxes.whoseTitle.is('Include User Timestamps').first.checkboxSet({ targetValue: "Disable", }); sf.ui.proTools.windows.whoseTitle.is('Export Session Text').first.radioButtons.whoseDescription.is('').allItems[3].checkboxSet({ targetValue: "Enable", }); sf.ui.proTools.windows.whoseTitle.is('Export Session Text').first.popupButtons.whoseDescription.is('').first.popupMenuSelect({ menuPath: ["Timecode"], }); sf.ui.proTools.windows.whoseTitle.is('Export Session Text').first.popupButtons.whoseDescription.is('').allItems[1].popupMenuSelect({ menuPath: ["UTF-8 'TEXT'"], }); sf.ui.proTools.windows.whoseTitle.is('Export Session Text').first.buttons.whoseTitle.is('OK').first.elementClick(); sf.wait(); ///////////////////////// SAVE text LOCATION /////////////////////////// sf.ui.proTools.invalidate(); var sessionPath = sf.ui.proTools.mainWindow.sessionPath; var folderPath = sessionPath.split(".").slice(0,-1).join().split("/").slice(1 ,-1).join("/"); var sessionName = sessionPath.split('/').slice(-1)[0].split('.').slice(0, -1).join('.'); sf.keyboard.type({ text: '/' }); sf.keyboard.type({ text: folderPath }); sf.keyboard.press({ keys: 'enter' }); sf.ui.proTools.windows.whoseTitle.is('Save').first.sheets.first.elementWaitFor({ waitType: "Disappear", }); sf.ui.proTools.windows.whoseTitle.is('Save').first.getElement('AXDefaultButton').elementClick(); sf.ui.proTools.windows.whoseTitle.is('Save').first.sheets.first.elementWaitFor({ waitType: "Disappear", }); ///////////////////////////////////////// Kitch Clean TXT ///////////////////////////// ///////////////////////////////////////// Kitch Clean TXT ///////////////////////////// var sessionPath = sf.ui.proTools.mainWindow.sessionPath; var sessionName = sessionPath.split('/').slice(-1)[0].split('.').slice(0, -1).join('.'); //const outputPath = folderPath + '/' + sessionName + '_' + 'CLEAN' + '.' + 'txt'; const inputPath = folderPath + '/' + sessionName + '.' + 'txt'; function convertTextFile(inputPath, outputPath) { let data = sf.file.readText({ path: inputPath }).text; let sessionNameHeading = data.toString().split(':')[0].trim() + ':'; let sessionName = data.toString().split(':')[1].split('\n')[0].trim(); let header = data.split('\n').filter(line => line.startsWith('CHANNEL'))[0].split('\t').map(item => item.trim()).slice(2, -1); let textFileLines = data.split(/\n/g).filter(line => line.endsWith('Unmuted')) .filter(line => line.startsWith('1')) .join().split('\t').map(item => item.replace(/\,/g,"").trim()) .join().split('Unmuted'); let filteredLines = []; for (let i = 0; i < textFileLines.length; i++) { if (textFileLines[i]) { filteredLines[i] = textFileLines[i].split(',').filter(item => item).slice(2).join(',').split(','); } } filteredLines.unshift(header); function convertToArrayOfObjects(data) { var keys = data.shift(), i = 0, k = 0, obj = null, output = []; for (i = 0; i < data.length; i++) { obj = {}; for (k = 0; k < keys.length; k++) { obj[keys[k]] = data[i][k]; } output.push(obj); } return output; } let sortArray = convertToArrayOfObjects(filteredLines); sortArray.sort(function (a, b) { var nameA = a["START TIME"] var nameB = b["START TIME"] if (nameA < nameB) { return -1; } if (nameA > nameB) { return 1; } return 0; }); sortArray.forEach((e, i, array) => { sortArray[i] = e["CLIP NAME"] + '\t' + e["START TIME"] + '\t' + e["END TIME"] + '\t' + e["DURATION"] + '\n'; }); sortArray.unshift("CLIP NAME" + '\t' + "START TIME" + '\t' + "END TIME" + '\t' + "DURATION" + '\n'); sortArray.unshift(sessionNameHeading + '\t' + sessionName + '\n'); sf.clipboard.setText({ // path: outputPath, text: sortArray.join(''), }) } convertTextFile(inputPath); ///////////////////////////////////////////////////////// ////////////////////////////// WAIT AND PASTE ON NEOOFFICE ///////////////// sf.ui.app('org.neooffice.NeoOfficeSecureEdition').appActivate(); sf.app.launch({ path: "/Applications/NeoOffice.app", }); sf.ui.app('org.neooffice.NeoOfficeSecureEdition').menuClick({ menuPath: ["File","New", "Spreadsheet"], }); sf.wait({ intervalMs: 2000, }) sf.ui.app('org.neooffice.NeoOfficeSecureEdition').menuClick({ menuPath: ["Edit","Paste Special..."], }); sf.ui.app('org.neooffice.NeoOfficeSecureEdition').windows.whoseTitle.is('Paste Special').first.elementWaitFor({ waitType: "Appear", failIfNotFound: true, timeout: -1, }); sf.keyboard.press({ keys: "down", fast: true, }); sf.keyboard.press({ keys: "tab", fast: true, }); sf.keyboard.press({ keys: "ctrl+o", fast: true, }); sf.ui.app('org.neooffice.NeoOfficeSecureEdition').windows.whoseTitle.is('Text Import').first.elementWaitFor({ waitType: "Appear", }); sf.keyboard.press({ keys: "tab", repetitions: 2, fast: true, }); sf.keyboard.press({ keys: "ctrl+o", fast: true, }); sf.wait(); ////////////////SAVE NEOOFFICE FILE//////// sf.keyboard.press({ keys: "cmd+s", }); sf.ui.app('org.neooffice.NeoOfficeSecureEdition').mainWindow.sheets.first.elementWaitFor({ waitType: "Appear", }); ////Destination ///Set Destination Folder //////Navigate to destination Folder sf.keyboard.type({text: `/`,}); sf.keyboard.type({ text: folderPath }); sf.wait({ intervalMs: 1000, }) sf.keyboard.press({ keys: 'enter' }); ///////Name the file sf.ui.app('org.neooffice.NeoOfficeSecureEdition').mainWindow.sheets.first.elementWaitFor({ waitType: "Appear", }); sf.keyboard.type({ text: newname, }); sf.ui.app('org.neooffice.NeoOfficeSecureEdition').mainWindow.sheets.first.buttons.whoseTitle.is('Save').first.elementClick(); ////////////////////// close neoOffice, Pro Tools Session sf.ui.app('org.neooffice.NeoOfficeSecureEdition').appActivate(); sf.ui.app('org.neooffice.NeoOfficeSecureEdition').menuClick({ menuPath: ["File","Close"], }); sf.ui.proTools.appActivate(); sf.ui.proTools.menuClick({ menuPath: ["File","Save"], }); sf.ui.proTools.menuClick({ menuPath: ["File","Close Session"], });
And here is a tool to write "MUSICLIST" on all selected tracks' comments. This is a separate tool only for the already existing sessions, all the new ones I'm already adding the keyword to the comments.
sf.ui.proTools.appActivateMainWindow(); // Make sure comments are visible if (!sf.ui.proTools.getMenuItem("View","Edit Window Views","Comments").isMenuChecked) { sf.ui.proTools.menuClick({ menuPath: ["View","Edit Window Views","Comments"], }); } function selectTracks(trackNames = []) { var allNames = sf.ui.proTools.selectedTrackNames; var namesToSelect = allNames.filter(n => trackNames); //Make sure the first track is in view var firstTrack = sf.ui.proTools.trackGetByName({ name: namesToSelect[0] }).track; firstTrack.trackScrollToView(); //Now select all of them sf.ui.proTools.trackSelectByName({ names: namesToSelect, deselectOthers: true, }); } selectTracks() var size = 'small'; var f = sf.ui.proTools.selectedTrack.frame; var popupMenu = sf.ui.proTools.selectedTrack.popupMenuOpenFromElement({ relativePosition: { x: f.w - 10, y: 5 }, isOption: true, isShift: true, }).popupMenu; popupMenu.menuClickPopupMenu({ menuPath: [size] }); function setComment(trackHeader, text) { var commentsField = trackHeader.textFields.whoseTitle.startsWith('Comments').first; commentsField.mouseClickElement(); sf.keyboard.press({ keys: 'cmd+a' }); sf.keyboard.type({ text: "MUSICLIST" }); sf.keyboard.press({ keys: 'return' }); } function setCommentsToTrackNames() { var tracks = sf.ui.proTools.selectedTracks.trackHeaders; for (var i = 0; i < tracks.length; i++) { var track = tracks[i]; setComment(track, track.normalizedTrackName); } } setCommentsToTrackNames();
Have fun!!
- KKensuke Matsui @Kensuke_Matsui
Hi @samuel_henriques @Kitch
thank you so much for sharing ! It works well on my system.
It would be even better if this could be extened to sessions with the stem track.
I'll try to find out a good way.
If you have any good ideas, please let me know....samuel henriques @samuel_henriques
Hello @Kensuke_Matsui, I'm happy it works for you. Could you elaborate on what actions you would like the script to to?
- KKensuke Matsui @Kensuke_Matsui
I edit the music with stem files. like this
and then,
I want it to look like this in the text file.music cue_MUSIC LIST.txt (715 B)
Do you think you can do it this way?
samuel henriques @samuel_henriques
I get it.....let me think about it.
And you only want these tracks to be as one line and duration, on the list or there are other tracks you want to include?samuel henriques @samuel_henriques
If it's only these, I would propose create a clip group fro each "song" and name it the song name. That way there is duration and name.
type "MUSICLIST" on the comments of one of the tracks only.then delete from script:
////clean fades and clip groups sf.ui.proTools.selectedTrack.popupButtons.whoseTitle.is('Track View selector').first.popupMenuSelect({ menuPath: ["waveform"], isOption: true, }); sf.ui.proTools.menuClick({ menuPath: ["Edit","Select All"], }); sf.ui.proTools.menuClick({ menuPath: ["Clip","Ungroup All"], }); selecttrackbywordincomments(); sf.ui.proTools.menuClick({ menuPath: ["Edit","Select All"], }); sf.ui.proTools.menuClick({ menuPath: ["Edit","Fades","Delete"], }); selecttrackbywordincomments();
The problem with this is that it wont be a "open session and click" operation...
- KKensuke Matsui @Kensuke_Matsui
Hello @samuel_henriques
Thank you for your advice! And , There are a lot of editing points in my music session. so need to consolidate each song.
It would be great If I could. I'll try to dig more.- KKensuke Matsui @Kensuke_Matsui
or create the region group of in and out points on the another track like this.
samuel henriques @samuel_henriques
That could work as well. I have a script to select all regions with same name, you could do a clip group from there, or on a new track, problem, as is, is if you have the same song more than once.
Untitled.mov (0.98 MiB)
samuel henriques @samuel_henriques
but if you need to go one by one, better solution is to write a script to take selection start time, end time, duration and name and paste it on a spreadsheet one by one, as you select and press a button.
You need the list to have all the information of start, end and duration of each? Or you need only duration of all clips with same name?- KKensuke Matsui @Kensuke_Matsui
Awesome!
I need all the information of name ,start and end,duration.samuel henriques @samuel_henriques
I think the idea I was proposing takes longer than what you proposed.
you can find the script to select all clips by same name here:
hope it helps
- KKensuke Matsui @Kensuke_Matsui
Wow This is really helpful macro. Thanks!
- In reply tosamuel_henriques⬆:
Kitch Membery @Kitch2020-10-20 10:54:16.617Z
Nice work mate. If I find some time this week I’ll have a look through it :-)
samuel henriques @samuel_henriques
Not sure if this is an a weird request, but the reason I had the original script on the automator, was so I could use the filter on computers I don't have yet or can't have SoundFlow. But now I really like the filter you made. Could you point me in the right direction to figure out how I could run/adapt your filter to javascript on automator? I'm having really hard time to even write stuff I thought was general javascript on the automator javascript. Thank you so much and I'm sorry if this is too weird.
Kitch Membery @Kitch2020-10-20 19:27:16.976Z
My knowledge of javascript it quite SoundFlow focused. Although I have used automator before I gave up on it as I found SoundFlow more than met all my needs.
samuel henriques @samuel_henriques
I see what you mean...although I'm just starting to learn about javascript, I'm exited to continue because I keep figuring out how stuff works in SoundFlow. Every time I try anything on automator, I get frustrated so fast, I guess I need more coding bases to start doing anything.
Anyway, thank you so much, you rock!!Kitch Membery @Kitch2020-10-20 19:40:05.557Z
You are totally welcome mate. Keep up the great work. Rock on!!!
- In reply tosamuel_henriques⬆:
Curtis Macdonald @Curtis_Macdonald
Wow what an amazing Script !!! Really want to use this, but when I try running this script I get an error: ClickButtonAction requires UIElement (line 273)
samuel henriques @samuel_henriques
Hello Curtis,
I've made several changes to this script, you can find the latest version in the store
https://soundflow.org/store/lista-de-musica
You need change some details for you
on line 327 change what you would like appended to the save as pro tools and new saved list file:
const appendToName = "LISTA DE MUSICA"
on line 367 change the word you add to comments so those tracks are included in the list:
let musicTracks = selecttrackbywordincomments("LISTADEMUSICA")
line 339 change location to saved list file
const cueParentFolder = "~/MEOCloud/CUE SHEET";
there are other custom bits, but try this for now, and then we can figure out how to set it up to for your preference.
- In reply tosamuel_henriques⬆:
Kitch Membery @Kitch2020-10-17 22:58:23.421Z
What are the steps that the automator applescript takes? I'm sure there would be a way to do it with SoundFlow.
samuel henriques @samuel_henriques
Hey Kitch,
Soundlow does all the funny stuff and export of txt file, and saves on a folder with the automator action.
Automator has a applescript (long story) to clean unwanted lines of text. Would you like me to send it, and you can figure out how to get it to work with sound flow?
It filters lines starting with "1" and ending with "Unmuted" and deletes the first two words and last word.
filters lines staring with Channel. deletes the first two words and last word and keeps only one.Then it gets the cleaned text to clipboard, opens OpenOffice and pastes the text.
Soundflow waits for the import text menu and takes care of the sorting and save to destination folder, it knows the destination because all the info is on the session name(I love this part).
Kitch Membery @Kitch2020-10-17 23:22:32.582Z
I can take a look at it... No promises yet though :-) Along with the applescript, can you also share a sample of a text file exported by pro tools that I can work with?
samuel henriques @samuel_henriques
Here you go,
on run {input, parameters} -- Automator's "Run AppleScript" actions appear always to receive their input in list form. -- If the input here contains only one item, and it's text, get a list of the text's 'paragraphs'. if (((count input) is 1) and (class of item 1 of input is text)) then set input to paragraphs of item 1 of input end if set unwanteds to {"SAMPLE RATE:", "BIT DEPTH:", "SESSION START TIMECODE", ¬ "TIMECODE FORMAT:", "# OF AUDIO TRACKS:", "# OF AUDIO CLIPS:", ¬ "# OF AUDIO FILES:", "T", "TRACK NAME:", ¬ "COMMENTS:", "USER DELAY:", "STATE:", "2"} set output to {} repeat with thisLine in input set OKSoFar to true repeat with thisBeginning in unwanteds if (thisLine begins with thisBeginning) then set OKSoFar to false exit repeat end if end repeat if (OKSoFar) then if ((thisLine begins with "CHANNEL") or (character 1 of thisLine is in "0123456789")) then set thisLine to text from word 3 to word -2 of thisLine end if if (thisLine is not in output) then set end of output to thisLine as text end if end repeat -- The following four lines convert the list of output lines to a single text. -- Delete them if not wanted. set astid to AppleScript's text item delimiters set AppleScript's text item delimiters to linefeed set output to output as text set AppleScript's text item delimiters to astid return output end run
SoundFlow_2020_PGM 00_LISTA DE MUSICA.txt (59.98 KiB)
Kitch Membery @Kitch2020-10-18 00:17:15.859Z
Thanks @samuel_henriques, Can you post what you want the text to look like after the applescript has run?
samuel henriques @samuel_henriques
this one is also sorted by start time. the sorting is not done by the script.
SoundFlow_2020_PGM 00_LISTA DE MUSICA_clean and sorted by start time.xlsx (61.01 KiB)
samuel henriques @samuel_henriques
the sorting is not done by the script, but if it's possible, I guess would make sense to do. that way we would end up with clean and sorted text to place on any program anyone might need.
Kitch Membery @Kitch2020-10-18 08:47:41.637Z
@samuel_henriques... This my first attempt. It doesn't sort for you but will do the rest!
Rock on! :-)
const inputPath = '~/Desktop/SoundFlow_2020_PGM 00_LISTA DE MUSICA.txt'; const outputPath = '~/Desktop/Cue Sheet.txt'; function convertTextFile(inputPath, outputPath) { let text = sf.file.readText({ path: inputPath }).text; let sessionNameHeading = text.toString().split(':')[0].trim() + ':'; let sessionName = text.toString().split(':')[1].split('\n')[0].trim(); let textFileLines = text.split(/\n/g).filter(line => line.endsWith('Unmuted')) .filter(line => line.startsWith('1')) .join().split('\t').map(item => item.trim()) .join().split('Unmuted'); for (let i = 0; i < textFileLines.length; i++) { if (textFileLines[i]) { textFileLines[i] = textFileLines[i].split(',').filter(item => item).slice(2).join('\t'); } } let outputText = `${sessionNameHeading}\t${sessionName}\nCLIP NAME\tSTART TIME\tEND TIME\tDURATION\n${textFileLines.join('\n')}`; sf.file.writeText({ path: outputPath, text: outputText, }); } convertTextFile(inputPath, outputPath);
samuel henriques @samuel_henriques
Nice one @Kitch, already found an error on the applescript.
samuel henriques @samuel_henriques
so far so good, but. if the name of song has a "," on the name, your script will replace it with a "tab" and when importing to openOffice will ad a column on those lines
samuel henriques @samuel_henriques
this:
1 29 Big Sean - Blessings ft. Drake, Kanye West 00:22:04:18 00:22:07:16 00:00:02:23 Unmutedwill become:
Big Sean - Blessings ft. Drake Kanye West 00:22:04:18 00:22:07:16 00:00:02:23
notice the "," on "Drake, Kanye"
Kitch Membery @Kitch2020-10-18 12:18:09.311Z
Darn it!!!
I'll have to see how I can fix that tomorrow....
For now... Here is a version that sorts by start time :-)
const inputPath = '~/Desktop/SoundFlow_2020_PGM 00_LISTA DE MUSICA.txt'; const outputPath = '~/Desktop/Cue Sheet.txt'; function convertTextFile(inputPath, outputPath) { let data = sf.file.readText({ path: inputPath }).text; let sessionNameHeading = data.toString().split(':')[0].trim() + ':'; let sessionName = data.toString().split(':')[1].split('\n')[0].trim(); let header = data.split('\n').filter(line => line.startsWith('CHANNEL'))[0].split('\t').map(item => item.trim()).slice(2, -1); let textFileLines = data.split(/\n/g).filter(line => line.endsWith('Unmuted')) .filter(line => line.startsWith('1')) .join().split('\t').map(item => item.trim()) .join().split('Unmuted'); let filteredLines = []; for (let i = 0; i < textFileLines.length; i++) { if (textFileLines[i]) { filteredLines[i] = textFileLines[i].split(',').filter(item => item).slice(2).join(',').split(','); } } filteredLines.unshift(header); function convertToArrayOfObjects(data) { var keys = data.shift(), i = 0, k = 0, obj = null, output = []; for (i = 0; i < data.length; i++) { obj = {}; for (k = 0; k < keys.length; k++) { obj[keys[k]] = data[i][k]; } output.push(obj); } return output; } let sortArray = convertToArrayOfObjects(filteredLines); sortArray.sort(function (a, b) { var nameA = a["START TIME"] var nameB = b["START TIME"] if (nameA < nameB) { return -1; } if (nameA > nameB) { return 1; } return 0; }); sortArray.forEach((e, i, array) => { sortArray[i] = e["CLIP NAME"] + '\t' + e["START TIME"] + '\t' + e["END TIME"] + '\t' + e["DURATION"] + '\n'; }); sortArray.unshift("CLIP NAME" + '\t' + "START TIME" + '\t' + "END TIME" + '\t' + "DURATION" + '\n'); sortArray.unshift(sessionNameHeading + '\t' + sessionName + '\n'); sf.file.writeText({ path: outputPath, text: sortArray.join(''), }) } convertTextFile(inputPath, outputPath);
samuel henriques @samuel_henriques
Cool, thank you,
this one is deleting the time codes when the "," separation exists to compensate for columns.
and it's converting special characters like "á" and probably all others to "�".
Christian Scheuer @chrscheuer2020-10-18 13:55:13.132Z
and it's converting special characters like "á" and probably all others to "�".
SoundFlow reads/writes files in the global standard UTF-8. Make sure to set the export to use UTF-8 encoding instead of TextEdit encoding when exporting data from Pro Tools.
samuel henriques @samuel_henriques
Nice one, fixed it. Thank you
Kitch Membery @Kitch2020-10-18 16:40:12.433Z
Awesome. Is it all working now @samuel_henriques?
samuel henriques @samuel_henriques
The special characters yes. The "," thing is the same
samuel henriques @samuel_henriques
Hello Kitch, if an easy solution is delete all "," from the hole text, I'm not seeing any problem with that. Even if the words glue. Ex "yadda,yadda" will be "yaddayadda". Does anyone see a problem with that?
Kitch Membery @Kitch2020-10-19 10:15:50.809Z
Removing the “,” completely is certainly possible. I’ll take a look in the morning. There is also a way to paste the document into OpenOffice where it will ignore the “,” and only delimit by the tab “\t”
samuel henriques @samuel_henriques
I think you're right. I tried some stuff but failed. I'll check it out again as soon as I get to computer. In the mean time I made it Automator free and the output of your filter goes to the clipboard, that way anyone can past it on a spreadsheet by changing the neoofice steps to whatever they are using. It also keeps the export from protools txt and final file on session folder. Seems easier to implement to anyone.
- In reply tosamuel_henriques⬆:
Kitch Membery @Kitch2020-10-19 10:21:53.457Z2020-10-19 10:27:54.507Z
Here you go @samuel_henriques :-)
const inputPath = '~/Desktop/SoundFlow_2020_PGM 00_LISTA DE MUSICA.txt'; const outputPath = '~/Desktop/Cue Sheet.txt'; function convertTextFile(inputPath, outputPath) { let data = sf.file.readText({ path: inputPath }).text; let sessionNameHeading = data.toString().split(':')[0].trim() + ':'; let sessionName = data.toString().split(':')[1].split('\n')[0].trim(); let header = data.split('\n').filter(line => line.startsWith('CHANNEL'))[0].split('\t').map(item => item.trim()).slice(2, -1); let textFileLines = data.split(/\n/g).filter(line => line.endsWith('Unmuted')) .filter(line => line.startsWith('1')) .join().split('\t').map(item => item.replace(/\,/g,"").trim()) .join().split('Unmuted'); let filteredLines = []; for (let i = 0; i < textFileLines.length; i++) { if (textFileLines[i]) { filteredLines[i] = textFileLines[i].split(',').filter(item => item).slice(2).join(',').split(','); } } filteredLines.unshift(header); function convertToArrayOfObjects(data) { var keys = data.shift(), i = 0, k = 0, obj = null, output = []; for (i = 0; i < data.length; i++) { obj = {}; for (k = 0; k < keys.length; k++) { obj[keys[k]] = data[i][k]; } output.push(obj); } return output; } let sortArray = convertToArrayOfObjects(filteredLines); sortArray.sort(function (a, b) { var nameA = a["START TIME"] var nameB = b["START TIME"] if (nameA < nameB) { return -1; } if (nameA > nameB) { return 1; } return 0; }); sortArray.forEach((e, i, array) => { sortArray[i] = e["CLIP NAME"] + '\t' + e["START TIME"] + '\t' + e["END TIME"] + '\t' + e["DURATION"] + '\n'; }); sortArray.unshift("CLIP NAME" + '\t' + "START TIME" + '\t' + "END TIME" + '\t' + "DURATION" + '\n'); sortArray.unshift(sessionNameHeading + '\t' + sessionName + '\n'); sf.file.writeText({ path: outputPath, text: sortArray.join(''), }) } convertTextFile(inputPath, outputPath);
- KIn reply toKensuke_Matsui⬆:Kensuke Matsui @Kensuke_Matsui
Thanks @samuel_henriques , @Kitch !!
I really appreciate your help!!