Skip to content

Commit

Permalink
Add streamURLsToQuery and options to querying
Browse files Browse the repository at this point in the history
`streamURLsToQuery` allows clients to specify which URLs to query, this can greatly improve performance. The new `options` argument is reserved for return use and right not can be disregarded
  • Loading branch information
SoneeJohn committed Feb 24, 2020
1 parent 1702427 commit 16cb754
Show file tree
Hide file tree
Showing 4 changed files with 48 additions and 9 deletions.
3 changes: 3 additions & 0 deletions XCDYouTubeKit/XCDYouTubeClient.h
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,9 @@ NS_ASSUME_NONNULL_BEGIN
*/
- (XCDYouTubeVideoQueryOperation *) queryVideo:(XCDYouTubeVideo *)video cookies:(nullable NSArray <NSHTTPCookie *>*)cookies completionHandler:(void (^)(NSDictionary * __nullable streamURLs, NSError * __nullable error, NSDictionary<id, NSError *> * __nullable streamErrors))completionHandler;

- (XCDYouTubeVideoQueryOperation *) queryVideo:(XCDYouTubeVideo *)video streamURLsToQuery:(NSDictionary<id, NSURL *> * __nullable)streamURLsToQuery options:(NSDictionary * __nullable)options cookies:(nullable NSArray <NSHTTPCookie *>*)cookies completionHandler:(void (^)(NSDictionary *__nullable streamURLs, NSError * __nullable error, NSDictionary<id, NSError *> *__nullable streamErrors))completionHandler;


@end

NS_ASSUME_NONNULL_END
9 changes: 7 additions & 2 deletions XCDYouTubeKit/XCDYouTubeClient.m
Original file line number Diff line number Diff line change
Expand Up @@ -86,12 +86,17 @@ - (instancetype) initWithLanguageIdentifier:(NSString *)languageIdentifier
return [self getVideoWithIdentifier:videoIdentifier cookies:cookies customPatterns:nil completionHandler:completionHandler];
}

- (XCDYouTubeVideoQueryOperation *)queryVideo:(XCDYouTubeVideo *)video cookies:(NSArray<NSHTTPCookie *> *)cookies completionHandler:(void (^)(NSDictionary * _Nullable, NSError * _Nullable, NSDictionary<id,NSError *> * _Nullable))completionHandler
- (XCDYouTubeVideoQueryOperation *)queryVideo:(XCDYouTubeVideo *)video cookies:(NSArray<NSHTTPCookie *> *)cookies completionHandler:(void (^)(NSDictionary * _Nonnull, NSError * _Nullable, NSDictionary<id,NSError *> * _Nonnull))completionHandler
{
return [self queryVideo:video streamURLsToQuery:nil options:nil cookies:cookies completionHandler:completionHandler];
}

- (XCDYouTubeVideoQueryOperation *)queryVideo:(XCDYouTubeVideo *)video streamURLsToQuery:(NSDictionary<id,NSURL *> *)streamURLsToQuery options:(NSDictionary *)options cookies:(NSArray<NSHTTPCookie *> *)cookies completionHandler:(void (^)(NSDictionary * _Nullable, NSError * _Nullable, NSDictionary<id,NSError *> * _Nullable))completionHandler
{
if (!completionHandler)
@throw [NSException exceptionWithName:NSInvalidArgumentException reason:@"The `completionHandler` argument must not be nil." userInfo:nil];

XCDYouTubeVideoQueryOperation *operation = [[XCDYouTubeVideoQueryOperation alloc]initWithVideo:video cookies:cookies];
XCDYouTubeVideoQueryOperation *operation = [[XCDYouTubeVideoQueryOperation alloc]initWithVideo:video streamURLsToQuery:streamURLsToQuery options:options cookies:cookies];
operation.completionBlock = ^{
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
#pragma clang diagnostic push
Expand Down
4 changes: 3 additions & 1 deletion XCDYouTubeKit/XCDYouTubeVideoQueryOperation.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,9 @@ NS_ASSUME_NONNULL_BEGIN
/// Initializes a video query operation with the specified video and cookies.
/// @param video The `<XCDYouTubeVideo>` object that this operation will query. Passing a `nil` video will throw an `NSInvalidArgumentException` exception.
/// @param cookies An array of `NSHTTPCookie` objects, can be nil. These cookies can be used for certain videos that require a login.
- (instancetype) initWithVideo:(XCDYouTubeVideo *)video cookies:(nullable NSArray<NSHTTPCookie *> *)cookies NS_DESIGNATED_INITIALIZER;
- (instancetype) initWithVideo:(XCDYouTubeVideo *)video cookies:(nullable NSArray<NSHTTPCookie *> *)cookies;

- (instancetype) initWithVideo:(XCDYouTubeVideo *)video streamURLsToQuery:(nullable NSDictionary<id, NSURL *>*)streamURLsToQuery options:(nullable NSDictionary *)options cookies:(nullable NSArray<NSHTTPCookie *> *)cookies NS_DESIGNATED_INITIALIZER;

/// The `video` object that the operation initialized initialized with.
@property (atomic, strong, readonly) XCDYouTubeVideo *video;
Expand Down
41 changes: 35 additions & 6 deletions XCDYouTubeKit/XCDYouTubeVideoQueryOperation.m
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ @interface XCDYouTubeVideoQueryOperation ()
@property (atomic, readwrite, nullable) NSError *error;
@property (atomic, readwrite, nullable) NSDictionary<id, NSError *> *streamErrors;

@property (atomic, strong) NSDictionary<id, NSURL *> *streamURLsToQuery;
@property (atomic, strong) NSOperationQueue *queryQueue;
@property (atomic, readonly) dispatch_semaphore_t operationStartSemaphore;
@end
Expand All @@ -35,25 +36,52 @@ - (instancetype)init
}
#pragma clang diagnostic pop

- (instancetype) initWithVideo:(XCDYouTubeVideo *)video cookies:(nullable NSArray<NSHTTPCookie *> *)cookies
- (instancetype)initWithVideo:(XCDYouTubeVideo *)video streamURLsToQuery:(NSDictionary<id,NSURL *> *)streamURLsToQuery options:(NSDictionary *)options cookies:(NSArray<NSHTTPCookie *> *)cookies
{
if (video == nil)
@throw [NSException exceptionWithName:NSInvalidArgumentException reason:@"`video` must not be nil" userInfo:nil];

if (!(self = [super init]))
return nil;

_video = video;

NSMutableDictionary *streamURLsToQueryMutable = [NSMutableDictionary new];

for (id key in streamURLsToQuery)
{
//Has to contain the same key and value of in the `video` object `streamURLs` or we skip
if (_video.streamURLs[key] == nil && [_video.streamURLs[key] isNotEqualTo:streamURLsToQuery[key]])
{
continue;
}
streamURLsToQueryMutable[key] = _video.streamURLs[key];
}

if (streamURLsToQueryMutable.count == 0)
{
//No key and value matched so we disregard `streamURLs` and simply use the `streamURLs` from the `video` object
_streamURLsToQuery = _video.streamURLs;
}
else
{
_streamURLsToQuery = streamURLsToQueryMutable.copy;
}
_cookies = [cookies copy];

_queryQueue = [NSOperationQueue new];
_queryQueue.name = [NSString stringWithFormat:@"%@ Query Queue", NSStringFromClass(self.class)];
_queryQueue.maxConcurrentOperationCount = 6; // paul_irish: Chrome re-confirmed that the 6 connections-per-host limit is the right magic number: https://code.google.com/p/chromium/issues/detail?id=285567#c14 [https://twitter.com/paul_irish/status/422808635698212864]

_operationStartSemaphore = dispatch_semaphore_create(0);
return self;
}

- (instancetype) initWithVideo:(XCDYouTubeVideo *)video cookies:(nullable NSArray<NSHTTPCookie *> *)cookies
{
return [self initWithVideo:video streamURLsToQuery:nil options:nil cookies:cookies];
}

#pragma mark - NSOperation

+ (BOOL) automaticallyNotifiesObserversForKey:(NSString *)key
Expand Down Expand Up @@ -104,9 +132,10 @@ - (void) startQuery
XCDYouTubeLogDebug(@"Starting query request for video: %@", self.video);

NSMutableArray <XCDURLHEADOperation *>*HEADOperations = [NSMutableArray new];
//Always use the `video` to check if it's a live stream (clients might not include `XCDYouTubeVideoQualityHTTPLiveStreaming` in `streamURLsToQuery`)
BOOL isHTTPLiveStream = self.video.streamURLs[XCDYouTubeVideoQualityHTTPLiveStreaming] != nil;

[self.video.streamURLs enumerateKeysAndObjectsUsingBlock:^(id _Nonnull key, NSURL * _Nonnull obj, BOOL * _Nonnull stop)
[self.streamURLsToQuery enumerateKeysAndObjectsUsingBlock:^(id _Nonnull key, NSURL * _Nonnull obj, BOOL * _Nonnull stop)
{

XCDURLHEADOperation *operation = [[XCDURLHEADOperation alloc]initWithURL:obj info:@{key : obj} cookes:self.cookies];
Expand Down

0 comments on commit 16cb754

Please sign in to comment.