Significantly improve memory usage of loading tags
This especially helps with bad or brutal files. Signed-off-by: Christopher Snowhill <kode54@gmail.com>
This commit is contained in:
parent
34edc003db
commit
a1bd2e0d44
11 changed files with 343 additions and 290 deletions
|
@ -746,25 +746,29 @@ NSURL *_Nullable urlForPath(NSString *_Nullable path);
|
||||||
NSURL *url = urlForPath(key);
|
NSURL *url = urlForPath(key);
|
||||||
|
|
||||||
[op addExecutionBlock:^{
|
[op addExecutionBlock:^{
|
||||||
DLog(@"Loading metadata for %@", url);
|
@autoreleasepool {
|
||||||
[[FIRCrashlytics crashlytics] logWithFormat:@"Loading metadata for %@", url];
|
DLog(@"Loading metadata for %@", url);
|
||||||
|
[[FIRCrashlytics crashlytics] logWithFormat:@"Loading metadata for %@", url];
|
||||||
|
|
||||||
NSDictionary *entryProperties = [AudioPropertiesReader propertiesForURL:url];
|
NSDictionary *entryProperties = [AudioPropertiesReader propertiesForURL:url];
|
||||||
if(entryProperties == nil)
|
if(entryProperties == nil)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
NSDictionary *entryMetadata = [AudioMetadataReader metadataForURL:url];
|
NSDictionary *entryMetadata = [AudioMetadataReader metadataForURL:url];
|
||||||
|
|
||||||
NSDictionary *entryInfo = [NSDictionary dictionaryByMerging:entryProperties with:entryMetadata];
|
NSDictionary *entryInfo = [NSDictionary dictionaryByMerging:entryProperties with:entryMetadata];
|
||||||
|
|
||||||
[weakLock lock];
|
[weakLock lock];
|
||||||
entryInfo = [weakDataStore coalesceEntryInfo:entryInfo];
|
@autoreleasepool {
|
||||||
[weakArray addObject:key];
|
entryInfo = [weakDataStore coalesceEntryInfo:entryInfo];
|
||||||
[weakArray addObject:entryInfo];
|
}
|
||||||
[uniquePathsEntries setObject:[[NSMutableArray alloc] init] forKey:key];
|
[weakArray addObject:key];
|
||||||
progress += progressstep;
|
[weakArray addObject:entryInfo];
|
||||||
[self setProgressJobStatus:progress];
|
[uniquePathsEntries setObject:[[NSMutableArray alloc] init] forKey:key];
|
||||||
[weakLock unlock];
|
progress += progressstep;
|
||||||
|
[self setProgressJobStatus:progress];
|
||||||
|
[weakLock unlock];
|
||||||
|
}
|
||||||
}];
|
}];
|
||||||
|
|
||||||
[queue addOperation:op];
|
[queue addOperation:op];
|
||||||
|
|
|
@ -562,75 +562,77 @@ static void setDictionary(NSMutableDictionary *dict, NSString *tag, NSString *va
|
||||||
}
|
}
|
||||||
tag = NULL;
|
tag = NULL;
|
||||||
while((tag = av_dict_get(metadata, "", tag, AV_DICT_IGNORE_SUFFIX))) {
|
while((tag = av_dict_get(metadata, "", tag, AV_DICT_IGNORE_SUFFIX))) {
|
||||||
if(!strcasecmp(tag->key, "streamtitle")) {
|
@autoreleasepool {
|
||||||
NSString *artistTitle = guess_encoding_of_string(tag->value);
|
if(!strcasecmp(tag->key, "streamtitle")) {
|
||||||
NSArray *splitValues = [artistTitle componentsSeparatedByString:@" - "];
|
NSString *artistTitle = guess_encoding_of_string(tag->value);
|
||||||
NSString *_artist = @"";
|
NSArray *splitValues = [artistTitle componentsSeparatedByString:@" - "];
|
||||||
NSString *_title = [splitValues objectAtIndex:0];
|
NSString *_artist = @"";
|
||||||
if([splitValues count] > 1) {
|
NSString *_title = [splitValues objectAtIndex:0];
|
||||||
_artist = _title;
|
if([splitValues count] > 1) {
|
||||||
_title = [splitValues objectAtIndex:1];
|
_artist = _title;
|
||||||
setDictionary(_metaDict, @"artist", _artist);
|
_title = [splitValues objectAtIndex:1];
|
||||||
setDictionary(_metaDict, @"title", _title);
|
setDictionary(_metaDict, @"artist", _artist);
|
||||||
} else {
|
setDictionary(_metaDict, @"title", _title);
|
||||||
setDictionary(_metaDict, @"title", _title);
|
} else {
|
||||||
}
|
setDictionary(_metaDict, @"title", _title);
|
||||||
} else if(!strcasecmp(tag->key, "unsynced lyrics") ||
|
|
||||||
!strcasecmp(tag->key, "lyrics")) {
|
|
||||||
setDictionary(_metaDict, @"unsyncedlyrics", guess_encoding_of_string(tag->value));
|
|
||||||
} else if(!strcasecmp(tag->key, "icy-url")) {
|
|
||||||
setDictionary(_metaDict, @"album", guess_encoding_of_string(tag->value));
|
|
||||||
} else if(!strcasecmp(tag->key, "icy-genre")) {
|
|
||||||
setDictionary(_metaDict, @"genre", guess_encoding_of_string(tag->value));
|
|
||||||
} else if(!strcasecmp(tag->key, "title")) {
|
|
||||||
NSString *_tag = guess_encoding_of_string(tag->value);
|
|
||||||
if(i == 0 && formatCtx->nb_chapters > 1) {
|
|
||||||
setDictionary(_metaDict, @"album", _tag);
|
|
||||||
} else {
|
|
||||||
setDictionary(_metaDict, @"title", _tag);
|
|
||||||
}
|
|
||||||
} else if(!strcasecmp(tag->key, "date_recorded")) {
|
|
||||||
setDictionary(_metaDict, @"date", guess_encoding_of_string(tag->value));
|
|
||||||
} else if(!strcasecmp(tag->key, "replaygain_gain")) {
|
|
||||||
// global or chapter gain
|
|
||||||
NSString *tagName;
|
|
||||||
if(i == 0)
|
|
||||||
tagName = @"replaygain_album_gain";
|
|
||||||
else
|
|
||||||
tagName = @"replaygain_track_gain";
|
|
||||||
setDictionary(_metaDict, tagName, guess_encoding_of_string(tag->value));
|
|
||||||
} else if(!strcasecmp(tag->key, "replaygain_peak")) {
|
|
||||||
// global or chapter peak
|
|
||||||
NSString *tagName;
|
|
||||||
if(i == 0)
|
|
||||||
tagName = @"replaygain_album_peak";
|
|
||||||
else
|
|
||||||
tagName = @"replaygain_track_peak";
|
|
||||||
setDictionary(_metaDict, tagName, guess_encoding_of_string(tag->value));
|
|
||||||
} else if(!strcasecmp(tag->key, "iTunNORM")) {
|
|
||||||
NSString *tagString = guess_encoding_of_string(tag->value);
|
|
||||||
NSArray *tag = [tagString componentsSeparatedByString:@" "];
|
|
||||||
NSMutableArray *wantedTag = [[NSMutableArray alloc] init];
|
|
||||||
for(size_t i = 0; i < [tag count]; ++i) {
|
|
||||||
NSString *tagValue = tag[i];
|
|
||||||
if([tagValue length] == 8) {
|
|
||||||
[wantedTag addObject:tagValue];
|
|
||||||
}
|
}
|
||||||
|
} else if(!strcasecmp(tag->key, "unsynced lyrics") ||
|
||||||
|
!strcasecmp(tag->key, "lyrics")) {
|
||||||
|
setDictionary(_metaDict, @"unsyncedlyrics", guess_encoding_of_string(tag->value));
|
||||||
|
} else if(!strcasecmp(tag->key, "icy-url")) {
|
||||||
|
setDictionary(_metaDict, @"album", guess_encoding_of_string(tag->value));
|
||||||
|
} else if(!strcasecmp(tag->key, "icy-genre")) {
|
||||||
|
setDictionary(_metaDict, @"genre", guess_encoding_of_string(tag->value));
|
||||||
|
} else if(!strcasecmp(tag->key, "title")) {
|
||||||
|
NSString *_tag = guess_encoding_of_string(tag->value);
|
||||||
|
if(i == 0 && formatCtx->nb_chapters > 1) {
|
||||||
|
setDictionary(_metaDict, @"album", _tag);
|
||||||
|
} else {
|
||||||
|
setDictionary(_metaDict, @"title", _tag);
|
||||||
|
}
|
||||||
|
} else if(!strcasecmp(tag->key, "date_recorded")) {
|
||||||
|
setDictionary(_metaDict, @"date", guess_encoding_of_string(tag->value));
|
||||||
|
} else if(!strcasecmp(tag->key, "replaygain_gain")) {
|
||||||
|
// global or chapter gain
|
||||||
|
NSString *tagName;
|
||||||
|
if(i == 0)
|
||||||
|
tagName = @"replaygain_album_gain";
|
||||||
|
else
|
||||||
|
tagName = @"replaygain_track_gain";
|
||||||
|
setDictionary(_metaDict, tagName, guess_encoding_of_string(tag->value));
|
||||||
|
} else if(!strcasecmp(tag->key, "replaygain_peak")) {
|
||||||
|
// global or chapter peak
|
||||||
|
NSString *tagName;
|
||||||
|
if(i == 0)
|
||||||
|
tagName = @"replaygain_album_peak";
|
||||||
|
else
|
||||||
|
tagName = @"replaygain_track_peak";
|
||||||
|
setDictionary(_metaDict, tagName, guess_encoding_of_string(tag->value));
|
||||||
|
} else if(!strcasecmp(tag->key, "iTunNORM")) {
|
||||||
|
NSString *tagString = guess_encoding_of_string(tag->value);
|
||||||
|
NSArray *tag = [tagString componentsSeparatedByString:@" "];
|
||||||
|
NSMutableArray *wantedTag = [[NSMutableArray alloc] init];
|
||||||
|
for(size_t i = 0; i < [tag count]; ++i) {
|
||||||
|
NSString *tagValue = tag[i];
|
||||||
|
if([tagValue length] == 8) {
|
||||||
|
[wantedTag addObject:tagValue];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if([wantedTag count] >= 10) {
|
||||||
|
NSScanner *scanner1 = [NSScanner scannerWithString:wantedTag[0]];
|
||||||
|
NSScanner *scanner2 = [NSScanner scannerWithString:wantedTag[1]];
|
||||||
|
unsigned int hexvalue1 = 0, hexvalue2 = 0;
|
||||||
|
[scanner1 scanHexInt:&hexvalue1];
|
||||||
|
[scanner2 scanHexInt:&hexvalue2];
|
||||||
|
float volume1 = -log10((double)(hexvalue1) / 1000) * 10;
|
||||||
|
float volume2 = -log10((double)(hexvalue2) / 1000) * 10;
|
||||||
|
float volumeToUse = MIN(volume1, volume2);
|
||||||
|
NSNumber *_volumeScale = @(pow(10, volumeToUse / 20));
|
||||||
|
setDictionary(_metaDict, @"volume", [_volumeScale stringValue]);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
setDictionary(_metaDict, guess_encoding_of_string(tag->key), guess_encoding_of_string(tag->value));
|
||||||
}
|
}
|
||||||
if([wantedTag count] >= 10) {
|
|
||||||
NSScanner *scanner1 = [NSScanner scannerWithString:wantedTag[0]];
|
|
||||||
NSScanner *scanner2 = [NSScanner scannerWithString:wantedTag[1]];
|
|
||||||
unsigned int hexvalue1 = 0, hexvalue2 = 0;
|
|
||||||
[scanner1 scanHexInt:&hexvalue1];
|
|
||||||
[scanner2 scanHexInt:&hexvalue2];
|
|
||||||
float volume1 = -log10((double)(hexvalue1) / 1000) * 10;
|
|
||||||
float volume2 = -log10((double)(hexvalue2) / 1000) * 10;
|
|
||||||
float volumeToUse = MIN(volume1, volume2);
|
|
||||||
NSNumber *_volumeScale = @(pow(10, volumeToUse / 20));
|
|
||||||
setDictionary(_metaDict, @"volume", [_volumeScale stringValue]);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
setDictionary(_metaDict, guess_encoding_of_string(tag->key), guess_encoding_of_string(tag->value));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -639,29 +641,33 @@ static void setDictionary(NSMutableDictionary *dict, NSString *tag, NSString *va
|
||||||
if([sourceClass isEqual:NSClassFromString(@"HTTPSource")]) {
|
if([sourceClass isEqual:NSClassFromString(@"HTTPSource")]) {
|
||||||
HTTPSource *httpSource = (HTTPSource *)source;
|
HTTPSource *httpSource = (HTTPSource *)source;
|
||||||
if([httpSource hasMetadata]) {
|
if([httpSource hasMetadata]) {
|
||||||
NSDictionary *metadata = [httpSource metadata];
|
@autoreleasepool {
|
||||||
NSString *_genre = [metadata valueForKey:@"genre"];
|
NSDictionary *metadata = [httpSource metadata];
|
||||||
NSString *_album = [metadata valueForKey:@"album"];
|
NSString *_genre = [metadata valueForKey:@"genre"];
|
||||||
NSString *_artist = [metadata valueForKey:@"artist"];
|
NSString *_album = [metadata valueForKey:@"album"];
|
||||||
NSString *_title = [metadata valueForKey:@"title"];
|
NSString *_artist = [metadata valueForKey:@"artist"];
|
||||||
|
NSString *_title = [metadata valueForKey:@"title"];
|
||||||
|
|
||||||
if(_genre && [_genre length]) {
|
if(_genre && [_genre length]) {
|
||||||
[_metaDict setObject:@[_genre] forKey:@"genre"];
|
[_metaDict setObject:@[_genre] forKey:@"genre"];
|
||||||
}
|
}
|
||||||
if(_album && [_album length]) {
|
if(_album && [_album length]) {
|
||||||
[_metaDict setObject:@[_album] forKey:@"album"];
|
[_metaDict setObject:@[_album] forKey:@"album"];
|
||||||
}
|
}
|
||||||
if(_artist && [_artist length]) {
|
if(_artist && [_artist length]) {
|
||||||
[_metaDict setObject:@[_artist] forKey:@"artist"];
|
[_metaDict setObject:@[_artist] forKey:@"artist"];
|
||||||
}
|
}
|
||||||
if(_title && [_title length]) {
|
if(_title && [_title length]) {
|
||||||
[_metaDict setObject:@[_title] forKey:@"title"];
|
[_metaDict setObject:@[_title] forKey:@"title"];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(![_metaDict isEqualToDictionary:metaDict]) {
|
if(![_metaDict isEqualToDictionary:metaDict]) {
|
||||||
metaDict = _metaDict;
|
@autoreleasepool {
|
||||||
|
metaDict = _metaDict;
|
||||||
|
}
|
||||||
if(![source seekable]) {
|
if(![source seekable]) {
|
||||||
[self willChangeValueForKey:@"metadata"];
|
[self willChangeValueForKey:@"metadata"];
|
||||||
[self didChangeValueForKey:@"metadata"];
|
[self didChangeValueForKey:@"metadata"];
|
||||||
|
|
|
@ -42,6 +42,7 @@
|
||||||
83747C4A2862DCF40021245F /* Shared.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Shared.xcconfig; sourceTree = "<group>"; };
|
83747C4A2862DCF40021245F /* Shared.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Shared.xcconfig; sourceTree = "<group>"; };
|
||||||
8384912D180816C900E7332D /* Logging.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Logging.h; path = ../../Utils/Logging.h; sourceTree = "<group>"; };
|
8384912D180816C900E7332D /* Logging.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Logging.h; path = ../../Utils/Logging.h; sourceTree = "<group>"; };
|
||||||
83AA660A27B7DAE40098D4B8 /* cuesheet.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = cuesheet.m; sourceTree = "<group>"; };
|
83AA660A27B7DAE40098D4B8 /* cuesheet.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = cuesheet.m; sourceTree = "<group>"; };
|
||||||
|
83C70A392ACC32FB004B60E4 /* RedundantPlaylistDataStore.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RedundantPlaylistDataStore.h; path = ../../Utils/RedundantPlaylistDataStore.h; sourceTree = "<group>"; };
|
||||||
8D5B49B6048680CD000E48DA /* Flac.bundle */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = Flac.bundle; sourceTree = BUILT_PRODUCTS_DIR; };
|
8D5B49B6048680CD000E48DA /* Flac.bundle */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = Flac.bundle; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
8D5B49B7048680CD000E48DA /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
8D5B49B7048680CD000E48DA /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||||
D2F7E65807B2D6F200F64583 /* CoreData.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreData.framework; path = /System/Library/Frameworks/CoreData.framework; sourceTree = "<absolute>"; };
|
D2F7E65807B2D6F200F64583 /* CoreData.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreData.framework; path = /System/Library/Frameworks/CoreData.framework; sourceTree = "<absolute>"; };
|
||||||
|
@ -93,6 +94,7 @@
|
||||||
08FB77AFFE84173DC02AAC07 /* Classes */ = {
|
08FB77AFFE84173DC02AAC07 /* Classes */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
83C70A392ACC32FB004B60E4 /* RedundantPlaylistDataStore.h */,
|
||||||
834A42B4287AF7AA00EB9D9B /* AudioChunk.h */,
|
834A42B4287AF7AA00EB9D9B /* AudioChunk.h */,
|
||||||
8301C145287805F500651A6E /* NSDictionary+Merge.h */,
|
8301C145287805F500651A6E /* NSDictionary+Merge.h */,
|
||||||
8301C146287805F500651A6E /* NSDictionary+Merge.m */,
|
8301C146287805F500651A6E /* NSDictionary+Merge.m */,
|
||||||
|
|
|
@ -14,6 +14,8 @@
|
||||||
|
|
||||||
#import "Plugin.h"
|
#import "Plugin.h"
|
||||||
|
|
||||||
|
#import "RedundantPlaylistDataStore.h"
|
||||||
|
|
||||||
@interface FlacDecoder : NSObject <CogDecoder> {
|
@interface FlacDecoder : NSObject <CogDecoder> {
|
||||||
FLAC__StreamDecoder *decoder;
|
FLAC__StreamDecoder *decoder;
|
||||||
void *blockBuffer;
|
void *blockBuffer;
|
||||||
|
@ -42,6 +44,8 @@
|
||||||
|
|
||||||
BOOL cuesheetFound;
|
BOOL cuesheetFound;
|
||||||
NSString *cuesheet;
|
NSString *cuesheet;
|
||||||
|
|
||||||
|
RedundantPlaylistDataStore *dataStore;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)setSource:(id<CogSource>)s;
|
- (void)setSource:(id<CogSource>)s;
|
||||||
|
|
|
@ -210,14 +210,16 @@ void MetadataCallback(const FLAC__StreamDecoder *decoder, const FLAC__StreamMeta
|
||||||
if(metadata->type == FLAC__METADATA_TYPE_CUESHEET && !flacDecoder->cuesheetFound) {
|
if(metadata->type == FLAC__METADATA_TYPE_CUESHEET && !flacDecoder->cuesheetFound) {
|
||||||
flacDecoder->cuesheetFound = YES;
|
flacDecoder->cuesheetFound = YES;
|
||||||
|
|
||||||
NSString *_cuesheet;
|
@autoreleasepool {
|
||||||
grabbag__cuesheet_emit(&_cuesheet, metadata, [[NSString stringWithFormat:@"\"%@\"", [[[flacDecoder->source url] path] lastPathComponent]] UTF8String]);
|
NSString *_cuesheet;
|
||||||
|
grabbag__cuesheet_emit(&_cuesheet, metadata, [[NSString stringWithFormat:@"\"%@\"", [[[flacDecoder->source url] path] lastPathComponent]] UTF8String]);
|
||||||
|
|
||||||
if(![_cuesheet isEqual:flacDecoder->cuesheet]) {
|
if(![_cuesheet isEqual:flacDecoder->cuesheet]) {
|
||||||
flacDecoder->cuesheet = _cuesheet;
|
flacDecoder->cuesheet = _cuesheet;
|
||||||
if(![flacDecoder->source seekable]) {
|
if(![flacDecoder->source seekable]) {
|
||||||
[flacDecoder willChangeValueForKey:@"metadata"];
|
[flacDecoder willChangeValueForKey:@"metadata"];
|
||||||
[flacDecoder didChangeValueForKey:@"metadata"];
|
[flacDecoder didChangeValueForKey:@"metadata"];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -241,25 +243,29 @@ void MetadataCallback(const FLAC__StreamDecoder *decoder, const FLAC__StreamMeta
|
||||||
char *_name;
|
char *_name;
|
||||||
char *_value;
|
char *_value;
|
||||||
if(FLAC__metadata_object_vorbiscomment_entry_to_name_value_pair(vorbis_comment->comments[i], &_name, &_value)) {
|
if(FLAC__metadata_object_vorbiscomment_entry_to_name_value_pair(vorbis_comment->comments[i], &_name, &_value)) {
|
||||||
NSString *name = guess_encoding_of_string(_name);
|
@autoreleasepool {
|
||||||
NSString *value = guess_encoding_of_string(_value);
|
NSString *name = guess_encoding_of_string(_name);
|
||||||
free(_name);
|
NSString *value = guess_encoding_of_string(_value);
|
||||||
free(_value);
|
free(_name);
|
||||||
name = [name lowercaseString];
|
free(_value);
|
||||||
if([name isEqualToString:@"cuesheet"]) {
|
name = [name lowercaseString];
|
||||||
_cuesheet = value;
|
name = [flacDecoder->dataStore coalesceString:name];
|
||||||
flacDecoder->cuesheetFound = YES;
|
value = [flacDecoder->dataStore coalesceString:value];
|
||||||
} else if([name isEqualToString:@"waveformatextensible_channel_mask"]) {
|
if([name isEqualToString:@"cuesheet"]) {
|
||||||
if([value hasPrefix:@"0x"]) {
|
_cuesheet = value;
|
||||||
char *end;
|
flacDecoder->cuesheetFound = YES;
|
||||||
const char *_value = [value UTF8String] + 2;
|
} else if([name isEqualToString:@"waveformatextensible_channel_mask"]) {
|
||||||
flacDecoder->channelConfig = (uint32_t)strtoul(_value, &end, 16);
|
if([value hasPrefix:@"0x"]) {
|
||||||
|
char *end;
|
||||||
|
const char *_value = [value UTF8String] + 2;
|
||||||
|
flacDecoder->channelConfig = (uint32_t)strtoul(_value, &end, 16);
|
||||||
|
}
|
||||||
|
} else if([name isEqualToString:@"unsynced lyrics"] ||
|
||||||
|
[name isEqualToString:@"lyrics"]) {
|
||||||
|
setDictionary(_metaDict, @"unsyncedlyrics", value);
|
||||||
|
} else {
|
||||||
|
setDictionary(_metaDict, name, value);
|
||||||
}
|
}
|
||||||
} else if([name isEqualToString:@"unsynced lyrics"] ||
|
|
||||||
[name isEqualToString:@"lyrics"]) {
|
|
||||||
setDictionary(_metaDict, @"unsyncedlyrics", value);
|
|
||||||
} else {
|
|
||||||
setDictionary(_metaDict, name, value);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -308,6 +314,9 @@ void ErrorCallback(const FLAC__StreamDecoder *decoder, FLAC__StreamDecoderErrorS
|
||||||
cuesheetFound = NO;
|
cuesheetFound = NO;
|
||||||
cuesheet = @"";
|
cuesheet = @"";
|
||||||
|
|
||||||
|
id dataStoreClass = NSClassFromString(@"RedundantPlaylistDataStore");
|
||||||
|
dataStore = [[dataStoreClass alloc] init];
|
||||||
|
|
||||||
decoder = FLAC__stream_decoder_new();
|
decoder = FLAC__stream_decoder_new();
|
||||||
if(decoder == NULL)
|
if(decoder == NULL)
|
||||||
return NO;
|
return NO;
|
||||||
|
|
|
@ -61,70 +61,72 @@ static size_t http_curl_write_wrapper(HTTPSource *fp, void *ptr, size_t size) {
|
||||||
|
|
||||||
static int http_parse_shoutcast_meta(HTTPSource *fp, const char *meta, size_t size) {
|
static int http_parse_shoutcast_meta(HTTPSource *fp, const char *meta, size_t size) {
|
||||||
// DLog (@"reading %d bytes of metadata\n", size);
|
// DLog (@"reading %d bytes of metadata\n", size);
|
||||||
DLog(@"%s", meta);
|
@autoreleasepool {
|
||||||
const char *e = meta + size;
|
DLog(@"%s", meta);
|
||||||
const char strtitle[] = "StreamTitle='";
|
const char *e = meta + size;
|
||||||
char title[4096] = "";
|
const char strtitle[] = "StreamTitle='";
|
||||||
while(meta < e) {
|
char title[4096] = "";
|
||||||
if(!memcmp(meta, strtitle, sizeof(strtitle) - 1)) {
|
while(meta < e) {
|
||||||
meta += sizeof(strtitle) - 1;
|
if(!memcmp(meta, strtitle, sizeof(strtitle) - 1)) {
|
||||||
const char *substr_end = meta;
|
meta += sizeof(strtitle) - 1;
|
||||||
while(substr_end < e - 1 && (*substr_end != '\'' || *(substr_end + 1) != ';')) {
|
const char *substr_end = meta;
|
||||||
substr_end++;
|
while(substr_end < e - 1 && (*substr_end != '\'' || *(substr_end + 1) != ';')) {
|
||||||
}
|
substr_end++;
|
||||||
if(substr_end >= e) {
|
}
|
||||||
return -1; // end of string not found
|
if(substr_end >= e) {
|
||||||
}
|
return -1; // end of string not found
|
||||||
size_t s = substr_end - meta;
|
}
|
||||||
s = MIN(sizeof(title) - 1, s);
|
size_t s = substr_end - meta;
|
||||||
memcpy(title, meta, s);
|
s = MIN(sizeof(title) - 1, s);
|
||||||
title[s] = 0;
|
memcpy(title, meta, s);
|
||||||
DLog(@"got stream title: %s\n", title);
|
title[s] = 0;
|
||||||
{
|
DLog(@"got stream title: %s\n", title);
|
||||||
char *tit = strstr(title, " - ");
|
{
|
||||||
if(tit) {
|
char *tit = strstr(title, " - ");
|
||||||
*tit = 0;
|
if(tit) {
|
||||||
tit += 3;
|
*tit = 0;
|
||||||
|
tit += 3;
|
||||||
|
|
||||||
if(!strncmp(tit, "text=\"", 6)) { // Hack for a certain stream
|
if(!strncmp(tit, "text=\"", 6)) { // Hack for a certain stream
|
||||||
char *titfirst = tit + 6;
|
char *titfirst = tit + 6;
|
||||||
char *titlast = strchr(titfirst, '"');
|
char *titlast = strchr(titfirst, '"');
|
||||||
if(titlast) {
|
if(titlast) {
|
||||||
*titlast = 0;
|
*titlast = 0;
|
||||||
|
}
|
||||||
|
tit = titfirst;
|
||||||
}
|
}
|
||||||
tit = titfirst;
|
|
||||||
}
|
|
||||||
|
|
||||||
const char *orig_title = [fp->title UTF8String];
|
const char *orig_title = [fp->title UTF8String];
|
||||||
const char *orig_artist = [fp->artist UTF8String];
|
const char *orig_artist = [fp->artist UTF8String];
|
||||||
|
|
||||||
if(!orig_title || strcasecmp(orig_title, tit)) {
|
if(!orig_title || strcasecmp(orig_title, tit)) {
|
||||||
fp->title = guess_encoding_of_string(tit);
|
fp->title = guess_encoding_of_string(tit);
|
||||||
fp->gotmetadata = 1;
|
fp->gotmetadata = 1;
|
||||||
}
|
}
|
||||||
if(!orig_artist || strcasecmp(orig_artist, title)) {
|
if(!orig_artist || strcasecmp(orig_artist, title)) {
|
||||||
fp->artist = guess_encoding_of_string(title);
|
fp->artist = guess_encoding_of_string(title);
|
||||||
fp->gotmetadata = 1;
|
fp->gotmetadata = 1;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
const char *orig_title = [fp->title UTF8String];
|
const char *orig_title = [fp->title UTF8String];
|
||||||
if(!orig_title || strcasecmp(orig_title, title)) {
|
if(!orig_title || strcasecmp(orig_title, title)) {
|
||||||
fp->artist = @"";
|
fp->artist = @"";
|
||||||
fp->title = guess_encoding_of_string(title);
|
fp->title = guess_encoding_of_string(title);
|
||||||
fp->gotmetadata = 1;
|
fp->gotmetadata = 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
while(meta < e && *meta != ';') {
|
||||||
|
meta++;
|
||||||
|
}
|
||||||
|
if(meta < e) {
|
||||||
|
meta++;
|
||||||
}
|
}
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
while(meta < e && *meta != ';') {
|
|
||||||
meta++;
|
|
||||||
}
|
|
||||||
if(meta < e) {
|
|
||||||
meta++;
|
|
||||||
}
|
}
|
||||||
|
return -1;
|
||||||
}
|
}
|
||||||
return -1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static const uint8_t *parse_header(const uint8_t *p, const uint8_t *e, uint8_t *key, int keysize, uint8_t *value, int valuesize) {
|
static const uint8_t *parse_header(const uint8_t *p, const uint8_t *e, uint8_t *key, int keysize, uint8_t *value, int valuesize) {
|
||||||
|
@ -205,27 +207,29 @@ static size_t http_content_header_handler_int(void *ptr, size_t size, void *stre
|
||||||
while(p < end && (*p == 0x0d || *p == 0x0a)) {
|
while(p < end && (*p == 0x0d || *p == 0x0a)) {
|
||||||
p++;
|
p++;
|
||||||
}
|
}
|
||||||
p = parse_header(p, end, key, sizeof(key), value, sizeof(value));
|
@autoreleasepool {
|
||||||
DLog(@"%skey=%s value=%s\n", fp->icyheader ? "[icy] " : "", key, value);
|
p = parse_header(p, end, key, sizeof(key), value, sizeof(value));
|
||||||
if(!strcasecmp((char *)key, "Content-Type")) {
|
DLog(@"%skey=%s value=%s\n", fp->icyheader ? "[icy] " : "", key, value);
|
||||||
fp->content_type = guess_encoding_of_string((const char *)value);
|
if(!strcasecmp((char *)key, "Content-Type")) {
|
||||||
} else if(!strcasecmp((char *)key, "Content-Length")) {
|
fp->content_type = guess_encoding_of_string((const char *)value);
|
||||||
char *end;
|
} else if(!strcasecmp((char *)key, "Content-Length")) {
|
||||||
fp->length = strtol((const char *)value, &end, 10);
|
char *end;
|
||||||
} else if(!strcasecmp((char *)key, "icy-name")) {
|
fp->length = strtol((const char *)value, &end, 10);
|
||||||
fp->title = guess_encoding_of_string((const char *)value);
|
} else if(!strcasecmp((char *)key, "icy-name")) {
|
||||||
fp->gotmetadata = 1;
|
fp->title = guess_encoding_of_string((const char *)value);
|
||||||
} else if(!strcasecmp((char *)key, "icy-genre")) {
|
fp->gotmetadata = 1;
|
||||||
fp->genre = guess_encoding_of_string((const char *)value);
|
} else if(!strcasecmp((char *)key, "icy-genre")) {
|
||||||
fp->gotmetadata = 1;
|
fp->genre = guess_encoding_of_string((const char *)value);
|
||||||
} else if(!strcasecmp((char *)key, "icy-metaint")) {
|
fp->gotmetadata = 1;
|
||||||
// printf ("icy-metaint: %d\n", atoi (value));
|
} else if(!strcasecmp((char *)key, "icy-metaint")) {
|
||||||
char *end;
|
// printf ("icy-metaint: %d\n", atoi (value));
|
||||||
fp->icy_metaint = (int)strtoul((const char *)value, &end, 10);
|
char *end;
|
||||||
fp->wait_meta = fp->icy_metaint;
|
fp->icy_metaint = (int)strtoul((const char *)value, &end, 10);
|
||||||
} else if(!strcasecmp((char *)key, "icy-url")) {
|
fp->wait_meta = fp->icy_metaint;
|
||||||
fp->album = guess_encoding_of_string((const char *)value);
|
} else if(!strcasecmp((char *)key, "icy-url")) {
|
||||||
fp->gotmetadata = 1;
|
fp->album = guess_encoding_of_string((const char *)value);
|
||||||
|
fp->gotmetadata = 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// for icy streams, reset length
|
// for icy streams, reset length
|
||||||
|
|
|
@ -74,19 +74,23 @@ static void setDictionary(NSMutableDictionary *dict, NSString *tag, NSString *va
|
||||||
NSMutableDictionary *dict = [[NSMutableDictionary alloc] init];
|
NSMutableDictionary *dict = [[NSMutableDictionary alloc] init];
|
||||||
|
|
||||||
for(size_t i = 0; i < metadata.get_count(); ++i) {
|
for(size_t i = 0; i < metadata.get_count(); ++i) {
|
||||||
const midi_meta_data_item &item = metadata[i];
|
@autoreleasepool {
|
||||||
NSString *name = [guess_encoding_of_string(item.m_name.c_str()) lowercaseString];
|
const midi_meta_data_item &item = metadata[i];
|
||||||
if(![name isEqualToString:@"type"]) {
|
NSString *name = [guess_encoding_of_string(item.m_name.c_str()) lowercaseString];
|
||||||
if(remap_display_name && [name isEqualToString:@"display_name"])
|
if(![name isEqualToString:@"type"]) {
|
||||||
name = @"title";
|
if(remap_display_name && [name isEqualToString:@"display_name"])
|
||||||
setDictionary(dict, name, guess_encoding_of_string(item.m_value.c_str()));
|
name = @"title";
|
||||||
|
setDictionary(dict, name, guess_encoding_of_string(item.m_value.c_str()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<uint8_t> albumArt;
|
std::vector<uint8_t> albumArt;
|
||||||
|
|
||||||
if(metadata.get_bitmap(albumArt)) {
|
if(metadata.get_bitmap(albumArt)) {
|
||||||
[dict setObject:[NSData dataWithBytes:&albumArt[0] length:albumArt.size()] forKey:@"albumArt"];
|
@autoreleasepool {
|
||||||
|
[dict setObject:[NSData dataWithBytes:&albumArt[0] length:albumArt.size()] forKey:@"albumArt"];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return dict;
|
return dict;
|
||||||
|
|
|
@ -69,14 +69,16 @@ static void setDictionary(NSMutableDictionary *dict, NSString *tag, NSString *va
|
||||||
std::vector<std::string> keys = mod->get_metadata_keys();
|
std::vector<std::string> keys = mod->get_metadata_keys();
|
||||||
|
|
||||||
for(std::vector<std::string>::iterator key = keys.begin(); key != keys.end(); ++key) {
|
for(std::vector<std::string>::iterator key = keys.begin(); key != keys.end(); ++key) {
|
||||||
NSString *tag = guess_encoding_of_string((*key).c_str());
|
@autoreleasepool {
|
||||||
NSString *value = guess_encoding_of_string(mod->get_metadata(*key).c_str());
|
NSString *tag = guess_encoding_of_string((*key).c_str());
|
||||||
if(*key == "type")
|
NSString *value = guess_encoding_of_string(mod->get_metadata(*key).c_str());
|
||||||
continue;
|
if(*key == "type")
|
||||||
else if(*key == "type_long") {
|
continue;
|
||||||
setDictionary(dict, @"codec", value);
|
else if(*key == "type_long") {
|
||||||
} else {
|
setDictionary(dict, @"codec", value);
|
||||||
setDictionary(dict, tag, value);
|
} else {
|
||||||
|
setDictionary(dict, tag, value);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -154,37 +154,41 @@ static void setDictionary(NSMutableDictionary *dict, NSString *tag, NSString *va
|
||||||
FLAC__StreamMetadata_VorbisComment_Entry entry = { .entry = (FLAC__byte *)tags->user_comments[i], .length = tags->comment_lengths[i] };
|
FLAC__StreamMetadata_VorbisComment_Entry entry = { .entry = (FLAC__byte *)tags->user_comments[i], .length = tags->comment_lengths[i] };
|
||||||
char *name, *value;
|
char *name, *value;
|
||||||
if(FLAC__metadata_object_vorbiscomment_entry_to_name_value_pair(entry, &name, &value)) {
|
if(FLAC__metadata_object_vorbiscomment_entry_to_name_value_pair(entry, &name, &value)) {
|
||||||
NSString *tagName = guess_encoding_of_string(name);
|
@autoreleasepool {
|
||||||
free(name);
|
NSString *tagName = guess_encoding_of_string(name);
|
||||||
|
free(name);
|
||||||
|
|
||||||
tagName = [tagName lowercaseString];
|
tagName = [tagName lowercaseString];
|
||||||
|
|
||||||
if([tagName isEqualToString:@"metadata_block_picture"]) {
|
if([tagName isEqualToString:@"metadata_block_picture"]) {
|
||||||
OpusPictureTag _pic = { 0 };
|
OpusPictureTag _pic = { 0 };
|
||||||
if(opus_picture_tag_parse(&_pic, value) >= 0) {
|
if(opus_picture_tag_parse(&_pic, value) >= 0) {
|
||||||
if(_pic.format == OP_PIC_FORMAT_PNG ||
|
if(_pic.format == OP_PIC_FORMAT_PNG ||
|
||||||
_pic.format == OP_PIC_FORMAT_JPEG ||
|
_pic.format == OP_PIC_FORMAT_JPEG ||
|
||||||
_pic.format == OP_PIC_FORMAT_GIF ||
|
_pic.format == OP_PIC_FORMAT_GIF ||
|
||||||
_pic.format == OP_PIC_FORMAT_UNKNOWN) {
|
_pic.format == OP_PIC_FORMAT_UNKNOWN) {
|
||||||
_albumArt = [NSData dataWithBytes:_pic.data length:_pic.data_length];
|
_albumArt = [NSData dataWithBytes:_pic.data length:_pic.data_length];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
opus_picture_tag_clear(&_pic);
|
||||||
|
} else if([tagName isEqualToString:@"unsynced lyrics"] ||
|
||||||
|
[tagName isEqualToString:@"lyrics"]) {
|
||||||
|
setDictionary(_metaDict, @"unsyncedlyrics", guess_encoding_of_string(value));
|
||||||
|
} else {
|
||||||
|
setDictionary(_metaDict, tagName, guess_encoding_of_string(value));
|
||||||
}
|
}
|
||||||
opus_picture_tag_clear(&_pic);
|
|
||||||
} else if([tagName isEqualToString:@"unsynced lyrics"] ||
|
|
||||||
[tagName isEqualToString:@"lyrics"]) {
|
|
||||||
setDictionary(_metaDict, @"unsyncedlyrics", guess_encoding_of_string(value));
|
|
||||||
} else {
|
|
||||||
setDictionary(_metaDict, tagName, guess_encoding_of_string(value));
|
|
||||||
}
|
|
||||||
|
|
||||||
free(value);
|
free(value);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(![_albumArt isEqualToData:albumArt] ||
|
if(![_albumArt isEqualToData:albumArt] ||
|
||||||
![_metaDict isEqualToDictionary:metaDict]) {
|
![_metaDict isEqualToDictionary:metaDict]) {
|
||||||
metaDict = _metaDict;
|
@autoreleasepool {
|
||||||
albumArt = _albumArt;
|
metaDict = _metaDict;
|
||||||
|
albumArt = _albumArt;
|
||||||
|
}
|
||||||
|
|
||||||
if(![source seekable]) {
|
if(![source seekable]) {
|
||||||
[self willChangeValueForKey:@"metadata"];
|
[self willChangeValueForKey:@"metadata"];
|
||||||
|
@ -203,29 +207,33 @@ static void setDictionary(NSMutableDictionary *dict, NSString *tag, NSString *va
|
||||||
if([sourceClass isEqual:NSClassFromString(@"HTTPSource")]) {
|
if([sourceClass isEqual:NSClassFromString(@"HTTPSource")]) {
|
||||||
HTTPSource *httpSource = (HTTPSource *)source;
|
HTTPSource *httpSource = (HTTPSource *)source;
|
||||||
if([httpSource hasMetadata]) {
|
if([httpSource hasMetadata]) {
|
||||||
NSDictionary *metadata = [httpSource metadata];
|
@autoreleasepool {
|
||||||
NSString *_genre = [metadata valueForKey:@"genre"];
|
NSDictionary *metadata = [httpSource metadata];
|
||||||
NSString *_album = [metadata valueForKey:@"album"];
|
NSString *_genre = [metadata valueForKey:@"genre"];
|
||||||
NSString *_artist = [metadata valueForKey:@"artist"];
|
NSString *_album = [metadata valueForKey:@"album"];
|
||||||
NSString *_title = [metadata valueForKey:@"title"];
|
NSString *_artist = [metadata valueForKey:@"artist"];
|
||||||
|
NSString *_title = [metadata valueForKey:@"title"];
|
||||||
|
|
||||||
if(_genre && [_genre length]) {
|
if(_genre && [_genre length]) {
|
||||||
setDictionary(_icyMetaDict, @"genre", _genre);
|
setDictionary(_icyMetaDict, @"genre", _genre);
|
||||||
}
|
}
|
||||||
if(_album && [_album length]) {
|
if(_album && [_album length]) {
|
||||||
setDictionary(_icyMetaDict, @"album", _album);
|
setDictionary(_icyMetaDict, @"album", _album);
|
||||||
}
|
}
|
||||||
if(_artist && [_artist length]) {
|
if(_artist && [_artist length]) {
|
||||||
setDictionary(_icyMetaDict, @"artist", _artist);
|
setDictionary(_icyMetaDict, @"artist", _artist);
|
||||||
}
|
}
|
||||||
if(_title && [_title length]) {
|
if(_title && [_title length]) {
|
||||||
setDictionary(_icyMetaDict, @"title", _title);
|
setDictionary(_icyMetaDict, @"title", _title);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(![_icyMetaDict isEqualToDictionary:icyMetaDict]) {
|
if(![_icyMetaDict isEqualToDictionary:icyMetaDict]) {
|
||||||
icyMetaDict = _icyMetaDict;
|
@autoreleasepool {
|
||||||
|
icyMetaDict = _icyMetaDict;
|
||||||
|
}
|
||||||
[self willChangeValueForKey:@"metadata"];
|
[self willChangeValueForKey:@"metadata"];
|
||||||
[self didChangeValueForKey:@"metadata"];
|
[self didChangeValueForKey:@"metadata"];
|
||||||
}
|
}
|
||||||
|
|
|
@ -134,34 +134,38 @@ static void setDictionary(NSMutableDictionary *dict, NSString *tag, NSString *va
|
||||||
FLAC__StreamMetadata_VorbisComment_Entry entry = { .entry = (FLAC__byte *)tags->user_comments[i], .length = tags->comment_lengths[i] };
|
FLAC__StreamMetadata_VorbisComment_Entry entry = { .entry = (FLAC__byte *)tags->user_comments[i], .length = tags->comment_lengths[i] };
|
||||||
char *name, *value;
|
char *name, *value;
|
||||||
if(FLAC__metadata_object_vorbiscomment_entry_to_name_value_pair(entry, &name, &value)) {
|
if(FLAC__metadata_object_vorbiscomment_entry_to_name_value_pair(entry, &name, &value)) {
|
||||||
NSString *tagName = guess_encoding_of_string(name);
|
@autoreleasepool {
|
||||||
free(name);
|
NSString *tagName = guess_encoding_of_string(name);
|
||||||
|
free(name);
|
||||||
|
|
||||||
tagName = [tagName lowercaseString];
|
tagName = [tagName lowercaseString];
|
||||||
|
|
||||||
if([tagName isEqualToString:@"metadata_block_picture"]) {
|
if([tagName isEqualToString:@"metadata_block_picture"]) {
|
||||||
flac_picture_t *picture = flac_picture_parse_from_base64(value);
|
flac_picture_t *picture = flac_picture_parse_from_base64(value);
|
||||||
if(picture) {
|
if(picture) {
|
||||||
if(picture->binary && picture->binary_length) {
|
if(picture->binary && picture->binary_length) {
|
||||||
_albumArt = [NSData dataWithBytes:picture->binary length:picture->binary_length];
|
_albumArt = [NSData dataWithBytes:picture->binary length:picture->binary_length];
|
||||||
|
}
|
||||||
|
flac_picture_free(picture);
|
||||||
}
|
}
|
||||||
flac_picture_free(picture);
|
} else if([tagName isEqualToString:@"unsynced lyrics"] ||
|
||||||
|
[tagName isEqualToString:@"lyrics"]) {
|
||||||
|
setDictionary(_metaDict, @"unsyncedlyrics", guess_encoding_of_string(value));
|
||||||
|
} else {
|
||||||
|
setDictionary(_metaDict, tagName, guess_encoding_of_string(value));
|
||||||
}
|
}
|
||||||
} else if([tagName isEqualToString:@"unsynced lyrics"] ||
|
|
||||||
[tagName isEqualToString:@"lyrics"]) {
|
|
||||||
setDictionary(_metaDict, @"unsyncedlyrics", guess_encoding_of_string(value));
|
|
||||||
} else {
|
|
||||||
setDictionary(_metaDict, tagName, guess_encoding_of_string(value));
|
|
||||||
}
|
|
||||||
|
|
||||||
free(value);
|
free(value);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(![_albumArt isEqualToData:albumArt] ||
|
if(![_albumArt isEqualToData:albumArt] ||
|
||||||
![_metaDict isEqualToDictionary:metaDict]) {
|
![_metaDict isEqualToDictionary:metaDict]) {
|
||||||
metaDict = _metaDict;
|
@autoreleasepool {
|
||||||
albumArt = _albumArt;
|
metaDict = _metaDict;
|
||||||
|
albumArt = _albumArt;
|
||||||
|
}
|
||||||
|
|
||||||
if(![source seekable]) {
|
if(![source seekable]) {
|
||||||
[self willChangeValueForKey:@"metadata"];
|
[self willChangeValueForKey:@"metadata"];
|
||||||
|
@ -180,29 +184,33 @@ static void setDictionary(NSMutableDictionary *dict, NSString *tag, NSString *va
|
||||||
if([sourceClass isEqual:NSClassFromString(@"HTTPSource")]) {
|
if([sourceClass isEqual:NSClassFromString(@"HTTPSource")]) {
|
||||||
HTTPSource *httpSource = (HTTPSource *)source;
|
HTTPSource *httpSource = (HTTPSource *)source;
|
||||||
if([httpSource hasMetadata]) {
|
if([httpSource hasMetadata]) {
|
||||||
NSDictionary *metadata = [httpSource metadata];
|
@autoreleasepool {
|
||||||
NSString *_genre = [metadata valueForKey:@"genre"];
|
NSDictionary *metadata = [httpSource metadata];
|
||||||
NSString *_album = [metadata valueForKey:@"album"];
|
NSString *_genre = [metadata valueForKey:@"genre"];
|
||||||
NSString *_artist = [metadata valueForKey:@"artist"];
|
NSString *_album = [metadata valueForKey:@"album"];
|
||||||
NSString *_title = [metadata valueForKey:@"title"];
|
NSString *_artist = [metadata valueForKey:@"artist"];
|
||||||
|
NSString *_title = [metadata valueForKey:@"title"];
|
||||||
|
|
||||||
if(_genre && [_genre length]) {
|
if(_genre && [_genre length]) {
|
||||||
setDictionary(_icyMetaDict, @"genre", _genre);
|
setDictionary(_icyMetaDict, @"genre", _genre);
|
||||||
}
|
}
|
||||||
if(_album && [_album length]) {
|
if(_album && [_album length]) {
|
||||||
setDictionary(_icyMetaDict, @"album", _album);
|
setDictionary(_icyMetaDict, @"album", _album);
|
||||||
}
|
}
|
||||||
if(_artist && [_artist length]) {
|
if(_artist && [_artist length]) {
|
||||||
setDictionary(_icyMetaDict, @"artist", _artist);
|
setDictionary(_icyMetaDict, @"artist", _artist);
|
||||||
}
|
}
|
||||||
if(_title && [_title length]) {
|
if(_title && [_title length]) {
|
||||||
setDictionary(_icyMetaDict, @"title", _title);
|
setDictionary(_icyMetaDict, @"title", _title);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(![_icyMetaDict isEqualToDictionary:icyMetaDict]) {
|
if(![_icyMetaDict isEqualToDictionary:icyMetaDict]) {
|
||||||
icyMetaDict = _icyMetaDict;
|
@autoreleasepool {
|
||||||
|
icyMetaDict = _icyMetaDict;
|
||||||
|
}
|
||||||
[self willChangeValueForKey:@"metadata"];
|
[self willChangeValueForKey:@"metadata"];
|
||||||
[self didChangeValueForKey:@"metadata"];
|
[self didChangeValueForKey:@"metadata"];
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,6 +23,8 @@ NS_ASSUME_NONNULL_BEGIN
|
||||||
- (NSDictionary *)coalesceEntryInfo:(NSDictionary *)pe;
|
- (NSDictionary *)coalesceEntryInfo:(NSDictionary *)pe;
|
||||||
- (void)reset;
|
- (void)reset;
|
||||||
|
|
||||||
|
- (NSString *)coalesceString:(NSString *)in;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
NS_ASSUME_NONNULL_END
|
NS_ASSUME_NONNULL_END
|
||||||
|
|
Loading…
Reference in a new issue