public string[] ParseKey(string line) { if (!downloadingM3u8KeyTip) { LOGGER.PrintLine(strings.downloadingM3u8Key, LOGGER.Warning); downloadingM3u8KeyTip = true; } string[] tmp = line.Replace(HLSTags.ext_x_key + ":", "").Split(','); string[] key = new string[] { "NONE", "", "" }; string u_l = Global.GetTagAttribute(lastKeyLine.Replace(HLSTags.ext_x_key + ":", ""), "URI"); string method = Global.GetTagAttribute(line.Replace(HLSTags.ext_x_key + ":", ""), "METHOD"); string uri = Global.GetTagAttribute(line.Replace(HLSTags.ext_x_key + ":", ""), "URI"); string iv = Global.GetTagAttribute(line.Replace(HLSTags.ext_x_key + ":", ""), "IV"); //存在加密 if (method != "") { if (method != "AES-128") { LOGGER.PrintLine(string.Format(strings.notSupportMethod, method), LOGGER.Error); DownloadManager.BinaryMerge = true; return(new string[] { $"{method}(NOTSUPPORTED)", "", "" }); } //METHOD key[0] = method; //URI key[1] = uri; if (u_l == uri) { key[1] = m3u8CurrentKey[1]; } else { LOGGER.WriteLine(strings.downloadingM3u8Key + " " + key[1]); if (key[1].StartsWith("http")) { string keyUrl = key[1]; if (isQiQiuYun) { /*string encKey = Encoding.Default.GetString(Global.HttpDownloadFileToBytes(keyUrl, Headers)); * var indexs = "0-1-2-3-4-5-6-7-8-10-11-12-14-15-16-18".Split('-'); * if (encKey.Length == 20) * { * var algorithmCharCode = (int)Encoding.ASCII.GetBytes(encKey)[0]; * var algorithmChar = Encoding.ASCII.GetString(new byte[] { (byte)algorithmCharCode }); * var algorithmCharStart = Global.GetNum(algorithmChar, 36) % 7; * var firstAlgorithmCharCode = (int)Encoding.ASCII.GetBytes(encKey)[algorithmCharStart]; * var firstAlgorithmChar = Encoding.ASCII.GetString(new byte[] { (byte)firstAlgorithmCharCode }); * var secondAlgorithmCharCode = (int)Encoding.ASCII.GetBytes(encKey)[algorithmCharStart + 1]; * var secondAlgorithmChar = Encoding.ASCII.GetString(new byte[] { (byte)secondAlgorithmCharCode }); * var algorithmNum = Global.GetNum(firstAlgorithmChar + secondAlgorithmChar, 36) % 3; * * if (algorithmNum == 1) * { * indexs = "0-1-2-3-4-5-6-7-18-16-15-13-12-11-10-8".Split('-'); * } * else if (algorithmNum == 0) * { * indexs = "0-1-2-3-4-5-6-7-8-10-11-12-14-15-16-18".Split('-'); * } * else if (algorithmNum == 2) * { * var a_CODE = (int)Encoding.ASCII.GetBytes("a")[0]; * * var c9 = (int)Encoding.ASCII.GetBytes(encKey)[8]; * var c9t = (int)Encoding.ASCII.GetBytes(encKey)[9]; * var c10 = (int)Encoding.ASCII.GetBytes(encKey)[10]; * var c10t = (int)Encoding.ASCII.GetBytes(encKey)[11]; * var c14 = (int)Encoding.ASCII.GetBytes(encKey)[15]; * var c14t = (int)Encoding.ASCII.GetBytes(encKey)[16]; * var c15 = (int)Encoding.ASCII.GetBytes(encKey)[17]; * var c15t = (int)Encoding.ASCII.GetBytes(encKey)[18]; * * var c9r = c9 - a_CODE + (Global.GetNum(Encoding.ASCII.GetString(new byte[] { (byte)c9t }), 10) + 1) * 26 - a_CODE; * var c10r = c10 - a_CODE + (Global.GetNum(Encoding.ASCII.GetString(new byte[] { (byte)c10t }), 10) + 1) * 26 - a_CODE; * var c14r = c14 - a_CODE + (Global.GetNum(Encoding.ASCII.GetString(new byte[] { (byte)c14t }), 10) + 1) * 26 - a_CODE; * var c15r = c15 - a_CODE + (Global.GetNum(Encoding.ASCII.GetString(new byte[] { (byte)c15t }), 10) + 2) * 26 - a_CODE; * * //构造key * key[1] = Convert.ToBase64String( * new byte[] * { * Encoding.ASCII.GetBytes(encKey)[0], * Encoding.ASCII.GetBytes(encKey)[1], * Encoding.ASCII.GetBytes(encKey)[2], * Encoding.ASCII.GetBytes(encKey)[3], * Encoding.ASCII.GetBytes(encKey)[4], * Encoding.ASCII.GetBytes(encKey)[5], * Encoding.ASCII.GetBytes(encKey)[6], * Encoding.ASCII.GetBytes(encKey)[7], * (byte)c9r, * (byte)c10r, * Encoding.ASCII.GetBytes(encKey)[12], * Encoding.ASCII.GetBytes(encKey)[13], * Encoding.ASCII.GetBytes(encKey)[14], * (byte)c14r, * (byte)c15r, * Encoding.ASCII.GetBytes(encKey)[19] * } * ); * //IV * key[2] = i; * return key; * } * } * else if (encKey.Length == 17) * { * indexs = "1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16".Split('-'); * } * else * { * indexs = "1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16".Split('-'); * } * * string decKey = ""; * foreach (var _i in indexs) * { * decKey += encKey[Convert.ToInt32(_i)]; * } * key[1] = Convert.ToBase64String(Encoding.Default.GetBytes(decKey));*/ key[1] = Convert.ToBase64String(Global.HttpDownloadFileToBytes(keyUrl, "User-Agent:Mozilla/5.0 (Linux; U; Android 7.0; zh-cn; 15 Plus Build/NRD90M) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/66.0.3359.126 MQQBrowser/9.4 Mobile Safari/537.36")); } //气球云 else if (key[1].Contains("imooc.com/")) { key[1] = DecodeImooc.DecodeKey(Global.GetWebSource(key[1], Headers)); } else { if (keyUrl.Contains("https://keydeliver.linetv.tw/jurassicPark")) //linetv { keyUrl = keyUrl + "?time=" + Global.GetTimeStamp(false); } key[1] = Convert.ToBase64String(Global.HttpDownloadFileToBytes(keyUrl, Headers)); } } //DMM网站 else if (key[1].StartsWith("base64:")) { key[1] = key[1].Replace("base64:", ""); } else { string keyUrl = CombineURL(BaseUrl, key[1]); if (keyUrl.Contains("edu.51cto.com")) //51cto { string lessonId = Global.GetQueryString("lesson_id", keyUrl); keyUrl = keyUrl + "&sign=" + Decode51CtoKey.GetSign(lessonId); var encodeKey = Encoding.UTF8.GetString(Global.HttpDownloadFileToBytes(keyUrl, Headers)); key[1] = Decode51CtoKey.GetDecodeKey(encodeKey, lessonId); } else { key[1] = Convert.ToBase64String(Global.HttpDownloadFileToBytes(keyUrl, Headers));//keyUrl读取key } } } //IV key[2] = iv; } return(key); }
public void Parse() { FFmpeg.REC_TIME = ""; m3u8SavePath = Path.Combine(DownDir, "raw.m3u8"); jsonSavePath = Path.Combine(DownDir, "meta.json"); if (!Directory.Exists(DownDir)) //若文件夹不存在则新建文件夹 { Directory.CreateDirectory(DownDir); //新建文件夹 } //存放分部的所有信息(#EXT-X-DISCONTINUITY) JArray parts = new JArray(); //存放分片的所有信息 JArray segments = new JArray(); JObject segInfo = new JObject(); extLists.Clear(); string m3u8Content = string.Empty; string m3u8Method = string.Empty; string[] extMAP = { "", "" }; string[] extList = new string[10]; long segIndex = 0; long startIndex = 0; int targetDuration = 0; double totalDuration = 0; bool expectSegment = false, expectPlaylist = false, isIFramesOnly = false, isIndependentSegments = false, isEndlist = false, isAd = false, isM3u = false; //获取m3u8内容 if (!LiveStream) { LOGGER.PrintLine(strings.downloadingM3u8, LOGGER.Warning); } if (M3u8Url.StartsWith("http")) { if (M3u8Url.Contains("nfmovies.com/hls")) { m3u8Content = DecodeNfmovies.DecryptM3u8(Global.HttpDownloadFileToBytes(M3u8Url, Headers)); } else if (M3u8Url.Contains("hls.ddyunp.com/ddyun")) { m3u8Content = DecodeDdyun.DecryptM3u8(Global.HttpDownloadFileToBytes(DecodeDdyun.GetVaildM3u8Url(M3u8Url), Headers)); } else { m3u8Content = Global.GetWebSource(M3u8Url, Headers); } } else if (M3u8Url.StartsWith("file:")) { Uri t = new Uri(M3u8Url); m3u8Content = File.ReadAllText(t.LocalPath); } else if (File.Exists(M3u8Url)) { m3u8Content = File.ReadAllText(M3u8Url); if (!M3u8Url.Contains("\\")) { M3u8Url = Path.Combine(Environment.CurrentDirectory, M3u8Url); } Uri t = new Uri(M3u8Url); M3u8Url = t.ToString(); } if (m3u8Content == "") { return; } if (m3u8Content.Contains("qiqiuyun.net/") || m3u8Content.Contains("aliyunedu.net/") || m3u8Content.Contains("qncdn.edusoho.net/")) //气球云 { isQiQiuYun = true; } if (M3u8Url.Contains("tlivecloud-playback-cdn.ysp.cctv.cn") && M3u8Url.Contains("endtime=")) { isEndlist = true; } if (M3u8Url.Contains("imooc.com/")) { m3u8Content = DecodeImooc.DecodeM3u8(m3u8Content); } if (m3u8Content.Contains("</MPD>") && m3u8Content.Contains("<MPD")) { var mpdSavePath = Path.Combine(DownDir, "dash.mpd"); //输出mpd文件 File.WriteAllText(mpdSavePath, m3u8Content); //分析mpd文件 M3u8Url = Global.Get302(M3u8Url, Headers); var newUri = MPDParser.Parse(DownDir, M3u8Url, m3u8Content, BaseUrl); M3u8Url = newUri; m3u8Content = File.ReadAllText(new Uri(M3u8Url).LocalPath); } //输出m3u8文件 File.WriteAllText(m3u8SavePath, m3u8Content); //针对优酷#EXT-X-VERSION:7杜比视界片源修正 if (m3u8Content.Contains("#EXT-X-DISCONTINUITY") && m3u8Content.Contains("#EXT-X-MAP") && m3u8Content.Contains("ott.cibntv.net") && m3u8Content.Contains("ccode=")) { Regex ykmap = new Regex("#EXT-X-DISCONTINUITY\\s+#EXT-X-MAP:URI=\\\"(.*?)\\\",BYTERANGE=\\\"(.*?)\\\""); foreach (Match m in ykmap.Matches(m3u8Content)) { m3u8Content = m3u8Content.Replace(m.Value, $"#EXTINF:0.000000,\n#EXT-X-BYTERANGE:{m.Groups[2].Value}\n{m.Groups[1].Value}"); } } //如果BaseUrl为空则截取字符串充当 if (BaseUrl == "") { if (new Regex("#YUMING\\|(.*)").IsMatch(m3u8Content)) { BaseUrl = new Regex("#YUMING\\|(.*)").Match(m3u8Content).Groups[1].Value; } else { BaseUrl = GetBaseUrl(M3u8Url, Headers); } } if (!LiveStream) { LOGGER.WriteLine(strings.parsingM3u8); LOGGER.PrintLine(strings.parsingM3u8); } if (!string.IsNullOrEmpty(KeyBase64)) { string line = ""; if (string.IsNullOrEmpty(KeyIV)) { line = $"#EXT-X-KEY:METHOD=AES-128,URI=\"base64:{KeyBase64}\""; } else { line = $"#EXT-X-KEY:METHOD=AES-128,URI=\"base64:{KeyBase64}\",IV=0x{KeyIV.Replace("0x", "")}"; } m3u8CurrentKey = ParseKey(line); } if (!string.IsNullOrEmpty(KeyFile)) { string line = ""; Uri u = new Uri(KeyFile); if (string.IsNullOrEmpty(KeyIV)) { line = $"#EXT-X-KEY:METHOD=AES-128,URI=\"{u.ToString()}\""; } else { line = $"#EXT-X-KEY:METHOD=AES-128,URI=\"{u.ToString()}\",IV=0x{KeyIV.Replace("0x", "")}"; } m3u8CurrentKey = ParseKey(line); } //逐行分析 using (StringReader sr = new StringReader(m3u8Content)) { string line; double segDuration = 0; string segUrl = string.Empty; //#EXT-X-BYTERANGE:<n>[@<o>] long expectByte = -1; //parm n long startByte = 0; //parm o while ((line = sr.ReadLine()) != null) { if (string.IsNullOrEmpty(line)) { continue; } if (line.StartsWith(HLSTags.ext_m3u)) { isM3u = true; } //只下载部分字节 else if (line.StartsWith(HLSTags.ext_x_byterange)) { string[] t = line.Replace(HLSTags.ext_x_byterange + ":", "").Split('@'); if (t.Length > 0) { if (t.Length == 1) { expectByte = Convert.ToInt64(t[0]); segInfo.Add("expectByte", expectByte); } if (t.Length == 2) { expectByte = Convert.ToInt64(t[0]); startByte = Convert.ToInt64(t[1]); segInfo.Add("expectByte", expectByte); segInfo.Add("startByte", startByte); } } expectSegment = true; } //国家地理去广告 else if (line.StartsWith("#UPLYNK-SEGMENT")) { if (line.Contains(",ad")) { isAd = true; } else if (line.Contains(",segment")) { isAd = false; } } //国家地理去广告 else if (isAd) { continue; } //解析定义的分段长度 else if (line.StartsWith(HLSTags.ext_x_targetduration)) { targetDuration = Convert.ToInt32(Convert.ToDouble(line.Replace(HLSTags.ext_x_targetduration + ":", "").Trim())); } //解析起始编号 else if (line.StartsWith(HLSTags.ext_x_media_sequence)) { segIndex = Convert.ToInt64(line.Replace(HLSTags.ext_x_media_sequence + ":", "").Trim()); startIndex = segIndex; } else if (line.StartsWith(HLSTags.ext_x_discontinuity_sequence)) { } else if (line.StartsWith(HLSTags.ext_x_program_date_time)) { if (string.IsNullOrEmpty(FFmpeg.REC_TIME)) { FFmpeg.REC_TIME = line.Replace(HLSTags.ext_x_program_date_time + ":", "").Trim(); } } //解析不连续标记,需要单独合并(timestamp不同) else if (line.StartsWith(HLSTags.ext_x_discontinuity)) { //修复优酷去除广告后的遗留问题 if (hasAd && parts.Count > 0) { segments = (JArray)parts[parts.Count - 1]; parts.RemoveAt(parts.Count - 1); hasAd = false; continue; } //常规情况的#EXT-X-DISCONTINUITY标记,新建part if (!hasAd && segments.Count > 1) { parts.Add(segments); segments = new JArray(); } } else if (line.StartsWith(HLSTags.ext_x_cue_out)) { ; } else if (line.StartsWith(HLSTags.ext_x_cue_out_start)) { ; } else if (line.StartsWith(HLSTags.ext_x_cue_span)) { ; } else if (line.StartsWith(HLSTags.ext_x_version)) { ; } else if (line.StartsWith(HLSTags.ext_x_allow_cache)) { ; } //解析KEY else if (line.StartsWith(HLSTags.ext_x_key)) { //自定义KEY情况 判断是否需要读取IV if (!string.IsNullOrEmpty(KeyFile) || !string.IsNullOrEmpty(KeyBase64)) { if (m3u8CurrentKey[2] == "" && line.Contains("IV=0x")) { var temp = ParseKey(line); //{METHOD,Key,IV} m3u8CurrentKey[2] = temp[2]; //使用m3u8中的IV } } else { m3u8CurrentKey = ParseKey(line);//获取method,key,iv //存储为上一行的key信息 lastKeyLine = line; } } //解析分片时长(暂时不考虑标题属性) else if (line.StartsWith(HLSTags.extinf)) { string[] tmp = line.Replace(HLSTags.extinf + ":", "").Split(','); segDuration = Convert.ToDouble(tmp[0]); segInfo.Add("index", segIndex); segInfo.Add("method", m3u8CurrentKey[0]); //是否有加密,有的话写入KEY和IV if (m3u8CurrentKey[0] != "NONE") { segInfo.Add("key", m3u8CurrentKey[1]); //没有读取到IV,自己生成 if (m3u8CurrentKey[2] == "") { segInfo.Add("iv", "0x" + Convert.ToString(segIndex, 16).PadLeft(32, '0')); } else { segInfo.Add("iv", m3u8CurrentKey[2]); } } totalDuration += segDuration; segInfo.Add("duration", segDuration); expectSegment = true; segIndex++; } //解析STREAM属性 else if (line.StartsWith(HLSTags.ext_x_stream_inf)) { expectPlaylist = true; string bandwidth = Global.GetTagAttribute(line, "BANDWIDTH"); string average_bandwidth = Global.GetTagAttribute(line, "AVERAGE-BANDWIDTH"); string codecs = Global.GetTagAttribute(line, "CODECS"); string resolution = Global.GetTagAttribute(line, "RESOLUTION"); string frame_rate = Global.GetTagAttribute(line, "FRAME-RATE"); string hdcp_level = Global.GetTagAttribute(line, "HDCP-LEVEL"); string audio = Global.GetTagAttribute(line, "AUDIO"); string video = Global.GetTagAttribute(line, "VIDEO"); string subtitles = Global.GetTagAttribute(line, "SUBTITLES"); string closed_captions = Global.GetTagAttribute(line, "CLOSED-CAPTIONS"); extList = new string[] { bandwidth, average_bandwidth, codecs, resolution, frame_rate, hdcp_level, audio, video, subtitles, closed_captions }; } else if (line.StartsWith(HLSTags.ext_x_i_frame_stream_inf)) { ; } else if (line.StartsWith(HLSTags.ext_x_media)) { if (Global.GetTagAttribute(line, "TYPE") == "AUDIO" && !MEDIA_AUDIO.ContainsKey(Global.GetTagAttribute(line, "GROUP-ID"))) { MEDIA_AUDIO.Add(Global.GetTagAttribute(line, "GROUP-ID"), CombineURL(BaseUrl, Global.GetTagAttribute(line, "URI"))); } if (Global.GetTagAttribute(line, "TYPE") == "SUBTITLES") { if (!MEDIA_SUB.ContainsKey(Global.GetTagAttribute(line, "GROUP-ID"))) { MEDIA_SUB.Add(Global.GetTagAttribute(line, "GROUP-ID"), CombineURL(BaseUrl, Global.GetTagAttribute(line, "URI"))); } } } else if (line.StartsWith(HLSTags.ext_x_playlist_type)) { ; } else if (line.StartsWith(HLSTags.ext_i_frames_only)) { isIFramesOnly = true; } else if (line.StartsWith(HLSTags.ext_is_independent_segments)) { isIndependentSegments = true; } //m3u8主体结束 else if (line.StartsWith(HLSTags.ext_x_endlist)) { if (segments.Count > 0) { parts.Add(segments); } segments = new JArray(); isEndlist = true; } //#EXT-X-MAP else if (line.StartsWith(HLSTags.ext_x_map)) { if (extMAP[0] == "") { extMAP[0] = Global.GetTagAttribute(line, "URI"); if (line.Contains("BYTERANGE")) { extMAP[1] = Global.GetTagAttribute(line, "BYTERANGE"); } if (!extMAP[0].StartsWith("http")) { extMAP[0] = CombineURL(BaseUrl, extMAP[0]); } } //遇到了其他的map,说明已经不是一个视频了,全部丢弃即可 else { if (segments.Count > 0) { parts.Add(segments); } segments = new JArray(); isEndlist = true; break; } } else if (line.StartsWith(HLSTags.ext_x_start)) { ; } //评论行不解析 else if (line.StartsWith("#")) { continue; } //空白行不解析 else if (line.StartsWith("\r\n")) { continue; } //解析分片的地址 else if (expectSegment) { segUrl = CombineURL(BaseUrl, line); if (M3u8Url.Contains("?__gda__")) { segUrl += new Regex("\\?__gda__.*").Match(M3u8Url).Value; } if (M3u8Url.Contains("//dlsc.hcs.cmvideo.cn") && (segUrl.EndsWith(".ts") || segUrl.EndsWith(".mp4"))) { segUrl += new Regex("\\?.*").Match(M3u8Url).Value; } segInfo.Add("segUri", segUrl); segments.Add(segInfo); segInfo = new JObject(); //优酷的广告分段则清除此分片 //需要注意,遇到广告说明程序对上文的#EXT-X-DISCONTINUITY做出的动作是不必要的, //其实上下文是同一种编码,需要恢复到原先的part上 if (DelAd && segUrl.Contains("ccode=") && segUrl.Contains("/ad/") && segUrl.Contains("duration=")) { segments.RemoveAt(segments.Count - 1); segIndex--; hasAd = true; } //优酷广告(4K分辨率测试) if (DelAd && segUrl.Contains("ccode=0902") && segUrl.Contains("duration=")) { segments.RemoveAt(segments.Count - 1); segIndex--; hasAd = true; } expectSegment = false; } //解析STREAM属性的URI else if (expectPlaylist) { string listUrl; listUrl = CombineURL(BaseUrl, line); if (M3u8Url.Contains("?__gda__")) { listUrl += new Regex("\\?__gda__.*").Match(M3u8Url).Value; } StringBuilder sb = new StringBuilder(); sb.Append("{"); sb.Append("\"URL\":\"" + listUrl + "\","); for (int i = 0; i < 10; i++) { if (extList[i] != "") { switch (i) { case 0: sb.Append("\"BANDWIDTH\":\"" + extList[i] + "\","); break; case 1: sb.Append("\"AVERAGE-BANDWIDTH\":\"" + extList[i] + "\","); break; case 2: sb.Append("\"CODECS\":\"" + extList[i] + "\","); break; case 3: sb.Append("\"RESOLUTION\":\"" + extList[i] + "\","); break; case 4: sb.Append("\"FRAME-RATE\":\"" + extList[i] + "\","); break; case 5: sb.Append("\"HDCP-LEVEL\":\"" + extList[i] + "\","); break; case 6: sb.Append("\"AUDIO\":\"" + extList[i] + "\","); break; case 7: sb.Append("\"VIDEO\":\"" + extList[i] + "\","); break; case 8: sb.Append("\"SUBTITLES\":\"" + extList[i] + "\","); break; case 9: sb.Append("\"CLOSED-CAPTIONS\":\"" + extList[i] + "\","); break; } } } sb.Append("}"); extLists.Add(sb.ToString().Replace(",}", "}")); if (Convert.ToInt64(extList[0]) > bestBandwidth) { bestBandwidth = Convert.ToInt64(extList[0]); bestUrl = listUrl; bestUrlAudio = extList[6]; bestUrlSub = extList[8]; } extList = new string[8]; expectPlaylist = false; } } } if (isM3u == false) { LOGGER.WriteLineError(strings.invalidM3u8); LOGGER.PrintLine(strings.invalidM3u8, LOGGER.Error); return; } //直播的情况,无法遇到m3u8结束标记,需要手动将segments加入parts if (parts.HasValues == false) { parts.Add(segments); } //构造JSON文件 JObject jsonResult = new JObject(); jsonResult.Add("m3u8", M3u8Url); jsonResult.Add("m3u8BaseUri", BaseUrl); jsonResult.Add("updateTime", DateTime.Now.ToString("o")); JObject jsonM3u8Info = new JObject(); jsonM3u8Info.Add("originalCount", segIndex - startIndex); jsonM3u8Info.Add("count", segIndex - startIndex); jsonM3u8Info.Add("vod", isEndlist); jsonM3u8Info.Add("targetDuration", targetDuration); jsonM3u8Info.Add("totalDuration", totalDuration); if (bestUrlAudio != "" && MEDIA_AUDIO.ContainsKey(bestUrlAudio)) { jsonM3u8Info.Add("audio", MEDIA_AUDIO[bestUrlAudio]); } if (bestUrlSub != "" && MEDIA_SUB.ContainsKey(bestUrlSub)) { jsonM3u8Info.Add("sub", MEDIA_SUB[bestUrlSub]); } if (extMAP[0] != "") { if (extMAP[1] == "") { jsonM3u8Info.Add("extMAP", extMAP[0]); } else { jsonM3u8Info.Add("extMAP", extMAP[0] + "|" + extMAP[1]); } } //根据DurRange来生成分片Range if (DurStart != "" || DurEnd != "") { double secStart = 0; double secEnd = -1; if (DurEnd == "") { secEnd = totalDuration; } //时间码 Regex reg2 = new Regex(@"(\d+):(\d+):(\d+)"); if (reg2.IsMatch(DurStart)) { int HH = Convert.ToInt32(reg2.Match(DurStart).Groups[1].Value); int MM = Convert.ToInt32(reg2.Match(DurStart).Groups[2].Value); int SS = Convert.ToInt32(reg2.Match(DurStart).Groups[3].Value); secStart = SS + MM * 60 + HH * 60 * 60; } if (reg2.IsMatch(DurEnd)) { int HH = Convert.ToInt32(reg2.Match(DurEnd).Groups[1].Value); int MM = Convert.ToInt32(reg2.Match(DurEnd).Groups[2].Value); int SS = Convert.ToInt32(reg2.Match(DurEnd).Groups[3].Value); secEnd = SS + MM * 60 + HH * 60 * 60; } bool flag1 = false; bool flag2 = false; if (secEnd - secStart > 0) { double dur = 0; //当前时间 foreach (JArray part in parts) { foreach (var seg in part) { dur += Convert.ToDouble(seg["duration"].ToString()); if (flag1 == false && dur > secStart) { RangeStart = seg["index"].Value <int>(); flag1 = true; } if (flag2 == false && dur >= secEnd) { RangeEnd = seg["index"].Value <int>(); flag2 = true; } } } } } //根据Range来清除部分分片 if (RangeStart != 0 || RangeEnd != -1) { if (RangeEnd == -1) { RangeEnd = (int)(segIndex - startIndex - 1); } int newCount = 0; double newTotalDuration = 0; JArray newParts = new JArray(); foreach (JArray part in parts) { JArray newPart = new JArray(); foreach (var seg in part) { if (RangeStart <= seg["index"].Value <int>() && seg["index"].Value <int>() <= RangeEnd) { newPart.Add(seg); newCount++; newTotalDuration += Convert.ToDouble(seg["duration"].ToString()); } } if (newPart.Count != 0) { newParts.Add(newPart); } } parts = newParts; jsonM3u8Info["count"] = newCount; jsonM3u8Info["totalDuration"] = newTotalDuration; } //添加 jsonM3u8Info.Add("segments", parts); jsonResult.Add("m3u8Info", jsonM3u8Info); //输出JSON文件 if (!LiveStream) { LOGGER.WriteLine(strings.wrtingMeta); LOGGER.PrintLine(strings.wrtingMeta); } File.WriteAllText(jsonSavePath, jsonResult.ToString()); //检测是否为master list MasterListCheck(); }
public string[] ParseKey(string line) { if (!downloadingM3u8KeyTip) { LOGGER.PrintLine(strings.downloadingM3u8Key, LOGGER.Warning); downloadingM3u8KeyTip = true; } string[] tmp = line.Replace(HLSTags.ext_x_key + ":", "").Split(','); string[] key = new string[] { "NONE", "", "" }; string u_l = Global.GetTagAttribute(lastKeyLine.Replace(HLSTags.ext_x_key + ":", ""), "URI"); string m = Global.GetTagAttribute(line.Replace(HLSTags.ext_x_key + ":", ""), "METHOD"); string u = Global.GetTagAttribute(line.Replace(HLSTags.ext_x_key + ":", ""), "URI"); string i = Global.GetTagAttribute(line.Replace(HLSTags.ext_x_key + ":", ""), "IV"); //存在加密 if (m != "") { if (m != "AES-128") { LOGGER.PrintLine(string.Format(strings.notSupportMethod, m), LOGGER.Error); DownloadManager.BinaryMerge = true; return(new string[] { $"{m}(NOTSUPPORTED)", "", "" }); } //METHOD key[0] = m; //URI key[1] = u; if (u_l == u) { key[1] = m3u8CurrentKey[1]; } else { LOGGER.WriteLine(strings.downloadingM3u8Key + " " + key[1]); if (key[1].StartsWith("http")) { string keyUrl = key[1]; if (isQiQiuYun) { string encKey = Encoding.Default.GetString(Global.HttpDownloadFileToBytes(keyUrl, Headers)); var indexs = "0-1-2-3-4-5-6-7-8-10-11-12-14-15-16-18".Split('-'); if (encKey.Length == 20) { var algorithmCharCode = (int)Encoding.ASCII.GetBytes(encKey)[0]; var algorithmChar = Encoding.ASCII.GetString(new byte[] { (byte)algorithmCharCode }); var algorithmCharStart = Global.GetNum(algorithmChar, 36) % 7; var firstAlgorithmCharCode = (int)Encoding.ASCII.GetBytes(encKey)[algorithmCharStart]; var firstAlgorithmChar = Encoding.ASCII.GetString(new byte[] { (byte)firstAlgorithmCharCode }); var secondAlgorithmCharCode = (int)Encoding.ASCII.GetBytes(encKey)[algorithmCharStart + 1]; var secondAlgorithmChar = Encoding.ASCII.GetString(new byte[] { (byte)secondAlgorithmCharCode }); var algorithmNum = Global.GetNum(firstAlgorithmChar + secondAlgorithmChar, 36) % 3; if (algorithmNum == 1) { indexs = "0-1-2-3-4-5-6-7-18-16-15-13-12-11-10-8".Split('-'); } else if (algorithmNum == 0) { indexs = "0-1-2-3-4-5-6-7-8-10-11-12-14-15-16-18".Split('-'); } else if (algorithmNum == 2) { var a_CODE = (int)Encoding.ASCII.GetBytes("a")[0]; var c9 = (int)Encoding.ASCII.GetBytes(encKey)[8]; var c9t = (int)Encoding.ASCII.GetBytes(encKey)[9]; var c10 = (int)Encoding.ASCII.GetBytes(encKey)[10]; var c10t = (int)Encoding.ASCII.GetBytes(encKey)[11]; var c14 = (int)Encoding.ASCII.GetBytes(encKey)[15]; var c14t = (int)Encoding.ASCII.GetBytes(encKey)[16]; var c15 = (int)Encoding.ASCII.GetBytes(encKey)[17]; var c15t = (int)Encoding.ASCII.GetBytes(encKey)[18]; var c9r = c9 - a_CODE + (Global.GetNum(Encoding.ASCII.GetString(new byte[] { (byte)c9t }), 10) + 1) * 26 - a_CODE; var c10r = c10 - a_CODE + (Global.GetNum(Encoding.ASCII.GetString(new byte[] { (byte)c10t }), 10) + 1) * 26 - a_CODE; var c14r = c14 - a_CODE + (Global.GetNum(Encoding.ASCII.GetString(new byte[] { (byte)c14t }), 10) + 1) * 26 - a_CODE; var c15r = c15 - a_CODE + (Global.GetNum(Encoding.ASCII.GetString(new byte[] { (byte)c15t }), 10) + 2) * 26 - a_CODE; //构造key key[1] = Convert.ToBase64String( new byte[] { Encoding.ASCII.GetBytes(encKey)[0], Encoding.ASCII.GetBytes(encKey)[1], Encoding.ASCII.GetBytes(encKey)[2], Encoding.ASCII.GetBytes(encKey)[3], Encoding.ASCII.GetBytes(encKey)[4], Encoding.ASCII.GetBytes(encKey)[5], Encoding.ASCII.GetBytes(encKey)[6], Encoding.ASCII.GetBytes(encKey)[7], (byte)c9r, (byte)c10r, Encoding.ASCII.GetBytes(encKey)[12], Encoding.ASCII.GetBytes(encKey)[13], Encoding.ASCII.GetBytes(encKey)[14], (byte)c14r, (byte)c15r, Encoding.ASCII.GetBytes(encKey)[19] } ); //IV key[2] = i; return(key); } } else if (encKey.Length == 17) { indexs = "1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16".Split('-'); } else { indexs = "1-2-3-4-5-6-7-8-9-10-11-12-13-14-15-16".Split('-'); } string decKey = ""; foreach (var _i in indexs) { decKey += encKey[Convert.ToInt32(_i)]; } key[1] = Convert.ToBase64String(Encoding.Default.GetBytes(decKey)); } //气球云 else if (key[1].Contains("imooc.com/")) { key[1] = DecodeImooc.DecodeKey(Global.GetWebSource(key[1], Headers)); } else { if (keyUrl.Contains("https://keydeliver.linetv.tw/jurassicPark")) //linetv { keyUrl = keyUrl + "?time=" + Global.GetTimeStamp(false); } key[1] = Convert.ToBase64String(Global.HttpDownloadFileToBytes(keyUrl, Headers)); } } //DMM网站 else if (key[1].StartsWith("base64:")) { key[1] = key[1].Replace("base64:", ""); } else { string keyUrl = CombineURL(BaseUrl, key[1]); if (keyUrl.Contains("edu.51cto.com")) //51cto { string lessonId = Global.GetQueryString("lesson_id", keyUrl); keyUrl = keyUrl + "&sign=" + Decode51CtoKey.GetSign(lessonId); var encodeKey = Encoding.UTF8.GetString(Global.HttpDownloadFileToBytes(keyUrl, Headers)); key[1] = Decode51CtoKey.GetDecodeKey(encodeKey, lessonId); } else { key[1] = Convert.ToBase64String(Global.HttpDownloadFileToBytes(keyUrl, Headers)); } } } //IV key[2] = i; } return(key); }