Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions XCDYouTubeKit/XCDYouTubeClient.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ NS_ASSUME_NONNULL_BEGIN
*/
@interface XCDYouTubeClient : NSObject

+ (NSString *)innertubeApiKey;
+ (void)setInnertubeApiKey:(NSString *)key;

/**
* ------------------
* @name Initializing
Expand Down
11 changes: 11 additions & 0 deletions XCDYouTubeKit/XCDYouTubeClient.m
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ @implementation XCDYouTubeClient

@synthesize languageIdentifier = _languageIdentifier;

static NSString * _innertubeApiKey = @"AIzaSyAO_FJ2SlqU8Q4STEHLGCilw_Y9_11qcW8";

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't its private someone's key? I think you shouldn't commit it.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

according to one comment here, it's the internal API used by YouTube's UI


+ (instancetype) defaultClient
{
static XCDYouTubeClient *defaultClient;
Expand All @@ -29,6 +31,15 @@ - (instancetype) init
return [self initWithLanguageIdentifier:nil];
}


+ (NSString *)innertubeApiKey {
return _innertubeApiKey;
}

+ (void)setInnertubeApiKey:(NSString *)key {
_innertubeApiKey = key;
}

- (instancetype) initWithLanguageIdentifier:(NSString *)languageIdentifier
{
if (!(self = [super init]))
Expand Down
55 changes: 50 additions & 5 deletions XCDYouTubeKit/XCDYouTubeVideoOperation.m
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#import "XCDYouTubeDashManifestXML.h"
#import "XCDYouTubePlayerScript.h"
#import "XCDYouTubeLogger+Private.h"
#import "XCDYouTubeClient.h"

typedef NS_ENUM(NSUInteger, XCDYouTubeRequestType) {
XCDYouTubeRequestTypeGetVideoInfo = 1,
Expand Down Expand Up @@ -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" };
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];
}
}

Expand Down Expand Up @@ -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
Expand Down