private async Task <List <Hashtable> > DecipherFormats( List <Hashtable> formats, string videoHtmlPage) { var decodedFormats = new List <Hashtable>(); var cipheredFormats = new List <Hashtable>(); Parallel.ForEach(formats, format => { if (format.ContainsKey("signatureCipher")) { var signatureCipher = format["signatureCipher"] as string; signatureCipher?.Split('&') .Select(pair => pair.Split('=')) .ToList() .ForEach(keyAndValue => format.Add(keyAndValue[0], keyAndValue[1])); } var undecodedUrl = format["url"] as string; if (undecodedUrl != null) { format["url"] = HttpUtility.UrlDecode(undecodedUrl); } var url = format["url"] as string ?? String.Empty; if (url.Contains("signature") || (!format.ContainsKey("s") && (url.Contains("&sig=") || url.Contains("&lsig=")) ) ) { decodedFormats.Add(format); return; } cipheredFormats.Add(format); }); if (cipheredFormats.Count != 0) { var baseJsUrl = ExtractBaseJsUrl(videoHtmlPage); // TODO add exception handling // wrapped within a Lazy to prevent multiple invocations var signatureDecoderFunc = SignatureDecoderCache.GetOrAdd(baseJsUrl, new Lazy <Task <SignatureDecoder> >(() => SignatureDecoderExtractor.ExtractDecoder(baseJsUrl))); var signatureDecoder = await signatureDecoderFunc.Value; var uncipheredFormats = cipheredFormats.Select(format => { format["url"] = (format["url"] as string) + "&sig=" + signatureDecoder.Decode(format["s"] as string ?? String.Empty); return(format); }); decodedFormats.AddRange(uncipheredFormats); } return(decodedFormats); }
public StreamingDataDecoder(IYoutubeApiConfig youtubeApiConfig) { YoutubeApiConfig = youtubeApiConfig; SignatureDecoderExtractor = new SignatureDecoderExtractor(youtubeApiConfig.Client); SignatureDecoderCache = new ConcurrentDictionary <string, Lazy <Task <SignatureDecoder> > >(); }