public IEnumerable <ICipherOperation> GetCipherOperations() { string?TryGetDeciphererFuncBody() { var funcName = Regex.Match(_root, @"(\w+)=function\(\w+\){(\w+)=\2\.split\(\x22{2}\);.*?return\s+\2\.join\(\x22{2}\)}") .Groups[1] .Value; var escapedFuncName = Regex.Escape(funcName); return(Regex.Match(_root, $@"(?!h\.){escapedFuncName}=function\(\w+\)\{{(.*?)\}}", RegexOptions.Singleline) .Groups[1] .Value .NullIfWhiteSpace()); } string?TryGetDeciphererDefinitionBody(string deciphererFuncBody) { var funcName = Regex.Match(deciphererFuncBody, "(\\w+).\\w+\\(\\w+,\\d+\\);") .Groups[1] .Value; var escapedFuncName = Regex.Escape(funcName); return(Regex.Match(_root, $@"var\s+{escapedFuncName}=\{{(\w+:function\(\w+(,\w+)?\)\{{(.*?)\}}),?\}};", RegexOptions.Singleline) .Groups[0] .Value .NullIfWhiteSpace()); } var deciphererFuncBody = TryGetDeciphererFuncBody() ?? throw FatalFailureException.Generic("Could not find signature decipherer function body."); var deciphererDefinitionBody = TryGetDeciphererDefinitionBody(deciphererFuncBody) ?? throw FatalFailureException.Generic("Could not find signature decipherer definition body."); // Analyze statements to determine cipher function names foreach (var statement in deciphererFuncBody.Split(";")) { // Get the name of the function called in this statement var calledFuncName = Regex.Match(statement, @"\w+(?:.|\[)(\""?\w+(?:\"")?)\]?\(").Groups[1].Value; if (string.IsNullOrWhiteSpace(calledFuncName)) { continue; } // Slice if (Regex.IsMatch(deciphererDefinitionBody, $@"{Regex.Escape(calledFuncName)}:\bfunction\b\([a],b\).(\breturn\b)?.?\w+\.")) { var index = Regex.Match(statement, @"\(\w+,(\d+)\)").Groups[1].Value.ParseInt(); yield return(new SliceCipherOperation(index)); } // Swap else if (Regex.IsMatch(deciphererDefinitionBody, $@"{Regex.Escape(calledFuncName)}:\bfunction\b\(\w+\,\w\).\bvar\b.\bc=a\b")) { var index = Regex.Match(statement, @"\(\w+,(\d+)\)").Groups[1].Value.ParseInt(); yield return(new SwapCipherOperation(index)); } // Reverse else if (Regex.IsMatch(deciphererDefinitionBody, $@"{Regex.Escape(calledFuncName)}:\bfunction\b\(\w+\)")) { yield return(new ReverseCipherOperation()); } } }
private async Task <StreamManifest> GetManifestAsync(StreamContext streamContext) { // To make sure there are no duplicates streams, group them by tag var streams = new Dictionary <int, IStreamInfo>(); foreach (var streamInfo in streamContext.StreamInfoProviders) { var tag = streamInfo.GetTag(); var url = streamInfo.GetUrl(); // Signature var signature = streamInfo.TryGetSignature(); var signatureParameter = streamInfo.TryGetSignatureParameter() ?? "signature"; if (!string.IsNullOrWhiteSpace(signature)) { signature = streamContext.CipherOperations.Decipher(signature); url = Url.SetQueryParameter(url, signatureParameter, signature); } // Content length var contentLength = streamInfo.TryGetContentLength() ?? await _httpClient.TryGetContentLengthAsync(url, false) ?? 0; if (contentLength <= 0) { continue; // broken stream URL? } // Common var container = Container.Parse(streamInfo.GetContainer()); var fileSize = new FileSize(contentLength); var bitrate = new Bitrate(streamInfo.GetBitrate()); var audioCodec = streamInfo.TryGetAudioCodec(); var videoCodec = streamInfo.TryGetVideoCodec(); // Muxed or Video-only if (!string.IsNullOrWhiteSpace(videoCodec)) { var framerate = new Framerate(streamInfo.TryGetFramerate() ?? 24); var videoQualityLabel = streamInfo.TryGetVideoQualityLabel() ?? Heuristics.GetVideoQualityLabel(tag, framerate.FramesPerSecond); var videoQuality = Heuristics.GetVideoQuality(videoQualityLabel); var videoWidth = streamInfo.TryGetVideoWidth(); var videoHeight = streamInfo.TryGetVideoHeight(); var videoResolution = videoWidth != null && videoHeight != null ? new VideoResolution(videoWidth.Value, videoHeight.Value) : Heuristics.GetVideoResolution(videoQuality); // Muxed if (!string.IsNullOrWhiteSpace(audioCodec)) { streams[tag] = new MuxedStreamInfo( tag, url, container, fileSize, bitrate, audioCodec, videoCodec, videoQualityLabel, videoQuality, videoResolution, framerate ); } // Video-only else { streams[tag] = new VideoOnlyStreamInfo( tag, url, container, fileSize, bitrate, videoCodec, videoQualityLabel, videoQuality, videoResolution, framerate ); } } // Audio-only else if (!string.IsNullOrWhiteSpace(audioCodec)) { streams[tag] = new AudioOnlyStreamInfo( tag, url, container, fileSize, bitrate, audioCodec ); } else { #if DEBUG throw FatalFailureException.Generic("Stream info doesn't contain audio/video codec information."); #endif } } return(new StreamManifest(streams.Values.ToArray())); }
public string GetSts() { return(_root .Pipe(s => Regex.Match(s, @"(?<=invalid namespace.*?;[\w\s]+=)\d+").Value) .NullIfWhiteSpace() ?? throw FatalFailureException.Generic("Could not find sts in player source.")); }