1

I'm trying to access the user’s local music library using MPMediaQuery to sort the results in the following manner:

Artist A (sorted alphabetically) > All of Artist A's albums, sorted alphabetically
Artist B (sorted alphabetically) > All of Artist B's albums, sorted alphabetically
Artist C (sorted alphabetically) > All of Artist C's albums, sorted alphabetically
...

My query is structured thusly:

MPMediaQuery *albumsQuery = [MPMediaQuery albumsQuery];
albumsQuery.groupingType = MPMediaGroupingAlbumArtist;

But the issue is that while the aforementioned mostly works, only first album per given artist is returned. That is, only one of Alt-J’s albums is returned, not the two that exist in the library.

Why is that? And how can I structure my query to return the desired result?

EDIT: This is how I’m accessing the albumsQuery:

AlbumCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:CellID forIndexPath:indexPath];
MPMediaItemCollection *collection = [_items objectAtIndex:indexPath.row];
MPMediaItem *cellItem = [collection representativeItem];

Where _items is an NSArray that contains the MPMediaQuery’s result.

David Barsky
  • 108
  • 1
  • 3
  • 10

3 Answers3

6

It seems that David Barsky's algorithm is super slow with large libraries. I've taken different approach:

-(NSArray *)albumsSortedByArtist
{
    CFTimeInterval start = CACurrentMediaTime();
    MPMediaQuery *albumsQuery = [MPMediaQuery albumsQuery];

    NSSortDescriptor *sorter = [NSSortDescriptor sortDescriptorWithKey:@"representativeItem"
                                                         ascending:YES comparator:^NSComparisonResult(MPMediaItem * _Nonnull album1, MPMediaItem * _Nonnull album2) {
                                                                 NSString *album1Name = [album1 valueForProperty:MPMediaItemPropertyAlbumArtist];
                                                                 NSString *album2Name = [album2 valueForProperty:MPMediaItemPropertyAlbumArtist];
                                                                 if (!album1Name || !album2Name) {
                                                                     album1Name = [album1 valueForProperty:MPMediaItemPropertyArtist];
                                                                     album2Name = [album2 valueForProperty:MPMediaItemPropertyArtist];
                                                                 }
                                                                 return [album1Name localizedStandardCompare:album2Name];
                                                             }];
    NSArray *allAlbums = [albumsQuery.collections sortedArrayUsingDescriptors:@[sorter]];
    NSLog(@"It took %f", CACurrentMediaTime() - start);
    return allAlbums;
}

It's much faster. On iPhone 6 library with 1161 albums:

2016-06-07 20:28:51.620 Musica[1205:515625] Mine: 0.394556
2016-06-07 20:29:20.528 Musica[1205:515625] David Barsky's: 28.906980
Adam Różyński
  • 451
  • 3
  • 10
0

How are you accessing the albums from the query? There's the items method or, if I recall correctly, the collection method. They're a bit different in how they give you the data.

Obviously you can do it all by hand yourself and just get a list of all the albums and sort them yourself!

EDIT I: grouping by album artist means that the collection is giving you each album artist and then getting the representative item is basically just one album. So you'd need to do another query on the collection to get each album for that artist

EDIT II: You can use what you've already found to do additional queries, so for example what you've already got is a collection of all the different album artists. You want to know what each artist's albums are so you could repeat the query with an additional predicate:

MPMediaItem* albumArtist; // previously sourced MPMediaQuery* albumQuery = [MPMediaQuery albumsQuery]; MPMediaPropertyPredicate* albumPredicate = [MPMediaPropertyPredicate predicateWithValue: [albumArtist valueForProperty: MPMediaItemPropertyAlbumArtist] forProperty: MPMediaItemPropertyAlbumArtist]; [albumQuery addFilterPredicate: albumPredicate]; NSArray* allAlbums = [albumQuery collections];

CMash
  • 1,987
  • 22
  • 35
  • I’ve edited the original question to explain how I’m accessing the query. As for doing by hand — I’ve considered that, but I’d prefer using the nice accessors that MPMediaQuery has. – David Barsky Mar 21 '15 at 21:52
  • What do you mean, “do another query on the collection”? Can you give an example? (And I think the “doing by hand” approach you suggested is becoming and increasingly attractive option...) – David Barsky Mar 22 '15 at 01:21
  • Added another edit. The 'by hand' option isn't so bad as it gives you more control over how the sorting is actually done, so you can say you want to sort everything by artist, then by album, then by song title (or position on the album), whereas I don't know what the built in sorting would do, probably something similar! – CMash Mar 22 '15 at 19:31
  • Thanks, that worked! Main issue with that is that MPMedia item operation is slow on a decently large library. I’ve added my solution as an answer for future reference. I’m not marking it as a solution as it can still be improved. – David Barsky Mar 23 '15 at 13:26
  • Interesting, I hadn't got round to checking the performance, interesting to see how it compares to the more 'by hand' approach – CMash Mar 23 '15 at 13:32
0

The following works (slowly, on a decently large music library. Your results may vary). Credit goes to CMash for helping.

- (NSArray *)queryForMusic {
    NSMutableArray *allAlbums = [[NSMutableArray alloc] init];

    MPMediaQuery *artistsQuery = [MPMediaQuery artistsQuery];
    artistsQuery.groupingType = MPMediaGroupingAlbumArtist;
    NSArray *allArtists = [artistsQuery collections];

    for (MPMediaItemCollection *collection in allArtists) {
        MPMediaItem *albumArtist = [collection representativeItem];

        MPMediaQuery *albumQuery = [MPMediaQuery albumsQuery];
        MPMediaPropertyPredicate* albumPredicate = [MPMediaPropertyPredicate predicateWithValue: [albumArtist valueForProperty: MPMediaItemPropertyAlbumArtist] forProperty: MPMediaItemPropertyAlbumArtist];
        [albumQuery addFilterPredicate: albumPredicate];

        NSArray *artistsAblums = [albumQuery collections];
        [allAlbums addObjectsFromArray:artistsAblums];
    }

    return allAlbums;
}
Community
  • 1
  • 1
David Barsky
  • 108
  • 1
  • 3
  • 10