# N_m3u8DL-RE 项目文档 ## 项目简介 N_m3u8DL-RE 是一个跨平台的流媒体下载工具,使用 C# 和 .NET 9.0 开发。该项目专门用于下载 DASH(Dynamic Adaptive Streaming over HTTP)、HLS(HTTP Live Streaming) 和 MSS(Microsoft Smooth Streaming) 格式的视频流。它不仅支持点播内容的下载,还能够录制直播流,是一个功能全面的流媒体处理解决方案。 该工具提供了丰富的命令行选项,支持多线程下载、流媒体加密解密(AES-128、ChaCha20等)、自动流选择、实时合并、自定义范围下载等高级功能。项目采用模块化设计,分为主应用程序(N_m3u8DL-RE)、流媒体解析器(N_m3u8DL-RE.Parser)和公共库(N_m3u8DL-RE.Common)三个主要部分,便于维护和扩展。支持 Windows、Linux 和 macOS 等多个平台。 --- ## 核心 API 与功能 ### 命令行基本使用 基本命令行调用方式 ```bash # 下载 HLS 视频流 N_m3u8DL-RE "https://example.com/playlist.m3u8" # 下载 DASH 视频流并指定输出目录和文件名 N_m3u8DL-RE "https://example.com/manifest.mpd" --save-dir "/output" --save-name "video" # 自动选择最佳视频和音频轨道 N_m3u8DL-RE "https://example.com/master.m3u8" --auto-select # 使用 16 个线程下载,设置下载速度限制为 10Mbps N_m3u8DL-RE "https://example.com/playlist.m3u8" --thread-count 16 --max-speed 10M # 录制直播流,限制录制时长为 1 小时 30 分钟 N_m3u8DL-RE "https://live.example.com/stream.m3u8" --live-record-limit "01:30:00" # 下载指定分辨率的视频流,并选择所有英语音轨 N_m3u8DL-RE "https://example.com/master.m3u8" \ --select-video "res=1920x1080:for=best" \ --select-audio "lang=en:for=all" # 下载加密视频并提供解密密钥 N_m3u8DL-RE "https://example.com/encrypted.m3u8" \ --key "0x1234567890abcdef1234567890abcdef" \ --custom-hls-method AES_128 # 仅下载部分内容(第 5 分钟到第 20 分钟) N_m3u8DL-RE "https://example.com/playlist.m3u8" --custom-range "05:00-20:00" # 下载后自动合并为 MP4 格式 N_m3u8DL-RE "https://example.com/playlist.m3u8" \ --mux-after-done "format=mp4:muxer=ffmpeg" # 添加自定义 HTTP 请求头 N_m3u8DL-RE "https://example.com/playlist.m3u8" \ -H "Cookie: session=abc123" \ -H "User-Agent: Mozilla/5.0" \ -H "Referer: https://example.com" ``` --- ### 流媒体提取器 (StreamExtractor) 核心流媒体解析和提取功能,自动识别 HLS/DASH/MSS 格式 ```csharp using N_m3u8DL_RE.Parser; using N_m3u8DL_RE.Parser.Config; using N_m3u8DL_RE.Common.Entity; // 创建解析器配置 var parserConfig = new ParserConfig { Headers = new Dictionary { { "User-Agent", "Mozilla/5.0" }, { "Referer", "https://example.com" } }, BaseUrl = "https://example.com/", AppendUrlParams = false }; // 初始化流提取器 var extractor = new StreamExtractor(parserConfig); // 从 URL 加载流媒体清单(支持 HLS/DASH/MSS) await extractor.LoadSourceFromUrlAsync("https://example.com/master.m3u8"); // 提取所有可用流(视频、音频、字幕) List streams = await extractor.ExtractStreamsAsync(); // 获取播放列表详细信息(包含所有分片 URL) await extractor.FetchPlayListAsync(streams); // 遍历提取的流 foreach (var stream in streams) { Console.WriteLine($"类型: {stream.MediaType}"); Console.WriteLine($"编解码器: {stream.Codecs}"); Console.WriteLine($"分辨率: {stream.Resolution}"); Console.WriteLine($"带宽: {stream.Bandwidth} bps"); Console.WriteLine($"语言: {stream.Language}"); Console.WriteLine($"分片数量: {stream.Playlist?.MediaParts.Sum(p => p.MediaSegments.Count)}"); Console.WriteLine($"总时长: {stream.Playlist?.TotalDuration} 秒"); Console.WriteLine("---"); } // 对于直播流,可以刷新播放列表获取新分片 if (streams.First().Playlist?.IsLive == true) { await Task.Delay(5000); // 等待 5 秒 await extractor.RefreshPlayListAsync(streams); Console.WriteLine("播放列表已刷新,获取到新分片"); } // 提取器类型检测 switch (extractor.ExtractorType) { case ExtractorType.HLS: Console.WriteLine("检测到 HLS (m3u8) 流"); break; case ExtractorType.MPEG_DASH: Console.WriteLine("检测到 DASH (MPD) 流"); break; case ExtractorType.MSS: Console.WriteLine("检测到 MSS (Smooth Streaming) 流"); break; } ``` --- ### 流过滤器 (FilterUtil) 使用正则表达式和条件筛选流 ```csharp using N_m3u8DL_RE.Util; using N_m3u8DL_RE.Entity; using N_m3u8DL_RE.Common.Entity; using N_m3u8DL_RE.Common.Enum; using System.Text.RegularExpressions; // 假设已经提取了流列表 List allStreams = await extractor.ExtractStreamsAsync(); // 创建视频流过滤器 - 选择 1080p HEVC 编码的视频 var videoFilter = new StreamFilter { ResolutionReg = new Regex("1920x1080"), CodecsReg = new Regex("hvc1|hevc", RegexOptions.IgnoreCase), For = "best" // 选择最佳的一个 }; var selectedVideos = FilterUtil.DoFilterKeep(allStreams, videoFilter); Console.WriteLine($"找到 {selectedVideos.Count} 个 1080p HEVC 视频流"); // 创建音频流过滤器 - 选择所有英语音轨 var audioFilter = new StreamFilter { LanguageReg = new Regex("^en", RegexOptions.IgnoreCase), For = "all" // 选择所有匹配的 }; var englishAudios = FilterUtil.DoFilterKeep( allStreams.Where(s => s.MediaType == MediaType.AUDIO), audioFilter ); Console.WriteLine($"找到 {englishAudios.Count} 个英语音轨"); // 带宽过滤 - 选择带宽在 3-8 Mbps 之间的流 var bandwidthFilter = new StreamFilter { BandwidthMin = 3_000_000, // 3 Mbps BandwidthMax = 8_000_000, // 8 Mbps For = "best" }; var mediumQuality = FilterUtil.DoFilterKeep(allStreams, bandwidthFilter); // 排除特定流 - 移除所有 AVC1 编码的视频 var dropFilter = new StreamFilter { CodecsReg = new Regex("avc1", RegexOptions.IgnoreCase) }; var nonAVC = FilterUtil.DoFilterDrop(allStreams, dropFilter); Console.WriteLine($"排除 AVC1 后剩余 {nonAVC.Count} 个流"); // 复杂过滤 - 选择最佳的 3 个日语或英语 5.1 声道音轨 var complexFilter = new StreamFilter { LanguageReg = new Regex("ja|en", RegexOptions.IgnoreCase), ChannelsReg = new Regex("6|5.1"), For = "best3" // 选择最佳的 3 个 }; var multiChannelAudios = FilterUtil.DoFilterKeep( allStreams.Where(s => s.MediaType == MediaType.AUDIO), complexFilter ); // 时长过滤 - 选择时长超过 1 小时的流 var durationFilter = new StreamFilter { PlaylistMinDur = TimeSpan.FromHours(1), For = "all" }; var longStreams = FilterUtil.DoFilterKeep(allStreams, durationFilter); // 自定义范围下载 - 仅下载前 100 个分片 var customRange = new CustomRange { StartSegIndex = 0, EndSegIndex = 99 }; FilterUtil.ApplyCustomRange(selectedVideos, customRange); // 或者使用时间范围 - 下载第 10 分钟到第 30 分钟 var timeRange = new CustomRange { StartSec = 600, // 10 分钟 EndSec = 1800 // 30 分钟 }; FilterUtil.ApplyCustomRange(selectedVideos, timeRange); ``` --- ### 分片下载器 (SimpleDownloader) 实现单个分片的下载和解密 ```csharp using N_m3u8DL_RE.Downloader; using N_m3u8DL_RE.Config; using N_m3u8DL_RE.Common.Entity; using N_m3u8DL_RE.Common.Enum; using N_m3u8DL_RE.Entity; // 配置下载器 var downloaderConfig = new DownloaderConfig { MyOptions = new MyOption { DownloadRetryCount = 3, HttpRequestTimeout = 100 }, Headers = new Dictionary { { "User-Agent", "N_m3u8DL-RE/0.5" } } }; // 创建下载器实例 var downloader = new SimpleDownloader(downloaderConfig); // 创建速度容器用于限速 var speedContainer = new SpeedContainer(); // 定义要下载的分片 var segment = new MediaSegment { Index = 0, Url = "https://example.com/segment000.ts", Duration = 10.0, // 如果分片已加密,设置加密信息 EncryptInfo = new EncryptInfo { Method = EncryptMethod.AES_128, Key = Convert.FromHexString("0123456789abcdef0123456789abcdef"), IV = Convert.FromHexString("00000000000000000000000000000000") } }; // 下载分片(自动处理解密) string savePath = "/tmp/segments/segment000.ts.tmp"; var result = await downloader.DownloadSegmentAsync( segment, savePath, speedContainer, headers: null ); // 检查下载结果 if (result?.Success == true) { Console.WriteLine($"分片下载成功"); Console.WriteLine($"保存路径: {result.ActualFilePath}"); Console.WriteLine($"文件大小: {result.ActualContentLength} 字节"); Console.WriteLine($"预期大小: {result.RespContentLength} 字节"); } else { Console.WriteLine("分片下载失败"); } // 下载带 Range 请求的分片 var rangedSegment = new MediaSegment { Index = 1, Url = "https://example.com/video.mp4", StartRange = 0, StopRange = 1024 * 1024 - 1, // 下载前 1MB ExpectLength = 1024 * 1024 }; var rangedResult = await downloader.DownloadSegmentAsync( rangedSegment, "/tmp/segments/segment001.ts.tmp", speedContainer ); // 批量下载多个分片 var segments = new List(); for (int i = 0; i < 10; i++) { segments.Add(new MediaSegment { Index = i, Url = $"https://example.com/segment{i:D3}.ts", Duration = 10.0 }); } // 使用并行下载 var downloadTasks = segments.Select(seg => downloader.DownloadSegmentAsync( seg, $"/tmp/segments/segment{seg.Index:D3}.ts.tmp", speedContainer ) ); var results = await Task.WhenAll(downloadTasks); int successCount = results.Count(r => r?.Success == true); Console.WriteLine($"成功下载 {successCount}/{segments.Count} 个分片"); ``` --- ### AES 解密工具 (AESUtil) 处理 AES-128 加密分片的解密 ```csharp using N_m3u8DL_RE.Crypto; using System.Security.Cryptography; // 方式 1: 直接解密文件(原地替换) string encryptedFile = "/tmp/encrypted_segment.ts"; byte[] key = Convert.FromHexString("0123456789abcdef0123456789abcdef"); byte[] iv = Convert.FromHexString("00000000000000000000000000000000"); // 默认使用 CBC 模式和 PKCS7 填充 AESUtil.AES128Decrypt(encryptedFile, key, iv); Console.WriteLine("文件已解密并原地替换"); // 方式 2: 使用 ECB 模式解密 AESUtil.AES128Decrypt( encryptedFile, key, iv, CipherMode.ECB, PaddingMode.PKCS7 ); // 方式 3: 解密字节数组(不修改文件) byte[] encryptedData = File.ReadAllBytes(encryptedFile); byte[] decryptedData = AESUtil.AES128Decrypt( encryptedData, key, iv, CipherMode.CBC, PaddingMode.PKCS7 ); File.WriteAllBytes("/tmp/decrypted_segment.ts", decryptedData); // 实际使用场景: 从 HLS #EXT-X-KEY 标签中提取密钥和 IV // #EXT-X-KEY:METHOD=AES-128,URI="https://example.com/key.bin",IV=0x00000000000000000000000000000001 string keyUrl = "https://example.com/key.bin"; byte[] keyBytes = await HTTPUtil.GetBytesAsync(keyUrl); byte[] ivBytes = Convert.FromHexString("00000000000000000000000000000001"); // 批量解密多个分片 var encryptedFiles = Directory.GetFiles("/tmp/encrypted/", "*.ts"); foreach (var file in encryptedFiles) { try { AESUtil.AES128Decrypt(file, keyBytes, ivBytes); Console.WriteLine($"已解密: {Path.GetFileName(file)}"); } catch (Exception ex) { Console.WriteLine($"解密失败 {Path.GetFileName(file)}: {ex.Message}"); } } // 处理多个密钥的情况(KID:KEY 格式) var keyMappings = new Dictionary { { "key1", Convert.FromHexString("0123456789abcdef0123456789abcdef") }, { "key2", Convert.FromHexString("fedcba9876543210fedcba9876543210") } }; // 根据 KID 选择对应的密钥进行解密 string kid = "key1"; if (keyMappings.TryGetValue(kid, out var decryptKey)) { AESUtil.AES128Decrypt(encryptedFile, decryptKey, iv); } ``` --- ### ChaCha20 解密工具 (ChaCha20Util) 处理 ChaCha20 加密算法 ```csharp using N_m3u8DL_RE.Crypto; // ChaCha20 解密(按 1024 字节块) string encryptedFile = "/tmp/chacha20_encrypted.ts"; byte[] key = new byte[32]; // ChaCha20 使用 256 位密钥 byte[] nonce = new byte[16]; // 16 字节随机数 // 从十六进制字符串生成密钥和 nonce key = Convert.FromHexString("0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"); nonce = Convert.FromHexString("00000000000000000000000000000000"); // 读取加密数据 byte[] encryptedData = File.ReadAllBytes(encryptedFile); // 解密(按 1024 字节块处理) byte[] decryptedData = ChaCha20Util.DecryptPer1024Bytes( encryptedData, key, nonce ); // 保存解密后的数据 File.WriteAllBytes("/tmp/chacha20_decrypted.ts", decryptedData); Console.WriteLine($"ChaCha20 解密完成,原始大小: {encryptedData.Length} 字节"); // 实际使用示例: 配合下载器自动解密 var segment = new MediaSegment { Index = 0, Url = "https://example.com/chacha_segment.ts", EncryptInfo = new EncryptInfo { Method = EncryptMethod.CHACHA20, Key = key, IV = nonce } }; // SimpleDownloader 会自动调用 ChaCha20Util 进行解密 var result = await downloader.DownloadSegmentAsync( segment, "/tmp/segment.ts.tmp", speedContainer ); // 手动批量解密多个 ChaCha20 加密文件 var chacha20Files = Directory.GetFiles("/tmp/chacha_encrypted/", "*.ts"); foreach (var file in chacha20Files) { byte[] encrypted = File.ReadAllBytes(file); byte[] decrypted = ChaCha20Util.DecryptPer1024Bytes(encrypted, key, nonce); string outputFile = file.Replace("chacha_encrypted", "chacha_decrypted"); File.WriteAllBytes(outputFile, decrypted); Console.WriteLine($"已解密: {Path.GetFileName(file)}"); } ``` --- ### 分片合并工具 (MergeUtil) 将多个分片合并成完整视频 ```csharp using N_m3u8DL_RE.Util; // 方式 1: 二进制直接合并(适用于 MPEG-TS 格式) string[] tsFiles = new string[] { "/tmp/segment000.ts", "/tmp/segment001.ts", "/tmp/segment002.ts", "/tmp/segment003.ts" }; string outputFile = "/tmp/output.ts"; MergeUtil.CombineMultipleFilesIntoSingleFile(tsFiles, outputFile); Console.WriteLine($"二进制合并完成: {outputFile}"); // 方式 2: 使用 FFmpeg 合并并转换格式 string ffmpegPath = "/usr/bin/ffmpeg"; // 或 Windows 上的 "C:\\ffmpeg\\bin\\ffmpeg.exe" bool success = MergeUtil.MergeByFFmpeg( binary: ffmpegPath, files: tsFiles, outputPath: "/tmp/output.mp4", muxFormat: "mp4", useAACFilter: false, fastStart: true, // 启用 faststart 优化 Web 播放 writeDate: true, useConcatDemuxer: false, poster: null ); if (success) { Console.WriteLine("FFmpeg 合并为 MP4 成功"); } // 方式 3: 大量文件合并(超过 90000 个文件时自动分批) var manyFiles = Directory.GetFiles("/tmp/segments/", "*.ts") .OrderBy(f => f) .ToArray(); if (manyFiles.Length > 90000) { // 先部分合并成较大的块 string[] partialFiles = MergeUtil.PartialCombineMultipleFiles(manyFiles); Console.WriteLine($"分批合并为 {partialFiles.Length} 个中间文件"); // 再合并中间文件 MergeUtil.CombineMultipleFilesIntoSingleFile(partialFiles, outputFile); } else { MergeUtil.CombineMultipleFilesIntoSingleFile(manyFiles, outputFile); } // 方式 4: 使用 mkvmerge 合并为 MKV 格式 string mkvmergePath = "/usr/bin/mkvmerge"; bool mkvSuccess = MergeUtil.MergeByMkvmerge( binary: mkvmergePath, files: tsFiles, outputPath: "/tmp/output.mkv", mkvCodec: "V_MPEG2", // 视频编解码器 audioCodec: "A_AAC", // 音频编解码器 muxImports: null, writeDate: true, languages: new List() ); if (mkvSuccess) { Console.WriteLine("mkvmerge 合并为 MKV 成功"); } // 实际场景: 合并音视频分离的流 string[] videoFiles = Directory.GetFiles("/tmp/video/", "*.m4s").OrderBy(f => f).ToArray(); string[] audioFiles = Directory.GetFiles("/tmp/audio/", "*.m4s").OrderBy(f => f).ToArray(); // 先分别合并音视频 MergeUtil.CombineMultipleFilesIntoSingleFile(videoFiles, "/tmp/video.m4v"); MergeUtil.CombineMultipleFilesIntoSingleFile(audioFiles, "/tmp/audio.m4a"); // 再使用 FFmpeg 混流 MergeUtil.MergeByFFmpeg( binary: ffmpegPath, files: new[] { "/tmp/video.m4v", "/tmp/audio.m4a" }, outputPath: "/tmp/final.mp4", muxFormat: "mp4", useAACFilter: false, fastStart: true, writeDate: true, useConcatDemuxer: false, poster: null ); ``` --- ### 点播下载管理器 (SimpleDownloadManager) 管理完整的点播视频下载流程 ```csharp using N_m3u8DL_RE.DownloadManager; using N_m3u8DL_RE.Config; using N_m3u8DL_RE.Common.Entity; // 创建下载配置 var downloaderConfig = new DownloaderConfig { MyOptions = new MyOption { ThreadCount = 8, DownloadRetryCount = 3, TmpDir = "/tmp/n_m3u8dl", SaveDir = "/output", SaveName = "myvideo", BinaryMerge = false, // 使用 FFmpeg 而非二进制合并 SkipMerge = false, DelAfterDone = true, // 完成后删除临时文件 CheckSegmentsCount = true, MuxAfterDone = new MuxOptions { UseMkvmerge = false, MuxToMp4 = true, MuxToMkv = false, KeepFiles = false, SkipSubtitle = false } }, Headers = new Dictionary { { "User-Agent", "Mozilla/5.0" } } }; // 假设已经提取并过滤了流 List selectedStreams = new List { videoStream, // 视频流 audioStream, // 音频流 subtitleStream // 字幕流 }; // 创建下载管理器 var downloadManager = new SimpleDownloadManager(downloaderConfig, selectedStreams); // 开始下载 bool success = await downloadManager.StartDownloadAsync(); if (success) { Console.WriteLine("下载完成!"); // 输出文件位置: /output/myvideo.mp4 } else { Console.WriteLine("下载失败或被取消"); } // 下载流程说明: // 1. 创建临时目录和子目录 // 2. 并行下载所有流的分片 // 3. 自动解密加密分片 // 4. 合并每个流的分片 // 5. 混流音视频和字幕 // 6. 删除临时文件 // 7. 移动最终文件到输出目录 // 高级选项示例 var advancedConfig = new DownloaderConfig { MyOptions = new MyOption { ThreadCount = 16, DownloadRetryCount = 5, MaxSpeed = "10M", // 限速 10Mbps UseSystemProxy = true, CustomProxy = "http://127.0.0.1:7890", SavePattern = "__", // 自定义命名模式 BinaryMerge = true, // 对于 TS 流使用二进制合并更快 Mp4RealTimeDecryption = true, // MP4 实时解密 DecryptionBinaryPath = "/usr/bin/mp4decrypt", FfmpegBinaryPath = "/usr/bin/ffmpeg", ConcurrentDownload = true, // 并发下载音视频字幕 SubtitleFormat = SubtitleFormat.SRT, AutoSubtitleFix = true, LogLevel = "INFO" } }; ``` --- ### 直播录制管理器 (SimpleLiveRecordManager2) 录制 HLS/DASH 直播流 ```csharp using N_m3u8DL_RE.DownloadManager; using N_m3u8DL_RE.Config; using N_m3u8DL_RE.Common.Entity; // 配置直播录制 var liveConfig = new DownloaderConfig { MyOptions = new MyOption { ThreadCount = 4, // 直播推荐较少线程 LivePerformAsVod = false, // 直播模式 LiveRealTimeMerge = true, // 实时合并分片 LiveKeepSegments = false, // 不保留分片文件 LiveRecordLimit = TimeSpan.FromHours(2), // 录制 2 小时 LiveWaitTime = 5, // 刷新间隔 5 秒 LiveTakeCount = 16, // 首次获取 16 个分片 TmpDir = "/tmp/live_recording", SaveDir = "/output/live", SaveName = "livestream" } }; // 假设已经提取了直播流 List liveStreams = new List { liveVideoStream, liveAudioStream }; // 确认流为直播类型 if (liveStreams.First().Playlist?.IsLive == true) { Console.WriteLine("检测到直播流"); // 创建直播录制管理器 var liveManager = new SimpleLiveRecordManager2(liveConfig, liveStreams, extractor); // 开始录制 bool success = await liveManager.StartRecordAsync(); if (success) { Console.WriteLine("录制完成!"); } } // 使用管道混流模式(实验性功能,需要稳定网络) var pipeMuxConfig = new DownloaderConfig { MyOptions = new MyOption { LiveRealTimeMerge = true, LivePipeMux = true, // 启用管道混流 LiveKeepSegments = false, FfmpegBinaryPath = "/usr/bin/ffmpeg" } }; // 可以通过环境变量自定义 FFmpeg 选项 Environment.SetEnvironmentVariable("RE_LIVE_PIPE_OPTIONS", "-c:v copy -c:a aac -b:a 128k -movflags faststart"); // 录制时长限制示例 var timeLimitConfig = new DownloaderConfig { MyOptions = new MyOption { LiveRecordLimit = TimeSpan.Parse("01:30:00"), // 录制 1 小时 30 分钟 LiveRealTimeMerge = true, SaveDir = "/output", SaveName = "live_20250106" } }; // VTT 字幕时间修正(通过音频时间戳) var subtitleFixConfig = new DownloaderConfig { MyOptions = new MyOption { LiveFixVttByAudio = true, // 修正 VTT 字幕时间 SubtitleFormat = SubtitleFormat.SRT, AutoSubtitleFix = true } }; ``` --- ### HTTP 工具 (HTTPUtil) HTTP 请求和内容获取 ```csharp using N_m3u8DL_RE.Common.Util; // 获取网页内容 string url = "https://example.com/playlist.m3u8"; var headers = new Dictionary { { "User-Agent", "Mozilla/5.0" }, { "Referer", "https://example.com" }, { "Cookie", "session=abc123" } }; string content = await HTTPUtil.GetWebSourceAsync(url, headers); Console.WriteLine($"获取到内容,长度: {content.Length} 字符"); // 获取二进制数据(如密钥文件) string keyUrl = "https://example.com/key.bin"; byte[] keyData = await HTTPUtil.GetBytesAsync(keyUrl, headers); Console.WriteLine($"密钥长度: {keyData.Length} 字节"); // 获取内容并跟踪重定向后的 URL var (content2, finalUrl) = await HTTPUtil.GetWebSourceAndNewUrlAsync( "https://example.com/redirect", headers ); Console.WriteLine($"最终 URL: {finalUrl}"); // POST 请求 string postUrl = "https://api.example.com/auth"; byte[] postData = System.Text.Encoding.UTF8.GetBytes( "{\"username\":\"user\",\"password\":\"pass\"}" ); string response = await HTTPUtil.GetPostResponseAsync(postUrl, postData); // 支持 file:// 协议 string localFile = "file:///home/user/local_playlist.m3u8"; string localContent = await HTTPUtil.GetWebSourceAsync(localFile, null); // 自动处理压缩(gzip/deflate/brotli) string compressedUrl = "https://example.com/compressed.m3u8"; string decompressed = await HTTPUtil.GetWebSourceAsync(compressedUrl, headers); // HTTPUtil 配置(静态属性) HTTPUtil.AppHttpClient.Timeout = TimeSpan.FromSeconds(120); // 设置超时 // 下载大文件到本地 async Task DownloadFileAsync(string url, string savePath) { byte[] data = await HTTPUtil.GetBytesAsync(url, headers); await File.WriteAllBytesAsync(savePath, data); Console.WriteLine($"文件已保存: {savePath}"); } await DownloadFileAsync("https://example.com/video.mp4", "/tmp/video.mp4"); // 批量请求示例 var urls = new List { "https://example.com/segment1.ts", "https://example.com/segment2.ts", "https://example.com/segment3.ts" }; var downloadTasks = urls.Select(u => HTTPUtil.GetBytesAsync(u, headers)); byte[][] results = await Task.WhenAll(downloadTasks); Console.WriteLine($"下载了 {results.Length} 个分片"); ``` --- ### 日志记录 (Logger) 多级别日志输出 ```csharp using N_m3u8DL_RE.Common.Log; using N_m3u8DL_RE.Common.Enum; // 设置日志级别 Logger.LogLevel = LogLevel.INFO; // DEBUG, INFO, WARN, ERROR, OFF // 启用文件日志 Logger.IsWriteFile = true; Logger.LogFilePath = "/var/log/n_m3u8dl.log"; Logger.InitLogFile(); // 基本日志输出 Logger.Info("开始下载视频"); Logger.Warn("网络连接不稳定"); Logger.Error("下载失败,文件不存在"); Logger.Debug("HTTP 响应状态码: 200"); // 带标记的日志(支持 Spectre.Console 标记语法) Logger.InfoMarkUp("[green]下载成功![/green]"); Logger.WarnMarkUp("[yellow]警告:[/yellow] 磁盘空间不足"); Logger.ErrorMarkUp("[red bold]错误:[/red bold] 无法连接到服务器"); // 额外详细日志 Logger.Extra("调试信息: 当前分片索引 = 42"); // 实际使用示例 void LogDownloadProgress(int current, int total) { double percentage = (current / (double)total) * 100; Logger.InfoMarkUp($"下载进度: [cyan]{current}/{total}[/cyan] ([green]{percentage:F2}%[/green])"); } LogDownloadProgress(50, 100); // 异常日志 try { // 某些操作 throw new Exception("模拟错误"); } catch (Exception ex) { Logger.Error($"发生异常: {ex.Message}"); Logger.Debug($"堆栈跟踪:\n{ex.StackTrace}"); } // 条件日志 if (Logger.LogLevel == LogLevel.DEBUG) { Logger.Debug("详细调试信息"); } // 日志级别控制 Logger.LogLevel = LogLevel.ERROR; // 只输出错误 Logger.Info("这条消息不会显示"); Logger.Error("这条消息会显示"); // 禁用日志 Logger.LogLevel = LogLevel.OFF; ``` --- ### 完整下载示例 端到端完整流程 ```csharp using N_m3u8DL_RE.Parser; using N_m3u8DL_RE.Parser.Config; using N_m3u8DL_RE.DownloadManager; using N_m3u8DL_RE.Config; using N_m3u8DL_RE.Util; using N_m3u8DL_RE.Common.Log; using N_m3u8DL_RE.Common.Enum; public async Task DownloadStreamAsync(string url) { // 1. 配置日志 Logger.LogLevel = LogLevel.INFO; Logger.Info($"开始处理: {url}"); // 2. 配置解析器 var parserConfig = new ParserConfig { Headers = new Dictionary { { "User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36" }, { "Referer", "https://example.com" } } }; // 3. 提取流 var extractor = new StreamExtractor(parserConfig); await extractor.LoadSourceFromUrlAsync(url); var streams = await extractor.ExtractStreamsAsync(); await extractor.FetchPlayListAsync(streams); Logger.Info($"找到 {streams.Count} 个流"); // 4. 过滤选择流 var videoFilter = new StreamFilter { ResolutionReg = new Regex("1920x1080"), For = "best" }; var selectedVideos = FilterUtil.DoFilterKeep( streams.Where(s => s.MediaType == MediaType.VIDEO), videoFilter ); var audioFilter = new StreamFilter { LanguageReg = new Regex("en"), For = "best" }; var selectedAudios = FilterUtil.DoFilterKeep( streams.Where(s => s.MediaType == MediaType.AUDIO), audioFilter ); var selectedStreams = new List(); selectedStreams.AddRange(selectedVideos); selectedStreams.AddRange(selectedAudios); Logger.Info($"选择了 {selectedStreams.Count} 个流进行下载"); // 5. 配置下载管理器 var downloaderConfig = new DownloaderConfig { MyOptions = new MyOption { ThreadCount = 16, DownloadRetryCount = 3, TmpDir = "/tmp/download", SaveDir = "/output", SaveName = "video_1080p", BinaryMerge = false, DelAfterDone = true, MuxAfterDone = new MuxOptions { MuxToMp4 = true, KeepFiles = false } }, Headers = parserConfig.Headers }; // 6. 执行下载 var downloadManager = new SimpleDownloadManager(downloaderConfig, selectedStreams); bool success = await downloadManager.StartDownloadAsync(); if (success) { Logger.InfoMarkUp("[green]下载完成![/green]"); Logger.Info($"输出文件: /output/video_1080p.mp4"); } else { Logger.ErrorMarkUp("[red]下载失败[/red]"); } } // 调用示例 await DownloadStreamAsync("https://example.com/master.m3u8"); ``` --- ## 主要使用场景和集成模式 N_m3u8DL-RE 作为功能强大的流媒体下载工具,适用于多种场景。**作为命令行工具**,它可以直接在终端中使用,通过丰富的命令行参数控制下载行为,适合手动下载、脚本自动化和批处理任务。用户可以通过 Bash/PowerShell 脚本批量下载多个视频,或使用 cron/Task Scheduler 定时录制直播流。**作为 C# 库**,开发者可以将其核心模块集成到自己的应用程序中,如视频下载管理器、在线教育平台的课程下载功能、流媒体代理服务器等。项目的模块化设计使得各个组件可以独立使用,例如只使用 Parser 模块解析流媒体清单而不实际下载,或使用 Crypto 模块单独处理加密内容。 在实际部署中,该工具支持 **Docker 容器化部署**,可以构建为微服务提供 API 接口;支持 **CI/CD 集成**,在自动化测试中验证流媒体内容;支持 **跨平台运行**,在 Linux 服务器、Windows 桌面和 macOS 开发环境中无缝工作。对于直播录制场景,工具提供了灵活的时间控制和实时合并功能,可以持续录制 24/7 直播流或按时间段自动切片。加密内容处理方面,支持 AES-128、ChaCha20 等多种加密算法,可以自动从清单中提取密钥或使用用户提供的密钥文件。工具还提供了完善的错误处理和重试机制,支持断点续传和网络中断后的自动恢复,确保在不稳定网络环境下也能可靠完成下载任务。通过日志系统和进度显示,用户可以实时监控下载状态并进行问题诊断。