Пример #1
0
        public static (string, List <Video>, List <Audio>, List <string>, List <string>) ExtractTracks(bool hevc, string aidOri, string aid, string cid, string epId, bool tvApi, bool intl, string qn = "0")
        {
            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 webJsonStr = GetPlayJson(aidOri, aid, cid, epId, tvApi, intl);

            JObject respJson = JObject.Parse(webJsonStr);

            //intl接口
            if (webJsonStr.Contains("\"stream_list\""))
            {
                int    pDur  = respJson.SelectToken("data.video_info.timelength").Value <int>() / 1000;
                JArray audio = JArray.Parse(respJson.SelectToken("data.video_info.dash_audio").ToString());
                foreach (JObject stream in JArray.Parse(respJson.SelectToken("data.video_info.stream_list").ToString()))
                {
                    if (stream.ContainsKey("dash_video"))
                    {
                        if (stream["dash_video"]["base_url"].ToString() != "")
                        {
                            Video v = new Video();
                            v.dur      = pDur;
                            v.id       = stream["stream_info"]["quality"].ToString();
                            v.dfn      = Program.qualitys[v.id];
                            v.bandwith = Convert.ToInt64(stream["dash_video"]["bandwidth"].ToString()) / 1000;
                            v.baseUrl  = stream["dash_video"]["base_url"].ToString();
                            v.codecs   = stream["dash_video"]["codecid"].ToString() == "12" ? "HEVC" : "AVC";
                            if (hevc && v.codecs == "AVC")
                            {
                                continue;
                            }
                            if (!videoTracks.Contains(v))
                            {
                                videoTracks.Add(v);
                            }
                        }
                    }
                }

                foreach (JObject node in audio)
                {
                    Audio a = new Audio();
                    a.id       = node["id"].ToString();
                    a.dfn      = node["id"].ToString();
                    a.dur      = pDur;
                    a.bandwith = Convert.ToInt64(node["bandwidth"].ToString()) / 1000;
                    a.baseUrl  = node["base_url"].ToString();
                    a.codecs   = "M4A";
                    audioTracks.Add(a);
                }

                return(webJsonStr, videoTracks, audioTracks, clips, dfns);
            }

            if (webJsonStr.Contains("\"dash\":{")) //dash
            {
                JArray audio    = null;
                JArray video    = null;
                int    pDur     = 0;
                string nodeName = "data";
                if (webJsonStr.Contains("\"result\":{"))
                {
                    nodeName = "result";
                }

                try { pDur = !tvApi ? respJson[nodeName]["dash"]["duration"].Value <int>() : respJson["dash"]["duration"].Value <int>(); } catch { }
                try { pDur = !tvApi ? respJson[nodeName]["timelength"].Value <int>() / 1000 : respJson["timelength"].Value <int>() / 1000; } catch { }

                bool reParse = false;
reParse:
                if (reParse)
                {
                    webJsonStr = GetPlayJson(aidOri, aid, cid, epId, tvApi, intl, "125");
                    respJson   = JObject.Parse(webJsonStr);
                }
                try { video = JArray.Parse(!tvApi ? respJson[nodeName]["dash"]["video"].ToString() : respJson["dash"]["video"].ToString()); } catch { }
                try { audio = JArray.Parse(!tvApi ? respJson[nodeName]["dash"]["audio"].ToString() : respJson["dash"]["audio"].ToString()); } catch { }
                if (video != null)
                {
                    foreach (JObject node in video)
                    {
                        Video v = new Video();
                        v.dur      = pDur;
                        v.id       = node["id"].ToString();
                        v.dfn      = Program.qualitys[node["id"].ToString()];
                        v.bandwith = Convert.ToInt64(node["bandwidth"].ToString()) / 1000;
                        v.baseUrl  = node["base_url"].ToString();
                        v.codecs   = node["codecid"].ToString() == "12" ? "HEVC" : "AVC";
                        if (!tvApi)
                        {
                            v.res = node["width"].ToString() + "x" + node["height"].ToString();
                            v.fps = node["frame_rate"].ToString();
                        }
                        if (hevc && v.codecs == "AVC")
                        {
                            continue;
                        }
                        if (!videoTracks.Contains(v))
                        {
                            videoTracks.Add(v);
                        }
                    }
                }

                //此处处理免二压视频,需要单独再请求一次
                if (!reParse)
                {
                    reParse = true;
                    goto reParse;
                }

                if (audio != null)
                {
                    foreach (JObject node in audio)
                    {
                        Audio a = new Audio();
                        a.id       = node["id"].ToString();
                        a.dfn      = node["id"].ToString();
                        a.dur      = pDur;
                        a.bandwith = Convert.ToInt64(node["bandwidth"].ToString()) / 1000;
                        a.baseUrl  = node["base_url"].ToString();
                        a.codecs   = "M4A";
                        audioTracks.Add(a);
                    }
                }
            }
            else if (webJsonStr.Contains("\"durl\":[")) //flv
            {
                //默认以最高清晰度解析
                webJsonStr = GetPlayJson(aidOri, aid, cid, epId, tvApi, intl, "125");
                respJson   = JObject.Parse(webJsonStr);
                string quality      = "";
                string videoCodecid = "";
                string url          = "";
                double size         = 0;
                double length       = 0;
                if (webJsonStr.Contains("\"data\":{"))
                {
                    quality      = respJson["data"]["quality"].ToString();
                    videoCodecid = respJson["data"]["video_codecid"].ToString();
                    //获取所有分段
                    foreach (JObject node in JArray.Parse(respJson["data"]["durl"].ToString()))
                    {
                        clips.Add(node["url"].ToString());
                        size   += node["size"].Value <double>();
                        length += node["length"].Value <double>();
                    }
                    //TV模式可用清晰度
                    if (respJson["data"]["qn_extras"] != null)
                    {
                        foreach (JObject node in JArray.Parse(respJson["data"]["qn_extras"].ToString()))
                        {
                            dfns.Add(node["qn"].ToString());
                        }
                    }
                    else if (respJson["data"]["accept_quality"] != null) //非tv模式可用清晰度
                    {
                        foreach (JValue node in JArray.Parse(respJson["data"]["accept_quality"].ToString()))
                        {
                            string _qn = node.ToString();
                            if (_qn != null && _qn.Length > 0)
                            {
                                dfns.Add(node.ToString());
                            }
                        }
                    }
                }
                else
                {
                    //如果获取数据失败,尝试从根路径获取数据
                    string nodeinfo = respJson.ToString();
                    quality      = JObject.Parse(nodeinfo)["quality"].ToString();
                    videoCodecid = JObject.Parse(nodeinfo)["video_codecid"].ToString();
                    //获取所有分段
                    foreach (JObject node in JArray.Parse(JObject.Parse(nodeinfo)["durl"].ToString()))
                    {
                        clips.Add(node["url"].ToString());
                        size   += node["size"].Value <double>();
                        length += node["length"].Value <double>();
                    }
                    //TV模式可用清晰度
                    if (JObject.Parse(nodeinfo)["qn_extras"] != null)
                    {
                        //获取可用清晰度
                        foreach (JObject node in JArray.Parse(JObject.Parse(nodeinfo)["qn_extras"].ToString()))
                        {
                            dfns.Add(node["qn"].ToString());
                        }
                    }
                    else if (JObject.Parse(nodeinfo)["accept_quality"] != null) //非tv模式可用清晰度
                    {
                        foreach (JValue node in JArray.Parse(JObject.Parse(nodeinfo)["accept_quality"].ToString()))
                        {
                            string _qn = node.ToString();
                            if (_qn != null && _qn.Length > 0)
                            {
                                dfns.Add(node.ToString());
                            }
                        }
                    }
                }

                Video v = new Video();
                v.id      = quality;
                v.dfn     = Program.qualitys[quality];
                v.baseUrl = url;
                v.codecs  = videoCodecid == "12" ? "HEVC" : "AVC";
                v.dur     = (int)length / 1000;
                v.size    = size;
                if (hevc && v.codecs == "AVC")
                {
                }
                if (!videoTracks.Contains(v))
                {
                    videoTracks.Add(v);
                }
            }

            return(webJsonStr, videoTracks, audioTracks, clips, dfns);
        }
Пример #2
0
        private static async Task DoWorkAsync(MyOption myOption)
        {
            Console.BackgroundColor = ConsoleColor.DarkBlue;
            Console.ForegroundColor = ConsoleColor.White;
            Console.Write("BBDown version 20200816[RC3], Bilibili Downloader.\r\n");
            Console.ResetColor();
            Console.Write("请注意:这是一个测试版本,任何BUG请前往以下网址反馈:\r\n" +
                          "https://github.com/nilaoda/BBDown/issues\r\n");
            Console.WriteLine();
            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;
                string input        = myOption.Url;
                string selectPage   = myOption.SelectPage;
                string aid          = "";
                COOKIE = myOption.Cookie;
                TOKEN  = myOption.AccessToken != null?myOption.AccessToken.Replace("access_token=", "") : "";

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

                if (File.Exists(Path.Combine(AppContext.BaseDirectory, "BBDown.data")) && !tvApi)
                {
                    Log("加载本地cookie...");
                    COOKIE = File.ReadAllText(Path.Combine(AppContext.BaseDirectory, "BBDown.data"));
                }
                if (File.Exists(Path.Combine(AppContext.BaseDirectory, "BBDownTV.data")) && tvApi)
                {
                    Log("加载本地token...");
                    TOKEN = File.ReadAllText(Path.Combine(AppContext.BaseDirectory, "BBDownTV.data"));
                }
                Log("获取aid...");
                aid = await GetAvIdAsync(input);

                Log("获取aid结束: " + aid);
                //-p的优先级大于URL中的自带p参数,所以先清空selectedPages
                if (!string.IsNullOrEmpty(selectPage))
                {
                    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 (string.IsNullOrEmpty(aid))
                {
                    throw new Exception("输入有误");
                }
                string  api      = $"https://api.bilibili.com/x/web-interface/view?aid={aid}";
                string  json     = GetWebSource(api);
                JObject infoJson = JObject.Parse(json);
                Log("获取视频信息...");
                string title   = infoJson["data"]["title"].ToString();
                string desc    = infoJson["data"]["desc"].ToString();
                string pic     = infoJson["data"]["pic"].ToString();
                string pubTime = infoJson["data"]["pubdate"].ToString();
                LogColor("视频标题: " + title);
                Log("发布时间: " + new DateTime(1970, 1, 1, 0, 0, 0, 0).AddSeconds(Convert.ToDouble(pubTime)));
                JArray          subs         = JArray.Parse(infoJson["data"]["subtitle"]["list"].ToString());
                JArray          pages        = JArray.Parse(infoJson["data"]["pages"].ToString());
                List <Page>     pagesInfo    = new List <Page>();
                List <Subtitle> subtitleInfo = new List <Subtitle>();
                foreach (JObject sub in subs)
                {
                    Subtitle subtitle = new Subtitle();
                    subtitle.url  = sub["subtitle_url"].ToString();
                    subtitle.lan  = sub["lan"].ToString();
                    subtitle.path = $"{aid}/{aid}.{subtitle.lan}.srt";
                    subtitleInfo.Add(subtitle);
                }
                bool   more    = false;
                bool   bangumi = false;
                string epId    = "";

                if (!infoMode)
                {
                    if (!Directory.Exists(aid))
                    {
                        Directory.CreateDirectory(aid);
                    }
                    Log("下载封面...");
                    new WebClient().DownloadFile(pic, $"{aid}/{aid}.jpg");
                    foreach (Subtitle s in subtitleInfo)
                    {
                        Log($"下载字幕 {s.lan}...");
                        BBDownSubUtil.SaveSubtitle(s.url, s.path);
                    }
                }

                try
                {
                    if (infoJson["data"]["redirect_url"].ToString().Contains("bangumi"))
                    {
                        bangumi = true;
                        epId    = Regex.Match(infoJson["data"]["redirect_url"].ToString(), "ep(\\d+)").Groups[1].Value;
                    }
                }
                catch { }
                foreach (JObject page in pages)
                {
                    Page p = new Page(page["page"].Value <int>(),
                                      page["cid"].ToString(), page["part"].ToString(),
                                      page["duration"].Value <int>(), page["dimension"]["width"] + "x" + page["dimension"]["height"]);
                    pagesInfo.Add(p);

                    if (more)
                    {
                        continue;
                    }
                    if (Convert.ToInt32(page["page"].ToString()) > 5)
                    {
                        Log("P...");
                        Log("分P太多, 已经省略部分...");
                        more = true;
                    }
                    else
                    {
                        Log($"P{p.index}: [{p.cid}] [{p.title}] [{FormatTime(p.dur)}]");
                    }
                }
                Log($"共计 {pagesInfo.Count} 个分P, 已选择:" + (selectedPages == null ? "ALL" : string.Join(",", selectedPages)));

                foreach (Page p in pagesInfo)
                {
                    //跳过不需要的分P
                    if (selectedPages != null && !selectedPages.Contains(p.index.ToString()))
                    {
                        continue;
                    }

                    Log($"开始解析P{p.index}...");
                    List <Video> videoInfo = new List <Video>();
                    List <Audio> audioInfo = new List <Audio>();
                    string       videoPath = $"{aid}/{aid}.P{p.index}.{p.cid}.mp4";
                    string       audioPath = $"{aid}/{aid}.P{p.index}.{p.cid}.m4a";
                    string       outPath   = GetValidFileName(title + (pagesInfo.Count > 1 ? $"[P{p.index}.{p.title}].mp4" : ".mp4"));
                    //调用解析
                    string webJson = GetPlayJson(aid, p.cid, epId, tvApi, bangumi);
                    //File.WriteAllText($"debug.json", JObject.Parse(webJson).ToString());

                    JArray audio = null;
                    JArray video = null;

                    //此处代码简直灾难,后续优化吧
                    if (webJson.Contains("\"dash\":[")) //dash
                    {
                        string nodeName = "data";
                        if (webJson.Contains("\"result\":{"))
                        {
                            nodeName = "result";
                        }

                        try { video = JArray.Parse(!tvApi ? JObject.Parse(webJson)[nodeName]["dash"]["video"].ToString() : JObject.Parse(webJson)["dash"]["video"].ToString()); } catch { }
                        try { audio = JArray.Parse(!tvApi ? JObject.Parse(webJson)[nodeName]["dash"]["audio"].ToString() : JObject.Parse(webJson)["dash"]["audio"].ToString()); } catch { }
                        if (video != null)
                        {
                            foreach (JObject node in video)
                            {
                                Video v = new Video();
                                v.id       = node["id"].ToString();
                                v.dfn      = qualitys[node["id"].ToString()];
                                v.bandwith = Convert.ToInt64(node["bandwidth"].ToString()) / 1000;
                                v.baseUrl  = node["base_url"].ToString();
                                v.codecs   = node["codecid"].ToString() == "12" ? "HEVC" : "AVC";
                                if (!tvApi)
                                {
                                    v.res = node["width"].ToString() + "x" + node["height"].ToString();
                                    v.fps = node["frame_rate"].ToString();
                                }
                                if (hevc && v.codecs == "AVC")
                                {
                                    continue;
                                }
                                videoInfo.Add(v);
                            }
                        }

                        if (audio != null)
                        {
                            foreach (JObject node in audio)
                            {
                                Audio a = new Audio();
                                a.id       = node["id"].ToString();
                                a.dfn      = node["id"].ToString();
                                a.bandwith = Convert.ToInt64(node["bandwidth"].ToString()) / 1000;
                                a.baseUrl  = node["base_url"].ToString();
                                a.codecs   = "M4A";
                                audioInfo.Add(a);
                            }
                        }

                        if (video != null && videoInfo.Count == 0)
                        {
                            LogError("没有找到符合要求的视频流");
                            continue;
                        }
                        if (audio != null && audioInfo.Count == 0)
                        {
                            LogError("没有找到符合要求的音频流");
                            continue;
                        }
                        //降序
                        videoInfo.Sort(Compare);
                        audioInfo.Sort(Compare);
                        int vIndex = 0;
                        int aIndex = 0;
                        if (!hideStreams)
                        {
                            //展示所有的音视频流信息
                            Log($"共计{videoInfo.Count}条视频流.");
                            int index = 0;
                            foreach (var v in videoInfo)
                            {
                                LogColor($"{index++}. [{v.dfn}] [{v.res}] [{v.codecs}] [{v.fps}] [{v.bandwith} kbps] [~{FormatFileSize(p.dur * v.bandwith * 1024 / 8)}]".Replace("[] ", ""), false);
                            }
                            Log($"共计{audioInfo.Count}条音频流.");
                            index = 0;
                            foreach (var a in audioInfo)
                            {
                                LogColor($"{index++}. [{a.codecs}] [{a.bandwith} kbps] [~{FormatFileSize(p.dur * a.bandwith * 1024 / 8)}]", false);
                            }
                            if (infoMode)
                            {
                                continue;
                            }
                            if (interactMode)
                            {
                                Log("请选择一条视频流(输入序号): ", false);
                                Console.ForegroundColor = ConsoleColor.Cyan;
                                vIndex = Convert.ToInt32(Console.ReadLine());
                                Console.ResetColor();
                                Log("请选择一条音频流(输入序号): ", false);
                                Console.ForegroundColor = ConsoleColor.Cyan;
                                aIndex = Convert.ToInt32(Console.ReadLine());
                                Console.ResetColor();
                            }
                        }
                        if (File.Exists(outPath) && new FileInfo(outPath).Length != 0)
                        {
                            Log($"{outPath}已存在, 跳过下载...");
                            continue;
                        }

                        Log($"已选择的流:");
                        if (video != null)
                        {
                            LogColor($"[视频] [{videoInfo[vIndex].dfn}] [{videoInfo[vIndex].res}] [{videoInfo[vIndex].codecs}] [{videoInfo[vIndex].fps}] [{videoInfo[vIndex].bandwith} kbps] [~{FormatFileSize(p.dur * videoInfo[vIndex].bandwith * 1024 / 8)}]".Replace("[] ", ""), false);
                        }
                        if (audio != null)
                        {
                            LogColor($"[音频] [{audioInfo[aIndex].codecs}] [{audioInfo[aIndex].bandwith} kbps] [~{FormatFileSize(p.dur * audioInfo[aIndex].bandwith * 1024 / 8)}]", false);
                        }

                        if (multiThread && !videoInfo[vIndex].baseUrl.Contains("-cmcc-"))
                        {
                            if (video != null)
                            {
                                Log($"开始多线程下载P{p.index}视频...");
                                MultiThreadDownloadFile(videoInfo[vIndex].baseUrl, videoPath);
                                Log("合并视频分片...");
                                CombineMultipleFilesIntoSingleFile(GetFiles(Path.GetDirectoryName(videoPath), ".vclip"), videoPath);
                            }
                            if (audio != null)
                            {
                                Log($"开始多线程下载P{p.index}音频...");
                                MultiThreadDownloadFile(audioInfo[aIndex].baseUrl, audioPath);
                                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 && videoInfo[vIndex].baseUrl.Contains("-cmcc-"))
                            {
                                LogError("检测到cmcc域名cdn, 已经禁用多线程");
                            }
                            if (video != null)
                            {
                                Log($"开始下载P{p.index}视频...");
                                await DownloadFile(videoInfo[vIndex].baseUrl, videoPath);
                            }
                            if (audio != null)
                            {
                                Log($"开始下载P{p.index}音频...");
                                await DownloadFile(audioInfo[aIndex].baseUrl, audioPath);
                            }
                        }
                        Log($"下载P{p.index}完毕");
                        if (video == null)
                        {
                            videoPath = "";
                        }
                        if (audio == null)
                        {
                            audioPath = "";
                        }
                        Log("开始合并音视频" + (subtitleInfo.Count > 0 ? "和字幕" : "") + "...");
                        int code = MuxAV(videoPath, audioPath, outPath,
                                         desc.Replace("\"", ""),
                                         title.Replace("\"", ""),
                                         pagesInfo.Count > 1 ? GetValidFileName($"P{p.index}.{p.title}") : "",
                                         File.Exists($"{aid}/{aid}.jpg") ? $"{aid}/{aid}.jpg" : "",
                                         subtitleInfo);
                        if (code != 0 || !File.Exists(outPath) || new FileInfo(outPath).Length == 0)
                        {
                            LogError("合并失败"); continue;
                        }
                        Log("清理临时文件...");
                        if (video != null)
                        {
                            File.Delete(videoPath);
                        }
                        if (audio != null)
                        {
                            File.Delete(audioPath);
                        }
                        foreach (var s in subtitleInfo)
                        {
                            File.Delete(s.path);
                        }
                        if (pagesInfo.Count == 1 || p.index == pagesInfo.Count)
                        {
                            File.Delete($"{aid}/{aid}.jpg");
                        }
                        if (Directory.Exists(aid) && Directory.GetFiles(aid).Length == 0)
                        {
                            Directory.Delete(aid, true);
                        }
                    }
                    else if (webJson.Contains("\"durl\":["))  //flv
                    {
                        //重新解析最高清晰度
                        webJson = GetPlayJson(aid, p.cid, epId, tvApi, bangumi, "120");
                        string quality      = "";
                        string videoCodecid = "";
                        string url          = "";
                        string format       = "";
                        double size         = 0;
                        double length       = 0;
                        if (webJson.Contains("\"data\":{"))
                        {
                            format       = JObject.Parse(webJson)["data"]["format"].ToString();
                            quality      = JObject.Parse(webJson)["data"]["quality"].ToString();
                            videoCodecid = JObject.Parse(webJson)["data"]["video_codecid"].ToString();
                            url          = JObject.Parse(webJson)["data"]["durl"][0]["url"].ToString();
                            size         = JObject.Parse(webJson)["data"]["durl"][0]["size"].Value <double>();
                            length       = JObject.Parse(webJson)["data"]["durl"][0]["length"].Value <double>();
                        }
                        else
                        {
                            format       = JObject.Parse(webJson)["format"].ToString();
                            quality      = JObject.Parse(webJson)["quality"].ToString();
                            videoCodecid = JObject.Parse(webJson)["video_codecid"].ToString();
                            url          = JObject.Parse(webJson)["durl"][0]["url"].ToString();
                            size         = JObject.Parse(webJson)["durl"][0]["size"].Value <double>();
                            length       = JObject.Parse(webJson)["durl"][0]["length"].Value <double>();
                        }
                        Video v1 = new Video();
                        v1.id      = quality;
                        v1.dfn     = qualitys[quality];
                        v1.baseUrl = url;
                        v1.codecs  = videoCodecid == "12" ? "HEVC" : "AVC";
                        if (hevc && v1.codecs == "AVC")
                        {
                            ;
                        }
                        else
                        {
                            videoInfo.Add(v1);
                        }

                        //降序
                        videoInfo.Sort(Compare);

                        Log($"共计{videoInfo.Count}条流({format}).");
                        int index  = 0;
                        int vIndex = 0;
                        foreach (var v in videoInfo)
                        {
                            LogColor($"{index++}. [{v.dfn}] [{v.res}] [{v.codecs}] [{v.fps}] [~{(size / 1024 / (length / 1000) * 8).ToString("00")} kbps] [{FormatFileSize(size)}]".Replace("[] ", ""), false);
                        }
                        if (infoMode)
                        {
                            continue;
                        }
                        if (interactMode)
                        {
                            Log("请选择一条流(输入序号): ", false);
                            Console.ForegroundColor = ConsoleColor.Cyan;
                            vIndex = Convert.ToInt32(Console.ReadLine());
                            Console.ResetColor();
                        }
                        if (File.Exists(outPath) && new FileInfo(outPath).Length != 0)
                        {
                            Log($"{outPath}已存在, 跳过下载...");
                            continue;
                        }

                        if (multiThread && !videoInfo[0].baseUrl.Contains("-cmcc-"))
                        {
                            if (videoInfo.Count != 0)
                            {
                                Log($"开始多线程下载P{p.index}视频...");
                                MultiThreadDownloadFile(videoInfo[vIndex].baseUrl, videoPath);
                                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 && videoInfo[vIndex].baseUrl.Contains("-cmcc-"))
                            {
                                LogError("检测到cmcc域名cdn, 已经禁用多线程");
                            }
                            if (videoInfo.Count != 0)
                            {
                                Log($"开始下载P{p.index}视频...");
                                await DownloadFile(videoInfo[vIndex].baseUrl, videoPath);
                            }
                        }
                        Log($"下载P{p.index}完毕");
                        Log("开始混流视频" + (subtitleInfo.Count > 0 ? "和字幕" : "") + "...");
                        int code = MuxAV(videoPath, "", outPath,
                                         desc.Replace("\"", ""),
                                         title.Replace("\"", ""),
                                         pagesInfo.Count > 1 ? GetValidFileName($"P{p.index}.{p.title}") : "",
                                         File.Exists($"{aid}/{aid}.jpg") ? $"{aid}/{aid}.jpg" : "",
                                         subtitleInfo);
                        if (code != 0 || !File.Exists(outPath) || new FileInfo(outPath).Length == 0)
                        {
                            LogError("合并失败"); continue;
                        }
                        Log("清理临时文件...");
                        if (videoInfo.Count != 0)
                        {
                            File.Delete(videoPath);
                        }
                        foreach (var s in subtitleInfo)
                        {
                            File.Delete(s.path);
                        }
                        if (pagesInfo.Count == 1 || p.index == pagesInfo.Count)
                        {
                            File.Delete($"{aid}/{aid}.jpg");
                        }
                        if (Directory.Exists(aid) && Directory.GetFiles(aid).Length == 0)
                        {
                            Directory.Delete(aid, true);
                        }
                    }
                    else
                    {
                        LogError("解析此分P失败");
                        continue;
                    }
                }
                Log("任务完成");
            }
            catch (Exception e)
            {
                Console.BackgroundColor = ConsoleColor.Red;
                Console.ForegroundColor = ConsoleColor.White;
                Console.WriteLine(e.Message);
                Console.ResetColor();
                Thread.Sleep(1);
            }
        }
Пример #3
0
 private static int Compare(Audio r1, Audio r2)
 {
     return(r1.bandwith - r2.bandwith > 0 ? -1 : 1);
 }
Пример #4
0
        public static async Task <(string, List <Video>, List <Audio>, List <string>, List <string>)> ExtractTracksAsync(bool onlyHevc, bool onlyAvc, string aidOri, string aid, string cid, string epId, bool tvApi, bool intlApi, bool appApi, string qn = "0")
        {
            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 webJsonStr = await GetPlayJsonAsync(onlyAvc, aidOri, aid, cid, epId, tvApi, intlApi, appApi, qn);

            var respJson = JsonDocument.Parse(webJsonStr);
            var data     = respJson.RootElement;

            //intl接口
            if (webJsonStr.Contains("\"stream_list\""))
            {
                int pDur  = data.GetProperty("data").GetProperty("video_info").GetProperty("timelength").GetInt32() / 1000;
                var audio = data.GetProperty("data").GetProperty("video_info").GetProperty("dash_audio").EnumerateArray().ToList();
                foreach (var stream in data.GetProperty("data").GetProperty("video_info").GetProperty("stream_list").EnumerateArray())
                {
                    JsonElement dashVideo;
                    if (stream.TryGetProperty("dash_video", out dashVideo))
                    {
                        if (dashVideo.GetProperty("base_url").ToString() != "")
                        {
                            Video v = new Video();
                            v.dur      = pDur;
                            v.id       = stream.GetProperty("stream_info").GetProperty("quality").ToString();
                            v.dfn      = Program.qualitys[v.id];
                            v.bandwith = Convert.ToInt64(dashVideo.GetProperty("bandwidth").ToString()) / 1000;
                            v.baseUrl  = dashVideo.GetProperty("base_url").ToString();
                            v.codecs   = dashVideo.GetProperty("codecid").ToString() == "12" ? "HEVC" : "AVC";
                            if (onlyHevc && v.codecs == "AVC")
                            {
                                continue;
                            }
                            if (!videoTracks.Contains(v))
                            {
                                videoTracks.Add(v);
                            }
                        }
                    }
                }

                foreach (var node in audio)
                {
                    Audio a = new Audio();
                    a.id       = node.GetProperty("id").ToString();
                    a.dfn      = node.GetProperty("id").ToString();
                    a.dur      = pDur;
                    a.bandwith = Convert.ToInt64(node.GetProperty("bandwidth").ToString()) / 1000;
                    a.baseUrl  = node.GetProperty("base_url").ToString();
                    a.codecs   = "M4A";
                    audioTracks.Add(a);
                }

                return(webJsonStr, videoTracks, audioTracks, clips, dfns);
            }

            if (webJsonStr.Contains("\"dash\":{")) //dash
            {
                List <JsonElement> audio = null;
                List <JsonElement> video = null;
                int    pDur     = 0;
                string nodeName = "data";
                if (webJsonStr.Contains("\"result\":{"))
                {
                    nodeName = "result";
                }

                try { pDur = !tvApi?respJson.RootElement.GetProperty(nodeName).GetProperty("dash").GetProperty("duration").GetInt32() : respJson.RootElement.GetProperty("dash").GetProperty("duration").GetInt32(); } catch { }
                try { pDur = !tvApi?respJson.RootElement.GetProperty(nodeName).GetProperty("timelength").GetInt32() / 1000 : respJson.RootElement.GetProperty("timelength").GetInt32() / 1000; } catch { }

                bool reParse = false;
reParse:
                if (reParse)
                {
                    webJsonStr = await GetPlayJsonAsync(onlyAvc, aidOri, aid, cid, epId, tvApi, intlApi, appApi, GetMaxQn());

                    respJson = JsonDocument.Parse(webJsonStr);
                }
                try { video = !tvApi?respJson.RootElement.GetProperty(nodeName).GetProperty("dash").GetProperty("video").EnumerateArray().ToList() : respJson.RootElement.GetProperty("dash").GetProperty("video").EnumerateArray().ToList(); } catch { }
                try { audio = !tvApi?respJson.RootElement.GetProperty(nodeName).GetProperty("dash").GetProperty("audio").EnumerateArray().ToList() : respJson.RootElement.GetProperty("dash").GetProperty("audio").EnumerateArray().ToList(); } catch { }
                //处理杜比音频
                try
                {
                    if (!tvApi && respJson.RootElement.GetProperty(nodeName).GetProperty("dash").TryGetProperty("dolby", out JsonElement dolby))
                    {
                        if (dolby.TryGetProperty("audio", out JsonElement db))
                        {
                            audio.AddRange(db.EnumerateArray().ToList());
                        }
                    }
                }
                catch (Exception) {; }

                if (video != null)
                {
                    foreach (var node in video)
                    {
                        Video v = new Video();
                        v.dur      = pDur;
                        v.id       = node.GetProperty("id").ToString();
                        v.dfn      = Program.qualitys[v.id];
                        v.bandwith = Convert.ToInt64(node.GetProperty("bandwidth").ToString()) / 1000;
                        v.baseUrl  = node.GetProperty("base_url").ToString();
                        v.codecs   = node.GetProperty("codecid").ToString() == "12" ? "HEVC" : "AVC";
                        if (!tvApi && !appApi)
                        {
                            v.res = node.GetProperty("width").ToString() + "x" + node.GetProperty("height").ToString();
                            v.fps = node.GetProperty("frame_rate").ToString();
                        }
                        if (onlyHevc && v.codecs == "AVC")
                        {
                            continue;
                        }
                        if (onlyAvc && v.codecs == "HEVC")
                        {
                            continue;
                        }
                        if (!videoTracks.Contains(v))
                        {
                            videoTracks.Add(v);
                        }
                    }
                }

                //此处处理免二压视频,需要单独再请求一次
                if (!reParse && !appApi)
                {
                    reParse = true;
                    goto reParse;
                }

                if (audio != null)
                {
                    foreach (var node in audio)
                    {
                        Audio a = new Audio();
                        a.id       = node.GetProperty("id").ToString();
                        a.dfn      = a.id;
                        a.dur      = pDur;
                        a.bandwith = Convert.ToInt64(node.GetProperty("bandwidth").ToString()) / 1000;
                        a.baseUrl  = node.GetProperty("base_url").ToString();
                        a.codecs   = node.GetProperty("codecs").ToString().Replace("mp4a.40.2", "M4A").Replace("ec-3", "E-AC-3");
                        audioTracks.Add(a);
                    }
                }
            }
            else if (webJsonStr.Contains("\"durl\":[")) //flv
            {
                //默认以最高清晰度解析
                webJsonStr = await GetPlayJsonAsync(onlyAvc, aidOri, aid, cid, epId, tvApi, intlApi, appApi, GetMaxQn());

                respJson = JsonDocument.Parse(webJsonStr);
                string quality      = "";
                string videoCodecid = "";
                string url          = "";
                double size         = 0;
                double length       = 0;
                var    nodeName     = "data";
flvParse:
                if (webJsonStr.Contains($"\"{nodeName}\":{{"))
                {
                    var dataNode = respJson.RootElement.GetProperty(nodeName);
                    quality      = dataNode.GetProperty("quality").ToString();
                    videoCodecid = dataNode.GetProperty("video_codecid").ToString();
                    //获取所有分段
                    foreach (var node in dataNode.GetProperty("durl").EnumerateArray().ToList())
                    {
                        clips.Add(node.GetProperty("url").ToString());
                        size   += node.GetProperty("size").GetDouble();
                        length += node.GetProperty("length").GetDouble();
                    }
                    //TV模式可用清晰度
                    JsonElement qnExtras;
                    JsonElement acceptQuality;
                    if (dataNode.TryGetProperty("qn_extras", out qnExtras))
                    {
                        foreach (var node in qnExtras.EnumerateArray())
                        {
                            dfns.Add(node.GetProperty("qn").ToString());
                        }
                    }
                    else if (dataNode.TryGetProperty("accept_quality", out acceptQuality)) //非tv模式可用清晰度
                    {
                        foreach (var node in acceptQuality.EnumerateArray())
                        {
                            string _qn = node.ToString();
                            if (_qn != null && _qn.Length > 0)
                            {
                                dfns.Add(node.ToString());
                            }
                        }
                    }
                }
                else if (webJsonStr.Contains("\"result\":{"))
                {
                    nodeName = "result";
                    goto flvParse;
                }
                else
                {
                    //如果获取数据失败,尝试从根路径获取数据
                    string nodeinfo = respJson.RootElement.ToString();
                    var    nodeJson = JsonDocument.Parse(nodeinfo).RootElement;
                    quality      = nodeJson.GetProperty("quality").ToString();
                    videoCodecid = nodeJson.GetProperty("video_codecid").ToString();
                    //获取所有分段
                    foreach (var node in nodeJson.GetProperty("durl").EnumerateArray())
                    {
                        clips.Add(node.GetProperty("url").ToString());
                        size   += node.GetProperty("size").GetDouble();
                        length += node.GetProperty("length").GetDouble();
                    }
                    //TV模式可用清晰度
                    JsonElement qnExtras;
                    JsonElement acceptQuality;
                    if (nodeJson.TryGetProperty("qn_extras", out qnExtras))
                    {
                        //获取可用清晰度
                        foreach (var node in qnExtras.EnumerateArray())
                        {
                            dfns.Add(node.GetProperty("qn").ToString());
                        }
                    }
                    else if (nodeJson.TryGetProperty("accept_quality", out acceptQuality)) //非tv模式可用清晰度
                    {
                        foreach (var node in acceptQuality.EnumerateArray())
                        {
                            string _qn = node.ToString();
                            if (_qn != null && _qn.Length > 0)
                            {
                                dfns.Add(node.ToString());
                            }
                        }
                    }
                }

                Video v = new Video();
                v.id      = quality;
                v.dfn     = Program.qualitys[quality];
                v.baseUrl = url;
                v.codecs  = videoCodecid == "12" ? "HEVC" : "AVC";
                v.dur     = (int)length / 1000;
                v.size    = size;
                if (onlyHevc && v.codecs == "AVC")
                {
                }
                if (!videoTracks.Contains(v))
                {
                    videoTracks.Add(v);
                }
            }

            return(webJsonStr, videoTracks, audioTracks, clips, dfns);
        }