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); }
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); } }
private static int Compare(Audio r1, Audio r2) { return(r1.bandwith - r2.bandwith > 0 ? -1 : 1); }
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); }