Add movie to iTunes using Scripting Bridge - objective-c

I want to use Scripting Bridge to add a movie to iTunes. And preferably letting me choose between a 'music video' and a 'movie'. I know both Objective-C and AppleScript so I thought it wouldn't be that hard but I can't figure it out. I know how I would use NSAppleScript for it but I'm targeting 10.5 or later and read that Scripting Bridge obsoletes NSAppleScript. Is that right?
All I got is
iTunesApplication *iTunes = [SBApplication applicationWithBundleIdentifier: #"com.apple.iTunes"];
Which is, as you can see, not much at all.

Step 1. Generate iTunes.h header file:
sdef /Applications/iTunes.app | sdp -fh --basename "iTunes"
Step 2. The code to add a media file looks like the following:
NSString* sourceMediaFile = ...;
iTunesApplication *iTunes = [SBApplication applicationWithBundleIdentifier:#"com.apple.iTunes"];
iTunesTrack * track = [iTunes add:[NSArray arrayWithObject:[NSURL fileURLWithPath:sourceMediaFile]] to:nil];
NSLog(#"Added %# to track: %#",sourceMediaFile,track);

You should use the "scripting definition processor" (sdp) program to generate a header file from iTunes' scripting definition (.sdef) file (which you can get using the sdef program):
sdef /Applications/iTunes.app | sdp -fh --basename "iTunes"
This'll give you a file called iTunes.h. Then you include that header into your project and read through it to see what the iTunes scripting bridge interface offers.
If it seems like you won't be able to do this with the scripting bridge (it's possible -- not everything that can be done via an app's AppleScript interface can also be done via the scripting bridge), just go ahead and write an AppleScript to do it instead, and then execute that in your program with NSAppleScript.

For the second parameter, it takes a playlist object (or nil as previously mentioned). Once you have fetched an instance of a iTunesPlaylist* object through some means (there are several depending on your needs), you can pass it as the second parameter.

Related

Opening a file using ScriptingBridge

I have an AppleScript that I am trying to convert to ScriptingBridge. Since my application is a C++/Obj-C application, ScriptingBridge is much easier to use and quite a bit faster (not to mention I hate dynamically building AppleScripts).
The AppleScript sends a message to Photoshop to open a file. The file parameter is sent as an alias, but ScriptingBridge imports the parameter as an id. I don't know what Obj-C object I should pass in?
I've tried passing an NSURL and an NSString (probably incorrectly :-P), but to no avail. Any suggestions on what I should be passing for the file alias?
The short answer is that you can't open documents in Photoshop with Scripting Bridge.
Apple's docs really spell it out like it is. All classes must have a container, which is a mutable array, that they need to be added to before they can be acted upon, as shown in the generated header...
#interface photoshopCS4Application : SBApplication
- (SBElementArray *) documents;
- (SBElementArray *) fonts;
- (SBElementArray *) notifiers;
... and that is the complete list of top-level containers available to us. The open command requires a photoshopCS4OpenOptions to be generated and populated. Because the API doesn't expose the array to store the newly created photoshopCS4OpenOptions object, we can't use a newly created photoshopCS4OpenOptions object. Therefore we can't make a target document and by extensions can't use the open command in Scripting Bridge. The same can be said of all the commands that require some kind of options object.
The only workaround that I have sorted out is to either open a document with native Applescript called from Cocoa or objc-appscript, and then parse the documents array looking for the one just opened. It's not ideal, but then neither is Scripting Bridge because it requires application developers write their scripting APIs in a very specific way that is not native to the OSA framework.
If your program is such that opening a Photoshop document can be executed outside your AppleScript script/Scripting Bridge code, Cocoa provides a method to open files with a specific application:
[[NSWorkspace sharedWorkspace] openFile:#"/Users/bavarious/Desktop/test.psd" withApplication:#"Adobe Photoshop CS4"];
or, if you want to use the default application that handles that file type, you can drop the application name altogether:
[[NSWorkspace sharedWorkspace] openFile:#"/Users/bavarious/Desktop/test.psd"];
Consider Appscript. http://appscript.sourceforge.net/
Here's the code using that:
APApplication *adobePhotoshopCs4 = [APApplication applicationWithName: #"Adobe Photoshop CS4"];
id result = [[adobePhotoshopCs4 open_] send];
(Note, I'm not a Cocoa programmer - I mainly use Appscript with Python but Appscript comes with ASTranslate which translates Applescript into Python, Ruby or Obj-C and that's the output - but I've found there are subtle mistakes in the past sometimes with the translator)

Get Path to selected file in Finder

How would I retrieve an array of paths to the selected files in Finder?
I have searched around but have only found links regarding AppleScript. I have also looked at NSWorkspace and NSFileManager but I didn't find anything.
Expanding on #Bavarious's (correct) answer, here's how I've gotten the selection from Finder using the Scripting Bridge:
#import "Finder.h" //my copy is here: https://github.com/davedelong/BetterInfo/blob/master/Finder.h
FinderApplication * finder = [SBApplication applicationWithBundleIdentifier:#"com.apple.finder"];
SBElementArray * selection = [[finder selection] get];
NSArray * items = [selection arrayByApplyingSelector:#selector(URL)];
for (NSString * item in items) {
NSURL * url = [NSURL URLWithString:item];
NSLog(#"selected item url: %#", url);
}
If it is possible to get the list of selected files in a given Finder window using AppleScript, you can probably use Scripting Bridge in a Cocoa application to interface with Finder. Quoting Apple’s documentation,
Scripting Bridge is a framework and a technology that makes it much easier for Cocoa developers to control and communicate with scriptable applications. Instead of incorporating AppleScript scripts in your application or dealing with the complexities of sending and handling Apple events, you can simply send Objective-C messages to an object that represents an application with a scripting interface. Your Cocoa application can do anything an AppleScript script can, but it does so in Objective-C code that is integrated with the rest of your project’s code.
There is no Cocoa class that represents Finder or, more specifically, Finder windows. Finder is an application, and a scriptable application at that, so Scripting Bridge is the way to go.

cocoa open multiple files with associated app

I have an array of filenames that i would like to pass to an external application for opening. I'd like to do one of the following:
a) Somehow instruct OSX to open all these files with an associated application, but it must invoke the target app's openFiles NSApplication delegate method
b) Specify the application to open these files with (and also invoke openFiles)
Basically it doesn't matter which solution to realise, because these files will be associated with the target application anyway. How would i do one of these things?
To open a whole bunch of files at once, send the shared NSWorkspace object an openURLs:withAppBundleIdentifier:options:additionalEventParamDescriptor:launchIdentifiers: message, or call the LSOpenURLsWithRole function or the LSOpenFromURLSpec function. Either way, you'll pass an array of URLs to items to open.
Each one of these will let you identify a specific application to use. NSWorkspace lets you specify it by bundle identifier, while the two Launch Services functions let you provide the URL or FSRef to a specific application bundle.
… it must invoke the target app's openFiles NSApplication delegate method
That isn't possible to require, because (a) the application may be document-based, in which case it probably does not have an NSApplication delegate and, even if it does, such a delegate would probably not respond to application:openFiles:, and (b) the application may not be Cocoa-based, in which case it would handle the Open Documents Apple Event directly. None of this is your application's business, so don't worry about it.
First add your video files to Resources Folder.The code like follow:
NSString* bundlePath = [[NSBundle mainBundle] bundlePath];
NSString* videoPath = [bundlePath stringByAppendingPathComponent:#"/Contents/Resources/video.mov"];
[[NSWorkspace sharedWorkspace] openFile:videoPath];

How can I discover what apps are installed on OS X using Objective C or Macruby?

could you tell me please - which object and which method can i use to fetch information about installed applications on OSX with objective c or macruby?
You can just call the Apple command-line tool system_profiler, e.g.
% system_profiler SPApplicationsDataType
For C or Objective-C you could use system() to do this.
For more info:
% man system
% man system_profiler
If you meant to list all of GUI apps, you can use Launch Services. For the list of functions, see the reference.
Do not manually list .app bundles! It's already done by the system, and you just have to query it.
There is a command-line program called lsregister at
/System/Library/Frameworks/CoreServices.framework/Frameworks/LaunchServices.framework/Support/lsregister
which can be used to get the detailed dump of the Launch Service database. This tool is sometimes helpful,
but don't depend on that in a shipping program,
OS X doesn't really have a notion of "installing an app". OS X applications are self contained bundles that appear in the Finder as a single virtual file. There's no central registry, just a convention that applications are placed in /Applications or ~/Applications for a per user "install".
About the best you can do will be enumerate those directories for their contents, and every ".app" directory you find is an application. It will not be a exhaustive (I often run small apps from e.g. my Desktop if I've just downloaded them).
I moved in a following way:
execute command system_profiler SPApplicationsDataType -xml
from code. Key SPApplicationsDataType means that only application data is required. -xml means that I expect to see xml result for easier parsing.
parse result array
Here you can find nice example regarding command execution from code: Execute a terminal command from a Cocoa app
Total code example is looked like following:
NSStirng * commangString = #"system_profiler SPApplicationsDataType -xml";
NSData * resultData = [CommandLineTool runCommand:commangString];
NSArray * appArray = [PlistManager objectFromData:resultData];
// parse array of NSDictionaries here ....
// method from PlistManager
+ (id) objectFromData: (NSData *) data {
NSString *errorString = nil;
NSPropertyListFormat format;
id object = [NSPropertyListSerialization propertyListFromData:data mutabilityOption:NSPropertyListMutableContainers format:&format errorDescription:&errorString];
if (!errorString) {
return object;
} else {
NSLog(#"Error while plist to object conversion: %#", errorString);
return nil;
}
}
// runCommand method was used from post I mentioned before
Use Spotlight via NSMetadataQuery.
You can view the results you'll get using mdfind at the command line:
mdfind "kMDItemContentType == 'com.apple.application-bundle'"
Works like a charm and very very fast.
[[NSWorkspace sharedWorkspace] absolutePathForAppBundleWithIdentifier:#"com.google.Chrome"];
This would return a path to Google Chrome app if it is installed, nil otherwise.
If you don't know the BundleID, there are two options to find it:
1) Open .plist file of the app by right-clicking the app icon and choosing Show Package Contents option.
Default path to Chrome .plist is /Applications/Google\ Chrome.app/Contents/Info.plist
2) Use lsregister alongside with grep.
Try typing the following into the Terminal app, you will find the BundleID there:
/System/Library/Frameworks/CoreServices.framework/Frameworks/LaunchServices.framework/Support/lsregister -dump | grep -i chrome
As it is a unix-like OS there is no real way of telling what you have installed (unless of course you have control of the other app - then there is a lot of ways of doing this for obvious reasons).
Generally apps get put into /Applications or ~/Applications BUT you can run it from anywhere and not those folders (just like a unix machine)

Make new chat in Adium using Cocoa ScriptingBridge

The following AppleScript code works fine:
tell application "Adium" to tell first account to make new chat with contacts {first contact} with new chat window
But how can I do the same using Cocoa's ScriptingBridge?
Generally, you ought to be able to do it following Apple's Scripting Bridge Programming Guide for Cocoa. To start, I created a header file for Adium by running sdef /Applications/Adium.app | sdp -fh --basename Adium in Terminal (creates Adium.h in the current directory). The header file produced gives clues about making the AppleScript calls via the Scripting Bridge.
The problem that I ran into is that I cannot see a way, based on the generated header file, to do make new chat with contacts {...} with new chat window (I can make a new chat and maybe even hook it into a new window, but I could not find a way to make that chat take the contact).
The next best thing might be to use NSAppleScript to execute your valid AppleScript code:
NSAppleScript *appleScript = [[NSAppleScript alloc] initWithSource:#"tell application \"Adium\" to tell first account to make new chat with contacts {first contact} with new chat window"];
NSDictionary *errorDictionary;
NSAppleEventDescriptor *eventDescriptor = [appleScript executeAndReturnError:&errorDictionary];
Short of using raw Apple event codes, you can't. Should work with objc-appscript though. Running your AppleScript command through appscript's ASTranslate tool produces the following:
#import "ADGlue/ADGlue.h"
ADApplication *adium = [ADApplication applicationWithName: #"Adium"];
ADReference *ref = [[adium accounts] at: 1];
ADMakeCommand *cmd = [[[[ref make] newChatWindow: ASTrue] withContacts: [NSArray arrayWithObject: [[[[adium accounts] at: 1] contacts] at: 1]]] new_: [ADConstant chat]];
id result = [cmd send];