Skip to content

Commit

Permalink
Implement proper support for multiple audio tracks and new client to …
Browse files Browse the repository at this point in the history
…fetch more streams.
  • Loading branch information
Hexer10 committed Apr 1, 2024
1 parent 6ab8ce3 commit f4587df
Show file tree
Hide file tree
Showing 18 changed files with 322 additions and 141 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -63,4 +63,4 @@ abstract class StreamInfoProvider {

///
AudioTrack? get audioTrack => null;
}
}
4 changes: 2 additions & 2 deletions lib/src/reverse_engineering/player/player_response.dart
Original file line number Diff line number Diff line change
Expand Up @@ -96,8 +96,8 @@ class PlayerResponse {
// extract the preview video ID using regex.
?.replaceAll('-', '+')
.replaceAll('_', '/')
.pipe(base64.decode)
.pipe(utf8.decode)
.pipe((e) => base64.decode(e))
.pipe((e) => utf8.decode(e, allowMalformed: true))
.pipe(
(value) => RegExp('video_id=(.{11})').firstMatch(value)?.group(1),
)
Expand Down
41 changes: 33 additions & 8 deletions lib/src/reverse_engineering/youtube_http_client.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import 'dart:async';
import 'dart:convert';

import 'package:collection/collection.dart';
import 'package:http/http.dart' as http;

import '../exceptions/exceptions.dart';
Expand Down Expand Up @@ -142,6 +143,7 @@ class YoutubeHttpClient extends http.BaseClient {
bool validate = true,
int start = 0,
int errorCount = 0,
required StreamClient streamClient,
}) {
if (streamInfo.fragments.isNotEmpty) {
// DASH(fragmented) stream
Expand All @@ -156,6 +158,7 @@ class YoutubeHttpClient extends http.BaseClient {
// Normal stream
return _getStream(
streamInfo,
streamClient: streamClient,
headers: headers,
validate: validate,
start: start,
Expand Down Expand Up @@ -187,24 +190,45 @@ class YoutubeHttpClient extends http.BaseClient {
bool validate = true,
int start = 0,
int errorCount = 0,
required StreamClient streamClient,
}) async* {
final url = streamInfo.url;
var url = streamInfo.url;
var bytesCount = start;

while (!_closed && bytesCount != streamInfo.size.totalBytes) {
try {
final response = await retry(this, () {
final response = await retry(this, () async {
final from = bytesCount;
final to = (streamInfo.isThrottled
? (bytesCount + 9898989)
? (bytesCount + 10379935)
: streamInfo.size.totalBytes) -
1;
final request =
http.Request('get', url.setQueryParam('range', '$from-$to'));

late final http.Request request;
if (url.queryParameters['c'] == 'ANDROID') {
request = http.Request('get', url);
request.headers['Range'] = 'bytes=$from-$to';
} else {
request =
http.Request('get', url.setQueryParam('range', '$from-$to'));
}
return send(request);
});
if (validate) {
_validateResponse(response, response.statusCode);
try {
_validateResponse(response, response.statusCode);
} on FatalFailureException {
final newManifest =
await streamClient.getManifest(streamInfo.videoId);
final stream = newManifest.streams
.firstWhereOrNull((e) => e.tag == streamInfo.tag);
if (stream == null) {
print(
'Error: Could not find the stream in the new manifest (due to Youtube error)');
rethrow;
}
url = stream.url;
continue;
}
}
final stream = StreamController<List<int>>();
response.stream.listen(
Expand All @@ -227,6 +251,7 @@ class YoutubeHttpClient extends http.BaseClient {
await Future.delayed(const Duration(milliseconds: 500));
yield* _getStream(
streamInfo,
streamClient: streamClient,
headers: headers,
validate: validate,
start: bytesCount,
Expand Down Expand Up @@ -316,7 +341,7 @@ class YoutubeHttpClient extends http.BaseClient {
}
});

//print(request);
// print(request);
// print(StackTrace.current);
return _httpClient.send(request);
}
Expand Down
2 changes: 0 additions & 2 deletions lib/src/videos/streams/mixins/audio_stream_info.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,4 @@ mixin AudioStreamInfo on StreamInfo {

/// Audio track which describes the language of the audio.
AudioTrack? get audioTrack;


}
19 changes: 17 additions & 2 deletions lib/src/videos/streams/mixins/stream_info.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ import '../models/audio_track.dart';

/// Generic YouTube media stream.
mixin StreamInfo {
/// The video id of the video this stream belongs to.
VideoId get videoId;

/// Whether the stream is throttled or not.
bool get isThrottled =>
url.queryParameters['ratebypass']?.toLowerCase() != 'yes';
Expand Down Expand Up @@ -42,7 +45,18 @@ mixin StreamInfo {
/// Extension for Iterables of StreamInfo.
extension StreamInfoIterableExt<T extends StreamInfo> on Iterable<T> {
/// Gets the stream with highest bitrate.
T withHighestBitrate() => sortByBitrate().first;
T withHighestBitrate({String? language}) {
return where((stream) {
if (stream is AudioStreamInfo) {
if (language == null &&
(stream.audioTrack == null || stream.audioTrack!.audioIsDefault)) {
return true;
}
return stream.audioTrack?.id == language;
}
return true;
}).sortByBitrate().first;
}

/// Gets the video streams sorted by bitrate in ascending order.
/// This returns new list without editing the original list.
Expand All @@ -64,7 +78,8 @@ extension StreamInfoIterableExt<T extends StreamInfo> on Iterable<T> {
if (e is VideoOnlyStreamInfo) 'video only',
if (e is MuxedStreamInfo) 'muxed',
e.size,
if (e case AudioStreamInfo(:AudioTrack audioTrack)) audioTrack.displayName,
if (e case AudioStreamInfo(:AudioTrack audioTrack))
audioTrack.displayName,
]);
}
return column.toString();
Expand Down
4 changes: 2 additions & 2 deletions lib/src/videos/streams/models/audio_track.dart
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,6 @@ class AudioTrack with _$AudioTrack {
required String id,
required bool audioIsDefault}) = _AudioTrack;

factory AudioTrack.fromJson(Map<String, Object?> json)
=> _$AudioTrackFromJson(json);
factory AudioTrack.fromJson(Map<String, Object?> json) =>
_$AudioTrackFromJson(json);
}
Loading

0 comments on commit f4587df

Please sign in to comment.