I'm looking for a way to make a "saved" variable that stays the same until I need to intentionally change it.
I'm thinking of making a plain text file and saving it on the hard drive, and putting the variable in the text file, saving it, and then copying the value when I need it.
How can I write/modify a text file like this, and then copy that for use elsewhere?
Linked from:
- samuel henriques @samuel_henriques
Hey Andrew,
To save something definitely, maybe this is a good option. You should check out the forum for something called
globalState
there are a few threads where Christian explains how it works. They can store anything on soundFlow's memory. But if you restart or quit soundFlow you'll loose the info you stored.To write/read a txt you keep on your computer, you can use;
let txtPath = "~/Desktop/Untitled.txt" sf.file.writeText({ path: txtPath, text: "wasuupp" }) let txt = sf.file.readText({ path: txtPath }).text; log(txt)
- AAndrew Sherman @Andrew_Sherman
This is great, Samuel - thank you. I think I'll go with the text file for now but the other method is good to know as well.
- In reply tosamuel_henriques⬆:AAndrew Sherman @Andrew_Sherman
Hi Samuel,
I'm have the text file version of this working fine, but it's a little slow and I think global state might work better. I'm getting stuck though, can you see where I'm going wrong?
Workflow:
In Finder select folder, press trigger while holding command to "set current project". Then once "current project" is set, just press the trigger to open the folder in Finder.if (event.keyboardState.asString == 'cmd') { // Set folder // Copy path sf.keyboard.modifiers({ isOption: true }) sf.ui.finder.menuClick({ looseMatch: true, menuPath: ["Edit", "Copy "], }); sf.keyboard.modifiers({ isOption: false }) // I'm getting stuck here, I don't think globalState is set up correctly. I want to put the contents of the clipboard into aa globalState variable. globalState.currentPath = sf.clipboard.getText().text; var currentPath = globalState.currentPath; } else { // Open folder sf.directory.open({ path: currentPath }); }
samuel henriques @samuel_henriques
hello @Andrew_Sherman,
You want to open the folder of the opened pro tools session?
I'm not getting what you are trying to do, sorry.
- AAndrew Sherman @Andrew_Sherman
Hi Samuel,
Apologies for not making it clear.
It's a script for opening a folder in Finder.
The first part of the workflow defines the path (only used if CMD is pressed; I use a stream deck trigger for it while pressing CMD).
The second part of the workflow is to open the folder in Finder. (I press the same stream deck trigger, but not while using command).Where I'm getting stuck is how to use globalState - I don't know how to properly use it. I tried various links on the forum bus couldn't get it right. I need the folder path to go into a global state variable.
samuel henriques @samuel_henriques
ok, I'm getting it now.
And the folder doesn't have any relationship with the opened session?
Or you don't want it to depend on any session?- AAndrew Sherman @Andrew_Sherman
No, it doesn't need to be related to any session, just a folder.
samuel henriques @samuel_henriques
and, the original script was getting the path of the open finder window.
If you want to get the selected folder it is easier, code wise, and much more stable.
with one do you intend to use?
- AAndrew Sherman @Andrew_Sherman
This is a different version I currently have which uses a text file instead of globalState (it's working fine, excepting sometimes it doesn't reliably choose the selected folder, I think maybe it's not opening or saving the text file on the disk quickly enough if I'm working fast / clicking fast). Hard drive is an SSD. But as I say, it is working. The thing is that my "current project" changes quite frequently (sometimes I'm editing videos, sometimes I'm doing audio postproduction, sometimes I'm doing music etc).
let settingsDoc = "/Users/andrewsherman/Make Perceive Dropbox/Andrew Sherman/Application Settings/Finder/Current-Project.txt" if (event.keyboardState.asString == 'cmd') { // Set folder sf.keyboard.modifiers({ isOption: true }) sf.ui.finder.menuClick({ looseMatch: true, menuPath: ["Edit", "Copy "], }); sf.keyboard.modifiers({ isOption: false }) const finderPathCopied = sf.clipboard.getText().text sf.file.writeText({ path: settingsDoc, text: finderPathCopied }) } else { // Open folder let currentProject = sf.file.readText({ path: settingsDoc }).text; sf.directory.open({ path: currentProject }); }
Which method do you recommend I use, globalState or the text file?
samuel henriques @samuel_henriques
hey @Andrew_Sherman
this is working here:
const cmdIsEnabled = event.keyboardState.hasCommand if (cmdIsEnabled || globalState.folderPath == undefined) { globalState.folderPath = "" sf.keyboard.modifiers({ isOption: true, isCommand: true }) sf.ui.finder.menuClick({ looseMatch: true, menuPath: ["Edit", "Copy "], }); sf.wait({ intervalMs: 100 }) globalState.folderPath = sf.clipboard.getText().text; } else { sf.directory.open({ path: globalState.folderPath }); }
From my testing just now, you might need to add a wait on the text version to give a bit of time for the clipboard to get the new text.
- AAndrew Sherman @Andrew_Sherman
Thank you Samuel, as always it's much appreciated. I see where I was going wrong with global state.
- In reply tosamuel_henriques⬆:AAndrew Sherman @Andrew_Sherman
Hi Samuel,
Hope you're doing well! How can I add a second line of text to this code?
let txtPath = "~/Desktop/Untitled.txt" sf.file.writeText({ path: txtPath, text: "wasuupp" // I'd like add a second line of text here on a new line }) let txt = sf.file.readText({ path: txtPath }).text; log(txt)
samuel henriques @samuel_henriques
Hey @Andrew_Sherman,
All good, hope the same for you.
"\n" makes a new line. Or check this article:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literalslet txtPath = "~/Desktop/Untitled.txt" sf.file.writeText({ path: txtPath, text: "wasuupp\nmore stuff" // I'd like add a second line of text here on a new line }) let txt = sf.file.readText({ path: txtPath }).text; log(txt)
Or with the template literals:
let txtPath = "~/Desktop/Untitled.txt" sf.file.writeText({ path: txtPath, text: `wasuupp more stuff even more` // I'd like add a second line of text here on a new line }) let txt = sf.file.readText({ path: txtPath }).text; log(txt)
- AAndrew Sherman @Andrew_Sherman
Excellent, thank you Samuel!
Dustin Harris @Dustin_Harris
I’m on my phone and can’t remember the syntax off the top of my head, but maybe look into read/write JSON files instead of text. The advantage is you can store key:value pairs and don’t have to split the text file by line breaks to separate values…
- In reply toAndrew_Sherman⬆:
Dustin Harris @Dustin_Harris
this is an easy and elegant way to handle multiple settings being saved/recalled. This assumes both parts of the code are in the same package, but the save / recall file path can be anywhere you want really :)
//save settings (goes in the script that saves the settings) let setting_one = "This"; let setting_two = "Is"; let setting_three = "Cool"; let settingsToSave = { setting_one, setting_two, setting_three, }; //this saves the settings file in the package directory of the command it is run from let jsonSavePath = sf.soundflow.thisPackage.getDirectory().path + '/savedSettings.json'; let success = sf.file.writeJson({ path: jsonSavePath, json: settingsToSave }).success; if (!success) throw `Error saving settings` //recall settings (goes in the script that needs to use the settings) let recallSettings = sf.file.readJson({ path: sf.soundflow.thisPackage.getDirectory().path + '/savedSettings.json' }).json let {setting_one, setting_two, setting_three} = recallSettings; log(setting_one)
samuel henriques @samuel_henriques
Nice one,
Wold this file open on any computer you log your account from?
Or you wold need to carry the file with you?Thank you so much.
Dustin Harris @Dustin_Harris
I don't think the package folder is actively synced when you save files in it's own path... but in theory you could set the save path to a dropbox folder so it does get actively synced?
samuel henriques @samuel_henriques
Cool. Dropbox could work.
- In reply toDustin_Harris⬆:AAndrew Sherman @Andrew_Sherman
This looks very cool Dustin, I'll need a while to get my head around it.
Dustin Harris @Dustin_Harris
It might look a little daunting but it's really no different than using a text file like you were earlier. In fact is IS just a text file, but formatted in a way that makes it easy for scripts to read and write to :)
- AAndrew Sherman @Andrew_Sherman
Putting the filepath in Dropbox works great. This is going to be very handy for me, I was looking for something that handles key value pairs but had no idea that's what it was called. First time json file user... seems easy and lightweight, can edit with text editor if needed as well.
Dustin Harris @Dustin_Harris
You probably have already realized this, but in case you haven't: the script that READS the JSON file should test that it exists at the beginning of the script, and if it doesn't, it should create the file with default values so the script doesn't throw an error :)
- AAndrew Sherman @Andrew_Sherman
Yes, it's a nice failsafe.
- In reply toDustin_Harris⬆:AAndrew Sherman @Andrew_Sherman
Hi Dustin, I hope you're well!
I'm trying a variation on this script where I need a variable to change depending on whether or not it's been run before.
I will use Script A to write the saved json file, and this will generate the "current" version of the variable value (in my case I'm measuring the size of a folder that's syncing / uploading).
Then at a later stage, I will run Script B which checks the current size of the folder again and measures it against the previous value.
So when Script 1 is run, the folder could be 20GB. Then when Script 2 is run again, the same folder could be 25GB. And then when Script 2 is run again after that (in case the sync is not yet complete), the folder could be 30GB. And I may run Script 2 multiple times until the upload is complete (some folders are very large, TBs in size, hence the need for checking and babysitting them!).
I know how to determine the folder sizes via command line and do most of the script; what I don't know is how to name the variables in such a way that their name or value can change based on whether or not the script was previously run.
I suppose it's dependent on date and time. Would I need to create a new variable each time I run it? In that case how do I get the script to automatically generate variables? Or is there a way of using only the current value, and previous value?
Dustin Harris @Dustin_Harris
Heyya!
So I assume script A sets the path of the folder being watched (and it's initial size value) and then script B just checks if the file size has changed?if so, you only need 2 variables. Pseudocode below:
let previousFolderSize = get_this_from_JSON_file(); let currentFolderSize = get_this_from_terminal_command(); if (currentFolderSize === previousFolderSize) { log('done uploading!') } else { write_to_JSON_file(currentFolderSize) }
does that answer it? :)
- AAndrew Sherman @Andrew_Sherman
Thanks Dustin, much appreciated! I've done something based on that approach. I added in a third variable that is an "old" result and then at the end of the script it gets overwritten with the current result, so that I can see if the upload has stalled instead of just showing overall progress.