computeManifests function

Future<VideoManifest> computeManifests(
  1. ComputeManifestParams params
)

Used to be able to get both manifests at the same time.

Implementation

Future<VideoManifest> computeManifests(ComputeManifestParams params) async {
  YoutubeExplode yt = YoutubeExplode();

  List<Future> futures = [
    yt.videos.streamsClient.getManifest(params.videoId),
  ];
  if (params.closedCaptionsManifest == null) {
    futures.add(yt.videos.closedCaptions.getManifest(
      params.videoId,
      formats: [ClosedCaptionFormat.vtt],
    ));
  }

  final manifests = await Future.wait(futures);
  StreamManifest streamManifest = manifests.elementAt(0) as StreamManifest;
  late ClosedCaptionManifest closedCaptionManifest;

  if (params.closedCaptionsManifest != null) {
    closedCaptionManifest = params.closedCaptionsManifest!;
  } else {
    closedCaptionManifest = manifests.elementAt(1) as ClosedCaptionManifest;
  }

  Map<String, ClosedCaptionTrackInfo> tracksByLanguage = {};
  List<ClosedCaptionTrackInfo> ccTracks =
      closedCaptionManifest.tracks.where((e) => !e.isAutoGenerated).toList();
  List<ClosedCaptionTrackInfo> autoTracks =
      closedCaptionManifest.tracks.where((e) => e.isAutoGenerated).toList();

  for (ClosedCaptionTrackInfo track in [...ccTracks, ...autoTracks]) {
    String shortCode = track.language.code.substring(0, 2);
    tracksByLanguage.putIfAbsent(shortCode, () => track);
  }

  List<MapEntry<String, String>> entries = [];

  if (!params.subtitlesCached) {
    entries.addAll(
      await Future.wait(
        tracksByLanguage.values.map(
          (trackInfo) async {
            String shortCode = trackInfo.language.code.substring(0, 2);
            String subtitles =
                await yt.videos.closedCaptions.getSubTitles(trackInfo);

            if (trackInfo.isAutoGenerated) {
              subtitles = subtitles.replaceAllMapped(vttRegex, (match) {
                String text = match.group(11) ?? '';
                List<String> lines = text.split('\n');

                String currentSubtitle =
                    subtitles.substring(match.start, match.end);
                int position = currentSubtitle
                    .lastIndexOf(text)
                    .clamp(0, currentSubtitle.length);

                String newSubtitle = currentSubtitle.replaceFirst(
                    text, lines.last.trim(), position);

                return newSubtitle;
              });
            }

            String metadata = trackInfo.isAutoGenerated
                ? 'YouTube - Auto - [$shortCode]'
                : 'YouTube - CC - [$shortCode]';

            return MapEntry(metadata, subtitles);
          },
        ),
      ),
    );
  }

  Map<String, String> subtitlesByLanguageCache = {};
  for (MapEntry<String, String> entry in entries) {
    subtitlesByLanguageCache.putIfAbsent(entry.key, () => entry.value);
  }

  return VideoManifest(
    streamManifest: streamManifest,
    closedCaptionManifest: closedCaptionManifest,
    subtitlesByLanguageCache: subtitlesByLanguageCache,
  );
}