Skip to content

Commit 95dfb3d

Browse files
committed
workaround for endless hls duration extraction
this only fixes adding m3u8 videos to playlist, there's still massive buffering when playing some with many audiotracks. no solution see media-kit/media-kit#1372
1 parent 20c60f8 commit 95dfb3d

1 file changed

Lines changed: 76 additions & 1 deletion

File tree

lib/models/player.dart

Lines changed: 76 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,13 @@ class PlayerModel extends ChangeNotifier {
4747

4848
PlayerModel(this.app, this.playlist) {
4949
player = Player();
50-
controller = VideoController(player);
50+
controller = VideoController(
51+
player,
52+
// configuration: VideoControllerConfiguration(
53+
// androidAttachSurfaceAfterVideoParameters: false,
54+
// enableHardwareAcceleration: false,
55+
// ),
56+
);
5157

5258
player.stream.completed.listen((completed) {
5359
if (completed) {
@@ -194,6 +200,18 @@ class PlayerModel extends ChangeNotifier {
194200
LocalClosedCaptionFile? get currentCaptions => _currentCaptions;
195201

196202
Future<double> getVideoDuration(String url) async {
203+
// mediakit will parse segment durations for a minute while loading them,
204+
// so we need to parse times itself (or make better exoplayer fork)
205+
if (url.contains('.m3u8')) {
206+
try {
207+
final duration = await _getHlsDuration(url);
208+
print('[getVideoDuration] HLS parsed duration=$duration');
209+
return duration;
210+
} catch (e) {
211+
print('[getVideoDuration] HLS parse failed: $e');
212+
return 0;
213+
}
214+
}
197215
if (url.contains('youtu')) {
198216
url = (await getYoutubeVideoUrl(url)).video;
199217
}
@@ -212,6 +230,63 @@ class PlayerModel extends ChangeNotifier {
212230
return duration.inMilliseconds / 1000;
213231
}
214232

233+
Future<double> _getHlsDuration(String url) async {
234+
final response = await http
235+
.get(Uri.parse(url))
236+
.timeout(const Duration(seconds: 10));
237+
238+
if (response.statusCode != 200) {
239+
// print('[_getHlsDuration] bad status: ${response.statusCode}');
240+
return 0;
241+
}
242+
243+
final body = response.body;
244+
// print('[_getHlsDuration] manifest length=${body.length}');
245+
246+
// Master playlist — find and follow the first variant stream
247+
if (body.contains('#EXT-X-STREAM-INF')) {
248+
// print('[_getHlsDuration] master playlist detected, following first variant');
249+
final lines = body
250+
.split('\n')
251+
.map((l) => l.trim())
252+
.where((l) => l.isNotEmpty)
253+
.toList();
254+
for (var i = 0; i < lines.length; i++) {
255+
if (lines[i].startsWith('#EXT-X-STREAM-INF') && i + 1 < lines.length) {
256+
var variantUrl = lines[i + 1];
257+
if (!variantUrl.startsWith('http')) {
258+
final base = Uri.parse(url);
259+
variantUrl = base.resolve(variantUrl).toString();
260+
}
261+
// print('[_getHlsDuration] variant url=$variantUrl');
262+
return _getHlsDuration(variantUrl);
263+
}
264+
}
265+
// print('[_getHlsDuration] no variant found in master playlist');
266+
return 0;
267+
}
268+
269+
// Media playlist — sum #EXTINF durations
270+
// Also check for #EXT-X-ENDLIST (VOD vs live)
271+
final isVod = body.contains('#EXT-X-ENDLIST');
272+
273+
if (!isVod) {
274+
// Live stream — return sentinel value
275+
// print('[_getHlsDuration] live stream, returning sentinel 356400');
276+
return 356400.0;
277+
}
278+
279+
double total = 0;
280+
final extinfReg = RegExp(r'#EXTINF:([\d.]+)');
281+
for (final match in extinfReg.allMatches(body)) {
282+
final seg = double.tryParse(match.group(1)!) ?? 0;
283+
total += seg;
284+
}
285+
286+
// print('[_getHlsDuration] summed ${extinfReg.allMatches(body).length} segments, total=$total');
287+
return total;
288+
}
289+
215290
static String extractVideoId(String url) {
216291
if (url.contains('youtu.be/')) {
217292
return RegExp(r'youtu.be\/([A-z0-9_-]+)').firstMatch(url)!.group(1)!;

0 commit comments

Comments
 (0)