From c0c2b3674161f24cf24b330b455279cc8bbeb475 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ce=CC=81dric=20Luthi?= Date: Thu, 31 Jul 2014 11:36:38 +0200 Subject: [PATCH 01/57] Add unit test for geoblocked video --- .../testGeoblockedVideo.json | 55 +++++++++++++++++++ .../XCDYouTubeClientTestCase.m | 13 +++++ 2 files changed, 68 insertions(+) create mode 100644 XCDYouTubeKit Tests/Cassettes/XCDYouTubeClientTestCase/testGeoblockedVideo.json diff --git a/XCDYouTubeKit Tests/Cassettes/XCDYouTubeClientTestCase/testGeoblockedVideo.json b/XCDYouTubeKit Tests/Cassettes/XCDYouTubeClientTestCase/testGeoblockedVideo.json new file mode 100644 index 00000000..228cda06 --- /dev/null +++ b/XCDYouTubeKit Tests/Cassettes/XCDYouTubeClientTestCase/testGeoblockedVideo.json @@ -0,0 +1,55 @@ +[ + { + "body" : "eventid=JQvaU737NYOiyQPdyIGoCQ&errorcode=150&reason=The+uploader+has+not+made+this+video+available+in+your+country.&c=WEB&status=fail&errordetail=0", + "headers" : { + "X-Content-Type-Options" : "nosniff", + "Server" : "gwiseguy\/2.0", + "Content-Type" : "application\/x-www-form-urlencoded", + "Transfer-Encoding" : "Identity", + "Date" : "Thu, 31 Jul 2014 09:23:49 GMT", + "X-Frame-Options" : "SAMEORIGIN", + "X-XSS-Protection" : "1; mode=block; report=https:\/\/www.google.com\/appserve\/security-bugs\/log\/youtube", + "Expires" : "Tue, 27 Apr 1971 19:44:06 EST", + "Cache-Control" : "no-store" + }, + "method" : "GET", + "status" : 200, + "uri" : "https:\/\/www.youtube.com\/get_video_info?el=vevo&hl=en&ps=default&video_id=j4GAs9TJVjM" + }, + { + "body" : "csi_page_type=embed&errordetail=0&eventid=JQvaU-uvKMTrywPSoICIBQ&atc=a%3D3%26b%3DaBEhJaqb6mNu02iPcUeb__SqKxE%26c%3D1406798629%26d%3D1%26e%3Dj4GAs9TJVjM%26c3a%3D17%26hh%3DiBftfLPb8PBrBhsY-FbylA5Gy28&status=fail&reason=The+uploader+has+not+made+this+video+available+in+your+country.&enablecsi=1&errorcode=150&c=WEB", + "headers" : { + "Server" : "gwiseguy\/2.0", + "Set-Cookie" : "YSC=4B4Cp2LLsAA; path=\/; domain=.youtube.com; httponly", + "Content-Type" : "application\/x-www-form-urlencoded", + "X-Content-Type-Options" : "nosniff", + "Transfer-Encoding" : "Identity", + "P3P" : "CP=\"This is not a P3P policy! See http:\/\/support.google.com\/accounts\/bin\/answer.py?answer=151657&hl=en for more info.\"", + "Date" : "Thu, 31 Jul 2014 09:23:49 GMT", + "X-Frame-Options" : "SAMEORIGIN", + "X-XSS-Protection" : "1; mode=block; report=https:\/\/www.google.com\/appserve\/security-bugs\/log\/youtube", + "Cache-Control" : "no-store", + "Expires" : "Tue, 27 Apr 1971 19:44:06 EST" + }, + "method" : "GET", + "status" : 200, + "uri" : "https:\/\/www.youtube.com\/get_video_info?el=embedded&hl=en&ps=default&video_id=j4GAs9TJVjM" + }, + { + "body" : "enablecsi=1&reason=The+uploader+has+not+made+this+video+available+in+your+country.&eventid=JQvaU_b_LZLwygP-vIHgDQ&c=WEB&errorcode=150&csi_page_type=watch%2Cwatch7&errordetail=0&status=fail", + "headers" : { + "X-Content-Type-Options" : "nosniff", + "Server" : "gwiseguy\/2.0", + "Content-Type" : "application\/x-www-form-urlencoded", + "Transfer-Encoding" : "Identity", + "Date" : "Thu, 31 Jul 2014 09:23:49 GMT", + "X-Frame-Options" : "SAMEORIGIN", + "X-XSS-Protection" : "1; mode=block; report=https:\/\/www.google.com\/appserve\/security-bugs\/log\/youtube", + "Expires" : "Tue, 27 Apr 1971 19:44:06 EST", + "Cache-Control" : "no-store" + }, + "method" : "GET", + "status" : 200, + "uri" : "https:\/\/www.youtube.com\/get_video_info?el=detailpage&hl=en&ps=default&video_id=j4GAs9TJVjM" + } +] \ No newline at end of file diff --git a/XCDYouTubeKit Tests/XCDYouTubeClientTestCase.m b/XCDYouTubeKit Tests/XCDYouTubeClientTestCase.m index d5dbbb83..8ab64ef2 100644 --- a/XCDYouTubeKit Tests/XCDYouTubeClientTestCase.m +++ b/XCDYouTubeKit Tests/XCDYouTubeClientTestCase.m @@ -118,6 +118,19 @@ - (void) testRemovedVideo XCTAssertTrue([monitor waitWithTimeout:10]); } +- (void) testGeoblockedVideo +{ + TRVSMonitor *monitor = [TRVSMonitor monitor]; + [[XCDYouTubeClient defaultClient] getVideoWithIdentifier:@"j4GAs9TJVjM" completionHandler:^(XCDYouTubeVideo *video, NSError *error) { + XCTAssertNil(video); + XCTAssertEqualObjects(error.domain, XCDYouTubeVideoErrorDomain); + XCTAssertEqual(error.code, XCDYouTubeErrorRestrictedPlayback); + XCTAssertEqualObjects(error.localizedDescription, @"The uploader has not made this video available in your country."); + [monitor signal]; + }]; + XCTAssertTrue([monitor waitWithTimeout:10]); +} + - (void) testInvalidVideoIdentifier { TRVSMonitor *monitor = [TRVSMonitor monitor]; From e26ac86261c3e86427c8c7cefa265b9a945b0dc8 Mon Sep 17 00:00:00 2001 From: Wayne Date: Tue, 29 Jul 2014 19:32:12 -0500 Subject: [PATCH 02/57] Fix "Create Coveralls Script" build phase Enclose script in quotes in order to fix error when the $SRCROOT path contains spaces. --- XCDYouTubeKit.xcodeproj/project.pbxproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/XCDYouTubeKit.xcodeproj/project.pbxproj b/XCDYouTubeKit.xcodeproj/project.pbxproj index 60d81cce..f09acaa2 100644 --- a/XCDYouTubeKit.xcodeproj/project.pbxproj +++ b/XCDYouTubeKit.xcodeproj/project.pbxproj @@ -500,7 +500,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "${SRCROOT}/Scripts/create-coveralls-script.sh"; + shellScript = "\"${SRCROOT}/Scripts/create-coveralls-script.sh\" "; }; /* End PBXShellScriptBuildPhase section */ From a9a9ed9f9d5f78c558afc067cf5fc019399c313c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ce=CC=81dric=20Luthi?= Date: Wed, 20 Aug 2014 14:34:28 +0200 Subject: [PATCH 03/57] Move blocks' opening curly bracket on a new line Setting breakpoints is much easier this way --- XCDYouTubeKit Demo/OS X Demo/AppDelegate.m | 3 +- XCDYouTubeKit Demo/iOS Demo/AppDelegate.m | 3 +- .../iOS Demo/DemoAsynchronousViewController.m | 3 +- .../iOS Demo/DemoThumbnailViewController.m | 3 +- .../XCDYouTubeClientTestCase.m | 45 ++++++++++++------- .../XCDYouTubeProtectedVideosTestCase.m | 33 +++++++++----- ...YouTubeVideoPlayerViewControllerTestCase.m | 12 +++-- XCDYouTubeKit/XCDYouTubePlayerScript.m | 3 +- .../XCDYouTubeVideoPlayerViewController.m | 3 +- XCDYouTubeKit/XCDYouTubeVideoWebpage.m | 6 ++- 10 files changed, 76 insertions(+), 38 deletions(-) diff --git a/XCDYouTubeKit Demo/OS X Demo/AppDelegate.m b/XCDYouTubeKit Demo/OS X Demo/AppDelegate.m index c41dd127..37d0e87b 100644 --- a/XCDYouTubeKit Demo/OS X Demo/AppDelegate.m +++ b/XCDYouTubeKit Demo/OS X Demo/AppDelegate.m @@ -16,7 +16,8 @@ - (IBAction) playVideo:(id)sender self.playerView.player = nil; [self.progressIndicator startAnimation:sender]; - [[XCDYouTubeClient defaultClient] getVideoWithIdentifier:[sender stringValue] completionHandler:^(XCDYouTubeVideo *video, NSError *error) { + [[XCDYouTubeClient defaultClient] getVideoWithIdentifier:[sender stringValue] completionHandler:^(XCDYouTubeVideo *video, NSError *error) + { [self.progressIndicator stopAnimation:sender]; if (video) diff --git a/XCDYouTubeKit Demo/iOS Demo/AppDelegate.m b/XCDYouTubeKit Demo/iOS Demo/AppDelegate.m index 2c134a1f..414460ed 100644 --- a/XCDYouTubeKit Demo/iOS Demo/AppDelegate.m +++ b/XCDYouTubeKit Demo/iOS Demo/AppDelegate.m @@ -45,7 +45,8 @@ - (void) videoPlayerViewControllerDidReceiveVideo:(NSNotification *)notification if (title) [MPNowPlayingInfoCenter defaultCenter].nowPlayingInfo = @{ MPMediaItemPropertyTitle: title }; - [NSURLConnection sendAsynchronousRequest:[NSURLRequest requestWithURL:video.mediumThumbnailURL] queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) { + [NSURLConnection sendAsynchronousRequest:[NSURLRequest requestWithURL:video.mediumThumbnailURL] queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) + { if (data) { UIImage *image = [UIImage imageWithData:data]; diff --git a/XCDYouTubeKit Demo/iOS Demo/DemoAsynchronousViewController.m b/XCDYouTubeKit Demo/iOS Demo/DemoAsynchronousViewController.m index 07a2be16..fa439639 100644 --- a/XCDYouTubeKit Demo/iOS Demo/DemoAsynchronousViewController.m +++ b/XCDYouTubeKit Demo/iOS Demo/DemoAsynchronousViewController.m @@ -14,7 +14,8 @@ - (IBAction) play:(id)sender // https://developers.google.com/youtube/2.0/developers_guide_protocol_video_feeds#Standard_feeds NSURL *url = [NSURL URLWithString:@"https://gdata.youtube.com/feeds/api/standardfeeds/most_popular?v=2&alt=json&time=today&max-results=1"]; - [NSURLConnection sendAsynchronousRequest:[NSURLRequest requestWithURL:url] queue:[NSOperationQueue new] completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) { + [NSURLConnection sendAsynchronousRequest:[NSURLRequest requestWithURL:url] queue:[NSOperationQueue new] completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) + { id json = [NSJSONSerialization JSONObjectWithData:data ?: [NSData new] options:0 error:NULL]; NSString *videoIdentifier = [[[json valueForKeyPath:@"feed.entry.media$group.yt$videoid.$t"] lastObject] description]; videoPlayerViewController.videoIdentifier = videoIdentifier; diff --git a/XCDYouTubeKit Demo/iOS Demo/DemoThumbnailViewController.m b/XCDYouTubeKit Demo/iOS Demo/DemoThumbnailViewController.m index d9967ed4..dad824f5 100644 --- a/XCDYouTubeKit Demo/iOS Demo/DemoThumbnailViewController.m +++ b/XCDYouTubeKit Demo/iOS Demo/DemoThumbnailViewController.m @@ -36,7 +36,8 @@ - (void) videoPlayerViewControllerDidReceiveVideo:(NSNotification *)notification self.titleLabel.text = video.title; NSURL *thumbnailURL = video.mediumThumbnailURL ?: video.smallThumbnailURL; - [NSURLConnection sendAsynchronousRequest:[NSURLRequest requestWithURL:thumbnailURL] queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) { + [NSURLConnection sendAsynchronousRequest:[NSURLRequest requestWithURL:thumbnailURL] queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) + { self.thumbnailImageView.image = [UIImage imageWithData:data]; [self.actionButton setTitle:NSLocalizedString(@"Play Video", nil) forState:UIControlStateNormal]; diff --git a/XCDYouTubeKit Tests/XCDYouTubeClientTestCase.m b/XCDYouTubeKit Tests/XCDYouTubeClientTestCase.m index 8ab64ef2..ffd82bd1 100644 --- a/XCDYouTubeKit Tests/XCDYouTubeClientTestCase.m +++ b/XCDYouTubeKit Tests/XCDYouTubeClientTestCase.m @@ -14,7 +14,8 @@ @implementation XCDYouTubeClientTestCase - (void) testThatVideoIsAvailalbeOnDetailPageEventLabel { TRVSMonitor *monitor = [TRVSMonitor monitor]; - [[XCDYouTubeClient defaultClient] getVideoWithIdentifier:@"dQw4w9WgXcQ" completionHandler:^(XCDYouTubeVideo *video, NSError *error) { + [[XCDYouTubeClient defaultClient] getVideoWithIdentifier:@"dQw4w9WgXcQ" completionHandler:^(XCDYouTubeVideo *video, NSError *error) + { XCTAssertNil(error); XCTAssertNotNil(video); [monitor signal]; @@ -25,7 +26,8 @@ - (void) testThatVideoIsAvailalbeOnDetailPageEventLabel - (void) testThatVideoHasMetadata { TRVSMonitor *monitor = [TRVSMonitor monitor]; - [[XCDYouTubeClient defaultClient] getVideoWithIdentifier:@"9TTioMbNT9I" completionHandler:^(XCDYouTubeVideo *video, NSError *error) { + [[XCDYouTubeClient defaultClient] getVideoWithIdentifier:@"9TTioMbNT9I" completionHandler:^(XCDYouTubeVideo *video, NSError *error) + { XCTAssertNil(error); XCTAssertEqualObjects(video.identifier, @"9TTioMbNT9I"); XCTAssertEqualObjects(video.title, @"Super Mario Bros Theme Song on Wine Glasses and a Frying Pan (슈퍼 마리오 브라더스 - スーパーマリオブラザーズ - 超級瑪莉)"); @@ -42,7 +44,8 @@ - (void) testThatVideoHasMetadata - (void) testMobileRestrictedVideo { TRVSMonitor *monitor = [TRVSMonitor monitor]; - [[XCDYouTubeClient defaultClient] getVideoWithIdentifier:@"JHaA9bKi-xs" completionHandler:^(XCDYouTubeVideo *video, NSError *error) { + [[XCDYouTubeClient defaultClient] getVideoWithIdentifier:@"JHaA9bKi-xs" completionHandler:^(XCDYouTubeVideo *video, NSError *error) + { XCTAssertNil(error); XCTAssertNotNil(video.title); XCTAssertNotNil(video.smallThumbnailURL); @@ -61,7 +64,8 @@ - (void) testMobileRestrictedVideo - (void) testLiveVideo { TRVSMonitor *monitor = [TRVSMonitor monitor]; - [[XCDYouTubeClient defaultClient] getVideoWithIdentifier:@"xrM34fdmloc" completionHandler:^(XCDYouTubeVideo *video, NSError *error) { + [[XCDYouTubeClient defaultClient] getVideoWithIdentifier:@"xrM34fdmloc" completionHandler:^(XCDYouTubeVideo *video, NSError *error) + { XCTAssertNil(error); XCTAssertNotNil(video.title); XCTAssertNotNil(video.smallThumbnailURL); @@ -78,7 +82,8 @@ - (void) testLiveVideo - (void) testDVRVideo { TRVSMonitor *monitor = [TRVSMonitor monitor]; - [[XCDYouTubeClient defaultClient] getVideoWithIdentifier:@"H7iQ4sAf0OE" completionHandler:^(XCDYouTubeVideo *video, NSError *error) { + [[XCDYouTubeClient defaultClient] getVideoWithIdentifier:@"H7iQ4sAf0OE" completionHandler:^(XCDYouTubeVideo *video, NSError *error) + { XCTAssertNil(error); XCTAssertNotNil(video.title); XCTAssertNotNil(video.smallThumbnailURL); @@ -95,7 +100,8 @@ - (void) testDVRVideo - (void) testRestrictedVideo { TRVSMonitor *monitor = [TRVSMonitor monitor]; - [[XCDYouTubeClient defaultClient] getVideoWithIdentifier:@"1kIsylLeHHU" completionHandler:^(XCDYouTubeVideo *video, NSError *error) { + [[XCDYouTubeClient defaultClient] getVideoWithIdentifier:@"1kIsylLeHHU" completionHandler:^(XCDYouTubeVideo *video, NSError *error) + { XCTAssertNil(video); XCTAssertEqualObjects(error.domain, XCDYouTubeVideoErrorDomain); XCTAssertEqual(error.code, XCDYouTubeErrorRestrictedPlayback); @@ -108,7 +114,8 @@ - (void) testRestrictedVideo - (void) testRemovedVideo { TRVSMonitor *monitor = [TRVSMonitor monitor]; - [[XCDYouTubeClient defaultClient] getVideoWithIdentifier:@"BXnA9FjvLSU" completionHandler:^(XCDYouTubeVideo *video, NSError *error) { + [[XCDYouTubeClient defaultClient] getVideoWithIdentifier:@"BXnA9FjvLSU" completionHandler:^(XCDYouTubeVideo *video, NSError *error) + { XCTAssertNil(video); XCTAssertEqualObjects(error.domain, XCDYouTubeVideoErrorDomain); XCTAssertEqual(error.code, XCDYouTubeErrorRemovedVideo); @@ -134,7 +141,8 @@ - (void) testGeoblockedVideo - (void) testInvalidVideoIdentifier { TRVSMonitor *monitor = [TRVSMonitor monitor]; - [[XCDYouTubeClient defaultClient] getVideoWithIdentifier:@"tooShort" completionHandler:^(XCDYouTubeVideo *video, NSError *error) { + [[XCDYouTubeClient defaultClient] getVideoWithIdentifier:@"tooShort" completionHandler:^(XCDYouTubeVideo *video, NSError *error) + { XCTAssertNil(video); XCTAssertEqualObjects(error.domain, XCDYouTubeVideoErrorDomain); XCTAssertEqual(error.code, XCDYouTubeErrorInvalidVideoIdentifier); @@ -147,7 +155,8 @@ - (void) testInvalidVideoIdentifier - (void) testNonExistentVideoIdentifier { TRVSMonitor *monitor = [TRVSMonitor monitor]; - [[XCDYouTubeClient defaultClient] getVideoWithIdentifier:@"xxxxxxxxxxx" completionHandler:^(XCDYouTubeVideo *video, NSError *error) { + [[XCDYouTubeClient defaultClient] getVideoWithIdentifier:@"xxxxxxxxxxx" completionHandler:^(XCDYouTubeVideo *video, NSError *error) + { XCTAssertNil(video); XCTAssertEqualObjects(error.domain, XCDYouTubeVideoErrorDomain); XCTAssertEqual(error.code, XCDYouTubeErrorRemovedVideo); @@ -160,7 +169,8 @@ - (void) testNonExistentVideoIdentifier - (void) testFrenchClient { TRVSMonitor *monitor = [TRVSMonitor monitor]; - [[[XCDYouTubeClient alloc] initWithLanguageIdentifier:@"fr"] getVideoWithIdentifier:@"xxxxxxxxxxx" completionHandler:^(XCDYouTubeVideo *video, NSError *error) { + [[[XCDYouTubeClient alloc] initWithLanguageIdentifier:@"fr"] getVideoWithIdentifier:@"xxxxxxxxxxx" completionHandler:^(XCDYouTubeVideo *video, NSError *error) + { XCTAssertNil(video); XCTAssertEqualObjects(error.domain, XCDYouTubeVideoErrorDomain); XCTAssertEqual(error.code, XCDYouTubeErrorRemovedVideo); @@ -173,7 +183,8 @@ - (void) testFrenchClient - (void) testNilVideoIdentifier { TRVSMonitor *monitor = [TRVSMonitor monitor]; - [[XCDYouTubeClient defaultClient] getVideoWithIdentifier:nil completionHandler:^(XCDYouTubeVideo *video, NSError *error) { + [[XCDYouTubeClient defaultClient] getVideoWithIdentifier:nil completionHandler:^(XCDYouTubeVideo *video, NSError *error) + { XCTAssertNil(video); XCTAssertEqualObjects(error.domain, XCDYouTubeVideoErrorDomain); XCTAssertEqual(error.code, XCDYouTubeErrorInvalidVideoIdentifier); @@ -186,7 +197,8 @@ - (void) testNilVideoIdentifier - (void) testSpaceVideoIdentifier { TRVSMonitor *monitor = [TRVSMonitor monitor]; - [[XCDYouTubeClient defaultClient] getVideoWithIdentifier:@" " completionHandler:^(XCDYouTubeVideo *video, NSError *error) { + [[XCDYouTubeClient defaultClient] getVideoWithIdentifier:@" " completionHandler:^(XCDYouTubeVideo *video, NSError *error) + { XCTAssertNil(video); XCTAssertEqualObjects(error.domain, XCDYouTubeVideoErrorDomain); XCTAssertEqual(error.code, XCDYouTubeErrorInvalidVideoIdentifier); @@ -199,7 +211,8 @@ - (void) testSpaceVideoIdentifier - (void) testConnectionError { TRVSMonitor *monitor = [TRVSMonitor monitor]; - [[XCDYouTubeClient defaultClient] getVideoWithIdentifier:@"EdeVaT-zZt4" completionHandler:^(XCDYouTubeVideo *video, NSError *error) { + [[XCDYouTubeClient defaultClient] getVideoWithIdentifier:@"EdeVaT-zZt4" completionHandler:^(XCDYouTubeVideo *video, NSError *error) + { XCTAssertNil(video); XCTAssertEqualObjects(error.domain, XCDYouTubeVideoErrorDomain); XCTAssertEqual(error.code, XCDYouTubeErrorNetwork); @@ -217,7 +230,8 @@ - (void) testUsingClientOnNonMainThread TRVSMonitor *monitor = [TRVSMonitor monitor]; dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ XCTAssertFalse([NSThread isMainThread]); - [[XCDYouTubeClient defaultClient] getVideoWithIdentifier:@"EdeVaT-zZt4" completionHandler:^(XCDYouTubeVideo *video, NSError *error) { + [[XCDYouTubeClient defaultClient] getVideoWithIdentifier:@"EdeVaT-zZt4" completionHandler:^(XCDYouTubeVideo *video, NSError *error) + { XCTAssertTrue([NSThread isMainThread]); [monitor signal]; }]; @@ -228,7 +242,8 @@ - (void) testUsingClientOnNonMainThread - (void) testCancelingOperation { TRVSMonitor *monitor = [TRVSMonitor monitor]; - id operation = [[XCDYouTubeClient defaultClient] getVideoWithIdentifier:@"EdeVaT-zZt4" completionHandler:^(XCDYouTubeVideo *video, NSError *error) { + id operation = [[XCDYouTubeClient defaultClient] getVideoWithIdentifier:@"EdeVaT-zZt4" completionHandler:^(XCDYouTubeVideo *video, NSError *error) + { XCTFail(); }]; [operation cancel]; diff --git a/XCDYouTubeKit Tests/XCDYouTubeProtectedVideosTestCase.m b/XCDYouTubeKit Tests/XCDYouTubeProtectedVideosTestCase.m index 42aa5377..7cf3b56f 100644 --- a/XCDYouTubeKit Tests/XCDYouTubeProtectedVideosTestCase.m +++ b/XCDYouTubeKit Tests/XCDYouTubeProtectedVideosTestCase.m @@ -34,7 +34,8 @@ - (void) tearDown - (void) testProtectedVEVOVideo { TRVSMonitor *monitor = [TRVSMonitor monitor]; - [[XCDYouTubeClient defaultClient] getVideoWithIdentifier:@"rId6PKlDXeU" completionHandler:^(XCDYouTubeVideo *video, NSError *error) { + [[XCDYouTubeClient defaultClient] getVideoWithIdentifier:@"rId6PKlDXeU" completionHandler:^(XCDYouTubeVideo *video, NSError *error) + { XCTAssertNil(error); XCTAssertNotNil(video.title); XCTAssertNotNil(video.smallThumbnailURL); @@ -42,7 +43,8 @@ - (void) testProtectedVEVOVideo XCTAssertNotNil(video.largeThumbnailURL); XCTAssertTrue(video.streamURLs.count > 0); XCTAssertTrue(video.duration > 0); - [video.streamURLs enumerateKeysAndObjectsUsingBlock:^(NSNumber *key, NSURL *streamURL, BOOL *stop) { + [video.streamURLs enumerateKeysAndObjectsUsingBlock:^(NSNumber *key, NSURL *streamURL, BOOL *stop) + { XCTAssertTrue([streamURL.query rangeOfString:@"signature="].location != NSNotFound); }]; [monitor signal]; @@ -53,7 +55,8 @@ - (void) testProtectedVEVOVideo - (void) testProtectedVideoWithDollarSignature { TRVSMonitor *monitor = [TRVSMonitor monitor]; - [[XCDYouTubeClient defaultClient] getVideoWithIdentifier:@"Pgum6OT_VH8" completionHandler:^(XCDYouTubeVideo *video, NSError *error) { + [[XCDYouTubeClient defaultClient] getVideoWithIdentifier:@"Pgum6OT_VH8" completionHandler:^(XCDYouTubeVideo *video, NSError *error) + { XCTAssertNil(error); XCTAssertNotNil(video.title); XCTAssertNotNil(video.smallThumbnailURL); @@ -61,7 +64,8 @@ - (void) testProtectedVideoWithDollarSignature XCTAssertNil(video.largeThumbnailURL); XCTAssertTrue(video.streamURLs.count > 0); XCTAssertTrue(video.duration > 0); - [video.streamURLs enumerateKeysAndObjectsUsingBlock:^(NSNumber *key, NSURL *streamURL, BOOL *stop) { + [video.streamURLs enumerateKeysAndObjectsUsingBlock:^(NSNumber *key, NSURL *streamURL, BOOL *stop) + { XCTAssertTrue([streamURL.query rangeOfString:@"signature="].location != NSNotFound); }]; [monitor signal]; @@ -72,7 +76,8 @@ - (void) testProtectedVideoWithDollarSignature - (void) testProtectedVideoWithJavaScriptFunctionsInVarScope { TRVSMonitor *monitor = [TRVSMonitor monitor]; - [[XCDYouTubeClient defaultClient] getVideoWithIdentifier:@"Ntn1-SocNiY" completionHandler:^(XCDYouTubeVideo *video, NSError *error) { + [[XCDYouTubeClient defaultClient] getVideoWithIdentifier:@"Ntn1-SocNiY" completionHandler:^(XCDYouTubeVideo *video, NSError *error) + { XCTAssertNil(error); XCTAssertNotNil(video.title); XCTAssertNotNil(video.smallThumbnailURL); @@ -80,7 +85,8 @@ - (void) testProtectedVideoWithJavaScriptFunctionsInVarScope XCTAssertNotNil(video.largeThumbnailURL); XCTAssertTrue(video.streamURLs.count > 0); XCTAssertTrue(video.duration > 0); - [video.streamURLs enumerateKeysAndObjectsUsingBlock:^(NSNumber *key, NSURL *streamURL, BOOL *stop) { + [video.streamURLs enumerateKeysAndObjectsUsingBlock:^(NSNumber *key, NSURL *streamURL, BOOL *stop) + { XCTAssertTrue([streamURL.query rangeOfString:@"signature="].location != NSNotFound); }]; [monitor signal]; @@ -94,7 +100,8 @@ - (void) testProtectedVideoWithJavaScriptFunctionsInVarScope - (void) testProtectedVideoWithWebPageConnectionError { TRVSMonitor *monitor = [TRVSMonitor monitor]; - [[XCDYouTubeClient defaultClient] getVideoWithIdentifier:@"Pgum6OT_VH8" completionHandler:^(XCDYouTubeVideo *video, NSError *error) { + [[XCDYouTubeClient defaultClient] getVideoWithIdentifier:@"Pgum6OT_VH8" completionHandler:^(XCDYouTubeVideo *video, NSError *error) + { XCTAssertNil(video); XCTAssertEqualObjects(error.domain, XCDYouTubeVideoErrorDomain); XCTAssertEqual(error.code, XCDYouTubeErrorRestrictedPlayback); @@ -108,7 +115,8 @@ - (void) testProtectedVideoWithWebPageConnectionError - (void) testProtectedVideoWithPlayerScriptConnectionError { TRVSMonitor *monitor = [TRVSMonitor monitor]; - [[XCDYouTubeClient defaultClient] getVideoWithIdentifier:@"Pgum6OT_VH8" completionHandler:^(XCDYouTubeVideo *video, NSError *error) { + [[XCDYouTubeClient defaultClient] getVideoWithIdentifier:@"Pgum6OT_VH8" completionHandler:^(XCDYouTubeVideo *video, NSError *error) + { XCTAssertNil(video); XCTAssertEqualObjects(error.domain, XCDYouTubeVideoErrorDomain); XCTAssertEqual(error.code, XCDYouTubeErrorRestrictedPlayback); @@ -122,7 +130,8 @@ - (void) testProtectedVideoWithPlayerScriptConnectionError - (void) testProtectedVideoWithoutSignatureFunction { TRVSMonitor *monitor = [TRVSMonitor monitor]; - [[XCDYouTubeClient defaultClient] getVideoWithIdentifier:@"Pgum6OT_VH8" completionHandler:^(XCDYouTubeVideo *video, NSError *error) { + [[XCDYouTubeClient defaultClient] getVideoWithIdentifier:@"Pgum6OT_VH8" completionHandler:^(XCDYouTubeVideo *video, NSError *error) + { XCTAssertNil(video); XCTAssertEqualObjects(error.domain, XCDYouTubeVideoErrorDomain); XCTAssertEqual(error.code, XCDYouTubeErrorRestrictedPlayback); @@ -136,7 +145,8 @@ - (void) testProtectedVideoWithoutSignatureFunction - (void) testProtectedVideoWithBrokenSignatureFunction { TRVSMonitor *monitor = [TRVSMonitor monitor]; - [[XCDYouTubeClient defaultClient] getVideoWithIdentifier:@"Pgum6OT_VH8" completionHandler:^(XCDYouTubeVideo *video, NSError *error) { + [[XCDYouTubeClient defaultClient] getVideoWithIdentifier:@"Pgum6OT_VH8" completionHandler:^(XCDYouTubeVideo *video, NSError *error) + { XCTAssertNil(video); XCTAssertEqualObjects(error.domain, XCDYouTubeVideoErrorDomain); XCTAssertEqual(error.code, XCDYouTubeErrorRestrictedPlayback); @@ -150,7 +160,8 @@ - (void) testProtectedVideoWithBrokenSignatureFunction - (void) testProtectedVideoWithoutJavaScriptPlayerURL { TRVSMonitor *monitor = [TRVSMonitor monitor]; - [[XCDYouTubeClient defaultClient] getVideoWithIdentifier:@"Pgum6OT_VH8" completionHandler:^(XCDYouTubeVideo *video, NSError *error) { + [[XCDYouTubeClient defaultClient] getVideoWithIdentifier:@"Pgum6OT_VH8" completionHandler:^(XCDYouTubeVideo *video, NSError *error) + { XCTAssertNil(video); XCTAssertEqualObjects(error.domain, XCDYouTubeVideoErrorDomain); XCTAssertEqual(error.code, XCDYouTubeErrorRestrictedPlayback); diff --git a/XCDYouTubeKit Tests/XCDYouTubeVideoPlayerViewControllerTestCase.m b/XCDYouTubeKit Tests/XCDYouTubeVideoPlayerViewControllerTestCase.m index ccd7c230..cc59f466 100644 --- a/XCDYouTubeKit Tests/XCDYouTubeVideoPlayerViewControllerTestCase.m +++ b/XCDYouTubeKit Tests/XCDYouTubeVideoPlayerViewControllerTestCase.m @@ -21,7 +21,8 @@ - (void) testVideoNotification { TRVSMonitor *monitor = [TRVSMonitor monitor]; XCDYouTubeVideoPlayerViewController *videoPlayerViewController = [[XCDYouTubeVideoPlayerViewController alloc] initWithVideoIdentifier:@"EdeVaT-zZt4"]; - [[NSNotificationCenter defaultCenter] addObserverForName:XCDYouTubeVideoPlayerViewControllerDidReceiveVideoNotification object:videoPlayerViewController queue:nil usingBlock:^(NSNotification *notification) { + [[NSNotificationCenter defaultCenter] addObserverForName:XCDYouTubeVideoPlayerViewControllerDidReceiveVideoNotification object:videoPlayerViewController queue:nil usingBlock:^(NSNotification *notification) + { XCTAssertNotNil(notification.userInfo[XCDYouTubeVideoUserInfoKey]); [monitor signal]; }]; @@ -35,7 +36,8 @@ - (void) testAsynchronousVideoNotification [[NSOperationQueue mainQueue] addOperationWithBlock:^{ videoPlayerViewController.videoIdentifier = @"EdeVaT-zZt4"; }]; - [[NSNotificationCenter defaultCenter] addObserverForName:XCDYouTubeVideoPlayerViewControllerDidReceiveVideoNotification object:videoPlayerViewController queue:nil usingBlock:^(NSNotification *notification) { + [[NSNotificationCenter defaultCenter] addObserverForName:XCDYouTubeVideoPlayerViewControllerDidReceiveVideoNotification object:videoPlayerViewController queue:nil usingBlock:^(NSNotification *notification) + { XCTAssertNotNil(notification.userInfo[XCDYouTubeVideoUserInfoKey]); [monitor signal]; }]; @@ -47,7 +49,8 @@ - (void) testNoStreamAvailableErrorNotification TRVSMonitor *monitor = [TRVSMonitor monitor]; XCDYouTubeVideoPlayerViewController *videoPlayerViewController = [[XCDYouTubeVideoPlayerViewController alloc] initWithVideoIdentifier:@"EdeVaT-zZt4"]; videoPlayerViewController.preferredVideoQualities = @[]; - [[NSNotificationCenter defaultCenter] addObserverForName:MPMoviePlayerPlaybackDidFinishNotification object:videoPlayerViewController.moviePlayer queue:nil usingBlock:^(NSNotification *notification) { + [[NSNotificationCenter defaultCenter] addObserverForName:MPMoviePlayerPlaybackDidFinishNotification object:videoPlayerViewController.moviePlayer queue:nil usingBlock:^(NSNotification *notification) + { NSError *error = notification.userInfo[XCDMoviePlayerPlaybackDidFinishErrorUserInfoKey]; MPMovieFinishReason finishReason = [notification.userInfo[MPMoviePlayerPlaybackDidFinishReasonUserInfoKey] integerValue]; XCTAssertEqual(finishReason, MPMovieFinishReasonPlaybackError); @@ -62,7 +65,8 @@ - (void) testRestrictedPlaybackErrorNotification { TRVSMonitor *monitor = [TRVSMonitor monitor]; XCDYouTubeVideoPlayerViewController *videoPlayerViewController = [[XCDYouTubeVideoPlayerViewController alloc] initWithVideoIdentifier:@"1kIsylLeHHU"]; - [[NSNotificationCenter defaultCenter] addObserverForName:MPMoviePlayerPlaybackDidFinishNotification object:videoPlayerViewController.moviePlayer queue:nil usingBlock:^(NSNotification *notification) { + [[NSNotificationCenter defaultCenter] addObserverForName:MPMoviePlayerPlaybackDidFinishNotification object:videoPlayerViewController.moviePlayer queue:nil usingBlock:^(NSNotification *notification) + { NSError *error = notification.userInfo[XCDMoviePlayerPlaybackDidFinishErrorUserInfoKey]; MPMovieFinishReason finishReason = [notification.userInfo[MPMoviePlayerPlaybackDidFinishReasonUserInfoKey] integerValue]; XCTAssertEqual(finishReason, MPMovieFinishReasonPlaybackError); diff --git a/XCDYouTubeKit/XCDYouTubePlayerScript.m b/XCDYouTubeKit/XCDYouTubePlayerScript.m index e9d18fe6..9c3d635b 100644 --- a/XCDYouTubeKit/XCDYouTubePlayerScript.m +++ b/XCDYouTubeKit/XCDYouTubePlayerScript.m @@ -33,7 +33,8 @@ - (instancetype) initWithString:(NSString *)string __block NSString *signatureFunctionName = nil; NSRegularExpression *signatureRegularExpression = [NSRegularExpression regularExpressionWithPattern:@"signature\\s*=\\s*([^\\(]+)" options:NSRegularExpressionCaseInsensitive error:NULL]; - [signatureRegularExpression enumerateMatchesInString:script options:(NSMatchingOptions)0 range:NSMakeRange(0, script.length) usingBlock:^(NSTextCheckingResult *result, NSMatchingFlags flags, BOOL *stop) { + [signatureRegularExpression enumerateMatchesInString:script options:(NSMatchingOptions)0 range:NSMakeRange(0, script.length) usingBlock:^(NSTextCheckingResult *result, NSMatchingFlags flags, BOOL *stop) + { signatureFunctionName = [script substringWithRange:[result rangeAtIndex:1]]; *stop = YES; }]; diff --git a/XCDYouTubeKit/XCDYouTubeVideoPlayerViewController.m b/XCDYouTubeKit/XCDYouTubeVideoPlayerViewController.m index e3068515..d7889885 100644 --- a/XCDYouTubeKit/XCDYouTubeVideoPlayerViewController.m +++ b/XCDYouTubeKit/XCDYouTubeVideoPlayerViewController.m @@ -93,7 +93,8 @@ - (void) setVideoIdentifier:(NSString *)videoIdentifier _videoIdentifier = [videoIdentifier copy]; [self.videoOperation cancel]; - self.videoOperation = [[XCDYouTubeClient defaultClient] getVideoWithIdentifier:videoIdentifier completionHandler:^(XCDYouTubeVideo *video, NSError *error) { + self.videoOperation = [[XCDYouTubeClient defaultClient] getVideoWithIdentifier:videoIdentifier completionHandler:^(XCDYouTubeVideo *video, NSError *error) + { if (video) { NSURL *streamURL = nil; diff --git a/XCDYouTubeKit/XCDYouTubeVideoWebpage.m b/XCDYouTubeKit/XCDYouTubeVideoWebpage.m index dd95fb86..e1ab07a5 100644 --- a/XCDYouTubeKit/XCDYouTubeVideoWebpage.m +++ b/XCDYouTubeKit/XCDYouTubeVideoWebpage.m @@ -37,7 +37,8 @@ - (NSDictionary *) playerConfiguration CFStringEncoding encoding = CFStringConvertIANACharSetNameToEncoding((__bridge CFStringRef)self.response.textEncodingName ?: CFSTR("")); NSString *html = CFBridgingRelease(CFStringCreateWithBytes(kCFAllocatorDefault, [self.data bytes], (CFIndex)[self.data length], encoding != kCFStringEncodingInvalidId ? encoding : kCFStringEncodingISOLatin1, false)); NSRegularExpression *playerConfigRegularExpression = [NSRegularExpression regularExpressionWithPattern:@"ytplayer.config\\s*=\\s*(\\{.*?\\});" options:NSRegularExpressionCaseInsensitive error:NULL]; - [playerConfigRegularExpression enumerateMatchesInString:html options:(NSMatchingOptions)0 range:NSMakeRange(0, html.length) usingBlock:^(NSTextCheckingResult *result, NSMatchingFlags flags, BOOL *stop) { + [playerConfigRegularExpression enumerateMatchesInString:html options:(NSMatchingOptions)0 range:NSMakeRange(0, html.length) usingBlock:^(NSTextCheckingResult *result, NSMatchingFlags flags, BOOL *stop) + { NSString *configString = [html substringWithRange:[result rangeAtIndex:1]]; NSDictionary *playerConfiguration = [NSJSONSerialization JSONObjectWithData:[configString dataUsingEncoding:NSUTF8StringEncoding] options:(NSJSONReadingOptions)0 error:NULL]; if ([playerConfiguration isKindOfClass:[NSDictionary class]]) @@ -59,7 +60,8 @@ - (NSDictionary *) videoInfo if ([args isKindOfClass:[NSDictionary class]]) { NSMutableDictionary *info = [NSMutableDictionary new]; - [args enumerateKeysAndObjectsUsingBlock:^(id key, id value, BOOL *stop) { + [args enumerateKeysAndObjectsUsingBlock:^(id key, id value, BOOL *stop) + { if ([value isKindOfClass:[NSString class]] || [value isKindOfClass:[NSNumber class]]) info[key] = [value description]; }]; From cd56a9814803ba79d8c8e0583e852a35ded2b936 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ce=CC=81dric=20Luthi?= Date: Wed, 20 Aug 2014 14:35:29 +0200 Subject: [PATCH 04/57] Set the project class prefix to `XCD` --- XCDYouTubeKit.xcodeproj/project.pbxproj | 1 + 1 file changed, 1 insertion(+) diff --git a/XCDYouTubeKit.xcodeproj/project.pbxproj b/XCDYouTubeKit.xcodeproj/project.pbxproj index f09acaa2..75e377ed 100644 --- a/XCDYouTubeKit.xcodeproj/project.pbxproj +++ b/XCDYouTubeKit.xcodeproj/project.pbxproj @@ -437,6 +437,7 @@ C2A3F2C517F4827800AC1C3B /* Project object */ = { isa = PBXProject; attributes = { + CLASSPREFIX = XCD; LastUpgradeCheck = 0510; ORGANIZATIONNAME = "Cédric Luthi"; TargetAttributes = { From 0b9dacaf2ecd5cb14211746a9212783e63e11fc3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ce=CC=81dric=20Luthi?= Date: Fri, 5 Sep 2014 12:33:15 +0200 Subject: [PATCH 05/57] More consistent documentation of error codes --- XCDYouTubeKit/XCDYouTubeError.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/XCDYouTubeKit/XCDYouTubeError.h b/XCDYouTubeKit/XCDYouTubeError.h index ce360bb8..5bbc474d 100644 --- a/XCDYouTubeKit/XCDYouTubeError.h +++ b/XCDYouTubeKit/XCDYouTubeError.h @@ -14,7 +14,7 @@ extern NSString *const XCDYouTubeVideoErrorDomain; */ typedef NS_ENUM(NSInteger, XCDYouTubeErrorCode) { /** - * Returned if no suitable video stream is available. + * Returned when no suitable video stream is available. */ XCDYouTubeErrorNoStreamAvailable = -2, @@ -29,12 +29,12 @@ typedef NS_ENUM(NSInteger, XCDYouTubeErrorCode) { XCDYouTubeErrorInvalidVideoIdentifier = 2, /** - * Returned when the video has been removed as a violation of YouTube's policy or if the video does not exist. + * Returned when the video has been removed as a violation of YouTube's policy or when the video does not exist. */ XCDYouTubeErrorRemovedVideo = 100, /** - * Returned when the video is not playable because of legal reasons or the this is a private video. + * Returned when the video is not playable because of legal reasons or when the video is private. */ XCDYouTubeErrorRestrictedPlayback = 150 }; From 91471e67a3b7c968051f0ad995c6a0d0f1db2990 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ce=CC=81dric=20Luthi?= Date: Mon, 8 Sep 2014 11:47:11 +0200 Subject: [PATCH 06/57] Update restricted video error description --- .../testRestrictedVideo.json | 36 ++++++++++--------- .../XCDYouTubeClientTestCase.m | 2 +- 2 files changed, 20 insertions(+), 18 deletions(-) diff --git a/XCDYouTubeKit Tests/Cassettes/XCDYouTubeClientTestCase/testRestrictedVideo.json b/XCDYouTubeKit Tests/Cassettes/XCDYouTubeClientTestCase/testRestrictedVideo.json index c138b440..67f4b0a7 100644 --- a/XCDYouTubeKit Tests/Cassettes/XCDYouTubeClientTestCase/testRestrictedVideo.json +++ b/XCDYouTubeKit Tests/Cassettes/XCDYouTubeClientTestCase/testRestrictedVideo.json @@ -1,53 +1,55 @@ [ { - "body" : "errorcode=150&status=fail&reason=This+video+contains+content+from+Youtube+test+content+owner%2C+who+has+blocked+it+on+copyright+grounds.&eventid=p0RzU-WjIoOu8wOtnoGgAw&enablecsi=1&errordetail=0&csi_page_type=watch%2Cwatch7&c=WEB", + "body" : "enablecsi=1&csi_page_type=watch%2Cwatch7&errordetail=0&eventid=ynkNVLmhJoqI8QOfiIGIDA&status=fail&c=WEB&reason=This+video+is+currently+unavailable.+We+are+working+to+bring+it+back.&errorcode=150", "headers" : { "X-Content-Type-Options" : "nosniff", - "Server" : "gwiseguy\/2.0", "Content-Type" : "application\/x-www-form-urlencoded", + "Server" : "gwiseguy\/2.0", "Transfer-Encoding" : "Identity", - "Date" : "Wed, 14 May 2014 10:25:43 GMT", + "Date" : "Mon, 08 Sep 2014 09:41:30 GMT", "X-Frame-Options" : "SAMEORIGIN", "X-XSS-Protection" : "1; mode=block; report=https:\/\/www.google.com\/appserve\/security-bugs\/log\/youtube", "Alternate-Protocol" : "443:quic", - "Expires" : "Tue, 27 Apr 1971 19:44:06 EST", - "Cache-Control" : "no-store" + "Cache-Control" : "no-store", + "Expires" : "Tue, 27 Apr 1971 19:44:06 EST" }, "method" : "GET", "status" : 200, "uri" : "https:\/\/www.youtube.com\/get_video_info?el=detailpage&hl=en&ps=default&video_id=1kIsylLeHHU" }, { - "body" : "c=WEB&errorcode=150&errordetail=0&reason=This+video+contains+content+from+Youtube+test+content+owner%2C+who+has+blocked+it+on+copyright+grounds.&eventid=p0RzU-ykMNPx8QO-24DgAg&status=fail", + "body" : "reason=This+video+is+currently+unavailable.+We+are+working+to+bring+it+back.&eventid=ynkNVKmeLIeH8APpt4GwDw&errordetail=0&c=WEB&errorcode=150&status=fail", "headers" : { "X-Content-Type-Options" : "nosniff", "Content-Type" : "application\/x-www-form-urlencoded", "Server" : "gwiseguy\/2.0", "Transfer-Encoding" : "Identity", - "Date" : "Wed, 14 May 2014 10:25:43 GMT", + "Date" : "Mon, 08 Sep 2014 09:41:30 GMT", "X-Frame-Options" : "SAMEORIGIN", "X-XSS-Protection" : "1; mode=block; report=https:\/\/www.google.com\/appserve\/security-bugs\/log\/youtube", "Alternate-Protocol" : "443:quic", - "Expires" : "Tue, 27 Apr 1971 19:44:06 EST", - "Cache-Control" : "no-store" + "Cache-Control" : "no-store", + "Expires" : "Tue, 27 Apr 1971 19:44:06 EST" }, "method" : "GET", "status" : 200, "uri" : "https:\/\/www.youtube.com\/get_video_info?el=vevo&hl=en&ps=default&video_id=1kIsylLeHHU" }, { - "body" : "status=fail&enablecsi=1&errordetail=0&eventid=p0RzU42RGYGY8wPWuIC4Aw&errorcode=150&c=WEB&reason=This+video+contains+content+from+Youtube+test+content+owner%2C+who+has+blocked+it+on+copyright+grounds.&csi_page_type=embed&atc=a%3D3%26b%3DNT5sJxCYIijuRcwzJK8Z668dHYA%26c%3D1400063143%26d%3D1%26e%3D1kIsylLeHHU%26c3a%3D16%26hh%3DnUwRo0ZePbPax9ksLWBmXOPZzyI", + "body" : "errorcode=150&csi_page_type=embed&errordetail=0&c=WEB&atc=a%3D3%26b%3DMvHQUdPztUgFDpy5wx2zfWwgPRw%26c%3D1410169290%26d%3D1%26e%3D1kIsylLeHHU%26c3a%3D22%26hh%3DAh1pUdYFgkIdL0dIQKdof-F6uik&status=fail&enablecsi=1&reason=This+video+is+currently+unavailable.+We+are+working+to+bring+it+back.&eventid=ynkNVKawINTH8gPNpICwBQ", "headers" : { - "Server" : "gwiseguy\/2.0", "Content-Type" : "application\/x-www-form-urlencoded", - "X-Content-Type-Options" : "nosniff", - "Transfer-Encoding" : "Identity", - "Date" : "Wed, 14 May 2014 10:25:43 GMT", - "X-Frame-Options" : "SAMEORIGIN", - "X-XSS-Protection" : "1; mode=block; report=https:\/\/www.google.com\/appserve\/security-bugs\/log\/youtube", + "P3P" : "CP=\"This is not a P3P policy! See http:\/\/support.google.com\/accounts\/bin\/answer.py?answer=151657&hl=en for more info.\"", "Alternate-Protocol" : "443:quic", + "Set-Cookie" : "YSC=LMiN8XaKfNg; path=\/; domain=.youtube.com; httponly", + "Server" : "gwiseguy\/2.0", + "X-XSS-Protection" : "1; mode=block; report=https:\/\/www.google.com\/appserve\/security-bugs\/log\/youtube", + "Transfer-Encoding" : "Identity", "Expires" : "Tue, 27 Apr 1971 19:44:06 EST", - "Cache-Control" : "no-store" + "Cache-Control" : "no-store", + "Date" : "Mon, 08 Sep 2014 09:41:30 GMT", + "X-Content-Type-Options" : "nosniff", + "X-Frame-Options" : "SAMEORIGIN" }, "method" : "GET", "status" : 200, diff --git a/XCDYouTubeKit Tests/XCDYouTubeClientTestCase.m b/XCDYouTubeKit Tests/XCDYouTubeClientTestCase.m index ffd82bd1..547b0c51 100644 --- a/XCDYouTubeKit Tests/XCDYouTubeClientTestCase.m +++ b/XCDYouTubeKit Tests/XCDYouTubeClientTestCase.m @@ -105,7 +105,7 @@ - (void) testRestrictedVideo XCTAssertNil(video); XCTAssertEqualObjects(error.domain, XCDYouTubeVideoErrorDomain); XCTAssertEqual(error.code, XCDYouTubeErrorRestrictedPlayback); - XCTAssertEqualObjects(error.localizedDescription, @"This video contains content from Youtube test content owner, who has blocked it on copyright grounds."); + XCTAssertEqualObjects(error.localizedDescription, @"This video is currently unavailable. We are working to bring it back."); [monitor signal]; }]; XCTAssertTrue([monitor waitWithTimeout:10]); From f2f3cc8dc9a8f7b6d03352ded2a5420cef774c45 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ce=CC=81dric=20Luthi?= Date: Mon, 8 Sep 2014 11:49:38 +0200 Subject: [PATCH 07/57] Update video identifier of geoblocked test video --- .../testGeoblockedVideo.json | 39 ++++++++++--------- .../XCDYouTubeClientTestCase.m | 2 +- 2 files changed, 22 insertions(+), 19 deletions(-) diff --git a/XCDYouTubeKit Tests/Cassettes/XCDYouTubeClientTestCase/testGeoblockedVideo.json b/XCDYouTubeKit Tests/Cassettes/XCDYouTubeClientTestCase/testGeoblockedVideo.json index 228cda06..f6aedbdb 100644 --- a/XCDYouTubeKit Tests/Cassettes/XCDYouTubeClientTestCase/testGeoblockedVideo.json +++ b/XCDYouTubeKit Tests/Cassettes/XCDYouTubeClientTestCase/testGeoblockedVideo.json @@ -1,55 +1,58 @@ [ { - "body" : "eventid=JQvaU737NYOiyQPdyIGoCQ&errorcode=150&reason=The+uploader+has+not+made+this+video+available+in+your+country.&c=WEB&status=fail&errordetail=0", + "body" : "errorcode=150&atc=a%3D3%26b%3Dq-TT-kKwnO6ZTJf96zRTGkEu-2A%26c%3D1410169662%26d%3D1%26e%3DvwkFTztnl7Y%26c3a%3D17%26hh%3DwEa6m7g-xiBiv69bEPs1Q89Tb5Y&csi_page_type=embed&reason=The+uploader+has+not+made+this+video+available+in+your+country.&status=fail&enablecsi=1&eventid=PnsNVNevMcH98gOzyYEo&errordetail=0&c=WEB", "headers" : { - "X-Content-Type-Options" : "nosniff", - "Server" : "gwiseguy\/2.0", "Content-Type" : "application\/x-www-form-urlencoded", - "Transfer-Encoding" : "Identity", - "Date" : "Thu, 31 Jul 2014 09:23:49 GMT", - "X-Frame-Options" : "SAMEORIGIN", + "P3P" : "CP=\"This is not a P3P policy! See http:\/\/support.google.com\/accounts\/bin\/answer.py?answer=151657&hl=en for more info.\"", + "Alternate-Protocol" : "443:quic", + "Set-Cookie" : "YSC=TWJwXoBQ48E; path=\/; domain=.youtube.com; httponly", + "Server" : "gwiseguy\/2.0", "X-XSS-Protection" : "1; mode=block; report=https:\/\/www.google.com\/appserve\/security-bugs\/log\/youtube", + "Transfer-Encoding" : "Identity", "Expires" : "Tue, 27 Apr 1971 19:44:06 EST", - "Cache-Control" : "no-store" + "Cache-Control" : "no-store", + "Date" : "Mon, 08 Sep 2014 09:47:42 GMT", + "X-Content-Type-Options" : "nosniff", + "X-Frame-Options" : "SAMEORIGIN" }, "method" : "GET", "status" : 200, - "uri" : "https:\/\/www.youtube.com\/get_video_info?el=vevo&hl=en&ps=default&video_id=j4GAs9TJVjM" + "uri" : "https:\/\/www.youtube.com\/get_video_info?el=embedded&hl=en&ps=default&video_id=vwkFTztnl7Y" }, { - "body" : "csi_page_type=embed&errordetail=0&eventid=JQvaU-uvKMTrywPSoICIBQ&atc=a%3D3%26b%3DaBEhJaqb6mNu02iPcUeb__SqKxE%26c%3D1406798629%26d%3D1%26e%3Dj4GAs9TJVjM%26c3a%3D17%26hh%3DiBftfLPb8PBrBhsY-FbylA5Gy28&status=fail&reason=The+uploader+has+not+made+this+video+available+in+your+country.&enablecsi=1&errorcode=150&c=WEB", + "body" : "errorcode=150&eventid=PnsNVN3KNpTc8gPmsoHgBA&status=fail&csi_page_type=watch%2Cwatch7&c=WEB&reason=The+uploader+has+not+made+this+video+available+in+your+country.&errordetail=0&enablecsi=1", "headers" : { "Server" : "gwiseguy\/2.0", - "Set-Cookie" : "YSC=4B4Cp2LLsAA; path=\/; domain=.youtube.com; httponly", "Content-Type" : "application\/x-www-form-urlencoded", "X-Content-Type-Options" : "nosniff", "Transfer-Encoding" : "Identity", - "P3P" : "CP=\"This is not a P3P policy! See http:\/\/support.google.com\/accounts\/bin\/answer.py?answer=151657&hl=en for more info.\"", - "Date" : "Thu, 31 Jul 2014 09:23:49 GMT", + "Date" : "Mon, 08 Sep 2014 09:47:42 GMT", "X-Frame-Options" : "SAMEORIGIN", "X-XSS-Protection" : "1; mode=block; report=https:\/\/www.google.com\/appserve\/security-bugs\/log\/youtube", + "Alternate-Protocol" : "443:quic", "Cache-Control" : "no-store", "Expires" : "Tue, 27 Apr 1971 19:44:06 EST" }, "method" : "GET", "status" : 200, - "uri" : "https:\/\/www.youtube.com\/get_video_info?el=embedded&hl=en&ps=default&video_id=j4GAs9TJVjM" + "uri" : "https:\/\/www.youtube.com\/get_video_info?el=detailpage&hl=en&ps=default&video_id=vwkFTztnl7Y" }, { - "body" : "enablecsi=1&reason=The+uploader+has+not+made+this+video+available+in+your+country.&eventid=JQvaU_b_LZLwygP-vIHgDQ&c=WEB&errorcode=150&csi_page_type=watch%2Cwatch7&errordetail=0&status=fail", + "body" : "eventid=PnsNVJPkOoqH8wOEloKgDg&status=fail&errordetail=0&c=WEB&reason=The+uploader+has+not+made+this+video+available+in+your+country.&errorcode=150", "headers" : { - "X-Content-Type-Options" : "nosniff", "Server" : "gwiseguy\/2.0", "Content-Type" : "application\/x-www-form-urlencoded", - "Transfer-Encoding" : "Identity", - "Date" : "Thu, 31 Jul 2014 09:23:49 GMT", + "X-Content-Type-Options" : "nosniff", + "Date" : "Mon, 08 Sep 2014 09:47:42 GMT", "X-Frame-Options" : "SAMEORIGIN", "X-XSS-Protection" : "1; mode=block; report=https:\/\/www.google.com\/appserve\/security-bugs\/log\/youtube", + "Alternate-Protocol" : "443:quic", + "Content-Length" : "147", "Expires" : "Tue, 27 Apr 1971 19:44:06 EST", "Cache-Control" : "no-store" }, "method" : "GET", "status" : 200, - "uri" : "https:\/\/www.youtube.com\/get_video_info?el=detailpage&hl=en&ps=default&video_id=j4GAs9TJVjM" + "uri" : "https:\/\/www.youtube.com\/get_video_info?el=vevo&hl=en&ps=default&video_id=vwkFTztnl7Y" } ] \ No newline at end of file diff --git a/XCDYouTubeKit Tests/XCDYouTubeClientTestCase.m b/XCDYouTubeKit Tests/XCDYouTubeClientTestCase.m index 547b0c51..f91115db 100644 --- a/XCDYouTubeKit Tests/XCDYouTubeClientTestCase.m +++ b/XCDYouTubeKit Tests/XCDYouTubeClientTestCase.m @@ -128,7 +128,7 @@ - (void) testRemovedVideo - (void) testGeoblockedVideo { TRVSMonitor *monitor = [TRVSMonitor monitor]; - [[XCDYouTubeClient defaultClient] getVideoWithIdentifier:@"j4GAs9TJVjM" completionHandler:^(XCDYouTubeVideo *video, NSError *error) { + [[XCDYouTubeClient defaultClient] getVideoWithIdentifier:@"vwkFTztnl7Y" completionHandler:^(XCDYouTubeVideo *video, NSError *error) { XCTAssertNil(video); XCTAssertEqualObjects(error.domain, XCDYouTubeVideoErrorDomain); XCTAssertEqual(error.code, XCDYouTubeErrorRestrictedPlayback); From a5688d2043804b303f6eedef070f4c2f2fc3d698 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ce=CC=81dric=20Luthi?= Date: Mon, 8 Sep 2014 15:32:31 +0200 Subject: [PATCH 08/57] Add possibility to run tests in online mode --- .../XCDYouTubeClientTestCase.m | 5 ++-- XCDYouTubeKit Tests/XCDYouTubeKitTestCase.m | 30 ++++++++++++++++++- .../XCDYouTubeProtectedVideosTestCase.m | 10 +++---- .../xcschemes/XCDYouTubeKit iOS.xcscheme | 5 ++++ 4 files changed, 42 insertions(+), 8 deletions(-) diff --git a/XCDYouTubeKit Tests/XCDYouTubeClientTestCase.m b/XCDYouTubeKit Tests/XCDYouTubeClientTestCase.m index f91115db..0da425c2 100644 --- a/XCDYouTubeKit Tests/XCDYouTubeClientTestCase.m +++ b/XCDYouTubeKit Tests/XCDYouTubeClientTestCase.m @@ -61,7 +61,7 @@ - (void) testMobileRestrictedVideo XCTAssertTrue([monitor waitWithTimeout:10]); } -- (void) testLiveVideo +- (void) testLiveVideo_offline { TRVSMonitor *monitor = [TRVSMonitor monitor]; [[XCDYouTubeClient defaultClient] getVideoWithIdentifier:@"xrM34fdmloc" completionHandler:^(XCDYouTubeVideo *video, NSError *error) @@ -208,7 +208,8 @@ - (void) testSpaceVideoIdentifier XCTAssertTrue([monitor waitWithTimeout:10]); } -- (void) testConnectionError +// Disable internet connection before running +- (void) testConnectionError_offline { TRVSMonitor *monitor = [TRVSMonitor monitor]; [[XCDYouTubeClient defaultClient] getVideoWithIdentifier:@"EdeVaT-zZt4" completionHandler:^(XCDYouTubeVideo *video, NSError *error) diff --git a/XCDYouTubeKit Tests/XCDYouTubeKitTestCase.m b/XCDYouTubeKit Tests/XCDYouTubeKitTestCase.m index 1e32f095..06a6adac 100644 --- a/XCDYouTubeKit Tests/XCDYouTubeKitTestCase.m +++ b/XCDYouTubeKit Tests/XCDYouTubeKitTestCase.m @@ -12,12 +12,36 @@ @interface XCDYouTubeKitTestCase () @property NSURL *cassetteURL; @end +static NSString *const offlineSuffix = @"_offline"; + @implementation XCDYouTubeKitTestCase +// When running tests with the `ONLINE_TESTS` environment variable, tests whose +// selector ends with `_offline` are not executed. +// Also, VCR is not used at all, neither for recording nor for replaying. ++ (NSArray *) testInvocations +{ + BOOL onlineTests = [[[[NSProcessInfo processInfo] environment] objectForKey:@"ONLINE_TESTS"] boolValue]; + if (!onlineTests) + return [super testInvocations]; + + NSMutableArray *testInvocations = [NSMutableArray new]; + for (NSInvocation *invocation in [super testInvocations]) + { + if (![NSStringFromSelector(invocation.selector) hasSuffix:offlineSuffix]) + [testInvocations addObject:invocation]; + } + return [testInvocations copy]; +} + - (void) setUpTestWithSelector:(SEL)selector { [super setUpTestWithSelector:selector]; + BOOL onlineTests = [[[[NSProcessInfo processInfo] environment] objectForKey:@"ONLINE_TESTS"] boolValue]; + if (onlineTests) + return; + NSString *cassettesDirectory = [[[NSProcessInfo processInfo] environment] objectForKey:@"VCR_CASSETTES_DIRECTORY"]; cassettesDirectory = [cassettesDirectory stringByAppendingPathComponent:NSStringFromClass(self.class)]; if ([[NSFileManager defaultManager] fileExistsAtPath:cassettesDirectory]) @@ -28,7 +52,11 @@ - (void) setUpTestWithSelector:(SEL)selector } else { - self.cassetteURL = [[NSBundle bundleForClass:self.class] URLForResource:NSStringFromSelector(selector) withExtension:@"json" subdirectory:[@"Cassettes" stringByAppendingPathComponent:NSStringFromClass(self.class)]]; + NSString *testName = NSStringFromSelector(selector); + if ([testName hasSuffix:offlineSuffix]) + testName = [testName substringToIndex:testName.length - offlineSuffix.length]; + + self.cassetteURL = [[NSBundle bundleForClass:self.class] URLForResource:testName withExtension:@"json" subdirectory:[@"Cassettes" stringByAppendingPathComponent:NSStringFromClass(self.class)]]; XCTAssertNotNil(self.cassetteURL); [VCR loadCassetteWithContentsOfURL:self.cassetteURL]; [VCR setReplaying:YES]; diff --git a/XCDYouTubeKit Tests/XCDYouTubeProtectedVideosTestCase.m b/XCDYouTubeKit Tests/XCDYouTubeProtectedVideosTestCase.m index 7cf3b56f..fb791258 100644 --- a/XCDYouTubeKit Tests/XCDYouTubeProtectedVideosTestCase.m +++ b/XCDYouTubeKit Tests/XCDYouTubeProtectedVideosTestCase.m @@ -97,7 +97,7 @@ - (void) testProtectedVideoWithJavaScriptFunctionsInVarScope // With Charles // * Enable SSL proxying for *.youtube.com // * Tools -> Black List... -> Add host:www.youtube.com and path:watch to simulate connection error on the web page -- (void) testProtectedVideoWithWebPageConnectionError +- (void) testProtectedVideoWithWebPageConnectionError_offline { TRVSMonitor *monitor = [TRVSMonitor monitor]; [[XCDYouTubeClient defaultClient] getVideoWithIdentifier:@"Pgum6OT_VH8" completionHandler:^(XCDYouTubeVideo *video, NSError *error) @@ -112,7 +112,7 @@ - (void) testProtectedVideoWithWebPageConnectionError } // With Charles: Tools -> Black List... -> Add `s.ytimg.com` to simulate connection error on the player script -- (void) testProtectedVideoWithPlayerScriptConnectionError +- (void) testProtectedVideoWithPlayerScriptConnectionError_offline { TRVSMonitor *monitor = [TRVSMonitor monitor]; [[XCDYouTubeClient defaultClient] getVideoWithIdentifier:@"Pgum6OT_VH8" completionHandler:^(XCDYouTubeVideo *video, NSError *error) @@ -127,7 +127,7 @@ - (void) testProtectedVideoWithPlayerScriptConnectionError } // Edit testProtectedVideoWithoutSignatureFunction.json by replacing `signature=` with `signaturX=` -- (void) testProtectedVideoWithoutSignatureFunction +- (void) testProtectedVideoWithoutSignatureFunction_offline { TRVSMonitor *monitor = [TRVSMonitor monitor]; [[XCDYouTubeClient defaultClient] getVideoWithIdentifier:@"Pgum6OT_VH8" completionHandler:^(XCDYouTubeVideo *video, NSError *error) @@ -142,7 +142,7 @@ - (void) testProtectedVideoWithoutSignatureFunction } // Edit testProtectedVideoWithBrokenSignatureFunction.json by returning null in the signature function -- (void) testProtectedVideoWithBrokenSignatureFunction +- (void) testProtectedVideoWithBrokenSignatureFunction_offline { TRVSMonitor *monitor = [TRVSMonitor monitor]; [[XCDYouTubeClient defaultClient] getVideoWithIdentifier:@"Pgum6OT_VH8" completionHandler:^(XCDYouTubeVideo *video, NSError *error) @@ -157,7 +157,7 @@ - (void) testProtectedVideoWithBrokenSignatureFunction } // Edit testProtectedVideoWithoutJavaScriptPlayerURL.json by replacing `\"js\":` with `\"xs\":` -- (void) testProtectedVideoWithoutJavaScriptPlayerURL +- (void) testProtectedVideoWithoutJavaScriptPlayerURL_offline { TRVSMonitor *monitor = [TRVSMonitor monitor]; [[XCDYouTubeClient defaultClient] getVideoWithIdentifier:@"Pgum6OT_VH8" completionHandler:^(XCDYouTubeVideo *video, NSError *error) diff --git a/XCDYouTubeKit.xcodeproj/xcshareddata/xcschemes/XCDYouTubeKit iOS.xcscheme b/XCDYouTubeKit.xcodeproj/xcshareddata/xcschemes/XCDYouTubeKit iOS.xcscheme index b7ba053a..0ef0eb63 100644 --- a/XCDYouTubeKit.xcodeproj/xcshareddata/xcschemes/XCDYouTubeKit iOS.xcscheme +++ b/XCDYouTubeKit.xcodeproj/xcshareddata/xcschemes/XCDYouTubeKit iOS.xcscheme @@ -54,6 +54,11 @@ value = "$(SRCROOT)/XCDYouTubeKit Tests/Cassettes" isEnabled = "NO"> + + Date: Tue, 9 Sep 2014 11:57:48 +0200 Subject: [PATCH 09/57] Add unit test for age restricted video --- .../testAgeRestrictedVideo.json | 22 +++++++++++++++++++ .../XCDYouTubeProtectedVideosTestCase.m | 20 +++++++++++++++++ 2 files changed, 42 insertions(+) create mode 100644 XCDYouTubeKit Tests/Cassettes/XCDYouTubeProtectedVideosTestCase/testAgeRestrictedVideo.json diff --git a/XCDYouTubeKit Tests/Cassettes/XCDYouTubeProtectedVideosTestCase/testAgeRestrictedVideo.json b/XCDYouTubeKit Tests/Cassettes/XCDYouTubeProtectedVideosTestCase/testAgeRestrictedVideo.json new file mode 100644 index 00000000..f70e77d6 --- /dev/null +++ b/XCDYouTubeKit Tests/Cassettes/XCDYouTubeProtectedVideosTestCase/testAgeRestrictedVideo.json @@ -0,0 +1,22 @@ +[ + { + "body" : "atc=a%3D3%26b%3Dz54bvUIYgZeIDPGgUea_kZbGuUc%26c%3D1410256429%26d%3D1%26e%3DzKovmts2KSk%26c3a%3D13%26hh%3DllHTr-Mdxp16gKaEujY5Udb0DB0&has_cc=False&status=ok&avg_rating=4.84961439589&baseUrl=https%3A%2F%2Fgoogleads.g.doubleclick.net%2Fpagead%2Fviewthroughconversion%2F962985656%2F&remarketing_url=https%3A%2F%2Fgoogleads.g.doubleclick.net%2Fpagead%2Fviewthroughconversion%2F962985656%2F%3Fbackend%3Dplayer_vars%26data%3Dbackend%253Dplayer_vars%253Bcname%253DWEB%253Bcver%253DAS3%253Bptype%253Dview%253Btype%253Dview%253Butuid%253D3GriadTkHBnfgd2UFETGOA%253Butvid%253DzKovmts2KSk%26cname%3DWEB%26aid%3DP9BjEyDXMQw%26cver%3DAS3%26label%3Dfollowon_view%26ptype%3Dview%26foc_id%3D3GriadTkHBnfgd2UFETGOA&account_playback_token=QUFFLUhqbDE1ZVpJTldFTHk0NzdicWNnM282RFZsR2p0Z3xBQ3Jtc0ttSTkyR0tpRTBlUVBMOFZFNWpLTDA1bmxyUFVOYjduUnNreVBpMmVrMjdVcE5nZ2xEandvSGotcjdrdFc5RlpWYXFfakRqc0VSUzRQY2NyN2R0c2FPUW05NkQ5M3dZYlpwcUthTWpwc1YxU3plYnBsQQ%3D%3D&adaptive_fmts=size%3D1920x816%26fps%3D29%26index%3D709-1064%26lmt%3D1390819922164503%26itag%3D137%26url%3Dhttp%253A%252F%252Fr15---sn-1gi7zn7y.googlevideo.com%252Fvideoplayback%253Fsver%253D3%2526itag%253D137%2526mm%253D31%2526gir%253Dyes%2526ipbits%253D0%2526dur%253D131.297%2526id%253Do-AK4tbFNn-kBD6bAyrHwY6rWPQHu9AQfudTA7ZQ0hD3D7%2526lmt%253D1390819922164503%2526ms%253Dau%2526mv%253Dm%2526mt%253D1410256308%2526key%253Dyt5%2526clen%253D31138136%2526source%253Dyoutube%2526ip%253D193.5.216.83%2526expire%253D1410278029%2526upn%253DyZGeJGScHyk%2526sparams%253Dclen%25252Cdur%25252Cgir%25252Cid%25252Cinitcwndbps%25252Cip%25252Cipbits%25252Citag%25252Clmt%25252Cmm%25252Cms%25252Cmv%25252Csource%25252Cupn%25252Cexpire%2526signature%253D9EE82746C2040C83586236338D378664FDAA0B77.4F73C92C09AB71A241339903A1877D9944DD8E8E%2526fexp%253D927622%25252C931983%25252C932404%25252C932623%25252C934024%25252C934030%25252C936117%25252C938644%25252C940215%25252C946013%25252C953801%25252C955100%2526initcwndbps%253D9552000%26init%3D0-708%26type%3Dvideo%252Fmp4%253B%2Bcodecs%253D%2522avc1.640028%2522%26clen%3D31138136%26bitrate%3D3269886%2Csize%3D1920x816%26fps%3D1%26index%3D235-690%26lmt%3D1407639763428125%26itag%3D248%26url%3Dhttp%253A%252F%252Fr15---sn-1gi7zn7y.googlevideo.com%252Fvideoplayback%253Fsver%253D3%2526itag%253D248%2526mm%253D31%2526gir%253Dyes%2526ipbits%253D0%2526dur%253D131.264%2526id%253Do-AK4tbFNn-kBD6bAyrHwY6rWPQHu9AQfudTA7ZQ0hD3D7%2526lmt%253D1407639763428125%2526ms%253Dau%2526mv%253Dm%2526mt%253D1410256308%2526key%253Dyt5%2526clen%253D21284554%2526source%253Dyoutube%2526ip%253D193.5.216.83%2526expire%253D1410278029%2526upn%253DyZGeJGScHyk%2526sparams%253Dclen%25252Cdur%25252Cgir%25252Cid%25252Cinitcwndbps%25252Cip%25252Cipbits%25252Citag%25252Clmt%25252Cmm%25252Cms%25252Cmv%25252Csource%25252Cupn%25252Cexpire%2526signature%253D0F898A012FB0CCB1576406A66E614F4F9FBF19EB.9BE6D09440D7997D91E028C7E5FA5DE8C3271EA4%2526fexp%253D927622%25252C931983%25252C932404%25252C932623%25252C934024%25252C934030%25252C936117%25252C938644%25252C940215%25252C946013%25252C953801%25252C955100%2526initcwndbps%253D9552000%26init%3D0-234%26type%3Dvideo%252Fwebm%253B%2Bcodecs%253D%2522vp9%2522%26clen%3D21284554%26bitrate%3D2597119%2Csize%3D1280x544%26fps%3D29%26index%3D708-1063%26lmt%3D1390819917153673%26itag%3D136%26url%3Dhttp%253A%252F%252Fr15---sn-1gi7zn7y.googlevideo.com%252Fvideoplayback%253Fsver%253D3%2526itag%253D136%2526mm%253D31%2526gir%253Dyes%2526ipbits%253D0%2526dur%253D131.297%2526id%253Do-AK4tbFNn-kBD6bAyrHwY6rWPQHu9AQfudTA7ZQ0hD3D7%2526lmt%253D1390819917153673%2526ms%253Dau%2526mv%253Dm%2526mt%253D1410256308%2526key%253Dyt5%2526clen%253D18206689%2526source%253Dyoutube%2526ip%253D193.5.216.83%2526expire%253D1410278029%2526upn%253DyZGeJGScHyk%2526sparams%253Dclen%25252Cdur%25252Cgir%25252Cid%25252Cinitcwndbps%25252Cip%25252Cipbits%25252Citag%25252Clmt%25252Cmm%25252Cms%25252Cmv%25252Csource%25252Cupn%25252Cexpire%2526signature%253D2B6B365B6A8CA2A169A93620CE51C83887F27991.684CDEFA6F4D2EE2B2C2FDDC435B963EF8CD0D86%2526fexp%253D927622%25252C931983%25252C932404%25252C932623%25252C934024%25252C934030%25252C936117%25252C938644%25252C940215%25252C946013%25252C953801%25252C955100%2526initcwndbps%253D9552000%26init%3D0-707%26type%3Dvideo%252Fmp4%253B%2Bcodecs%253D%2522avc1.4d401f%2522%26clen%3D18206689%26bitrate%3D2351771%2Csize%3D1280x544%26fps%3D1%26index%3D235-683%26lmt%3D1407639731724242%26itag%3D247%26url%3Dhttp%253A%252F%252Fr15---sn-1gi7zn7y.googlevideo.com%252Fvideoplayback%253Fsver%253D3%2526itag%253D247%2526mm%253D31%2526gir%253Dyes%2526ipbits%253D0%2526dur%253D131.264%2526id%253Do-AK4tbFNn-kBD6bAyrHwY6rWPQHu9AQfudTA7ZQ0hD3D7%2526lmt%253D1407639731724242%2526ms%253Dau%2526mv%253Dm%2526mt%253D1410256308%2526key%253Dyt5%2526clen%253D10865194%2526source%253Dyoutube%2526ip%253D193.5.216.83%2526expire%253D1410278029%2526upn%253DyZGeJGScHyk%2526sparams%253Dclen%25252Cdur%25252Cgir%25252Cid%25252Cinitcwndbps%25252Cip%25252Cipbits%25252Citag%25252Clmt%25252Cmm%25252Cms%25252Cmv%25252Csource%25252Cupn%25252Cexpire%2526signature%253DE10849EE8A1CAC9A9DF30431F20A0D3D1D4970BA.C66A90B61A76B0E9D0AFAC142D26864C88E002CE%2526fexp%253D927622%25252C931983%25252C932404%25252C932623%25252C934024%25252C934030%25252C936117%25252C938644%25252C940215%25252C946013%25252C953801%25252C955100%2526initcwndbps%253D9552000%26init%3D0-234%26type%3Dvideo%252Fwebm%253B%2Bcodecs%253D%2522vp9%2522%26clen%3D10865194%26bitrate%3D1471453%2Csize%3D854x362%26fps%3D29%26index%3D709-1064%26lmt%3D1390819912153885%26itag%3D135%26url%3Dhttp%253A%252F%252Fr15---sn-1gi7zn7y.googlevideo.com%252Fvideoplayback%253Fsver%253D3%2526itag%253D135%2526mm%253D31%2526gir%253Dyes%2526ipbits%253D0%2526dur%253D131.297%2526id%253Do-AK4tbFNn-kBD6bAyrHwY6rWPQHu9AQfudTA7ZQ0hD3D7%2526lmt%253D1390819912153885%2526ms%253Dau%2526mv%253Dm%2526mt%253D1410256308%2526key%253Dyt5%2526clen%253D8989594%2526source%253Dyoutube%2526ip%253D193.5.216.83%2526expire%253D1410278029%2526upn%253DyZGeJGScHyk%2526sparams%253Dclen%25252Cdur%25252Cgir%25252Cid%25252Cinitcwndbps%25252Cip%25252Cipbits%25252Citag%25252Clmt%25252Cmm%25252Cms%25252Cmv%25252Csource%25252Cupn%25252Cexpire%2526signature%253D075A332FC99178F4F8B8382E1A086AF46D8ECF1A.688C02FE0AD0B185A1535DBEE5FFC076AFB2F630%2526fexp%253D927622%25252C931983%25252C932404%25252C932623%25252C934024%25252C934030%25252C936117%25252C938644%25252C940215%25252C946013%25252C953801%25252C955100%2526initcwndbps%253D9552000%26init%3D0-708%26type%3Dvideo%252Fmp4%253B%2Bcodecs%253D%2522avc1.4d401e%2522%26clen%3D8989594%26bitrate%3D1051180%2Csize%3D854x362%26fps%3D1%26index%3D235-683%26lmt%3D1407639728179350%26itag%3D244%26url%3Dhttp%253A%252F%252Fr15---sn-1gi7zn7y.googlevideo.com%252Fvideoplayback%253Fsver%253D3%2526itag%253D244%2526mm%253D31%2526gir%253Dyes%2526ipbits%253D0%2526dur%253D131.264%2526id%253Do-AK4tbFNn-kBD6bAyrHwY6rWPQHu9AQfudTA7ZQ0hD3D7%2526lmt%253D1407639728179350%2526ms%253Dau%2526mv%253Dm%2526mt%253D1410256308%2526key%253Dyt5%2526clen%253D6261671%2526source%253Dyoutube%2526ip%253D193.5.216.83%2526expire%253D1410278029%2526upn%253DyZGeJGScHyk%2526sparams%253Dclen%25252Cdur%25252Cgir%25252Cid%25252Cinitcwndbps%25252Cip%25252Cipbits%25252Citag%25252Clmt%25252Cmm%25252Cms%25252Cmv%25252Csource%25252Cupn%25252Cexpire%2526signature%253D8B32AD0C1BDB4F6EA5280A519F6D68AF112798A4.6B31F459C6C3728EAA48530DB8EAE8336A40B568%2526fexp%253D927622%25252C931983%25252C932404%25252C932623%25252C934024%25252C934030%25252C936117%25252C938644%25252C940215%25252C946013%25252C953801%25252C955100%2526initcwndbps%253D9552000%26init%3D0-234%26type%3Dvideo%252Fwebm%253B%2Bcodecs%253D%2522vp9%2522%26clen%3D6261671%26bitrate%3D859631%2Csize%3D640x272%26fps%3D29%26index%3D707-1062%26lmt%3D1390819912153163%26itag%3D134%26url%3Dhttp%253A%252F%252Fr15---sn-1gi7zn7y.googlevideo.com%252Fvideoplayback%253Fsver%253D3%2526itag%253D134%2526mm%253D31%2526gir%253Dyes%2526ipbits%253D0%2526dur%253D131.297%2526id%253Do-AK4tbFNn-kBD6bAyrHwY6rWPQHu9AQfudTA7ZQ0hD3D7%2526lmt%253D1390819912153163%2526ms%253Dau%2526mv%253Dm%2526mt%253D1410256308%2526key%253Dyt5%2526clen%253D4227718%2526source%253Dyoutube%2526ip%253D193.5.216.83%2526expire%253D1410278029%2526upn%253DyZGeJGScHyk%2526sparams%253Dclen%25252Cdur%25252Cgir%25252Cid%25252Cinitcwndbps%25252Cip%25252Cipbits%25252Citag%25252Clmt%25252Cmm%25252Cms%25252Cmv%25252Csource%25252Cupn%25252Cexpire%2526signature%253D89C8ACCBE752743DE10EC9C8DAAD903F8F287604.D740E7E7FA637E9141BB6E48315444A039841A1C%2526fexp%253D927622%25252C931983%25252C932404%25252C932623%25252C934024%25252C934030%25252C936117%25252C938644%25252C940215%25252C946013%25252C953801%25252C955100%2526initcwndbps%253D9552000%26init%3D0-706%26type%3Dvideo%252Fmp4%253B%2Bcodecs%253D%2522avc1.4d401e%2522%26clen%3D4227718%26bitrate%3D481662%2Csize%3D640x272%26fps%3D1%26index%3D235-681%26lmt%3D1407639727173505%26itag%3D243%26url%3Dhttp%253A%252F%252Fr15---sn-1gi7zn7y.googlevideo.com%252Fvideoplayback%253Fsver%253D3%2526itag%253D243%2526mm%253D31%2526gir%253Dyes%2526ipbits%253D0%2526dur%253D131.264%2526id%253Do-AK4tbFNn-kBD6bAyrHwY6rWPQHu9AQfudTA7ZQ0hD3D7%2526lmt%253D1407639727173505%2526ms%253Dau%2526mv%253Dm%2526mt%253D1410256308%2526key%253Dyt5%2526clen%253D3352706%2526source%253Dyoutube%2526ip%253D193.5.216.83%2526expire%253D1410278029%2526upn%253DyZGeJGScHyk%2526sparams%253Dclen%25252Cdur%25252Cgir%25252Cid%25252Cinitcwndbps%25252Cip%25252Cipbits%25252Citag%25252Clmt%25252Cmm%25252Cms%25252Cmv%25252Csource%25252Cupn%25252Cexpire%2526signature%253DC9D745CAD1443C195F4EAFE78358238090A93FEF.A0FED3EDA9871C52A616633B3D4EF987EEC3B1B5%2526fexp%253D927622%25252C931983%25252C932404%25252C932623%25252C934024%25252C934030%25252C936117%25252C938644%25252C940215%25252C946013%25252C953801%25252C955100%2526initcwndbps%253D9552000%26init%3D0-234%26type%3Dvideo%252Fwebm%253B%2Bcodecs%253D%2522vp9%2522%26clen%3D3352706%26bitrate%3D411270%2Csize%3D426x182%26fps%3D29%26index%3D673-1028%26lmt%3D1390819912152585%26itag%3D133%26url%3Dhttp%253A%252F%252Fr15---sn-1gi7zn7y.googlevideo.com%252Fvideoplayback%253Fsver%253D3%2526itag%253D133%2526mm%253D31%2526gir%253Dyes%2526ipbits%253D0%2526dur%253D131.297%2526id%253Do-AK4tbFNn-kBD6bAyrHwY6rWPQHu9AQfudTA7ZQ0hD3D7%2526lmt%253D1390819912152585%2526ms%253Dau%2526mv%253Dm%2526mt%253D1410256308%2526key%253Dyt5%2526clen%253D3703418%2526source%253Dyoutube%2526ip%253D193.5.216.83%2526expire%253D1410278029%2526upn%253DyZGeJGScHyk%2526sparams%253Dclen%25252Cdur%25252Cgir%25252Cid%25252Cinitcwndbps%25252Cip%25252Cipbits%25252Citag%25252Clmt%25252Cmm%25252Cms%25252Cmv%25252Csource%25252Cupn%25252Cexpire%2526signature%253D317FF73C7CD6F045277AC52D0F06E41C35AE6807.B6C40A0DD92C204FB47266ED01B1DDC333BE15EA%2526fexp%253D927622%25252C931983%25252C932404%25252C932623%25252C934024%25252C934030%25252C936117%25252C938644%25252C940215%25252C946013%25252C953801%25252C955100%2526initcwndbps%253D9552000%26init%3D0-672%26type%3Dvideo%252Fmp4%253B%2Bcodecs%253D%2522avc1.4d400d%2522%26clen%3D3703418%26bitrate%3D251393%2Csize%3D426x182%26fps%3D1%26index%3D234-680%26lmt%3D1407639714287107%26itag%3D242%26url%3Dhttp%253A%252F%252Fr15---sn-1gi7zn7y.googlevideo.com%252Fvideoplayback%253Fsver%253D3%2526itag%253D242%2526mm%253D31%2526gir%253Dyes%2526ipbits%253D0%2526dur%253D131.264%2526id%253Do-AK4tbFNn-kBD6bAyrHwY6rWPQHu9AQfudTA7ZQ0hD3D7%2526lmt%253D1407639714287107%2526ms%253Dau%2526mv%253Dm%2526mt%253D1410256308%2526key%253Dyt5%2526clen%253D1809613%2526source%253Dyoutube%2526ip%253D193.5.216.83%2526expire%253D1410278029%2526upn%253DyZGeJGScHyk%2526sparams%253Dclen%25252Cdur%25252Cgir%25252Cid%25252Cinitcwndbps%25252Cip%25252Cipbits%25252Citag%25252Clmt%25252Cmm%25252Cms%25252Cmv%25252Csource%25252Cupn%25252Cexpire%2526signature%253DD7939754C2448822B5CEBECF43DD074100D30950.7C3D90F985036458740722B303FAA45838B44252%2526fexp%253D927622%25252C931983%25252C932404%25252C932623%25252C934024%25252C934030%25252C936117%25252C938644%25252C940215%25252C946013%25252C953801%25252C955100%2526initcwndbps%253D9552000%26init%3D0-233%26type%3Dvideo%252Fwebm%253B%2Bcodecs%253D%2522vp9%2522%26clen%3D1809613%26bitrate%3D216971%2Csize%3D256x108%26fps%3D15%26index%3D673-1028%26lmt%3D1390819917152885%26itag%3D160%26url%3Dhttp%253A%252F%252Fr15---sn-1gi7zn7y.googlevideo.com%252Fvideoplayback%253Fsver%253D3%2526itag%253D160%2526mm%253D31%2526gir%253Dyes%2526ipbits%253D0%2526dur%253D131.297%2526id%253Do-AK4tbFNn-kBD6bAyrHwY6rWPQHu9AQfudTA7ZQ0hD3D7%2526lmt%253D1390819917152885%2526ms%253Dau%2526mv%253Dm%2526mt%253D1410256308%2526key%253Dyt5%2526clen%253D1650879%2526source%253Dyoutube%2526ip%253D193.5.216.83%2526expire%253D1410278029%2526upn%253DyZGeJGScHyk%2526sparams%253Dclen%25252Cdur%25252Cgir%25252Cid%25252Cinitcwndbps%25252Cip%25252Cipbits%25252Citag%25252Clmt%25252Cmm%25252Cms%25252Cmv%25252Csource%25252Cupn%25252Cexpire%2526signature%253D2CD0DCD507BB29E91DACC6E0D33D16BADE3FBFF6.352F1B5C759E242BDB2CB224539C9091A395C8B6%2526fexp%253D927622%25252C931983%25252C932404%25252C932623%25252C934024%25252C934030%25252C936117%25252C938644%25252C940215%25252C946013%25252C953801%25252C955100%2526initcwndbps%253D9552000%26init%3D0-672%26type%3Dvideo%252Fmp4%253B%2Bcodecs%253D%2522avc1.4d400c%2522%26clen%3D1650879%26bitrate%3D114170%2Cindex%3D592-791%26lmt%3D1390819934156430%26itag%3D140%26url%3Dhttp%253A%252F%252Fr15---sn-1gi7zn7y.googlevideo.com%252Fvideoplayback%253Fsver%253D3%2526itag%253D140%2526mm%253D31%2526gir%253Dyes%2526ipbits%253D0%2526dur%253D131.355%2526id%253Do-AK4tbFNn-kBD6bAyrHwY6rWPQHu9AQfudTA7ZQ0hD3D7%2526lmt%253D1390819934156430%2526ms%253Dau%2526mv%253Dm%2526mt%253D1410256308%2526key%253Dyt5%2526clen%253D2086905%2526source%253Dyoutube%2526ip%253D193.5.216.83%2526expire%253D1410278029%2526upn%253DyZGeJGScHyk%2526sparams%253Dclen%25252Cdur%25252Cgir%25252Cid%25252Cinitcwndbps%25252Cip%25252Cipbits%25252Citag%25252Clmt%25252Cmm%25252Cms%25252Cmv%25252Csource%25252Cupn%25252Cexpire%2526signature%253D4ABF97EE6F645B8979EA3405ABF725B2D9B6A398.E4DEC7B30CF7F53C4861DA7A9139623BAC08ECE3%2526fexp%253D927622%25252C931983%25252C932404%25252C932623%25252C934024%25252C934030%25252C936117%25252C938644%25252C940215%25252C946013%25252C953801%25252C955100%2526initcwndbps%253D9552000%26init%3D0-591%26type%3Daudio%252Fmp4%253B%2Bcodecs%253D%2522mp4a.40.2%2522%26clen%3D2086905%26bitrate%3D127845%2Cfps%3D1%26index%3D4452-4685%26lmt%3D1407639705686075%26itag%3D171%26url%3Dhttp%253A%252F%252Fr15---sn-1gi7zn7y.googlevideo.com%252Fvideoplayback%253Fsver%253D3%2526itag%253D171%2526mm%253D31%2526gir%253Dyes%2526ipbits%253D0%2526dur%253D131.302%2526id%253Do-AK4tbFNn-kBD6bAyrHwY6rWPQHu9AQfudTA7ZQ0hD3D7%2526lmt%253D1407639705686075%2526ms%253Dau%2526mv%253Dm%2526mt%253D1410256308%2526key%253Dyt5%2526clen%253D1706190%2526source%253Dyoutube%2526ip%253D193.5.216.83%2526expire%253D1410278029%2526upn%253DyZGeJGScHyk%2526sparams%253Dclen%25252Cdur%25252Cgir%25252Cid%25252Cinitcwndbps%25252Cip%25252Cipbits%25252Citag%25252Clmt%25252Cmm%25252Cms%25252Cmv%25252Csource%25252Cupn%25252Cexpire%2526signature%253DEA83C370E0265810E49119F92DCC3B16893C6B7E.CFBE436990ED46047F83342EC4D2C2805593B538%2526fexp%253D927622%25252C931983%25252C932404%25252C932623%25252C934024%25252C934030%25252C936117%25252C938644%25252C940215%25252C946013%25252C953801%25252C955100%2526initcwndbps%253D9552000%26init%3D0-4451%26type%3Daudio%252Fwebm%253B%2Bcodecs%253D%2522vorbis%2522%26clen%3D1706190%26bitrate%3D117641&thumbnail_url=https%3A%2F%2Fi.ytimg.com%2Fvi%2FzKovmts2KSk%2Fdefault.jpg&eventid=Lc4OVOTAM4e58gPmnIGYDA&vq=auto&focEnabled=1&video_id=zKovmts2KSk&tmi=1&use_cipher_signature=False&keywords=D3%2Cteaser%2C1080p&idpj=-8×tamp=1410256429&muted=0&video_verticals=%5B41%2C+622%2C+3%2C+935%5D&csi_page_type=embed&iurlsd=https%3A%2F%2Fi.ytimg.com%2Fvi%2FzKovmts2KSk%2Fsddefault.jpg&allow_embed=1&fexp=927622%2C931983%2C932404%2C932623%2C934024%2C934030%2C936117%2C938644%2C940215%2C946013%2C953801%2C955100&length_seconds=132&view_count=256303&iurl=https%3A%2F%2Fi.ytimg.com%2Fvi%2FzKovmts2KSk%2Fhqdefault.jpg&pltype=contentugc&storyboard_spec=https%3A%2F%2Fi.ytimg.com%2Fsb%2FzKovmts2KSk%2Fstoryboard3_L%24L%2F%24N.jpg%7C48%2327%23100%2310%2310%230%23default%23thR8Ii-F2PQF_S650FyNNW-vEqY%7C106%2345%2367%2310%2310%232000%23M%24M%23YsPVlaPKnzWnON-tbuDmUyBg4v0%7C212%2390%2367%235%235%232000%23M%24M%23t38-TYr4aCOpXm_2XzdGTzP5ejM&ptk=Blizzard&loudness=-20.1499996185&plid=AAUCnumLVVJhAF4B&aid=P9dxBYtW3fg&ldpj=-22&title=Diablo+III+Teaser&dashmpd=http%3A%2F%2Fmanifest.googlevideo.com%2Fapi%2Fmanifest%2Fdash%2Fkey%2Fyt5%2Fitag%2F0%2Fmm%2F31%2Fip%2F193.5.216.83%2Fsource%2Fyoutube%2Fexpire%2F1410278029%2Fupn%2Fjgm8hC6p7aY%2Fipbits%2F0%2Fsparams%2Fas%252Cid%252Cip%252Cipbits%252Citag%252Cmm%252Cms%252Cmv%252Cplayback_host%252Csource%252Cexpire%2Fid%2Fo-AK4tbFNn-kBD6bAyrHwY6rWPQHu9AQfudTA7ZQ0hD3D7%2Fplayback_host%2Fr15---sn-1gi7zn7y.googlevideo.com%2Ffexp%2F927622%252C931983%252C932404%252C932623%252C934024%252C934030%252C936117%252C938644%252C940215%252C946013%252C953801%252C955100%2Fms%2Fau%2Fsver%2F3%2Fmt%2F1410256308%2Fas%2Ffmp4_audio_clear%252Cwebm_audio_clear%252Cfmp4_sd_hd_clear%252Cwebm_sd_hd_clear%252Cwebm2_sd_hd_clear%2Fmv%2Fm%2Fsignature%2F23245B4C4D8A70B1F0324CA302E9D533668A21DE.944F6713784B7D0C608E1447CDAD1BF8D8CFF114&no_get_video_log=1&vid=zKovmts2KSk&enablecsi=1&iurlhq=https%3A%2F%2Fi.ytimg.com%2Fvi%2FzKovmts2KSk%2Fhqdefault.jpg&iurlmaxres=https%3A%2F%2Fi.ytimg.com%2Fvi%2FzKovmts2KSk%2Fmaxresdefault.jpg&rmktEnabled=1&dash=1&author=Blizzard+Entertainment&fmt_list=22%2F1280x720%2F9%2F0%2F115%2C43%2F640x360%2F99%2F0%2F0%2C18%2F640x360%2F9%2F0%2F115%2C5%2F320x240%2F7%2F0%2F0%2C36%2F320x240%2F99%2F1%2F0%2C17%2F176x144%2F99%2F1%2F0&c=WEB&allow_ratings=1&url_encoded_fmt_stream_map=type%3Dvideo%252Fmp4%253B%2Bcodecs%253D%2522avc1.64001F%252C%2Bmp4a.40.2%2522%26quality%3Dhd720%26fallback_host%3Dtc.v18.cache6.googlevideo.com%26itag%3D22%26url%3Dhttp%253A%252F%252Fr15---sn-1gi7zn7y.googlevideo.com%252Fvideoplayback%253Fsver%253D3%2526itag%253D22%2526mm%253D31%2526ip%253D193.5.216.83%2526ratebypass%253Dyes%2526ipbits%253D0%2526id%253Do-AK4tbFNn-kBD6bAyrHwY6rWPQHu9AQfudTA7ZQ0hD3D7%2526ms%253Dau%2526mv%253Dm%2526mt%253D1410256308%2526key%253Dyt5%2526source%253Dyoutube%2526expire%253D1410278029%2526upn%253Dfe6r2N-wlh8%2526sparams%253Did%25252Cinitcwndbps%25252Cip%25252Cipbits%25252Citag%25252Cmm%25252Cms%25252Cmv%25252Cratebypass%25252Csource%25252Cupn%25252Cexpire%2526signature%253DD9D05DDDAEFF183144AB5FE471E82643CA4B25EB.DBE7EC8286DD6A6C4503BB4F1A28F3F1A2566FB1%2526fexp%253D927622%25252C931983%25252C932404%25252C932623%25252C934024%25252C934030%25252C936117%25252C938644%25252C940215%25252C946013%25252C953801%25252C955100%2526initcwndbps%253D9552000%2Ctype%3Dvideo%252Fwebm%253B%2Bcodecs%253D%2522vp8.0%252C%2Bvorbis%2522%26quality%3Dmedium%26fallback_host%3Dtc.v11.cache2.googlevideo.com%26itag%3D43%26url%3Dhttp%253A%252F%252Fr15---sn-1gi7zn7y.googlevideo.com%252Fvideoplayback%253Fsver%253D3%2526itag%253D43%2526mm%253D31%2526ip%253D193.5.216.83%2526ratebypass%253Dyes%2526ipbits%253D0%2526id%253Do-AK4tbFNn-kBD6bAyrHwY6rWPQHu9AQfudTA7ZQ0hD3D7%2526ms%253Dau%2526mv%253Dm%2526mt%253D1410256308%2526key%253Dyt5%2526source%253Dyoutube%2526expire%253D1410278029%2526upn%253Dfe6r2N-wlh8%2526sparams%253Did%25252Cinitcwndbps%25252Cip%25252Cipbits%25252Citag%25252Cmm%25252Cms%25252Cmv%25252Cratebypass%25252Csource%25252Cupn%25252Cexpire%2526signature%253D879488316BF0B7BA35367B94AF560A5298EFEDAB.40BD546C281DBC16A898B4EBF52E61BEDA1E95BE%2526fexp%253D927622%25252C931983%25252C932404%25252C932623%25252C934024%25252C934030%25252C936117%25252C938644%25252C940215%25252C946013%25252C953801%25252C955100%2526initcwndbps%253D9552000%2Ctype%3Dvideo%252Fmp4%253B%2Bcodecs%253D%2522avc1.42001E%252C%2Bmp4a.40.2%2522%26quality%3Dmedium%26fallback_host%3Dtc.v17.cache5.googlevideo.com%26itag%3D18%26url%3Dhttp%253A%252F%252Fr15---sn-1gi7zn7y.googlevideo.com%252Fvideoplayback%253Fsver%253D3%2526itag%253D18%2526mm%253D31%2526ip%253D193.5.216.83%2526ratebypass%253Dyes%2526ipbits%253D0%2526id%253Do-AK4tbFNn-kBD6bAyrHwY6rWPQHu9AQfudTA7ZQ0hD3D7%2526ms%253Dau%2526mv%253Dm%2526mt%253D1410256308%2526key%253Dyt5%2526source%253Dyoutube%2526expire%253D1410278029%2526upn%253Dfe6r2N-wlh8%2526sparams%253Did%25252Cinitcwndbps%25252Cip%25252Cipbits%25252Citag%25252Cmm%25252Cms%25252Cmv%25252Cratebypass%25252Csource%25252Cupn%25252Cexpire%2526signature%253DF77DFBA1519B8FED005FD7AC35316D2D650693DD.0498BEB47CE8487B5782492A8C1E15C525B0D59B%2526fexp%253D927622%25252C931983%25252C932404%25252C932623%25252C934024%25252C934030%25252C936117%25252C938644%25252C940215%25252C946013%25252C953801%25252C955100%2526initcwndbps%253D9552000%2Ctype%3Dvideo%252Fx-flv%26quality%3Dsmall%26fallback_host%3Dtc.v2.cache4.googlevideo.com%26itag%3D5%26url%3Dhttp%253A%252F%252Fr15---sn-1gi7zn7y.googlevideo.com%252Fvideoplayback%253Fsver%253D3%2526itag%253D5%2526mm%253D31%2526ip%253D193.5.216.83%2526ipbits%253D0%2526id%253Do-AK4tbFNn-kBD6bAyrHwY6rWPQHu9AQfudTA7ZQ0hD3D7%2526ms%253Dau%2526mv%253Dm%2526mt%253D1410256308%2526key%253Dyt5%2526source%253Dyoutube%2526expire%253D1410278029%2526upn%253Dfe6r2N-wlh8%2526sparams%253Did%25252Cinitcwndbps%25252Cip%25252Cipbits%25252Citag%25252Cmm%25252Cms%25252Cmv%25252Csource%25252Cupn%25252Cexpire%2526signature%253D66DF986A815202C96EDE4A22A49A4B1743831519.F9D12491EE118FF4B6E590B1A97E141F04F5CD13%2526fexp%253D927622%25252C931983%25252C932404%25252C932623%25252C934024%25252C934030%25252C936117%25252C938644%25252C940215%25252C946013%25252C953801%25252C955100%2526initcwndbps%253D9552000%2Ctype%3Dvideo%252F3gpp%253B%2Bcodecs%253D%2522mp4v.20.3%252C%2Bmp4a.40.2%2522%26quality%3Dsmall%26fallback_host%3Dtc.v7.cache6.googlevideo.com%26itag%3D36%26url%3Dhttp%253A%252F%252Fr15---sn-1gi7zn7y.googlevideo.com%252Fvideoplayback%253Fsver%253D3%2526itag%253D36%2526mm%253D31%2526ip%253D193.5.216.83%2526ipbits%253D0%2526id%253Do-AK4tbFNn-kBD6bAyrHwY6rWPQHu9AQfudTA7ZQ0hD3D7%2526ms%253Dau%2526mv%253Dm%2526mt%253D1410256308%2526key%253Dyt5%2526source%253Dyoutube%2526expire%253D1410278029%2526upn%253Dfe6r2N-wlh8%2526sparams%253Did%25252Cinitcwndbps%25252Cip%25252Cipbits%25252Citag%25252Cmm%25252Cms%25252Cmv%25252Csource%25252Cupn%25252Cexpire%2526signature%253DA8A7C3911558E3E596C73C3D08EDB8359B189A42.EBBA84D7DDF2D4020DF48E731D4661D6B37BE98D%2526fexp%253D927622%25252C931983%25252C932404%25252C932623%25252C934024%25252C934030%25252C936117%25252C938644%25252C940215%25252C946013%25252C953801%25252C955100%2526initcwndbps%253D9552000%2Ctype%3Dvideo%252F3gpp%253B%2Bcodecs%253D%2522mp4v.20.3%252C%2Bmp4a.40.2%2522%26quality%3Dsmall%26fallback_host%3Dtc.v21.cache3.googlevideo.com%26itag%3D17%26url%3Dhttp%253A%252F%252Fr15---sn-1gi7zn7y.googlevideo.com%252Fvideoplayback%253Fsver%253D3%2526itag%253D17%2526mm%253D31%2526ip%253D193.5.216.83%2526ipbits%253D0%2526id%253Do-AK4tbFNn-kBD6bAyrHwY6rWPQHu9AQfudTA7ZQ0hD3D7%2526ms%253Dau%2526mv%253Dm%2526mt%253D1410256308%2526key%253Dyt5%2526source%253Dyoutube%2526expire%253D1410278029%2526upn%253Dfe6r2N-wlh8%2526sparams%253Did%25252Cinitcwndbps%25252Cip%25252Cipbits%25252Citag%25252Cmm%25252Cms%25252Cmv%25252Csource%25252Cupn%25252Cexpire%2526signature%253D164BDF45841C51534738AA6D81D4090F1063534E.D47701993FD7BB4DB8A85DCC4199881B07B8D91B%2526fexp%253D927622%25252C931983%25252C932404%25252C932623%25252C934024%25252C934030%25252C936117%25252C938644%25252C940215%25252C946013%25252C953801%25252C955100%2526initcwndbps%253D9552000&cl=74803881&oid=_LWWINuQopYxk9obaGY6Ew&ytfocEnabled=1&token=vjVQa1PpcFNSfqKJVuBmLj9XqL2KZXnqUs_6ZWetRsQ%3D&watermark=%2Chttps%3A%2F%2Fs.ytimg.com%2Fyts%2Fimg%2Fwatermark%2Fyoutube_watermark-vflHX6b6E.png%2Chttps%3A%2F%2Fs.ytimg.com%2Fyts%2Fimg%2Fwatermark%2Fyoutube_hd_watermark-vflAzLcD6.png&iurlmq=https%3A%2F%2Fi.ytimg.com%2Fvi%2FzKovmts2KSk%2Fmqdefault.jpg&uid=3GriadTkHBnfgd2UFETGOA", + "headers" : { + "Content-Type" : "application\/x-www-form-urlencoded", + "P3P" : "CP=\"This is not a P3P policy! See http:\/\/support.google.com\/accounts\/bin\/answer.py?answer=151657&hl=en for more info.\"", + "Alternate-Protocol" : "443:quic", + "Set-Cookie" : "YSC=yEamrWNB2Lc; path=\/; domain=.youtube.com; httponly", + "Server" : "gwiseguy\/2.0", + "X-XSS-Protection" : "1; mode=block; report=https:\/\/www.google.com\/appserve\/security-bugs\/log\/youtube", + "Transfer-Encoding" : "Identity", + "Expires" : "Tue, 27 Apr 1971 19:44:06 EST", + "Cache-Control" : "no-store", + "Date" : "Tue, 09 Sep 2014 09:53:49 GMT", + "X-Content-Type-Options" : "nosniff", + "X-Frame-Options" : "SAMEORIGIN" + }, + "method" : "GET", + "status" : 200, + "uri" : "https:\/\/www.youtube.com\/get_video_info?el=embedded&hl=en&ps=default&video_id=zKovmts2KSk" + } +] \ No newline at end of file diff --git a/XCDYouTubeKit Tests/XCDYouTubeProtectedVideosTestCase.m b/XCDYouTubeKit Tests/XCDYouTubeProtectedVideosTestCase.m index fb791258..3c41d4dc 100644 --- a/XCDYouTubeKit Tests/XCDYouTubeProtectedVideosTestCase.m +++ b/XCDYouTubeKit Tests/XCDYouTubeProtectedVideosTestCase.m @@ -31,6 +31,26 @@ - (void) tearDown [NSURLRequest setAllowsAnyHTTPSCertificate:NO forHost:@"www.youtube.com"]; } +- (void) testAgeRestrictedVideo +{ + TRVSMonitor *monitor = [TRVSMonitor monitor]; + [[XCDYouTubeClient defaultClient] getVideoWithIdentifier:@"zKovmts2KSk" completionHandler:^(XCDYouTubeVideo *video, NSError *error) + { + XCTAssertNil(error); + XCTAssertNotNil(video.title); + XCTAssertNotNil(video.smallThumbnailURL); + XCTAssertNotNil(video.mediumThumbnailURL); + XCTAssertNotNil(video.largeThumbnailURL); + XCTAssertTrue(video.streamURLs.count > 0); + XCTAssertTrue(video.duration > 0); + [video.streamURLs enumerateKeysAndObjectsUsingBlock:^(NSNumber *key, NSURL *streamURL, BOOL *stop) { + XCTAssertTrue([streamURL.query rangeOfString:@"signature="].location != NSNotFound); + }]; + [monitor signal]; + }]; + XCTAssertTrue([monitor waitWithTimeout:10]); +} + - (void) testProtectedVEVOVideo { TRVSMonitor *monitor = [TRVSMonitor monitor]; From c3b6985e85b90092efba0e6a5871f87c1a208d15 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ce=CC=81dric=20Luthi?= Date: Wed, 10 Sep 2014 10:45:53 +0200 Subject: [PATCH 10/57] Better fix for invalid certificates in unit tests --- XCDYouTubeKit Tests/XCDYouTubeKitTestCase.m | 14 +++++++++++++ .../XCDYouTubeProtectedVideosTestCase.m | 20 ------------------- 2 files changed, 14 insertions(+), 20 deletions(-) diff --git a/XCDYouTubeKit Tests/XCDYouTubeKitTestCase.m b/XCDYouTubeKit Tests/XCDYouTubeKitTestCase.m index 06a6adac..3c67ba2a 100644 --- a/XCDYouTubeKit Tests/XCDYouTubeKitTestCase.m +++ b/XCDYouTubeKit Tests/XCDYouTubeKitTestCase.m @@ -8,6 +8,10 @@ @interface XCTest (Private) - (void) setUpTestWithSelector:(SEL)selector; @end +@interface NSURLRequest (Private) ++ (void) setAllowsAnyHTTPSCertificate:(BOOL)allowsAnyHTTPSCertificate forHost:(NSString *)host; +@end + @interface XCDYouTubeKitTestCase () @property NSURL *cassetteURL; @end @@ -16,6 +20,16 @@ @interface XCDYouTubeKitTestCase () @implementation XCDYouTubeKitTestCase ++ (void) setUp +{ + NSDictionary *environment = NSProcessInfo.processInfo.environment; + if ([environment[@"VCR_CASSETTES_DIRECTORY"] boolValue] || [environment[@"ONLINE_TESTS"] boolValue]) + { + [NSURLRequest setAllowsAnyHTTPSCertificate:YES forHost:@"www.youtube.com"]; + [NSURLRequest setAllowsAnyHTTPSCertificate:YES forHost:@"s.ytimg.com"]; + } +} + // When running tests with the `ONLINE_TESTS` environment variable, tests whose // selector ends with `_offline` are not executed. // Also, VCR is not used at all, neither for recording nor for replaying. diff --git a/XCDYouTubeKit Tests/XCDYouTubeProtectedVideosTestCase.m b/XCDYouTubeKit Tests/XCDYouTubeProtectedVideosTestCase.m index 3c41d4dc..4e3a6317 100644 --- a/XCDYouTubeKit Tests/XCDYouTubeProtectedVideosTestCase.m +++ b/XCDYouTubeKit Tests/XCDYouTubeProtectedVideosTestCase.m @@ -6,31 +6,11 @@ #import -@interface NSURLRequest (Private) -+ (void) setAllowsAnyHTTPSCertificate:(BOOL)allowsAnyHTTPSCertificate forHost:(NSString *)host; -@end - @interface XCDYouTubeProtectedVideosTestCase : XCDYouTubeKitTestCase @end @implementation XCDYouTubeProtectedVideosTestCase -- (void) setUp -{ - [super setUp]; - - if (NSProcessInfo.processInfo.environment[@"VCR_CASSETTES_DIRECTORY"]) - [NSURLRequest setAllowsAnyHTTPSCertificate:YES forHost:@"www.youtube.com"]; -} - -- (void) tearDown -{ - [super tearDown]; - - if (NSProcessInfo.processInfo.environment[@"VCR_CASSETTES_DIRECTORY"]) - [NSURLRequest setAllowsAnyHTTPSCertificate:NO forHost:@"www.youtube.com"]; -} - - (void) testAgeRestrictedVideo { TRVSMonitor *monitor = [TRVSMonitor monitor]; From 3a7a4bdb0225f97717ea9f651d54c85338b2e893 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ce=CC=81dric=20Luthi?= Date: Wed, 10 Sep 2014 10:51:19 +0200 Subject: [PATCH 11/57] Add script phase for deleting *.gcda files This gets rid of the "profiling:invalid arc tag" logs when running unit tests * Add script phase in both iOS and OS X unit tests targets * See http://stackoverflow.com/questions/22519530/dozens-of-profilinginvalid-arc-tag-when-running-code-coverage-in-xcode-5/22976101#22976101 --- XCDYouTubeKit.xcodeproj/project.pbxproj | 30 +++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/XCDYouTubeKit.xcodeproj/project.pbxproj b/XCDYouTubeKit.xcodeproj/project.pbxproj index 75e377ed..ac28fcda 100644 --- a/XCDYouTubeKit.xcodeproj/project.pbxproj +++ b/XCDYouTubeKit.xcodeproj/project.pbxproj @@ -367,6 +367,7 @@ C20F579718817D5400EDBFB0 /* Sources */, C20F579818817D5400EDBFB0 /* Frameworks */, C20F579918817D5400EDBFB0 /* Resources */, + C249AAA519C045C400BA17AE /* Delete *.gcda files */, ); buildRules = ( ); @@ -420,6 +421,7 @@ C2DD4D9E191FFDE100AF6F85 /* Sources */, C2DD4D9F191FFDE100AF6F85 /* Frameworks */, C2DD4DA0191FFDE100AF6F85 /* Resources */, + C249AAA619C0462700BA17AE /* Delete *.gcda files */, ); buildRules = ( ); @@ -503,6 +505,34 @@ shellPath = /bin/sh; shellScript = "\"${SRCROOT}/Scripts/create-coveralls-script.sh\" "; }; + C249AAA519C045C400BA17AE /* Delete *.gcda files */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Delete *.gcda files"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "find \"${CONFIGURATION_TEMP_DIR}\" -name \"*.gcda\" -delete"; + }; + C249AAA619C0462700BA17AE /* Delete *.gcda files */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Delete *.gcda files"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "find \"${CONFIGURATION_TEMP_DIR}\" -name \"*.gcda\" -delete"; + }; /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ From fea6e3b75936928adf7001904f711259f011054d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ce=CC=81dric=20Luthi?= Date: Wed, 10 Sep 2014 11:24:16 +0200 Subject: [PATCH 12/57] Fix typo in CHANGELOG --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b9fe65b9..c52e7894 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,7 +13,7 @@ #### Version 2.0.0 * Project renamed to `XCDYouTubeKit`. -* Support for protected video. (#6, #11, #12, #21, #27, #31, #33) +* Support for protected videos. (#6, #11, #12, #21, #27, #31, #33) * OS X compatibility thanks to the new `XCDYouTubeClient` class. (#14, #18) * OS X dynamic framework target * Support for live videos. (#34) From 632eabb416d8a3c4bb5dad8d5375571f2fa7416a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ce=CC=81dric=20Luthi?= Date: Wed, 10 Sep 2014 11:25:38 +0200 Subject: [PATCH 13/57] Support for age restricted videos --- CHANGELOG.md | 4 + .../testAgeRestrictedVEVOVideo.json | 97 +++++++++++++++++++ .../XCDYouTubeProtectedVideosTestCase.m | 20 ++++ XCDYouTubeKit/XCDYouTubeVideoOperation.m | 29 ++++-- XCDYouTubeKit/XCDYouTubeVideoWebpage.h | 2 + XCDYouTubeKit/XCDYouTubeVideoWebpage.m | 13 +++ 6 files changed, 159 insertions(+), 6 deletions(-) create mode 100644 XCDYouTubeKit Tests/Cassettes/XCDYouTubeProtectedVideosTestCase/testAgeRestrictedVEVOVideo.json diff --git a/CHANGELOG.md b/CHANGELOG.md index c52e7894..345f6dcd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +#### Version 2.0.3 + +* Support for age restricted videos. + #### Version 2.0.2 * Fixed errors on protected videos. (#52) diff --git a/XCDYouTubeKit Tests/Cassettes/XCDYouTubeProtectedVideosTestCase/testAgeRestrictedVEVOVideo.json b/XCDYouTubeKit Tests/Cassettes/XCDYouTubeProtectedVideosTestCase/testAgeRestrictedVEVOVideo.json new file mode 100644 index 00000000..cc0cd275 --- /dev/null +++ b/XCDYouTubeKit Tests/Cassettes/XCDYouTubeProtectedVideosTestCase/testAgeRestrictedVEVOVideo.json @@ -0,0 +1,97 @@ +[ + { + "body" : "idpj=-2&avg_rating=4.71935282752&ad_flags=0&fmt_list=22%2F1280x720%2F9%2F0%2F115%2C43%2F640x360%2F99%2F0%2F0%2C18%2F640x360%2F9%2F0%2F115%2C5%2F320x240%2F7%2F0%2F0%2C36%2F320x240%2F99%2F1%2F0%2C17%2F176x144%2F99%2F1%2F0&iv_load_policy=1&host_language=en&video_verticals=%5B35%2C+1021%2C+3%5D&length_seconds=419&iurlmq=https%3A%2F%2Fi.ytimg.com%2Fvi%2F07FYdnEawAQ%2Fmqdefault.jpg&focEnabled=1&tmi=1&use_cipher_signature=True&dash=1&thumbnail_url=https%3A%2F%2Fi.ytimg.com%2Fvi%2F07FYdnEawAQ%2Fdefault.jpg&baseUrl=https%3A%2F%2Fgoogleads.g.doubleclick.net%2Fpagead%2Fviewthroughconversion%2F962985656%2F&midroll_prefetch_size=1&mpu=True&allowed_ads=%5B0%2C+1%2C+4%5D&video_id=07FYdnEawAQ&url_encoded_fmt_stream_map=type%3Dvideo%252Fmp4%253B%2Bcodecs%253D%2522avc1.64001F%252C%2Bmp4a.40.2%2522%26url%3Dhttp%253A%252F%252Fr9---sn-1gi7zn7l.googlevideo.com%252Fvideoplayback%253Fitag%253D22%2526ratebypass%253Dyes%2526fexp%253D927622%25252C931983%25252C932404%25252C932623%25252C934024%25252C934030%25252C936117%25252C938644%25252C940215%25252C946013%25252C947209%25252C953801%25252C955100%2526initcwndbps%253D7887000%2526key%253Dyt5%2526source%253Dyoutube%2526upn%253DvRt0WRiX6N0%2526id%253Do-ANnfoW7VWvxVYQTOGvBgDQxyUIThLwYAR3wFGqXjjvOV%2526sver%253D3%2526ipbits%253D0%2526mm%253D31%2526expire%253D1410362304%2526ip%253D193.5.216.83%2526ms%253Dau%2526mt%253D1410340588%2526mv%253Dm%2526sparams%253Did%25252Cinitcwndbps%25252Cip%25252Cipbits%25252Citag%25252Cmm%25252Cms%25252Cmv%25252Cratebypass%25252Csource%25252Cupn%25252Cexpire%26fallback_host%3Dtc.v7.cache6.googlevideo.com%26itag%3D22%26quality%3Dhd720%26s%3DCD315B3E96D8F649A8E980D67F0F094B94DC5DE8E.40C0CD32DC4576802BD35D3A4F0A01B72868D5E888%2Ctype%3Dvideo%252Fwebm%253B%2Bcodecs%253D%2522vp8.0%252C%2Bvorbis%2522%26url%3Dhttp%253A%252F%252Fr9---sn-1gi7zn7l.googlevideo.com%252Fvideoplayback%253Fitag%253D43%2526ratebypass%253Dyes%2526fexp%253D927622%25252C931983%25252C932404%25252C932623%25252C934024%25252C934030%25252C936117%25252C938644%25252C940215%25252C946013%25252C947209%25252C953801%25252C955100%2526initcwndbps%253D7887000%2526key%253Dyt5%2526source%253Dyoutube%2526upn%253DvRt0WRiX6N0%2526id%253Do-ANnfoW7VWvxVYQTOGvBgDQxyUIThLwYAR3wFGqXjjvOV%2526sver%253D3%2526ipbits%253D0%2526mm%253D31%2526expire%253D1410362304%2526ip%253D193.5.216.83%2526ms%253Dau%2526mt%253D1410340588%2526mv%253Dm%2526sparams%253Did%25252Cinitcwndbps%25252Cip%25252Cipbits%25252Citag%25252Cmm%25252Cms%25252Cmv%25252Cratebypass%25252Csource%25252Cupn%25252Cexpire%26fallback_host%3Dtc.v18.cache6.googlevideo.com%26itag%3D43%26quality%3Dmedium%26s%3DD7E1D92BCFAED662D0AC8B38793A95803FA5A0F16.AA00E076D455544EC57DEA8E3604B3EC7EC0845BBB%2Ctype%3Dvideo%252Fmp4%253B%2Bcodecs%253D%2522avc1.42001E%252C%2Bmp4a.40.2%2522%26url%3Dhttp%253A%252F%252Fr9---sn-1gi7zn7l.googlevideo.com%252Fvideoplayback%253Fitag%253D18%2526ratebypass%253Dyes%2526fexp%253D927622%25252C931983%25252C932404%25252C932623%25252C934024%25252C934030%25252C936117%25252C938644%25252C940215%25252C946013%25252C947209%25252C953801%25252C955100%2526initcwndbps%253D7887000%2526key%253Dyt5%2526source%253Dyoutube%2526upn%253DvRt0WRiX6N0%2526id%253Do-ANnfoW7VWvxVYQTOGvBgDQxyUIThLwYAR3wFGqXjjvOV%2526sver%253D3%2526ipbits%253D0%2526mm%253D31%2526expire%253D1410362304%2526ip%253D193.5.216.83%2526ms%253Dau%2526mt%253D1410340588%2526mv%253Dm%2526sparams%253Did%25252Cinitcwndbps%25252Cip%25252Cipbits%25252Citag%25252Cmm%25252Cms%25252Cmv%25252Cratebypass%25252Csource%25252Cupn%25252Cexpire%26fallback_host%3Dtc.v6.cache8.googlevideo.com%26itag%3D18%26quality%3Dmedium%26s%3DF71D7DA6A45E84B4199F0345759399B5A07B433A8.23920D24F6F25A75B871EF32A39BA0B4746CEA6222%2Ctype%3Dvideo%252Fx-flv%26url%3Dhttp%253A%252F%252Fr9---sn-1gi7zn7l.googlevideo.com%252Fvideoplayback%253Fitag%253D5%2526fexp%253D927622%25252C931983%25252C932404%25252C932623%25252C934024%25252C934030%25252C936117%25252C938644%25252C940215%25252C946013%25252C947209%25252C953801%25252C955100%2526initcwndbps%253D7887000%2526key%253Dyt5%2526source%253Dyoutube%2526upn%253DvRt0WRiX6N0%2526id%253Do-ANnfoW7VWvxVYQTOGvBgDQxyUIThLwYAR3wFGqXjjvOV%2526sver%253D3%2526ipbits%253D0%2526mm%253D31%2526expire%253D1410362304%2526ip%253D193.5.216.83%2526ms%253Dau%2526mt%253D1410340588%2526mv%253Dm%2526sparams%253Did%25252Cinitcwndbps%25252Cip%25252Cipbits%25252Citag%25252Cmm%25252Cms%25252Cmv%25252Csource%25252Cupn%25252Cexpire%26fallback_host%3Dtc.v17.cache1.googlevideo.com%26itag%3D5%26quality%3Dsmall%26s%3D24DCC65E12F4C621A2C9F8BE3233C3CC4A239DD7E.1E8A232DADB413A1CA4C5F49E9DD23C74420BFA999%2Ctype%3Dvideo%252F3gpp%253B%2Bcodecs%253D%2522mp4v.20.3%252C%2Bmp4a.40.2%2522%26url%3Dhttp%253A%252F%252Fr9---sn-1gi7zn7l.googlevideo.com%252Fvideoplayback%253Fitag%253D36%2526fexp%253D927622%25252C931983%25252C932404%25252C932623%25252C934024%25252C934030%25252C936117%25252C938644%25252C940215%25252C946013%25252C947209%25252C953801%25252C955100%2526initcwndbps%253D7887000%2526key%253Dyt5%2526source%253Dyoutube%2526upn%253DvRt0WRiX6N0%2526id%253Do-ANnfoW7VWvxVYQTOGvBgDQxyUIThLwYAR3wFGqXjjvOV%2526sver%253D3%2526ipbits%253D0%2526mm%253D31%2526expire%253D1410362304%2526ip%253D193.5.216.83%2526ms%253Dau%2526mt%253D1410340588%2526mv%253Dm%2526sparams%253Did%25252Cinitcwndbps%25252Cip%25252Cipbits%25252Citag%25252Cmm%25252Cms%25252Cmv%25252Csource%25252Cupn%25252Cexpire%26fallback_host%3Dtc.v9.cache7.googlevideo.com%26itag%3D36%26quality%3Dsmall%26s%3D4289427F2403CC86381E0760DC72E38B1FC7ED9E1.D971A51A02C035571428EFDF8C9A7BDEEED388CBBB%2Ctype%3Dvideo%252F3gpp%253B%2Bcodecs%253D%2522mp4v.20.3%252C%2Bmp4a.40.2%2522%26url%3Dhttp%253A%252F%252Fr9---sn-1gi7zn7l.googlevideo.com%252Fvideoplayback%253Fitag%253D17%2526fexp%253D927622%25252C931983%25252C932404%25252C932623%25252C934024%25252C934030%25252C936117%25252C938644%25252C940215%25252C946013%25252C947209%25252C953801%25252C955100%2526initcwndbps%253D7887000%2526key%253Dyt5%2526source%253Dyoutube%2526upn%253DvRt0WRiX6N0%2526id%253Do-ANnfoW7VWvxVYQTOGvBgDQxyUIThLwYAR3wFGqXjjvOV%2526sver%253D3%2526ipbits%253D0%2526mm%253D31%2526expire%253D1410362304%2526ip%253D193.5.216.83%2526ms%253Dau%2526mt%253D1410340588%2526mv%253Dm%2526sparams%253Did%25252Cinitcwndbps%25252Cip%25252Cipbits%25252Citag%25252Cmm%25252Cms%25252Cmv%25252Csource%25252Cupn%25252Cexpire%26fallback_host%3Dtc.v10.cache8.googlevideo.com%26itag%3D17%26quality%3Dsmall%26s%3D1F0CF5132F09F92488373FAC6E2EE0D6F30FE9C5D.FB0F83A16B843360C8F93D9D0345C5A3658F5E9555&mpvid=8zYeiihq-kfXAEXN&adaptive_fmts=itag%3D137%26init%3D0-710%26type%3Dvideo%252Fmp4%253B%2Bcodecs%253D%2522avc1.640028%2522%26url%3Dhttp%253A%252F%252Fr9---sn-1gi7zn7l.googlevideo.com%252Fvideoplayback%253Fitag%253D137%2526fexp%253D927622%25252C931983%25252C932404%25252C932623%25252C934024%25252C934030%25252C936117%25252C938644%25252C940215%25252C946013%25252C947209%25252C953801%25252C955100%2526initcwndbps%253D7887000%2526gir%253Dyes%2526key%253Dyt5%2526source%253Dyoutube%2526upn%253Dy346fy9WUkk%2526id%253Do-ANnfoW7VWvxVYQTOGvBgDQxyUIThLwYAR3wFGqXjjvOV%2526sver%253D3%2526ipbits%253D0%2526mm%253D31%2526dur%253D418.836%2526clen%253D109101107%2526expire%253D1410362304%2526lmt%253D1402636620154284%2526ip%253D193.5.216.83%2526ms%253Dau%2526mt%253D1410340588%2526mv%253Dm%2526sparams%253Dclen%25252Cdur%25252Cgir%25252Cid%25252Cinitcwndbps%25252Cip%25252Cipbits%25252Citag%25252Clmt%25252Cmm%25252Cms%25252Cmv%25252Csource%25252Cupn%25252Cexpire%26lmt%3D1402636620154284%26clen%3D109101107%26bitrate%3D4069845%26size%3D1920x1080%26fps%3D23%26index%3D711-1750%26s%3D5C3C1A1BCFFE09FE7A82C02570C90478152B121A7.4078CFB35132D689B6CD763C249D8BFDF4A5156999%2Citag%3D248%26init%3D0-234%26type%3Dvideo%252Fwebm%253B%2Bcodecs%253D%2522vp9%2522%26url%3Dhttp%253A%252F%252Fr9---sn-1gi7zn7l.googlevideo.com%252Fvideoplayback%253Fitag%253D248%2526fexp%253D927622%25252C931983%25252C932404%25252C932623%25252C934024%25252C934030%25252C936117%25252C938644%25252C940215%25252C946013%25252C947209%25252C953801%25252C955100%2526initcwndbps%253D7887000%2526gir%253Dyes%2526key%253Dyt5%2526source%253Dyoutube%2526upn%253Dy346fy9WUkk%2526id%253Do-ANnfoW7VWvxVYQTOGvBgDQxyUIThLwYAR3wFGqXjjvOV%2526sver%253D3%2526ipbits%253D0%2526mm%253D31%2526dur%253D418.919%2526clen%253D76306547%2526expire%253D1410362304%2526lmt%253D1392434445000295%2526ip%253D193.5.216.83%2526ms%253Dau%2526mt%253D1410340588%2526mv%253Dm%2526sparams%253Dclen%25252Cdur%25252Cgir%25252Cid%25252Cinitcwndbps%25252Cip%25252Cipbits%25252Citag%25252Clmt%25252Cmm%25252Cms%25252Cmv%25252Csource%25252Cupn%25252Cexpire%26lmt%3D1392434445000295%26clen%3D76306547%26bitrate%3D2234551%26size%3D1920x1080%26fps%3D1%26index%3D235-1715%26s%3D926F0D6FE6DA5C311D40037719A10E8CDC83EA68A.1CE9CEABC603BDFD6E2DE7AFE45B9C28BE9C5C8000%2Citag%3D136%26init%3D0-708%26type%3Dvideo%252Fmp4%253B%2Bcodecs%253D%2522avc1.4d401f%2522%26url%3Dhttp%253A%252F%252Fr9---sn-1gi7zn7l.googlevideo.com%252Fvideoplayback%253Fitag%253D136%2526fexp%253D927622%25252C931983%25252C932404%25252C932623%25252C934024%25252C934030%25252C936117%25252C938644%25252C940215%25252C946013%25252C947209%25252C953801%25252C955100%2526initcwndbps%253D7887000%2526gir%253Dyes%2526key%253Dyt5%2526source%253Dyoutube%2526upn%253Dy346fy9WUkk%2526id%253Do-ANnfoW7VWvxVYQTOGvBgDQxyUIThLwYAR3wFGqXjjvOV%2526sver%253D3%2526ipbits%253D0%2526mm%253D31%2526dur%253D418.836%2526clen%253D56528516%2526expire%253D1410362304%2526lmt%253D1402636388544569%2526ip%253D193.5.216.83%2526ms%253Dau%2526mt%253D1410340588%2526mv%253Dm%2526sparams%253Dclen%25252Cdur%25252Cgir%25252Cid%25252Cinitcwndbps%25252Cip%25252Cipbits%25252Citag%25252Clmt%25252Cmm%25252Cms%25252Cmv%25252Csource%25252Cupn%25252Cexpire%26lmt%3D1402636388544569%26clen%3D56528516%26bitrate%3D2211967%26size%3D1280x720%26fps%3D23%26index%3D709-1748%26s%3D89A949B05300FDE1634427602EFD86A74768EB8AA.565838EB38406B1A2B99CA872D3FA535F7A8573888%2Citag%3D247%26init%3D0-234%26type%3Dvideo%252Fwebm%253B%2Bcodecs%253D%2522vp9%2522%26url%3Dhttp%253A%252F%252Fr9---sn-1gi7zn7l.googlevideo.com%252Fvideoplayback%253Fitag%253D247%2526fexp%253D927622%25252C931983%25252C932404%25252C932623%25252C934024%25252C934030%25252C936117%25252C938644%25252C940215%25252C946013%25252C947209%25252C953801%25252C955100%2526initcwndbps%253D7887000%2526gir%253Dyes%2526key%253Dyt5%2526source%253Dyoutube%2526upn%253Dy346fy9WUkk%2526id%253Do-ANnfoW7VWvxVYQTOGvBgDQxyUIThLwYAR3wFGqXjjvOV%2526sver%253D3%2526ipbits%253D0%2526mm%253D31%2526dur%253D418.919%2526clen%253D45209729%2526expire%253D1410362304%2526lmt%253D1392434237871509%2526ip%253D193.5.216.83%2526ms%253Dau%2526mt%253D1410340588%2526mv%253Dm%2526sparams%253Dclen%25252Cdur%25252Cgir%25252Cid%25252Cinitcwndbps%25252Cip%25252Cipbits%25252Citag%25252Clmt%25252Cmm%25252Cms%25252Cmv%25252Csource%25252Cupn%25252Cexpire%26lmt%3D1392434237871509%26clen%3D45209729%26bitrate%3D1404003%26size%3D1280x720%26fps%3D1%26index%3D235-1700%26s%3DE97C9D2DB8DEEA0EE3C001570B898C2179458813B.AEDD0A5D4D6A01D990992E23CAB3CA11D8D1D3A000%2Citag%3D135%26init%3D0-708%26type%3Dvideo%252Fmp4%253B%2Bcodecs%253D%2522avc1.4d401e%2522%26url%3Dhttp%253A%252F%252Fr9---sn-1gi7zn7l.googlevideo.com%252Fvideoplayback%253Fitag%253D135%2526fexp%253D927622%25252C931983%25252C932404%25252C932623%25252C934024%25252C934030%25252C936117%25252C938644%25252C940215%25252C946013%25252C947209%25252C953801%25252C955100%2526initcwndbps%253D7887000%2526gir%253Dyes%2526key%253Dyt5%2526source%253Dyoutube%2526upn%253Dy346fy9WUkk%2526id%253Do-ANnfoW7VWvxVYQTOGvBgDQxyUIThLwYAR3wFGqXjjvOV%2526sver%253D3%2526ipbits%253D0%2526mm%253D31%2526dur%253D418.836%2526clen%253D29092558%2526expire%253D1410362304%2526lmt%253D1402636084227495%2526ip%253D193.5.216.83%2526ms%253Dau%2526mt%253D1410340588%2526mv%253Dm%2526sparams%253Dclen%25252Cdur%25252Cgir%25252Cid%25252Cinitcwndbps%25252Cip%25252Cipbits%25252Citag%25252Clmt%25252Cmm%25252Cms%25252Cmv%25252Csource%25252Cupn%25252Cexpire%26lmt%3D1402636084227495%26clen%3D29092558%26bitrate%3D1104099%26size%3D854x480%26fps%3D23%26index%3D709-1748%26s%3D212C173D507FB6DCCEB941920FF8B5CFF0D26A404.9ECCE6A5F0E398748F1532D251E87E7BE7B327D666%2Citag%3D244%26init%3D0-234%26type%3Dvideo%252Fwebm%253B%2Bcodecs%253D%2522vp9%2522%26url%3Dhttp%253A%252F%252Fr9---sn-1gi7zn7l.googlevideo.com%252Fvideoplayback%253Fitag%253D244%2526fexp%253D927622%25252C931983%25252C932404%25252C932623%25252C934024%25252C934030%25252C936117%25252C938644%25252C940215%25252C946013%25252C947209%25252C953801%25252C955100%2526initcwndbps%253D7887000%2526gir%253Dyes%2526key%253Dyt5%2526source%253Dyoutube%2526upn%253Dy346fy9WUkk%2526id%253Do-ANnfoW7VWvxVYQTOGvBgDQxyUIThLwYAR3wFGqXjjvOV%2526sver%253D3%2526ipbits%253D0%2526mm%253D31%2526dur%253D418.919%2526clen%253D24732940%2526expire%253D1410362304%2526lmt%253D1392434255501575%2526ip%253D193.5.216.83%2526ms%253Dau%2526mt%253D1410340588%2526mv%253Dm%2526sparams%253Dclen%25252Cdur%25252Cgir%25252Cid%25252Cinitcwndbps%25252Cip%25252Cipbits%25252Citag%25252Clmt%25252Cmm%25252Cms%25252Cmv%25252Csource%25252Cupn%25252Cexpire%26lmt%3D1392434255501575%26clen%3D24732940%26bitrate%3D713441%26size%3D854x480%26fps%3D1%26index%3D235-1679%26s%3D97C557CB3308593CE074C821EF09E2F1E14979BA8.4943F396DEB2A5BF1077110E1D54D9835BA9B70111%2Citag%3D245%26init%3D0-234%26type%3Dvideo%252Fwebm%253B%2Bcodecs%253D%2522vp9%2522%26url%3Dhttp%253A%252F%252Fr9---sn-1gi7zn7l.googlevideo.com%252Fvideoplayback%253Fitag%253D245%2526fexp%253D927622%25252C931983%25252C932404%25252C932623%25252C934024%25252C934030%25252C936117%25252C938644%25252C940215%25252C946013%25252C947209%25252C953801%25252C955100%2526initcwndbps%253D7887000%2526gir%253Dyes%2526key%253Dyt5%2526source%253Dyoutube%2526upn%253Dy346fy9WUkk%2526id%253Do-ANnfoW7VWvxVYQTOGvBgDQxyUIThLwYAR3wFGqXjjvOV%2526sver%253D3%2526ipbits%253D0%2526mm%253D31%2526dur%253D418.919%2526clen%253D47033301%2526expire%253D1410362304%2526lmt%253D1375964528081445%2526ip%253D193.5.216.83%2526ms%253Dau%2526mt%253D1410340588%2526mv%253Dm%2526sparams%253Dclen%25252Cdur%25252Cgir%25252Cid%25252Cinitcwndbps%25252Cip%25252Cipbits%25252Citag%25252Clmt%25252Cmm%25252Cms%25252Cmv%25252Csource%25252Cupn%25252Cexpire%26lmt%3D1375964528081445%26clen%3D47033301%26bitrate%3D1074248%26size%3D854x480%26fps%3D1%26index%3D235-1704%26s%3D7AC777B463D65DAF0374A922585C52EFA676FE7E0.917329F6296F062335AA384C410D20163218444333%2Citag%3D246%26init%3D0-234%26type%3Dvideo%252Fwebm%253B%2Bcodecs%253D%2522vp9%2522%26url%3Dhttp%253A%252F%252Fr9---sn-1gi7zn7l.googlevideo.com%252Fvideoplayback%253Fitag%253D246%2526fexp%253D927622%25252C931983%25252C932404%25252C932623%25252C934024%25252C934030%25252C936117%25252C938644%25252C940215%25252C946013%25252C947209%25252C953801%25252C955100%2526initcwndbps%253D7887000%2526gir%253Dyes%2526key%253Dyt5%2526source%253Dyoutube%2526upn%253Dy346fy9WUkk%2526id%253Do-ANnfoW7VWvxVYQTOGvBgDQxyUIThLwYAR3wFGqXjjvOV%2526sver%253D3%2526ipbits%253D0%2526mm%253D31%2526dur%253D418.919%2526clen%253D74288072%2526expire%253D1410362304%2526lmt%253D1375964493656987%2526ip%253D193.5.216.83%2526ms%253Dau%2526mt%253D1410340588%2526mv%253Dm%2526sparams%253Dclen%25252Cdur%25252Cgir%25252Cid%25252Cinitcwndbps%25252Cip%25252Cipbits%25252Citag%25252Clmt%25252Cmm%25252Cms%25252Cmv%25252Csource%25252Cupn%25252Cexpire%26lmt%3D1375964493656987%26clen%3D74288072%26bitrate%3D2102754%26size%3D854x480%26fps%3D1%26index%3D235-1713%26s%3DF96DB33258FBFD688DF4A0DCA32678765497173DC.C95F47BDF0739600D69C16DBF412C8C0F1EAFE8999%2Citag%3D134%26init%3D0-708%26type%3Dvideo%252Fmp4%253B%2Bcodecs%253D%2522avc1.4d401e%2522%26url%3Dhttp%253A%252F%252Fr9---sn-1gi7zn7l.googlevideo.com%252Fvideoplayback%253Fitag%253D134%2526fexp%253D927622%25252C931983%25252C932404%25252C932623%25252C934024%25252C934030%25252C936117%25252C938644%25252C940215%25252C946013%25252C947209%25252C953801%25252C955100%2526initcwndbps%253D7887000%2526gir%253Dyes%2526key%253Dyt5%2526source%253Dyoutube%2526upn%253Dy346fy9WUkk%2526id%253Do-ANnfoW7VWvxVYQTOGvBgDQxyUIThLwYAR3wFGqXjjvOV%2526sver%253D3%2526ipbits%253D0%2526mm%253D31%2526dur%253D418.836%2526clen%253D14415570%2526expire%253D1410362304%2526lmt%253D1402636232847505%2526ip%253D193.5.216.83%2526ms%253Dau%2526mt%253D1410340588%2526mv%253Dm%2526sparams%253Dclen%25252Cdur%25252Cgir%25252Cid%25252Cinitcwndbps%25252Cip%25252Cipbits%25252Citag%25252Clmt%25252Cmm%25252Cms%25252Cmv%25252Csource%25252Cupn%25252Cexpire%26lmt%3D1402636232847505%26clen%3D14415570%26bitrate%3D603965%26size%3D640x360%26fps%3D23%26index%3D709-1748%26s%3D66DB2B695E6989978B31CE349F9E042C29FD96C3B.94DA967CF89E9F3AB0605B10B7CDF42CF03381F666%2Citag%3D243%26init%3D0-234%26type%3Dvideo%252Fwebm%253B%2Bcodecs%253D%2522vp9%2522%26url%3Dhttp%253A%252F%252Fr9---sn-1gi7zn7l.googlevideo.com%252Fvideoplayback%253Fitag%253D243%2526fexp%253D927622%25252C931983%25252C932404%25252C932623%25252C934024%25252C934030%25252C936117%25252C938644%25252C940215%25252C946013%25252C947209%25252C953801%25252C955100%2526initcwndbps%253D7887000%2526gir%253Dyes%2526key%253Dyt5%2526source%253Dyoutube%2526upn%253Dy346fy9WUkk%2526id%253Do-ANnfoW7VWvxVYQTOGvBgDQxyUIThLwYAR3wFGqXjjvOV%2526sver%253D3%2526ipbits%253D0%2526mm%253D31%2526dur%253D418.919%2526clen%253D13564613%2526expire%253D1410362304%2526lmt%253D1392434226045066%2526ip%253D193.5.216.83%2526ms%253Dau%2526mt%253D1410340588%2526mv%253Dm%2526sparams%253Dclen%25252Cdur%25252Cgir%25252Cid%25252Cinitcwndbps%25252Cip%25252Cipbits%25252Citag%25252Clmt%25252Cmm%25252Cms%25252Cmv%25252Csource%25252Cupn%25252Cexpire%26lmt%3D1392434226045066%26clen%3D13564613%26bitrate%3D400565%26size%3D640x360%26fps%3D1%26index%3D235-1652%26s%3D8EBB1C3ED2871FE375A1C5AE252D24C5CCEED8BF3.367574296435FB9040EE45B5C4836982324CF52EEE%2Citag%3D133%26init%3D0-672%26type%3Dvideo%252Fmp4%253B%2Bcodecs%253D%2522avc1.4d4015%2522%26url%3Dhttp%253A%252F%252Fr9---sn-1gi7zn7l.googlevideo.com%252Fvideoplayback%253Fitag%253D133%2526fexp%253D927622%25252C931983%25252C932404%25252C932623%25252C934024%25252C934030%25252C936117%25252C938644%25252C940215%25252C946013%25252C947209%25252C953801%25252C955100%2526initcwndbps%253D7887000%2526gir%253Dyes%2526key%253Dyt5%2526source%253Dyoutube%2526upn%253Dy346fy9WUkk%2526id%253Do-ANnfoW7VWvxVYQTOGvBgDQxyUIThLwYAR3wFGqXjjvOV%2526sver%253D3%2526ipbits%253D0%2526mm%253D31%2526dur%253D418.836%2526clen%253D12585383%2526expire%253D1410362304%2526lmt%253D1402636014143274%2526ip%253D193.5.216.83%2526ms%253Dau%2526mt%253D1410340588%2526mv%253Dm%2526sparams%253Dclen%25252Cdur%25252Cgir%25252Cid%25252Cinitcwndbps%25252Cip%25252Cipbits%25252Citag%25252Clmt%25252Cmm%25252Cms%25252Cmv%25252Csource%25252Cupn%25252Cexpire%26lmt%3D1402636014143274%26clen%3D12585383%26bitrate%3D249540%26size%3D426x240%26fps%3D23%26index%3D673-1712%26s%3DB8E5C028DA977292FB4037BBF8A9A22F75DB3D84C.4F38302BA797CF60248DC0A1C466AF75A6A610D888%2Citag%3D242%26init%3D0-233%26type%3Dvideo%252Fwebm%253B%2Bcodecs%253D%2522vp9%2522%26url%3Dhttp%253A%252F%252Fr9---sn-1gi7zn7l.googlevideo.com%252Fvideoplayback%253Fitag%253D242%2526fexp%253D927622%25252C931983%25252C932404%25252C932623%25252C934024%25252C934030%25252C936117%25252C938644%25252C940215%25252C946013%25252C947209%25252C953801%25252C955100%2526initcwndbps%253D7887000%2526gir%253Dyes%2526key%253Dyt5%2526source%253Dyoutube%2526upn%253Dy346fy9WUkk%2526id%253Do-ANnfoW7VWvxVYQTOGvBgDQxyUIThLwYAR3wFGqXjjvOV%2526sver%253D3%2526ipbits%253D0%2526mm%253D31%2526dur%253D418.919%2526clen%253D7322195%2526expire%253D1410362304%2526lmt%253D1392434222688189%2526ip%253D193.5.216.83%2526ms%253Dau%2526mt%253D1410340588%2526mv%253Dm%2526sparams%253Dclen%25252Cdur%25252Cgir%25252Cid%25252Cinitcwndbps%25252Cip%25252Cipbits%25252Citag%25252Clmt%25252Cmm%25252Cms%25252Cmv%25252Csource%25252Cupn%25252Cexpire%26lmt%3D1392434222688189%26clen%3D7322195%26bitrate%3D222626%26size%3D426x240%26fps%3D1%26index%3D234-1651%26s%3DE5691DDAAC66DB33491B7C2B02781431705A14FCB.6CE272A0039315C1DA5220F8931E87F6B9929AC888%2Citag%3D160%26init%3D0-670%26type%3Dvideo%252Fmp4%253B%2Bcodecs%253D%2522avc1.4d400c%2522%26url%3Dhttp%253A%252F%252Fr9---sn-1gi7zn7l.googlevideo.com%252Fvideoplayback%253Fitag%253D160%2526fexp%253D927622%25252C931983%25252C932404%25252C932623%25252C934024%25252C934030%25252C936117%25252C938644%25252C940215%25252C946013%25252C947209%25252C953801%25252C955100%2526initcwndbps%253D7887000%2526gir%253Dyes%2526key%253Dyt5%2526source%253Dyoutube%2526upn%253Dy346fy9WUkk%2526id%253Do-ANnfoW7VWvxVYQTOGvBgDQxyUIThLwYAR3wFGqXjjvOV%2526sver%253D3%2526ipbits%253D0%2526mm%253D31%2526dur%253D418.836%2526clen%253D5609783%2526expire%253D1410362304%2526lmt%253D1402635971473749%2526ip%253D193.5.216.83%2526ms%253Dau%2526mt%253D1410340588%2526mv%253Dm%2526sparams%253Dclen%25252Cdur%25252Cgir%25252Cid%25252Cinitcwndbps%25252Cip%25252Cipbits%25252Citag%25252Clmt%25252Cmm%25252Cms%25252Cmv%25252Csource%25252Cupn%25252Cexpire%26lmt%3D1402635971473749%26clen%3D5609783%26bitrate%3D111926%26size%3D256x144%26fps%3D11%26index%3D671-1710%26s%3D985348CDF4E870FD1E395FA679713860B20691D12.3F8B7C19315AE1AE798EE2821739BC002B7188FDDD%2Cbitrate%3D129724%26lmt%3D1402623468656624%26url%3Dhttp%253A%252F%252Fr9---sn-1gi7zn7l.googlevideo.com%252Fvideoplayback%253Fitag%253D140%2526fexp%253D927622%25252C931983%25252C932404%25252C932623%25252C934024%25252C934030%25252C936117%25252C938644%25252C940215%25252C946013%25252C947209%25252C953801%25252C955100%2526initcwndbps%253D7887000%2526gir%253Dyes%2526key%253Dyt5%2526source%253Dyoutube%2526upn%253Dy346fy9WUkk%2526id%253Do-ANnfoW7VWvxVYQTOGvBgDQxyUIThLwYAR3wFGqXjjvOV%2526sver%253D3%2526ipbits%253D0%2526mm%253D31%2526dur%253D418.933%2526clen%253D6726152%2526expire%253D1410362304%2526lmt%253D1402623468656624%2526ip%253D193.5.216.83%2526ms%253Dau%2526mt%253D1410340588%2526mv%253Dm%2526sparams%253Dclen%25252Cdur%25252Cgir%25252Cid%25252Cinitcwndbps%25252Cip%25252Cipbits%25252Citag%25252Clmt%25252Cmm%25252Cms%25252Cmv%25252Csource%25252Cupn%25252Cexpire%26type%3Daudio%252Fmp4%253B%2Bcodecs%253D%2522mp4a.40.2%2522%26clen%3D6726152%26itag%3D140%26s%3D6DC5F5CD615034314676B03E39EE25FCFD2CFD4B9.77CD61C67D01A0FE66D29E792588B4FFBC48AC9FFF%26index%3D592-1127%26init%3D0-591%2Cbitrate%3D125066%26fps%3D1%26lmt%3D1392434342615655%26url%3Dhttp%253A%252F%252Fr9---sn-1gi7zn7l.googlevideo.com%252Fvideoplayback%253Fitag%253D171%2526fexp%253D927622%25252C931983%25252C932404%25252C932623%25252C934024%25252C934030%25252C936117%25252C938644%25252C940215%25252C946013%25252C947209%25252C953801%25252C955100%2526initcwndbps%253D7887000%2526gir%253Dyes%2526key%253Dyt5%2526source%253Dyoutube%2526upn%253Dy346fy9WUkk%2526id%253Do-ANnfoW7VWvxVYQTOGvBgDQxyUIThLwYAR3wFGqXjjvOV%2526sver%253D3%2526ipbits%253D0%2526mm%253D31%2526dur%253D418.962%2526clen%253D5948234%2526expire%253D1410362304%2526lmt%253D1392434342615655%2526ip%253D193.5.216.83%2526ms%253Dau%2526mt%253D1410340588%2526mv%253Dm%2526sparams%253Dclen%25252Cdur%25252Cgir%25252Cid%25252Cinitcwndbps%25252Cip%25252Cipbits%25252Citag%25252Clmt%25252Cmm%25252Cms%25252Cmv%25252Csource%25252Cupn%25252Cexpire%26type%3Daudio%252Fwebm%253B%2Bcodecs%253D%2522vorbis%2522%26clen%3D5948234%26itag%3D171%26s%3D44D493EF788C077D24722075636A83DCCB3B9B242.83F4F7A14AFBAF283A409B0155E62FD4F6FDA32111%26index%3D4452-5162%26init%3D0-4451&iv_module=https%3A%2F%2Fs.ytimg.com%2Fyts%2Fswfbin%2Fplayer-vflLstCoX%2Fiv_module.swf&muted=0&storyboard_spec=https%3A%2F%2Fi.ytimg.com%2Fsb%2F07FYdnEawAQ%2Fstoryboard3_L%24L%2F%24N.jpg%7C48%2327%23100%2310%2310%230%23default%23yNfY6QaCsYUziT-nOaPcBItXbNU%7C80%2345%2385%2310%2310%235000%23M%24M%23UtOKQnVUs9RvQ0CkkgZJx96yIyQ%7C160%2390%2385%235%235%235000%23M%24M%23CKWCippawai_FaD-yja5CiiVIOY&enablecsi=1&ptchn=sXfDf1CDgU3SCt0gxJNXGg&plid=AAUCsoi5jCQPD6n-&adsense_video_doc_id=yt_07FYdnEawAQ&fexp=927622%2C931983%2C932404%2C932623%2C934024%2C934030%2C936117%2C938644%2C940215%2C946013%2C947209%2C953801%2C955100&iurlmaxres=https%3A%2F%2Fi.ytimg.com%2Fvi%2F07FYdnEawAQ%2Fmaxresdefault.jpg&ptk=&gut_tag=%2F4061%2Fytpwatch%2Fmain_10481&loeid=936117%2C938644%2C940215%2C946013%2C955100&iurlhq=https%3A%2F%2Fi.ytimg.com%2Fvi%2F07FYdnEawAQ%2Fhqdefault.jpg&no_get_video_log=1&allow_html5_ads=1&ad_logging_flag=1&vq=auto&pyv_in_related_cafe_experiment_id=&midroll_freqcap=420.0&dashmpd=http%3A%2F%2Fmanifest.googlevideo.com%2Fapi%2Fmanifest%2Fdash%2Fid%2Fo-ANnfoW7VWvxVYQTOGvBgDQxyUIThLwYAR3wFGqXjjvOV%2Fmt%2F1410340588%2Fupn%2Fwvi4l5gGjjk%2Fkey%2Fyt5%2Fipbits%2F0%2Fmm%2F31%2Fplayback_host%2Fr9---sn-1gi7zn7l.googlevideo.com%2Fitag%2F0%2Fexpire%2F1410362304%2Fas%2Ffmp4_audio_clear%252Cwebm_audio_clear%252Cfmp4_sd_hd_clear%252Cwebm_sd_hd_clear%252Cwebm2_sd_hd_clear%2Fsver%2F3%2Fip%2F193.5.216.83%2Ffexp%2F927622%252C931983%252C932404%252C932623%252C934024%252C934030%252C936117%252C938644%252C940215%252C946013%252C947209%252C953801%252C955100%2Fms%2Fau%2Fsource%2Fyoutube%2Fs%2FB1D46399B9C1B3F266D3A60C00C1B53A0EB5F7904.139AB1A6A4F0692D1D19F8CBCD1FCE09037459CCCC%2Fmv%2Fm%2Fsparams%2Fas%252Cid%252Cip%252Cipbits%252Citag%252Cmm%252Cms%252Cmv%252Cplayback_host%252Csource%252Cexpire&uid=sXfDf1CDgU3SCt0gxJNXGg&ad_device=1&author=justintimberlakeVEVO&ad_slots=0&title=Justin+Timberlake+-+Tunnel+Vision+%28Explicit%29&afc_rm_fallback=1&ytfocHistoryEnabled=1&dclk=True&cl=75038744&view_count=11309549&shortform=True&iurlsd=https%3A%2F%2Fi.ytimg.com%2Fvi%2F07FYdnEawAQ%2Fsddefault.jpg&allow_embed=1×tamp=1410340704&keywords=Justin+Timberlake%2CTunnel+Vision%2Cnew+song%2Cnew+video%2Cofficial+video%2Cofficial%2Cnew%2CJT%2C20%2F20+Experience%2Cnaked%2Cboobs%2Cblurred+lines%2Cnudity%2Cnsfw&status=ok&instream_long=False&iv_invideo_url=https%3A%2F%2Fwww.youtube.com%2Fannotations_invideo%3Fcap_hist%3D1%26cta%3D2%26video_id%3D07FYdnEawAQ&token=vjVQa1PpcFM68l8YYqnN5jQm-DKWMNfqaoe1zb27uLk%3D&vid=07FYdnEawAQ&cafe_experiment_id=&eventid=YBcQVNu1Msbk8AO6iYDAAg&c=WEB&ldpj=-1&cid=10481&invideo=True&sffb=True&as_launched_in_country=1&tag_for_child_directed=False&allow_ratings=1&csi_page_type=watch%2Cwatch7ad&pltype=content&iurl=https%3A%2F%2Fi.ytimg.com%2Fvi%2F07FYdnEawAQ%2Fhqdefault.jpg&oid=yZJtHRi2SvzOOKZfbA-GRA&cut_ad_for_ypc=False&ad_tag=https%3A%2F%2Fad.doubleclick.net%2FN4061%2Fpfadx%2Fcom.ytpwatch.partnerracy%2Fmain_10481%3Bkpeid%3DsXfDf1CDgU3SCt0gxJNXGg%3Bkpid%3D10481%3Bkpu%3DjustintimberlakeVEVO%3Bkvid%3D07FYdnEawAQ%3Bmpvid%3D8zYeiihq-kfXAEXN%3Bssl%3D1%3Bsz%3DWIDTHxHEIGHT%3Bafc%3D1%3Bdc_output%3Dxml_vast3%3Bdc_yt%3D1%3Bdc_yt_pt%3DAPb3F28y9eth79OtpFmbBWdT6gQVnyckAS225iN4tnJucp8Z1mMX5GL13v-upj1djsxzMyIGkXwdj80kc15tO51w2vyay7xaafn56p1rQI6B0aRQ7v4Yclzn148WMj514vaagky6l9FugEH3ek--5mCdaolTcKBbe40YNJWygw%3Bk2%3D3%3Bk2%3D35%3Bk2%3D1021%3Bk5%3D3_35_1021%3Bkga%3D-1%3Bkgg%3D-1%3Bklg%3Den%3Bkmsrd%3D1%3Bko%3Dp%3Bkr%3DR%3Bkvlg%3Den%3Bkvz%3D206%3Bnlfb%3D1%3Byt3pav%3D1%3Bytcat%3D10%3Bytdevice%3D1%3Bytexp%3D936117%2C938644%2C940215%2C946013%2C955100%3Byt_ec%3D7%3B%21c%3D10481&ad3_module=1&excluded_ads=26%3D1_2_1%2C2_1%2C2_2%2C2_2_1%2C2_3%3B2%3D1_2&has_cc=False&rmktEnabled=1&ytfocEnabled=1&aid=P-AaxUGumcQ&watermark=%2Chttps%3A%2F%2Fs.ytimg.com%2Fyts%2Fimg%2Fwatermark%2Fyoutube_watermark-vflHX6b6E.png%2Chttps%3A%2F%2Fs.ytimg.com%2Fyts%2Fimg%2Fwatermark%2Fyoutube_hd_watermark-vflAzLcD6.png&iv3_module=1&rvs=id%3DuuZE_IRwLNI%26session_data%3Dfeature%253Dendscreen%26title%3DJustin%2BTimberlake%2B-%2BMirrors%26view_count%3D219%252C953%252C866%26length_seconds%3D501%26author%3DjustintimberlakeVEVO%2Cid%3DFyXtoTLLcDk%26session_data%3Dfeature%253Dendscreen%26title%3DJustin%2BTimberlake%2B-%2BTKO%26view_count%3D48%252C876%252C741%26length_seconds%3D430%26author%3DjustintimberlakeVEVO%2Cid%3Dxjpe7EGyiw8%26session_data%3Dfeature%253Dendscreen%26title%3DJustin%2BTimberlake%2Bfeat.%2BT.I.%2B-%2BMedley%253A%2BLet%2BMe%2BTalk%2BTo%2BYou%252FMy%2BLove%2Bft.%2BT.I.%26view_count%3D39%252C725%252C687%26length_seconds%3D371%26author%3DjustintimberlakeVEVO%2Cid%3DTOrnUquxtwA%26session_data%3Dfeature%253Dendscreen%26title%3DJustin%2BTimberlake%2B-%2BWhat%2BGoes%2BAround...Comes%2BAround%26view_count%3D109%252C732%252C058%26length_seconds%3D562%26author%3DjustintimberlakeVEVO%2Cid%3Dd28tKYSvDBo%26session_data%3Dfeature%253Dendscreen%26title%3DBest%2BSongs%2BOf%2BJustin%2BTimberlake%2Bl%2BJustin%2BTimberlake%2527s%2BGreatest%2BHits%26view_count%3D642%252C056%26length_seconds%3D5991%26author%3DM4U%2Cid%3DfvEZUbzqqyM%26session_data%3Dfeature%253Dendscreen%26title%3DMirrors%2B-%2BJustin%2BTimberlake%2B%2528Boyce%2BAvenue%2Bfeat.%2BFifth%2BHarmony%2Bcover%2529%2Bon%2BiTunes%2B%2526%2BSpotify%26view_count%3D55%252C719%252C926%26length_seconds%3D337%26author%3Dboyceavenue%2Cid%3DIsUsVbTj2AY%26session_data%3Dfeature%253Dendscreen%26title%3DJustin%2BTimberlake%2B-%2BSuit%2B%2526%2BTie%2B%2528Official%2529%2Bft.%2BJAY%2BZ%26view_count%3D75%252C333%252C238%26length_seconds%3D331%26author%3DjustintimberlakeVEVO%2Cid%3DDksSPZTZES0%26session_data%3Dfeature%253Dendscreen%26title%3DJustin%2BTimberlake%2B-%2BCry%2BMe%2BA%2BRiver%26view_count%3D92%252C775%252C623%26length_seconds%3D291%26author%3DjustintimberlakeVEVO%2Cid%3DehcVomMexkY%26session_data%3Dfeature%253Dendscreen%26title%3DRihanna%2B-%2BPour%2BIt%2BUp%2B%2528Explicit%2529%26view_count%3D133%252C121%252C361%26length_seconds%3D197%26author%3DRihannaVEVO%2Cid%3D3gOHvDP_vCs%26session_data%3Dfeature%253Dendscreen%26title%3DJustin%2BTimberlake%2B-%2BSexyBack%2B%2528Director%2527s%2BCut%2529%2Bft.%2BTimbaland%26view_count%3D42%252C544%252C864%26length_seconds%3D266%26author%3DjustintimberlakeVEVO%2Cid%3DoG08ukJPtR8%26session_data%3Dfeature%253Dendscreen%26title%3DMichael%2BJackson%252C%2BJustin%2BTimberlake%2B-%2BLove%2BNever%2BFelt%2BSo%2BGood%26view_count%3D57%252C343%252C163%26length_seconds%3D248%26author%3DmichaeljacksonVEVO%2Cid%3D2YcIgow6TDk%26session_data%3Dfeature%253Dendscreen%26title%3DJAY%2BZ%2B%2522Holy%2BGrail%2522%2Bfeaturing%2BJustin%2BTimberlake%26view_count%3D47%252C355%252C392%26length_seconds%3D339%26author%3DJAY%2BZ%2527s%2BLife%252BTimes&account_playback_token=QUFFLUhqbnZySHIwb1RHQVR3LVpIb1ZGYzdBa1BoSDY1Z3xBQ3Jtc0tuUlRrcE5qNWpQM1FIZkpjZTJWWk1IZVNObnhqdUJackk5RGRacG4tbldVdGFFaExGVkVzQjg2ekxjYmdlUEtvbXI4aTBoYUhxRWowVERMUWQySFFVbnJRX2dfdW1JamRNcVJmRm5HbEhCaEh4RnJoZw%3D%3D", + "headers" : { + "Server" : "gwiseguy\/2.0", + "Content-Type" : "application\/x-www-form-urlencoded", + "X-Content-Type-Options" : "nosniff", + "Transfer-Encoding" : "Identity", + "Date" : "Wed, 10 Sep 2014 09:18:24 GMT", + "X-Frame-Options" : "SAMEORIGIN", + "X-XSS-Protection" : "1; mode=block; report=https:\/\/www.google.com\/appserve\/security-bugs\/log\/youtube", + "Alternate-Protocol" : "443:quic", + "Cache-Control" : "no-store", + "Expires" : "Tue, 27 Apr 1971 19:44:06 EST" + }, + "method" : "GET", + "status" : 200, + "uri" : "https:\/\/www.youtube.com\/get_video_info?el=detailpage&hl=en&ps=default&video_id=07FYdnEawAQ" + }, + { + "body" : "reason=This+video+contains+content+from+VEVO.+It+is+restricted+from+playback+on+certain+sites.%3Cbr%2F%3E%3Cu%3E%3Ca+href%3D%27http%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3D07FYdnEawAQ%26feature%3Dplayer_embedded%27+target%3D%27_blank%27%3EWatch+on+YouTube%3C%2Fa%3E%3C%2Fu%3E&eventid=YBcQVIPMLc_G8AON3YHgCw&c=WEB&errorcode=150&status=fail&csi_page_type=embed&enablecsi=1&atc=a%3D3%26b%3DCSLHL1AFY7Bk7qsakcXktFZROjo%26c%3D1410340704%26d%3D1%26e%3D07FYdnEawAQ%26c3a%3D15%26hh%3D5kBgUAiNSyMAQaXvqCeWAXnbyrI&errordetail=0", + "headers" : { + "Content-Type" : "application\/x-www-form-urlencoded", + "P3P" : "CP=\"This is not a P3P policy! See http:\/\/support.google.com\/accounts\/bin\/answer.py?answer=151657&hl=en for more info.\"", + "Alternate-Protocol" : "443:quic", + "Set-Cookie" : "YSC=fvNSgPwfxA8; path=\/; domain=.youtube.com; httponly", + "Server" : "gwiseguy\/2.0", + "X-XSS-Protection" : "1; mode=block; report=https:\/\/www.google.com\/appserve\/security-bugs\/log\/youtube", + "Transfer-Encoding" : "Identity", + "Expires" : "Tue, 27 Apr 1971 19:44:06 EST", + "Cache-Control" : "no-store", + "Date" : "Wed, 10 Sep 2014 09:18:24 GMT", + "X-Content-Type-Options" : "nosniff", + "X-Frame-Options" : "SAMEORIGIN" + }, + "method" : "GET", + "status" : 200, + "uri" : "https:\/\/www.youtube.com\/get_video_info?el=embedded&hl=en&ps=default&video_id=07FYdnEawAQ" + }, + { + "body" : "