feat: play resolved Miruro HLS with external VTT and stable episode progress
This commit is contained in:
@@ -248,6 +248,47 @@ int main(int argc, char **argv) {
|
||||
"https://www.youtube.com/watch?v=AAAAAAAAAA%", "https://www.youtube.com/watch?v=AAAAAAAAAA\n"})
|
||||
require(!ProgressReporter::validMediaURL(invalid), "noncanonical URLs are rejected");
|
||||
|
||||
require(ProgressReporter::validMediaIdentity("miruro:opaque_A-1:12") &&
|
||||
ProgressReporter::validMediaIdentity("miruro:a:2147483647"), "strict Miruro identities accepted");
|
||||
for (const char *invalid : {"miruro::1", "miruro:a:0", "miruro:a:01", "miruro:a:-1",
|
||||
"miruro:a:1.5", "miruro:a:2147483648", "miruro:a:1?token=secret",
|
||||
"miruro:a:b:1", "miruro:a:1\n", "miruro:bad/id:1", "miruro:é:1"})
|
||||
require(!ProgressReporter::validMediaIdentity(invalid), "ambiguous or token-bearing identities rejected");
|
||||
const auto miruroPath = root + "/miruro.jsonl";
|
||||
{
|
||||
ProgressReporter reporter(miruroPath, [] { return 0.; });
|
||||
reporter.begin("miruro:opaque_A-1:12");
|
||||
reporter.observe(sample(PLAYER_PLAYING, 95));
|
||||
reporter.begin("miruro:opaque_A-1:13");
|
||||
reporter.observe(sample(PLAYER_ENDED, 100));
|
||||
reporter.finish();
|
||||
require(reporter.error().empty(), "Miruro progress drains successfully");
|
||||
}
|
||||
NSString *miruroText = [NSString stringWithContentsOfFile:nativePath(miruroPath)
|
||||
encoding:NSUTF8StringEncoding error:nullptr];
|
||||
NSArray<NSString *> *miruroLines = [miruroText componentsSeparatedByString:@"\n"];
|
||||
require(miruroLines.count == 5, "Miruro replacement emits progress, close, ended, close");
|
||||
NSSet *miruroKeys = [NSSet setWithArray:@[@"version", @"event", @"media_identity",
|
||||
@"position_seconds", @"duration_seconds", @"completion_eligible"]];
|
||||
for (NSUInteger i = 0; i < 4; ++i) {
|
||||
NSDictionary *row = [NSJSONSerialization JSONObjectWithData:[miruroLines[i]
|
||||
dataUsingEncoding:NSUTF8StringEncoding] options:0 error:nullptr];
|
||||
require([row isKindOfClass:[NSDictionary class]] &&
|
||||
[[NSSet setWithArray:row.allKeys] isEqual:miruroKeys] &&
|
||||
[row[@"version"] intValue] == 2, "Miruro schema is v2 without media_url");
|
||||
NSDictionary *identity = row[@"media_identity"];
|
||||
require([identity isKindOfClass:[NSDictionary class]] &&
|
||||
[[NSSet setWithArray:identity.allKeys] isEqual:
|
||||
[NSSet setWithArray:@[@"provider", @"media_id", @"episode"]]] &&
|
||||
[identity[@"provider"] isEqual:@"miruro"] &&
|
||||
[identity[@"media_id"] isEqual:@"opaque_A-1"] &&
|
||||
[identity[@"episode"] intValue] == (i < 2 ? 12 : 13),
|
||||
"structured stable episode identity contains no CDN token");
|
||||
require([row[@"event"] isEqual:(i == 0 ? @"progress" : i == 2 ? @"ended" : @"closed")] &&
|
||||
[row[@"position_seconds"] doubleValue] == (i < 2 ? 95 : 100) &&
|
||||
[row[@"completion_eligible"] boolValue], "Miruro retains terminal snapshot semantics");
|
||||
}
|
||||
|
||||
const auto clockFailurePath = root + "/clock-failure.jsonl";
|
||||
{
|
||||
ProgressReporter reporter(clockFailurePath, []() -> double { throw std::runtime_error("clock failure"); });
|
||||
|
||||
Reference in New Issue
Block a user