diff --git a/XCDYouTubeKit/XCDYouTubeVideoOperation.m b/XCDYouTubeKit/XCDYouTubeVideoOperation.m index 6663ab39..b3d42648 100644 --- a/XCDYouTubeKit/XCDYouTubeVideoOperation.m +++ b/XCDYouTubeKit/XCDYouTubeVideoOperation.m @@ -12,6 +12,7 @@ #import "XCDYouTubeDashManifestXML.h" #import "XCDYouTubePlayerScript.h" #import "XCDYouTubeLogger+Private.h" +#import "XCDYouTubeClient.h" typedef NS_ENUM(NSUInteger, XCDYouTubeRequestType) { XCDYouTubeRequestTypeGetVideoInfo = 1, @@ -146,13 +147,24 @@ - (void) startNextRequest } else { - NSString *eventLabel = [self.eventLabels objectAtIndex:0]; [self.eventLabels removeObjectAtIndex:0]; - NSDictionary *query = @{ @"video_id": self.videoIdentifier, @"hl": self.languageIdentifier, @"el": eventLabel, @"ps": @"default", @"html5" : @"1", @"eurl": [@"https://youtube.googleapis.com/v/" stringByAppendingString:self.videoIdentifier],@"c": @"TVHTML5", @"cver": @"6.20180913"}; - NSString *queryString = XCDQueryStringWithDictionary(query); - NSURL *videoInfoURL = [NSURL URLWithString:[@"https://www.youtube.com/get_video_info?" stringByAppendingString:queryString]]; - [self startRequestWithURL:videoInfoURL type:XCDYouTubeRequestTypeGetVideoInfo]; + NSString *urlString = [NSString stringWithFormat:@"https://youtubei.googleapis.com/youtubei/v1/player?key=%@", XCDYouTubeClient.innertubeApiKey]; + NSURL *url = [NSURL URLWithString:urlString]; + + NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:10]; + + [request setHTTPMethod:@"POST"]; + + NSString *string = [NSString stringWithFormat:@"{'context': {'client': {'hl': 'en','clientName': 'WEB','clientVersion': '2.20210721.00.00','mainAppWebInfo': {'graftUrl': '/watch?v=%@'}}},'videoId': '%@'}", self.videoIdentifier, self.videoIdentifier]; + + NSData *postData = [string dataUsingEncoding:NSASCIIStringEncoding]; + + [request setHTTPBody:postData]; + + [request setValue:@"application/json" forHTTPHeaderField:@"Content-Type"]; + + [self startRequestWith:request type:XCDYouTubeRequestTypeGetVideoInfo]; } } @@ -206,6 +218,39 @@ - (void) startRequestWithURL:(NSURL *)url type:(XCDYouTubeRequestType)requestTyp self.requestType = requestType; } +- (void) startRequestWith:(NSMutableURLRequest *)request type:(XCDYouTubeRequestType)requestType +{ + if (self.isCancelled) + return; + + // Max (age-restricted VEVO) = 2×GetVideoInfo + 1×WatchPage + 2×EmbedPage + 1×JavaScriptPlayer + 1×GetVideoInfo + 1xDashManifest + if (++self.requestCount > 8) + { + // This condition should never happen but the request flow is quite complex so better abort here than go into an infinite loop of requests + [self finishWithError]; + return; + } + + XCDYouTubeLogDebug(@"Starting request: %@", [request URL]); + + [request setValue:self.languageIdentifier forHTTPHeaderField:@"Accept-Language"]; + [request setValue:[NSString stringWithFormat:@"https://youtube.com/watch?v=%@", self.videoIdentifier] forHTTPHeaderField:@"Referer"]; + + self.dataTask = [self.session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) + { + if (self.isCancelled) + return; + + if (error) + [self handleConnectionError:error requestType:requestType]; + else + [self handleConnectionSuccessWithData:data response:response requestType:requestType]; + }]; + [self.dataTask resume]; + + self.requestType = requestType; +} + #pragma mark - Response Dispatch - (void) handleConnectionSuccessWithData:(NSData *)data response:(NSURLResponse *)response requestType:(XCDYouTubeRequestType)requestType @@ -270,49 +315,10 @@ - (void) handleConnectionError:(NSError *)connectionError requestType:(XCDYouTub #pragma mark - Response Parsing -- (void) initializeConsentWithResponse:(NSURLResponse *)response { - NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response; - if (httpResponse && response.URL) { - NSArray *cookies = [NSHTTPCookie cookiesWithResponseHeaderFields:httpResponse.allHeaderFields forURL:(NSURL *_Nonnull)response.URL]; - - for (NSHTTPCookie *cookie in cookies) { - if ([cookie.name isEqualToString:@"__Secure-3PSID"]) return; - } - - for (NSHTTPCookie *cookie in cookies) { - if ([cookie.name isEqualToString:@"CONSENT"]) { - if ([cookie.value isEqualToString:@"YES"]) return; - - NSString *rawConsentID = [cookie.value stringByReplacingOccurrencesOfString:@"PENDING+" withString:@""]; - int consentID = [rawConsentID intValue]; - - // generate random consent id, if doesn't match expected format - if (consentID < 100 || consentID > 999) { - consentID = 100 + (int)arc4random_uniform((uint32_t)(999 - 100 + 1)); - } - - NSString *cookieValue = [[NSString alloc] initWithFormat:@"YES+cb.20210328-17-p0.en+FX+%i", consentID]; - NSHTTPCookie *consentCookie = [NSHTTPCookie cookieWithProperties:@{ - NSHTTPCookiePath: @"/", - NSHTTPCookieName: @"CONSENT", - NSHTTPCookieValue: cookieValue, - NSHTTPCookieDomain:@".youtube.com", - NSHTTPCookieSecure:@"TRUE" - }]; - [self.session.configuration.HTTPCookieStorage setCookie:consentCookie]; - return; - } - } - - } -} - - (void) handleVideoInfoResponseWithInfo:(NSDictionary *)info response:(NSURLResponse *)response { XCDYouTubeLogDebug(@"Handling video info response"); - [self initializeConsentWithResponse:response]; - NSError *error = nil; XCDYouTubeVideo *video = [[XCDYouTubeVideo alloc] initWithIdentifier:self.videoIdentifier info:info playerScript:self.playerScript response:response error:&error]; if (video) @@ -395,13 +401,9 @@ - (void) handleJavaScriptPlayerWithScript:(NSString *)script if (self.webpage.isAgeRestricted && self.cookies.count == 0) { - NSString *eventLabel = [self.eventLabels objectAtIndex:0]; - [self.eventLabels removeObjectAtIndex:0]; - NSString *eurl = [@"https://youtube.googleapis.com/v/" stringByAppendingString:self.videoIdentifier]; NSString *sts = self.embedWebpage.sts ?: self.webpage.sts ?: @""; - //NSDictionary *query = @{ @"video_id": self.videoIdentifier, @"hl": self.languageIdentifier, @"eurl": eurl, @"sts": sts}; - NSDictionary *query = @{ @"video_id": self.videoIdentifier, @"hl": self.languageIdentifier, @"el": eventLabel, @"ps": @"default" , @"html5" : @1,@"html5" : @1, @"c":@"TVHTML5", @"cver":@6.20180913}; + NSDictionary *query = @{ @"video_id": self.videoIdentifier, @"hl": self.languageIdentifier, @"eurl": eurl, @"sts": sts}; NSString *queryString = XCDQueryStringWithDictionary(query); NSURL *videoInfoURL = [NSURL URLWithString:[@"https://www.youtube.com/get_video_info?" stringByAppendingString:queryString]]; [self startRequestWithURL:videoInfoURL type:XCDYouTubeRequestTypeGetVideoInfo];