Skip to content
This repository has been archived by the owner on Jan 3, 2019. It is now read-only.

Commit

Permalink
4oD: Search for MP4 versions of HDS programmes
Browse files Browse the repository at this point in the history
Some 4oD programmes available via HTTP Dynamic Streaming may also be
available in normal MP4 versions catalogued with programme IDs close
to the default HDS programme ID (h/t: 4od-dl).  GiA will search adjacent
PIDs for MP4 versions of a programme, preferring the PS3 version to any
other MP4 version.  The range of PIDs searched is +/- the value of the
hidden preference 4oD_MP4SearchRange (default=10). The MP4 search may be
disabled by setting boolean hidden preference 4oD_SkipMP4Search to YES.
Alternate MP4 version are generally only available for one month after
transmission.
  • Loading branch information
[email protected] committed Dec 30, 2012
1 parent 588e90e commit 1b4ab87
Show file tree
Hide file tree
Showing 3 changed files with 189 additions and 22 deletions.
207 changes: 186 additions & 21 deletions FourODDownload.m
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ - (void)launchMetaRequest

-(void)dataRequestFinished:(ASIHTTPRequest *)request
{
if (!running)
return;
NSString *responseString = [[NSString alloc] initWithData:[request responseData] encoding:NSUTF8StringEncoding];
BOOL verbose = [[NSUserDefaults standardUserDefaults] boolForKey:@"Verbose"];
if (verbose)
Expand Down Expand Up @@ -103,6 +105,8 @@ -(void)dataRequestFinished:(ASIHTTPRequest *)request

-(void)hostLookupFinished:(NSHost *)aHost
{
if (!running)
return;
NSString *hostAddr = nil;
if (aHost)
hostAddr = [aHost address];
Expand All @@ -118,7 +122,6 @@ -(void)hostLookupFinished:(NSHost *)aHost
NSLog(@"Metadata URL: %@",requestURL);
[self addToLog:[NSString stringWithFormat:@"INFO: Metadata URL: %@", requestURL] noTag:YES];


ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:requestURL];
[request setDidFinishSelector:@selector(metaRequestFinished:)];
[request setTimeOutSeconds:10];
Expand Down Expand Up @@ -180,8 +183,20 @@ -(void)hostLookupFinished:(NSHost *)aHost
[self addToLog:@"INFO: Requesting Metadata." noTag:YES];
[request startAsynchronous];
}

-(void)metaRequestFinished:(ASIHTTPRequest *)request
{
if (!running)
return;
BOOL skipSearch = [[NSUserDefaults standardUserDefaults] boolForKey:[NSString stringWithFormat:@"%@SkipMP4Search", defaultsPrefix]];
NSInteger searchRange = [[NSUserDefaults standardUserDefaults] integerForKey:[NSString stringWithFormat:@"%@MP4SearchRange", defaultsPrefix]];
if (!searchRange)
searchRange = 10;
NSInteger realPID = [[show realPID] integerValue];
NSInteger minPID = realPID - searchRange;
NSInteger maxPID = realPID + searchRange;
NSInteger currentPID = [request tag];
NSString *mp4UriData = [[request userInfo] valueForKey:@"mp4UriData"];
NSLog(@"Response Status Code: %ld",(long)[request responseStatusCode]);
if ([request responseStatusCode] == 0)
{
Expand All @@ -201,13 +216,37 @@ -(void)metaRequestFinished:(ASIHTTPRequest *)request
}
else if ([request responseStatusCode] != 200)
{
[self addToLog:@"ERROR: Could not retrieve program metadata." noTag:YES];
[show setSuccessful:[NSNumber numberWithBool:NO]];
[show setComplete:[NSNumber numberWithBool:YES]];
[show setValue:@"Download Failed" forKey:@"status"];
[nc postNotificationName:@"DownloadFinished" object:show];
[self addToLog:@"Download Failed" noTag:NO];
return;
if (currentPID != 0)
{
if (currentPID < maxPID)
{
if (realPID - currentPID == 1)
++currentPID;
[self retryMetaRequest:request pid:++currentPID];
return;
}
else
{
[self addToLog:@"GiA does not support downloading this show."];
[self addToLog:@" HTTP Dynamic Streaming Detected"];
[show setReasonForFailure:@"4oDHTTP"];
[show setComplete:[NSNumber numberWithBool:YES]];
[show setSuccessful:[NSNumber numberWithBool:NO]];
[show setValue:@"Download Failed" forKey:@"status"];
[nc postNotificationName:@"DownloadFinished" object:show];
return;
}
}
else
{
[self addToLog:@"ERROR: Could not retrieve program metadata." noTag:YES];
[show setSuccessful:[NSNumber numberWithBool:NO]];
[show setComplete:[NSNumber numberWithBool:YES]];
[show setValue:@"Download Failed" forKey:@"status"];
[nc postNotificationName:@"DownloadFinished" object:show];
[self addToLog:@"Download Failed" noTag:NO];
return;
}
}


Expand All @@ -221,27 +260,124 @@ -(void)metaRequestFinished:(ASIHTTPRequest *)request

NSScanner *scanner = [NSScanner scannerWithString:responseString];

if ([responseString rangeOfString:@"f4m"].location != NSNotFound)
NSString *programmeNumber = nil;
[scanner scanUpToString:@"<programmeNumber>" intoString:nil];
[scanner scanString:@"<programmeNumber>" intoString:nil];
[scanner scanUpToString:@"</programmeNumber>" intoString:&programmeNumber];

NSString *brandTitle = nil;
[scanner scanUpToString:@"<brandTitle>" intoString:nil];
[scanner scanString:@"<brandTitle>" intoString:nil];
[scanner scanUpToString:@"</brandTitle>" intoString:&brandTitle];

NSString *uriData = nil;
[scanner scanUpToString:@"<uriData>" intoString:nil];
[scanner scanString:@"<uriData>" intoString:nil];
[scanner scanUpToString:@"</uriData>" intoString:&uriData];

scanner = [NSScanner scannerWithString:uriData];
[scanner scanUpToString:@"<streamUri>" intoString:nil];
[scanner scanString:@"<streamUri>" intoString:nil];
NSString *streamUri = nil;
[scanner scanUpToString:@"</" intoString:&streamUri];

if (verbose)
[self addToLog:[NSString stringWithFormat:@"DEBUG: Metadata Processed: programmeNumber=%@ brandTitle=%@ uriData=%@ streamUri=%@", programmeNumber, brandTitle, uriData, streamUri]];

if ([streamUri hasSuffix:@".f4m"])
{
if (skipSearch)
{
[self addToLog:@"GiA does not support downloading this show."];
[self addToLog:@" HTTP Dynamic Streaming Detected"];
[show setReasonForFailure:@"4oDHTTP"];
[show setComplete:[NSNumber numberWithBool:YES]];
[show setSuccessful:[NSNumber numberWithBool:NO]];
[show setValue:@"Download Failed" forKey:@"status"];
[nc postNotificationName:@"DownloadFinished" object:show];
return;
}
if (currentPID == 0)
{
[self retryMetaRequest:request pid:minPID brandTitle:brandTitle programmeNumber:programmeNumber];
return;
}
else if (currentPID < maxPID)
{
if (realPID - currentPID == 1)
++currentPID;
[self retryMetaRequest:request pid:++currentPID];
return;
}
}
else if ([streamUri hasSuffix:@".mp4"])
{
if (currentPID != 0)
{
if ([brandTitle isEqualToString:[[request userInfo] valueForKey:@"brandTitle"]] && [programmeNumber isEqualToString:[[request userInfo] valueForKey:@"programmeNumber"]])
{
[self addToLog:[NSString stringWithFormat:@"INFO: MP4 Stream Found: %@", streamUri] noTag:YES];
mp4UriData = uriData;
if ([streamUri rangeOfString:@"PS3" options:NSCaseInsensitiveSearch].location == NSNotFound)
{
if (currentPID < maxPID)
{
if (realPID - currentPID == 1)
++currentPID;
[self retryMetaRequest:request pid:++currentPID brandTitle:brandTitle programmeNumber:programmeNumber mp4UriData:mp4UriData];
return;
}
}
}
else if (currentPID < maxPID)
{
if (realPID - currentPID == 1)
++currentPID;
[self retryMetaRequest:request pid:++currentPID];
return;
}
}
else
{
mp4UriData = uriData;
}
}
else if (currentPID != 0)
{
if (currentPID < maxPID)
{
if (realPID - currentPID == 1)
currentPID++;
[self retryMetaRequest:request pid:++currentPID];
return;
}
}

if (!mp4UriData)
{
[self addToLog:@"GiA does not support downloading this show."];
[self addToLog:@" HTTP Dynamic Streaming Detected"];
if (currentPID != 0)
{
[self addToLog:@" HTTP Dynamic Streaming Detected"];
[show setReasonForFailure:@"4oDHTTP"];
}
else
{
[self addToLog:@" Did not find suitable download format"];
[show setReasonForFailure:@"4oDFormat"];
}
[show setComplete:[NSNumber numberWithBool:YES]];
[show setSuccessful:[NSNumber numberWithBool:NO]];
[show setValue:@"Download Failed" forKey:@"status"];
[show setReasonForFailure:@"4oDHTTP"];
[nc postNotificationName:@"DownloadFinished" object:show];
return;
}

NSString *uriData = nil;
[scanner scanUpToString:@"<uriData>" intoString:nil];
[scanner scanString:@"<uriData>" intoString:nil];
[scanner scanUpToString:@"</uriData>" intoString:&uriData];

uriData = mp4UriData;
scanner = [NSScanner scannerWithString:uriData];
[scanner scanUpToString:@"<streamUri>" intoString:nil];
[scanner scanString:@"<streamUri>" intoString:nil];
NSString *streamUri = nil;
streamUri = nil;
[scanner scanUpToString:@"</" intoString:&streamUri];
[scanner scanUpToString:@"<token>" intoString:nil];
[scanner scanString:@"<token>" intoString:nil];
Expand All @@ -254,15 +390,15 @@ -(void)metaRequestFinished:(ASIHTTPRequest *)request

NSString *decodedToken = [self decodeToken:token];
NSLog(@"%@",decodedToken);

if (verbose)
[self addToLog:[NSString stringWithFormat:@"DEBUG: Metadata Processed: uriData=%@ streamUri=%@ token=%@ cdn=%@ decodedToken=%@", uriData, streamUri, token, cdn, decodedToken]];
if (!(uriData && streamUri && token && cdn && decodedToken))
[NSException raise:@"Parsing Error." format:@"Could not process 4oD Metadata"];


NSString *auth = nil, *rtmpURL = nil, *app = nil, *playpath = nil;
@try
{
if (!(uriData && streamUri && token && cdn && decodedToken))
[NSException raise:@"Parsing Error." format:@"Could not process 4oD Metadata"];

if ([cdn isEqualToString:@"ll"])
{
[scanner setScanLocation:0];
Expand Down Expand Up @@ -399,6 +535,35 @@ -(void)metaRequestFinished:(ASIHTTPRequest *)request

}

-(void)retryMetaRequest:(ASIHTTPRequest *)request pid:(NSInteger)pid pidInfo:(NSDictionary *)pidInfo
{
NSURL *retryURL = [[[request url] URLByDeletingLastPathComponent] URLByAppendingPathComponent:[NSString stringWithFormat:@"%ld", pid]];
[self addToLog:[NSString stringWithFormat:@"INFO: Retry Metadata URL: %@", retryURL] noTag:YES];
ASIHTTPRequest *retryRequest = [[request copy] autorelease];
[retryRequest setURL:retryURL];
[retryRequest setTag:pid];
if (pidInfo)
[retryRequest setUserInfo:pidInfo];
[retryRequest startAsynchronous];
}

-(void)retryMetaRequest:(ASIHTTPRequest *)request pid:(NSInteger)pid
{
[self retryMetaRequest:request pid:pid pidInfo:nil];
}

-(void)retryMetaRequest:(ASIHTTPRequest *)request pid:(NSInteger)pid brandTitle:(NSString *)brandTitle programmeNumber:(NSString *)programmeNumber
{
NSDictionary *pidInfo = [NSDictionary dictionaryWithObjectsAndKeys: brandTitle, @"brandTitle", programmeNumber, @"programmeNumber", nil];
[self retryMetaRequest:request pid:pid pidInfo:pidInfo];
}

-(void)retryMetaRequest:(ASIHTTPRequest *)request pid:(NSInteger)pid brandTitle:(NSString *)brandTitle programmeNumber:(NSString *)programmeNumber mp4UriData:(NSString *)mp4UriData
{
NSDictionary *pidInfo = [NSDictionary dictionaryWithObjectsAndKeys: brandTitle, @"brandTitle", programmeNumber, @"programmeNumber", mp4UriData, @"mp4UriData", nil];
[self retryMetaRequest:request pid:pid pidInfo:pidInfo];
}

- (NSString *)decodeToken:(NSString *)string
{
PyObject *pName, *pModule, *pFunc;
Expand Down
2 changes: 1 addition & 1 deletion Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>482</string>
<string>483</string>
<key>CFBundleShortVersionString</key>
<string>1.5.1</string>
<key>NSMainNibFile</key>
Expand Down
2 changes: 2 additions & 0 deletions ReasonsForFailure.plist
Original file line number Diff line number Diff line change
Expand Up @@ -40,5 +40,7 @@ proxy option in preferences.</string>
<key>4oDHTTP</key>
<string>GiA does not support downloading HTTP Dynamic Streaming programmes from 4oD.
Unfortunately, this show is one of them.</string>
<key>4oDFormat</key>
<string>GiA did not find a suitable download format for the 4oD programme.</string>
</dict>
</plist>

0 comments on commit 1b4ab87

Please sign in to comment.