diff --git a/Playlist/PlaylistLoader.m b/Playlist/PlaylistLoader.m index 65c77d010..1945d2f90 100644 --- a/Playlist/PlaylistLoader.m +++ b/Playlist/PlaylistLoader.m @@ -746,25 +746,29 @@ NSURL *_Nullable urlForPath(NSString *_Nullable path); NSURL *url = urlForPath(key); [op addExecutionBlock:^{ - DLog(@"Loading metadata for %@", url); - [[FIRCrashlytics crashlytics] logWithFormat:@"Loading metadata for %@", url]; + @autoreleasepool { + DLog(@"Loading metadata for %@", url); + [[FIRCrashlytics crashlytics] logWithFormat:@"Loading metadata for %@", url]; - NSDictionary *entryProperties = [AudioPropertiesReader propertiesForURL:url]; - if(entryProperties == nil) - return; + NSDictionary *entryProperties = [AudioPropertiesReader propertiesForURL:url]; + if(entryProperties == nil) + 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]; - entryInfo = [weakDataStore coalesceEntryInfo:entryInfo]; - [weakArray addObject:key]; - [weakArray addObject:entryInfo]; - [uniquePathsEntries setObject:[[NSMutableArray alloc] init] forKey:key]; - progress += progressstep; - [self setProgressJobStatus:progress]; - [weakLock unlock]; + [weakLock lock]; + @autoreleasepool { + entryInfo = [weakDataStore coalesceEntryInfo:entryInfo]; + } + [weakArray addObject:key]; + [weakArray addObject:entryInfo]; + [uniquePathsEntries setObject:[[NSMutableArray alloc] init] forKey:key]; + progress += progressstep; + [self setProgressJobStatus:progress]; + [weakLock unlock]; + } }]; [queue addOperation:op]; diff --git a/Plugins/FFMPEG/FFMPEGDecoder.m b/Plugins/FFMPEG/FFMPEGDecoder.m index 7676e3cd2..ef1def498 100644 --- a/Plugins/FFMPEG/FFMPEGDecoder.m +++ b/Plugins/FFMPEG/FFMPEGDecoder.m @@ -562,75 +562,77 @@ static void setDictionary(NSMutableDictionary *dict, NSString *tag, NSString *va } tag = NULL; while((tag = av_dict_get(metadata, "", tag, AV_DICT_IGNORE_SUFFIX))) { - if(!strcasecmp(tag->key, "streamtitle")) { - NSString *artistTitle = guess_encoding_of_string(tag->value); - NSArray *splitValues = [artistTitle componentsSeparatedByString:@" - "]; - NSString *_artist = @""; - NSString *_title = [splitValues objectAtIndex:0]; - if([splitValues count] > 1) { - _artist = _title; - _title = [splitValues objectAtIndex:1]; - setDictionary(_metaDict, @"artist", _artist); - 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]; + @autoreleasepool { + if(!strcasecmp(tag->key, "streamtitle")) { + NSString *artistTitle = guess_encoding_of_string(tag->value); + NSArray *splitValues = [artistTitle componentsSeparatedByString:@" - "]; + NSString *_artist = @""; + NSString *_title = [splitValues objectAtIndex:0]; + if([splitValues count] > 1) { + _artist = _title; + _title = [splitValues objectAtIndex:1]; + setDictionary(_metaDict, @"artist", _artist); + 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]; + } + } + 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")]) { HTTPSource *httpSource = (HTTPSource *)source; if([httpSource hasMetadata]) { - NSDictionary *metadata = [httpSource metadata]; - NSString *_genre = [metadata valueForKey:@"genre"]; - NSString *_album = [metadata valueForKey:@"album"]; - NSString *_artist = [metadata valueForKey:@"artist"]; - NSString *_title = [metadata valueForKey:@"title"]; - - if(_genre && [_genre length]) { - [_metaDict setObject:@[_genre] forKey:@"genre"]; - } - if(_album && [_album length]) { - [_metaDict setObject:@[_album] forKey:@"album"]; - } - if(_artist && [_artist length]) { - [_metaDict setObject:@[_artist] forKey:@"artist"]; - } - if(_title && [_title length]) { - [_metaDict setObject:@[_title] forKey:@"title"]; + @autoreleasepool { + NSDictionary *metadata = [httpSource metadata]; + NSString *_genre = [metadata valueForKey:@"genre"]; + NSString *_album = [metadata valueForKey:@"album"]; + NSString *_artist = [metadata valueForKey:@"artist"]; + NSString *_title = [metadata valueForKey:@"title"]; + + if(_genre && [_genre length]) { + [_metaDict setObject:@[_genre] forKey:@"genre"]; + } + if(_album && [_album length]) { + [_metaDict setObject:@[_album] forKey:@"album"]; + } + if(_artist && [_artist length]) { + [_metaDict setObject:@[_artist] forKey:@"artist"]; + } + if(_title && [_title length]) { + [_metaDict setObject:@[_title] forKey:@"title"]; + } } } } if(![_metaDict isEqualToDictionary:metaDict]) { - metaDict = _metaDict; + @autoreleasepool { + metaDict = _metaDict; + } if(![source seekable]) { [self willChangeValueForKey:@"metadata"]; [self didChangeValueForKey:@"metadata"]; diff --git a/Plugins/Flac/Flac.xcodeproj/project.pbxproj b/Plugins/Flac/Flac.xcodeproj/project.pbxproj index d0a7840f4..aea5f3cda 100644 --- a/Plugins/Flac/Flac.xcodeproj/project.pbxproj +++ b/Plugins/Flac/Flac.xcodeproj/project.pbxproj @@ -42,6 +42,7 @@ 83747C4A2862DCF40021245F /* Shared.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Shared.xcconfig; sourceTree = ""; }; 8384912D180816C900E7332D /* Logging.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Logging.h; path = ../../Utils/Logging.h; sourceTree = ""; }; 83AA660A27B7DAE40098D4B8 /* cuesheet.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = cuesheet.m; sourceTree = ""; }; + 83C70A392ACC32FB004B60E4 /* RedundantPlaylistDataStore.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RedundantPlaylistDataStore.h; path = ../../Utils/RedundantPlaylistDataStore.h; sourceTree = ""; }; 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 = ""; }; D2F7E65807B2D6F200F64583 /* CoreData.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreData.framework; path = /System/Library/Frameworks/CoreData.framework; sourceTree = ""; }; @@ -93,6 +94,7 @@ 08FB77AFFE84173DC02AAC07 /* Classes */ = { isa = PBXGroup; children = ( + 83C70A392ACC32FB004B60E4 /* RedundantPlaylistDataStore.h */, 834A42B4287AF7AA00EB9D9B /* AudioChunk.h */, 8301C145287805F500651A6E /* NSDictionary+Merge.h */, 8301C146287805F500651A6E /* NSDictionary+Merge.m */, diff --git a/Plugins/Flac/FlacDecoder.h b/Plugins/Flac/FlacDecoder.h index 75fe1fb53..87c97a949 100644 --- a/Plugins/Flac/FlacDecoder.h +++ b/Plugins/Flac/FlacDecoder.h @@ -14,6 +14,8 @@ #import "Plugin.h" +#import "RedundantPlaylistDataStore.h" + @interface FlacDecoder : NSObject { FLAC__StreamDecoder *decoder; void *blockBuffer; @@ -42,6 +44,8 @@ BOOL cuesheetFound; NSString *cuesheet; + + RedundantPlaylistDataStore *dataStore; } - (void)setSource:(id)s; diff --git a/Plugins/Flac/FlacDecoder.m b/Plugins/Flac/FlacDecoder.m index 1be3c60e6..6fa8b44d7 100644 --- a/Plugins/Flac/FlacDecoder.m +++ b/Plugins/Flac/FlacDecoder.m @@ -210,14 +210,16 @@ void MetadataCallback(const FLAC__StreamDecoder *decoder, const FLAC__StreamMeta if(metadata->type == FLAC__METADATA_TYPE_CUESHEET && !flacDecoder->cuesheetFound) { flacDecoder->cuesheetFound = YES; - NSString *_cuesheet; - grabbag__cuesheet_emit(&_cuesheet, metadata, [[NSString stringWithFormat:@"\"%@\"", [[[flacDecoder->source url] path] lastPathComponent]] UTF8String]); + @autoreleasepool { + NSString *_cuesheet; + grabbag__cuesheet_emit(&_cuesheet, metadata, [[NSString stringWithFormat:@"\"%@\"", [[[flacDecoder->source url] path] lastPathComponent]] UTF8String]); - if(![_cuesheet isEqual:flacDecoder->cuesheet]) { - flacDecoder->cuesheet = _cuesheet; - if(![flacDecoder->source seekable]) { - [flacDecoder willChangeValueForKey:@"metadata"]; - [flacDecoder didChangeValueForKey:@"metadata"]; + if(![_cuesheet isEqual:flacDecoder->cuesheet]) { + flacDecoder->cuesheet = _cuesheet; + if(![flacDecoder->source seekable]) { + [flacDecoder willChangeValueForKey:@"metadata"]; + [flacDecoder didChangeValueForKey:@"metadata"]; + } } } } @@ -241,25 +243,29 @@ void MetadataCallback(const FLAC__StreamDecoder *decoder, const FLAC__StreamMeta char *_name; char *_value; if(FLAC__metadata_object_vorbiscomment_entry_to_name_value_pair(vorbis_comment->comments[i], &_name, &_value)) { - NSString *name = guess_encoding_of_string(_name); - NSString *value = guess_encoding_of_string(_value); - free(_name); - free(_value); - name = [name lowercaseString]; - if([name isEqualToString:@"cuesheet"]) { - _cuesheet = value; - flacDecoder->cuesheetFound = YES; - } else if([name isEqualToString:@"waveformatextensible_channel_mask"]) { - if([value hasPrefix:@"0x"]) { - char *end; - const char *_value = [value UTF8String] + 2; - flacDecoder->channelConfig = (uint32_t)strtoul(_value, &end, 16); + @autoreleasepool { + NSString *name = guess_encoding_of_string(_name); + NSString *value = guess_encoding_of_string(_value); + free(_name); + free(_value); + name = [name lowercaseString]; + name = [flacDecoder->dataStore coalesceString:name]; + value = [flacDecoder->dataStore coalesceString:value]; + if([name isEqualToString:@"cuesheet"]) { + _cuesheet = value; + flacDecoder->cuesheetFound = YES; + } else if([name isEqualToString:@"waveformatextensible_channel_mask"]) { + 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; cuesheet = @""; + id dataStoreClass = NSClassFromString(@"RedundantPlaylistDataStore"); + dataStore = [[dataStoreClass alloc] init]; + decoder = FLAC__stream_decoder_new(); if(decoder == NULL) return NO; diff --git a/Plugins/HTTPSource/HTTPSource.m b/Plugins/HTTPSource/HTTPSource.m index 217d73fad..3b00c93b0 100644 --- a/Plugins/HTTPSource/HTTPSource.m +++ b/Plugins/HTTPSource/HTTPSource.m @@ -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) { // DLog (@"reading %d bytes of metadata\n", size); - DLog(@"%s", meta); - const char *e = meta + size; - const char strtitle[] = "StreamTitle='"; - char title[4096] = ""; - while(meta < e) { - if(!memcmp(meta, strtitle, sizeof(strtitle) - 1)) { - meta += sizeof(strtitle) - 1; - const char *substr_end = meta; - while(substr_end < e - 1 && (*substr_end != '\'' || *(substr_end + 1) != ';')) { - substr_end++; - } - if(substr_end >= e) { - return -1; // end of string not found - } - size_t s = substr_end - meta; - s = MIN(sizeof(title) - 1, s); - memcpy(title, meta, s); - title[s] = 0; - DLog(@"got stream title: %s\n", title); - { - char *tit = strstr(title, " - "); - if(tit) { - *tit = 0; - tit += 3; - - if(!strncmp(tit, "text=\"", 6)) { // Hack for a certain stream - char *titfirst = tit + 6; - char *titlast = strchr(titfirst, '"'); - if(titlast) { - *titlast = 0; + @autoreleasepool { + DLog(@"%s", meta); + const char *e = meta + size; + const char strtitle[] = "StreamTitle='"; + char title[4096] = ""; + while(meta < e) { + if(!memcmp(meta, strtitle, sizeof(strtitle) - 1)) { + meta += sizeof(strtitle) - 1; + const char *substr_end = meta; + while(substr_end < e - 1 && (*substr_end != '\'' || *(substr_end + 1) != ';')) { + substr_end++; + } + if(substr_end >= e) { + return -1; // end of string not found + } + size_t s = substr_end - meta; + s = MIN(sizeof(title) - 1, s); + memcpy(title, meta, s); + title[s] = 0; + DLog(@"got stream title: %s\n", title); + { + char *tit = strstr(title, " - "); + if(tit) { + *tit = 0; + tit += 3; + + if(!strncmp(tit, "text=\"", 6)) { // Hack for a certain stream + char *titfirst = tit + 6; + char *titlast = strchr(titfirst, '"'); + if(titlast) { + *titlast = 0; + } + tit = titfirst; } - tit = titfirst; - } - const char *orig_title = [fp->title UTF8String]; - const char *orig_artist = [fp->artist UTF8String]; + const char *orig_title = [fp->title UTF8String]; + const char *orig_artist = [fp->artist UTF8String]; - if(!orig_title || strcasecmp(orig_title, tit)) { - fp->title = guess_encoding_of_string(tit); - fp->gotmetadata = 1; - } - if(!orig_artist || strcasecmp(orig_artist, title)) { - fp->artist = guess_encoding_of_string(title); - fp->gotmetadata = 1; - } - } else { - const char *orig_title = [fp->title UTF8String]; - if(!orig_title || strcasecmp(orig_title, title)) { - fp->artist = @""; - fp->title = guess_encoding_of_string(title); - fp->gotmetadata = 1; + if(!orig_title || strcasecmp(orig_title, tit)) { + fp->title = guess_encoding_of_string(tit); + fp->gotmetadata = 1; + } + if(!orig_artist || strcasecmp(orig_artist, title)) { + fp->artist = guess_encoding_of_string(title); + fp->gotmetadata = 1; + } + } else { + const char *orig_title = [fp->title UTF8String]; + if(!orig_title || strcasecmp(orig_title, title)) { + fp->artist = @""; + fp->title = guess_encoding_of_string(title); + 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) { @@ -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)) { p++; } - p = parse_header(p, end, key, sizeof(key), value, sizeof(value)); - DLog(@"%skey=%s value=%s\n", fp->icyheader ? "[icy] " : "", key, value); - if(!strcasecmp((char *)key, "Content-Type")) { - fp->content_type = guess_encoding_of_string((const char *)value); - } else if(!strcasecmp((char *)key, "Content-Length")) { - char *end; - fp->length = strtol((const char *)value, &end, 10); - } else if(!strcasecmp((char *)key, "icy-name")) { - fp->title = guess_encoding_of_string((const char *)value); - fp->gotmetadata = 1; - } else if(!strcasecmp((char *)key, "icy-genre")) { - fp->genre = guess_encoding_of_string((const char *)value); - fp->gotmetadata = 1; - } else if(!strcasecmp((char *)key, "icy-metaint")) { - // printf ("icy-metaint: %d\n", atoi (value)); - char *end; - fp->icy_metaint = (int)strtoul((const char *)value, &end, 10); - fp->wait_meta = fp->icy_metaint; - } else if(!strcasecmp((char *)key, "icy-url")) { - fp->album = guess_encoding_of_string((const char *)value); - fp->gotmetadata = 1; + @autoreleasepool { + p = parse_header(p, end, key, sizeof(key), value, sizeof(value)); + DLog(@"%skey=%s value=%s\n", fp->icyheader ? "[icy] " : "", key, value); + if(!strcasecmp((char *)key, "Content-Type")) { + fp->content_type = guess_encoding_of_string((const char *)value); + } else if(!strcasecmp((char *)key, "Content-Length")) { + char *end; + fp->length = strtol((const char *)value, &end, 10); + } else if(!strcasecmp((char *)key, "icy-name")) { + fp->title = guess_encoding_of_string((const char *)value); + fp->gotmetadata = 1; + } else if(!strcasecmp((char *)key, "icy-genre")) { + fp->genre = guess_encoding_of_string((const char *)value); + fp->gotmetadata = 1; + } else if(!strcasecmp((char *)key, "icy-metaint")) { + // printf ("icy-metaint: %d\n", atoi (value)); + char *end; + fp->icy_metaint = (int)strtoul((const char *)value, &end, 10); + fp->wait_meta = fp->icy_metaint; + } else if(!strcasecmp((char *)key, "icy-url")) { + fp->album = guess_encoding_of_string((const char *)value); + fp->gotmetadata = 1; + } } // for icy streams, reset length diff --git a/Plugins/MIDI/MIDI/MIDIMetadataReader.mm b/Plugins/MIDI/MIDI/MIDIMetadataReader.mm index 6fde95f49..a4f29c79b 100644 --- a/Plugins/MIDI/MIDI/MIDIMetadataReader.mm +++ b/Plugins/MIDI/MIDI/MIDIMetadataReader.mm @@ -74,19 +74,23 @@ static void setDictionary(NSMutableDictionary *dict, NSString *tag, NSString *va NSMutableDictionary *dict = [[NSMutableDictionary alloc] init]; for(size_t i = 0; i < metadata.get_count(); ++i) { - const midi_meta_data_item &item = metadata[i]; - NSString *name = [guess_encoding_of_string(item.m_name.c_str()) lowercaseString]; - if(![name isEqualToString:@"type"]) { - if(remap_display_name && [name isEqualToString:@"display_name"]) - name = @"title"; - setDictionary(dict, name, guess_encoding_of_string(item.m_value.c_str())); + @autoreleasepool { + const midi_meta_data_item &item = metadata[i]; + NSString *name = [guess_encoding_of_string(item.m_name.c_str()) lowercaseString]; + if(![name isEqualToString:@"type"]) { + if(remap_display_name && [name isEqualToString:@"display_name"]) + name = @"title"; + setDictionary(dict, name, guess_encoding_of_string(item.m_value.c_str())); + } } } std::vector 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; diff --git a/Plugins/OpenMPT/OpenMPT/OMPTMetadataReader.mm b/Plugins/OpenMPT/OpenMPT/OMPTMetadataReader.mm index b5922805e..ba6eec5f0 100644 --- a/Plugins/OpenMPT/OpenMPT/OMPTMetadataReader.mm +++ b/Plugins/OpenMPT/OpenMPT/OMPTMetadataReader.mm @@ -69,14 +69,16 @@ static void setDictionary(NSMutableDictionary *dict, NSString *tag, NSString *va std::vector keys = mod->get_metadata_keys(); for(std::vector::iterator key = keys.begin(); key != keys.end(); ++key) { - NSString *tag = guess_encoding_of_string((*key).c_str()); - NSString *value = guess_encoding_of_string(mod->get_metadata(*key).c_str()); - if(*key == "type") - continue; - else if(*key == "type_long") { - setDictionary(dict, @"codec", value); - } else { - setDictionary(dict, tag, value); + @autoreleasepool { + NSString *tag = guess_encoding_of_string((*key).c_str()); + NSString *value = guess_encoding_of_string(mod->get_metadata(*key).c_str()); + if(*key == "type") + continue; + else if(*key == "type_long") { + setDictionary(dict, @"codec", value); + } else { + setDictionary(dict, tag, value); + } } } diff --git a/Plugins/Opus/Opus/OpusDecoder.m b/Plugins/Opus/Opus/OpusDecoder.m index ee58c08ea..e5ca355e3 100644 --- a/Plugins/Opus/Opus/OpusDecoder.m +++ b/Plugins/Opus/Opus/OpusDecoder.m @@ -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] }; char *name, *value; if(FLAC__metadata_object_vorbiscomment_entry_to_name_value_pair(entry, &name, &value)) { - NSString *tagName = guess_encoding_of_string(name); - free(name); + @autoreleasepool { + NSString *tagName = guess_encoding_of_string(name); + free(name); - tagName = [tagName lowercaseString]; + tagName = [tagName lowercaseString]; - if([tagName isEqualToString:@"metadata_block_picture"]) { - OpusPictureTag _pic = { 0 }; - if(opus_picture_tag_parse(&_pic, value) >= 0) { - if(_pic.format == OP_PIC_FORMAT_PNG || - _pic.format == OP_PIC_FORMAT_JPEG || - _pic.format == OP_PIC_FORMAT_GIF || - _pic.format == OP_PIC_FORMAT_UNKNOWN) { - _albumArt = [NSData dataWithBytes:_pic.data length:_pic.data_length]; + if([tagName isEqualToString:@"metadata_block_picture"]) { + OpusPictureTag _pic = { 0 }; + if(opus_picture_tag_parse(&_pic, value) >= 0) { + if(_pic.format == OP_PIC_FORMAT_PNG || + _pic.format == OP_PIC_FORMAT_JPEG || + _pic.format == OP_PIC_FORMAT_GIF || + _pic.format == OP_PIC_FORMAT_UNKNOWN) { + _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] || ![_metaDict isEqualToDictionary:metaDict]) { - metaDict = _metaDict; - albumArt = _albumArt; + @autoreleasepool { + metaDict = _metaDict; + albumArt = _albumArt; + } if(![source seekable]) { [self willChangeValueForKey:@"metadata"]; @@ -203,29 +207,33 @@ static void setDictionary(NSMutableDictionary *dict, NSString *tag, NSString *va if([sourceClass isEqual:NSClassFromString(@"HTTPSource")]) { HTTPSource *httpSource = (HTTPSource *)source; if([httpSource hasMetadata]) { - NSDictionary *metadata = [httpSource metadata]; - NSString *_genre = [metadata valueForKey:@"genre"]; - NSString *_album = [metadata valueForKey:@"album"]; - NSString *_artist = [metadata valueForKey:@"artist"]; - NSString *_title = [metadata valueForKey:@"title"]; + @autoreleasepool { + NSDictionary *metadata = [httpSource metadata]; + NSString *_genre = [metadata valueForKey:@"genre"]; + NSString *_album = [metadata valueForKey:@"album"]; + NSString *_artist = [metadata valueForKey:@"artist"]; + NSString *_title = [metadata valueForKey:@"title"]; - if(_genre && [_genre length]) { - setDictionary(_icyMetaDict, @"genre", _genre); - } - if(_album && [_album length]) { - setDictionary(_icyMetaDict, @"album", _album); - } - if(_artist && [_artist length]) { - setDictionary(_icyMetaDict, @"artist", _artist); - } - if(_title && [_title length]) { - setDictionary(_icyMetaDict, @"title", _title); + if(_genre && [_genre length]) { + setDictionary(_icyMetaDict, @"genre", _genre); + } + if(_album && [_album length]) { + setDictionary(_icyMetaDict, @"album", _album); + } + if(_artist && [_artist length]) { + setDictionary(_icyMetaDict, @"artist", _artist); + } + if(_title && [_title length]) { + setDictionary(_icyMetaDict, @"title", _title); + } } } } if(![_icyMetaDict isEqualToDictionary:icyMetaDict]) { - icyMetaDict = _icyMetaDict; + @autoreleasepool { + icyMetaDict = _icyMetaDict; + } [self willChangeValueForKey:@"metadata"]; [self didChangeValueForKey:@"metadata"]; } diff --git a/Plugins/Vorbis/VorbisDecoder.m b/Plugins/Vorbis/VorbisDecoder.m index a65b43215..58d979dbf 100644 --- a/Plugins/Vorbis/VorbisDecoder.m +++ b/Plugins/Vorbis/VorbisDecoder.m @@ -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] }; char *name, *value; if(FLAC__metadata_object_vorbiscomment_entry_to_name_value_pair(entry, &name, &value)) { - NSString *tagName = guess_encoding_of_string(name); - free(name); + @autoreleasepool { + NSString *tagName = guess_encoding_of_string(name); + free(name); - tagName = [tagName lowercaseString]; + tagName = [tagName lowercaseString]; - if([tagName isEqualToString:@"metadata_block_picture"]) { - flac_picture_t *picture = flac_picture_parse_from_base64(value); - if(picture) { - if(picture->binary && picture->binary_length) { - _albumArt = [NSData dataWithBytes:picture->binary length:picture->binary_length]; + if([tagName isEqualToString:@"metadata_block_picture"]) { + flac_picture_t *picture = flac_picture_parse_from_base64(value); + if(picture) { + if(picture->binary && 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] || ![_metaDict isEqualToDictionary:metaDict]) { - metaDict = _metaDict; - albumArt = _albumArt; + @autoreleasepool { + metaDict = _metaDict; + albumArt = _albumArt; + } if(![source seekable]) { [self willChangeValueForKey:@"metadata"]; @@ -180,29 +184,33 @@ static void setDictionary(NSMutableDictionary *dict, NSString *tag, NSString *va if([sourceClass isEqual:NSClassFromString(@"HTTPSource")]) { HTTPSource *httpSource = (HTTPSource *)source; if([httpSource hasMetadata]) { - NSDictionary *metadata = [httpSource metadata]; - NSString *_genre = [metadata valueForKey:@"genre"]; - NSString *_album = [metadata valueForKey:@"album"]; - NSString *_artist = [metadata valueForKey:@"artist"]; - NSString *_title = [metadata valueForKey:@"title"]; + @autoreleasepool { + NSDictionary *metadata = [httpSource metadata]; + NSString *_genre = [metadata valueForKey:@"genre"]; + NSString *_album = [metadata valueForKey:@"album"]; + NSString *_artist = [metadata valueForKey:@"artist"]; + NSString *_title = [metadata valueForKey:@"title"]; - if(_genre && [_genre length]) { - setDictionary(_icyMetaDict, @"genre", _genre); - } - if(_album && [_album length]) { - setDictionary(_icyMetaDict, @"album", _album); - } - if(_artist && [_artist length]) { - setDictionary(_icyMetaDict, @"artist", _artist); - } - if(_title && [_title length]) { - setDictionary(_icyMetaDict, @"title", _title); + if(_genre && [_genre length]) { + setDictionary(_icyMetaDict, @"genre", _genre); + } + if(_album && [_album length]) { + setDictionary(_icyMetaDict, @"album", _album); + } + if(_artist && [_artist length]) { + setDictionary(_icyMetaDict, @"artist", _artist); + } + if(_title && [_title length]) { + setDictionary(_icyMetaDict, @"title", _title); + } } } } if(![_icyMetaDict isEqualToDictionary:icyMetaDict]) { - icyMetaDict = _icyMetaDict; + @autoreleasepool { + icyMetaDict = _icyMetaDict; + } [self willChangeValueForKey:@"metadata"]; [self didChangeValueForKey:@"metadata"]; } diff --git a/Utils/RedundantPlaylistDataStore.h b/Utils/RedundantPlaylistDataStore.h index 2b2899e0d..64b29b421 100644 --- a/Utils/RedundantPlaylistDataStore.h +++ b/Utils/RedundantPlaylistDataStore.h @@ -23,6 +23,8 @@ NS_ASSUME_NONNULL_BEGIN - (NSDictionary *)coalesceEntryInfo:(NSDictionary *)pe; - (void)reset; +- (NSString *)coalesceString:(NSString *)in; + @end NS_ASSUME_NONNULL_END