youtube_explode/lib/src/videos/closed_captions/closed_caption_client.dart

96 lines
3.5 KiB
Dart

import 'package:xml/xml.dart' as xml;
import '../../extensions/helpers_extension.dart';
import '../../reverse_engineering/responses/responses.dart'
hide ClosedCaption, ClosedCaptionPart, ClosedCaptionTrack;
import '../../reverse_engineering/youtube_http_client.dart';
import '../videos.dart';
import 'closed_caption.dart';
import 'closed_caption_format.dart';
import 'closed_caption_manifest.dart';
import 'closed_caption_part.dart';
import 'closed_caption_track.dart';
import 'closed_caption_track_info.dart';
import 'language.dart';
/// Queries related to closed captions of YouTube videos.
class ClosedCaptionClient {
final YoutubeHttpClient _httpClient;
/// Initializes an instance of [ClosedCaptionClient]
ClosedCaptionClient(this._httpClient);
/// Gets the manifest that contains information
/// about available closed caption tracks in the specified video.
Future<ClosedCaptionManifest> getManifest(dynamic videoId,
{bool autoGenerated = false}) async {
videoId = VideoId.fromString(videoId);
var tracks = <ClosedCaptionTrackInfo>[];
if (!autoGenerated) {
var subList = await _httpClient.get(
'https://video.google.com/timedtext?hl=en&type=list&v=${videoId.value}',
validate: true);
// ignore: deprecated_member_use
var content = xml.parse(subList.body);
var langList = <String>[];
for (var track in content.findAllElements('track')) {
var lang = track.getAttribute('lang_code');
if (langList.contains(lang)) {
continue;
}
langList.add(lang);
for (var ext in ClosedCaptionFormat.values) {
tracks.add(ClosedCaptionTrackInfo(
Uri.parse('https://www.youtube.com/api/timedtext')
.replaceQueryParameters({
'lang': lang,
'v': videoId.value,
'fmt': ext.formatCode,
'name': track.getAttribute('name'),
}),
Language(lang, track.getAttribute('lang_translated')),
format: ext));
}
}
if (langList.isEmpty) {
return ClosedCaptionManifest([]);
}
return ClosedCaptionManifest(tracks);
} else {
var videoInfoResponse =
await VideoInfoResponse.get(_httpClient, videoId.value);
var playerResponse = videoInfoResponse.playerResponse;
for (var track in playerResponse.closedCaptionTrack) {
for (var ext in ClosedCaptionFormat.values) {
tracks.add(ClosedCaptionTrackInfo(
Uri.parse(track.url)
.replaceQueryParameters({'fmt': ext.formatCode}),
Language(track.languageCode, track.languageName),
isAutoGenerated: track.autoGenerated,
format: ext));
}
}
}
return ClosedCaptionManifest(tracks);
}
/// Gets the actual closed caption track which is
/// identified by the specified metadata.
Future<ClosedCaptionTrack> get(ClosedCaptionTrackInfo trackInfo) async {
var response = await ClosedCaptionTrackResponse.get(
_httpClient, trackInfo.url);
var captions = response.closedCaptions
.where((e) => !e.text.isNullOrWhiteSpace)
.map((e) => ClosedCaption(e.text, e.offset, e.duration,
e.getParts().map((f) => ClosedCaptionPart(f.text, f.offset))));
return ClosedCaptionTrack(captions);
}
/// Returns the subtitles as a string.
Future<String> getSubTitles(ClosedCaptionTrackInfo trackInfo) =>
_httpClient.getString(trackInfo.url);
}