/// <summary> /// Accepts <paramref name="cipher"/> as a URI-encoded string in the following form: /// <para>sp=sig&s=wggj6zg7m-...&url=https%3A%2F%2Fr5---sn-5hnekn7k.googlevideo.com%2Fvideoplayback...</para> /// Extracts its useful encoded parameters and puts them into the specified <paramref name="streamInfo"/>. /// </summary> protected virtual void UpdateStreamCipherInfo(YouTubeStreamInfo streamInfo, string cipher) { if (string.IsNullOrEmpty(cipher)) { throw new ArgumentNullException(nameof(cipher)); } var map = YouTubeUtils.ExtractUrlEncodedParamMap(cipher); streamInfo.Url = map.GetOrDefault("url") ?? throw new GrabParseException("Failed to extract URL from cipher."); streamInfo.Signature = map.GetOrDefault("s") ?? throw new GrabParseException("Failed to extract signature from cipher."); }
/// <summary> /// Appends the specified <paramref name="stream"/> to the specified <paramref name="result"/>. /// </summary> protected virtual void AppendStreamToResult(GrabResult result, YouTubeStreamInfo stream) { MediaChannels channels; // get iTag info var itagInfo = stream.iTag == null ? null : YouTubeTags.For(stream.iTag.Value); // extract extension from mime var extension = stream.Extension ?? stream.Mime?.Split('/')?.Last(); // decide according to stream type - adaptive, or muxed if (stream is YouTubeMuxedStream muxedStream) { // Muxed stream channels = MediaChannels.Both; } else if (stream is YouTubeAdaptiveStream adaptiveStream) { // Adaptive stream var hasVideo = itagInfo?.HasVideo ?? stream.Mime.StartsWith("video"); channels = hasVideo ? MediaChannels.Video : MediaChannels.Audio; } else { throw new NotSupportedException($"YouTube stream of type {stream.GetType()} is not implemented in {nameof(YouTubeGrabber)}.{nameof(AppendStreamToResult)}."); } var format = new MediaFormat(stream.Mime, extension); var grabbed = new GrabbedMedia(new Uri(stream.Url), null, format, channels); result.Resources.Add(grabbed); // update grabbed media iTag info if (itagInfo != null) { UpdateStreamITagInfo(grabbed, itagInfo.Value); } }