public async Task <M3U8DownloadResult> DownloadM3U8FilesAsParallel(M3U8File mfile, string savedir, CancellationToken token, bool skipexistfile = true) { var result = new M3U8DownloadResult(); try { Directory.CreateDirectory(savedir); DownloadM3U8Head(mfile.Head, savedir); if (token.IsCancellationRequested) { result.IsCancelled = true; return(result); } var action = new Action <M3U8Segment, bool>((node, succ) => { if (succ) { result.DownloadedNodes.Add(node); } var e = new M3U8VideoDownloadProgressChangedArgs(mfile, node) { IsNodeDownloadSuccess = succ, Count = result.DownloadedNodes.Count }; OnReport(e); }); await DownloadM3U8SegmentsAsParallel(mfile.Segments, savedir, token, skipexistfile, action); } catch (Exception ex) { result.Exception = ex; } return(result); }
/// <summary> /// 解密(如果需要)并合并M3U8片段为一个文件 /// </summary> /// <param name="target"></param> /// <param name="srcdir"></param> /// <param name="dstfile"></param> /// <param name="ignoreerror"></param> public static async Task <OperateResult> CombineM3U8Segments(M3U8File target, string srcdir, string dstfile, Action <M3U8SegmentsCombineProgressChangedEventArgs> progressChangedAction = null, bool ignoreerror = true) { var result = new OperateResult(); try { using (var fs = new FileStream(dstfile, FileMode.Create, FileAccess.ReadWrite)) { var totallength = target.Segments.Length; for (var index = 0; index < totallength; index++) { var node = target.Segments[index]; var srcfile = Path.Combine(srcdir, node.SegmentName); var arg = new M3U8SegmentsCombineProgressChangedEventArgs { M3U8File = target, IsInProgress = true, CurrIndex = index, IsComplete = false }; if (File.Exists(srcfile)) { if (target.Head.IsEncrypt || node.IsEncrypt) { var buff = File.ReadAllBytes(srcfile); var data = AesDecrypt(buff, target.Head.Key, target.Head.IV); await fs.WriteAsync(data, 0, data.Length); } else { var srcfs = new FileStream(srcfile, FileMode.Open); await srcfs.CopyToAsync(fs); fs.Flush(); } arg.CurrNodeCombined = true; } else { if (ignoreerror) { arg.CurrNodeCombined = false; } else { throw new FileNotFoundException(); } } progressChangedAction?.Invoke(arg); } } result.IsComplete = true; } catch (Exception ex) { result.Exception = ex; System.Diagnostics.Trace.TraceWarning($"CombineM3U8Segments Ex:{ex.Message}"); } return(result); }
public static string GetM3U8Content(M3U8File m3u8, bool keepurl) { StringBuilder sb = new StringBuilder(); var maxd = m3u8.Segments.Max(p => p.Seconds); maxd = Math.Truncate(maxd) + 1; sb.Append("#EXTM3U\n"); sb.Append($"#EXT-X-VERSION:{m3u8.Head.VERSION}\n"); sb.Append($"#EXT-X-TARGETDURATION:{m3u8.Head.TARGETDURATION}\n"); sb.Append($"#EXT-X-MEDIA-SEQUENCE:{m3u8.Head.MEDIA_SEQUENCE}\n"); if (keepurl) { if (m3u8.Head.HasMap) { sb.Append($"#EXT-X-MAP:URI=\"{m3u8.Head.MapUrl}\",\n"); } if (m3u8.Head.IsEncrypt) { sb.Append($"#EXT-X-KEY:METHOD={m3u8.Head.EncryptMethod},KEYURI=\"{m3u8.Head.KeyUrl}\"\n"); } foreach (var node in m3u8.Segments) { sb.Append($"#EXTINF:{node.Seconds},\n"); sb.Append($"{node.Target}\n"); } } else { if (m3u8.Head.HasMap) { var mapurl = new Uri(m3u8.Head.MapUrl); sb.Append($"#EXT-X-MAP:URI=\"{mapurl.LocalPath.Substring(mapurl.LocalPath.LastIndexOf('/') + 1)}\",\n"); } if (m3u8.Head.IsEncrypt) { var keyurl = new Uri(m3u8.Head.KeyUrl); sb.Append($"#EXT-X-KEY:METHOD={m3u8.Head.EncryptMethod},KEYURI=\"{keyurl.LocalPath.Substring(keyurl.LocalPath.LastIndexOf('/') + 1)}\"\n"); } foreach (var node in m3u8.Segments) { sb.Append($"#EXTINF:{node.Seconds},\n"); sb.Append($"{node.SegmentName}\n"); } } sb.Append("#EXT-X-ENDLIST\n"); return(sb.ToString()); }
public static M3U8File AnalyzeM3U8Content(Uri url, string content) { if (!content.StartsWith("#EXTM3U")) { return(null); } var path = url.LocalPath; path = path.Substring(0, path.LastIndexOf('/') + 1); var fixurl = url.Scheme + "://" + url.Authority + path; var fixuri = new Uri(fixurl); var head = AnalyzeM3U8Head(fixuri, content); var nodes = AnalyzeSegments(fixuri, content); var m3u8 = new M3U8File(url, head, nodes); return(m3u8); }
/// <summary> /// 异步下载M3U8视频 /// </summary> /// <param name="target"></param> /// <param name="savedir"></param> public async Task <M3U8DownloadResult> DownloadM3U8VideoFilesAsync(M3U8File target, string savedir, CancellationToken token, bool skipexistsegment = false) { var result = new M3U8DownloadResult(); try { Directory.CreateDirectory(savedir); await Task.Factory.StartNew(() => DownloadM3U8Head(target.Head, savedir), token); var action = new Action <M3U8Segment, bool>((node, succ) => { if (succ) { result.DownloadedNodes.Add(node); } var arg = new M3U8VideoDownloadProgressChangedArgs(target, node) { IsNodeDownloadSuccess = succ, Count = result.DownloadedNodes.Count }; OnReport(arg); token.ThrowIfCancellationRequested(); }); result.IsCancelled = await DownloadM3U8VideoSegments(target.Segments, savedir, skipexistsegment, action); if (!result.IsCancelled) { result.IsComplete = true; } } catch (Exception ex) { result.Exception = ex; } return(result); }
public static void SaveToFile(M3U8File m3u8, string filename, bool keepurl = true) { var content = GetM3U8Content(m3u8, keepurl); File.WriteAllText(filename, content); }
public M3U8SegmentDownloadedEventArgs(M3U8Segment node, M3U8File file) { Segment = node; M3U8File = file; }
public M3U8VideoDownloadProgressChangedArgs(M3U8File file, M3U8Segment node) { File = file; Node = node; TotalCount = file.Segments.Length; }