When I view my purchased music in iTunes songs that are explicit are shown with a red explicit box next to them in the list of songs. I.e. the explicit tag you see when purchasing the song is passed through to the iTunes library. Also the same is done for songs itunes tagged as clean. I'd really like to be able to identify any songs that are explicit in my ios app but I can't find any resources on whether the tag is passed through to the ios devices with the other song information (MPMediaItemProperty) and if so how to get it (I've searched endlessly!) Some songs include "[Explicit] at the end of their title, but this is only the case with some songs, not all. Does anyone know if the tag is present and readable from Objective-C code?
Well, this took me a good few hours to find! Be prepared, it's not as simple as just using MPMediaQuery
.
Overview
The data you need isn't easily accessible via the standard channels (i.e There is no pre-defined key in the MediaPlayer or AVFoundation frameworks to access this metadata). It is available via most ID3 tag editors, under the name ITUNESADVISORY
. This tag can take 3 values:
0 - No advisory information
1 - Explicit
2 - Clean
This is what we're looking for, but how do we access it. The MediaPlayer
framework is fairly high level, which means that we are limited in what data we can access/use. In order to look more deeply at a media file, we need to use AVFoundation
framework as well.
How-to
I don't know what exact workflow you're using, but I hope you can adapt this to your needs. In order to determine whether a media file is explicit/clean or otherwise, I did as follows:
I needed to get a hold of a
MPMediaItem
containing the file I want to inspect. I did this usingMPMediaPickerController
, presenting it, and retrieved the selected media items through its delegate method:- (void)mediaPicker:(MPMediaPickerController *)mediaPicker didPickMediaItems:(MPMediaItemCollection *)mediaItemCollection { // Do whatever is appropriate in your case to extract the media items MPMediaItem* item = [mediaItemCollection.items objectAtIndex:...]; .... }
Once I have my
MPMediaItem
, I need to get the equivalentAVAsset
so I can work with theAVFoundation
framework. To do this, I can use the URL path from the media item to create the asset.MPMediaItem* item = ...; NSURL* path = [item valueForProperty:MPMediaItemPropertyAssetURL]; // We have to check if a path exists, because a media item may not be present on the device. Blame iTunes Match. if (path) { AVAsset* asset = [AVAsset assetWithURL:path]; ....
Once we have our asset, we need to get the metadata from it. From experimentation, this is iTunes metadata. As such, lets extract it from our asset.
NSArray* metadata = [asset metadataForFormat:AVMetadataFormatiTunesMetadata];
This is where the fun, undocumented work begins. We now have to find the correct metadata item for what we want. As the advisory key is not declared as a constant in any of Apple's documentation, after a lot of trial and error, I've found it to be
1920233063
.To get the correct metadata item, we need to perform a filter on the metadata array, as so:
NSUInteger advisoryIndex = [metadata indexOfObjectPassingTest:^BOOL(id obj, NSUInteger idx, BOOL *stop) { if ([obj isKindOfClass:[AVMetadataItem class]]) { AVMetadataItem* metaItem = (AVMetadataItem*)obj; NSNumber* advisoryKeyTest = @(1920233063); if ([metaItem.key isEqual:advisoryKeyTest]) { *stop = YES; return YES; } return NO; } return NO; }];
Once we've done that, then we need to determine what it is. This is relatively simple, and can be adapted to your own needs. I've just written a simple switch statement that says what type the song/media is. (Remember: It's entirely possible the media file doesn't include an advisory tag, so we have to check whether the index exists or not)
if (advisoryIndex != NSNotFound) { AVMetadataItem* metaItem = [metadata objectAtIndex:advisoryIndex]; NSInteger advisoryValue = [metaItem.numberValue integerValue]; switch (advisoryValue) { case 0: NSLog(@"%@", @"Unspecified"); break; case 1: NSLog(@"%@", @"Explicit"); break; case 2: NSLog(@"%@", @"Clean"); break; default: NSLog(@"%@", @"Unexpected Value"); break; } }
Conclusion
And there you have it, a way to determine whether a song is clean or explicit, or neither. Because this is using a static number to find the advisory data, and is not linked to any constant defined in Apple's headers, I can not guarantee that this will work for all iOS versions in the past, present and future. I have tested this on iOS 6, and it seemed to be ok. Your milage may vary.
It's a shame Apple don't provide this functionality out of the box (even if they just provided the Advisory Key). This functionality could be placed within a Category, if you so desire.
ITUNESADVISORY
tag? I wouldn't write this off yet, it may be a case that AVFoundation didn't load that key yet (for performance reasons, it loads only some metadata, it may have missed the Advisory ones). Will investigate when I can, this is an interesting project for myself! –
Holpen © 2022 - 2024 — McMap. All rights reserved.