import 'dart:convert'; import '../../extensions/helpers_extension.dart'; import '../../reverse_engineering/clients/closed_caption_client.dart' as re show ClosedCaptionClient; import '../../reverse_engineering/pages/watch_page.dart'; import '../../reverse_engineering/youtube_http_client.dart'; import '../videos.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 getManifest( dynamic videoId, {@Deprecated('Not used anymore, use track.isAutoGenerated to see if a track is autogenerated or not.') // ignore: lines_longer_than_80_chars bool autoGenerated = false, List formats = const [ ClosedCaptionFormat.srv1, ClosedCaptionFormat.srv2, ClosedCaptionFormat.srv3, ClosedCaptionFormat.ttml, ClosedCaptionFormat.vtt ]}) async { videoId = VideoId.fromString(videoId); var tracks = {}; var watchPage = await WatchPage.get(_httpClient, (videoId as VideoId).value); var playerResponse = watchPage.playerResponse!; for (final track in playerResponse.closedCaptionTrack) { for (final ext in formats) { 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 get(ClosedCaptionTrackInfo trackInfo) async { var response = await re.ClosedCaptionClient.get(_httpClient, trackInfo.url); var captions = response.closedCaptions .where((e) => !e.text.isNullOrWhiteSpace) .map((e) => ClosedCaption(e.text, e.offset, e.duration, e.parts.map((f) => ClosedCaptionPart(f.text, f.offset)))); return ClosedCaptionTrack(captions); } /// Returns the subtitles as a string. In XML format. Future getSubTitles(ClosedCaptionTrackInfo trackInfo) async { final r = await _httpClient.get(trackInfo.url); return utf8.decode(r.bodyBytes, allowMalformed: true); } }