Request playback with non-site player types and swap to backup Usher streams on stitched ad breaks; inject vendored vaft into the legacy WebView and expose a Settings toggle. Co-authored-by: Cursor <cursoragent@cursor.com>
84 lines
2.5 KiB
Dart
84 lines
2.5 KiB
Dart
import 'package:flutter_test/flutter_test.dart';
|
|
import 'package:frosty/apis/twitch_ad_solutions.dart';
|
|
|
|
void main() {
|
|
group('TwitchAdSolutions', () {
|
|
test('playlistContainsAds detects stitched signifier', () {
|
|
expect(
|
|
TwitchAdSolutions.playlistContainsAds(
|
|
'#EXTM3U\n#EXT-X-DATERANGE:ID="stitched-ad-1"\n',
|
|
),
|
|
isTrue,
|
|
);
|
|
expect(
|
|
TwitchAdSolutions.playlistContainsAds('#EXTM3U\n#EXTINF:2.0,\nlive.ts\n'),
|
|
isFalse,
|
|
);
|
|
});
|
|
|
|
test('playlistIsMidroll detects midroll markers', () {
|
|
expect(
|
|
TwitchAdSolutions.playlistIsMidroll('X-TV-TWITCH-AD-POD-TYPE="MIDROLL"'),
|
|
isTrue,
|
|
);
|
|
expect(TwitchAdSolutions.playlistIsMidroll('live only'), isFalse);
|
|
});
|
|
|
|
test('platformForPlayerType matches vaft', () {
|
|
expect(TwitchAdSolutions.platformForPlayerType('autoplay'), 'android');
|
|
expect(TwitchAdSolutions.platformForPlayerType('popout'), 'web');
|
|
expect(TwitchAdSolutions.platformForPlayerType('embed'), 'web');
|
|
});
|
|
|
|
test('force / backup player types match vaft defaults', () {
|
|
expect(TwitchAdSolutions.forceAccessTokenPlayerType, 'popout');
|
|
expect(TwitchAdSolutions.backupPlayerTypes, [
|
|
'embed',
|
|
'popout',
|
|
'autoplay',
|
|
]);
|
|
expect(TwitchAdSolutions.fallbackPlayerType, 'embed');
|
|
});
|
|
});
|
|
|
|
group('TwitchAdFreePlayback.firstMediaPlaylistUrl', () {
|
|
test('resolves relative media playlist against base', () {
|
|
const master = '''
|
|
#EXTM3U
|
|
#EXT-X-STREAM-INF:BANDWIDTH=1000,RESOLUTION=640x360
|
|
360p.m3u8
|
|
''';
|
|
final url = TwitchAdFreePlayback.firstMediaPlaylistUrl(
|
|
master,
|
|
baseUrl: 'https://usher.ttvnw.net/api/v2/channel/hls/foo.m3u8?x=1',
|
|
);
|
|
expect(
|
|
url,
|
|
'https://usher.ttvnw.net/api/v2/channel/hls/360p.m3u8',
|
|
);
|
|
});
|
|
|
|
test('keeps absolute media playlist URLs', () {
|
|
const master = '''
|
|
#EXTM3U
|
|
#EXT-X-STREAM-INF:BANDWIDTH=1000
|
|
https://video-weaver.example/v1/playlist/abc.m3u8
|
|
''';
|
|
final url = TwitchAdFreePlayback.firstMediaPlaylistUrl(
|
|
master,
|
|
baseUrl: 'https://usher.ttvnw.net/foo.m3u8',
|
|
);
|
|
expect(url, 'https://video-weaver.example/v1/playlist/abc.m3u8');
|
|
});
|
|
|
|
test('returns null when no media URI present', () {
|
|
expect(
|
|
TwitchAdFreePlayback.firstMediaPlaylistUrl(
|
|
'#EXTM3U\n',
|
|
baseUrl: 'https://example.com/x.m3u8',
|
|
),
|
|
isNull,
|
|
);
|
|
});
|
|
});
|
|
}
|