Cocoa iTunes ScriptingBridge - How to play playlist shuffled / unshuffled playlist? - objective-c

I see there is a property shuffle but it doesn't seem to do anything (it still plays shuffled).
I tried this but it plays shuffled. I'd like it unshuffled. Is this not possible?
iTunesPlaylist *p;
for (iTunesSource *source in [iTunes sources]) {
if ([source kind] == iTunesESrcLibrary) {
p = [[source userPlaylists] objectWithName:playlist];
break;
}
}
if (p != nil) {
p.shuffle = NO;
[p playOnce:NO];
}

A bug in iTunes 11 breaks the AppleScript (and hence Scripting Bridge) Shuffle command.

Related

Find out if Mac is Force Touch - capable

Is it possible to find out if a Mac is Force Touch capable - either via a built-in Trackpad, like the new MacBook, or a Bluetooth device like the Magic Trackpad 2?
I'd like to present preferences specific to Force Touch if the Mac is Force Touch capable, but not display (or disable) those preferences if Force Touch is not available.
In the portion after the separator, you see the options I have in mind in the pic linked here. (sorry, embedding the pic itself didn't work).
So, not showing the preferences wouldn't restrict users who don't have force touch, it would just let users who have it configure how it should work, and those settings would be useless to users who don't have it.
Is there a way to achieve this?
Thank you and kind regards,
Matt
Edit: It's in Objective-C.
I figured it out:
+ (BOOL)isForceTouchCapable
{
if (![[self class] isAtLeastElCapitan])
return NO;
io_iterator_t iterator;
//get default HIDDevice dictionary
CFMutableDictionaryRef mDict = IOServiceMatching(kIOHIDDeviceKey);
//add manufacturer "Apple Inc." to dict
CFDictionaryAddValue(mDict, CFSTR(kIOHIDManufacturerKey), CFSTR("Apple Inc."));
//get matching services, depending on dict
IOReturn ioReturnValue = IOServiceGetMatchingServices(kIOMasterPortDefault, mDict, &iterator);
BOOL result = YES;
if (ioReturnValue != kIOReturnSuccess)
NSLog(#"error getting matching services for force touch devices");
else
{
//recursively go through each device found and its children and grandchildren, etc.
result = [[self class] _containsForceTouchDevice:iterator];
IOObjectRelease(iterator);
}
return result;
}
+ (BOOL)_containsForceTouchDevice:(io_iterator_t)iterator
{
io_object_t object = 0;
BOOL success = NO;
while ((object = IOIteratorNext(iterator)))
{
CFMutableDictionaryRef result = NULL;
kern_return_t state = IORegistryEntryCreateCFProperties(object, &result, kCFAllocatorDefault, 0);
if (state == KERN_SUCCESS && result != NULL)
{
if (CFDictionaryContainsKey(result, CFSTR("DefaultMultitouchProperties")))
{
CFDictionaryRef dict = CFDictionaryGetValue(result, CFSTR("DefaultMultitouchProperties"));
CFTypeRef val = NULL;
if (CFDictionaryGetValueIfPresent(dict, CFSTR("ForceSupported"), &val))
{
Boolean aBool = CFBooleanGetValue(val);
if (aBool) //supported
success = YES;
}
}
}
if (result != NULL)
CFRelease(result);
if (success)
{
IOObjectRelease(object);
break;
} else
{
io_iterator_t childIterator = 0;
kern_return_t err = IORegistryEntryGetChildIterator(object, kIOServicePlane, &childIterator);
if (!err)
{
success = [[self class] _containsForceTouchDevice:childIterator];
IOObjectRelease(childIterator);
} else
success = NO;
IOObjectRelease(object);
}
}
return success;
}
Just call + (BOOL)isForceTouchCapable and it will return YES if a Force Touch device is available (a Magic Trackpad 2 or a built in force-touch-trackpad) or NO if there isn't.
For those interested in how this came to be, I wrote about it on my blog with an example project.

Skip to Previous AVPlayerItem on AVQueuePlayer / Play selected Item from queue

I am playing a Tv-show that has been sliced to different chapters on my project using an AVQueuePlayer.
I also want to offer the possibility to skip to the previous/next chapter or to select a different chapter on the fly, while the AVQueuePlayer is already playing.
Skipping to next Item is no problem with the advanceToNextItem provided by AVQueuePlayer, but there is nothing alike for skipping back or playing a certainitem from the queue.
So I am not quite sure what would be the best approach here:
Using an AVPlayer instead of AVQueuePlayer, invoke replaceCurrentItemWithPlayerItem: at actionAtItemEnd to play the nextItem and just use 'replaceCurrentItemWithPlayerItem' to let the User select a certain Chapter
or
reorganise the queue or the current player by using 'insertItem:afterItem:' and 'removeAllItems'
Additional information:
I store the Path to the different videos in the order they should appear in a NSArray
The user is supposed to jump to certain chapters by pressing buttons that represent the chapter. The Buttons have tags, that are also the indexes of the corresponding videos in the array.
Hope I could make myself clear?
Anyone having any experience with this situation?
If anyone knows where to buy a good IOS VideoPlayerFramework which provides the functionality, I would also appreciate the link.
If you want your program can play previous item and play the selected item from your playeritems(NSArray),you can do this.
- (void)playAtIndex:(NSInteger)index
{
[player removeAllItems];
for (int i = index; i <playerItems.count; i ++) {
AVPlayerItem* obj = [playerItems objectAtIndex:i];
if ([player canInsertItem:obj afterItem:nil]) {
[obj seekToTime:kCMTimeZero];
[player insertItem:obj afterItem:nil];
}
}
}
edit:
playerItems is the NSMutableArray(NSArray) where you store your AVPlayerItems.
The first answer removes all items from AVQueuePlayer, and repopulates queue starting with iteration passed as index arg. This would start the newly populated queue with previous item(assuming you passed correct index) as well the rest of the items in existing playerItems array from that point forward, BUT it does not allow for multiple reverses, e.g. you are on track 10 and want to go back and replay track 9, then replay track 5, with above you cannot accomplish. But here you can...
-(IBAction) prevSongTapped: (id) sender
{
if (globalSongCount>1){ //keep running tally of items already played
[self resetAVQueue]; //removes all items from AVQueuePlayer
for (int i = 1; i < globalSongCount-1; i++){
[audioQueuePlayer advanceToNextItem];
}
globalSongCount--;
[audioQueuePlayer play];
}
}
The following code allows you to jump to any item in your. No playerhead advancing. Plain and simple. playerItemList is your NSArray with AVPlayerItem objects.
- (void)playAtIndex:(NSInteger)index
{
[audioPlayer removeAllItems];
AVPlayerItem* obj = [playerItemList objectAtIndex:index];
[obj seekToTime:kCMTimeZero];
[audioPlayer insertItem:obj afterItem:nil];
[audioPlayer play];
}
djiovann created a subclass of AVQueuePlayer that provides exactly this functionality.
You can find it on github.
I haven't tested it yet but from browsing through the code it seems to get the job done. Also the code is well documented, so it should at least serve as a good reference for a custom implementation of the functionality (I suggest using a category instead of subclassing though).
This should be the responsability of the AVQueuePlayer object and not your view controller itself, thus you should make it reusable and expose other answers implementations through an extension and use it in a similar way of advanceToNextItem() :
extension AVQueuePlayer {
func advanceToPreviousItem(for currentItem: Int, with initialItems: [AVPlayerItem]) {
self.removeAllItems()
for i in currentItem..<initialItems.count {
let obj: AVPlayerItem? = initialItems[i]
if self.canInsert(obj!, after: nil) {
obj?.seek(to: kCMTimeZero, completionHandler: nil)
self.insert(obj!, after: nil)
}
}
}
}
Usage (you only have to store an index and a reference to initial queue player items) :
self.queuePlayer.advanceToPreviousItem(for: self.currentIndex, with: self.playerItems)
One way of maintaining an index is to observe the AVPlayerItemDidPlayToEndTime notification for each of your video items :
func addDidFinishObserver() {
queuePlayer.items().forEach { item in
NotificationCenter.default.addObserver(self, selector: #selector(playerDidFinishPlaying), name: Notification.Name.AVPlayerItemDidPlayToEndTime, object: item)
}
}
func removeDidFinishObserver() {
queuePlayer.items().forEach { item in
NotificationCenter.default.removeObserver(self, name: NSNotification.Name.AVPlayerItemDidPlayToEndTime, object: item)
}
}
#objc func playerDidFinishPlaying(note: NSNotification) {
if queuePlayer.currentItem == queuePlayer.items().last {
print("last item finished")
} else {
print("item \(currentIndex) finished")
currentIndex += 1
}
}
This observation can also be really useful for other use cases (progress bar, current video timer reset ...).
Swift 5.2
var playerItems = [AVPlayerItem]()
func play(at itemIndex: Int) {
player.removeAllItems()
for index in itemIndex...playerItems.count {
if let item = playerItems[safe: index] {
if player.canInsert(item, after: nil) {
item.seek(to: .zero, completionHandler: nil)
player.insert(item, after: nil)
}
}
}
}
#saiday's answer works for me, here is swift version of his answer
func play(at index: Int) {
queue.removeAllItems()
for i in index..<items.count {
let obj: AVPlayerItem? = items[i]
if queue.canInsert(obj!, after: nil) {
obj?.seek(to: kCMTimeZero, completionHandler: nil)
queue.insert(obj!, after: nil)
}
}
}
If you want to play a song from any index using AVQueuePlayer.Then this below code can help to.
NSMutableArray *musicListarray (add song that you want to play in queue);
AVQueuePlayer *audioPlayer;
AVPlayerItem *item;
-(void) nextTapped
{
nowPlayingIndex = nowPlayingIndex + 1;
if (nowPlayingIndex > musicListarray.count)
{
}
else
{
[self playTrack];
}
}
-(void) playback
{
if (nowPlayingIndex < 0)
{
}
else
{
nowPlayingIndex = nowPlayingIndex - 1;
[self playTrack];
}
}
-(void) playTrack
{
#try
{
if (musicArray.count > 0)
{
item =[[AVPlayerItem alloc] initWithURL: [NSURL URLWithString:musicListarray
[nowPlayingIndex]]];
[audioPlayer replaceCurrentItemWithPlayerItem:item];
[audioPlayer play];
}
}
#catch (NSException *exception)
{
}
}
-(void) PlaySongAtIndex
{
//yore code...
nowPlayingIndex = i (stating index from queue)[audioPlayer play];
[self playTrack];
}
Here PlaySongAtIndex call when you want to play a song.

How to get the Start Timecode (SMPTE) of a Quicktime-Movie in Objective-C in 64-bit?

I've been pulling my hair out over this.
I've found a few things here, but nothing actually seems to work. And the documentation is really limited.
What I'm trying to figure out here is how to get the start time code of a Quicktime movie in Objective-C from the timecode track, and getting a human-readable output from that.
I've found this:
SMPTE TimeCode from Quick Time
It works perfectly in 32-bit mode. But it doesn't work in 64-bit mode because of the Quicktime API. The software I need to incorporate it into already has been and must continue to run 64-bit.
I'm losing my mind here. Anyone out there know about these APIs?
Ultimately, the goal here is to figure out the start timecode of the Quicktime because its needed to set the OFFSET in FCP-X XML files. Without it, the video files are brought in without audio (or, really, its just slipped a lot).
Use AVFoundation framework instead of QuickTime. The player initialisation is well explained in the documentation: https://developer.apple.com/library/mac/#documentation/AudioVideo/Conceptual/AVFoundationPG/Articles/02_Playback.html#//apple_ref/doc/uid/TP40010188-CH3-SW2
Once your AVAsset is loaded in memory, you can extract the first sample frame number (timeStampFrame) by reading the content of the timecode track if present:
long timeStampFrame = 0;
for (AVAssetTrack * track in [_asset tracks]) {
if ([[track mediaType] isEqualToString:AVMediaTypeTimecode]) {
AVAssetReader *assetReader = [AVAssetReader assetReaderWithAsset:_asset error:nil];
AVAssetReaderTrackOutput *assetReaderOutput = [AVAssetReaderTrackOutput assetReaderTrackOutputWithTrack:track outputSettings:nil];
if ([assetReader canAddOutput:assetReaderOutput]) {
[assetReader addOutput:assetReaderOutput];
if ([assetReader startReading] == YES) {
int count = 0;
while ( [assetReader status]==AVAssetReaderStatusReading ) {
CMSampleBufferRef sampleBuffer = [assetReaderOutput copyNextSampleBuffer];
if (sampleBuffer == NULL) {
if ([assetReader status] == AVAssetReaderStatusFailed)
break;
else
continue;
}
count++;
CMBlockBufferRef blockBuffer = CMSampleBufferGetDataBuffer(sampleBuffer);
size_t length = CMBlockBufferGetDataLength(blockBuffer);
if (length>0) {
unsigned char *buffer = malloc(length);
memset(buffer, 0, length);
CMBlockBufferCopyDataBytes(blockBuffer, 0, length, buffer);
for (int i=0; i<length; i++) {
timeStampFrame = (timeStampFrame << 8) + buffer[i];
}
free(buffer);
}
CFRelease(sampleBuffer);
}
if (count == 0) {
NSLog(#"No sample in the timecode track: %#", [assetReader error]);
}
NSLog(#"Processed %d sample", count);
}
}
if ([assetReader status] != AVAssetReaderStatusCompleted)
[assetReader cancelReading];
}
}
This is a little more tricky than the QuickTime API and there must be some improvement to the code above but it works for me.

Get a list of unmountable drives using Cocoa

I would like to obtain a list of drives that are unmountable/ejectable using Cocoa/Objective-C under OS X.
I was hoping that NSWorkspace getFileSystemInfoForPath::::: would help me:
NSArray* listOfMedia = [[NSWorkspace sharedWorkspace] mountedLocalVolumePaths];
NSLog(#"%#", listOfMedia);
for (NSString* volumePath in listOfMedia)
{
BOOL isRemovable = NO;
BOOL isWritable = NO;
BOOL isUnmountable = NO;
NSString* description = [NSString string];
NSString* type = [NSString string];
BOOL result = [[NSWorkspace sharedWorkspace] getFileSystemInfoForPath:volumePath
isRemovable:&isRemovable
isWritable:&isWritable
isUnmountable:&isUnmountable
description:&description
type:&type];
NSLog(#"Result:%i Volume: %#, Removable:%i, W:%i, Unmountable:%i, Desc:%#, type:%#", result, volumePath, isRemovable, isWritable, isUnmountable, description, type);
}
Output:
...
Result:1 Volume: /Volumes/LR Photos, Removable:0, W:1, Unmountable:0, Desc:hfs, type:hfs
...
"LR Photos" is an external drive (connected via Thunderbolt) that should be removable and/or unmountable (or, at least I think it should be). :)
Should I be going about this a different way?
Thanks in advance!
You can use diskArbitration framework.
#import <DiskArbitration/DiskArbitration.h>
+(NSMutableArray *)getListOfEjectableMedia
{
NSArray *mountedRemovableMedia = [[NSFileManager defaultManager] mountedVolumeURLsIncludingResourceValuesForKeys:nil options:NSVolumeEnumerationSkipHiddenVolumes];
NSMutableArray *result = [NSMutableArray array];
for(NSURL *volURL in mountedRemovableMedia)
{
int err = 0;
DADiskRef disk;
DASessionRef session;
CFDictionaryRef descDict;
session = DASessionCreate(NULL);
if (session == NULL) {
err = EINVAL;
}
if (err == 0) {
disk = DADiskCreateFromVolumePath(NULL,session,(CFURLRef)volURL);
if (session == NULL) {
err = EINVAL;
}
}
if (err == 0) {
descDict = DADiskCopyDescription(disk);
if (descDict == NULL) {
err = EINVAL;
}
}
if (err == 0) {
CFTypeRef mediaEjectableKey = CFDictionaryGetValue(descDict,kDADiskDescriptionMediaEjectableKey);
CFTypeRef deviceProtocolName = CFDictionaryGetValue(descDict,kDADiskDescriptionDeviceProtocolKey);
if (mediaEjectableKey != NULL)
{
BOOL op = CFEqual(mediaEjectableKey, CFSTR("0")) || CFEqual(deviceProtocolName, CFSTR("USB"));
if (op) {
[result addObject:volURL];
}
}
}
if (descDict != NULL) {
CFRelease(descDict);
}
if (disk != NULL) {
CFRelease(disk);
}
if (session != NULL) {
CFRelease(session);
}
}
return result;
}
Unfortunately getFileSystemInfoForPath: is not really the right way to do this. What removable means is that the volume is on removable media such as a CD or DVD. In practice unmountable seems to give the same results as removable. See for example, this post on results using getFileSystemInfoForPath. So unless you want to simply know if a volume is on removable media, you'll need to use another technique.
What you really want to check is the connection bus type of the volume. Firewire, USB, Thunderbolt, etc. are unmountable in the sense you mean. You can see this information in Disk Utility if you select the volume and push the "Info" button under "Connection Bus". Getting this information programmatically is much harder and as far as I can tell is only possible using the IOKit. Details are in Apple's documentation on Accessing Hardware from Applications.
you can use command line version of Disk Utility app that is "diskutil", run it with parameter "list" and pipe output and get it in your program ( don't need to use cocoa ).

Close all windows of another app using Accessibility API

I already know how to use the Mac OSX Accessibility API within Objective-C to reposition windows of another running application, without the use of any kind of scripting bridge.
Now, I want to use this same Accessibility API (again, without any scripting bridge) to close all the open windows of another running application.
The code that I want to write in Objective-C should do the same thing as this AppleScript code:
tell application "TheApplication"
close every window
end tell
I would guess that this is possible, because it's permitted within AppleScript.
Here's my solution ...
+(void)closeWindowsOfApp:(NSString*)appName {
boolean_t result = false;
if (appName == nil) {
return;
}
ProcessSerialNumber psn;
psn.highLongOfPSN = 0;
psn.lowLongOfPSN = kNoProcess;
while (GetNextProcess(&psn) == noErr) {
pid_t pid = 0;
if (GetProcessPID(&psn, &pid) != noErr) {
continue;
}
AXUIElementRef elementRef = AXUIElementCreateApplication(pid);
NSString* title = nil;
AXUIElementCopyAttributeValue(elementRef, kAXTitleAttribute, (CFTypeRef *)&title);
if (title == nil) {
continue;
}
if ([title compare:appName] != NSOrderedSame) {
CFRelease(title);
continue;
}
CFRelease(title);
CFArrayRef windowArray = nil;
AXUIElementCopyAttributeValue(elementRef, kAXWindowsAttribute, (CFTypeRef*)&windowArray);
if (windowArray == nil) {
CFRelease(elementRef);
continue;
}
CFRelease(elementRef);
CFIndex nItems = CFArrayGetCount(windowArray);
if (nItems < 1) {
CFRelease(windowArray);
continue;
}
for (int i = 0; i < nItems; i++) {
AXUIElementRef itemRef = (AXUIElementRef) CFArrayGetValueAtIndex(windowArray, i);
AXUIElementRef buttonRef = nil;
AXUIElementCopyAttributeValue(itemRef, kAXCloseButtonAttribute, (CFTypeRef*)&buttonRef);
AXUIElementPerformAction(buttonRef, kAXPressAction);
CFRelease(buttonRef);
}
CFRelease(windowArray);
break;
}
}
There's a Cocoa class, NSApplescript, that lets you compile and run Applescript from within your ObjC code. You haven't really said why you don't want to use AS. Since you've already got the script that does what you want, you can make your program work right now and just use it:
NSApplescript * as = [[NSApplescript alloc] initWithSource:#"tell application \"TheApplication\"\nclose every window\nend tell"];
NSDictionary * errInfo;
NSAppleEventDescriptor * res = [as executeAndReturnError:&err];
if( !res ){
// An error occurred. Inspect errInfo and perform necessary actions
}
[as release];
Worry about ideological purity or performance later.