From 6feedbbae367369a9f0f361f4b355bbf6b88c2e5 Mon Sep 17 00:00:00 2001 From: Jonathan Lott <921121+jlott1@users.noreply.github.com> Date: Thu, 19 Nov 2020 01:34:05 -0600 Subject: [PATCH 1/4] cleaned up logic parsing video details and added keywords property --- XCDYouTubeKit/XCDYouTubeVideo.h | 4 +++ XCDYouTubeKit/XCDYouTubeVideo.m | 55 +++++++++++++++------------------ 2 files changed, 29 insertions(+), 30 deletions(-) diff --git a/XCDYouTubeKit/XCDYouTubeVideo.h b/XCDYouTubeKit/XCDYouTubeVideo.h index 067d7c12..31399a17 100644 --- a/XCDYouTubeKit/XCDYouTubeVideo.h +++ b/XCDYouTubeKit/XCDYouTubeVideo.h @@ -81,6 +81,10 @@ extern NSString *const XCDYouTubeVideoQualityHTTPLiveStreaming; * The description of the video. */ @property (nonatomic, readonly) NSString *videoDescription; +/** + * The keywords of the video. + */ +@property (nonatomic, readonly) NSString *keywords; /** * The duration of the video in seconds. */ diff --git a/XCDYouTubeKit/XCDYouTubeVideo.m b/XCDYouTubeKit/XCDYouTubeVideo.m index 4d370711..2601706c 100644 --- a/XCDYouTubeKit/XCDYouTubeVideo.m +++ b/XCDYouTubeKit/XCDYouTubeVideo.m @@ -186,36 +186,31 @@ - (instancetype) initWithIdentifier:(NSString *)identifier info:(NSDictionary *) NSMutableArray *streamQueries = [[streamMap componentsSeparatedByString:@","] mutableCopy]; [streamQueries addObjectsFromArray:[adaptiveFormats componentsSeparatedByString:@","]]; - if (streamQueries == nil && (alternativeStreamMap.count > 0 || alternativeAdaptiveFormats.count > 0)) - { - streamQueries = [NSMutableArray new]; - [streamQueries addObjectsFromArray:alternativeStreamMap]; - [streamQueries addObjectsFromArray:alternativeAdaptiveFormats]; - } - - NSString *title = info[@"title"] == nil? videoDetails[@"title"] : info[@"title"]; - if (title == nil) - title = @""; - _title = title; - - NSString *author = info[@"author"] == nil? videoDetails[@"author"] : info[@"author"]; - if (author == nil) - author = @""; - _author = author; - - NSString *channelIdentifier = info[@"ucid"] == nil? videoDetails[@"channelId"] : info[@"ucid"]; - if (channelIdentifier == nil) - channelIdentifier = @""; - _channelIdentifier = channelIdentifier; - - NSString *description = videoDetails[@"shortDescription"]; - if (description == nil) - description = @""; - _videoDescription = description; - - _viewCount = info[@"viewCount"] == nil? [(NSString *)videoDetails[@"viewCount"] integerValue] : [(NSString *)info[@"viewCount"] integerValue]; - - _duration = info[@"length_seconds"] == nil? [(NSString *)videoDetails[@"lengthSeconds"] doubleValue] : [(NSString *)info[@"length_seconds"] doubleValue]; + if (streamQueries == nil && (alternativeStreamMap.count > 0 || alternativeAdaptiveFormats.count > 0)) + { + streamQueries = [NSMutableArray new]; + [streamQueries addObjectsFromArray:alternativeStreamMap]; + [streamQueries addObjectsFromArray:alternativeAdaptiveFormats]; + } + + NSString *title = info[@"title"] ?: videoDetails[@"title"] ?: @""; + NSString *author = info[@"author"] ?: videoDetails[@"author"] ?: @""; + NSString *keywords = info[@"keywords"] ?: videoDetails[@"keywords"] ?: @""; + NSString *channelIdentifier = info[@"ucid"] ?: videoDetails[@"channelId"] ?: @""; + NSString *description = videoDetails[@"shortDescription"] ?: @""; + + if([keywords isKindOfClass:[NSArray class]]) { + keywords = [(NSArray*)keywords componentsJoinedByString:@","]; + } + _title = title; + _author = author; + _keywords = keywords; + _channelIdentifier = channelIdentifier; + _videoDescription = description; + + _viewCount = [(NSString *)info[@"viewCount"] integerValue] ?: [(NSString *)videoDetails[@"viewCount"] integerValue]; + + _duration = [(NSString *)info[@"length_seconds"] doubleValue] ?: [(NSString *)videoDetails[@"lengthSeconds"] doubleValue]; NSString *thumbnail = info[@"thumbnail_url"] ?: info[@"iurl"]; NSURL *thumbnailURL = thumbnail ? [NSURL URLWithString:thumbnail] : nil; From 380f5c92a989c760cfc16b6d6f669268867990ac Mon Sep 17 00:00:00 2001 From: Jonathan Lott <921121+jlott1@users.noreply.github.com> Date: Thu, 19 Nov 2020 01:51:30 -0600 Subject: [PATCH 2/4] changed logic to allow completion handler to use calling queue instead of main queue to prevent blocking main thread --- XCDYouTubeKit/XCDYouTubeClient.m | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/XCDYouTubeKit/XCDYouTubeClient.m b/XCDYouTubeKit/XCDYouTubeClient.m index 5305d1ae..3bd8e420 100644 --- a/XCDYouTubeKit/XCDYouTubeClient.m +++ b/XCDYouTubeKit/XCDYouTubeClient.m @@ -8,6 +8,8 @@ @interface XCDYouTubeClient () @property (nonatomic, strong) NSOperationQueue *queue; +@property (nonatomic, strong) NSOperationQueue *currentQueue; + @end @implementation XCDYouTubeClient @@ -55,18 +57,20 @@ - (instancetype) initWithLanguageIdentifier:(NSString *)languageIdentifier @throw [NSException exceptionWithName:NSInvalidArgumentException reason:@"The `completionHandler` argument must not be nil." userInfo:nil]; XCDYouTubeVideoOperation *operation = [[XCDYouTubeVideoOperation alloc] initWithVideoIdentifier:videoIdentifier languageIdentifier:self.languageIdentifier cookies:cookies customPatterns:customPatterns]; + self.currentQueue = [NSOperationQueue currentQueue]; + operation.completionBlock = ^{ - [[NSOperationQueue mainQueue] addOperationWithBlock:^{ + [self.currentQueue addOperationWithBlock:^{ #pragma clang diagnostic push #pragma clang diagnostic ignored "-Warc-retain-cycles" - if (operation.video || operation.error) + if (operation.video || operation.error) // If both `video` and `error` are nil, then the operation was cancelled { - NSAssert(!(operation.video && operation.error), @"One of `video` or `error` must be nil."); completionHandler(operation.video, operation.error); } else { - NSAssert(operation.isCancelled, @"Both `video` and `error` can not be nil if the operation was not canceled."); + NSError* error = operation.error ?: [NSError errorWithDomain:@"XCDYouTubeClientDomain" code:-1 userInfo:@{NSLocalizedFailureReasonErrorKey : @"operation was cancelled or data failed to be parsed"}]; + completionHandler(nil, error); } operation.completionBlock = nil; #pragma clang diagnostic pop From 31b66eea7023b0902a296a87b7b3e62647da992c Mon Sep 17 00:00:00 2001 From: Jonathan Lott <921121+jlott1@users.noreply.github.com> Date: Fri, 20 Nov 2020 17:27:42 -0600 Subject: [PATCH 3/4] Resolved build issue --- XCDYouTubeKit/XCDYouTubeVideo.m | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/XCDYouTubeKit/XCDYouTubeVideo.m b/XCDYouTubeKit/XCDYouTubeVideo.m index 2601706c..de2fe3bc 100644 --- a/XCDYouTubeKit/XCDYouTubeVideo.m +++ b/XCDYouTubeKit/XCDYouTubeVideo.m @@ -208,9 +208,11 @@ - (instancetype) initWithIdentifier:(NSString *)identifier info:(NSDictionary *) _channelIdentifier = channelIdentifier; _videoDescription = description; - _viewCount = [(NSString *)info[@"viewCount"] integerValue] ?: [(NSString *)videoDetails[@"viewCount"] integerValue]; + NSString* viewCount = (NSString *)info[@"viewCount"] ?: (NSString *)videoDetails[@"viewCount"]; + _viewCount = [viewCount integerValue] ?: [viewCount integerValue]; - _duration = [(NSString *)info[@"length_seconds"] doubleValue] ?: [(NSString *)videoDetails[@"lengthSeconds"] doubleValue]; + NSString* lengthSeconds = (NSString *)info[@"length_seconds"] ?: (NSString *)videoDetails[@"lengthSeconds"]; + _duration = [lengthSeconds doubleValue]; NSString *thumbnail = info[@"thumbnail_url"] ?: info[@"iurl"]; NSURL *thumbnailURL = thumbnail ? [NSURL URLWithString:thumbnail] : nil; From f38875fb81b3c0bd8dc166d2011b7c6a71fe0859 Mon Sep 17 00:00:00 2001 From: Jonathan Lott <921121+jlott1@users.noreply.github.com> Date: Sun, 22 Nov 2020 00:17:26 -0600 Subject: [PATCH 4/4] resolved some unit tests errors --- .../XCDYouTubeClientTestCase.m | 92 +++++++++++-------- XCDYouTubeKit Tests/XCDYouTubeKitTestCase.h | 2 + .../XCDYouTubeProtectedVideosTestCase.m | 52 +++++------ .../XCDYouTubeVideoOperationTestCase.m | 4 +- ...YouTubeVideoPlayerViewControllerTestCase.m | 10 +- .../XCDYouTubeVideoQueryOperationTestCase.m | 2 +- XCDYouTubeKit/XCDYouTubeClient.m | 12 ++- 7 files changed, 99 insertions(+), 75 deletions(-) diff --git a/XCDYouTubeKit Tests/XCDYouTubeClientTestCase.m b/XCDYouTubeKit Tests/XCDYouTubeClientTestCase.m index 7797e70f..fdf89edf 100644 --- a/XCDYouTubeKit Tests/XCDYouTubeClientTestCase.m +++ b/XCDYouTubeKit Tests/XCDYouTubeClientTestCase.m @@ -9,6 +9,8 @@ #import #import +NSTimeInterval kDefaultNetworkTimeout = 5; + @interface XCDYouTubeClientTestCase : XCDYouTubeKitTestCase @end @@ -23,7 +25,7 @@ - (void) testThatVideoIsAvailalbeOnDetailPageEventLabel XCTAssertNotNil(video); [expectation fulfill]; }]; - [self waitForExpectationsWithTimeout:5 handler:nil]; + [self waitForExpectationsWithTimeout:kDefaultNetworkTimeout handler:nil]; } - (void) testThatVideoHasMetadata @@ -42,7 +44,7 @@ - (void) testThatVideoHasMetadata XCTAssertTrue(video.duration > 0); [expectation fulfill]; }]; - [self waitForExpectationsWithTimeout:5 handler:nil]; + [self waitForExpectationsWithTimeout:kDefaultNetworkTimeout handler:nil]; } #if TARGET_OS_OSX - (void) testThatThumbnailsAreOrderedCorrectly @@ -70,7 +72,7 @@ - (void) testThatThumbnailsAreOrderedCorrectly [expectation fulfill]; }]; - [self waitForExpectationsWithTimeout:5 handler:nil]; + [self waitForExpectationsWithTimeout:kDefaultNetworkTimeout handler:nil]; } #endif - (void) testThatVideoHasOtherStreams @@ -105,7 +107,7 @@ - (void) testThatVideoHasOtherStreams [expectation fulfill]; }]; - [self waitForExpectationsWithTimeout:5 handler:nil]; + [self waitForExpectationsWithTimeout:kDefaultNetworkTimeout handler:nil]; } - (void) testVideoThatHasCaptions @@ -120,7 +122,7 @@ - (void) testVideoThatHasCaptions XCTAssertNotEqual(video.autoGeneratedCaptionURLs[@"en"], video.captionURLs[@"en"]); [expectation fulfill]; }]; - [self waitForExpectationsWithTimeout:5 handler:nil]; + [self waitForExpectationsWithTimeout:kDefaultNetworkTimeout handler:nil]; } @@ -133,7 +135,7 @@ - (void)testVideoWithDashManifest XCTAssertNotNil(video.streamURLs[@299], @"Could not find Dash video 299 in `streamURLs`"); //itag=299: {'ext': 'mp4', 'height': 1080, 'format_note': 'DASH video', 'vcodec': 'h264', 'fps': 60} [expectation fulfill]; }]; - [self waitForExpectationsWithTimeout:5 handler:nil]; + [self waitForExpectationsWithTimeout:kDefaultNetworkTimeout handler:nil]; } //See -[XCDYouTubeVideoOperation handleConnectionError:requestType] @@ -147,7 +149,7 @@ - (void)testConnectionErrorWithDashManifest [expectation fulfill]; }]; - [self waitForExpectationsWithTimeout:5 handler:nil]; + [self waitForExpectationsWithTimeout:kDefaultNetworkTimeout handler:nil]; } - (void) testVideoWithUndeterminedCaptionLanguageCode @@ -161,7 +163,7 @@ - (void) testVideoWithUndeterminedCaptionLanguageCode XCTAssertNotNil(video.captionURLs); [expectation fulfill]; }]; - [self waitForExpectationsWithTimeout:5 handler:nil]; + [self waitForExpectationsWithTimeout:kDefaultNetworkTimeout handler:nil]; } @@ -182,7 +184,7 @@ - (void) testMobileRestrictedVideo }]; [expectation fulfill]; }]; - [self waitForExpectationsWithTimeout:5 handler:nil]; + [self waitForExpectationsWithTimeout:kDefaultNetworkTimeout handler:nil]; } - (void) testLiveVideo @@ -197,7 +199,7 @@ - (void) testLiveVideo XCTAssertNotNil(video.streamURLs[XCDYouTubeVideoQualityHTTPLiveStreaming]); [expectation fulfill]; }]; - [self waitForExpectationsWithTimeout:5 handler:nil]; + [self waitForExpectationsWithTimeout:kDefaultNetworkTimeout handler:nil]; } // Test for https://github.com/0xced/XCDYouTubeKit/issues/420 @@ -218,7 +220,7 @@ - (void) testVideo1 }]; [expectation fulfill]; }]; - [self waitForExpectationsWithTimeout:5 handler:nil]; + [self waitForExpectationsWithTimeout:kDefaultNetworkTimeout handler:nil]; } // See https://github.com/0xced/XCDYouTubeKit/issues/420#issue-400541618 @@ -244,7 +246,7 @@ - (void) testVideo1IsPlayable }]; [dataTask resume]; }]; - [self waitForExpectationsWithTimeout:5 handler:nil]; + [self waitForExpectationsWithTimeout:kDefaultNetworkTimeout handler:nil]; } - (void) testVideo1ReturnsSomePlayableStreams @@ -283,7 +285,7 @@ - (void) testVideo1ReturnsSomePlayableStreams }]; }]; - [self waitForExpectationsWithTimeout:5 handler:nil]; + [self waitForExpectationsWithTimeout:kDefaultNetworkTimeout handler:nil]; } // Disable internet connection before running to allow some queries to fail @@ -319,7 +321,7 @@ - (void) testVideo1ReturnsSomePlayableStreamsEvenIfSomeFailDueToConnectionError_ }]; }]; - [self waitForExpectationsWithTimeout:5 handler:nil]; + [self waitForExpectationsWithTimeout:kDefaultNetworkTimeout handler:nil]; } // Disable internet connection before running to allow all queries to fail @@ -349,7 +351,7 @@ - (void) testVideo1ReturnsNoPlayableStreamsBecauseConnectionError_offline }]; }]; - [self waitForExpectationsWithTimeout:5 handler:nil]; + [self waitForExpectationsWithTimeout:kDefaultNetworkTimeout handler:nil]; } - (void) testVideo2ReturnsAllPlayableStreams @@ -378,7 +380,7 @@ - (void) testVideo2ReturnsAllPlayableStreams }]; }]; - [self waitForExpectationsWithTimeout:5 handler:nil]; + [self waitForExpectationsWithTimeout:kDefaultNetworkTimeout handler:nil]; } - (void) testVideo3ReturnsSomePlayableStreams @@ -419,7 +421,7 @@ - (void) testVideo3ReturnsSomePlayableStreams }]; }]; - [self waitForExpectationsWithTimeout:5 handler:nil]; + [self waitForExpectationsWithTimeout:kDefaultNetworkTimeout handler:nil]; } - (void) testThatQueryingLiveVideoReturnsPlayableStreams @@ -451,7 +453,7 @@ - (void) testThatQueryingLiveVideoReturnsPlayableStreams }]; }]; - [self waitForExpectationsWithTimeout:5 handler:nil]; + [self waitForExpectationsWithTimeout:kDefaultNetworkTimeout handler:nil]; } - (void) testQueryingWithSpecifiedStreamURLs @@ -495,7 +497,7 @@ - (void) testQueryingWithSpecifiedStreamURLs }]; }]; - [self waitForExpectationsWithTimeout:5 handler:nil]; + [self waitForExpectationsWithTimeout:kDefaultNetworkTimeout handler:nil]; } - (void) testQueryingWithSpecifiedStreamURLsSomeNotBeingInVideoObject @@ -544,7 +546,7 @@ - (void) testQueryingWithSpecifiedStreamURLsSomeNotBeingInVideoObject }]; }]; - [self waitForExpectationsWithTimeout:5 handler:nil]; + [self waitForExpectationsWithTimeout:kDefaultNetworkTimeout handler:nil]; } - (void) testQueryingWhenNoSpecifiedURLsAreInVideoObject @@ -580,7 +582,7 @@ - (void) testQueryingWhenNoSpecifiedURLsAreInVideoObject }]; }]; - [self waitForExpectationsWithTimeout:5 handler:nil]; + [self waitForExpectationsWithTimeout:kDefaultNetworkTimeout handler:nil]; } -(void) testQueryingWhenSpecifiedURLsAreEmpty @@ -609,7 +611,7 @@ -(void) testQueryingWhenSpecifiedURLsAreEmpty }]; }]; - [self waitForExpectationsWithTimeout:5 handler:nil]; + [self waitForExpectationsWithTimeout:kDefaultNetworkTimeout handler:nil]; } - (void) testExpiredLiveVideo @@ -622,7 +624,7 @@ - (void) testExpiredLiveVideo XCTAssertEqualObjects(error.localizedDescription, @"This live stream recording is not available."); [expectation fulfill]; }]; - [self waitForExpectationsWithTimeout:5 handler:nil]; + [self waitForExpectationsWithTimeout:kDefaultNetworkTimeout handler:nil]; } - (void) testRestrictedVideo @@ -638,7 +640,7 @@ - (void) testRestrictedVideo XCTAssertEqualObjects(error.localizedDescription, @"This video is no longer available because the YouTube account associated with this video has been terminated."); [expectation fulfill]; }]; - [self waitForExpectationsWithTimeout:5 handler:nil]; + [self waitForExpectationsWithTimeout:kDefaultNetworkTimeout handler:nil]; if (logLevel) setenv("XCDYouTubeKitLogLevel", logLevel, 1); @@ -660,7 +662,7 @@ - (void) testTooManyRequestsError XCTAssertEqualObjects(error.localizedDescription, @"The operation couldn’t be completed because too many requests were sent."); [expectation fulfill]; }]; - [self waitForExpectationsWithTimeout:5 handler:nil]; + [self waitForExpectationsWithTimeout:kDefaultNetworkTimeout handler:nil]; } - (void) testRemovedVideo @@ -674,7 +676,7 @@ - (void) testRemovedVideo XCTAssertEqualObjects(error.localizedDescription, @"This video is no longer available due to a copyright claim by Digital Rights Group Ltd."); [expectation fulfill]; }]; - [self waitForExpectationsWithTimeout:5 handler:nil]; + [self waitForExpectationsWithTimeout:kDefaultNetworkTimeout handler:nil]; } - (void) testGeoblockedVideo @@ -688,7 +690,7 @@ - (void) testGeoblockedVideo XCTAssertEqualObjects(error.localizedDescription, @"The uploader has not made this video available in your country."); [expectation fulfill]; }]; - [self waitForExpectationsWithTimeout:5 handler:nil]; + [self waitForExpectationsWithTimeout:kDefaultNetworkTimeout handler:nil]; } - (void) testInvalidVideoIdentifier @@ -702,7 +704,7 @@ - (void) testInvalidVideoIdentifier XCTAssertEqualObjects(error.localizedDescription, @"Invalid parameters."); [expectation fulfill]; }]; - [self waitForExpectationsWithTimeout:5 handler:nil]; + [self waitForExpectationsWithTimeout:kDefaultNetworkTimeout handler:nil]; } - (void) testEmptyResponse_offline @@ -715,21 +717,24 @@ - (void) testEmptyResponse_offline XCTAssertEqual(error.code, XCDYouTubeErrorEmptyResponse); [expectation fulfill]; }]; - [self waitForExpectationsWithTimeout:5 handler:nil]; + [self waitForExpectationsWithTimeout:kDefaultNetworkTimeout handler:nil]; } - (void) testNonExistentVideoIdentifier { __weak XCTestExpectation *expectation = [self expectationWithDescription:@""]; + NSDate* date = [NSDate date]; [[XCDYouTubeClient defaultClient] getVideoWithIdentifier:@"xxxxxxxxxxx" completionHandler:^(XCDYouTubeVideo *video, NSError *error) { + NSTimeInterval timeInterval = [date timeIntervalSinceNow]; + NSLog(@"time interval %@", @(timeInterval)); XCTAssertNil(video); XCTAssertEqualObjects(error.domain, XCDYouTubeVideoErrorDomain); XCTAssertEqual(error.code, XCDYouTubeErrorNoStreamAvailable); XCTAssertEqualObjects(error.localizedDescription, @"Video unavailable"); [expectation fulfill]; }]; - [self waitForExpectationsWithTimeout:5 handler:nil]; + [self waitForExpectationsWithTimeout:10 handler:nil]; } - (void) testFrenchClient @@ -743,7 +748,7 @@ - (void) testFrenchClient XCTAssertEqualObjects(error.localizedDescription, @"Vidéo non disponible"); [expectation fulfill]; }]; - [self waitForExpectationsWithTimeout:5 handler:nil]; + [self waitForExpectationsWithTimeout:kDefaultNetworkTimeout handler:nil]; } - (void) testNilVideoIdentifier @@ -757,7 +762,7 @@ - (void) testNilVideoIdentifier XCTAssertEqualObjects(error.localizedDescription, @"Invalid parameters."); [expectation fulfill]; }]; - [self waitForExpectationsWithTimeout:5 handler:nil]; + [self waitForExpectationsWithTimeout:kDefaultNetworkTimeout handler:nil]; } - (void) testSpaceVideoIdentifier @@ -771,7 +776,7 @@ - (void) testSpaceVideoIdentifier XCTAssertEqualObjects(error.localizedDescription, @"Invalid parameters."); [expectation fulfill]; }]; - [self waitForExpectationsWithTimeout:5 handler:nil]; + [self waitForExpectationsWithTimeout:kDefaultNetworkTimeout handler:nil]; } // Disable internet connection before running @@ -789,12 +794,12 @@ - (void) testConnectionError_offline XCTAssertEqual(underlyingError.code, NSURLErrorNotConnectedToInternet); [expectation fulfill]; }]; - [self waitForExpectationsWithTimeout:5 handler:nil]; + [self waitForExpectationsWithTimeout:kDefaultNetworkTimeout handler:nil]; } - (void) testUsingClientOnNonMainThread { - __weak XCTestExpectation *expectation = [self expectationWithDescription:@""]; + __weak XCTestExpectation *expectation = [self expectationWithDescription:@"It should call back on main thread"]; dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ XCTAssertFalse([NSThread isMainThread]); [[XCDYouTubeClient defaultClient] getVideoWithIdentifier:@"EdeVaT-zZt4" completionHandler:^(XCDYouTubeVideo *video, NSError *error) @@ -803,7 +808,20 @@ - (void) testUsingClientOnNonMainThread [expectation fulfill]; }]; }); - [self waitForExpectationsWithTimeout:5 handler:nil]; + + __weak XCTestExpectation *expectation2 = [self expectationWithDescription:@"It should call back on calling operation queue"]; + NSOperationQueue *queue = [[NSOperationQueue alloc] init]; + [queue addOperationWithBlock:^{ + XCTAssertFalse([NSThread isMainThread]); + [[XCDYouTubeClient defaultClient] getVideoWithIdentifier:@"EdeVaT-zZt4" completionHandler:^(XCDYouTubeVideo *video, NSError *error) + { + XCTAssertFalse([NSThread isMainThread]); + XCTAssertEqual(queue, [NSOperationQueue currentQueue]); + [expectation2 fulfill]; + }]; + }]; + + [self waitForExpectationsWithTimeout:kDefaultNetworkTimeout handler:nil]; } - (void) testCancelingOperation @@ -835,7 +853,7 @@ - (void) testCancelingOperationQueryOperation [expectation fulfill]; }]; - [self waitForExpectationsWithTimeout:5 handler:nil]; + [self waitForExpectationsWithTimeout:kDefaultNetworkTimeout handler:nil]; } - (void) testNilCompletionHandler diff --git a/XCDYouTubeKit Tests/XCDYouTubeKitTestCase.h b/XCDYouTubeKit Tests/XCDYouTubeKitTestCase.h index 532b8e31..6577b425 100644 --- a/XCDYouTubeKit Tests/XCDYouTubeKitTestCase.h +++ b/XCDYouTubeKit Tests/XCDYouTubeKitTestCase.h @@ -7,6 +7,8 @@ #import "VCR.h" #import "VCRCassetteManager.h" +extern NSTimeInterval kDefaultNetworkTimeout; + @interface XCDYouTubeKitTestCase : XCTestCase - (void) setUpTestWithSelector:(SEL)selector; @property NSArray*cookies; diff --git a/XCDYouTubeKit Tests/XCDYouTubeProtectedVideosTestCase.m b/XCDYouTubeKit Tests/XCDYouTubeProtectedVideosTestCase.m index e5ac8708..df11a6a5 100644 --- a/XCDYouTubeKit Tests/XCDYouTubeProtectedVideosTestCase.m +++ b/XCDYouTubeKit Tests/XCDYouTubeProtectedVideosTestCase.m @@ -62,7 +62,7 @@ - (void) testAgeRestrictedVideoThatRequiresCookiesWithUserCookies [expectation fulfill]; }]; - [self waitForExpectationsWithTimeout:5 handler:nil]; + [self waitForExpectationsWithTimeout:kDefaultNetworkTimeout handler:nil]; } - (void) testAgeRestrictedVideoThatRequiresCookiesWithUserCookiesIsPlayable @@ -88,7 +88,7 @@ - (void) testAgeRestrictedVideoThatRequiresCookiesWithUserCookiesIsPlayable [dataTask resume]; }]; - [self waitForExpectationsWithTimeout:5 handler:nil]; + [self waitForExpectationsWithTimeout:kDefaultNetworkTimeout handler:nil]; } - (void) testAgeRestrictedVideoThatRequiresCookiesWithoutCookies @@ -101,7 +101,7 @@ - (void) testAgeRestrictedVideoThatRequiresCookiesWithoutCookies [expectation fulfill]; }]; - [self waitForExpectationsWithTimeout:5 handler:nil]; + [self waitForExpectationsWithTimeout:kDefaultNetworkTimeout handler:nil]; } - (void) testAgeRestrictedVideo @@ -122,7 +122,7 @@ - (void) testAgeRestrictedVideo }]; [expectation fulfill]; }]; - [self waitForExpectationsWithTimeout:5 handler:nil]; + [self waitForExpectationsWithTimeout:kDefaultNetworkTimeout handler:nil]; } - (void) testAgeRestrictedUnratedVideo @@ -143,7 +143,7 @@ - (void) testAgeRestrictedUnratedVideo }]; [expectation fulfill]; }]; - [self waitForExpectationsWithTimeout:5 handler:nil]; + [self waitForExpectationsWithTimeout:kDefaultNetworkTimeout handler:nil]; } - (void) testProtectedVEVOVideoWithInvalidCustomPattern @@ -156,7 +156,7 @@ - (void) testProtectedVEVOVideoWithInvalidCustomPattern XCTAssertNil(video); [expectation fulfill]; }]; - [self waitForExpectationsWithTimeout:5 handler:nil]; + [self waitForExpectationsWithTimeout:kDefaultNetworkTimeout handler:nil]; } - (void) testProtectedVEVOVideoWithNilCustomPatternIsPlayable @@ -182,7 +182,7 @@ - (void) testProtectedVEVOVideoWithNilCustomPatternIsPlayable }]; [dataTask resume]; }]; - [self waitForExpectationsWithTimeout:5 handler:nil]; + [self waitForExpectationsWithTimeout:kDefaultNetworkTimeout handler:nil]; } - (void) testProtectedVEVOVideoWithEmptyCustomPatternIsPlayable @@ -208,7 +208,7 @@ - (void) testProtectedVEVOVideoWithEmptyCustomPatternIsPlayable }]; [dataTask resume]; }]; - [self waitForExpectationsWithTimeout:5 handler:nil]; + [self waitForExpectationsWithTimeout:kDefaultNetworkTimeout handler:nil]; } - (void) testProtectedVEVOVideoWithInvalidCustomPatternIsPlayable @@ -234,7 +234,7 @@ - (void) testProtectedVEVOVideoWithInvalidCustomPatternIsPlayable }]; [dataTask resume]; }]; - [self waitForExpectationsWithTimeout:5 handler:nil]; + [self waitForExpectationsWithTimeout:kDefaultNetworkTimeout handler:nil]; } - (void) testProtectedVEVOVideoWithValidCustomPatternIsPlayable @@ -260,7 +260,7 @@ - (void) testProtectedVEVOVideoWithValidCustomPatternIsPlayable }]; [dataTask resume]; }]; - [self waitForExpectationsWithTimeout:5 handler:nil]; + [self waitForExpectationsWithTimeout:kDefaultNetworkTimeout handler:nil]; } - (void) testProtectedVEVOVideo1 @@ -281,7 +281,7 @@ - (void) testProtectedVEVOVideo1 }]; [expectation fulfill]; }]; - [self waitForExpectationsWithTimeout:5 handler:nil]; + [self waitForExpectationsWithTimeout:kDefaultNetworkTimeout handler:nil]; } - (void) testProtectedVEVOVideo2 @@ -302,7 +302,7 @@ - (void) testProtectedVEVOVideo2 }]; [expectation fulfill]; }]; - [self waitForExpectationsWithTimeout:5 handler:nil]; + [self waitForExpectationsWithTimeout:kDefaultNetworkTimeout handler:nil]; } - (void) testProtectedVEVOVideo3 @@ -323,7 +323,7 @@ - (void) testProtectedVEVOVideo3 }]; [expectation fulfill]; }]; - [self waitForExpectationsWithTimeout:5 handler:nil]; + [self waitForExpectationsWithTimeout:kDefaultNetworkTimeout handler:nil]; } // See testAlternativeSignatureValue.xml for Charles Proxy Setting @@ -346,7 +346,7 @@ - (void) testAlternativeSignatureValue_offline }]; [expectation fulfill]; }]; - [self waitForExpectationsWithTimeout:5 handler:nil]; + [self waitForExpectationsWithTimeout:kDefaultNetworkTimeout handler:nil]; } - (void) testProtectedVEVOIsPlayable @@ -370,7 +370,7 @@ - (void) testProtectedVEVOIsPlayable }]; [dataTask resume]; }]; - [self waitForExpectationsWithTimeout:5 handler:nil]; + [self waitForExpectationsWithTimeout:kDefaultNetworkTimeout handler:nil]; } - (void) testDASHAudioWithRateBypassIsPlayable @@ -389,7 +389,7 @@ - (void) testDASHAudioWithRateBypassIsPlayable }]; [dataTask resume]; }]; - [self waitForExpectationsWithTimeout:5 handler:nil]; + [self waitForExpectationsWithTimeout:kDefaultNetworkTimeout handler:nil]; } - (void) testAgeRestrictedVEVOVideoWithUserCookies @@ -411,7 +411,7 @@ - (void) testAgeRestrictedVEVOVideoWithUserCookies [expectation fulfill]; }]; - [self waitForExpectationsWithTimeout:5 handler:nil]; + [self waitForExpectationsWithTimeout:kDefaultNetworkTimeout handler:nil]; } - (void) testAgeRestrictedVEVOVideoWithUserCookiesIsPlayable @@ -437,7 +437,7 @@ - (void) testAgeRestrictedVEVOVideoWithUserCookiesIsPlayable [dataTask resume]; }]; - [self waitForExpectationsWithTimeout:5 handler:nil]; + [self waitForExpectationsWithTimeout:kDefaultNetworkTimeout handler:nil]; } @@ -455,7 +455,7 @@ - (void) testProtectedVideoWithWebPageConnectionError_offline XCTAssertEqualObjects(error.localizedDescription, @"Video unavailable"); [expectation fulfill]; }]; - [self waitForExpectationsWithTimeout:5 handler:nil]; + [self waitForExpectationsWithTimeout:kDefaultNetworkTimeout handler:nil]; } // With Charles: Tools -> Black List... -> Add host:www.youtube.com and path:s/player/* to simulate connection error on the player script @@ -470,7 +470,7 @@ - (void) testProtectedVideoWithPlayerScriptConnectionError_offline XCTAssertEqualObjects(error.localizedDescription, @"Video unavailable"); [expectation fulfill]; }]; - [self waitForExpectationsWithTimeout:5 handler:nil]; + [self waitForExpectationsWithTimeout:kDefaultNetworkTimeout handler:nil]; } // Edit testProtectedVideoWithoutSignatureFunction.json by replacing `Xu=function` with `Xu=funXtion` and @@ -486,7 +486,7 @@ - (void) testProtectedVideoWithoutSignatureFunction_offline XCTAssertEqualObjects(error.localizedDescription, @"Video unavailable"); [expectation fulfill]; }]; - [self waitForExpectationsWithTimeout:5 handler:nil]; + [self waitForExpectationsWithTimeout:kDefaultNetworkTimeout handler:nil]; } // Edit testProtectedVideoWithoutJavaScriptPlayerURL.json by replacing `\"js\":` with `\"xs\":` and and `jsUrl` with `jsXUrl` @@ -501,7 +501,7 @@ - (void) testProtectedVideoWithoutJavaScriptPlayerURL_offline XCTAssertEqualObjects(error.localizedDescription, @"Video unavailable"); [expectation fulfill]; }]; - [self waitForExpectationsWithTimeout:5 handler:nil]; + [self waitForExpectationsWithTimeout:kDefaultNetworkTimeout handler:nil]; } // Edit testProtectedVideoWithNonAnonymousJavaScriptPlayerFunction.json by replacing all `(function` with `(Xfunction` @@ -518,7 +518,7 @@ - (void) testProtectedVideoWithNonAnonymousJavaScriptPlayerFunction_offline XCTAssertEqualObjects(error.localizedDescription, @"Video unavailable"); [expectation fulfill]; }]; - [self waitForExpectationsWithTimeout:5 handler:nil]; + [self waitForExpectationsWithTimeout:kDefaultNetworkTimeout handler:nil]; } // With Charles: Tools -> Black List... -> Add host:www.youtube.com and path:embed/* to simulate connection error on the web page and replace and `jsUrl` with `jsXUrl` in testAgeRestrictedVEVOVideoWithEmbedWebPageConnectionError.json @@ -533,7 +533,7 @@ - (void) testAgeRestrictedVEVOVideoWithEmbedWebPageConnectionError_offline XCTAssertEqualObjects(error.localizedDescription, @"This video may be inappropriate for some users."); [expectation fulfill]; }]; - [self waitForExpectationsWithTimeout:5 handler:nil]; + [self waitForExpectationsWithTimeout:kDefaultNetworkTimeout handler:nil]; } // See https://github.com/0xced/XCDYouTubeKit/issues/431 @@ -555,7 +555,7 @@ - (void) testAgeRestrictedVideo1 }]; [expectation fulfill]; }]; - [self waitForExpectationsWithTimeout:5 handler:nil]; + [self waitForExpectationsWithTimeout:kDefaultNetworkTimeout handler:nil]; } // See https://github.com/0xced/XCDYouTubeKit/issues/431 @@ -571,7 +571,7 @@ - (void) testAgeRestrictedVideo1WithNoJavaScriptPlayerURL_offline XCTAssertEqualObjects(error.localizedDescription, @"This video may be inappropriate for some users."); [expectation fulfill]; }]; - [self waitForExpectationsWithTimeout:5 handler:nil]; + [self waitForExpectationsWithTimeout:kDefaultNetworkTimeout handler:nil]; } @end diff --git a/XCDYouTubeKit Tests/XCDYouTubeVideoOperationTestCase.m b/XCDYouTubeKit Tests/XCDYouTubeVideoOperationTestCase.m index 1290f5b3..f15d2541 100644 --- a/XCDYouTubeKit Tests/XCDYouTubeVideoOperationTestCase.m +++ b/XCDYouTubeKit Tests/XCDYouTubeVideoOperationTestCase.m @@ -41,7 +41,7 @@ - (void) testStartingOnMainThread XCTAssertTrue([NSThread isMainThread]); [operation start]; - [self waitForExpectationsWithTimeout:5 handler:nil]; + [self waitForExpectationsWithTimeout:kDefaultNetworkTimeout handler:nil]; } - (void) testStartingOnBackgroundThread @@ -58,7 +58,7 @@ - (void) testStartingOnBackgroundThread XCTAssertFalse([NSThread isMainThread]); [operation start]; }); - [self waitForExpectationsWithTimeout:5 handler:nil]; + [self waitForExpectationsWithTimeout:kDefaultNetworkTimeout handler:nil]; } - (void) testCancelingOperationTwice diff --git a/XCDYouTubeKit Tests/XCDYouTubeVideoPlayerViewControllerTestCase.m b/XCDYouTubeKit Tests/XCDYouTubeVideoPlayerViewControllerTestCase.m index a919b14a..00318ab9 100644 --- a/XCDYouTubeKit Tests/XCDYouTubeVideoPlayerViewControllerTestCase.m +++ b/XCDYouTubeKit Tests/XCDYouTubeVideoPlayerViewControllerTestCase.m @@ -28,7 +28,7 @@ - (void) testAPIMisuseException XCTAssertThrowsSpecificNamed([[XCDYouTubeVideoPlayerViewController alloc] initWithVideoIdentifier:@"6v2L2UGZJAM"], NSException, NSGenericException); [expectation fulfill]; }]; - [self waitForExpectationsWithTimeout:5 handler:nil]; + [self waitForExpectationsWithTimeout:kDefaultNetworkTimeout handler:nil]; #endif } @@ -41,7 +41,7 @@ - (void) testVideoNotification XCTAssertNotNil(notification.userInfo[XCDYouTubeVideoUserInfoKey]); [expectation fulfill]; }]; - [self waitForExpectationsWithTimeout:5 handler:nil]; + [self waitForExpectationsWithTimeout:kDefaultNetworkTimeout handler:nil]; } - (void) testAsynchronousVideoNotification @@ -56,7 +56,7 @@ - (void) testAsynchronousVideoNotification XCTAssertNotNil(notification.userInfo[XCDYouTubeVideoUserInfoKey]); [expectation fulfill]; }]; - [self waitForExpectationsWithTimeout:5 handler:nil]; + [self waitForExpectationsWithTimeout:kDefaultNetworkTimeout handler:nil]; } - (void) testNoStreamAvailableErrorNotification @@ -73,7 +73,7 @@ - (void) testNoStreamAvailableErrorNotification XCTAssertEqual(error.code, XCDYouTubeErrorNoStreamAvailable); [expectation fulfill]; }]; - [self waitForExpectationsWithTimeout:5 handler:nil]; + [self waitForExpectationsWithTimeout:kDefaultNetworkTimeout handler:nil]; } - (void) testRestrictedPlaybackErrorNotification @@ -89,7 +89,7 @@ - (void) testRestrictedPlaybackErrorNotification XCTAssertEqual(error.code, XCDYouTubeErrorNoStreamAvailable); [expectation fulfill]; }]; - [self waitForExpectationsWithTimeout:5 handler:nil]; + [self waitForExpectationsWithTimeout:kDefaultNetworkTimeout handler:nil]; } - (void) testPresentInView diff --git a/XCDYouTubeKit Tests/XCDYouTubeVideoQueryOperationTestCase.m b/XCDYouTubeKit Tests/XCDYouTubeVideoQueryOperationTestCase.m index 492d5652..73775fa3 100644 --- a/XCDYouTubeKit Tests/XCDYouTubeVideoQueryOperationTestCase.m +++ b/XCDYouTubeKit Tests/XCDYouTubeVideoQueryOperationTestCase.m @@ -109,7 +109,7 @@ - (void) testCancelingOperationBeforeStart [operation start]; }]; - [self waitForExpectationsWithTimeout:5 handler:nil]; + [self waitForExpectationsWithTimeout:kDefaultNetworkTimeout handler:nil]; } - (void) testCancelingOperationAfterDelay diff --git a/XCDYouTubeKit/XCDYouTubeClient.m b/XCDYouTubeKit/XCDYouTubeClient.m index 3bd8e420..85ec2c4d 100644 --- a/XCDYouTubeKit/XCDYouTubeClient.m +++ b/XCDYouTubeKit/XCDYouTubeClient.m @@ -57,13 +57,17 @@ - (instancetype) initWithLanguageIdentifier:(NSString *)languageIdentifier @throw [NSException exceptionWithName:NSInvalidArgumentException reason:@"The `completionHandler` argument must not be nil." userInfo:nil]; XCDYouTubeVideoOperation *operation = [[XCDYouTubeVideoOperation alloc] initWithVideoIdentifier:videoIdentifier languageIdentifier:self.languageIdentifier cookies:cookies customPatterns:customPatterns]; - self.currentQueue = [NSOperationQueue currentQueue]; - + NSOperationQueue* callingQueue = [NSOperationQueue currentQueue]; + operation.completionBlock = ^{ - [self.currentQueue addOperationWithBlock:^{ + NSOperationQueue* callbackQueue = callingQueue ?: [NSOperationQueue mainQueue]; + [callbackQueue addOperationWithBlock:^{ #pragma clang diagnostic push #pragma clang diagnostic ignored "-Warc-retain-cycles" - if (operation.video || operation.error) // If both `video` and `error` are nil, then the operation was cancelled + if (operation.isCancelled) { + + } + else if (operation.video || operation.error) // If both `video` and `error` are nil, then the operation was cancelled { completionHandler(operation.video, operation.error); }