private static MatchData GetCurrentMatchData(AppHttpClient client, string matchId)
        {
            var fetchMatchDataTask = client.GetMatchDataAsync(matchId);

            fetchMatchDataTask.Wait();
            var currentMatchData = JsonMatchDeserializer.GetMatchDataFromJsonString(fetchMatchDataTask.Result);

            return(currentMatchData);
        }
Example #2
0
        protected override async Task OnInitializedAsync()
        {
            NewItem = new ItemCreateApiModel();

            ApiCallResult <IList <ItemApiModel> > apiCallResult = await AppHttpClient.GetAsync <IList <ItemApiModel> >(ApiUrls.GetItemsList);

            Items = apiCallResult.IsSuccess
        ? apiCallResult.Value !
        : throw new Exception("API call is not successful");
        }
Example #3
0
        public static async Task <string> GetLoginStatusAsync(string oauthKey)
        {
            string queryUrl = "https://passport.bilibili.com/qrcode/getLoginInfo";
            NameValueCollection postValues = new NameValueCollection();

            postValues.Add("oauthKey", oauthKey);
            postValues.Add("gourl", "https%3A%2F%2Fwww.bilibili.com%2F");
            byte[] responseArray = await(await AppHttpClient.PostAsync(queryUrl, new FormUrlEncodedContent(postValues.ToDictionary()))).Content.ReadAsByteArrayAsync();
            return(Encoding.UTF8.GetString(responseArray));
        }
        private void PopulateMainPage()
        {
            var client  = new AppHttpClient();
            var matchId = PhoneApplicationService.Current.State["MatchId"].ToString();

            Task.Factory.StartNew(new Action(() =>
            {
                var currentMatchData = GetCurrentMatchData(client, matchId);
                UpdatePageUI(currentMatchData);
            }));
        }
Example #5
0
        public MainPage()
        {
            InitializeComponent();
            var client = new AppHttpClient();

            Task.Factory.StartNew(new Action(() =>
            {
                var fetchMatchesTask = client.GetMatchesAsync();
                fetchMatchesTask.Wait();
                matches = JsonMatchDeserializer.GetMatchObservableCollectionFromJsonString(fetchMatchesTask.Result);
                PopulateMatchItemsControl(matches);
            }));
        }
Example #6
0
        private static async Task <long> GetFileSizeAsync(string url)
        {
            using var httpRequestMessage = new HttpRequestMessage();
            if (!url.Contains("platform=android_tv_yst") && !url.Contains("platform=android"))
            {
                httpRequestMessage.Headers.TryAddWithoutValidation("Referer", "https://www.bilibili.com");
            }
            httpRequestMessage.Headers.TryAddWithoutValidation("User-Agent", "Mozilla/5.0");
            httpRequestMessage.Headers.TryAddWithoutValidation("Cookie", Core.Config.COOKIE);
            httpRequestMessage.RequestUri = new(url);
            var  response       = (await AppHttpClient.SendAsync(httpRequestMessage, HttpCompletionOption.ResponseHeadersRead)).EnsureSuccessStatusCode();
            long totalSizeBytes = response.Content.Headers.ContentLength ?? 0;

            return(totalSizeBytes);
        }
Example #7
0
        public async Task <MatchData> GetMatchDataAsync()
        {
            if (MatchData != null)
            {
                return(MatchData);
            }

            var client = new AppHttpClient();

            var dataString = await client.GetMatchDataAsync(GetMatchId());

            MatchData = JsonMatchDeserializer.GetMatchDataFromJsonString(dataString);

            return(MatchData);
        }
Example #8
0
        protected override async Task HandleEventAsync()
        {
            if (!string.IsNullOrWhiteSpace(NewItem.Text))
            {
                ApiCallResult <ItemApiModel> itemCreationCallResult = await AppHttpClient
                                                                      .PostAsync <ItemApiModel>(ApiUrls.CreateItem, NewItem);

                Items.Add(
                    itemCreationCallResult.IsSuccess
            ? itemCreationCallResult.Value !
            : throw new Exception("API call is not successful")
                    );

                NewItem.Text = string.Empty;
            }
        }
        public JurosTaxa ObterTaxaJurosCorrente(int meses)
        {
            using (var client = new AppHttpClient())
            {
                var response = client.GetHttpClient().GetAsync($"{client.baseAddress}/taxajuros").Result;

                if (response.StatusCode != System.Net.HttpStatusCode.OK)
                {
                    throw new Exception($"Ocorreu um erro inesperado. StatusCode {response.StatusCode}, mensagem {response.RequestMessage}");
                }

                string conteudo  = response.Content.ReadAsStringAsync().Result;
                var    jurosTaxa = JsonConvert.DeserializeObject <JurosTaxa>(conteudo);

                return(jurosTaxa);
            }
        }
 public UserRestService(AppHttpClient httpClient)
 {
     _httpClient = httpClient;
 }
 public ProfileRestService(AppHttpClient httpClient)
 {
     _httpClient = httpClient;
 }
Example #12
0
 public SecurityService(AppHttpClient httpClient, ILocalStorageService localStorage)
 {
     _httpClient   = httpClient;
     _localStorage = localStorage;
 }
Example #13
0
        protected async Task DeleteItemAsync(ItemApiModel item)
        {
            Items.Remove(item);

            await AppHttpClient.DeleteAsync(ApiUrls.DeleteItem.Replace(ApiUrls.IdTemplate, item.Id.ToString()));
        }
Example #14
0
 private Task UpdateItemAsync(ItemApiModel item)
 {
     return(AppHttpClient.PutAsync(ApiUrls.UpdateItem.Replace(ApiUrls.IdTemplate, item.Id.ToString()), item));
 }
Example #15
0
 public UsersService(AplicationOptions options)
 {
     _options     = options;
     this.client  = new HttpClient();
     this._client = new AppHttpClient(options.ApiUrl, true);
 }
Example #16
0
        private static async Task RangeDownloadToTmpAsync(int id, string url, string tmpName, long fromPosition, long?toPosition, Action <int, long, long> onProgress, bool failOnRangeNotSupported = false)
        {
            DateTimeOffset?lastTime = File.Exists(tmpName) ? new FileInfo(tmpName).LastWriteTimeUtc : null;

            using (var fileStream = new FileStream(tmpName, FileMode.Create))
            {
                fileStream.Seek(0, SeekOrigin.End);
                var downloadedBytes = fromPosition + fileStream.Position;

                using var httpRequestMessage = new HttpRequestMessage();
                if (!url.Contains("platform=android_tv_yst") && !url.Contains("platform=android"))
                {
                    httpRequestMessage.Headers.TryAddWithoutValidation("Referer", "https://www.bilibili.com");
                }
                httpRequestMessage.Headers.TryAddWithoutValidation("User-Agent", "Mozilla/5.0");
                httpRequestMessage.Headers.TryAddWithoutValidation("Cookie", Core.Config.COOKIE);
                httpRequestMessage.Headers.Range   = new(downloadedBytes, toPosition);
                httpRequestMessage.Headers.IfRange = lastTime != null ? new(lastTime.Value) : null;
                httpRequestMessage.RequestUri      = new(url);

                using var response = (await AppHttpClient.SendAsync(httpRequestMessage, HttpCompletionOption.ResponseHeadersRead)).EnsureSuccessStatusCode();

                if (response.StatusCode == HttpStatusCode.OK) // server doesn't response a partial content
                {
                    if (failOnRangeNotSupported && (downloadedBytes > 0 || toPosition != null))
                    {
                        throw new NotSupportedException("Range request is not supported.");
                    }
                    downloadedBytes = 0;
                    fileStream.Seek(0, SeekOrigin.Begin);
                }

                using var stream = await response.Content.ReadAsStreamAsync();

                var totalBytes = downloadedBytes + (response.Content.Headers.ContentLength ?? long.MaxValue - downloadedBytes);

                const int blockSize = 1048576 / 4;
                var       buffer    = new byte[blockSize];

                while (downloadedBytes < totalBytes)
                {
                    var recevied = await stream.ReadAsync(buffer);

                    if (recevied == 0)
                    {
                        break;
                    }
                    await fileStream.WriteAsync(buffer.AsMemory(0, recevied));

                    await fileStream.FlushAsync();

                    downloadedBytes += recevied;
                    onProgress(id, downloadedBytes - fromPosition, totalBytes);
                }

                if (response.Content.Headers.ContentLength != null && (response.Content.Headers.ContentLength != new FileInfo(tmpName).Length))
                {
                    throw new Exception("Retry...");
                }
            }
        }
Example #17
0
        public static async Task <int> Main(params string[] args)
        {
            ServicePointManager.DefaultConnectionLimit = 2048;
            ServicePointManager.ServerCertificateValidationCallback = (sender, certificate, chain, errors) =>
            {
                return(true);
            };

            var rootCommand = new RootCommand
            {
                new Argument <string>(
                    "url",
                    description: "视频地址 或 av|bv|BV|ep|ss"),
                new Option <bool>(
                    new string[] { "--use-tv-api", "-tv" },
                    "使用TV端解析模式"),
                new Option <bool>(
                    new string[] { "--use-app-api", "-app" },
                    "使用APP端解析模式"),
                new Option <bool>(
                    new string[] { "--use-intl-api", "-intl" },
                    "使用国际版解析模式"),
                new Option <bool>(
                    new string[] { "--use-mp4box" },
                    "使用MP4Box来混流"),
                new Option <bool>(
                    new string[] { "--only-hevc", "-hevc" },
                    "只下载hevc编码"),
                new Option <bool>(
                    new string[] { "--only-avc", "-avc" },
                    "只下载avc编码"),
                new Option <bool>(
                    new string[] { "--only-show-info", "-info" },
                    "仅解析而不进行下载"),
                new Option <bool>(
                    new string[] { "--hide-streams", "-hs" },
                    "不要显示所有可用音视频流"),
                new Option <bool>(
                    new string[] { "--interactive", "-ia" },
                    "交互式选择清晰度"),
                new Option <bool>(
                    new string[] { "--show-all" },
                    "展示所有分P标题"),
                new Option <bool>(
                    new string[] { "--use-aria2c" },
                    "调用aria2c进行下载(你需要自行准备好二进制可执行文件)"),
                new Option <string>(
                    new string[] { "--aria2c-proxy" },
                    "调用aria2c进行下载时的代理地址配置"),
                new Option <bool>(
                    new string[] { "--multi-thread", "-mt" },
                    "使用多线程下载"),
                new Option <string>(
                    new string[] { "--select-page", "-p" },
                    "选择指定分p或分p范围:(-p 8 或 -p 1,2 或 -p 3-5 或 -p ALL)"),
                new Option <bool>(
                    new string[] { "--audio-only" },
                    "仅下载音频"),
                new Option <bool>(
                    new string[] { "--video-only" },
                    "仅下载视频"),
                new Option <bool>(
                    new string[] { "--sub-only" },
                    "仅下载字幕"),
                new Option <bool>(
                    new string[] { "--no-padding-page-num" },
                    "不给分P序号补零"),
                new Option <bool>(
                    new string[] { "--debug" },
                    "输出调试日志"),
                new Option <bool>(
                    new string[] { "--skip-mux" },
                    "跳过混流步骤"),
                new Option <bool>(
                    new string[] { "--skip-subtitle" },
                    "跳过字幕下载"),
                new Option <bool>(
                    new string[] { "--skip-cover" },
                    "跳过封面下载"),
                new Option <string>(
                    new string[] { "--language" },
                    "设置混流的音频语言(代码),如chi, jpn等"),
                new Option <string>(
                    new string[] { "--cookie", "-c" },
                    "设置字符串cookie用以下载网页接口的会员内容"),
                new Option <string>(
                    new string[] { "--access-token", "-token" },
                    "设置access_token用以下载TV/APP接口的会员内容"),
                new Option <string>(
                    new string[] { "--work-dir" },
                    "设置程序的工作目录"),
                new Option <string>(
                    new string[] { "--delay-per-page" },
                    "设置下载合集分P之间的下载间隔时间(单位: 秒, 默认无间隔)")
            };

            Command loginCommand = new Command(
                "login",
                "通过APP扫描二维码以登录您的WEB账号");

            rootCommand.AddCommand(loginCommand);
            Command loginTVCommand = new Command(
                "logintv",
                "通过APP扫描二维码以登录您的TV账号");

            rootCommand.AddCommand(loginTVCommand);
            rootCommand.Description = "BBDown是一个免费且便捷高效的哔哩哔哩下载/解析软件.";
            rootCommand.TreatUnmatchedTokensAsErrors = true;

            //WEB登录
            loginCommand.Handler = CommandHandler.Create(async delegate
            {
                try
                {
                    Log("获取登录地址...");
                    string loginUrl = "https://passport.bilibili.com/qrcode/getLoginUrl";
                    string url      = JsonDocument.Parse(await GetWebSourceAsync(loginUrl)).RootElement.GetProperty("data").GetProperty("url").ToString();
                    string oauthKey = GetQueryString("oauthKey", url);
                    //Log(oauthKey);
                    //Log(url);
                    bool flag = false;
                    Log("生成二维码...");
                    QRCodeGenerator qrGenerator = new QRCodeGenerator();
                    QRCodeData qrCodeData       = qrGenerator.CreateQrCode(url, QRCodeGenerator.ECCLevel.Q);
                    QRCode qrCode      = new QRCode(qrCodeData);
                    Bitmap qrCodeImage = qrCode.GetGraphic(7);
                    qrCodeImage.Save("qrcode.png", System.Drawing.Imaging.ImageFormat.Png);
                    Log("生成二维码成功:qrcode.png, 请打开并扫描");
                    while (true)
                    {
                        await Task.Delay(1000);
                        string w    = await GetLoginStatusAsync(oauthKey);
                        string data = JsonDocument.Parse(w).RootElement.GetProperty("data").ToString();
                        if (data == "-2")
                        {
                            LogColor("二维码已过期, 请重新执行登录指令.");
                            break;
                        }
                        else if (data == "-4") //等待扫码
                        {
                            continue;
                        }
                        else if (data == "-5") //等待确认
                        {
                            if (!flag)
                            {
                                Log("扫码成功, 请确认...");
                                flag = !flag;
                            }
                        }
                        else
                        {
                            string cc = JsonDocument.Parse(w).RootElement.GetProperty("data").GetProperty("url").ToString();
                            Log("登录成功: SESSDATA=" + GetQueryString("SESSDATA", cc));
                            //导出cookie
                            File.WriteAllText(Path.Combine(APP_DIR, "BBDown.data"), cc.Substring(cc.IndexOf('?') + 1).Replace("&", ";"));
                            File.Delete("qrcode.png");
                            break;
                        }
                    }
                }
                catch (Exception e) { LogError(e.Message); }
            });

            //TV登录
            loginTVCommand.Handler = CommandHandler.Create(async delegate
            {
                try
                {
                    string loginUrl = "https://passport.snm0516.aisee.tv/x/passport-tv-login/qrcode/auth_code";
                    string pollUrl  = "https://passport.bilibili.com/x/passport-tv-login/qrcode/poll";
                    var parms       = GetTVLoginParms();
                    Log("获取登录地址...");
                    byte[] responseArray = await(await AppHttpClient.PostAsync(loginUrl, new FormUrlEncodedContent(parms.ToDictionary()))).Content.ReadAsByteArrayAsync();
                    string web           = Encoding.UTF8.GetString(responseArray);
                    string url           = JsonDocument.Parse(web).RootElement.GetProperty("data").GetProperty("url").ToString();
                    string authCode      = JsonDocument.Parse(web).RootElement.GetProperty("data").GetProperty("auth_code").ToString();
                    Log("生成二维码...");
                    QRCodeGenerator qrGenerator = new QRCodeGenerator();
                    QRCodeData qrCodeData       = qrGenerator.CreateQrCode(url, QRCodeGenerator.ECCLevel.Q);
                    QRCode qrCode      = new QRCode(qrCodeData);
                    Bitmap qrCodeImage = qrCode.GetGraphic(7);
                    qrCodeImage.Save("qrcode.png", System.Drawing.Imaging.ImageFormat.Png);
                    Log("生成二维码成功:qrcode.png, 请打开并扫描");
                    parms.Set("auth_code", authCode);
                    parms.Set("ts", GetTimeStamp(true));
                    parms.Remove("sign");
                    parms.Add("sign", GetSign(ToQueryString(parms)));
                    while (true)
                    {
                        await Task.Delay(1000);
                        responseArray = await(await AppHttpClient.PostAsync(pollUrl, new FormUrlEncodedContent(parms.ToDictionary()))).Content.ReadAsByteArrayAsync();
                        web           = Encoding.UTF8.GetString(responseArray);
                        string code   = JsonDocument.Parse(web).RootElement.GetProperty("code").ToString();
                        if (code == "86038")
                        {
                            LogColor("二维码已过期, 请重新执行登录指令.");
                            break;
                        }
                        else if (code == "86039") //等待扫码
                        {
                            continue;
                        }
                        else
                        {
                            string cc = JsonDocument.Parse(web).RootElement.GetProperty("data").GetProperty("access_token").ToString();
                            Log("登录成功: AccessToken=" + cc);
                            //导出cookie
                            File.WriteAllText(Path.Combine(APP_DIR, "BBDownTV.data"), "access_token=" + cc);
                            File.Delete("qrcode.png");
                            break;
                        }
                    }
                }
                catch (Exception e) { LogError(e.Message); }
            });

            rootCommand.Handler = CommandHandler.Create <MyOption>(async(myOption) =>
            {
                await DoWorkAsync(myOption);
            });

            return(await rootCommand.InvokeAsync(args));
        }
Example #18
0
        private static async Task DoWorkAsync(MyOption myOption)
        {
            Console.BackgroundColor = ConsoleColor.DarkBlue;
            Console.ForegroundColor = ConsoleColor.White;
            var ver = System.Reflection.Assembly.GetExecutingAssembly().GetName().Version;

            Console.Write($"BBDown version {ver.Major}.{ver.Minor}.{ver.Build}, Bilibili Downloader.\r\n");
            Console.ResetColor();
            Console.Write("欢迎到讨论区交流:\r\n" +
                          "https://github.com/nilaoda/BBDown/discussions\r\n");
            Console.WriteLine();
            //检测更新
            new Thread(async() =>
            {
                await CheckUpdateAsync();
            }).Start();
            try
            {
                bool   interactMode = myOption.Interactive;
                bool   infoMode     = myOption.OnlyShowInfo;
                bool   tvApi        = myOption.UseTvApi;
                bool   appApi       = myOption.UseAppApi;
                bool   intlApi      = myOption.UseIntlApi;
                bool   useMp4box    = myOption.UseMP4box;
                bool   onlyHevc     = myOption.OnlyHevc;
                bool   onlyAvc      = myOption.OnlyAvc;
                bool   hideStreams  = myOption.HideStreams;
                bool   multiThread  = myOption.MultiThread;
                bool   audioOnly    = myOption.AudioOnly;
                bool   videoOnly    = myOption.VideoOnly;
                bool   subOnly      = myOption.SubOnly;
                bool   skipMux      = myOption.SkipMux;
                bool   skipSubtitle = myOption.SkipSubtitle;
                bool   skipCover    = myOption.SkipCover;
                bool   showAll      = myOption.ShowAll;
                bool   useAria2c    = myOption.UseAria2c;
                string aria2cProxy  = myOption.Aria2cProxy;
                DEBUG_LOG = myOption.Debug;
                string input      = myOption.Url;
                string lang       = myOption.Language;
                string selectPage = myOption.SelectPage.ToUpper();
                string aidOri     = ""; //原始aid
                int    delay      = Convert.ToInt32(myOption.DelayPerPage);
                COOKIE = myOption.Cookie;
                TOKEN  = myOption.AccessToken.Replace("access_token=", "");

                if (!string.IsNullOrEmpty(myOption.WorkDir))
                {
                    var dir = Path.GetFullPath(myOption.WorkDir);
                    if (!Directory.Exists(dir))
                    {
                        Directory.CreateDirectory(dir);
                    }
                    //设置工作目录
                    Environment.CurrentDirectory = dir;
                    LogDebug("切换工作目录至:{0}", dir);
                }

                //audioOnly和videoOnly同时开启则全部忽视
                if (audioOnly && videoOnly)
                {
                    audioOnly = false;
                    videoOnly = false;
                }

                //OnlyHevc和OnlyAvc同时开启则全部忽视
                if (onlyAvc && onlyHevc)
                {
                    onlyAvc  = false;
                    onlyHevc = false;
                }

                if (skipSubtitle)
                {
                    subOnly = false;
                }

                List <string> selectedPages = null;
                if (!string.IsNullOrEmpty(GetQueryString("p", input)))
                {
                    selectedPages = new List <string>();
                    selectedPages.Add(GetQueryString("p", input));
                }

                LogDebug("AppDirectory: {0}", APP_DIR);
                LogDebug("运行参数:{0}", JsonSerializer.Serialize(myOption));
                if (string.IsNullOrEmpty(COOKIE) && File.Exists(Path.Combine(APP_DIR, "BBDown.data")) && !tvApi)
                {
                    Log("加载本地cookie...");
                    LogDebug("文件路径:{0}", Path.Combine(APP_DIR, "BBDown.data"));
                    COOKIE = File.ReadAllText(Path.Combine(APP_DIR, "BBDown.data"));
                }
                if (string.IsNullOrEmpty(TOKEN) && File.Exists(Path.Combine(APP_DIR, "BBDownTV.data")) && tvApi)
                {
                    Log("加载本地token...");
                    LogDebug("文件路径:{0}", Path.Combine(APP_DIR, "BBDownTV.data"));
                    TOKEN = File.ReadAllText(Path.Combine(APP_DIR, "BBDownTV.data"));
                    TOKEN = TOKEN.Replace("access_token=", "");
                }
                if (string.IsNullOrEmpty(TOKEN) && File.Exists(Path.Combine(APP_DIR, "BBDownApp.data")) && appApi)
                {
                    Log("加载本地token...");
                    LogDebug("文件路径:{0}", Path.Combine(APP_DIR, "BBDownApp.data"));
                    TOKEN = File.ReadAllText(Path.Combine(APP_DIR, "BBDownApp.data"));
                    TOKEN = TOKEN.Replace("access_token=", "");
                }
                Log("获取aid...");
                aidOri = await GetAvIdAsync(input);

                Log("获取aid结束: " + aidOri);
                //-p的优先级大于URL中的自带p参数,所以先清空selectedPages
                if (!string.IsNullOrEmpty(selectPage) && selectPage != "ALL")
                {
                    selectedPages = new List <string>();
                    try
                    {
                        string tmp = selectPage;
                        tmp = tmp.Trim().Trim(',');
                        if (tmp.Contains("-"))
                        {
                            int start = int.Parse(tmp.Split('-')[0]);
                            int end   = int.Parse(tmp.Split('-')[1]);
                            for (int i = start; i <= end; i++)
                            {
                                selectedPages.Add(i.ToString());
                            }
                        }
                        else
                        {
                            foreach (var s in tmp.Split(','))
                            {
                                selectedPages.Add(s);
                            }
                        }
                    }
                    catch { LogError("解析分P参数时失败了~"); selectedPages = null; };
                }

                if (selectPage == "ALL")
                {
                    selectedPages = null;
                }

                if (string.IsNullOrEmpty(aidOri))
                {
                    throw new Exception("输入有误");
                }
                Log("获取视频信息...");
                IFetcher fetcher = new BBDownNormalInfoFetcher();
                if (aidOri.StartsWith("cheese"))
                {
                    fetcher = new BBDownCheeseInfoFetcher();
                }
                else if (aidOri.StartsWith("ep"))
                {
                    if (intlApi)
                    {
                        fetcher = new BBDownIntlBangumiInfoFetcher();
                    }
                    else
                    {
                        fetcher = new BBDownBangumiInfoFetcher();
                    }
                }
                else if (aidOri.StartsWith("mid"))
                {
                    fetcher = new BBDownSpaceVideoFetcher();
                }
                var vInfo = await fetcher.FetchAsync(aidOri);

                string title   = vInfo.Title;
                string desc    = vInfo.Desc;
                string pic     = vInfo.Pic;
                string pubTime = vInfo.PubTime;
                LogColor("视频标题: " + title);
                Log("发布时间: " + pubTime);
                List <Page>     pagesInfo    = vInfo.PagesInfo;
                List <Subtitle> subtitleInfo = new List <Subtitle>();
                bool            more         = false;
                bool            bangumi      = vInfo.IsBangumi;
                bool            cheese       = vInfo.IsCheese;

                //打印分P信息
                foreach (Page p in pagesInfo)
                {
                    if (!showAll && more && p.index != pagesInfo.Count)
                    {
                        continue;
                    }
                    if (!showAll && !more && p.index > 5)
                    {
                        Log("......");
                        more = true;
                    }
                    else
                    {
                        Log($"P{p.index}: [{p.cid}] [{p.title}] [{FormatTime(p.dur)}]");
                    }
                }

                //如果用户没有选择分P,根据epid来确定某一集
                if (selectedPages == null && selectPage != "ALL" && !string.IsNullOrEmpty(vInfo.Index))
                {
                    selectedPages = new List <string> {
                        vInfo.Index
                    };
                    Log("程序已自动选择你输入的集数,如果要下载其他集数请自行指定分P(如可使用-p ALL代表全部)");
                }

                Log($"共计 {pagesInfo.Count} 个分P, 已选择:" + (selectedPages == null ? "ALL" : string.Join(",", selectedPages)));

                //过滤不需要的分P
                if (selectedPages != null)
                {
                    pagesInfo = pagesInfo.Where(p => selectedPages.Contains(p.index.ToString())).ToList();
                }

                foreach (Page p in pagesInfo)
                {
                    if (pagesInfo.Count > 1 && delay > 0)
                    {
                        Log($"停顿{delay}秒...");
                        Thread.Sleep(delay * 1000);
                    }

                    Log($"开始解析P{p.index}...");

                    string        webJsonStr  = "";
                    List <Video>  videoTracks = new List <Video>();
                    List <Audio>  audioTracks = new List <Audio>();
                    List <string> clips       = new List <string>();
                    List <string> dfns        = new List <string>();

                    string indexStr  = myOption.NoPaddingPageNum ? p.index.ToString() : p.index.ToString("0".PadRight(pagesInfo.OrderByDescending(_p => _p.index).First().index.ToString().Length, '0'));
                    string videoPath = $"{p.aid}/{p.aid}.P{indexStr}.{p.cid}.mp4";
                    string audioPath = $"{p.aid}/{p.aid}.P{indexStr}.{p.cid}.m4a";

                    //处理文件夹以.结尾导致的异常情况
                    if (title.EndsWith("."))
                    {
                        title += "_fix";
                    }
                    string outPath = GetValidFileName(title) + (pagesInfo.Count > 1 ? $"/[P{indexStr}]{GetValidFileName(p.title)}" : (vInfo.PagesInfo.Count > 1 ? $"[P{indexStr}]{GetValidFileName(p.title)}" : "")) + ".mp4";
                    if (!infoMode && File.Exists(outPath) && new FileInfo(outPath).Length != 0)
                    {
                        Log($"{outPath}已存在, 跳过下载...");
                        if (pagesInfo.Count == 1 && Directory.Exists(p.aid))
                        {
                            Directory.Delete(p.aid, true);
                        }
                        continue;
                    }

                    //处理封面&&字幕
                    if (!infoMode)
                    {
                        if (!Directory.Exists(p.aid))
                        {
                            Directory.CreateDirectory(p.aid);
                        }
                        if (!skipCover && !subOnly && !File.Exists($"{p.aid}/{p.aid}.jpg"))
                        {
                            Log("下载封面...");
                            LogDebug("下载:{0}", pic);
                            await using var response = await AppHttpClient.GetStreamAsync(pic);

                            await using var fs = new FileStream($"{p.aid}/{p.aid}.jpg", FileMode.OpenOrCreate);
                            await response.CopyToAsync(fs);
                        }

                        if (!skipSubtitle)
                        {
                            LogDebug("获取字幕...");
                            subtitleInfo = await BBDownSubUtil.GetSubtitlesAsync(p.aid, p.cid, p.epid, intlApi);

                            foreach (Subtitle s in subtitleInfo)
                            {
                                Log($"下载字幕 {s.lan} => {BBDownSubUtil.GetSubtitleCode(s.lan).Item2}...");
                                LogDebug("下载:{0}", s.url);
                                await BBDownSubUtil.SaveSubtitleAsync(s.url, s.path);

                                if (subOnly && File.Exists(s.path) && File.ReadAllText(s.path) != "")
                                {
                                    string _indexStr = p.index.ToString("0".PadRight(pagesInfo.OrderByDescending(_p => _p.index).First().index.ToString().Length, '0'));
                                    //处理文件夹以.结尾导致的异常情况
                                    if (title.EndsWith("."))
                                    {
                                        title += "_fix";
                                    }
                                    string _outSubPath = GetValidFileName(title) + (pagesInfo.Count > 1 ? $"/[P{_indexStr}]{GetValidFileName(p.title)}" : (vInfo.PagesInfo.Count > 1 ? $"[P{_indexStr}]{GetValidFileName(p.title)}" : "")) + $"_{BBDownSubUtil.GetSubtitleCode(s.lan).Item2}.srt";
                                    if (_outSubPath.Contains("/"))
                                    {
                                        if (!Directory.Exists(Path.GetDirectoryName(_outSubPath)))
                                        {
                                            Directory.CreateDirectory(Path.GetDirectoryName(_outSubPath));
                                        }
                                    }
                                    File.Move(s.path, _outSubPath, true);
                                }
                            }
                        }

                        if (subOnly)
                        {
                            if (Directory.Exists(p.aid) && Directory.GetFiles(p.aid).Length == 0)
                            {
                                Directory.Delete(p.aid, true);
                            }
                            continue;
                        }
                    }

                    //调用解析
                    (webJsonStr, videoTracks, audioTracks, clips, dfns) = await ExtractTracksAsync(onlyHevc, onlyAvc, aidOri, p.aid, p.cid, p.epid, tvApi, intlApi, appApi);

                    //File.WriteAllText($"debug.json", JObject.Parse(webJson).ToString());


                    //此处代码简直灾难,后续优化吧
                    if ((videoTracks.Count != 0 || audioTracks.Count != 0) && clips.Count == 0)   //dash
                    {
                        if (webJsonStr.Contains("\"video\":[") && videoTracks.Count == 0)
                        {
                            LogError("没有找到符合要求的视频流");
                            if (!audioOnly)
                            {
                                continue;
                            }
                        }
                        if (webJsonStr.Contains("\"audio\":[") && audioTracks.Count == 0)
                        {
                            LogError("没有找到符合要求的音频流");
                            if (!videoOnly)
                            {
                                continue;
                            }
                        }
                        //降序
                        videoTracks.Sort(Compare);
                        audioTracks.Sort(Compare);

                        if (audioOnly)
                        {
                            videoTracks.Clear();
                        }
                        if (videoOnly)
                        {
                            audioTracks.Clear();
                        }

                        int vIndex = 0;
                        int aIndex = 0;

                        if (!hideStreams)
                        {
                            //展示所有的音视频流信息
                            if (videoTracks.Count > 0)
                            {
                                Log($"共计{videoTracks.Count}条视频流.");
                                int index = 0;
                                foreach (var v in videoTracks)
                                {
                                    int pDur = p.dur == 0 ? v.dur : p.dur;
                                    LogColor($"{index++}. [{v.dfn}] [{v.res}] [{v.codecs}] [{v.fps}] [{v.bandwith} kbps] [~{FormatFileSize(pDur * v.bandwith * 1024 / 8)}]".Replace("[] ", ""), false);
                                    if (infoMode)
                                    {
                                        Console.WriteLine(v.baseUrl);
                                    }
                                }
                            }
                            if (audioTracks.Count > 0)
                            {
                                Log($"共计{audioTracks.Count}条音频流.");
                                int index = 0;
                                foreach (var a in audioTracks)
                                {
                                    int pDur = p.dur == 0 ? a.dur : p.dur;
                                    LogColor($"{index++}. [{a.codecs}] [{a.bandwith} kbps] [~{FormatFileSize(pDur * a.bandwith * 1024 / 8)}]", false);
                                    if (infoMode)
                                    {
                                        Console.WriteLine(a.baseUrl);
                                    }
                                }
                            }
                        }
                        if (infoMode)
                        {
                            continue;
                        }
                        if (interactMode && !hideStreams)
                        {
                            if (videoTracks.Count > 0)
                            {
                                Log("请选择一条视频流(输入序号): ", false);
                                Console.ForegroundColor = ConsoleColor.Cyan;
                                vIndex = Convert.ToInt32(Console.ReadLine());
                                if (vIndex > videoTracks.Count || vIndex < 0)
                                {
                                    vIndex = 0;
                                }
                                Console.ResetColor();
                            }
                            if (audioTracks.Count > 0)
                            {
                                Log("请选择一条音频流(输入序号): ", false);
                                Console.ForegroundColor = ConsoleColor.Cyan;
                                aIndex = Convert.ToInt32(Console.ReadLine());
                                if (aIndex > audioTracks.Count || aIndex < 0)
                                {
                                    aIndex = 0;
                                }
                                Console.ResetColor();
                            }
                        }

                        Log($"已选择的流:");
                        if (videoTracks.Count > 0)
                        {
                            LogColor($"[视频] [{videoTracks[vIndex].dfn}] [{videoTracks[vIndex].res}] [{videoTracks[vIndex].codecs}] [{videoTracks[vIndex].fps}] [{videoTracks[vIndex].bandwith} kbps] [~{FormatFileSize(videoTracks[vIndex].dur * videoTracks[vIndex].bandwith * 1024 / 8)}]".Replace("[] ", ""), false);
                        }
                        if (audioTracks.Count > 0)
                        {
                            LogColor($"[音频] [{audioTracks[aIndex].codecs}] [{audioTracks[aIndex].bandwith} kbps] [~{FormatFileSize(audioTracks[aIndex].dur * audioTracks[aIndex].bandwith * 1024 / 8)}]", false);
                        }

                        if (videoTracks.Count > 0)
                        {
                            //杜比视界,使用mp4box封装
                            if (videoTracks[vIndex].dfn == qualitys["126"] && !useMp4box)
                            {
                                LogError($"检测到杜比视界清晰度,将强制使用mp4box混流...");
                                useMp4box = true;
                            }
                            if (multiThread && !videoTracks[vIndex].baseUrl.Contains("-cmcc-"))
                            {
                                Log($"开始多线程下载P{p.index}视频...");
                                await MultiThreadDownloadFileAsync(videoTracks[vIndex].baseUrl, videoPath, useAria2c, aria2cProxy);

                                Log("合并视频分片...");
                                CombineMultipleFilesIntoSingleFile(GetFiles(Path.GetDirectoryName(videoPath), ".vclip"), videoPath);
                                Log("清理分片...");
                                foreach (var file in new DirectoryInfo(Path.GetDirectoryName(videoPath)).EnumerateFiles("*.?clip"))
                                {
                                    file.Delete();
                                }
                            }
                            else
                            {
                                if (multiThread && videoTracks[vIndex].baseUrl.Contains("-cmcc-"))
                                {
                                    LogError("检测到cmcc域名cdn, 已经禁用多线程");
                                }
                                Log($"开始下载P{p.index}视频...");
                                await DownloadFile(videoTracks[vIndex].baseUrl, videoPath, useAria2c, aria2cProxy);
                            }
                        }
                        if (audioTracks.Count > 0)
                        {
                            if (multiThread && !audioTracks[aIndex].baseUrl.Contains("-cmcc-"))
                            {
                                Log($"开始多线程下载P{p.index}音频...");
                                await MultiThreadDownloadFileAsync(audioTracks[aIndex].baseUrl, audioPath, useAria2c, aria2cProxy);

                                Log("合并音频分片...");
                                CombineMultipleFilesIntoSingleFile(GetFiles(Path.GetDirectoryName(audioPath), ".aclip"), audioPath);
                                Log("清理分片...");
                                foreach (var file in new DirectoryInfo(Path.GetDirectoryName(videoPath)).EnumerateFiles("*.?clip"))
                                {
                                    file.Delete();
                                }
                            }
                            else
                            {
                                if (multiThread && audioTracks[aIndex].baseUrl.Contains("-cmcc-"))
                                {
                                    LogError("检测到cmcc域名cdn, 已经禁用多线程");
                                }
                                Log($"开始下载P{p.index}音频...");
                                await DownloadFile(audioTracks[aIndex].baseUrl, audioPath, useAria2c, aria2cProxy);
                            }
                        }

                        Log($"下载P{p.index}完毕");
                        if (videoTracks.Count == 0)
                        {
                            videoPath = "";
                        }
                        if (audioTracks.Count == 0)
                        {
                            audioPath = "";
                        }
                        if (skipMux)
                        {
                            continue;
                        }
                        Log("开始合并音视频" + (subtitleInfo.Count > 0 ? "和字幕" : "") + "...");
                        int code = MuxAV(useMp4box, videoPath, audioPath, outPath,
                                         desc,
                                         title,
                                         vInfo.PagesInfo.Count > 1 ? ($"P{indexStr}.{p.title}") : "",
                                         File.Exists($"{p.aid}/{p.aid}.jpg") ? $"{p.aid}/{p.aid}.jpg" : "",
                                         lang,
                                         subtitleInfo, audioOnly, videoOnly);
                        if (code != 0 || !File.Exists(outPath) || new FileInfo(outPath).Length == 0)
                        {
                            LogError("合并失败"); continue;
                        }
                        Log("清理临时文件...");
                        Thread.Sleep(200);
                        if (videoTracks.Count > 0)
                        {
                            File.Delete(videoPath);
                        }
                        if (audioTracks.Count > 0)
                        {
                            File.Delete(audioPath);
                        }
                        foreach (var s in subtitleInfo)
                        {
                            File.Delete(s.path);
                        }
                        if (pagesInfo.Count == 1 || p.index == pagesInfo.Last().index || p.aid != pagesInfo.Last().aid)
                        {
                            File.Delete($"{p.aid}/{p.aid}.jpg");
                        }
                        if (Directory.Exists(p.aid) && Directory.GetFiles(p.aid).Length == 0)
                        {
                            Directory.Delete(p.aid, true);
                        }
                    }
                    else if (clips.Count > 0 && dfns.Count > 0)   //flv
                    {
                        bool flag = false;
reParse:
                        //降序
                        videoTracks.Sort(Compare);

                        if (interactMode && !flag)
                        {
                            int i = 0;
                            dfns.ForEach(key => LogColor($"{i++}.{qualitys[key]}"));
                            Log("请选择最想要的清晰度(输入序号): ", false);
                            Console.ForegroundColor = ConsoleColor.Cyan;
                            var vIndex = Convert.ToInt32(Console.ReadLine());
                            if (vIndex > dfns.Count || vIndex < 0)
                            {
                                vIndex = 0;
                            }
                            Console.ResetColor();
                            //重新解析
                            videoTracks.Clear();
                            (webJsonStr, videoTracks, audioTracks, clips, dfns) = await ExtractTracksAsync(onlyHevc, onlyAvc, aidOri, p.aid, p.cid, p.epid, tvApi, intlApi, appApi, dfns[vIndex]);

                            flag = true;
                            goto reParse;
                        }

                        Log($"共计{videoTracks.Count}条流(共有{clips.Count}个分段).");
                        int index = 0;
                        foreach (var v in videoTracks)
                        {
                            LogColor($"{index++}. [{v.dfn}] [{v.res}] [{v.codecs}] [{v.fps}] [~{(v.size / 1024 / v.dur * 8).ToString("00")} kbps] [{FormatFileSize(v.size)}]".Replace("[] ", ""), false);
                            if (infoMode)
                            {
                                clips.ForEach(delegate(string c) { Console.WriteLine(c); });
                            }
                        }
                        if (infoMode)
                        {
                            continue;
                        }
                        if (File.Exists(outPath) && new FileInfo(outPath).Length != 0)
                        {
                            Log($"{outPath}已存在, 跳过下载...");
                            continue;
                        }
                        var pad = string.Empty.PadRight(clips.Count.ToString().Length, '0');
                        for (int i = 0; i < clips.Count; i++)
                        {
                            var link = clips[i];
                            videoPath = $"{p.aid}/{p.aid}.P{indexStr}.{p.cid}.{i.ToString(pad)}.mp4";
                            if (multiThread && !link.Contains("-cmcc-"))
                            {
                                if (videoTracks.Count != 0)
                                {
                                    Log($"开始多线程下载P{p.index}视频, 片段({(i + 1).ToString(pad)}/{clips.Count})...");
                                    await MultiThreadDownloadFileAsync(link, videoPath, useAria2c, aria2cProxy);

                                    Log("合并视频分片...");
                                    CombineMultipleFilesIntoSingleFile(GetFiles(Path.GetDirectoryName(videoPath), ".vclip"), videoPath);
                                }
                                Log("清理分片...");
                                foreach (var file in new DirectoryInfo(Path.GetDirectoryName(videoPath)).EnumerateFiles("*.?clip"))
                                {
                                    file.Delete();
                                }
                            }
                            else
                            {
                                if (multiThread && link.Contains("-cmcc-"))
                                {
                                    LogError("检测到cmcc域名cdn, 已经禁用多线程");
                                }
                                if (videoTracks.Count != 0)
                                {
                                    Log($"开始下载P{p.index}视频, 片段({(i + 1).ToString(pad)}/{clips.Count})...");
                                    await DownloadFile(link, videoPath, useAria2c, aria2cProxy);
                                }
                            }
                        }
                        Log($"下载P{p.index}完毕");
                        Log("开始合并分段...");
                        var files = GetFiles(Path.GetDirectoryName(videoPath), ".mp4");
                        videoPath = $"{p.aid}/{p.aid}.P{indexStr}.{p.cid}.mp4";
                        MergeFLV(files, videoPath);
                        if (skipMux)
                        {
                            continue;
                        }
                        Log("开始混流视频" + (subtitleInfo.Count > 0 ? "和字幕" : "") + "...");
                        int code = MuxAV(false, videoPath, "", outPath,
                                         desc,
                                         title,
                                         vInfo.PagesInfo.Count > 1 ? ($"P{indexStr}.{p.title}") : "",
                                         File.Exists($"{p.aid}/{p.aid}.jpg") ? $"{p.aid}/{p.aid}.jpg" : "",
                                         lang,
                                         subtitleInfo, audioOnly, videoOnly);
                        if (code != 0 || !File.Exists(outPath) || new FileInfo(outPath).Length == 0)
                        {
                            LogError("合并失败"); continue;
                        }
                        Log("清理临时文件...");
                        Thread.Sleep(200);
                        if (videoTracks.Count != 0)
                        {
                            File.Delete(videoPath);
                        }
                        foreach (var s in subtitleInfo)
                        {
                            File.Delete(s.path);
                        }
                        if (pagesInfo.Count == 1 || p.index == pagesInfo.Last().index || p.aid != pagesInfo.Last().aid)
                        {
                            File.Delete($"{p.aid}/{p.aid}.jpg");
                        }
                        if (Directory.Exists(p.aid) && Directory.GetFiles(p.aid).Length == 0)
                        {
                            Directory.Delete(p.aid, true);
                        }
                    }
                    else
                    {
                        if (webJsonStr.Contains("平台不可观看"))
                        {
                            throw new Exception("当前(WEB)平台不可观看,请尝试使用TV API解析。");
                        }
                        else if (webJsonStr.Contains("地区不可观看") || webJsonStr.Contains("地区不支持"))
                        {
                            throw new Exception("当前地区不可观看,尝试设置系统代理后解析。");
                        }
                        else if (webJsonStr.Contains("购买后才能观看"))
                        {
                            throw new Exception("购买后才能观看哦");
                        }
                        LogError("解析此分P失败(使用--debug查看详细信息)");
                        LogDebug("{0}", webJsonStr);
                        continue;
                    }
                }
                Log("任务完成");
            }
            catch (Exception e)
            {
                Console.BackgroundColor = ConsoleColor.Red;
                Console.ForegroundColor = ConsoleColor.White;
                Console.Write(DEBUG_LOG ? e.ToString() : e.Message);
                Console.ResetColor();
                Console.WriteLine();
                Thread.Sleep(1);
            }
        }