How to get only songs from itunes library with itlibrary.h - objective-c

This is the way the docs say to get all media items from library, however I want to get only the songs.
#import <iTunesLibrary/ITLibrary.h>
ITLibrary *library = [ITLibrary libraryWithAPIVersion:#"1.0" error:&error];
if (library)
{
tracks = library.allMediaItems; // <- NSArray of ITLibMediaItem
}
I found this answer: How to get all tracks from an album using iTunes.h/Scripting Bridge
I'm not sure how to adapt it to do what I need it to, or maybe it is less complicated now?

tracks = library.allMediaItems; is a NSArray of ITLibMediaItem objects.
According to the doc, it as a property mediaKind that gives it the item is a song (ITLib​Media​Item​Media​Kind​Song) or another kind of media.
So, you just have to use the predicate:
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"mediaKind == %d", ITLib​Media​Item​Media​Kind​Song]]
So to filter (with previous predicate)
tracks = [library.allMediaItems filteredArrayUsingPredicate:predicate];

Related

ITLibrary gives me nothing but (null)

My program automates a radio station. There is lots of communication back and forth between it and iTunes. I programmed it with scripting bridge. Scripting bridge suffers from memory leaks. Each call to scripting bridge leaks a small amount of memory. Add a lot of calls to a program that runs 24/7 and I've got software that will run for something less than 24 hours, and then quit.
My first attempt at a solution was to minimize my calls to scripting bridge. In researching that end, I came across ItunesLibrary. It isn't working for me.
NSError *error = nil;
ITLibrary *library = [ITLibrary libraryWithAPIVersion:#"1.0" error:&error];
if (library)
{
NSArray *playlists = [[NSArray alloc]init];
playlists = library.allPlaylists;
NSArray *tracks = [[NSArray alloc]init];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"mediaKind == %d", ITLibMediaItemMediaKindSong];
tracks = [library.allMediaItems filteredArrayUsingPredicate:predicate];
NSLog(#"Playlists - %#",playlists);
NSLog(#"Tracks - %#",tracks);
}
This code is pretty much right out of Apple's docs. It should work - I think.
Before I added the predicate, I got some info on each of the podcasts in my iTunes library. In the nslog output, each of my playlists produces an entry similar to "". Each of my songs shows nothing more than (null).
All of the info is in iTunes. I can read it with scripting bridge. I can read it with AVAsset
AVAsset *asset = [AVURLAsset URLAssetWithURL:myUrl options:nil];
NSArray *metadata = [asset commonMetadata];
for ( AVMetadataItem* item in metadata )
{
NSString *key = [item commonKey];
NSString *value = [item stringValue];
NSLog(#"key = %#, value = %#", key, value);
}
With AVAsset I only get the song name, album name, and artist name. I need to access the rest of iTune's ID3 tags.
What have I don to break ItunesLibrary?
The secret to getting ItunesLibrary to work seems to be in the entitlements file. You need to add the key "com.apple.security.assets.music.read-only", and set it to YES. I got this by digging through a project on github.com.

How to filter Core Data results from NSFetchedResultsController after the fetch

I'm trying to display some Spots in a UITableView with NSFetchedResultsController associated to the Database.
The problem is that I need to filter the results by a distance (radio) from a specific Location. I read that it is not possible to a NSPredicate that calculate distance, so I'm getting all the Spots in an area around the Location with a simple comparation with the coordinates. That gives me a Square around the Location. Then, I want to iterate the results of the fetchedObjects and remove the ones that are not in the Zone.
- (NSFetchedResultsController *)newFetchedResultsControllerWithSearch:(NSString *)searchString{
NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest
managedObjectContext:self.managedObjectContext
sectionNameKeyPath:sectionName
cacheName:#"Stores"];
aFetchedResultsController.delegate = self;
NSError *error = nil;
if (![aFetchedResultsController performFetch:&error])
{
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
// ITERATE FETCHED OBJECTS TO FILTER BY RADIO
int n = [aFetchedResultsController.fetchedObjects count];
NSLog(#"Square result count: %i",n);
self.footerLabel.text = [NSString stringWithFormat:NSLocalizedString(#"%i Spots", #"%i Spots"),n];
return aFetchedResultsController;
How can I remove object from the aFetchedResultsController.fetchedObjects before returning it?? Will it alterate the methods used in UITableView like [fetchedResultsController objectAtIndexPath:indexPath] ?
Thanks
You cannot modify the result set of a fetched results controller (FRC). You can filter the results, but that does not help if the FRC is used as data source for a table view.
Possible solutions:
Pre-calculate the distances to the current locations and store the distances as a persistent attribute in the database. Then you can use a filter on the distance directly in the fetch request of the FRC. The big disadvantage of course is that you have to re-calculate the distances when the current location changes.
Don't use a fetched results controller. Perform a fetch request and filter the results of the fetch request according to the radius into an array, and use this array as data source for the table view. The big disadvantage here is that you lose the automatic update features of the FRC. You would have to reload the table view each time the radius or other search parameters are changed.
I know that both solutions are not satisfying, but I don't think there is a method to combine a fetched results controller with a function based filter.
The proper method to do this is to set a new fetch request which includes your predicate. The fetched results controller's fetchRequest is read only, so you have to put the logic into the the lazy initialization of the fetched results controller itself.
if (searchIsActive) {
fetchRequest.predicate =
[NSPredicate predicateWithFormat:
#"(%K < %# && %K > %#) && (%K < %# && %K > %#)",
#"lat", #(locationLat + limit), #"lat", #(locationLat - limit),
#"lon", #(locationLon + limit), #"lon", #(locationLon - limit)];
}
else { fetchRequest.predicate = nil; }
Then, to update the table, just set the controller to nil and reload
self.fetchedResultsController = nil;
[self.tableView reloadData];
Use you fetchedResultController
NSError *error;
if (filter) { // check if you need to filter
// set predicate to self.fetchedResultController.fetchRequest
self.fetchedResultController.fetchRequest.predicate = [NSPredicate predicateWithFormat:#"your filter params"]];
}else{
// if you dont need to filter and you need to show all objects set predicate to nil
self.fetchedResultController.fetchRequest.predicate = nil;
}
// and perform fetch!
if (![self.fetchedResultController performFetch:&error]) {
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
}
don't forget update UI after perFormFetch
Geohash.
So its a string that has the location where you can do a 'like' SQL query and get results around an area.
From wikipedia
"
Geohash is a geocoding system invented by Gustavo Niemeyer and placed into the public domain. It is a hierarchical spatial data structure which subdivides space into buckets of grid shape, which is one of the many applications of what is known as a Z-order curve, and generally space-filling curves.
Geohashes offer properties like arbitrary precision and the possibility of gradually removing characters from the end of the code to reduce its size (and gradually lose precision).
As a consequence of the gradual precision degradation, nearby places will often (but not always) present similar prefixes. The longer a shared prefix is, the closer the two places are."
Use NSPredicate to search/filter trough the array.
Example:
NSString *modelName = #"honda";
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"model == %#", modelName];
NSArray *filteredArray = [results filteredArrayUsingPredicate:predicate];

Getting the results from core data based on a particular attribute in objective c

In my project, I implemented core data to save the bulk data under several entities. One of entity is "coupon" and it have nearly 10 attributes. One is username. When the app opens I need to fetch all the entries based on a particular user who logged in. Username is already saved into the core data when the user enters each entry. How can I set the predicate for this? I am new to core data concept. This is how i am fetching the all results under the entity coupon.
NSPredicate *pred = [NSPredicate predicateWithFormat:#"Category BEGINSWITH[c] #"All" and Used == NO"];
[request setPredicate: pred];
NSArray *objects = [context executeFetchRequest:request error:&error];
NSLog(#"%#arry",objects);
if ([objects count]>0) {
[self.couponList addObjectsFromArray:objects];
}
[request release];
Any help or suggestion will be appreciated.Thanks in advance!
Your predicate has wrong syntax. You need to substitute the parameters into the format string.
Also, your predicate seems to have nothing to do with the username. Why?
[NSPredicate predicateWithFormat:#"username = %#", userName];
// or, including your existing predicate:
[NSPredicate predicateWithFormat:#"username = %# &&
category BEGINSWITH[c] %# &&
used == %#",
userName, #"All", [NSNumber numberWithBool:NO]];
Note that I have used category and used with lower initial letters. I assume these are properties of your entity in being fetched, so by convention the first letter should be small.
Also, you could actually also use your version used == NO if you prefer.

Different result between app close and open again in many-to-many relationship with core data

In my Core Data model I have two entities: "Photo" and "Tag". The relationship between these is like :
Photo << ------->> Tag.
When I insert a Photo into database and setup the relationship, then I want to fetch an NSArray of photos using an entity "Tag". The code is:
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"(ANY tags == %#)", tag];
requestForPhoto.predicate = predicate;
NSSortDescriptor *sortD = [NSSortDescriptor sortDescriptorWithKey:#"title" ascending:YES];
requestForPhoto.sortDescriptors = [NSArray arrayWithObject:sortD];
NSArray *matches = [document.managedObjectContext executeFetchRequest:requestForPhoto error:nil];
the number of matches is 0; then I close the app, open it again and execute the upper code, the number turns normal, becoming 1.
I'm sure that I execute method saveToURL: forSaveOperation: to save the data.
I NSLog the content of database before and after re-open app, It works fine, the relationship has already established.
I have another Core Data model:
photo <<---------> location
when I fetch a photos, I use the NSString of location(not an entity) to do so.
the results is very regular.

Play specific title in iTunes via ScriptingBridge

I'm trying to write an application that interacts with iTunes via ScriptingBridge. I works well so far, but the options of this method seem to be very limited.
I want to play song with a given name, but it looks like there's no way to do this. I haven't found anything similar in iTunes.h…
In AppleScript it's just three lines of code:
tell application "iTunes"
play (some file track whose name is "Yesterday")
end tell
And then iTunes starts to play a classic Beatles song.
Is there any was I can do this with ScriptingBridge or do I have to run this AppleScript from my app?
It's not as simple as the AppleScript version, but it's certainly possible.
Method one
Get a pointer to the iTunes library:
iTunesApplication *iTunesApp = [SBApplication applicationWithBundleIdentifier:#"com.apple.iTunes"];
SBElementArray *iTunesSources = [iTunesApp sources];
iTunesSource *library;
for (iTunesSource *thisSource in iTunesSources) {
if ([thisSource kind] == iTunesESrcLibrary) {
library = thisSource;
break;
}
}
Get an array containing all the audio file tracks in the library:
SBElementArray *libraryPlaylists = [library libraryPlaylists];
iTunesLibraryPlaylist *libraryPlaylist = [libraryPlaylists objectAtIndex:0];
SBElementArray *musicTracks = [self.libraryPlaylist fileTracks];
Then filter the array to find tracks with the title you're looking for.
NSArray *tracksWithOurTitle = [musicTracks filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:#"%K == %#", #"name", #"Yesterday"]];
// Remember, there might be several tracks with that title; you need to figure out how to find the one you want.
iTunesTrack *rightTrack = [tracksWithOurTitle objectAtIndex:0];
[rightTrack playOnce:YES];
Method two
Get a pointer to the iTunes library as above. Then use the Scripting Bridge searchFor: only: method:
SBElementArray *tracksWithOurTitle = [library searchFor:#"Yesterday" only:kSrS];
// This returns every song whose title *contains* "Yesterday" ...
// You'll need a better way to than this to pick the one you want.
iTunesTrack *rightTrack = [tracksWithOurTitle objectAtIndex:0];
[rightTrack playOnce:YES];
Caveat to method two: The iTunes.h file incorrectly claims that the searchFor: only: method returns an iTunesTrack*, when in fact (for obvious reasons) it returns an SBElementArray*. You can edit the header file to get rid of the resulting compiler warning.