AppleScriptingBridge for Music app not returning sources - applescript

With the advent of Catalina OSX, iTunes has been removed from OSX, instead a Music app has been introduced, apparently similar but exclusively for music content with podcast etc.
Following code written in Objective C does not return any sources from the music, iTunes object.
Similar code worked for iTunes. but is not working for new Music app of apple.
Please advise, any experts.
MusicApplication* music = [SBApplication applicationWithBundleIdentifier:#"com.apple.music"];
NSArray *sources = [music sources];
// sources are empty, zero length array always
for (MusicSource *source in sources)
{
SBElementArray *userPlaylists = [source userPlaylists];
for(MusicUserPlaylist* playList in userPlaylists)
{ }
}

Related

Register for global file drag events in Cocoa

I'm trying to be notified when a OS X user is dragging any file in OS X, not only in my app.
My current approach was using addGlobalMonitorForEventsMatchingMask:handler: on NSEvent, as follows:
[NSEvent addGlobalMonitorForEventsMatchingMask:NSLeftMouseDraggedMask handler:^(NSEvent* event) {
NSPasteboard* pb = [NSPasteboard pasteboardWithName:NSDragPboard];
NSLog(#"%#", [pb propertyListForType:NSFilenamesPboardType]);
}];
This works partially - the handler is being called when I start dragging a file from my desktop or Finder, however it also is being called when I perform every other operation that contains a left-mouse-drag, e.g. moving a window. The issue is that the NSDragPboard still seems to contain the latest dragged file URL e.g. when I let off the file and start moving a window, which makes it hard to distinguish between these operations.
TL;DR - I am interested in file drag operations system-wide. I do not need any information about the dragged file itself, just the information that a file drag operation has been started or stopped. I would appreciate any hint to a possible solution for this question.
After having talked to Apple DTS, this is most likely a bug. I have filed rdar://25892115 for this issue. There currently seems to be no way to solve my original question with the given API.
To solve my problem, I am now using the Accessibility API to figure out if the item below the cursor is a file (kAXFilenameAttribute is not NULL).
NSPasteboard* pb = [NSPasteboard pasteboardWithName:NSDragPboard];
NSArray* filenames = [pb propertyListForType:NSFilenamesPboardType];
NSInteger changeCount = pb.changeCount;
//when moving a window. the changeCount is not changed, use it to distinguish
if (filenames.count > 0 && self.lastChangeCount != changeCount){
self.lastChangeCount = changeCount;
//your code here
}

Cannot play album with AVAudioPlayer

I'm working on a very simple OSX application that will allow me to play either a song or a folder of songs.
I can choose a song and play it and everything is fine. I can choose a folder and create an array of songs and... play the last one.
var currentSong: NSURL?
var album: [NSURL] = []
var audioPlayer = AVAudioPlayer()
My playing function is
func playCurrent(){
do {
try audioPlayer = AVAudioPlayer(contentsOfURL: currentSong!)
} catch {
print("Ignoring errors for now")
}
audioPlayer.play()
}
This works fine whenever I set currentSong to a NSURL. I choose one song, it plays it.
My album function is as follows:
#IBAction func chooseAlbumAndPlay(sender: AnyObject) {
album = albumFromFile()
for song in album {
currentSong = song
playCurrent()
}
}
and here I have the problem. albumFromFile opens an NSOpenPanel, lets me choose a folder, and dumps paths to playable items into an array of NSURLs. This part works, I've verified it, so I really have an array with 12 or 20 or whatever correct, playable URLs. If I just run it as is, only the last song in any album gets played. If I set a breakpoint in the playCurrent() function, I can hear that it will actually play a tiny snippet - less than a note in most cases - of all songs but the last.
According to the documentation, play() returns a boolean - and it will happily report that it has finished playing every song in this loop.
My - human - opinion is that a song has only finished playing when I have heard all of it.
If I query the duration of the current AVAudioPlayer, they all report perfectly reasonable-sounding values.
I'm completely stumped here. PlayCurrent seems to completely fail to assert itself as a running function. The expected behaviour is that it will not exit until play() has finished playing; observed behaviour is that it will touch every song for the briefest time, go 'been there' and return to the enclosing loop.
How can I force AVAudioPlayer to play the whole of a file before exiting my playCurrent() function? And where would I have found that information? (The class documentation is unhelpful, the mentioned audio guides do not exist - right now, the Developer Library does not mention any basic audio guides for OSX.
The answer is, once you get around to it, very obvious indeed. Unfortunately, the documentation really is no help at all. I could, in theory, have found the solution by looking at the entry for AVAudioPlayer in the AVFoundationFramework reference, but I didn't. (I found it by wildly casting about and a chance mention).
There appear to be no working OSX examples; Apple's iOS example uses the MediaPlayer framework that is not available on OSX.
The solution is as follows:
An AVAudioPlayer plays a single sound.
If you want to play more than one sound, you need an AVQueuePlayer which can also play a single sound from a URL or a queue of AVPlayerItems (this class has an init(URL:) method).
If anyone has an idea why AVAudioPlayer behaves as it does, I'm still interested to hear it.

How to automatically pair camera and microphone in QTKit

I am programming a video capture application using QTKit. It is set up so that users must select a webcam from the list obtained with [QTCaptureDevice inputDevicesWithMediaType: QTMediaTypeVideo]. I want the user to be able to choose a camera, and have the corresponding microphone automatically selected, but I don't see a way to accomplish this within QTKit.
My application needs to run on OSX 10.6 to 10.8, so I can't use AVFoundation, which arrived in 10.7. QuickTime is deprecated at this point.
So the question is: On Mac OS 10.6 to 10.8, how can I automatically match a webcam camera with it's embedded microphone.
Thanks
The documented way of doing this is found in the QTCaptureDevice Class Reference,
using method attributeForKey with a key of QTCaptureDeviceLinkeDevicesAttribute, which may be called like so.
QTCaptureDevice* device = [QTCaptureDevice deviceWithUniqueID:deviceUniqueID];
QTCaptureDevice* sibling = Nil;
NSArray* linkedDevices = [device attributeForKey: QTCaptureDeviceLinkeDevicesAttribute
NSUInteger linkedCount = [linkedDevices count];
for (NSUInteger i = 0; i < linkedCount; i++)
{
sibling = [linkedDevicesobjectAtIndex: i];
.
.
.
}
However, I have not seen this work, the returned array is always Nil. Additionally This Apple Mailing List Archive suggests that it may only work for Apple iSight devices :(.
Finally, An additional sample may be found here: Apple QTRecorder Sample

Notification of active document change on OS X?

I'm using NSWorkspace's NSWorkspaceDidActivateApplicationNotification to detect when the active application changes. I get NSRunningApplication from the userInfo key of the notification.
I need to get a notification when the active document changes. I can get the active document by using the accessibility framework's NSAccessibilityDocumentAttribute key through AXUIElementCopyAttributeValue().
I need a more accurate way of detecting when the document changes other than polling. Some applications use multiple windows, while others use a single window with multiple tabs. With tabbed applications the window returns the currently viewed document.
I don't have to use the accessibility framework. AppleScript (scripting bridge) seems to also be able to get a window's document, but the accessibility framework seems to work with more applications.
I only care about the active document, of the active window, of the active application. What currently has focus on the system.
I've been testing with applications like Sublime Text 2, and Xcode. Sublime returns the currently selected tab, where Xcode returns the active project.
I was actually trying to achieve exactly the same thing and I think I've found a solution for it.
What I did was using CFArrayRef windowList = CGWindowListCopyWindowInfo(kCGWindowListOptionOnScreenOnly | kCGWindowListExcludeDesktopElements, kCGNullWindowID);
That will give you a list of all the active windows, including windows that you probably don't care about.
I only care about windows that have kCGWindowLayer = 0; so I filtered the windows that are on layer 0.
Here's how I did it:
CFArrayRef windowList = CGWindowListCopyWindowInfo(kCGWindowListOptionOnScreenOnly | kCGWindowListExcludeDesktopElements, kCGNullWindowID);
NSMutableArray *data = [(__bridge NSArray *) windowList mutableCopy];
NSMutableArray *filteredData = [[NSMutableArray alloc] initWithCapacity:10];
for (NSMutableDictionary *theDict in data) {
id layer = [theDict objectForKey:(id)kCGWindowLayer];
if ([layer intValue] == 0) {
[filteredData addObject:theDict];
}
}
NSLog(#"window: %#", filteredData);
This might be the most elegant solution, so if anyone else has a better idea, please share. Also you should have a look at Apple's demo app Son of Grab.

Xcode app retrieving "now playing" music title even though it's paused

I'm creating a mac app that retrieves the title of the "now playing" music on iTunes.
I'm using this code:
iTunesApplication *iTunes = [SBApplication applicationWithBundleIdentifier:#"com.apple.iTunes"];
NSString* fullTrackName = [NSString stringWithFormat:#"Now Playing: %# - %#", iTunes.currentTrack.artist, iTunes.currentTrack.name];
NSLog(#"%#", fullTrackName);
If a music is playing it works just fine, but if i pause iTunes and run the code it will automatically grab the music name, even though it's not playing.
How can i fix it?
Thank you.
And btw, i'm using iTunes.h and ScriptingBridge.framework for this.
You probably need to look for a player state property on iTunesApplication to give you the state of the current track.
Apparently it's like this:
if ([iTunes playerState] == iTunesEPlSPlaying) {
// Code if iTunes is not playing
}

Resources