/***************************************************************************** * VLCPlayerControlWebSocket.m * VLC for iOS ***************************************************************************** * Copyright (c) 2015 VideoLAN. All rights reserved. * $Id$ * * Authors: Felix Paul Kühne * * Refer to the COPYING file of the official project for license. *****************************************************************************/ #import "VLCPlayerControlWebSocket.h" @implementation VLCPlayerControlWebSocket - (void)dealloc { [[NSNotificationCenter defaultCenter] removeObserver:self]; } - (void)didOpen { NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter]; [notificationCenter addObserver:self selector:@selector(playbackStarted) name:VLCPlaybackControllerPlaybackDidStart object:nil]; [notificationCenter addObserver:self selector:@selector(playbackStarted) name:VLCPlaybackControllerPlaybackDidResume object:nil]; [notificationCenter addObserver:self selector:@selector(_respondToPlaying) name:VLCPlaybackControllerPlaybackMetadataDidChange object:nil]; [notificationCenter addObserver:self selector:@selector(playbackPaused) name:VLCPlaybackControllerPlaybackDidPause object:nil]; [notificationCenter addObserver:self selector:@selector(playbackEnded) name:VLCPlaybackControllerPlaybackDidStop object:nil]; [notificationCenter addObserver:self selector:@selector(playbackEnded) name:VLCPlaybackControllerPlaybackDidFail object:nil]; [notificationCenter addObserver:self selector:@selector(playbackSeekTo) name:VLCPlaybackControllerPlaybackPositionUpdated object:nil]; APLog(@"web socket did open"); [super didOpen]; } - (void)didReceiveMessage:(NSString *)msg { NSError *error; NSDictionary *receivedDict = [NSJSONSerialization JSONObjectWithData:[msg dataUsingEncoding:NSUTF8StringEncoding] options:NSJSONReadingAllowFragments error:&error]; if (error != nil) { APLog(@"JSON deserialization failed for %@", msg); return; } NSString *type = receivedDict[@"type"]; if (!type) { APLog(@"No type in received JSON dict %@", receivedDict); } if ([type isEqualToString:@"playing"]) { [self _respondToPlaying]; } else if ([type isEqualToString:@"play"]) { [self _respondToPlay]; } else if ([type isEqualToString:@"pause"]) { [self _respondToPause]; } else if ([type isEqualToString:@"ended"]) { [self _respondToEnded]; } else if ([type isEqualToString:@"seekTo"]) { [self _respondToSeek:receivedDict]; } else if ([type isEqualToString:@"openURL"]) { [self performSelectorOnMainThread:@selector(_respondToOpenURL:) withObject:receivedDict waitUntilDone:NO]; } else if ([type isEqualToString:@"volume"]) { [self sendMessage:@"VOLUME CONTROL NOT SUPPORTED ON THIS DEVICE"]; } else [self sendMessage:@"INVALID REQUEST!"]; } #ifndef NDEBUG - (void)didClose { APLog(@"web socket did close"); [super didClose]; } #endif - (void)_respondToPlaying { /* JSON response { "type": "playing", "currentTime": 42, "media": { "id": "some id", "title": "some title", "duration": 120000 } } */ VLCPlaybackController *vpc = [VLCPlaybackController sharedInstance]; NSDictionary *returnDict; if (vpc.activePlaybackSession) { VLCMediaPlayer *player = vpc.mediaPlayer; if (player) { VLCMedia *media = player.media; if (media) { NSURL *url = media.url; NSString *mediaTitle = vpc.mediaTitle; if (!mediaTitle) mediaTitle = url.lastPathComponent; NSDictionary *mediaDict = @{ @"id" : url.absoluteString, @"title" : mediaTitle, @"duration" : @(media.length.intValue)}; returnDict = @{ @"currentTime" : @(player.time.intValue), @"type" : @"playing", @"media" : mediaDict }; } } } if (!returnDict) { returnDict = [NSDictionary dictionary]; } NSError *error; NSData *returnData = [NSJSONSerialization dataWithJSONObject:returnDict options:0 error:&error]; if (error != nil) { APLog(@"%s: JSON serialization failed %@", __PRETTY_FUNCTION__, error); } [self sendData:returnData]; } #pragma mark - play - (void)_respondToPlay { VLCPlaybackController *vpc = [VLCPlaybackController sharedInstance]; VLCMediaListPlayer *listPlayer = vpc.listPlayer; if (listPlayer) { [listPlayer play]; } } - (void)playbackStarted { /* { "type": "play", "currentTime": 42 } */ VLCPlaybackController *vpc = [VLCPlaybackController sharedInstance]; VLCMediaPlayer *player = vpc.mediaPlayer; if (player) { VLCMedia *media = player.media; if (media) { NSDictionary *returnDict = @{ @"currentTime" : @(player.time.intValue), @"type" : @"play" }; NSError *error; NSData *returnData = [NSJSONSerialization dataWithJSONObject:returnDict options:0 error:&error]; if (error != nil) { APLog(@"%s: JSON serialization failed %@", __PRETTY_FUNCTION__, error); } [self sendData:returnData]; } } } #pragma mark - pause - (void)_respondToPause { VLCPlaybackController *vpc = [VLCPlaybackController sharedInstance]; VLCMediaListPlayer *listPlayer = vpc.listPlayer; if (listPlayer) { [listPlayer pause]; } } - (void)playbackPaused { /* { "type": "pause", "currentTime": 42, } */ VLCPlaybackController *vpc = [VLCPlaybackController sharedInstance]; VLCMediaPlayer *player = vpc.mediaPlayer; if (player) { VLCMedia *media = player.media; if (media) { NSDictionary *returnDict = @{ @"currentTime" : @(player.time.intValue), @"type" : @"pause" }; NSError *error; NSData *returnData = [NSJSONSerialization dataWithJSONObject:returnDict options:0 error:&error]; if (error != nil) { APLog(@"%s: JSON serialization failed %@", __PRETTY_FUNCTION__, error); } [self sendData:returnData]; } } } #pragma mark - ended - (void)_respondToEnded { VLCPlaybackController *vpc = [VLCPlaybackController sharedInstance]; [vpc stopPlayback]; } - (void)playbackEnded { /* { "type": "ended" } */ VLCPlaybackController *vpc = [VLCPlaybackController sharedInstance]; VLCMediaPlayer *player = vpc.mediaPlayer; if (player) { VLCMedia *media = player.media; if (media) { NSDictionary *returnDict = @{ @"type" : @"ended" }; NSError *error; NSData *returnData = [NSJSONSerialization dataWithJSONObject:returnDict options:0 error:&error]; if (error != nil) { APLog(@"%s: JSON serialization failed %@", __PRETTY_FUNCTION__, error); } [self sendData:returnData]; } } } #pragma mark - seek - (void)_respondToSeek:(NSDictionary *)dictionary { /* { "currentTime" = 12514; "type" = seekTo; } */ VLCPlaybackController *vpc = [VLCPlaybackController sharedInstance]; VLCMediaPlayer *player = vpc.mediaPlayer; if (!player) return; VLCMedia *media = player.media; if (!media) return; player.position = [dictionary[@"currentTime"] floatValue] / (CGFloat)media.length.intValue; } - (void)playbackSeekTo { /* { "type": "seekTo", "currentTime": 42, "media": { "id": 42 } } */ VLCPlaybackController *vpc = [VLCPlaybackController sharedInstance]; VLCMediaPlayer *player = vpc.mediaPlayer; if (player) { VLCMedia *media = player.media; if (media) { NSDictionary *mediaDict = @{ @"id" : media.url.absoluteString}; NSDictionary *returnDict = @{ @"currentTime" : @(player.time.intValue), @"type" : @"seekTo", @"media" : mediaDict }; NSError *error; NSData *returnData = [NSJSONSerialization dataWithJSONObject:returnDict options:0 error:&error]; if (error != nil) { APLog(@"%s: JSON serialization failed %@", __PRETTY_FUNCTION__, error); } [self sendData:returnData]; } } } #pragma mark - openURL - (void)_respondToOpenURL:(NSDictionary *)dictionary { /*  { "type": "OpenURL", "url": "https://vimeo.com/74370512" } */ BOOL needsMediaList = NO; VLCPlaybackController *vpc = [VLCPlaybackController sharedInstance]; VLCMediaList *mediaList = vpc.mediaList; if (!mediaList) { needsMediaList = YES; mediaList = [[VLCMediaList alloc] init]; } NSString *urlString = dictionary[@"url"]; if (urlString == nil || urlString.length == 0) return; /* force store update */ NSUbiquitousKeyValueStore *ubiquitousKeyValueStore = [NSUbiquitousKeyValueStore defaultStore]; [ubiquitousKeyValueStore synchronize]; /* fetch data from cloud */ NSMutableArray *recentURLs = [NSMutableArray arrayWithArray:[ubiquitousKeyValueStore arrayForKey:kVLCRecentURLs]]; /* re-order array and add item */ if ([recentURLs indexOfObject:urlString] != NSNotFound) [recentURLs removeObject:urlString]; if (recentURLs.count >= 100) [recentURLs removeLastObject]; [recentURLs addObject:urlString]; /* sync back */ [ubiquitousKeyValueStore setArray:recentURLs forKey:kVLCRecentURLs]; [mediaList addMedia:[VLCMedia mediaWithURL:[NSURL URLWithString:urlString]]]; if (needsMediaList) { [vpc playMediaList:mediaList firstIndex:0]; } VLCFullscreenMovieTVViewController *movieVC = [VLCFullscreenMovieTVViewController fullscreenMovieTVViewController]; if (![movieVC isBeingPresented]) { if ([[[UIApplication sharedApplication].delegate.window rootViewController] presentedViewController] != nil) { [[[[UIApplication sharedApplication].delegate.window rootViewController] presentedViewController] presentViewController:movieVC animated:NO completion:nil]; } else { [[[UIApplication sharedApplication].delegate.window rootViewController] presentViewController:movieVC animated:NO completion:nil]; } } } @end