First of all, I admit that I'm starting with a new project with JXA (Javascript automation for mac os) without having much understanding about AppleScript.
Currently, I'm trying to run following command using JXA:
Application("System Events").processes.windows.name()
First, I used Script Editor to run it. It worked fine and I got the output quickly enough.
However, according to my use case, since I want to get the output of this code frequently from one of my bash script, I tried to execute it using osascript as follows
osascript -l JavaScript -e 'Application("System Events").processes.windows.name()'
But this time, it took few seconds to print the result in the console.
Now my question is why it takes too much time to execute the same script in osascript compare to Script Editor? Is there any way to optimize the performance of it?
Here's the JXA solution:
var winList = Application("System Events").processes.whose({backgroundOnly: {'=': false} }).windows.name();
var winList2 = winList.reduce(
function(accumulator, currentValue) {
return accumulator.concat(currentValue);
},
[]
);
winList2 = winList2.filter(e => (e !== ""));
winList2.join(',')
There may be a better JavaScript from those JavaScript masters.
This is not exactly the answer to your question, but one issue your JXA script has is that it is pulling in ALL processes (which can be a huge number), when all you probably need is those processes which are visible apps. So, let's start with that.
Here's the AppleScript to get a list of all non-empty window names, in a CSV list on one line, of all visible apps:
tell application "System Events"
set appList to (every application process whose background only is false)
set winList2 to {}
repeat with oApp in appList
set winList to (name of every window in oApp whose name is not "")
set winList2 to winList2 & (items of winList)
end repeat
end tell
set AppleScript's text item delimiters to ","
set winListText to winList2 as text
return winListText
-->All Notes,Keyboard Maestro Editor,macos - osascript is very slower than Script Editor - Stack Overflow - Google Chrome - JMichael,Untitled 2.scpt,Untitled 2
This should not be that hard to convert to JXA, but if you are going to just run it as is in a shell script using osascript, I see no advantage to converting to JXA.
I don't know the nature of your workflow, but if it were me I'd run this as a compiled script file (.scpt), and execute your bash script using the AppleScript do script (or JXA doScript()) command.
It would also be faster if you used a .scpt file with the osascript command.
I will continue to work on this script and convert it to JXA, for my own benefit if not yours.
I hope you find this useful. If not, maybe someone else will.
Questions?
Related
PreNote: I am open and hungry for any information, advice, tip etc.
Hello Everyone!
I am trying to create automation with applescript. This is my first personal applescript task but I have some valuable questions. Basically I am trying to catch live notifications from a website and display them in mac os notification.
I am trying to build process for a few days but I don't want to give a mess to you :) so I have roughly explained my process below.
(* Variables used in whole process
set $webToCheck > This is Safari webpage which I want to run my script on it. It won't be front window, script should be run with its name or other property.
set $theClass > This is class name of DOM element to check if it is exist or not. This class is not always exist on DOM of $webpage. It comes with notifications so when use it in "do Javascript" I got error "variable is not defined"
set $num > number of class to use in "do Javascript"
set $input > variable to assign HTML text
set $modifiedInput > Text of input seperated from HTML tags
*)
-- Step 1
tell application "Safari"
work on $webToCheck
-- Step 2
repeat until $input is not empty
set input do Javascript
document.getElementsByClassName > $theClass, $num of $webToCheck
end repeat
-- Step 3
modify text of $input to seperate from RAW HTML -- For example: <a class="" value=""> TEXT to be seperated </a>
Display notification $modifiedInput
-- Step 4
Go back to step 1 or 2 to check and display notification again
First of all, here are some general tips though:
Applescript won't accept $ at the start of variable names.
The variable assignment you are looking for is set {variable} to {value}. You can optionally at the end of it clarify the variable's class using as {class} at the end of the assignment.
Focusing a certain website does not happen with work on {URL} but as with most object oriented things in Applescript with the tell-statement. It will be shown in the full solution.
Text concatenation in Applescript happens with &. So something like "Hello " & "World" is the standard way to do it.
Modification of most things in Applescript happens with set.
It is easier to use innerText instead of innerHTML as splitting text in Applescript is a bit of a pain.
There is no goto but you could wrap the first few steps into a function, which are declared with on or to in Applescript.
Here is the full code with some documentation sprinkled in there:
global webToCheck, theClass, num, input --This says that all variables can be used even in functions.
set webToCheck to "youtube.com" --Strings can only use double quotes.
set theClass to "style-scope yt-alert-with-actions-renderer" --I will use an actual demo to prove that it is working
set num to 0 as integer -- This is the type declaration I was talking about. For numbers we have integer, real(float) and number.
set input to "" -- You don't have define everything at the top, but I will do so for this.
on displayNotification()
tell application "Safari"
tell window 1 -- This will target only the first window. For multiple windows you would have to write a repeat with-loop(for-loop), which I'm not going to do, for the sake of simplicity.
tell (first tab whose URL contains webToCheck) -- This targets just the first tab which contains the webToCheck variable.
set input to do JavaScript "document.getElementsByClassName('" & theClass & "')[" & num & "].innerText" -- This is the way I would go about writing the Javascript. I think you had something different in mind, but this works for my example.
display notification (paragraph 1 of input) with title webToCheck -- This displays the first line of my input since that is the relevant part. I also set a title so I doesn't just say "Script Editor"
end tell
end tell
end tell
end displayNotification
repeat 4 times -- I think this is quite obvious. Adjust this to your needs.
displayNotification()
delay 4
end repeat
Running this while having not used youtube on Safari in a while it displays this:
Note that this isn't the most elegant solution, but I think it is readable and it (hopefully) works for your needs.
I currently have an AppleScript that reads a hardcoded plain text file, which contains a long list, into a variable, which is then read into another variable as paragraphs. The script works very well for my purposes. This is basically what I am using:
set fileHandler to (read POSIX file "/path/to/my/file.txt")
set newList to paragraphs of fileHandler
repeat with i in newList
# do stuff
end repeat
The omitted script opens a Safari location, invokes JavaScript using i as a variable on that page, writes the result to a new plain text file, closes the Safari window, then repeats. It continues until it reaches the end of the list, then outside the repeat runs a do shell script that cleans up the new text file a bit.
The trouble is that every time I want to run the script using a different list, I have to open the hardcoded file and paste in the list. I'd rather just drop any .txt file on to an Automator application that wraps around my current script.
I've tried to use "Combine Text Files" and I feel like I get pretty close but I can't quite pass in the contents of the .txt file the way I'd like. I can't pass it in the same, unless I am doing something wrong with it. And "Get Value of Variable"/"Set Value of Variable" add on an extra "Text" item to my list, which I don't understand.
Ideally, I'd like to do something like this:
set fileHandler to (read POSIX file arg) -- the dropped text file
set newList to paragraphs of fileHandler
repeat with i in newList
# do stuff
end repeat
...but it doesn't work that way, unfortunately.
I'd really rather not reinvent a whole new script if I can help it. Any suggestions would be great. Thanks.
Automator is actually not needed. Save this code in Script Editor as application (bundle) and drop files onto it.
on open theFiles
repeat with aFile in theFiles
set newList to paragraphs of (read aFile)
repeat with i in newList
# do stuff
end repeat
end repeat
end open
thanks in advance for your help.
New to Applescript.
Trying to open in Preview all files in folder and save them.
The problem is that saving pops up a dialog half the time, which requires me to sit there hitting enter.
Code:
tell application "Finder"
set fl to files of folder POSIX file "/Users/myname/Desktop/myfolder/" as alias list
end tell
repeat with f in fl
tell application "Preview"
activate
open f
# Trying to save before the window has appeared will fail.
# Note: - This assumes that NO window was initially open.
# - The code should be made more robust to eventually time out.
repeat until (count of windows) > 0
delay 0.3
end repeat
save front document
close front document
end tell
end repeat
Thank you for the help
I made several PDF files and download from different sites and I get sample of version 1.3, 1.4, 1.5, 1.7, ..but no 1.6 ! For all of them no issue.
Anyway, because I have not been able to reproduce what you have, I took different approach.
1) I have added at top of the script, a list of encoding/version list which may required special treatment (like hit return key). You can of course amend these 2 lists to manage other cases you may have.
2) I changed your script to allow the script to get encoding and version values of the pdf. This is done by using spotlight data base via shell command 'mdls'. I use it 2 times to get version and encoding characteristics. The shell command returns characters before and after the value we want to get, so I use text x thru Y to extract the encoding and the version itself.
3) if PDF version/encoding are in the list predefined which requires special treatment, then I set OKReturn to true.
4) Just after the save instruction, the script now test if OKReturn is true. then I ask the script to hit return key for you. you may have to adjust this part, for instance, it could be not only 1 return, but 2 or something else. this is something I have not been able to test, because all my pdf are working. please keep in mind that because I simulate return key, you should not use the keyboard during the script run.
based on my test, I don't think the encoding is the blocking criteria. I think the version 1.6 is.Here is the script. It includes comment to make you able to adjust it:
set CodingReturn to {"Mac OS X 10.7.3 Quartz PDFContext"}
set VersionReturn to {"1.6"}
set myFolder to choose folder
tell application "Finder"
set fl to files of myFolder as alias list
end tell
repeat with f in fl
set FVersion to do shell script "mdls -name kMDItemVersion " & quoted form of POSIX path of f
set FEncoding to do shell script "mdls -name kMDItemEncodingApplications " & quoted form of POSIX path of f
if (length of FVersion) > 21 then set FVersion to text 19 thru -2 of FVersion -- extract only version number
if (length of FEncoding) > 42 then set FEncoding to text 38 thru -4 of FEncoding -- extract only the coding
set OKReturn to ((FVersion is in VersionReturn) and (FEncoding is in CodingReturn)) -- special treatment true/false
tell application "Preview"
activate
open f
repeat until (count of windows) > 0
delay 0.3
end repeat
save front document
if OKReturn then -- we need special key to be pressed
tell application "System Events" to keystroke return
end if
close front document
end tell
end repeat
I would be very interested to get your feedback about this version.
To save without the "save as" dialog popping up, add "in f". Here "f" was the filename. You could also put in a different path.
save front document in f
I have written a little AppleScript which returns "missing value" and I have no idea why this happens. The script is doing what it should do, but the output is not nice in my application where I use it.
The principle of this script is just to take the argument and run the file (with "VLC Media Player") which is provided through the argument.
So for example a use would be osascript open_video.scpt ~/Path/To/File/File.mp3
on run argv
tell application "VLC"
activate
open argv
end tell
end run
This question is old but brought to the top of the questions by a new answer before I added this answer.
The recently posted answer suggests this issue might be caused if there isn't an ending newline character, and this is not at all true in this case as I've tested both with and without an ending newline character and get the exact same results, both with compiled and plain text AppleScript scripts.
You do not need to have an ending newline character and it has nothing to do with why your code is returning missing value.
Looking in the AppleScript Dictionary for the open command, it shows the following:
open v : Open an object.
open alias : The file(s) to be opened.
→ document
Where the → represents returns as in it returns info about the documents, e.g. its name.
In this case VLC is returning missing value instead, and probably because VLC does not integrate AppleScript as nice as e.g. Apple's own apps.
If you do not want to see missing value then add a return "" to the code and it will return a blank line. Or you could add e.g., return "VLC is now playing: & (argv as string); however, because VLC's AppleScript integration is not the best, I'd just go with return "" as VLC does not respond to standard AppleScript error handling well. Again, an observation by testing error handling code that works with other apps but not VLC.
Or just simply directly use the command line open command, e.g:
open "/Applications/VLC.app" "/path/to/media_file.ext"
Which will open the VLC app, start the media_file.ext file, and return the prompt.
In my testing, you have to coerce the passed variable to a string.
osascript open_video.scpt '~/Path/To/File/File.mp3'
with a script as such:
on run argv
tell application "VLC"
activate
open (argv as string)
end tell
end run
Did you make sure that you have a newline on the end of the last line? For me I got this error when I had a script that ended abruptly at the last character of the script, instead of having a newline at the end.
AFAIK this doesn't affect the functionality of your script, just what it returns. In my case I got some other output instead after I made this change, but the output appeared to be merely informational.
I have an filemaker Pro 13 database with around 120 records (it's for a conference). I want to team it up with BBEdit to create individual files for each abstract, thus applescript. Much to my surprise (and despite a lot of web tips on scripting) '[tag:current record]' is not recognised in the script.
The relevant bit is this:
FM Script:
Loop
Perform Applescript
tell application "FileMaker Pro"
activate
set MyFileName to cell "WebAbstractFileName" of table "SelectionProcess"
set MyWebAbstract to cell "WebAbstract" of table "SelectionProcess" as text
end tell
-- (BBEdit bit, which works fine in testing)
Go to Next Record (exit after last)
End Loop
This works fine if I only want to retrieve the first record!
This applescript is set within a filemaker script which loops through the records but the script doesn't care which record it's in.
I've tried adding 'of current record' before the table reference but it then gives me errors (eg error "FileMaker Pro got an error: Object not found." number -1728 from cell "WebAbstractFileName" of current record of table "SelectionProcess") Without 'current record' it works fine, but only gives me the first record.
Here's (roughly) how you could do this in a Filemaker script:
Go to Layout [ “YourTable” ]
# FIND THE RECORDS OF INTEREST
Perform Find [ Restore ]
Go to Record/Request/Page [ First ]
Loop
New Window [ ]
# ISOLATE THE CURRENT RECORD
Show All Records
Omit Record
Show Omitted Only
Set Variable [ $path; Value:Get ( DocumentsPath ) & "someFolder/" & YourTable::Somefield & ".html" ]
Export Records [ No dialog; “$path” ]
Close Window [ Current Window ]
Go to Record/Request/Page [ Next; Exit after last ]
End Loop
This will export every record in the found set as an individual file into the folder "someFolder" located in the user's Documents folder, using the contents of the YourTable::Somefield field as the filename.
If, as you say, you don't need the separate files, then of course this can be much simpler.
More tinkering solved this. The crucial bit was to change the syntax. The script now reads:
tell application "FileMaker Pro"
activate
tell current record to set MyFileName to cell "WebAbstractFileName"
tell current record to set MyWebAbstract to cell "WebAbstract"
end tell
What seems to happen is that the fields must be visible (yet I had that not be a problem at one point...go figure. If they're visible, you can drop the table specification). This script, wrapped in a Loop block, will act on the found set and (if instructed) exit after the last record.
I append the bbedit script to create a new file and save it with a variable taken from another field in case it's of interest.
tell application "BBEdit"
set notesPath to ":Users:ophiochos:Dropbox:TL Conference Admin:Webpage materials:Abstracts:"
set newFilePath to notesPath & MyFileName & ".html"
set newDoc to make new text document with properties {contents:MyWebAbstract}
tell newDoc
set source language to "HTML"
save to newFilePath
close window
end tell
end tell
Or you could simply create a calculated field that contains the contents of the desired export file for each record, loop through the records one by one, and just use an Export Field Contents ['YourCalculatedField_c'] script step.
You can also use FM to preview the html output by using a web viewer to display your html. This, along with exporting individually, you can get all this functionality without the need for an external program.
If you need to change the text encoding for output files, you can also specify those in an xslt for outputting files:
http://filemakerhacks.com/2012/09/23/export-field-contents-as-utf-8/
Also, if performing applescript from within FileMaker, the "tell application" line is not needed since it is implied.