Beispiel #1
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 intlApi      = myOption.UseIntlApi;
                bool hevc         = myOption.OnlyHevc;
                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 showAll      = myOption.ShowAll;
                bool useAria2c    = myOption.UseAria2c;
                DEBUG_LOG = myOption.Debug;
                string input      = myOption.Url;
                string lang       = myOption.Language;
                string selectPage = myOption.SelectPage.ToUpper();
                string aidOri     = ""; //原始aid
                COOKIE = myOption.Cookie;
                TOKEN  = myOption.AccessToken.Replace("access_token=", "");

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

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

                LogDebug("运行参数:{0}", myOption);
                if (string.IsNullOrEmpty(COOKIE) && File.Exists(Path.Combine(AppContext.BaseDirectory, "BBDown.data")) && !tvApi)
                {
                    Log("加载本地cookie...");
                    LogDebug("文件路径:{0}", Path.Combine(AppContext.BaseDirectory, "BBDown.data"));
                    COOKIE = File.ReadAllText(Path.Combine(AppContext.BaseDirectory, "BBDown.data"));
                }
                if (string.IsNullOrEmpty(TOKEN) && File.Exists(Path.Combine(AppContext.BaseDirectory, "BBDownTV.data")) && tvApi)
                {
                    Log("加载本地token...");
                    LogDebug("文件路径:{0}", Path.Combine(AppContext.BaseDirectory, "BBDownTV.data"));
                    TOKEN = File.ReadAllText(Path.Combine(AppContext.BaseDirectory, "BBDownTV.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   = fetcher.Fetch(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)
                {
                    Log($"开始解析P{p.index}...");
                    if (!infoMode)
                    {
                        if (!Directory.Exists(p.aid))
                        {
                            Directory.CreateDirectory(p.aid);
                        }
                        if (!subOnly && !File.Exists($"{p.aid}/{p.aid}.jpg"))
                        {
                            Log("下载封面...");
                            LogDebug("下载:{0}", pic);
                            new WebClient().DownloadFile(pic, $"{p.aid}/{p.aid}.jpg");
                        }
                        LogDebug("获取字幕...");
                        subtitleInfo = BBDownSubUtil.GetSubtitles(p.aid, p.cid, p.epid, intlApi);
                        foreach (Subtitle s in subtitleInfo)
                        {
                            Log($"下载字幕 {s.lan} => {BBDownSubUtil.SubDescDic[s.lan]}...");
                            LogDebug("下载:{0}", s.url);
                            BBDownSubUtil.SaveSubtitle(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.SubDescDic[s.lan]}.srt";
                                if (_outSubPath.Contains("/"))
                                {
                                    if (!Directory.Exists(Path.GetDirectoryName(_outSubPath)))
                                    {
                                        Directory.CreateDirectory(Path.GetDirectoryName(_outSubPath));
                                    }
                                }
                                File.Move(s.path, _outSubPath);
                            }
                        }

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

                    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  = 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";

                    //调用解析
                    (webJsonStr, videoTracks, audioTracks, clips, dfns) = ExtractTracks(hevc, aidOri, p.aid, p.cid, p.epid, tvApi, intlApi);

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


                    //此处代码简直灾难,后续优化吧
                    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();
                            }
                        }
                        if (File.Exists(outPath) && new FileInfo(outPath).Length != 0)
                        {
                            Log($"{outPath}已存在, 跳过下载...");
                            continue;
                        }

                        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)
                        {
                            if (multiThread && !videoTracks[vIndex].baseUrl.Contains("-cmcc-"))
                            {
                                Log($"开始多线程下载P{p.index}视频...");
                                await MultiThreadDownloadFileAsync(videoTracks[vIndex].baseUrl, videoPath, useAria2c);

                                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);
                            }
                        }
                        if (audioTracks.Count > 0)
                        {
                            if (multiThread && !audioTracks[aIndex].baseUrl.Contains("-cmcc-"))
                            {
                                Log($"开始多线程下载P{p.index}音频...");
                                await MultiThreadDownloadFileAsync(audioTracks[aIndex].baseUrl, audioPath, useAria2c);

                                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);
                            }
                        }

                        Log($"下载P{p.index}完毕");
                        if (videoTracks.Count == 0)
                        {
                            videoPath = "";
                        }
                        if (audioTracks.Count == 0)
                        {
                            audioPath = "";
                        }
                        if (skipMux)
                        {
                            continue;
                        }
                        Log("开始合并音视频" + (subtitleInfo.Count > 0 ? "和字幕" : "") + "...");
                        int code = MuxAV(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("清理临时文件...");
                        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();
                            //重新解析
                            (webJsonStr, videoTracks, audioTracks, clips, dfns) = ExtractTracks(hevc, aidOri, p.aid, p.cid, p.epid, tvApi, intlApi, dfns[vIndex]);
                            flag = true;
                            videoTracks.Clear();
                            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);

                                    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);
                                }
                            }
                        }
                        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(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("清理临时文件...");
                        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(e.Message);
                Console.ResetColor();
                Console.WriteLine();
                Thread.Sleep(1);
            }
        }
Beispiel #2
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("请注意:任何BUG请前往以下网址反馈:\r\n" +
                          "https://github.com/nilaoda/BBDown/issues\r\n");
            Console.WriteLine();
            //检测更新
            new Thread(async() =>
            {
                await CheckUpdateAsync();
            }).Start();
            try
            {
                bool interactMode = myOption.Interactive;
                bool infoMode     = myOption.OnlyShowInfo;
                bool tvApi        = myOption.UseTvApi;
                bool hevc         = myOption.OnlyHevc;
                bool hideStreams  = myOption.HideStreams;
                bool multiThread  = myOption.MultiThread;
                bool audioOnly    = myOption.AudioOnly;
                bool videoOnly    = myOption.VideoOnly;
                bool skipMux      = myOption.SkipMux;
                bool showAll      = myOption.ShowAll;
                bool useAria2c    = myOption.UseAria2c;
                DEBUG_LOG = myOption.Debug;
                string input      = myOption.Url;
                string selectPage = myOption.SelectPage.ToUpper();
                string aidOri     = ""; //原始aid
                COOKIE = myOption.Cookie;
                TOKEN  = myOption.AccessToken.Replace("access_token=", "");

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

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

                LogDebug("运行参数:{0}", myOption);
                if (string.IsNullOrEmpty(COOKIE) && File.Exists(Path.Combine(AppContext.BaseDirectory, "BBDown.data")) && !tvApi)
                {
                    Log("加载本地cookie...");
                    LogDebug("文件路径:{0}", Path.Combine(AppContext.BaseDirectory, "BBDown.data"));
                    COOKIE = File.ReadAllText(Path.Combine(AppContext.BaseDirectory, "BBDown.data"));
                }
                if (string.IsNullOrEmpty(TOKEN) && File.Exists(Path.Combine(AppContext.BaseDirectory, "BBDownTV.data")) && tvApi)
                {
                    Log("加载本地token...");
                    LogDebug("文件路径:{0}", Path.Combine(AppContext.BaseDirectory, "BBDownTV.data"));
                    TOKEN = File.ReadAllText(Path.Combine(AppContext.BaseDirectory, "BBDownTV.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"))
                {
                    fetcher = new BBDownBangumiInfoFetcher();
                }
                else if (aidOri.StartsWith("mid"))
                {
                    fetcher = new BBDownSpaceVideoFetcher();
                }
                var    vInfo   = fetcher.Fetch(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)
                {
                    Log($"开始解析P{p.index}...");
                    if (!infoMode)
                    {
                        if (!Directory.Exists(p.aid))
                        {
                            Directory.CreateDirectory(p.aid);
                        }
                        if (!File.Exists($"{p.aid}/{p.aid}.jpg"))
                        {
                            Log("下载封面...");
                            LogDebug("下载:{0}", pic);
                            new WebClient().DownloadFile(pic, $"{p.aid}/{p.aid}.jpg");
                        }
                        LogDebug("获取字幕...");
                        subtitleInfo = BBDownSubUtil.GetSubtitles(p.aid, p.cid);
                        foreach (Subtitle s in subtitleInfo)
                        {
                            Log($"下载字幕 {s.lan} => {BBDownSubUtil.SubDescDic[s.lan]}...");
                            LogDebug("下载:{0}", s.url);
                            BBDownSubUtil.SaveSubtitle(s.url, s.path);
                        }
                    }
                    List <Video> videoTracks = new List <Video>();
                    List <Audio> audioTracks = new List <Audio>();
                    string       indexStr    = 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";
                    //调用解析
                    string webJson = GetPlayJson(aidOri, p.aid, p.cid, p.epid, tvApi);
                    //File.WriteAllText($"debug.json", JObject.Parse(webJson).ToString());

                    IBBDownParse webResp;
                    if (tvApi)
                    {
                        webResp = JsonConvert.DeserializeObject <TVResponse>(webJson);
                    }
                    else
                    {
                        webResp = JsonConvert.DeserializeObject <PlayInfoResp>(webJson);
                    }


                    int pDur;
                    //此处代码简直灾难,后续优化吧
                    if (webJson.Contains("\"dash\":{")) //dash
                    {
                        List <Video> videoTrackstmp;
                        List <Audio> audioTrackstmp;

                        (pDur, videoTrackstmp, audioTrackstmp) = webResp.GetVideoInfos(p, myOption);

                        #region 此处处理免二压视频,需要单独再请求一次
                        webJson = GetPlayJson(aidOri, p.aid, p.cid, p.epid, tvApi, "125");
                        //File.WriteAllText($"debug.json", JObject.Parse(webJson).ToString());

                        if (tvApi)
                        {
                            webResp = JsonConvert.DeserializeObject <TVResponse>(webJson);
                        }
                        else
                        {
                            webResp = JsonConvert.DeserializeObject <PlayInfoResp>(webJson);
                        }
                        int tmpdata;
                        (tmpdata, videoTracks, audioTracks) = webResp.GetVideoInfos(p, myOption);

                        //合并两次解析video结果
                        foreach (var item in videoTracks)
                        {
                            if (!videoTracks.Contains(item))
                            {
                                videoTracks.Add(item);
                            }
                        }

                        #endregion

                        if (videoTracks.Count == 0)
                        {
                            LogError("没有找到符合要求的视频流");
                            continue;
                        }
                        if (audioTracks.Count == 0)
                        {
                            LogError("没有找到符合要求的音频流");
                            continue;
                        }

                        //选择音视频

                        (Video dvideo, Audio daudio, bool isSelected) = VideoSelector(videoTracks, audioTracks, myOption, outPath, pDur);
                        if (!isSelected)
                        {
                            continue;
                        }

                        #region   载

                        await DownLoadData(vInfo, subtitleInfo, p, videoTracks, audioTracks, dvideo, daudio, myOption, indexStr);

                        #endregion
                    }
                    else if (webJson.Contains("\"durl\":["))  //flv
                    {
                        Video v1;
                        (videoTracks, v1) = webResp.GetVideoInfo(myOption);

                        //选择画质,如果和当前不一样,进行下载
                        string quality = SingleVideoSelector(v1, myOption);
                        if (v1.id != quality)
                        {
                            videoTracks.Clear();
                            webJson = GetPlayJson(aidOri, p.aid, p.cid, p.epid, tvApi, quality);
                            if (tvApi)
                            {
                                webResp = JsonConvert.DeserializeObject <TVResponse>(webJson);
                            }
                            else
                            {
                                webResp = JsonConvert.DeserializeObject <PlayInfoResp>(webJson);
                            }
                            (videoTracks, v1) = webResp.GetVideoInfo(myOption);
                        }

                        //下载

                        await DownloadFlvFile(vInfo, subtitleInfo, p, videoTracks, v1, myOption, indexStr);
                    }
                    else
                    {
                        if (webJson.Contains("平台不可观看"))
                        {
                            throw new Exception("当前(WEB)平台不可观看,请尝试使用TV API解析。");
                        }
                        else if (webJson.Contains("地区不可观看") || webJson.Contains("地区不支持"))
                        {
                            throw new Exception("当前地区不可观看,请尝试使用代理解析。");
                        }
                        else if (webJson.Contains("购买后才能观看"))
                        {
                            throw new Exception("购买后才能观看哦");
                        }
                        LogError("解析此分P失败(使用--debug查看详细信息)");
                        LogDebug("{0}", webJson);
                        continue;
                    }
                }
                Log("任务完成");
            }
            catch (Exception e)
            {
                Console.BackgroundColor = ConsoleColor.Red;
                Console.ForegroundColor = ConsoleColor.White;
                Console.Write(e.Message);
                Console.ResetColor();
                Console.WriteLine();
                Thread.Sleep(1);
            }
        }