diff --git a/index.js b/index.js index 4b11305..9cce9d1 100644 --- a/index.js +++ b/index.js @@ -49,10 +49,15 @@ class Player extends Component { ); ReactNativeAudioStreaming.getStatus((error, status) => { + console.log('Status', status); (error) ? console.log(error) : this.setState(status) }); } + componentWillUnmount() { + this.subscription.remove(); + } + _onPress() { switch (this.state.status) { case PLAYING: diff --git a/ios/ReactNativeAudioStreaming.h b/ios/ReactNativeAudioStreaming.h index 11bda88..0b38370 100644 --- a/ios/ReactNativeAudioStreaming.h +++ b/ios/ReactNativeAudioStreaming.h @@ -11,6 +11,9 @@ @property (nonatomic, readwrite) BOOL showNowPlayingInfo; @property (nonatomic, readwrite) NSString *lastUrlString; @property (nonatomic, retain) NSString *currentSong; +@property (nonatomic, retain) NSString *currentArtist; +@property (nonatomic, retain) NSString *currentArtwork; +@property (nonatomic, readwrite) BOOL fetchedAlbumArt; - (void)play:(NSString *) streamUrl options:(NSDictionary *)options; - (void)pause; diff --git a/ios/ReactNativeAudioStreaming.m b/ios/ReactNativeAudioStreaming.m index 0c03015..bd2fefb 100644 --- a/ios/ReactNativeAudioStreaming.m +++ b/ios/ReactNativeAudioStreaming.m @@ -27,10 +27,10 @@ - (ReactNativeAudioStreaming *)init [self.audioPlayer setDelegate:self]; self.lastUrlString = @""; [NSTimer scheduledTimerWithTimeInterval:0.5 target:self selector:@selector(tick:) userInfo:nil repeats:YES]; - + NSLog(@"AudioPlayer initialized"); } - + return self; } @@ -40,12 +40,12 @@ -(void) tick:(NSTimer*)timer if (!self.audioPlayer) { return; } - + if (self.audioPlayer.currentlyPlayingQueueItemId != nil && self.audioPlayer.state == STKAudioPlayerStatePlaying) { NSNumber *progress = [NSNumber numberWithFloat:self.audioPlayer.progress]; NSNumber *duration = [NSNumber numberWithFloat:self.audioPlayer.duration]; NSString *url = [NSString stringWithString:self.audioPlayer.currentlyPlayingQueueItemId]; - + [self.bridge.eventDispatcher sendDeviceEventWithName:@"AudioBridgeEvent" body:@{ @"status": @"STREAMING", @"progress": progress, @@ -73,20 +73,32 @@ - (void)dealloc } [self activate]; - + if (self.audioPlayer.state == STKAudioPlayerStatePaused && [self.lastUrlString isEqualToString:streamUrl]) { [self.audioPlayer resume]; } else { [self.audioPlayer play:streamUrl]; } + self.fetchedAlbumArt = false; self.lastUrlString = streamUrl; self.showNowPlayingInfo = false; - + if ([options objectForKey:@"showIniOSMediaCenter"]) { + if ([options objectForKey:@"metadata"]) { + NSDictionary *metadata = [options objectForKey:@"metadata"]; + self.currentSong = [metadata objectForKey:@"title"]; + self.currentArtist = [metadata objectForKey:@"artist"]; + self.currentArtwork = [metadata objectForKey:@"artwork"]; + [self.bridge.eventDispatcher sendDeviceEventWithName:@"AudioBridgeEvent" body:@{ + @"status": @"METADATA_UPDATED", + @"key": @"StreamTitle", + @"value": self.currentSong + }]; + } self.showNowPlayingInfo = [[options objectForKey:@"showIniOSMediaCenter"] boolValue]; } - + if (self.showNowPlayingInfo) { //unregister any existing registrations [self unregisterAudioInterruptionNotifications]; @@ -95,7 +107,7 @@ - (void)dealloc [self registerAudioInterruptionNotifications]; [self registerRemoteControlEvents]; } - + [self setNowPlayingInfo:true]; } @@ -113,9 +125,9 @@ - (void)dealloc if (!self.audioPlayer) { return; } - + double newtime = self.audioPlayer.progress + seconds; - + if (self.audioPlayer.duration < newtime) { [self.audioPlayer stop]; [self setNowPlayingInfo:false]; @@ -129,9 +141,9 @@ - (void)dealloc if (!self.audioPlayer) { return; } - + double newtime = self.audioPlayer.progress - seconds; - + if (newtime < 0) { [self.audioPlayer seekToTime:0.0]; } else { @@ -187,7 +199,7 @@ - (void)dealloc } else if ([self.audioPlayer state] == STKAudioPlayerStateBuffering) { status = @"BUFFERING"; } - + callback(@[[NSNull null], @{@"status": status, @"progress": progress, @"duration": duration, @"url": self.lastUrlString}]); } @@ -215,7 +227,7 @@ - (void)audioPlayer:(STKAudioPlayer *)player unexpectedError:(STKAudioPlayerErro - (void)audioPlayer:(STKAudioPlayer *)audioPlayer didReadStreamMetadata:(NSDictionary *)dictionary { NSLog(@"AudioPlayer SONG NAME %@", dictionary[@"StreamTitle"]); - + self.currentSong = dictionary[@"StreamTitle"] ? dictionary[@"StreamTitle"] : @""; [self.bridge.eventDispatcher sendDeviceEventWithName:@"AudioBridgeEvent" body:@{ @"status": @"METADATA_UPDATED", @@ -229,33 +241,33 @@ - (void)audioPlayer:(STKAudioPlayer *)player stateChanged:(STKAudioPlayerState)s { NSNumber *duration = [NSNumber numberWithFloat:self.audioPlayer.duration]; NSNumber *progress = [NSNumber numberWithFloat:self.audioPlayer.progress]; - + switch (state) { case STKAudioPlayerStatePlaying: [self.bridge.eventDispatcher sendDeviceEventWithName:@"AudioBridgeEvent" body:@{@"status": @"PLAYING", @"progress": progress, @"duration": duration, @"url": self.lastUrlString}]; break; - + case STKAudioPlayerStatePaused: [self.bridge.eventDispatcher sendDeviceEventWithName:@"AudioBridgeEvent" body:@{@"status": @"PAUSED", @"progress": progress, @"duration": duration, @"url": self.lastUrlString}]; break; - + case STKAudioPlayerStateStopped: [self.bridge.eventDispatcher sendDeviceEventWithName:@"AudioBridgeEvent" body:@{@"status": @"STOPPED", @"progress": progress, @"duration": duration, @"url": self.lastUrlString}]; break; - + case STKAudioPlayerStateBuffering: [self.bridge.eventDispatcher sendDeviceEventWithName:@"AudioBridgeEvent" body:@{@"status": @"BUFFERING"}]; break; - + case STKAudioPlayerStateError: [self.bridge.eventDispatcher sendDeviceEventWithName:@"AudioBridgeEvent" body:@{@"status": @"ERROR"}]; break; - + default: break; } @@ -267,10 +279,10 @@ - (void)audioPlayer:(STKAudioPlayer *)player stateChanged:(STKAudioPlayerState)s - (void)activate { NSError *categoryError = nil; - + [[AVAudioSession sharedInstance] setActive:YES error:&categoryError]; [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:&categoryError]; - + if (categoryError) { NSLog(@"Error setting category! %@", [categoryError description]); } @@ -279,9 +291,9 @@ - (void)activate - (void)deactivate { NSError *categoryError = nil; - + [[AVAudioSession sharedInstance] setActive:NO error:&categoryError]; - + if (categoryError) { NSLog(@"Error setting category! %@", [categoryError description]); } @@ -294,7 +306,7 @@ - (void)setSharedAudioSessionCategory [[AVAudioSession sharedInstance] setActive:NO error:&categoryError]; [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryAmbient error:&categoryError]; - + if (categoryError) { NSLog(@"Error setting category! %@", [categoryError description]); } @@ -328,10 +340,10 @@ - (void)onAudioInterruption:(NSNotification *)notification { // Get the user info dictionary NSDictionary *interruptionDict = notification.userInfo; - + // Get the AVAudioSessionInterruptionTypeKey enum from the dictionary NSInteger interuptionType = [[interruptionDict valueForKey:AVAudioSessionInterruptionTypeKey] integerValue]; - + // Decide what to do based on interruption type switch (interuptionType) { @@ -339,13 +351,13 @@ - (void)onAudioInterruption:(NSNotification *)notification NSLog(@"Audio Session Interruption case started."); [self.audioPlayer pause]; break; - + case AVAudioSessionInterruptionTypeEnded: NSLog(@"Audio Session Interruption case ended."); self.isPlayingWithOthers = [[AVAudioSession sharedInstance] isOtherAudioPlaying]; (self.isPlayingWithOthers) ? [self.audioPlayer stop] : [self.audioPlayer resume]; break; - + default: NSLog(@"Audio Session Interruption Notification case default."); break; @@ -354,41 +366,41 @@ - (void)onAudioInterruption:(NSNotification *)notification - (void)onRouteChangeInterruption:(NSNotification *)notification { - + NSDictionary *interruptionDict = notification.userInfo; NSInteger routeChangeReason = [[interruptionDict valueForKey:AVAudioSessionRouteChangeReasonKey] integerValue]; - + switch (routeChangeReason) { case AVAudioSessionRouteChangeReasonUnknown: NSLog(@"routeChangeReason : AVAudioSessionRouteChangeReasonUnknown"); break; - + case AVAudioSessionRouteChangeReasonNewDeviceAvailable: // A user action (such as plugging in a headset) has made a preferred audio route available. NSLog(@"routeChangeReason : AVAudioSessionRouteChangeReasonNewDeviceAvailable"); break; - + case AVAudioSessionRouteChangeReasonOldDeviceUnavailable: // The previous audio output path is no longer available. [self.audioPlayer stop]; break; - + case AVAudioSessionRouteChangeReasonCategoryChange: // The category of the session object changed. Also used when the session is first activated. NSLog(@"routeChangeReason : AVAudioSessionRouteChangeReasonCategoryChange"); //AVAudioSessionRouteChangeReasonCategoryChange break; - + case AVAudioSessionRouteChangeReasonOverride: // The output route was overridden by the app. NSLog(@"routeChangeReason : AVAudioSessionRouteChangeReasonOverride"); break; - + case AVAudioSessionRouteChangeReasonWakeFromSleep: // The route changed when the device woke up from sleep. NSLog(@"routeChangeReason : AVAudioSessionRouteChangeReasonWakeFromSleep"); break; - + case AVAudioSessionRouteChangeReasonNoSuitableRouteForCategory: // The route changed because no suitable route is now available for the specified category. NSLog(@"routeChangeReason : AVAudioSessionRouteChangeReasonNoSuitableRouteForCategory"); @@ -431,19 +443,55 @@ - (void)unregisterRemoteControlEvents [commandCenter.pauseCommand removeTarget:self]; } ++ (void) loadFromURL: (NSURL*) url callback:(void (^)(UIImage *image))callback { + dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0ul); + dispatch_async(queue, ^{ + NSData * imageData = [NSData dataWithContentsOfURL:url]; + dispatch_async(dispatch_get_main_queue(), ^{ + UIImage *image = [UIImage imageWithData:imageData]; + callback(image); + }); + }); +} + - (void)setNowPlayingInfo:(bool)isPlaying { if (self.showNowPlayingInfo) { - // TODO Get artwork from stream - // MPMediaItemArtwork *artwork = [[MPMediaItemArtwork alloc]initWithImage:[UIImage imageNamed:@"webradio1"]]; - - NSString* appName = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleName"]; - NSDictionary *nowPlayingInfo = [NSDictionary dictionaryWithObjectsAndKeys: - self.currentSong ? self.currentSong : @"", MPMediaItemPropertyAlbumTitle, - @"", MPMediaItemPropertyAlbumArtist, - appName ? appName : @"AppName", MPMediaItemPropertyTitle, - [NSNumber numberWithFloat:isPlaying ? 1.0f : 0.0], MPNowPlayingInfoPropertyPlaybackRate, nil]; - [MPNowPlayingInfoCenter defaultCenter].nowPlayingInfo = nowPlayingInfo; + + NSString* bundleDisplayName =[[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleDisplayName"]; + NSString* appName = bundleDisplayName? bundleDisplayName : + [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleName"]; + + NSString* title = self.currentSong ? self.currentSong : @""; + NSString* artist = self.currentArtist ? self.currentArtist : @""; + NSString* applicationName = appName ? appName : @"AppName"; + + if (!self.fetchedAlbumArt && self.currentArtwork != nil) { + NSString* imageUrl = self.currentArtwork; + dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); + dispatch_async(queue, ^{ + MPNowPlayingInfoCenter *infoCenter = [MPNowPlayingInfoCenter defaultCenter]; + UIImage *artworkImage = [UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:imageUrl]]]; + if(artworkImage) { + MPMediaItemArtwork *albumArt = [[MPMediaItemArtwork alloc] initWithImage: artworkImage]; + NSMutableDictionary *songInfo = + infoCenter.nowPlayingInfo ? [infoCenter.nowPlayingInfo mutableCopy] : [NSMutableDictionary dictionary]; + [songInfo setValue:albumArt forKey:MPMediaItemPropertyArtwork]; + infoCenter.nowPlayingInfo = songInfo; + } + self.fetchedAlbumArt = true; + }); + } + + MPNowPlayingInfoCenter *infoCenter = [MPNowPlayingInfoCenter defaultCenter]; + NSDictionary *nowPlayingInfo = infoCenter.nowPlayingInfo ? + [NSMutableDictionary dictionaryWithDictionary:infoCenter.nowPlayingInfo ] : [NSMutableDictionary dictionary]; + + [nowPlayingInfo setValue:title forKey:MPMediaItemPropertyAlbumTitle]; + [nowPlayingInfo setValue:artist forKey:MPMediaItemPropertyArtist]; + [nowPlayingInfo setValue:applicationName forKey:MPMediaItemPropertyTitle]; + [nowPlayingInfo setValue:[NSNumber numberWithFloat:isPlaying ? 1.0f : 0.0] forKey:MPNowPlayingInfoPropertyPlaybackRate]; + infoCenter.nowPlayingInfo = nowPlayingInfo; } else { [MPNowPlayingInfoCenter defaultCenter].nowPlayingInfo = nil; }