Пример #1
0
        private void CheckResponse(HttpResponseMessage response)
        {
            // Some pages redirect to https://www.google.com/sorry instead of return 429
            if (response.RequestMessage.RequestUri.Host.EndsWith(".google.com", StringComparison.OrdinalIgnoreCase) &&
                response.RequestMessage.RequestUri.LocalPath.StartsWith("/sorry/", StringComparison.OrdinalIgnoreCase))
            {
                throw RequestLimitExceededException.FailedHttpRequest(response);
            }

            var statusCode = (int)response.StatusCode;

            if (statusCode >= 500)
            {
                throw TransientFailureException.FailedHttpRequest(response);
            }

            if (statusCode == 429)
            {
                throw RequestLimitExceededException.FailedHttpRequest(response);
            }

            if (statusCode >= 400)
            {
                throw FatalFailureException.FailedHttpRequest(response);
            }
        }
Пример #2
0
 private static int GetRetryCost(this Exception ex)
 {
     return(ex switch
     {
         TransientFailureException _ => 1,
         RequestLimitExceededException _ => 2,
         FatalFailureException _ => 3,
         _ => 100
     });
Пример #3
0
        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()));
        }
Пример #4
0
        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());
                }
            }
        }
Пример #5
0
 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."));
 }