diff --git a/src/N_m3u8DL-RE.Common/Entity/EncryptInfo.cs b/src/N_m3u8DL-RE.Common/Entity/EncryptInfo.cs
index dedfee8..3ce359b 100644
--- a/src/N_m3u8DL-RE.Common/Entity/EncryptInfo.cs
+++ b/src/N_m3u8DL-RE.Common/Entity/EncryptInfo.cs
@@ -5,39 +5,38 @@ using System.Linq;
 using System.Text;
 using System.Threading.Tasks;
 
-namespace N_m3u8DL_RE.Common.Entity
+namespace N_m3u8DL_RE.Common.Entity;
+
+public class EncryptInfo
 {
-    public class EncryptInfo
+    /// <summary>
+    /// 加密方式,默认无加密
+    /// </summary>
+    public EncryptMethod Method { get; set; } = EncryptMethod.NONE;
+
+    public byte[]? Key { get; set; }
+    public byte[]? IV { get; set; }
+
+    public EncryptInfo() { }
+
+    /// <summary>
+    /// 创建EncryptInfo并尝试自动解析Method
+    /// </summary>
+    /// <param name="method"></param>
+    public EncryptInfo(string method)
     {
-        /// <summary>
-        /// 加密方式,默认无加密
-        /// </summary>
-        public EncryptMethod Method { get; set; } = EncryptMethod.NONE;
+        Method = ParseMethod(method);
+    }
 
-        public byte[]? Key { get; set; }
-        public byte[]? IV { get; set; }
-
-        public EncryptInfo() { }
-
-        /// <summary>
-        /// 创建EncryptInfo并尝试自动解析Method
-        /// </summary>
-        /// <param name="method"></param>
-        public EncryptInfo(string method)
+    public static EncryptMethod ParseMethod(string? method)
+    {
+        if (method != null && System.Enum.TryParse(method.Replace("-", "_"), out EncryptMethod m))
         {
-            Method = ParseMethod(method);
+            return m;
         }
-
-        public static EncryptMethod ParseMethod(string? method)
+        else
         {
-            if (method != null && System.Enum.TryParse(method.Replace("-", "_"), out EncryptMethod m))
-            {
-                return m;
-            }
-            else
-            {
-                return EncryptMethod.UNKNOWN;
-            }
+            return EncryptMethod.UNKNOWN;
         }
     }
-}
+}
\ No newline at end of file
diff --git a/src/N_m3u8DL-RE.Common/Entity/MSSData.cs b/src/N_m3u8DL-RE.Common/Entity/MSSData.cs
index 96a0ae2..370293f 100644
--- a/src/N_m3u8DL-RE.Common/Entity/MSSData.cs
+++ b/src/N_m3u8DL-RE.Common/Entity/MSSData.cs
@@ -1,25 +1,18 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
+namespace N_m3u8DL_RE.Common.Entity;
 
-namespace N_m3u8DL_RE.Common.Entity
+public class MSSData
 {
-    public class MSSData
-    {
-        public string FourCC { get; set; } = "";
-        public string CodecPrivateData { get; set; } = "";
-        public string Type { get; set; } = "";
-        public int Timesacle { get; set; }
-        public int SamplingRate { get; set; }
-        public int Channels { get; set; }
-        public int BitsPerSample { get; set; }
-        public int NalUnitLengthField { get; set; }
-        public long Duration { get; set; }
+    public string FourCC { get; set; } = "";
+    public string CodecPrivateData { get; set; } = "";
+    public string Type { get; set; } = "";
+    public int Timesacle { get; set; }
+    public int SamplingRate { get; set; }
+    public int Channels { get; set; }
+    public int BitsPerSample { get; set; }
+    public int NalUnitLengthField { get; set; }
+    public long Duration { get; set; }
 
-        public bool IsProtection { get; set; } = false;
-        public string ProtectionSystemID { get; set; } = "";
-        public string ProtectionData { get; set; } = "";
-    }
-}
+    public bool IsProtection { get; set; } = false;
+    public string ProtectionSystemID { get; set; } = "";
+    public string ProtectionData { get; set; } = "";
+}
\ No newline at end of file
diff --git a/src/N_m3u8DL-RE.Common/Entity/MediaPart.cs b/src/N_m3u8DL-RE.Common/Entity/MediaPart.cs
index b86fa64..c417524 100644
--- a/src/N_m3u8DL-RE.Common/Entity/MediaPart.cs
+++ b/src/N_m3u8DL-RE.Common/Entity/MediaPart.cs
@@ -1,14 +1,7 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
+namespace N_m3u8DL_RE.Common.Entity;
 
-namespace N_m3u8DL_RE.Common.Entity
+// 主要处理 EXT-X-DISCONTINUITY
+public class MediaPart
 {
-    //主要处理 EXT-X-DISCONTINUITY
-    public class MediaPart
-    {
-        public List<MediaSegment> MediaSegments { get; set; } = new List<MediaSegment>();
-    }
-}
+    public List<MediaSegment> MediaSegments { get; set; } = new List<MediaSegment>();
+}
\ No newline at end of file
diff --git a/src/N_m3u8DL-RE.Common/Entity/MediaSegment.cs b/src/N_m3u8DL-RE.Common/Entity/MediaSegment.cs
index 918b016..4055ee1 100644
--- a/src/N_m3u8DL-RE.Common/Entity/MediaSegment.cs
+++ b/src/N_m3u8DL-RE.Common/Entity/MediaSegment.cs
@@ -1,43 +1,36 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
+namespace N_m3u8DL_RE.Common.Entity;
 
-namespace N_m3u8DL_RE.Common.Entity
+public class MediaSegment
 {
-    public class MediaSegment
+    public long Index { get; set; }
+    public double Duration { get; set; }
+    public string? Title { get; set; }
+    public DateTime? DateTime { get; set; }
+
+    public long? StartRange { get; set; }
+    public long? StopRange => (StartRange != null && ExpectLength != null) ? StartRange + ExpectLength - 1 : null;
+    public long? ExpectLength { get; set; }
+
+    public EncryptInfo EncryptInfo { get; set; } = new EncryptInfo();
+
+    public string Url { get; set; }
+
+    public string? NameFromVar { get; set; } // MPD分段文件名
+
+    public override bool Equals(object? obj)
     {
-        public long Index { get; set; }
-        public double Duration { get; set; }
-        public string? Title { get; set; }
-        public DateTime? DateTime { get; set; }
-
-        public long? StartRange { get; set; }
-        public long? StopRange { get => (StartRange != null && ExpectLength != null) ? StartRange + ExpectLength - 1 : null; }
-        public long? ExpectLength { get; set; }
-
-        public EncryptInfo EncryptInfo { get; set; } = new EncryptInfo();
-
-        public string Url { get; set; }
-
-        public string? NameFromVar { get; set; } //MPD分段文件名
-
-        public override bool Equals(object? obj)
-        {
-            return obj is MediaSegment segment &&
-                   Index == segment.Index &&
-                   Duration == segment.Duration &&
-                   Title == segment.Title &&
-                   StartRange == segment.StartRange &&
-                   StopRange == segment.StopRange &&
-                   ExpectLength == segment.ExpectLength &&
-                   Url == segment.Url;
-        }
-
-        public override int GetHashCode()
-        {
-            return HashCode.Combine(Index, Duration, Title, StartRange, StopRange, ExpectLength, Url);
-        }
+        return obj is MediaSegment segment &&
+               Index == segment.Index &&
+               Math.Abs(Duration - segment.Duration) < 0.001 &&
+               Title == segment.Title &&
+               StartRange == segment.StartRange &&
+               StopRange == segment.StopRange &&
+               ExpectLength == segment.ExpectLength &&
+               Url == segment.Url;
     }
-}
+
+    public override int GetHashCode()
+    {
+        return HashCode.Combine(Index, Duration, Title, StartRange, StopRange, ExpectLength, Url);
+    }
+}
\ No newline at end of file
diff --git a/src/N_m3u8DL-RE.Common/Entity/Playlist.cs b/src/N_m3u8DL-RE.Common/Entity/Playlist.cs
index 65a8bb6..6cc194a 100644
--- a/src/N_m3u8DL-RE.Common/Entity/Playlist.cs
+++ b/src/N_m3u8DL-RE.Common/Entity/Playlist.cs
@@ -1,27 +1,19 @@
-using N_m3u8DL_RE.Common.Enum;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
+namespace N_m3u8DL_RE.Common.Entity;
 
-namespace N_m3u8DL_RE.Common.Entity
+public class Playlist
 {
-    public class Playlist
-    {
-        //对应Url信息
-        public string Url { get; set; }
-        //是否直播
-        public bool IsLive { get; set; } = false;
-        //直播刷新间隔毫秒(默认15秒)
-        public double RefreshIntervalMs { get; set; } = 15000;
-        //所有分片时长总和
-        public double TotalDuration { get => MediaParts.Sum(x => x.MediaSegments.Sum(m => m.Duration)); }
-        //所有分片中最长时长
-        public double? TargetDuration { get; set; }
-        //INIT信息
-        public MediaSegment? MediaInit { get; set; }
-        //分片信息
-        public List<MediaPart> MediaParts { get; set; } = new List<MediaPart>();
-    }
-}
+    // 对应Url信息
+    public string Url { get; set; }
+    // 是否直播
+    public bool IsLive { get; set; } = false;
+    // 直播刷新间隔毫秒(默认15秒)
+    public double RefreshIntervalMs { get; set; } = 15000;
+    // 所有分片时长总和
+    public double TotalDuration { get => MediaParts.Sum(x => x.MediaSegments.Sum(m => m.Duration)); }
+    // 所有分片中最长时长
+    public double? TargetDuration { get; set; }
+    // INIT信息
+    public MediaSegment? MediaInit { get; set; }
+    // 分片信息
+    public List<MediaPart> MediaParts { get; set; } = new List<MediaPart>();
+}
\ No newline at end of file
diff --git a/src/N_m3u8DL-RE.Common/Entity/StreamSpec.cs b/src/N_m3u8DL-RE.Common/Entity/StreamSpec.cs
index 20b29a0..4cec950 100644
--- a/src/N_m3u8DL-RE.Common/Entity/StreamSpec.cs
+++ b/src/N_m3u8DL-RE.Common/Entity/StreamSpec.cs
@@ -1,188 +1,182 @@
 using N_m3u8DL_RE.Common.Enum;
 using N_m3u8DL_RE.Common.Util;
 using Spectre.Console;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
 
-namespace N_m3u8DL_RE.Common.Entity
+namespace N_m3u8DL_RE.Common.Entity;
+
+public class StreamSpec
 {
-    public class StreamSpec
+    public MediaType? MediaType { get; set; }
+    public string? GroupId { get; set; }
+    public string? Language { get; set; }
+    public string? Name { get; set; }
+    public Choise? Default { get; set; }
+
+    // 由于用户选择 被跳过的分片总时长
+    public double? SkippedDuration { get; set; }
+
+    // MSS信息
+    public MSSData? MSSData { get; set; }
+
+    // 基本信息
+    public int? Bandwidth { get; set; }
+    public string? Codecs { get; set; }
+    public string? Resolution { get; set; }
+    public double? FrameRate { get; set; }
+    public string? Channels { get; set; }
+    public string? Extension { get; set; }
+
+    // Dash
+    public RoleType? Role { get; set; }
+
+    // 补充信息-色域
+    public string? VideoRange { get; set; }
+    // 补充信息-特征
+    public string? Characteristics { get; set; }
+    // 发布时间(仅MPD需要)
+    public DateTime? PublishTime { get; set; }
+
+    // 外部轨道GroupId (后续寻找对应轨道信息)
+    public string? AudioId { get; set; }
+    public string? VideoId { get; set; }
+    public string? SubtitleId { get; set; }
+
+    public string? PeriodId { get; set; }
+
+    /// <summary>
+    /// URL
+    /// </summary>
+    public string Url { get; set; }
+
+    /// <summary>
+    /// 原始URL
+    /// </summary>
+    public string OriginalUrl { get; set; }
+
+    public Playlist? Playlist { get; set; }
+
+    public int SegmentsCount
     {
-        public MediaType? MediaType { get; set; }
-        public string? GroupId { get; set; }
-        public string? Language { get; set; }
-        public string? Name { get; set; }
-        public Choise? Default { get; set; }
-
-        //由于用户选择 被跳过的分片总时长
-        public double? SkippedDuration { get; set; }
-
-        //MSS信息
-        public MSSData? MSSData { get; set; }
-
-        //基本信息
-        public int? Bandwidth { get; set; }
-        public string? Codecs { get; set; }
-        public string? Resolution { get; set; }
-        public double? FrameRate { get; set; }
-        public string? Channels { get; set; }
-        public string? Extension { get; set; }
-
-        //Dash
-        public RoleType? Role { get; set; }
-
-        //补充信息-色域
-        public string? VideoRange { get; set; }
-        //补充信息-特征
-        public string? Characteristics { get; set; }
-        //发布时间(仅MPD需要)
-        public DateTime? PublishTime { get; set; }
-
-        //外部轨道GroupId (后续寻找对应轨道信息)
-        public string? AudioId { get; set; }
-        public string? VideoId { get; set; }
-        public string? SubtitleId { get; set; }
-
-        public string? PeriodId { get; set; }
-
-        /// <summary>
-        /// URL
-        /// </summary>
-        public string Url { get; set; }
-
-        /// <summary>
-        /// 原始URL
-        /// </summary>
-        public string OriginalUrl { get; set; }
-
-        public Playlist? Playlist { get; set; }
-
-        public int SegmentsCount
+        get
         {
-            get
-            {
-                return Playlist != null ? Playlist.MediaParts.Sum(x => x.MediaSegments.Count) : 0;
-            }
-        }
-
-        public string ToShortString()
-        {
-            var prefixStr = "";
-            var returnStr = "";
-            var encStr = string.Empty;
-
-            if (MediaType == Enum.MediaType.AUDIO)
-            {
-                prefixStr = $"[deepskyblue3]Aud[/] {encStr}";
-                var d = $"{GroupId} | {(Bandwidth != null ? (Bandwidth / 1000) + " Kbps" : "")} | {Name} | {Codecs} | {Language} | {(Channels != null ? Channels + "CH" : "")} | {Role}";
-                returnStr = d.EscapeMarkup();
-            }
-            else if (MediaType == Enum.MediaType.SUBTITLES)
-            {
-                prefixStr = $"[deepskyblue3_1]Sub[/] {encStr}";
-                var d = $"{GroupId} | {Language} | {Name} | {Codecs} | {Role}";
-                returnStr = d.EscapeMarkup();
-            }
-            else
-            {
-                prefixStr = $"[aqua]Vid[/] {encStr}";
-                var d = $"{Resolution} | {Bandwidth / 1000} Kbps | {GroupId} | {FrameRate} | {Codecs} | {VideoRange} | {Role}";
-                returnStr = d.EscapeMarkup();
-            }
-
-            returnStr = prefixStr + returnStr.Trim().Trim('|').Trim();
-            while (returnStr.Contains("|  |"))
-            {
-                returnStr = returnStr.Replace("|  |", "|");
-            }
-
-            return returnStr.TrimEnd().TrimEnd('|').TrimEnd();
-        }
-
-        public string ToShortShortString()
-        {
-            var prefixStr = "";
-            var returnStr = "";
-            var encStr = string.Empty;
-
-            if (MediaType == Enum.MediaType.AUDIO)
-            {
-                prefixStr = $"[deepskyblue3]Aud[/] {encStr}";
-                var d = $"{(Bandwidth != null ? (Bandwidth / 1000) + " Kbps" : "")} | {Name} | {Language} | {(Channels != null ? Channels + "CH" : "")} | {Role}";
-                returnStr = d.EscapeMarkup();
-            }
-            else if (MediaType == Enum.MediaType.SUBTITLES)
-            {
-                prefixStr = $"[deepskyblue3_1]Sub[/] {encStr}";
-                var d = $"{Language} | {Name} | {Codecs} | {Role}";
-                returnStr = d.EscapeMarkup();
-            }
-            else
-            {
-                prefixStr = $"[aqua]Vid[/] {encStr}";
-                var d = $"{Resolution} | {Bandwidth / 1000} Kbps | {FrameRate} | {VideoRange} | {Role}";
-                returnStr = d.EscapeMarkup();
-            }
-
-            returnStr = prefixStr + returnStr.Trim().Trim('|').Trim();
-            while (returnStr.Contains("|  |"))
-            {
-                returnStr = returnStr.Replace("|  |", "|");
-            }
-
-            return returnStr.TrimEnd().TrimEnd('|').TrimEnd();
-        }
-
-        public override string ToString()
-        {
-            var prefixStr = "";
-            var returnStr = "";
-            var encStr = string.Empty;
-            var segmentsCountStr = SegmentsCount == 0 ? "" : (SegmentsCount > 1 ? $"{SegmentsCount} Segments" : $"{SegmentsCount} Segment");
-
-            //增加加密标志
-            if (Playlist != null && Playlist.MediaParts.Any(m => m.MediaSegments.Any(s => s.EncryptInfo.Method != EncryptMethod.NONE)))
-            {
-                var ms = Playlist.MediaParts.SelectMany(m => m.MediaSegments.Select(s => s.EncryptInfo.Method)).Where(e => e != EncryptMethod.NONE).Distinct();
-                encStr = $"[red]*{string.Join(",", ms).EscapeMarkup()}[/] ";
-            }
-
-            if (MediaType == Enum.MediaType.AUDIO)
-            {
-                prefixStr = $"[deepskyblue3]Aud[/] {encStr}";
-                var d = $"{GroupId} | {(Bandwidth != null ? (Bandwidth / 1000) + " Kbps" : "")} | {Name} | {Codecs} | {Language} | {(Channels != null ? Channels + "CH" : "")} | {segmentsCountStr} | {Role}";
-                returnStr = d.EscapeMarkup();
-            }
-            else if (MediaType == Enum.MediaType.SUBTITLES)
-            {
-                prefixStr = $"[deepskyblue3_1]Sub[/] {encStr}";
-                var d = $"{GroupId} | {Language} | {Name} | {Codecs} | {Characteristics} | {segmentsCountStr} | {Role}";
-                returnStr = d.EscapeMarkup();
-            }
-            else
-            {
-                prefixStr = $"[aqua]Vid[/] {encStr}";
-                var d = $"{Resolution} | {Bandwidth / 1000} Kbps | {GroupId} | {FrameRate} | {Codecs} | {VideoRange} | {segmentsCountStr} | {Role}";
-                returnStr = d.EscapeMarkup();
-            }
-
-            returnStr = prefixStr + returnStr.Trim().Trim('|').Trim();
-            while (returnStr.Contains("|  |"))
-            {
-                returnStr = returnStr.Replace("|  |", "|");
-            }
-
-            //计算时长
-            if (Playlist != null)
-            {
-                var total = Playlist.TotalDuration;
-                returnStr += " | ~" + GlobalUtil.FormatTime((int)total);
-            }
-
-            return returnStr.TrimEnd().TrimEnd('|').TrimEnd();
+            return Playlist != null ? Playlist.MediaParts.Sum(x => x.MediaSegments.Count) : 0;
         }
     }
-}
+
+    public string ToShortString()
+    {
+        var prefixStr = "";
+        var returnStr = "";
+        var encStr = string.Empty;
+
+        if (MediaType == Enum.MediaType.AUDIO)
+        {
+            prefixStr = $"[deepskyblue3]Aud[/] {encStr}";
+            var d = $"{GroupId} | {(Bandwidth != null ? (Bandwidth / 1000) + " Kbps" : "")} | {Name} | {Codecs} | {Language} | {(Channels != null ? Channels + "CH" : "")} | {Role}";
+            returnStr = d.EscapeMarkup();
+        }
+        else if (MediaType == Enum.MediaType.SUBTITLES)
+        {
+            prefixStr = $"[deepskyblue3_1]Sub[/] {encStr}";
+            var d = $"{GroupId} | {Language} | {Name} | {Codecs} | {Role}";
+            returnStr = d.EscapeMarkup();
+        }
+        else
+        {
+            prefixStr = $"[aqua]Vid[/] {encStr}";
+            var d = $"{Resolution} | {Bandwidth / 1000} Kbps | {GroupId} | {FrameRate} | {Codecs} | {VideoRange} | {Role}";
+            returnStr = d.EscapeMarkup();
+        }
+
+        returnStr = prefixStr + returnStr.Trim().Trim('|').Trim();
+        while (returnStr.Contains("|  |"))
+        {
+            returnStr = returnStr.Replace("|  |", "|");
+        }
+
+        return returnStr.TrimEnd().TrimEnd('|').TrimEnd();
+    }
+
+    public string ToShortShortString()
+    {
+        var prefixStr = "";
+        var returnStr = "";
+        var encStr = string.Empty;
+
+        if (MediaType == Enum.MediaType.AUDIO)
+        {
+            prefixStr = $"[deepskyblue3]Aud[/] {encStr}";
+            var d = $"{(Bandwidth != null ? (Bandwidth / 1000) + " Kbps" : "")} | {Name} | {Language} | {(Channels != null ? Channels + "CH" : "")} | {Role}";
+            returnStr = d.EscapeMarkup();
+        }
+        else if (MediaType == Enum.MediaType.SUBTITLES)
+        {
+            prefixStr = $"[deepskyblue3_1]Sub[/] {encStr}";
+            var d = $"{Language} | {Name} | {Codecs} | {Role}";
+            returnStr = d.EscapeMarkup();
+        }
+        else
+        {
+            prefixStr = $"[aqua]Vid[/] {encStr}";
+            var d = $"{Resolution} | {Bandwidth / 1000} Kbps | {FrameRate} | {VideoRange} | {Role}";
+            returnStr = d.EscapeMarkup();
+        }
+
+        returnStr = prefixStr + returnStr.Trim().Trim('|').Trim();
+        while (returnStr.Contains("|  |"))
+        {
+            returnStr = returnStr.Replace("|  |", "|");
+        }
+
+        return returnStr.TrimEnd().TrimEnd('|').TrimEnd();
+    }
+
+    public override string ToString()
+    {
+        var prefixStr = "";
+        var returnStr = "";
+        var encStr = string.Empty;
+        var segmentsCountStr = SegmentsCount == 0 ? "" : (SegmentsCount > 1 ? $"{SegmentsCount} Segments" : $"{SegmentsCount} Segment");
+
+        // 增加加密标志
+        if (Playlist != null && Playlist.MediaParts.Any(m => m.MediaSegments.Any(s => s.EncryptInfo.Method != EncryptMethod.NONE)))
+        {
+            var ms = Playlist.MediaParts.SelectMany(m => m.MediaSegments.Select(s => s.EncryptInfo.Method)).Where(e => e != EncryptMethod.NONE).Distinct();
+            encStr = $"[red]*{string.Join(",", ms).EscapeMarkup()}[/] ";
+        }
+
+        if (MediaType == Enum.MediaType.AUDIO)
+        {
+            prefixStr = $"[deepskyblue3]Aud[/] {encStr}";
+            var d = $"{GroupId} | {(Bandwidth != null ? (Bandwidth / 1000) + " Kbps" : "")} | {Name} | {Codecs} | {Language} | {(Channels != null ? Channels + "CH" : "")} | {segmentsCountStr} | {Role}";
+            returnStr = d.EscapeMarkup();
+        }
+        else if (MediaType == Enum.MediaType.SUBTITLES)
+        {
+            prefixStr = $"[deepskyblue3_1]Sub[/] {encStr}";
+            var d = $"{GroupId} | {Language} | {Name} | {Codecs} | {Characteristics} | {segmentsCountStr} | {Role}";
+            returnStr = d.EscapeMarkup();
+        }
+        else
+        {
+            prefixStr = $"[aqua]Vid[/] {encStr}";
+            var d = $"{Resolution} | {Bandwidth / 1000} Kbps | {GroupId} | {FrameRate} | {Codecs} | {VideoRange} | {segmentsCountStr} | {Role}";
+            returnStr = d.EscapeMarkup();
+        }
+
+        returnStr = prefixStr + returnStr.Trim().Trim('|').Trim();
+        while (returnStr.Contains("|  |"))
+        {
+            returnStr = returnStr.Replace("|  |", "|");
+        }
+
+        // 计算时长
+        if (Playlist != null)
+        {
+            var total = Playlist.TotalDuration;
+            returnStr += " | ~" + GlobalUtil.FormatTime((int)total);
+        }
+
+        return returnStr.TrimEnd().TrimEnd('|').TrimEnd();
+    }
+}
\ No newline at end of file
diff --git a/src/N_m3u8DL-RE.Common/Entity/SubCue.cs b/src/N_m3u8DL-RE.Common/Entity/SubCue.cs
index 2e22911..5e0549a 100644
--- a/src/N_m3u8DL-RE.Common/Entity/SubCue.cs
+++ b/src/N_m3u8DL-RE.Common/Entity/SubCue.cs
@@ -1,30 +1,23 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
+namespace N_m3u8DL_RE.Common.Entity;
 
-namespace N_m3u8DL_RE.Common.Entity
+public class SubCue
 {
-    public class SubCue
+    public TimeSpan StartTime { get; set; }
+    public TimeSpan EndTime { get; set; }
+    public required string Payload { get; set; }
+    public required string Settings { get; set; }
+
+    public override bool Equals(object? obj)
     {
-        public TimeSpan StartTime { get; set; }
-        public TimeSpan EndTime { get; set; }
-        public required string Payload { get; set; }
-        public required string Settings { get; set; }
-
-        public override bool Equals(object? obj)
-        {
-            return obj is SubCue cue &&
-                   StartTime.Equals(cue.StartTime) &&
-                   EndTime.Equals(cue.EndTime) &&
-                   Payload == cue.Payload &&
-                   Settings == cue.Settings;
-        }
-
-        public override int GetHashCode()
-        {
-            return HashCode.Combine(StartTime, EndTime, Payload, Settings);
-        }
+        return obj is SubCue cue &&
+               StartTime.Equals(cue.StartTime) &&
+               EndTime.Equals(cue.EndTime) &&
+               Payload == cue.Payload &&
+               Settings == cue.Settings;
     }
-}
+
+    public override int GetHashCode()
+    {
+        return HashCode.Combine(StartTime, EndTime, Payload, Settings);
+    }
+}
\ No newline at end of file
diff --git a/src/N_m3u8DL-RE.Common/Entity/WebVttSub.cs b/src/N_m3u8DL-RE.Common/Entity/WebVttSub.cs
index 2f1171e..c3b255f 100644
--- a/src/N_m3u8DL-RE.Common/Entity/WebVttSub.cs
+++ b/src/N_m3u8DL-RE.Common/Entity/WebVttSub.cs
@@ -1,276 +1,271 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
+using System.Text;
 using System.Text.RegularExpressions;
-using System.Threading.Tasks;
 
-namespace N_m3u8DL_RE.Common.Entity
+namespace N_m3u8DL_RE.Common.Entity;
+
+public partial class WebVttSub
 {
-    public partial class WebVttSub
+    [GeneratedRegex("X-TIMESTAMP-MAP.*")]
+    private static partial Regex TSMapRegex();
+    [GeneratedRegex("MPEGTS:(\\d+)")]
+    private static partial Regex TSValueRegex();
+    [GeneratedRegex("\\s")]
+    private static partial Regex SplitRegex();
+    [GeneratedRegex("<c\\..*?>([\\s\\S]*?)<\\/c>")]
+    private static partial Regex VttClassRegex();
+
+    public List<SubCue> Cues { get; set; } = new List<SubCue>();
+    public long MpegtsTimestamp { get; set; } = 0L;
+
+    /// <summary>
+    /// 从字节数组解析WEBVTT
+    /// </summary>
+    /// <param name="textBytes"></param>
+    /// <returns></returns>
+    public static WebVttSub Parse(byte[] textBytes, long BaseTimestamp = 0L)
     {
-        [GeneratedRegex("X-TIMESTAMP-MAP.*")]
-        private static partial Regex TSMapRegex();
-        [GeneratedRegex("MPEGTS:(\\d+)")]
-        private static partial Regex TSValueRegex();
-        [GeneratedRegex("\\s")]
-        private static partial Regex SplitRegex();
-        [GeneratedRegex("<c\\..*?>([\\s\\S]*?)<\\/c>")]
-        private static partial Regex VttClassRegex();
+        return Parse(Encoding.UTF8.GetString(textBytes), BaseTimestamp);
+    }
 
-        public List<SubCue> Cues { get; set; } = new List<SubCue>();
-        public long MpegtsTimestamp { get; set; } = 0L;
+    /// <summary>
+    /// 从字节数组解析WEBVTT
+    /// </summary>
+    /// <param name="textBytes"></param>
+    /// <param name="encoding"></param>
+    /// <returns></returns>
+    public static WebVttSub Parse(byte[] textBytes, Encoding encoding, long BaseTimestamp = 0L)
+    {
+        return Parse(encoding.GetString(textBytes), BaseTimestamp);
+    }
 
-        /// <summary>
-        /// 从字节数组解析WEBVTT
-        /// </summary>
-        /// <param name="textBytes"></param>
-        /// <returns></returns>
-        public static WebVttSub Parse(byte[] textBytes, long BaseTimestamp = 0L)
+    /// <summary>
+    /// 从字符串解析WEBVTT
+    /// </summary>
+    /// <param name="text"></param>
+    /// <returns></returns>
+    public static WebVttSub Parse(string text, long BaseTimestamp = 0L)
+    {
+        if (!text.Trim().StartsWith("WEBVTT"))
+            throw new Exception("Bad vtt!");
+
+        text += Environment.NewLine;
+
+        var webSub = new WebVttSub();
+        var needPayload = false;
+        var timeLine = "";
+        var regex1 = TSMapRegex();
+
+        if (regex1.IsMatch(text))
         {
-            return Parse(Encoding.UTF8.GetString(textBytes), BaseTimestamp);
+            var timestamp = TSValueRegex().Match(regex1.Match(text).Value).Groups[1].Value;
+            webSub.MpegtsTimestamp = Convert.ToInt64(timestamp);
         }
 
-        /// <summary>
-        /// 从字节数组解析WEBVTT
-        /// </summary>
-        /// <param name="textBytes"></param>
-        /// <param name="encoding"></param>
-        /// <returns></returns>
-        public static WebVttSub Parse(byte[] textBytes, Encoding encoding, long BaseTimestamp = 0L)
+        var payloads = new List<string>();
+        foreach (var line in text.Split('\n'))
         {
-            return Parse(encoding.GetString(textBytes), BaseTimestamp);
-        }
-
-        /// <summary>
-        /// 从字符串解析WEBVTT
-        /// </summary>
-        /// <param name="text"></param>
-        /// <returns></returns>
-        public static WebVttSub Parse(string text, long BaseTimestamp = 0L)
-        {
-            if (!text.Trim().StartsWith("WEBVTT"))
-                throw new Exception("Bad vtt!");
-
-            text += Environment.NewLine;
-
-            var webSub = new WebVttSub();
-            var needPayload = false;
-            var timeLine = "";
-            var regex1 = TSMapRegex();
-
-            if (regex1.IsMatch(text))
+            if (line.Contains(" --> "))
             {
-                var timestamp = TSValueRegex().Match(regex1.Match(text).Value).Groups[1].Value;
-                webSub.MpegtsTimestamp = Convert.ToInt64(timestamp);
+                needPayload = true;
+                timeLine = line.Trim();
+                continue;
             }
 
-            var payloads = new List<string>();
-            foreach (var line in text.Split('\n'))
+            if (needPayload)
             {
-                if (line.Contains(" --> "))
+                if (string.IsNullOrEmpty(line.Trim()))
                 {
-                    needPayload = true;
-                    timeLine = line.Trim();
-                    continue;
+                    var payload = string.Join(Environment.NewLine, payloads);
+                    if (string.IsNullOrEmpty(payload.Trim())) continue; // 没获取到payload 跳过添加
+
+                    var arr = SplitRegex().Split(timeLine.Replace("-->", "")).Where(s => !string.IsNullOrEmpty(s)).ToList();
+                    var startTime = ConvertToTS(arr[0]);
+                    var endTime = ConvertToTS(arr[1]);
+                    var style = arr.Count > 2 ? string.Join(" ", arr.Skip(2)) : "";
+                    webSub.Cues.Add(new SubCue()
+                    {
+                        StartTime = startTime,
+                        EndTime = endTime,
+                        Payload = RemoveClassTag(string.Join("", payload.Where(c => c != 8203))), // Remove Zero Width Space!
+                        Settings = style
+                    });
+                    payloads.Clear();
+                    needPayload = false;
                 }
-
-                if (needPayload)
+                else
                 {
-                    if (string.IsNullOrEmpty(line.Trim()))
-                    {
-                        var payload = string.Join(Environment.NewLine, payloads);
-                        if (string.IsNullOrEmpty(payload.Trim())) continue; //没获取到payload 跳过添加
-
-                        var arr = SplitRegex().Split(timeLine.Replace("-->", "")).Where(s => !string.IsNullOrEmpty(s)).ToList();
-                        var startTime = ConvertToTS(arr[0]);
-                        var endTime = ConvertToTS(arr[1]);
-                        var style = arr.Count > 2 ? string.Join(" ", arr.Skip(2)) : "";
-                        webSub.Cues.Add(new SubCue()
-                        {
-                            StartTime = startTime,
-                            EndTime = endTime,
-                            Payload = RemoveClassTag(string.Join("", payload.Where(c => c != 8203))), //Remove Zero Width Space!
-                            Settings = style
-                        });
-                        payloads.Clear();
-                        needPayload = false;
-                    }
-                    else
-                    {
-                        payloads.Add(line.Trim());
-                    }
+                    payloads.Add(line.Trim());
                 }
             }
-
-            if (BaseTimestamp != 0)
-            {
-                foreach (var item in webSub.Cues)
-                {
-                    if (item.StartTime.TotalMilliseconds - BaseTimestamp >= 0)
-                    {
-                        item.StartTime = TimeSpan.FromMilliseconds(item.StartTime.TotalMilliseconds - BaseTimestamp);
-                        item.EndTime = TimeSpan.FromMilliseconds(item.EndTime.TotalMilliseconds - BaseTimestamp);
-                    }
-                    else
-                    {
-                        break;
-                    }
-                }
-            }
-
-            return webSub;
         }
 
-        private static string RemoveClassTag(string text)
+        if (BaseTimestamp != 0)
         {
-            if (VttClassRegex().IsMatch(text))
-            {
-                return string.Join(Environment.NewLine, text.Split('\n').Select(line => line.TrimEnd()).Select(line =>
-                {
-                    return string.Concat(VttClassRegex().Matches(line).Select(x => x.Groups[1].Value + " "));
-                })).TrimEnd();
-            }
-            else return text;
-        }
-
-        /// <summary>
-        /// 从另一个字幕中获取所有Cue,并加载此字幕中,且自动修正偏移
-        /// </summary>
-        /// <param name="webSub"></param>
-        /// <returns></returns>
-        public WebVttSub AddCuesFromOne(WebVttSub webSub)
-        {
-            FixTimestamp(webSub, this.MpegtsTimestamp);
             foreach (var item in webSub.Cues)
             {
-                if (!this.Cues.Contains(item))
+                if (item.StartTime.TotalMilliseconds - BaseTimestamp >= 0)
                 {
-                    //如果相差只有1ms,且payload相同,则拼接
-                    var last = this.Cues.LastOrDefault();
-                    if (last != null && this.Cues.Count > 0 && (item.StartTime - last.EndTime).TotalMilliseconds <= 1 && item.Payload == last.Payload) 
-                    {
-                        last.EndTime = item.EndTime;
-                    }
-                    else
-                    {
-                        this.Cues.Add(item);
-                    }
+                    item.StartTime = TimeSpan.FromMilliseconds(item.StartTime.TotalMilliseconds - BaseTimestamp);
+                    item.EndTime = TimeSpan.FromMilliseconds(item.EndTime.TotalMilliseconds - BaseTimestamp);
                 }
-            }
-            return this;
-        }
-
-        private void FixTimestamp(WebVttSub sub, long baseTimestamp)
-        {
-            if (sub.MpegtsTimestamp == 0)
-            {
-                return;
-            }
-
-            //确实存在时间轴错误的情况,才修复
-            if ((this.Cues.Count > 0 && sub.Cues.Count > 0 && sub.Cues.First().StartTime < this.Cues.Last().EndTime && sub.Cues.First().EndTime != this.Cues.Last().EndTime) || this.Cues.Count == 0)
-            {
-                //The MPEG2 transport stream clocks (PCR, PTS, DTS) all have units of 1/90000 second
-                var seconds = (sub.MpegtsTimestamp - baseTimestamp) / 90000;
-                var offset = TimeSpan.FromSeconds(seconds);
-                //当前预添加的字幕的起始时间小于实际上已经走过的时间(如offset已经是100秒,而字幕起始却是2秒),才修复
-                if (sub.Cues.Count > 0 && sub.Cues.First().StartTime < offset)
+                else
                 {
-                    for (int i = 0; i < sub.Cues.Count; i++)
-                    {
-                        sub.Cues[i].StartTime += offset;
-                        sub.Cues[i].EndTime += offset;
-                    }
+                    break;
                 }
             }
         }
 
-        private IEnumerable<SubCue> GetCues()
+        return webSub;
+    }
+
+    private static string RemoveClassTag(string text)
+    {
+        if (VttClassRegex().IsMatch(text))
         {
-            return this.Cues.Where(c => !string.IsNullOrEmpty(c.Payload));
+            return string.Join(Environment.NewLine, text.Split('\n').Select(line => line.TrimEnd()).Select(line =>
+            {
+                return string.Concat(VttClassRegex().Matches(line).Select(x => x.Groups[1].Value + " "));
+            })).TrimEnd();
         }
+        else return text;
+    }
 
-        private static TimeSpan ConvertToTS(string str)
+    /// <summary>
+    /// 从另一个字幕中获取所有Cue,并加载此字幕中,且自动修正偏移
+    /// </summary>
+    /// <param name="webSub"></param>
+    /// <returns></returns>
+    public WebVttSub AddCuesFromOne(WebVttSub webSub)
+    {
+        FixTimestamp(webSub, this.MpegtsTimestamp);
+        foreach (var item in webSub.Cues)
         {
-            //17.0s
-            if (str.EndsWith('s'))
+            if (!this.Cues.Contains(item))
             {
-                double sec = Convert.ToDouble(str[..^1]);
-                return TimeSpan.FromSeconds(sec);
-            }
-
-            str = str.Replace(',', '.');
-            long time = 0;
-            string[] parts = str.Split('.');
-            if (parts.Length > 1)
-            {
-                time += Convert.ToInt32(parts.Last().PadRight(3, '0'));
-                str = parts.First();
-            }
-            var t = str.Split(':').Reverse().ToList();
-            for (int i = 0; i < t.Count(); i++)
-            {
-                time += (long)Math.Pow(60, i) * Convert.ToInt32(t[i]) * 1000;
-            }
-            return TimeSpan.FromMilliseconds(time);
-        }
-
-        public override string ToString()
-        {
-            StringBuilder sb = new StringBuilder();
-            foreach (var c in GetCues())  //输出时去除空串
-            {
-                sb.AppendLine(c.StartTime.ToString(@"hh\:mm\:ss\.fff") + " --> " + c.EndTime.ToString(@"hh\:mm\:ss\.fff") + " " + c.Settings);
-                sb.AppendLine(c.Payload);
-                sb.AppendLine();
-            }
-            sb.AppendLine();
-            return sb.ToString();
-        }
-
-        /// <summary>
-        /// 字幕向前平移指定时间
-        /// </summary>
-        /// <param name="time"></param>
-        public void LeftShiftTime(TimeSpan time)
-        {
-            foreach (var cue in this.Cues)
-            {
-                if (cue.StartTime.TotalSeconds - time.TotalSeconds > 0) cue.StartTime -= time;
-                else cue.StartTime = TimeSpan.FromSeconds(0);
-
-                if (cue.EndTime.TotalSeconds - time.TotalSeconds > 0) cue.EndTime -= time;
-                else cue.EndTime = TimeSpan.FromSeconds(0);
+                // 如果相差只有1ms,且payload相同,则拼接
+                var last = this.Cues.LastOrDefault();
+                if (last != null && this.Cues.Count > 0 && (item.StartTime - last.EndTime).TotalMilliseconds <= 1 && item.Payload == last.Payload) 
+                {
+                    last.EndTime = item.EndTime;
+                }
+                else
+                {
+                    this.Cues.Add(item);
+                }
             }
         }
+        return this;
+    }
 
-        public string ToVtt()
+    private void FixTimestamp(WebVttSub sub, long baseTimestamp)
+    {
+        if (sub.MpegtsTimestamp == 0)
         {
-            return "WEBVTT" + Environment.NewLine + Environment.NewLine + ToString();
+            return;
         }
 
-        public string ToSrt()
+        // 确实存在时间轴错误的情况,才修复
+        if ((this.Cues.Count > 0 && sub.Cues.Count > 0 && sub.Cues.First().StartTime < this.Cues.Last().EndTime && sub.Cues.First().EndTime != this.Cues.Last().EndTime) || this.Cues.Count == 0)
         {
-            StringBuilder sb = new StringBuilder();
-            int index = 1;
-            foreach (var c in GetCues())
+            // The MPEG2 transport stream clocks (PCR, PTS, DTS) all have units of 1/90000 second
+            var seconds = (sub.MpegtsTimestamp - baseTimestamp) / 90000;
+            var offset = TimeSpan.FromSeconds(seconds);
+            // 当前预添加的字幕的起始时间小于实际上已经走过的时间(如offset已经是100秒,而字幕起始却是2秒),才修复
+            if (sub.Cues.Count > 0 && sub.Cues.First().StartTime < offset)
             {
-                sb.AppendLine($"{index++}");
-                sb.AppendLine(c.StartTime.ToString(@"hh\:mm\:ss\,fff") + " --> " + c.EndTime.ToString(@"hh\:mm\:ss\,fff"));
-                sb.AppendLine(c.Payload);
-                sb.AppendLine();
+                for (int i = 0; i < sub.Cues.Count; i++)
+                {
+                    sub.Cues[i].StartTime += offset;
+                    sub.Cues[i].EndTime += offset;
+                }
             }
-            sb.AppendLine();
-
-            var srt = sb.ToString();
-
-            if (string.IsNullOrEmpty(srt.Trim()))
-            {
-                srt = "1\r\n00:00:00,000 --> 00:00:01,000"; //空字幕
-            }
-
-            return srt;
         }
     }
-}
+
+    private IEnumerable<SubCue> GetCues()
+    {
+        return this.Cues.Where(c => !string.IsNullOrEmpty(c.Payload));
+    }
+
+    private static TimeSpan ConvertToTS(string str)
+    {
+        // 17.0s
+        if (str.EndsWith('s'))
+        {
+            double sec = Convert.ToDouble(str[..^1]);
+            return TimeSpan.FromSeconds(sec);
+        }
+
+        str = str.Replace(',', '.');
+        long time = 0;
+        string[] parts = str.Split('.');
+        if (parts.Length > 1)
+        {
+            time += Convert.ToInt32(parts.Last().PadRight(3, '0'));
+            str = parts.First();
+        }
+        var t = str.Split(':').Reverse().ToList();
+        for (int i = 0; i < t.Count(); i++)
+        {
+            time += (long)Math.Pow(60, i) * Convert.ToInt32(t[i]) * 1000;
+        }
+        return TimeSpan.FromMilliseconds(time);
+    }
+
+    public override string ToString()
+    {
+        StringBuilder sb = new StringBuilder();
+        foreach (var c in GetCues())  // 输出时去除空串
+        {
+            sb.AppendLine(c.StartTime.ToString(@"hh\:mm\:ss\.fff") + " --> " + c.EndTime.ToString(@"hh\:mm\:ss\.fff") + " " + c.Settings);
+            sb.AppendLine(c.Payload);
+            sb.AppendLine();
+        }
+        sb.AppendLine();
+        return sb.ToString();
+    }
+
+    /// <summary>
+    /// 字幕向前平移指定时间
+    /// </summary>
+    /// <param name="time"></param>
+    public void LeftShiftTime(TimeSpan time)
+    {
+        foreach (var cue in this.Cues)
+        {
+            if (cue.StartTime.TotalSeconds - time.TotalSeconds > 0) cue.StartTime -= time;
+            else cue.StartTime = TimeSpan.FromSeconds(0);
+
+            if (cue.EndTime.TotalSeconds - time.TotalSeconds > 0) cue.EndTime -= time;
+            else cue.EndTime = TimeSpan.FromSeconds(0);
+        }
+    }
+
+    public string ToVtt()
+    {
+        return "WEBVTT" + Environment.NewLine + Environment.NewLine + ToString();
+    }
+
+    public string ToSrt()
+    {
+        StringBuilder sb = new StringBuilder();
+        int index = 1;
+        foreach (var c in GetCues())
+        {
+            sb.AppendLine($"{index++}");
+            sb.AppendLine(c.StartTime.ToString(@"hh\:mm\:ss\,fff") + " --> " + c.EndTime.ToString(@"hh\:mm\:ss\,fff"));
+            sb.AppendLine(c.Payload);
+            sb.AppendLine();
+        }
+        sb.AppendLine();
+
+        var srt = sb.ToString();
+
+        if (string.IsNullOrEmpty(srt.Trim()))
+        {
+            srt = "1\r\n00:00:00,000 --> 00:00:01,000"; // 空字幕
+        }
+
+        return srt;
+    }
+}
\ No newline at end of file
diff --git a/src/N_m3u8DL-RE.Common/Enum/Choise.cs b/src/N_m3u8DL-RE.Common/Enum/Choise.cs
index edba103..73f7072 100644
--- a/src/N_m3u8DL-RE.Common/Enum/Choise.cs
+++ b/src/N_m3u8DL-RE.Common/Enum/Choise.cs
@@ -1,14 +1,7 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
+namespace N_m3u8DL_RE.Common.Enum;
 
-namespace N_m3u8DL_RE.Common.Enum
+public enum Choise
 {
-    public enum Choise
-    {
-        YES = 1,
-        NO = 0
-    }
-}
+    YES = 1,
+    NO = 0
+}
\ No newline at end of file
diff --git a/src/N_m3u8DL-RE.Common/Enum/EncryptMethod.cs b/src/N_m3u8DL-RE.Common/Enum/EncryptMethod.cs
index 319a466..e87942b 100644
--- a/src/N_m3u8DL-RE.Common/Enum/EncryptMethod.cs
+++ b/src/N_m3u8DL-RE.Common/Enum/EncryptMethod.cs
@@ -1,20 +1,13 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
+namespace N_m3u8DL_RE.Common.Enum;
 
-namespace N_m3u8DL_RE.Common.Enum
+public enum EncryptMethod
 {
-    public enum EncryptMethod
-    {
-        NONE,
-        AES_128,
-        AES_128_ECB,
-        SAMPLE_AES,
-        SAMPLE_AES_CTR,
-        CENC,
-        CHACHA20,
-        UNKNOWN
-    }
-}
+    NONE,
+    AES_128,
+    AES_128_ECB,
+    SAMPLE_AES,
+    SAMPLE_AES_CTR,
+    CENC,
+    CHACHA20,
+    UNKNOWN
+}
\ No newline at end of file
diff --git a/src/N_m3u8DL-RE.Common/Enum/ExtractorType.cs b/src/N_m3u8DL-RE.Common/Enum/ExtractorType.cs
index 27e2616..6df2aec 100644
--- a/src/N_m3u8DL-RE.Common/Enum/ExtractorType.cs
+++ b/src/N_m3u8DL-RE.Common/Enum/ExtractorType.cs
@@ -1,16 +1,9 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
+namespace N_m3u8DL_RE.Common.Enum;
 
-namespace N_m3u8DL_RE.Common.Enum
+public enum ExtractorType
 {
-    public enum ExtractorType
-    {
-        MPEG_DASH,
-        HLS,
-        HTTP_LIVE,
-        MSS
-    }
-}
+    MPEG_DASH,
+    HLS,
+    HTTP_LIVE,
+    MSS
+}
\ No newline at end of file
diff --git a/src/N_m3u8DL-RE.Common/Enum/MediaType.cs b/src/N_m3u8DL-RE.Common/Enum/MediaType.cs
index 3857763..bd1f458 100644
--- a/src/N_m3u8DL-RE.Common/Enum/MediaType.cs
+++ b/src/N_m3u8DL-RE.Common/Enum/MediaType.cs
@@ -1,16 +1,9 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
+namespace N_m3u8DL_RE.Common.Enum;
 
-namespace N_m3u8DL_RE.Common.Enum
+public enum MediaType
 {
-    public enum MediaType
-    {
-        AUDIO = 0,
-        VIDEO = 1,
-        SUBTITLES = 2,
-        CLOSED_CAPTIONS = 3
-    }
-}
+    AUDIO = 0,
+    VIDEO = 1,
+    SUBTITLES = 2,
+    CLOSED_CAPTIONS = 3
+}
\ No newline at end of file
diff --git a/src/N_m3u8DL-RE.Common/Enum/RoleType.cs b/src/N_m3u8DL-RE.Common/Enum/RoleType.cs
index 787801f..4dbd1cf 100644
--- a/src/N_m3u8DL-RE.Common/Enum/RoleType.cs
+++ b/src/N_m3u8DL-RE.Common/Enum/RoleType.cs
@@ -1,15 +1,14 @@
-namespace N_m3u8DL_RE.Common.Enum
+namespace N_m3u8DL_RE.Common.Enum;
+
+public enum RoleType
 {
-    public enum RoleType
-    {
-        Subtitle = 0,
-        Main = 1,
-        Alternate = 2,
-        Supplementary = 3,
-        Commentary = 4,
-        Dub = 5,
-        Description = 6,
-        Sign = 7,
-        Metadata = 8,
-    }
-}
+    Subtitle = 0,
+    Main = 1,
+    Alternate = 2,
+    Supplementary = 3,
+    Commentary = 4,
+    Dub = 5,
+    Description = 6,
+    Sign = 7,
+    Metadata = 8,
+}
\ No newline at end of file
diff --git a/src/N_m3u8DL-RE.Common/JsonContext/JsonContext.cs b/src/N_m3u8DL-RE.Common/JsonContext/JsonContext.cs
index 617bc8d..3b7ea0c 100644
--- a/src/N_m3u8DL-RE.Common/JsonContext/JsonContext.cs
+++ b/src/N_m3u8DL-RE.Common/JsonContext/JsonContext.cs
@@ -2,21 +2,20 @@
 using N_m3u8DL_RE.Common.Enum;
 using System.Text.Json.Serialization;
 
-namespace N_m3u8DL_RE.Common
-{
-    [JsonSourceGenerationOptions(
+namespace N_m3u8DL_RE.Common;
+
+[JsonSourceGenerationOptions(
     WriteIndented = true,
     PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase,
     GenerationMode = JsonSourceGenerationMode.Metadata)]
-    [JsonSerializable(typeof(MediaType))]
-    [JsonSerializable(typeof(EncryptMethod))]
-    [JsonSerializable(typeof(ExtractorType))]
-    [JsonSerializable(typeof(Choise))]
-    [JsonSerializable(typeof(StreamSpec))]
-    [JsonSerializable(typeof(IOrderedEnumerable<StreamSpec>))]
-    [JsonSerializable(typeof(IEnumerable<MediaSegment>))]
-    [JsonSerializable(typeof(List<StreamSpec>))]
-    [JsonSerializable(typeof(List<MediaSegment>))]
-    [JsonSerializable(typeof(Dictionary<string, string>))]
-    internal partial class JsonContext : JsonSerializerContext { }
-}
+[JsonSerializable(typeof(MediaType))]
+[JsonSerializable(typeof(EncryptMethod))]
+[JsonSerializable(typeof(ExtractorType))]
+[JsonSerializable(typeof(Choise))]
+[JsonSerializable(typeof(StreamSpec))]
+[JsonSerializable(typeof(IOrderedEnumerable<StreamSpec>))]
+[JsonSerializable(typeof(IEnumerable<MediaSegment>))]
+[JsonSerializable(typeof(List<StreamSpec>))]
+[JsonSerializable(typeof(List<MediaSegment>))]
+[JsonSerializable(typeof(Dictionary<string, string>))]
+internal partial class JsonContext : JsonSerializerContext { }
\ No newline at end of file
diff --git a/src/N_m3u8DL-RE.Common/JsonConverter/BytesBase64Converter.cs b/src/N_m3u8DL-RE.Common/JsonConverter/BytesBase64Converter.cs
index f0ae02a..4097aed 100644
--- a/src/N_m3u8DL-RE.Common/JsonConverter/BytesBase64Converter.cs
+++ b/src/N_m3u8DL-RE.Common/JsonConverter/BytesBase64Converter.cs
@@ -6,12 +6,11 @@ using System.Text.Json;
 using System.Text.Json.Serialization;
 using System.Threading.Tasks;
 
-namespace N_m3u8DL_RE.Common.JsonConverter
-{
-    internal class BytesBase64Converter : JsonConverter<byte[]>
-    {
-        public override byte[] Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) => reader.GetBytesFromBase64();
+namespace N_m3u8DL_RE.Common.JsonConverter;
 
-        public override void Write(Utf8JsonWriter writer, byte[] value, JsonSerializerOptions options) => writer.WriteStringValue(Convert.ToBase64String(value));
-    }
-}
+internal class BytesBase64Converter : JsonConverter<byte[]>
+{
+    public override byte[] Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) => reader.GetBytesFromBase64();
+
+    public override void Write(Utf8JsonWriter writer, byte[] value, JsonSerializerOptions options) => writer.WriteStringValue(Convert.ToBase64String(value));
+}
\ No newline at end of file
diff --git a/src/N_m3u8DL-RE.Common/Log/CustomAnsiConsole.cs b/src/N_m3u8DL-RE.Common/Log/CustomAnsiConsole.cs
index 417bcf8..9337497 100644
--- a/src/N_m3u8DL-RE.Common/Log/CustomAnsiConsole.cs
+++ b/src/N_m3u8DL-RE.Common/Log/CustomAnsiConsole.cs
@@ -8,27 +8,27 @@ public class NonAnsiWriter : TextWriter
 {
     public override Encoding Encoding => Console.OutputEncoding;
 
-    private string lastOut = "";
+    private string? _lastOut = "";
 
     public override void Write(char value)
     {
         Console.Write(value);
     }
 
-    public override void Write(string value)
+    public override void Write(string? value)
     {
-        if (lastOut == value)
+        if (_lastOut == value)
         {
             return;
         }
-        lastOut = value;
+        _lastOut = value;
         RemoveAnsiEscapeSequences(value);
     }
 
-    private void RemoveAnsiEscapeSequences(string input)
+    private void RemoveAnsiEscapeSequences(string? input)
     {
         // Use regular expression to remove ANSI escape sequences
-        string output = Regex.Replace(input, @"\x1B\[(\d+;?)+m", "");
+        string output = Regex.Replace(input ?? "", @"\x1B\[(\d+;?)+m", "");
         output = Regex.Replace(output, @"\[\??\d+[AKlh]", "");
         output = Regex.Replace(output,"[\r\n] +","");
         if (string.IsNullOrWhiteSpace(output))
diff --git a/src/N_m3u8DL-RE.Common/Log/LogLevel.cs b/src/N_m3u8DL-RE.Common/Log/LogLevel.cs
index e971658..11c0387 100644
--- a/src/N_m3u8DL-RE.Common/Log/LogLevel.cs
+++ b/src/N_m3u8DL-RE.Common/Log/LogLevel.cs
@@ -4,14 +4,13 @@ using System.Linq;
 using System.Text;
 using System.Threading.Tasks;
 
-namespace N_m3u8DL_RE.Common.Log
+namespace N_m3u8DL_RE.Common.Log;
+
+public enum LogLevel
 {
-    public enum LogLevel
-    {
-        OFF,
-        ERROR,
-        WARN,
-        INFO,
-        DEBUG,
-    }
-}
+    OFF,
+    ERROR,
+    WARN,
+    INFO,
+    DEBUG,
+}
\ No newline at end of file
diff --git a/src/N_m3u8DL-RE.Common/Log/Logger.cs b/src/N_m3u8DL-RE.Common/Log/Logger.cs
index a5d69b1..d2f7cd8 100644
--- a/src/N_m3u8DL-RE.Common/Log/Logger.cs
+++ b/src/N_m3u8DL-RE.Common/Log/Logger.cs
@@ -1,243 +1,236 @@
 using Spectre.Console;
-using System;
-using System.Collections.Generic;
-using System.IO;
-using System.Linq;
 using System.Text;
 using System.Text.RegularExpressions;
-using System.Threading.Tasks;
-using static System.Net.Mime.MediaTypeNames;
 
-namespace N_m3u8DL_RE.Common.Log
+namespace N_m3u8DL_RE.Common.Log;
+
+public partial class Logger
 {
-    public partial class Logger
+    [GeneratedRegex("{}")]
+    private static partial Regex VarsRepRegex();
+
+    /// <summary>
+    /// 日志级别,默认为INFO
+    /// </summary>
+    public static LogLevel LogLevel { get; set; } = LogLevel.INFO;
+
+    /// <summary>
+    /// 是否写出日志文件
+    /// </summary>
+    public static bool IsWriteFile { get; set; } = true;
+
+    /// <summary>
+    /// 本次运行日志文件所在位置
+    /// </summary>
+    private static string? LogFilePath { get; set; }
+
+    // 读写锁
+    static ReaderWriterLockSlim LogWriteLock = new ReaderWriterLockSlim();
+
+    public static void InitLogFile()
     {
-        [GeneratedRegex("{}")]
-        private static partial Regex VarsRepRegex();
+        if (!IsWriteFile) return;
 
-        /// <summary>
-        /// 日志级别,默认为INFO
-        /// </summary>
-        public static LogLevel LogLevel { get; set; } = LogLevel.INFO;
-
-        /// <summary>
-        /// 是否写出日志文件
-        /// </summary>
-        public static bool IsWriteFile { get; set; } = true;
-
-        /// <summary>
-        /// 本次运行日志文件所在位置
-        /// </summary>
-        private static string? LogFilePath { get; set; }
-
-        //读写锁
-        static ReaderWriterLockSlim LogWriteLock = new ReaderWriterLockSlim();
-
-        public static void InitLogFile()
+        try
         {
-            if (!IsWriteFile) return;
-
-            try
+            var logDir = Path.GetDirectoryName(Environment.ProcessPath) + "/Logs";
+            if (!Directory.Exists(logDir))
             {
-                var logDir = Path.GetDirectoryName(Environment.ProcessPath) + "/Logs";
-                if (!Directory.Exists(logDir))
-                {
-                    Directory.CreateDirectory(logDir);
-                }
+                Directory.CreateDirectory(logDir);
+            }
 
-                var now = DateTime.Now;
-                LogFilePath = Path.Combine(logDir, now.ToString("yyyy-MM-dd_HH-mm-ss-fff") + ".log");
-                int index = 1;
-                var fileName = Path.GetFileNameWithoutExtension(LogFilePath);
-                string init = "LOG " + now.ToString("yyyy/MM/dd") + Environment.NewLine
-                              + "Save Path: " + Path.GetDirectoryName(LogFilePath) + Environment.NewLine
-                              + "Task Start: " + now.ToString("yyyy/MM/dd HH:mm:ss") + Environment.NewLine
-                              + "Task CommandLine: " + Environment.CommandLine;
-                init += $"{Environment.NewLine}{Environment.NewLine}";
-                //若文件存在则加序号
-                while (File.Exists(LogFilePath))
-                {
-                    LogFilePath = Path.Combine(Path.GetDirectoryName(LogFilePath)!, $"{fileName}-{index++}.log");
-                }
-                File.WriteAllText(LogFilePath, init, Encoding.UTF8);
-            }
-            catch (Exception ex)
+            var now = DateTime.Now;
+            LogFilePath = Path.Combine(logDir, now.ToString("yyyy-MM-dd_HH-mm-ss-fff") + ".log");
+            int index = 1;
+            var fileName = Path.GetFileNameWithoutExtension(LogFilePath);
+            string init = "LOG " + now.ToString("yyyy/MM/dd") + Environment.NewLine
+                          + "Save Path: " + Path.GetDirectoryName(LogFilePath) + Environment.NewLine
+                          + "Task Start: " + now.ToString("yyyy/MM/dd HH:mm:ss") + Environment.NewLine
+                          + "Task CommandLine: " + Environment.CommandLine;
+            init += $"{Environment.NewLine}{Environment.NewLine}";
+            // 若文件存在则加序号
+            while (File.Exists(LogFilePath))
             {
-                Error($"Init log failed! {ex.Message.RemoveMarkup()}");
+                LogFilePath = Path.Combine(Path.GetDirectoryName(LogFilePath)!, $"{fileName}-{index++}.log");
             }
+            File.WriteAllText(LogFilePath, init, Encoding.UTF8);
         }
-
-        private static string GetCurrTime()
+        catch (Exception ex)
         {
-            return DateTime.Now.ToString("HH:mm:ss.fff");
+            Error($"Init log failed! {ex.Message.RemoveMarkup()}");
         }
+    }
 
-        private static void HandleLog(string write, string subWrite = "")
+    private static string GetCurrTime()
+    {
+        return DateTime.Now.ToString("HH:mm:ss.fff");
+    }
+
+    private static void HandleLog(string write, string subWrite = "")
+    {
+        try
         {
-            try
+            if (subWrite == "")
             {
-                if (subWrite == "")
-                {
-                    CustomAnsiConsole.MarkupLine(write);
-                }
-                else
-                {
-                    CustomAnsiConsole.Markup(write);
-                    Console.WriteLine(subWrite);
-                }
-
-                if (IsWriteFile && File.Exists(LogFilePath))
-                {
-                    var plain = write.RemoveMarkup() + subWrite.RemoveMarkup();
-                    try
-                    {
-                        //进入写入
-                        LogWriteLock.EnterWriteLock();
-                        using (StreamWriter sw = File.AppendText(LogFilePath))
-                        {
-                            sw.WriteLine(plain);
-                        }
-                    }
-                    finally
-                    {
-                        //释放占用
-                        LogWriteLock.ExitWriteLock();
-                    }
-                }
+                CustomAnsiConsole.MarkupLine(write);
             }
-            catch (Exception)
+            else
             {
-                Console.WriteLine("Failed to write: " + write);
-            }
-        }
-
-        private static string ReplaceVars(string data, params object[] ps)
-        {
-            for (int i = 0; i < ps.Length; i++)
-            {
-                data = VarsRepRegex().Replace(data, $"{ps[i]}", 1);
+                CustomAnsiConsole.Markup(write);
+                Console.WriteLine(subWrite);
             }
 
-            return data;
-        }
-
-        public static void Info(string data, params object[] ps)
-        {
-            if (LogLevel >= LogLevel.INFO)
-            {
-                data = ReplaceVars(data, ps);
-                var write = GetCurrTime() + " " + "[underline #548c26]INFO[/] : ";
-                HandleLog(write, data);
-            }
-        }
-
-        public static void InfoMarkUp(string data, params object[] ps)
-        {
-            if (LogLevel >= LogLevel.INFO)
-            {
-                data = ReplaceVars(data, ps);
-                var write = GetCurrTime() + " " + "[underline #548c26]INFO[/] : " + data;
-                HandleLog(write);
-            }
-        }
-
-        public static void Debug(string data, params object[] ps)
-        {
-            if (LogLevel >= LogLevel.DEBUG)
-            {
-                data = ReplaceVars(data, ps);
-                var write = GetCurrTime() + " " + "[underline grey]DEBUG[/]: ";
-                HandleLog(write, data);
-            }
-        }
-
-        public static void DebugMarkUp(string data, params object[] ps)
-        {
-            if (LogLevel >= LogLevel.DEBUG)
-            {
-                data = ReplaceVars(data, ps);
-                var write = GetCurrTime() + " " + "[underline grey]DEBUG[/]: " + data;
-                HandleLog(write);
-            }
-        }
-
-        public static void Warn(string data, params object[] ps)
-        {
-            if (LogLevel >= LogLevel.WARN)
-            {
-                data = ReplaceVars(data, ps);
-                var write = GetCurrTime() + " " + "[underline #a89022]WARN[/] : ";
-                HandleLog(write, data);
-            }
-        }
-
-        public static void WarnMarkUp(string data, params object[] ps)
-        {
-            if (LogLevel >= LogLevel.WARN)
-            {
-                data = ReplaceVars(data, ps);
-                var write = GetCurrTime() + " " + "[underline #a89022]WARN[/] : " + data;
-                HandleLog(write);
-            }
-        }
-
-        public static void Error(string data, params object[] ps)
-        {
-            if (LogLevel >= LogLevel.ERROR)
-            {
-                data = ReplaceVars(data, ps);
-                var write = GetCurrTime() + " " + "[underline red1]ERROR[/]: ";
-                HandleLog(write, data);
-            }
-        }
-
-        public static void ErrorMarkUp(string data, params object[] ps)
-        {
-            if (LogLevel >= LogLevel.ERROR)
-            {
-                data = ReplaceVars(data, ps);
-                var write = GetCurrTime() + " " + "[underline red1]ERROR[/]: " + data;
-                HandleLog(write);
-            }
-        }
-
-        public static void ErrorMarkUp(Exception exception)
-        {
-            string data = exception.Message.EscapeMarkup();
-            if (LogLevel >= LogLevel.ERROR)
-            {
-                data = exception.ToString().EscapeMarkup();
-            }
-
-            ErrorMarkUp(data);
-        }
-
-        /// <summary>
-        /// This thing will only write to the log file.
-        /// </summary>
-        /// <param name="data"></param>
-        /// <param name="ps"></param>
-        public static void Extra(string data, params object[] ps)
-        {
             if (IsWriteFile && File.Exists(LogFilePath))
             {
-                data = ReplaceVars(data, ps);
-                var plain = GetCurrTime() + " " + "EXTRA: " + data.RemoveMarkup();
+                var plain = write.RemoveMarkup() + subWrite.RemoveMarkup();
                 try
                 {
-                    //进入写入
+                    // 进入写入
                     LogWriteLock.EnterWriteLock();
                     using (StreamWriter sw = File.AppendText(LogFilePath))
                     {
-                        sw.WriteLine(plain, Encoding.UTF8);
+                        sw.WriteLine(plain);
                     }
                 }
                 finally
                 {
-                    //释放占用
+                    // 释放占用
                     LogWriteLock.ExitWriteLock();
                 }
             }
         }
+        catch (Exception)
+        {
+            Console.WriteLine("Failed to write: " + write);
+        }
+    }
+
+    private static string ReplaceVars(string data, params object[] ps)
+    {
+        for (int i = 0; i < ps.Length; i++)
+        {
+            data = VarsRepRegex().Replace(data, $"{ps[i]}", 1);
+        }
+
+        return data;
+    }
+
+    public static void Info(string data, params object[] ps)
+    {
+        if (LogLevel >= LogLevel.INFO)
+        {
+            data = ReplaceVars(data, ps);
+            var write = GetCurrTime() + " " + "[underline #548c26]INFO[/] : ";
+            HandleLog(write, data);
+        }
+    }
+
+    public static void InfoMarkUp(string data, params object[] ps)
+    {
+        if (LogLevel >= LogLevel.INFO)
+        {
+            data = ReplaceVars(data, ps);
+            var write = GetCurrTime() + " " + "[underline #548c26]INFO[/] : " + data;
+            HandleLog(write);
+        }
+    }
+
+    public static void Debug(string data, params object[] ps)
+    {
+        if (LogLevel >= LogLevel.DEBUG)
+        {
+            data = ReplaceVars(data, ps);
+            var write = GetCurrTime() + " " + "[underline grey]DEBUG[/]: ";
+            HandleLog(write, data);
+        }
+    }
+
+    public static void DebugMarkUp(string data, params object[] ps)
+    {
+        if (LogLevel >= LogLevel.DEBUG)
+        {
+            data = ReplaceVars(data, ps);
+            var write = GetCurrTime() + " " + "[underline grey]DEBUG[/]: " + data;
+            HandleLog(write);
+        }
+    }
+
+    public static void Warn(string data, params object[] ps)
+    {
+        if (LogLevel >= LogLevel.WARN)
+        {
+            data = ReplaceVars(data, ps);
+            var write = GetCurrTime() + " " + "[underline #a89022]WARN[/] : ";
+            HandleLog(write, data);
+        }
+    }
+
+    public static void WarnMarkUp(string data, params object[] ps)
+    {
+        if (LogLevel >= LogLevel.WARN)
+        {
+            data = ReplaceVars(data, ps);
+            var write = GetCurrTime() + " " + "[underline #a89022]WARN[/] : " + data;
+            HandleLog(write);
+        }
+    }
+
+    public static void Error(string data, params object[] ps)
+    {
+        if (LogLevel >= LogLevel.ERROR)
+        {
+            data = ReplaceVars(data, ps);
+            var write = GetCurrTime() + " " + "[underline red1]ERROR[/]: ";
+            HandleLog(write, data);
+        }
+    }
+
+    public static void ErrorMarkUp(string data, params object[] ps)
+    {
+        if (LogLevel >= LogLevel.ERROR)
+        {
+            data = ReplaceVars(data, ps);
+            var write = GetCurrTime() + " " + "[underline red1]ERROR[/]: " + data;
+            HandleLog(write);
+        }
+    }
+
+    public static void ErrorMarkUp(Exception exception)
+    {
+        string data = exception.Message.EscapeMarkup();
+        if (LogLevel >= LogLevel.ERROR)
+        {
+            data = exception.ToString().EscapeMarkup();
+        }
+
+        ErrorMarkUp(data);
+    }
+
+    /// <summary>
+    /// This thing will only write to the log file.
+    /// </summary>
+    /// <param name="data"></param>
+    /// <param name="ps"></param>
+    public static void Extra(string data, params object[] ps)
+    {
+        if (IsWriteFile && File.Exists(LogFilePath))
+        {
+            data = ReplaceVars(data, ps);
+            var plain = GetCurrTime() + " " + "EXTRA: " + data.RemoveMarkup();
+            try
+            {
+                // 进入写入
+                LogWriteLock.EnterWriteLock();
+                using (StreamWriter sw = File.AppendText(LogFilePath))
+                {
+                    sw.WriteLine(plain, Encoding.UTF8);
+                }
+            }
+            finally
+            {
+                // 释放占用
+                LogWriteLock.ExitWriteLock();
+            }
+        }
     }
 }
\ No newline at end of file
diff --git a/src/N_m3u8DL-RE.Common/Resource/ResString.cs b/src/N_m3u8DL-RE.Common/Resource/ResString.cs
index a97f0d4..8c97681 100644
--- a/src/N_m3u8DL-RE.Common/Resource/ResString.cs
+++ b/src/N_m3u8DL-RE.Common/Resource/ResString.cs
@@ -1,151 +1,142 @@
-using System;
-using System.Collections.Generic;
-using System.Globalization;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
+namespace N_m3u8DL_RE.Common.Resource;
 
-namespace N_m3u8DL_RE.Common.Resource
+public class ResString
 {
-    public class ResString
+    public static readonly string ReLiveTs = "<RE_LIVE_TS>";
+    public static string singleFileRealtimeDecryptWarn => GetText("singleFileRealtimeDecryptWarn");
+    public static string singleFileSplitWarn => GetText("singleFileSplitWarn");
+    public static string customRangeWarn => GetText("customRangeWarn");
+    public static string customRangeFound => GetText("customRangeFound");
+    public static string customAdKeywordsFound => GetText("customAdKeywordsFound");
+    public static string customRangeInvalid => GetText("customRangeInvalid");
+    public static string consoleRedirected => GetText("consoleRedirected");
+    public static string autoBinaryMerge => GetText("autoBinaryMerge");
+    public static string autoBinaryMerge2 => GetText("autoBinaryMerge2");
+    public static string autoBinaryMerge3 => GetText("autoBinaryMerge3");
+    public static string autoBinaryMerge4 => GetText("autoBinaryMerge4");
+    public static string autoBinaryMerge5 => GetText("autoBinaryMerge5");
+    public static string autoBinaryMerge6 => GetText("autoBinaryMerge6");
+    public static string badM3u8 => GetText("badM3u8");
+    public static string binaryMerge => GetText("binaryMerge");
+    public static string checkingLast => GetText("checkingLast");
+    public static string cmd_appendUrlParams => GetText("cmd_appendUrlParams");
+    public static string cmd_autoSelect => GetText("cmd_autoSelect");
+    public static string cmd_binaryMerge => GetText("cmd_binaryMerge");
+    public static string cmd_useFFmpegConcatDemuxer => GetText("cmd_useFFmpegConcatDemuxer");
+    public static string cmd_checkSegmentsCount => GetText("cmd_checkSegmentsCount");
+    public static string cmd_decryptionBinaryPath => GetText("cmd_decryptionBinaryPath");
+    public static string cmd_delAfterDone => GetText("cmd_delAfterDone");
+    public static string cmd_ffmpegBinaryPath => GetText("cmd_ffmpegBinaryPath");
+    public static string cmd_mkvmergeBinaryPath => GetText("cmd_mkvmergeBinaryPath");
+    public static string cmd_baseUrl => GetText("cmd_baseUrl");
+    public static string cmd_maxSpeed => GetText("cmd_maxSpeed");
+    public static string cmd_adKeyword => GetText("cmd_adKeyword");
+    public static string cmd_moreHelp => GetText("cmd_moreHelp");
+    public static string cmd_header => GetText("cmd_header");
+    public static string cmd_muxImport => GetText("cmd_muxImport");
+    public static string cmd_muxImport_more => GetText("cmd_muxImport_more");
+    public static string cmd_selectVideo => GetText("cmd_selectVideo");
+    public static string cmd_dropVideo => GetText("cmd_dropVideo");
+    public static string cmd_selectVideo_more => GetText("cmd_selectVideo_more");
+    public static string cmd_selectAudio => GetText("cmd_selectAudio");
+    public static string cmd_dropAudio => GetText("cmd_dropAudio");
+    public static string cmd_selectAudio_more => GetText("cmd_selectAudio_more");
+    public static string cmd_selectSubtitle => GetText("cmd_selectSubtitle");
+    public static string cmd_dropSubtitle => GetText("cmd_dropSubtitle");
+    public static string cmd_selectSubtitle_more => GetText("cmd_selectSubtitle_more");
+    public static string cmd_custom_range => GetText("cmd_custom_range");
+    public static string cmd_customHLSMethod => GetText("cmd_customHLSMethod");
+    public static string cmd_customHLSKey => GetText("cmd_customHLSKey");
+    public static string cmd_customHLSIv => GetText("cmd_customHLSIv");
+    public static string cmd_Input => GetText("cmd_Input");
+    public static string cmd_forceAnsiConsole => GetText("cmd_forceAnsiConsole");
+    public static string cmd_noAnsiColor => GetText("cmd_noAnsiColor");
+    public static string cmd_keys => GetText("cmd_keys");
+    public static string cmd_keyText => GetText("cmd_keyText");
+    public static string cmd_loadKeyFailed => GetText("cmd_loadKeyFailed");
+    public static string cmd_logLevel => GetText("cmd_logLevel");
+    public static string cmd_MP4RealTimeDecryption => GetText("cmd_MP4RealTimeDecryption");
+    public static string cmd_saveDir => GetText("cmd_saveDir");
+    public static string cmd_saveName => GetText("cmd_saveName");
+    public static string cmd_savePattern => GetText("cmd_savePattern");
+    public static string cmd_skipDownload => GetText("cmd_skipDownload");
+    public static string cmd_noDateInfo => GetText("cmd_noDateInfo");
+    public static string cmd_noLog => GetText("cmd_noLog");
+    public static string cmd_skipMerge => GetText("cmd_skipMerge");
+    public static string cmd_subFormat => GetText("cmd_subFormat");
+    public static string cmd_subOnly => GetText("cmd_subOnly");
+    public static string cmd_subtitleFix => GetText("cmd_subtitleFix");
+    public static string cmd_threadCount => GetText("cmd_threadCount");
+    public static string cmd_downloadRetryCount => GetText("cmd_downloadRetryCount");
+    public static string cmd_tmpDir => GetText("cmd_tmpDir");
+    public static string cmd_uiLanguage => GetText("cmd_uiLanguage");
+    public static string cmd_urlProcessorArgs => GetText("cmd_urlProcessorArgs");
+    public static string cmd_useShakaPackager => GetText("cmd_useShakaPackager");
+    public static string cmd_concurrentDownload => GetText("cmd_concurrentDownload");
+    public static string cmd_useSystemProxy => GetText("cmd_useSystemProxy");
+    public static string cmd_customProxy => GetText("cmd_customProxy");
+    public static string cmd_customRange => GetText("cmd_customRange");
+    public static string cmd_liveKeepSegments => GetText("cmd_liveKeepSegments");
+    public static string cmd_livePipeMux => GetText("cmd_livePipeMux");
+    public static string cmd_liveRecordLimit => GetText("cmd_liveRecordLimit");
+    public static string cmd_taskStartAt => GetText("cmd_taskStartAt");
+    public static string cmd_liveWaitTime => GetText("cmd_liveWaitTime");
+    public static string cmd_liveTakeCount => GetText("cmd_liveTakeCount");
+    public static string cmd_liveFixVttByAudio => GetText("cmd_liveFixVttByAudio");
+    public static string cmd_liveRealTimeMerge => GetText("cmd_liveRealTimeMerge");
+    public static string cmd_livePerformAsVod => GetText("cmd_livePerformAsVod");
+    public static string cmd_muxAfterDone => GetText("cmd_muxAfterDone");
+    public static string cmd_muxAfterDone_more => GetText("cmd_muxAfterDone_more");
+    public static string cmd_writeMetaJson => GetText("cmd_writeMetaJson");
+    public static string liveLimit => GetText("liveLimit");
+    public static string realTimeDecMessage => GetText("realTimeDecMessage");
+    public static string liveLimitReached => GetText("liveLimitReached");
+    public static string saveName => GetText("saveName");
+    public static string taskStartAt => GetText("taskStartAt");
+    public static string namedPipeCreated => GetText("namedPipeCreated");
+    public static string namedPipeMux => GetText("namedPipeMux");
+    public static string partMerge => GetText("partMerge");
+    public static string fetch => GetText("fetch");
+    public static string ffmpegMerge => GetText("ffmpegMerge");
+    public static string ffmpegNotFound => GetText("ffmpegNotFound");
+    public static string fixingTTML => GetText("fixingTTML");
+    public static string fixingTTMLmp4 => GetText("fixingTTMLmp4");
+    public static string fixingVTT => GetText("fixingVTT");
+    public static string fixingVTTmp4 => GetText("fixingVTTmp4");
+    public static string keyProcessorNotFound => GetText("keyProcessorNotFound");
+    public static string liveFound => GetText("liveFound");
+    public static string loadingUrl => GetText("loadingUrl");
+    public static string masterM3u8Found => GetText("masterM3u8Found");
+    public static string matchDASH => GetText("matchDASH");
+    public static string matchMSS => GetText("matchMSS");
+    public static string matchTS => GetText("matchTS");
+    public static string matchHLS => GetText("matchHLS");
+    public static string notSupported => GetText("notSupported");
+    public static string parsingStream => GetText("parsingStream");
+    public static string promptChoiceText => GetText("promptChoiceText");
+    public static string promptInfo => GetText("promptInfo");
+    public static string promptTitle => GetText("promptTitle");
+    public static string readingInfo => GetText("readingInfo");
+    public static string searchKey => GetText("searchKey");
+    public static string segmentCountCheckNotPass => GetText("segmentCountCheckNotPass");
+    public static string selectedStream => GetText("selectedStream");
+    public static string startDownloading => GetText("startDownloading");
+    public static string streamsInfo => GetText("streamsInfo");
+    public static string writeJson => GetText("writeJson");
+    public static string noStreamsToDownload => GetText("noStreamsToDownload");
+    public static string newVersionFound => GetText("newVersionFound");
+    public static string processImageSub => GetText("processImageSub");
+
+    private static string GetText(string key)
     {
-        public readonly static string ReLiveTs = "<RE_LIVE_TS>";
-        public static string singleFileRealtimeDecryptWarn { get => GetText("singleFileRealtimeDecryptWarn"); }
-        public static string singleFileSplitWarn { get => GetText("singleFileSplitWarn"); }
-        public static string customRangeWarn { get => GetText("customRangeWarn"); }
-        public static string customRangeFound { get => GetText("customRangeFound"); }
-        public static string customAdKeywordsFound { get => GetText("customAdKeywordsFound"); }
-        public static string customRangeInvalid { get => GetText("customRangeInvalid"); }
-        public static string consoleRedirected { get => GetText("consoleRedirected"); }
-        public static string autoBinaryMerge { get => GetText("autoBinaryMerge"); }
-        public static string autoBinaryMerge2 { get => GetText("autoBinaryMerge2"); }
-        public static string autoBinaryMerge3 { get => GetText("autoBinaryMerge3"); }
-        public static string autoBinaryMerge4 { get => GetText("autoBinaryMerge4"); }
-        public static string autoBinaryMerge5 { get => GetText("autoBinaryMerge5"); }
-        public static string autoBinaryMerge6 { get => GetText("autoBinaryMerge6"); }
-        public static string badM3u8 { get => GetText("badM3u8"); }
-        public static string binaryMerge { get => GetText("binaryMerge"); }
-        public static string checkingLast { get => GetText("checkingLast"); }
-        public static string cmd_appendUrlParams { get => GetText("cmd_appendUrlParams"); }
-        public static string cmd_autoSelect { get => GetText("cmd_autoSelect"); }
-        public static string cmd_binaryMerge { get => GetText("cmd_binaryMerge"); }
-        public static string cmd_useFFmpegConcatDemuxer { get => GetText("cmd_useFFmpegConcatDemuxer"); }
-        public static string cmd_checkSegmentsCount { get => GetText("cmd_checkSegmentsCount"); }
-        public static string cmd_decryptionBinaryPath { get => GetText("cmd_decryptionBinaryPath"); }
-        public static string cmd_delAfterDone { get => GetText("cmd_delAfterDone"); }
-        public static string cmd_ffmpegBinaryPath { get => GetText("cmd_ffmpegBinaryPath"); }
-        public static string cmd_mkvmergeBinaryPath { get => GetText("cmd_mkvmergeBinaryPath"); }
-        public static string cmd_baseUrl { get => GetText("cmd_baseUrl"); }
-        public static string cmd_maxSpeed { get => GetText("cmd_maxSpeed"); }
-        public static string cmd_adKeyword { get => GetText("cmd_adKeyword"); }
-        public static string cmd_moreHelp { get => GetText("cmd_moreHelp"); }
-        public static string cmd_header { get => GetText("cmd_header"); }
-        public static string cmd_muxImport { get => GetText("cmd_muxImport"); }
-        public static string cmd_muxImport_more { get => GetText("cmd_muxImport_more"); }
-        public static string cmd_selectVideo { get => GetText("cmd_selectVideo"); }
-        public static string cmd_dropVideo { get => GetText("cmd_dropVideo"); }
-        public static string cmd_selectVideo_more { get => GetText("cmd_selectVideo_more"); }
-        public static string cmd_selectAudio { get => GetText("cmd_selectAudio"); }
-        public static string cmd_dropAudio { get => GetText("cmd_dropAudio"); }
-        public static string cmd_selectAudio_more { get => GetText("cmd_selectAudio_more"); }
-        public static string cmd_selectSubtitle { get => GetText("cmd_selectSubtitle"); }
-        public static string cmd_dropSubtitle { get => GetText("cmd_dropSubtitle"); }
-        public static string cmd_selectSubtitle_more { get => GetText("cmd_selectSubtitle_more"); }
-        public static string cmd_custom_range { get => GetText("cmd_custom_range"); }
-        public static string cmd_customHLSMethod { get => GetText("cmd_customHLSMethod"); }
-        public static string cmd_customHLSKey { get => GetText("cmd_customHLSKey"); }
-        public static string cmd_customHLSIv { get => GetText("cmd_customHLSIv"); }
-        public static string cmd_Input { get => GetText("cmd_Input"); }
-        public static string cmd_forceAnsiConsole { get => GetText("cmd_forceAnsiConsole"); }
-        public static string cmd_noAnsiColor { get => GetText("cmd_noAnsiColor"); }
-        public static string cmd_keys { get => GetText("cmd_keys"); }
-        public static string cmd_keyText { get => GetText("cmd_keyText"); }
-        public static string cmd_loadKeyFailed { get => GetText("cmd_loadKeyFailed"); }
-        public static string cmd_logLevel { get => GetText("cmd_logLevel"); }
-        public static string cmd_MP4RealTimeDecryption { get => GetText("cmd_MP4RealTimeDecryption"); }
-        public static string cmd_saveDir { get => GetText("cmd_saveDir"); }
-        public static string cmd_saveName { get => GetText("cmd_saveName"); }
-        public static string cmd_savePattern { get => GetText("cmd_savePattern"); }
-        public static string cmd_skipDownload { get => GetText("cmd_skipDownload"); }
-        public static string cmd_noDateInfo { get => GetText("cmd_noDateInfo"); }
-        public static string cmd_noLog { get => GetText("cmd_noLog"); }
-        public static string cmd_skipMerge { get => GetText("cmd_skipMerge"); }
-        public static string cmd_subFormat { get => GetText("cmd_subFormat"); }
-        public static string cmd_subOnly { get => GetText("cmd_subOnly"); }
-        public static string cmd_subtitleFix { get => GetText("cmd_subtitleFix"); }
-        public static string cmd_threadCount { get => GetText("cmd_threadCount"); }
-        public static string cmd_downloadRetryCount { get => GetText("cmd_downloadRetryCount"); }
-        public static string cmd_tmpDir { get => GetText("cmd_tmpDir"); }
-        public static string cmd_uiLanguage { get => GetText("cmd_uiLanguage"); }
-        public static string cmd_urlProcessorArgs { get => GetText("cmd_urlProcessorArgs"); }
-        public static string cmd_useShakaPackager { get => GetText("cmd_useShakaPackager"); }
-        public static string cmd_concurrentDownload { get => GetText("cmd_concurrentDownload"); }
-        public static string cmd_useSystemProxy { get => GetText("cmd_useSystemProxy"); }
-        public static string cmd_customProxy { get => GetText("cmd_customProxy"); }
-        public static string cmd_customRange { get => GetText("cmd_customRange"); }
-        public static string cmd_liveKeepSegments { get => GetText("cmd_liveKeepSegments"); }
-        public static string cmd_livePipeMux { get => GetText("cmd_livePipeMux"); }
-        public static string cmd_liveRecordLimit { get => GetText("cmd_liveRecordLimit"); }
-        public static string cmd_taskStartAt { get => GetText("cmd_taskStartAt"); }
-        public static string cmd_liveWaitTime { get => GetText("cmd_liveWaitTime"); }
-        public static string cmd_liveTakeCount { get => GetText("cmd_liveTakeCount"); }
-        public static string cmd_liveFixVttByAudio { get => GetText("cmd_liveFixVttByAudio"); }
-        public static string cmd_liveRealTimeMerge { get => GetText("cmd_liveRealTimeMerge"); }
-        public static string cmd_livePerformAsVod { get => GetText("cmd_livePerformAsVod"); }
-        public static string cmd_muxAfterDone { get => GetText("cmd_muxAfterDone"); }
-        public static string cmd_muxAfterDone_more { get => GetText("cmd_muxAfterDone_more"); }
-        public static string cmd_writeMetaJson { get => GetText("cmd_writeMetaJson"); }
-        public static string liveLimit { get => GetText("liveLimit"); }
-        public static string realTimeDecMessage { get => GetText("realTimeDecMessage"); }
-        public static string liveLimitReached { get => GetText("liveLimitReached"); }
-        public static string saveName { get => GetText("saveName"); }
-        public static string taskStartAt { get => GetText("taskStartAt"); }
-        public static string namedPipeCreated { get => GetText("namedPipeCreated"); }
-        public static string namedPipeMux { get => GetText("namedPipeMux"); }
-        public static string partMerge { get => GetText("partMerge"); }
-        public static string fetch { get => GetText("fetch"); }
-        public static string ffmpegMerge { get => GetText("ffmpegMerge"); }
-        public static string ffmpegNotFound { get => GetText("ffmpegNotFound"); }
-        public static string fixingTTML { get => GetText("fixingTTML"); }
-        public static string fixingTTMLmp4 { get => GetText("fixingTTMLmp4"); }
-        public static string fixingVTT { get => GetText("fixingVTT"); }
-        public static string fixingVTTmp4 { get => GetText("fixingVTTmp4"); }
-        public static string keyProcessorNotFound { get => GetText("keyProcessorNotFound"); }
-        public static string liveFound { get => GetText("liveFound"); }
-        public static string loadingUrl { get => GetText("loadingUrl"); }
-        public static string masterM3u8Found { get => GetText("masterM3u8Found"); }
-        public static string matchDASH { get => GetText("matchDASH"); }
-        public static string matchMSS { get => GetText("matchMSS"); }
-        public static string matchTS { get => GetText("matchTS"); }
-        public static string matchHLS { get => GetText("matchHLS"); }
-        public static string notSupported { get => GetText("notSupported"); }
-        public static string parsingStream { get => GetText("parsingStream"); }
-        public static string promptChoiceText { get => GetText("promptChoiceText"); }
-        public static string promptInfo { get => GetText("promptInfo"); }
-        public static string promptTitle { get => GetText("promptTitle"); }
-        public static string readingInfo { get => GetText("readingInfo"); }
-        public static string searchKey { get => GetText("searchKey"); }
-        public static string segmentCountCheckNotPass { get => GetText("segmentCountCheckNotPass"); }
-        public static string selectedStream { get => GetText("selectedStream"); }
-        public static string startDownloading { get => GetText("startDownloading"); }
-        public static string streamsInfo { get => GetText("streamsInfo"); }
-        public static string writeJson { get => GetText("writeJson"); }
-        public static string noStreamsToDownload { get => GetText("noStreamsToDownload"); }
-        public static string newVersionFound { get => GetText("newVersionFound"); }
-        public static string processImageSub { get => GetText("processImageSub"); }
+        if (!StaticText.LANG_DIC.ContainsKey(key))
+            return "<...LANG TEXT MISSING...>";
 
-        private static string GetText(string key)
-        {
-            if (!StaticText.LANG_DIC.ContainsKey(key))
-                return "<...LANG TEXT MISSING...>";
-
-            var current = Thread.CurrentThread.CurrentUICulture.Name;
-            if (current == "zh-CN" || current == "zh-SG" || current == "zh-Hans")
-                return StaticText.LANG_DIC[key].ZH_CN;
-            else if (current.StartsWith("zh-"))
-                return StaticText.LANG_DIC[key].ZH_TW;
-            else
-                return StaticText.LANG_DIC[key].EN_US;
-        }
+        var current = Thread.CurrentThread.CurrentUICulture.Name;
+        if (current == "zh-CN" || current == "zh-SG" || current == "zh-Hans")
+            return StaticText.LANG_DIC[key].ZH_CN;
+        if (current.StartsWith("zh-"))
+            return StaticText.LANG_DIC[key].ZH_TW;
+        return StaticText.LANG_DIC[key].EN_US;
     }
-}
+}
\ No newline at end of file
diff --git a/src/N_m3u8DL-RE.Common/Resource/StaticText.cs b/src/N_m3u8DL-RE.Common/Resource/StaticText.cs
index 23f84f3..1739d53 100644
--- a/src/N_m3u8DL-RE.Common/Resource/StaticText.cs
+++ b/src/N_m3u8DL-RE.Common/Resource/StaticText.cs
@@ -4,919 +4,918 @@ using System.Linq;
 using System.Text;
 using System.Threading.Tasks;
 
-namespace N_m3u8DL_RE.Common.Resource
-{
-    internal class StaticText
-    {
-        public static Dictionary<string, TextContainer> LANG_DIC = new()
-        {
-            ["singleFileSplitWarn"] = new TextContainer
-            (
-                zhCN: "整段文件已被自动切割为小分片以加速下载",
-                zhTW: "整段文件已被自動切割為小分片以加速下載",
-                enUS: "The entire file has been cut into small segments to accelerate"
-            ),
-            ["singleFileRealtimeDecryptWarn"] = new TextContainer
-            (
-                zhCN: "实时解密已被强制关闭",
-                zhTW: "即時解密已被強制關閉",
-                enUS: "Real-time decryption has been disabled"
-            ),
-            ["cmd_forceAnsiConsole"] = new TextContainer
-            (
-                zhCN: "强制认定终端为支持ANSI且可交互的终端",
-                zhTW: "強制認定終端為支援ANSI且可交往的終端",
-                enUS: "Force assuming the terminal is ANSI-compatible and interactive"
-            ),
-            ["cmd_noAnsiColor"] = new TextContainer
-            (
-                zhCN: "去除ANSI颜色",
-                zhTW: "關閉ANSI顏色",
-                enUS: "Remove ANSI colors"
-            ),
-            ["customRangeWarn"] = new TextContainer
-            (
-                zhCN: "请注意,自定义下载范围有时会导致音画不同步",
-                zhTW: "請注意,自定義下載範圍有時會導致音畫不同步",
-                enUS: "Please note that custom range may sometimes result in audio and video being out of sync"
-            ),
-            ["customRangeInvalid"] = new TextContainer
-            (
-                zhCN: "自定义下载范围无效",
-                zhTW: "自定義下載範圍無效",
-                enUS: "User customed range invalid"
-            ),
-            ["customAdKeywordsFound"] = new TextContainer
-            (
-                zhCN: "用户自定义广告分片URL关键字:",
-                zhTW: "用戶自定義廣告分片URL關鍵字:",
-                enUS: "User customed Ad keyword: "
-            ),
-            ["customRangeFound"] = new TextContainer
-            (
-                zhCN: "用户自定义下载范围:",
-                zhTW: "用戶自定義下載範圍:",
-                enUS: "User customed range: "
-            ),
-            ["consoleRedirected"] = new TextContainer
-            (
-                zhCN: "输出被重定向, 将清除ANSI颜色",
-                zhTW: "輸出被重定向, 將清除ANSI顏色",
-                enUS: "Output is redirected, ANSI colors are cleared."
-            ),
-            ["processImageSub"] = new TextContainer
-            (
-                zhCN: "正在处理图形字幕",
-                zhTW: "正在處理圖形字幕",
-                enUS: "Processing Image Sub"
-            ),
-            ["newVersionFound"] = new TextContainer
-            (
-                zhCN: "检测到新版本,请尽快升级!",
-                zhTW: "檢測到新版本,請盡快升級!",
-                enUS: "New version detected!"
-            ),
-            ["namedPipeCreated"] = new TextContainer
-            (
-                zhCN: "已创建命名管道:",
-                zhTW: "已創建命名管道:",
-                enUS: "Named pipe created: "
-            ),
-            ["namedPipeMux"] = new TextContainer
-            (
-                zhCN: "通过命名管道混流到",
-                zhTW: "通過命名管道混流到",
-                enUS: "Mux with named pipe, to"
-            ),
-            ["taskStartAt"] = new TextContainer
-            (
-                zhCN: "程序将等待,直到:",
-                zhTW: "程序將等待,直到:",
-                enUS: "The program will wait until: "
-            ),
-            ["autoBinaryMerge"] = new TextContainer
-            (
-                zhCN: "检测到fMP4,自动开启二进制合并",
-                zhTW: "檢測到fMP4,自動開啟二進位制合併",
-                enUS: "fMP4 is detected, binary merging is automatically enabled"
-            ),
-            ["autoBinaryMerge2"] = new TextContainer
-            (
-                zhCN: "检测到杜比视界内容,自动开启二进制合并",
-                zhTW: "檢測到杜比視界內容,自動開啟二進位制合併",
-                enUS: "Dolby Vision content is detected, binary merging is automatically enabled"
-            ),
-            ["autoBinaryMerge3"] = new TextContainer
-            (
-                zhCN: "检测到无法识别的加密方式,自动开启二进制合并",
-                zhTW: "檢測到無法識別的加密方式,自動開啟二進位制合併",
-                enUS: "An unrecognized encryption method is detected, binary merging is automatically enabled"
-            ),
-            ["autoBinaryMerge4"] = new TextContainer
-            (
-                zhCN: "检测到CENC加密方式,自动开启二进制合并",
-                zhTW: "檢測到CENC加密方式,自動開啟二進位制合併",
-                enUS: "When CENC encryption is detected, binary merging is automatically enabled"
-            ),
-            ["autoBinaryMerge5"] = new TextContainer
-            (
-                zhCN: "检测到杜比视界内容,混流功能已禁用",
-                zhTW: "檢測到杜比視界內容,混流功能已禁用",
-                enUS: "Dolby Vision content is detected, mux after done is automatically disabled"
-            ),
-            ["autoBinaryMerge6"] = new TextContainer
-            (
-                zhCN: "你已开启下载完成后混流,自动开启二进制合并",
-                zhTW: "你已開啟下載完成後混流,自動開啟二進制合併",
-                enUS: "MuxAfterDone is detected, binary merging is automatically enabled"
-            ),
-            ["badM3u8"] = new TextContainer
-            (
-                zhCN: "错误的m3u8",
-                zhTW: "錯誤的m3u8",
-                enUS: "Bad m3u8"
-            ),
-            ["binaryMerge"] = new TextContainer
-            (
-                zhCN: "二进制合并中...",
-                zhTW: "二進位制合併中...",
-                enUS: "Binary merging..."
-            ),
-            ["checkingLast"] = new TextContainer
-            (
-                zhCN: "验证最后一个分片有效性",
-                zhTW: "驗證最後一個分片有效性",
-                enUS: "Verifying the validity of the last segment"
-            ),
-            ["cmd_baseUrl"] = new TextContainer
-            (
-                zhCN: "设置BaseURL",
-                zhTW: "設置BaseURL",
-                enUS: "Set BaseURL"
-            ),
-            ["cmd_maxSpeed"] = new TextContainer
-            (
-                zhCN: "设置限速,单位支持 Mbps 或 Kbps,如:15M 100K",
-                zhTW: "設置限速,單位支持 Mbps 或 Kbps,如:15M 100K",
-                enUS: "Set speed limit, Mbps or Kbps, for example: 15M 100K."
-            ),
-            ["cmd_noDateInfo"] = new TextContainer
-            (
-                zhCN: "混流时不写入日期信息",
-                zhTW: "混流時不寫入日期訊息",
-                enUS: "Date information is not written during muxing"
-            ),
-            ["cmd_noLog"] = new TextContainer
-            (
-                zhCN: "关闭日志文件输出",
-                zhTW: "關閉日誌文件輸出",
-                enUS: "Disable log file output"
-            ),
-            ["cmd_appendUrlParams"] = new TextContainer
-            (
-                zhCN: "将输入Url的Params添加至分片, 对某些网站很有用, 例如 kakao.com",
-                zhTW: "將輸入Url的Params添加至分片, 對某些網站很有用, 例如 kakao.com",
-                enUS: "Add Params of input Url to segments, useful for some websites, such as kakao.com"
-            ),
-            ["cmd_autoSelect"] = new TextContainer
-            (
-                zhCN: "自动选择所有类型的最佳轨道",
-                zhTW: "自動選擇所有類型的最佳軌道",
-                enUS: "Automatically selects the best tracks of all types"
-            ),
-            ["cmd_binaryMerge"] = new TextContainer
-            (
-                zhCN: "二进制合并",
-                zhTW: "二進位制合併",
-                enUS: "Binary merge"
-            ),
-            ["cmd_useFFmpegConcatDemuxer"] = new TextContainer
-            (
-                zhCN: "使用 ffmpeg 合并时,使用 concat 分离器而非 concat 协议",
-                zhTW: "使用 ffmpeg 合併時,使用 concat 分離器而非 concat 協議",
-                enUS: "When merging with ffmpeg, use the concat demuxer instead of the concat protocol"
-            ),
-            ["cmd_checkSegmentsCount"] = new TextContainer
-            (
-                zhCN: "检测实际下载的分片数量和预期数量是否匹配",
-                zhTW: "檢測實際下載的分片數量和預期數量是否匹配",
-                enUS: "Check if the actual number of segments downloaded matches the expected number"
-            ),
-            ["cmd_downloadRetryCount"] = new TextContainer
-            (
-                zhCN: "每个分片下载异常时的重试次数",
-                zhTW: "每個分片下載異常時的重試次數",
-                enUS: "The number of retries when download segment error"
-            ),
-            ["cmd_decryptionBinaryPath"] = new TextContainer
-            (
-                zhCN: "MP4解密所用工具的全路径, 例如 C:\\Tools\\mp4decrypt.exe",
-                zhTW: "MP4解密所用工具的全路徑, 例如 C:\\Tools\\mp4decrypt.exe",
-                enUS: "Full path to the tool used for MP4 decryption, like C:\\Tools\\mp4decrypt.exe"
-            ),
-            ["cmd_delAfterDone"] = new TextContainer
-            (
-                zhCN: "完成后删除临时文件",
-                zhTW: "完成後刪除臨時文件",
-                enUS: "Delete temporary files when done"
-            ),
-            ["cmd_ffmpegBinaryPath"] = new TextContainer
-            (
-                zhCN: "ffmpeg可执行程序全路径, 例如 C:\\Tools\\ffmpeg.exe",
-                zhTW: "ffmpeg可執行程序全路徑, 例如 C:\\Tools\\ffmpeg.exe",
-                enUS: "Full path to the ffmpeg binary, like C:\\Tools\\ffmpeg.exe"
-            ),
-            ["cmd_mkvmergeBinaryPath"] = new TextContainer
-            (
-                zhCN: "mkvmerge可执行程序全路径, 例如 C:\\Tools\\mkvmerge.exe",
-                zhTW: "mkvmerge可執行程序全路徑, 例如 C:\\Tools\\mkvmerge.exe",
-                enUS: "Full path to the mkvmerge binary, like C:\\Tools\\mkvmerge.exe"
-            ),
-            ["cmd_liveFixVttByAudio"] = new TextContainer
-            (
-                zhCN: "通过读取音频文件的起始时间修正VTT字幕",
-                zhTW: "透過讀取音訊檔案的起始時間修正VTT字幕",
-                enUS: "Correct VTT sub by reading the start time of the audio file"
-            ),
-            ["cmd_header"] = new TextContainer
-            (
-                zhCN: "为HTTP请求设置特定的请求头, 例如:\r\n-H \"Cookie: mycookie\" -H \"User-Agent: iOS\"",
-                zhTW: "為HTTP請求設置特定的請求頭, 例如:\r\n-H \"Cookie: mycookie\" -H \"User-Agent: iOS\"",
-                enUS: "Pass custom header(s) to server, Example:\r\n-H \"Cookie: mycookie\" -H \"User-Agent: iOS\""
-            ),
-            ["cmd_Input"] = new TextContainer
-            (
-                zhCN: "链接或文件",
-                zhTW: "連結或文件",
-                enUS: "Input Url or File"
-            ),
-            ["cmd_keys"] = new TextContainer
-            (
-                zhCN: "设置解密密钥, 程序调用mp4decrpyt/shaka-packager进行解密. 格式:\r\n--key KID1:KEY1 --key KID2:KEY2\r\n对于KEY相同的情况可以直接输入 --key KEY",
-                zhTW: "設置解密密鑰, 程序調用mp4decrpyt/shaka-packager進行解密. 格式:\r\n--key KID1:KEY1 --key KID2:KEY2\r\n對於KEY相同的情況可以直接輸入 --key KEY",
-                enUS: "Set decryption key(s) to mp4decrypt/shaka-packager. format:\r\n--key KID1:KEY1 --key KID2:KEY2\r\nor use --key KEY if all tracks share the same key."
-            ),
-            ["cmd_keyText"] = new TextContainer
-            (
-                zhCN: "设置密钥文件,程序将从文件中按KID搜寻KEY以解密.(不建议使用特大文件)",
-                zhTW: "設置密鑰文件,程序將從文件中按KID搜尋KEY以解密.(不建議使用特大文件)",
-                enUS: "Set the kid-key file, the program will search the KEY with KID from the file.(Very large file are not recommended)"
-            ),
-            ["cmd_loadKeyFailed"] = new TextContainer
-            (
-                zhCN: "获取KEY失败,忽略读取.",
-                zhTW: "獲取KEY失敗,忽略讀取.",
-                enUS: "Failed to get KEY, ignore."
-            ),
-            ["cmd_logLevel"] = new TextContainer
-            (
-                zhCN: "设置日志级别",
-                zhTW: "設置日誌級別",
-                enUS: "Set log level"
-            ),
-            ["cmd_MP4RealTimeDecryption"] = new TextContainer
-            (
-                zhCN: "实时解密MP4分片",
-                zhTW: "即時解密MP4分片",
-                enUS: "Decrypt MP4 segments in real time"
-            ),
-            ["cmd_saveDir"] = new TextContainer
-            (
-                zhCN: "设置输出目录",
-                zhTW: "設置輸出目錄",
-                enUS: "Set output directory"
-            ),
-            ["cmd_saveName"] = new TextContainer
-            (
-                zhCN: "设置保存文件名",
-                zhTW: "設置保存檔案名",
-                enUS: "Set output filename"
-            ),
-            ["cmd_savePattern"] = new TextContainer
-            (
-                zhCN: "设置保存文件命名模板, 支持使用变量",
-                zhTW: "",
-                enUS: ""
-            ),
-            ["cmd_skipDownload"] = new TextContainer
-            (
-                zhCN: "跳过下载",
-                zhTW: "跳過下載",
-                enUS: "Skip download"
-            ),
-            ["cmd_skipMerge"] = new TextContainer
-            (
-                zhCN: "跳过合并分片",
-                zhTW: "跳過合併分片",
-                enUS: "Skip segments merge"
-            ),
-            ["cmd_subFormat"] = new TextContainer
-            (
-                zhCN: "字幕输出类型",
-                zhTW: "字幕輸出類型",
-                enUS: "Subtitle output format"
-            ),
-            ["cmd_subOnly"] = new TextContainer
-            (
-                zhCN: "只选取字幕轨道",
-                zhTW: "只選取字幕軌道",
-                enUS: "Select only subtitle tracks"
-            ),
-            ["cmd_subtitleFix"] = new TextContainer
-            (
-                zhCN: "自动修正字幕",
-                zhTW: "自動修正字幕",
-                enUS: "Automatically fix subtitles"
-            ),
-            ["cmd_threadCount"] = new TextContainer
-            (
-                zhCN: "设置下载线程数",
-                zhTW: "設置下載執行緒數",
-                enUS: "Set download thread count"
-            ),
-            ["cmd_tmpDir"] = new TextContainer
-            (
-                zhCN: "设置临时文件存储目录",
-                zhTW: "設置臨時文件儲存目錄",
-                enUS: "Set temporary file directory"
-            ),
-            ["cmd_uiLanguage"] = new TextContainer
-            (
-                zhCN: "设置UI语言",
-                zhTW: "設置UI語言",
-                enUS: "Set UI language"
-            ),
-            ["cmd_moreHelp"] = new TextContainer
-            (
-                zhCN: "查看某个选项的详细帮助信息",
-                zhTW: "查看某個選項的詳細幫助訊息",
-                enUS: "Set more help info about one option"
-            ),
-            ["cmd_urlProcessorArgs"] = new TextContainer
-            (
-                zhCN: "此字符串将直接传递给URL Processor",
-                zhTW: "此字符串將直接傳遞給URL Processor",
-                enUS: "Give these arguments to the URL Processors."
-            ),
-            ["cmd_liveRealTimeMerge"] = new TextContainer
-            (
-                zhCN: "录制直播时实时合并",
-                zhTW: "錄製直播時即時合併",
-                enUS: "Real-time merge into file when recording live"
-            ),
-            ["cmd_customProxy"] = new TextContainer
-            (
-                zhCN: "设置请求代理, 如 http://127.0.0.1:8888",
-                zhTW: "設置請求代理, 如 http://127.0.0.1:8888",
-                enUS: "Set web request proxy, like http://127.0.0.1:8888"
-            ),
-            ["cmd_customRange"] = new TextContainer
-            (
-                zhCN: "仅下载部分分片. 输入 \"--morehelp custom-range\" 以查看详细信息",
-                zhTW: "僅下載部分分片. 輸入 \"--morehelp custom-range\" 以查看詳細訊息",
-                enUS: "Download only part of the segments. Use \"--morehelp custom-range\" for more details"
-            ),
-            ["cmd_useSystemProxy"] = new TextContainer
-            (
-                zhCN: "使用系统默认代理",
-                zhTW: "使用系統默認代理",
-                enUS: "Use system default proxy"
-            ),
-            ["cmd_livePerformAsVod"] = new TextContainer
-            (
-                zhCN: "以点播方式下载直播流",
-                zhTW: "以點播方式下載直播流",
-                enUS: "Download live streams as vod"
-            ),
-            ["cmd_liveWaitTime"] = new TextContainer
-            (
-                zhCN: "手动设置直播列表刷新间隔",
-                zhTW: "手動設置直播列表刷新間隔",
-                enUS: "Manually set the live playlist refresh interval"
-            ),
-            ["cmd_adKeyword"] = new TextContainer
-            (
-                zhCN: "设置广告分片的URL关键字(正则表达式)",
-                zhTW: "設置廣告分片的URL關鍵字(正則表達式)",
-                enUS: "Set URL keywords (regular expressions) for AD segments"
-            ),
-            ["cmd_liveTakeCount"] = new TextContainer
-            (
-                zhCN: "手动设置录制直播时首次获取分片的数量",
-                zhTW: "手動設置錄製直播時首次獲取分片的數量",
-                enUS: "Manually set the number of segments downloaded for the first time when recording live"
-            ),
-            ["cmd_customHLSMethod"] = new TextContainer
-            (
-                zhCN: "指定HLS加密方式 (AES_128|AES_128_ECB|CENC|CHACHA20|NONE|SAMPLE_AES|SAMPLE_AES_CTR|UNKNOWN)",
-                zhTW: "指定HLS加密方式 (AES_128|AES_128_ECB|CENC|CHACHA20|NONE|SAMPLE_AES|SAMPLE_AES_CTR|UNKNOWN)",
-                enUS: "Set HLS encryption method (AES_128|AES_128_ECB|CENC|CHACHA20|NONE|SAMPLE_AES|SAMPLE_AES_CTR|UNKNOWN)"
-            ),
-            ["cmd_customHLSKey"] = new TextContainer
-            (
-                zhCN: "指定HLS解密KEY. 可以是文件, HEX或Base64",
-                zhTW: "指定HLS解密KEY. 可以是文件, HEX或Base64",
-                enUS: "Set the HLS decryption key. Can be file, HEX or Base64"
-            ),
-            ["cmd_customHLSIv"] = new TextContainer
-            (
-                zhCN: "指定HLS解密IV. 可以是文件, HEX或Base64",
-                zhTW: "指定HLS解密IV. 可以是文件, HEX或Base64",
-                enUS: "Set the HLS decryption iv. Can be file, HEX or Base64"
-            ),
-            ["cmd_livePipeMux"] = new TextContainer
-            (
-                zhCN: "录制直播并开启实时合并时通过管道+ffmpeg实时混流到TS文件",
-                zhTW: "錄製直播並開啟即時合併時通過管道+ffmpeg即時混流到TS文件",
-                enUS: "Real-time muxing to TS file through pipeline + ffmpeg (liveRealTimeMerge enabled)"
-            ),
-            ["cmd_liveKeepSegments"] = new TextContainer
-            (
-                zhCN: "录制直播并开启实时合并时依然保留分片",
-                zhTW: "錄製直播並開啟即時合併時依然保留分片",
-                enUS: "Keep segments when recording a live (liveRealTimeMerge enabled)"
-            ),
-            ["cmd_liveRecordLimit"] = new TextContainer
-            (
-                zhCN: "录制直播时的录制时长限制",
-                zhTW: "錄製直播時的錄製時長限制",
-                enUS: "Recording time limit when recording live"
-            ),
-            ["cmd_taskStartAt"] = new TextContainer
-            (
-                zhCN: "在此时间之前不会开始执行任务",
-                zhTW: "在此時間之前不會開始執行任務",
-                enUS: "Task execution will not start before this time"
-            ),
-            ["cmd_useShakaPackager"] = new TextContainer
-            (
-                zhCN: "解密时使用shaka-packager替代mp4decrypt",
-                zhTW: "解密時使用shaka-packager替代mp4decrypt",
-                enUS: "Use shaka-packager instead of mp4decrypt to decrypt"
-            ),
-            ["cmd_concurrentDownload"] = new TextContainer
-            (
-                zhCN: "并发下载已选择的音频、视频和字幕",
-                zhTW: "並發下載已選擇的音訊、影片和字幕",
-                enUS: "Concurrently download the selected audio, video and subtitles"
-            ),
-            ["cmd_selectVideo"] = new TextContainer
-            (
-                zhCN: "通过正则表达式选择符合要求的视频流. 输入 \"--morehelp select-video\" 以查看详细信息",
-                zhTW: "通過正則表達式選擇符合要求的影片軌. 輸入 \"--morehelp select-video\" 以查看詳細訊息",
-                enUS: "Select video streams by regular expressions. Use \"--morehelp select-video\" for more details"
-            ),
-            ["cmd_dropVideo"] = new TextContainer
-            (
-                zhCN: "通过正则表达式去除符合要求的视频流.",
-                zhTW: "通過正則表達式去除符合要求的影片串流.",
-                enUS: "Drop video streams by regular expressions."
-            ),
-            ["cmd_selectVideo_more"] = new TextContainer
-            (
-                zhCN: "通过正则表达式选择符合要求的视频流. 你能够以:分隔形式指定如下参数:\r\n\r\n" +
-                      "id=REGEX:lang=REGEX:name=REGEX:codecs=REGEX:res=REGEX:frame=REGEX\r\n" +
-                      "segsMin=number:segsMax=number:ch=REGEX:range=REGEX:url=REGEX\r\n" +
-                      "plistDurMin=hms:plistDurMax=hms:bwMin=int:bwMax=int:role=string:for=FOR\r\n\r\n" +
-                      "* for=FOR: 选择方式. best[number], worst[number], all (默认: best)\r\n\r\n" +
-                      "例如: \r\n" +
-                      "# 选择最佳视频\r\n" +
-                      "-sv best\r\n" +
-                      "# 选择4K+HEVC视频\r\n" +
-                      "-sv res=\"3840*\":codecs=hvc1:for=best\r\n" +
-                      "# 选择长度大于1小时20分钟30秒的视频\r\n" +
-                      "-sv plistDurMin=\"1h20m30s\":for=best\r\n" +
-                      "-sv role=\"main\":for=best\r\n" +
-                      "# 选择码率在800Kbps至1Mbps之间的视频\r\n" +
-                      "-sv bwMin=800:bwMax=1000\r\n",
-                zhTW: "通過正則表達式選擇符合要求的影片軌. 你能夠以:分隔形式指定如下參數:\r\n\r\n" +
-                      "id=REGEX:lang=REGEX:name=REGEX:codecs=REGEX:res=REGEX:frame=REGEX\r\n" +
-                      "segsMin=number:segsMax=number:ch=REGEX:range=REGEX:url=REGEX\r\n" +
-                      "plistDurMin=hms:plistDurMax=hms:bwMin=int:bwMax=int:role=string:for=FOR\r\n\r\n" +
-                      "* for=FOR: 選擇方式. best[number], worst[number], all (默認: best)\r\n\r\n" +
-                      "例如: \r\n" +
-                      "# 選擇最佳影片\r\n" +
-                      "-sv best\r\n" +
-                      "# 選擇4K+HEVC影片\r\n" +
-                      "-sv res=\"3840*\":codecs=hvc1:for=best\r\n" +
-                      "# 選擇長度大於1小時20分鐘30秒的影片\r\n" +
-                      "-sv plistDurMin=\"1h20m30s\":for=best\r\n" +
-                      "-sv role=\"main\":for=best\r\n" +
-                      "# 選擇碼率在800Kbps至1Mbps之間的影片\r\n" +
-                      "-sv bwMin=800:bwMax=1000\r\n",
-                enUS: "Select video streams by regular expressions. OPTIONS is a colon separated list of:\r\n\r\n" +
-                      "id=REGEX:lang=REGEX:name=REGEX:codecs=REGEX:res=REGEX:frame=REGEX\r\n" +
-                      "segsMin=number:segsMax=number:ch=REGEX:range=REGEX:url=REGEX\r\n" +
-                      "plistDurMin=hms:plistDurMax=hms:bwMin=int:bwMax=int:role=string:for=FOR\r\n\r\n" +
-                      "* for=FOR: Select type. best[number], worst[number], all (Default: best)\r\n\r\n" +
-                      "Examples: \r\n" +
-                      "# select best video\r\n" +
-                      "-sv best\r\n" +
-                      "# select 4K+HEVC video\r\n" +
-                      "-sv res=\"3840*\":codecs=hvc1:for=best\r\n" +
-                      "# Select best video with duration longer than 1 hour 20 minutes 30 seconds\r\n" +
-                      "-sv plistDurMin=\"1h20m30s\":for=best\r\n" +
-                      "-sv role=\"main\":for=best\r\n" +
-                      "# Select video with bandwidth between 800Kbps and 1Mbps\r\n" +
-                      "-sv bwMin=800:bwMax=1000\r\n"
-            ),
-            ["cmd_selectAudio"] = new TextContainer
-            (
-                zhCN: "通过正则表达式选择符合要求的音频流. 输入 \"--morehelp select-audio\" 以查看详细信息",
-                zhTW: "通過正則表達式選擇符合要求的音軌. 輸入 \"--morehelp select-audio\" 以查看詳細訊息",
-                enUS: "Select audio streams by regular expressions. Use \"--morehelp select-audio\" for more details"
-            ),
-            ["cmd_dropAudio"] = new TextContainer
-            (
-                zhCN: "通过正则表达式去除符合要求的音频流.",
-                zhTW: "通過正則表達式去除符合要求的音軌.",
-                enUS: "Drop audio streams by regular expressions."
-            ),
-            ["cmd_selectAudio_more"] = new TextContainer
-            (
-                zhCN: "通过正则表达式选择符合要求的音频流. 参考 --select-video\r\n\r\n" +
-                      "例如: \r\n" +
-                      "# 选择所有音频\r\n" +
-                      "-sa all\r\n" +
-                      "# 选择最佳英语音轨\r\n" +
-                      "-sa lang=en:for=best\r\n" +
-                      "# 选择最佳的2条英语(或日语)音轨\r\n" +
-                      "-sa lang=\"ja|en\":for=best2\r\n" +
-                      "-sa role=\"main\":for=best\r\n",
-                zhTW: "通過正則表達式選擇符合要求的音軌. 參考 --select-video\r\n\r\n" +
-                      "例如: \r\n" +
-                      "# 選擇所有音訊\r\n" +
-                      "-sa all\r\n" +
-                      "# 選擇最佳英語音軌\r\n" +
-                      "-sa lang=en:for=best\r\n" +
-                      "# 選擇最佳的2條英語(或日語)音軌\r\n" +
-                      "-sa lang=\"ja|en\":for=best2\r\n" +
-                      "-sa role=\"main\":for=best\r\n",
-                enUS: "Select audio streams by regular expressions. ref --select-video\r\n\r\n" +
-                      "Examples: \r\n" +
-                      "# select all\r\n" +
-                      "-sa all\r\n" +
-                      "# select best eng audio\r\n" +
-                      "-sa lang=en:for=best\r\n" +
-                      "# select best 2, and language is ja or en\r\n" +
-                      "-sa lang=\"ja|en\":for=best2\r\n" +
-                      "-sa role=\"main\":for=best\r\n"
-            ),
-            ["cmd_selectSubtitle"] = new TextContainer
-            (
-                zhCN: "通过正则表达式选择符合要求的字幕流. 输入 \"--morehelp select-subtitle\" 以查看详细信息",
-                zhTW: "通過正則表達式選擇符合要求的字幕流. 輸入 \"--morehelp select-subtitle\" 以查看詳細訊息",
-                enUS: "Select subtitle streams by regular expressions. Use \"--morehelp select-subtitle\" for more details"
-            ),
-            ["cmd_dropSubtitle"] = new TextContainer
-            (
-                zhCN: "通过正则表达式去除符合要求的字幕流.",
-                zhTW: "通過正則表達式去除符合要求的字幕流.",
-                enUS: "Drop subtitle streams by regular expressions."
-            ),
-            ["cmd_custom_range"] = new TextContainer
-            (
-                zhCN: "下载点播内容时, 仅下载部分分片.\r\n\r\n" +
-                      "例如: \r\n" +
-                      "# 下载[0,10]共11个分片\r\n" +
-                      "--custom-range 0-10\r\n" +
-                      "# 下载从序号10开始的后续分片\r\n" +
-                      "--custom-range 10-\r\n" +
-                      "# 下载前100个分片\r\n" +
-                      "--custom-range -99\r\n" +
-                      "# 下载第5分钟到20分钟的内容\r\n" +
-                      "--custom-range 05:00-20:00\r\n",
-                zhTW: "下載點播內容時, 僅下載部分分片.\r\n\r\n" +
-                      "例如: \r\n" +
-                      "# 下載[0,10]共11個分片\r\n" +
-                      "--custom-range 0-10\r\n" +
-                      "# 下載從序號10開始的後續分片\r\n" +
-                      "--custom-range 10-\r\n" +
-                      "# 下載前100個分片\r\n" +
-                      "--custom-range -99\r\n" +
-                      "# 下載第5分鐘到20分鐘的內容\r\n" +
-                      "--custom-range 05:00-20:00\r\n",
-                enUS: "Download only part of the segments when downloading vod content.\r\n\r\n" +
-                      "Examples: \r\n" +
-                      "# Download [0,10], a total of 11 segments\r\n" +
-                      "--custom-range 0-10\r\n" +
-                      "# Download subsequent segments starting from index 10\r\n" +
-                      "--custom-range 10-\r\n" +
-                      "# Download the first 100 segments\r\n" +
-                      "--custom-range -99\r\n" +
-                      "# Download content from the 05:00 to 20:00\r\n" +
-                      "--custom-range 05:00-20:00\r\n"
-            ),
-            ["cmd_selectSubtitle_more"] = new TextContainer
-            (
-                zhCN: "通过正则表达式选择符合要求的字幕流. 参考 --select-video\r\n\r\n" +
-                      "例如: \r\n" +
-                      "# 选择所有字幕\r\n" +
-                      "-ss all\r\n" +
-                      "# 选择所有带有\"中文\"的字幕\r\n" +
-                      "-ss name=\"中文\":for=all\r\n",
-                zhTW: "通過正則表達式選擇符合要求的字幕流. 參考 --select-video\r\n\r\n" +
-                      "例如: \r\n" +
-                      "# 選擇所有字幕\r\n" +
-                      "-ss all\r\n" +
-                      "# 選擇所有帶有\"中文\"的字幕\r\n" +
-                      "-ss name=\"中文\":for=all\r\n",
-                enUS: "Select subtitle streams by regular expressions. ref --select-video\r\n\r\n" +
-                      "Examples: \r\n" +
-                      "# select all subs\r\n" +
-                      "-ss all\r\n" +
-                      "# select all subs containing \"English\"\r\n" +
-                      "-ss name=\"English\":for=all\r\n"
-            ),
-            ["cmd_muxAfterDone_more"] = new TextContainer
-            (
-                zhCN: "所有工作完成时尝试混流分离的音视频. 你能够以:分隔形式指定如下参数:\r\n\r\n" +
-                      "* format=FORMAT: 指定混流容器 mkv, mp4, ts\r\n" +
-                      "* muxer=MUXER: 指定混流程序 ffmpeg, mkvmerge (默认: ffmpeg)\r\n" +
-                      "* bin_path=PATH: 指定程序路径 (默认: 自动寻找)\r\n" +
-                      "* skip_sub=BOOL: 是否忽略字幕文件 (默认: false)\r\n" +
-                      "* keep=BOOL: 混流完成是否保留文件 true, false (默认: false)\r\n\r\n" +
-                      "例如: \r\n" +
-                      "# 混流为mp4容器\r\n" +
-                      "-M format=mp4\r\n" +
-                      "# 使用mkvmerge, 自动寻找程序\r\n" +
-                      "-M format=mkv:muxer=mkvmerge\r\n" +
-                      "# 使用mkvmerge, 自定义程序路径\r\n" +
-                      "-M format=mkv:muxer=mkvmerge:bin_path=\"C\\:\\Program Files\\MKVToolNix\\mkvmerge.exe\"\r\n",
-                zhTW: "所有工作完成時嘗試混流分離的影音. 你能夠以:分隔形式指定如下參數:\r\n\r\n" +
-                      "* format=FORMAT: 指定混流容器 mkv, mp4, ts\r\n" +
-                      "* muxer=MUXER: 指定混流程序 ffmpeg, mkvmerge (默認: ffmpeg)\r\n" +
-                      "* bin_path=PATH: 指定程序路徑 (默認: 自動尋找)\r\n" +
-                      "* skip_sub=BOOL: 是否忽略字幕文件 (默認: false)\r\n" +
-                      "* keep=BOOL: 混流完成是否保留文件 true, false (默認: false)\r\n\r\n" +
-                      "例如: \r\n" +
-                      "# 混流為mp4容器\r\n" +
-                      "-M format=mp4\r\n" +
-                      "# 使用mkvmerge, 自動尋找程序\r\n" +
-                      "-M format=mkv:muxer=mkvmerge\r\n" +
-                      "# 使用mkvmerge, 自訂程序路徑\r\n" +
-                      "-M format=mkv:muxer=mkvmerge:bin_path=\"C\\:\\Program Files\\MKVToolNix\\mkvmerge.exe\"\r\n",
-                enUS: "When all works is done, try to mux the downloaded streams. OPTIONS is a colon separated list of:\r\n\r\n" +
-                      "* format=FORMAT: set container. mkv, mp4, ts\r\n" +
-                      "* muxer=MUXER: set muxer. ffmpeg, mkvmerge (Default: ffmpeg)\r\n" +
-                      "* bin_path=PATH: set binary file path. (Default: auto)\r\n" +
-                      "* skip_sub=BOOL: set whether or not skip subtitle files (Default: false)\r\n" +
-                      "* keep=BOOL: set whether or not keep files. true, false (Default: false)\r\n\r\n" +
-                      "Examples: \r\n" +
-                      "# mux to mp4\r\n" +
-                      "-M format=mp4\r\n" +
-                      "# use mkvmerge, auto detect bin path\r\n" +
-                      "-M format=mkv:muxer=mkvmerge\r\n" +
-                      "# use mkvmerge, set bin path\r\n" +
-                      "-M format=mkv:muxer=mkvmerge:bin_path=\"C\\:\\Program Files\\MKVToolNix\\mkvmerge.exe\"\r\n"
-            ),
-            ["cmd_muxAfterDone"] = new TextContainer
-            (
-                zhCN: "所有工作完成时尝试混流分离的音视频. 输入 \"--morehelp mux-after-done\" 以查看详细信息",
-                zhTW: "所有工作完成時嘗試混流分離的影音. 輸入 \"--morehelp mux-after-done\" 以查看詳細訊息",
-                enUS: "When all works is done, try to mux the downloaded streams. Use \"--morehelp mux-after-done\" for more details"
-            ),
-            ["cmd_muxImport"] = new TextContainer
-            (
-                zhCN: "混流时引入外部媒体文件. 输入 \"--morehelp mux-import\" 以查看详细信息",
-                zhTW: "混流時引入外部媒體檔案. 輸入 \"--morehelp mux-import\" 以查看詳細訊息",
-                enUS: "When MuxAfterDone enabled, allow to import local media files. Use \"--morehelp mux-import\" for more details"
-            ),
-            ["cmd_muxImport_more"] = new TextContainer
-            (
-                zhCN: "混流时引入外部媒体文件. 你能够以:分隔形式指定如下参数:\r\n\r\n" +
-                      "* path=PATH: 指定媒体文件路径\r\n" +
-                      "* lang=CODE: 指定媒体文件语言代码 (非必须)\r\n" +
-                      "* name=NAME: 指定媒体文件描述信息 (非必须)\r\n\r\n" +
-                      "例如: \r\n" +
-                      "# 引入外部字幕\r\n" +
-                      "--mux-import path=zh-Hans.srt:lang=chi:name=\"中文 (简体)\"\r\n" +
-                      "# 引入外部音轨+字幕\r\n" +
-                      "--mux-import path=\"D\\:\\media\\atmos.m4a\":lang=eng:name=\"English Description Audio\" --mux-import path=\"D\\:\\media\\eng.vtt\":lang=eng:name=\"English (Description)\"",
-                zhTW: "混流時引入外部媒體檔案. 你能夠以:分隔形式指定如下參數:\r\n\r\n" +
-                      "* path=PATH: 指定媒體檔案路徑\r\n" +
-                      "* lang=CODE: 指定媒體檔案語言代碼 (非必須)\r\n" +
-                      "* name=NAME: 指定媒體檔案描述訊息 (非必須)\r\n\r\n" +
-                      "例如: \r\n" +
-                      "# 引入外部字幕\r\n" +
-                      "--mux-import path=zh-Hant.srt:lang=chi:name=\"中文 (繁體)\"\r\n" +
-                      "# 引入外部音軌+字幕\r\n" +
-                      "--mux-import path=\"D\\:\\media\\atmos.m4a\":lang=eng:name=\"English Description Audio\" --mux-import path=\"D\\:\\media\\eng.vtt\":lang=eng:name=\"English (Description)\"",
-                enUS: "When MuxAfterDone enabled, allow to import local media files. OPTIONS is a colon separated list of:\r\n\r\n" +
-                      "* path=PATH: set file path\r\n" +
-                      "* lang=CODE: set media language code (not required)\r\n" +
-                      "* name=NAME: set description (not required)\r\n\r\n" +
-                      "Examples: \r\n" +
-                      "# import subtitle\r\n" +
-                      "--mux-import path=en-US.srt:lang=eng:name=\"English (Original)\"\r\n" +
-                      "# import audio and subtitle\r\n" +
-                      "--mux-import path=\"D\\:\\media\\atmos.m4a\":lang=eng:name=\"English Description Audio\" --mux-import path=\"D\\:\\media\\eng.vtt\":lang=eng:name=\"English (Description)\""
-            ),
-            ["cmd_writeMetaJson"] = new TextContainer
-            (
-                zhCN: "解析后的信息是否输出json文件",
-                zhTW: "解析後的訊息是否輸出json文件",
-                enUS: "Write meta json after parsed"
-            ),
-            ["liveLimit"] = new TextContainer
-            (
-                zhCN: "本次直播录制时长上限: ",
-                zhTW: "本次直播錄製時長上限: ",
-                enUS: "Live recording duration limit: "
-            ),
-            ["realTimeDecMessage"] = new TextContainer
-            (
-                zhCN: "启用实时解密时,建议用shaka-packager而非mp4decrypt",
-                zhTW: "啟用即時解密時,建議用shaka-packager而非mp4decrypt",
-                enUS: "When enabling real-time decryption, it is recommended to use shaka-packager instead of mp4decrypt"
-            ),
-            ["liveLimitReached"] = new TextContainer
-            (
-                zhCN: "到达直播录制上限,即将停止录制",
-                zhTW: "到達直播錄製上限,即將停止錄製",
-                enUS: "Live recording limit reached, will stop recording soon"
-            ),
-            ["saveName"] = new TextContainer
-            (
-                zhCN: "保存文件名: ",
-                zhTW: "保存檔案名: ",
-                enUS: "Save Name: "
-            ),
-            ["fetch"] = new TextContainer
-            (
-                zhCN: "获取: ",
-                zhTW: "獲取: ",
-                enUS: "Fetch: "
-            ),
-            ["ffmpegMerge"] = new TextContainer
-            (
-                zhCN: "调用ffmpeg合并中...",
-                zhTW: "調用ffmpeg合併中...",
-                enUS: "ffmpeg merging..."
-            ),
-            ["ffmpegNotFound"] = new TextContainer
-            (
-                zhCN: "找不到ffmpeg,请自行下载:https://ffmpeg.org/download.html",
-                zhTW: "找不到ffmpeg,請自行下載:https://ffmpeg.org/download.html",
-                enUS: "ffmpeg not found, please download at: https://ffmpeg.org/download.html"
-            ),
-            ["fixingTTML"] = new TextContainer
-            (
-                zhCN: "正在提取TTML(raw)字幕...",
-                zhTW: "正在提取TTML(raw)字幕...",
-                enUS: "Extracting TTML(raw) subtitle..."
-            ),
-            ["fixingTTMLmp4"] = new TextContainer
-            (
-                zhCN: "正在提取TTML(mp4)字幕...",
-                zhTW: "正在提取TTML(mp4)字幕...",
-                enUS: "Extracting TTML(mp4) subtitle..."
-            ),
-            ["fixingVTT"] = new TextContainer
-            (
-                zhCN: "正在提取VTT(raw)字幕...",
-                zhTW: "正在提取VTT(raw)字幕...",
-                enUS: "Extracting VTT(raw) subtitle..."
-            ),
-            ["fixingVTTmp4"] = new TextContainer
-            (
-                zhCN: "正在提取VTT(mp4)字幕...",
-                zhTW: "正在提取VTT(mp4)字幕...",
-                enUS: "Extracting VTT(mp4) subtitle..."
-            ),
-            ["keyProcessorNotFound"] = new TextContainer
-            (
-                zhCN: "找不到支持的Processor",
-                zhTW: "找不到支持的Processor",
-                enUS: "No Processor matched"
-            ),
-            ["liveFound"] = new TextContainer
-            (
-                zhCN: "检测到直播流",
-                zhTW: "檢測到直播流",
-                enUS: "Live stream found"
-            ),
-            ["loadingUrl"] = new TextContainer
-            (
-                zhCN: "加载URL: ",
-                zhTW: "載入URL: ",
-                enUS: "Loading URL: "
-            ),
-            ["masterM3u8Found"] = new TextContainer
-            (
-                zhCN: "检测到Master列表,开始解析全部流信息",
-                zhTW: "檢測到Master列表,開始解析全部流訊息",
-                enUS: "Master List detected, try parse all streams"
-            ),
-            ["matchTS"] = new TextContainer
-            (
-                zhCN: "内容匹配: [white on green3]HTTP Live MPEG2-TS[/]",
-                zhTW: "內容匹配: [white on green3]HTTP Live MPEG2-TS[/]",
-                enUS: "Content Matched: [white on green3]HTTP Live MPEG2-TS[/]"
-            ),
-            ["matchDASH"] = new TextContainer
-            (
-                zhCN: "内容匹配: [white on mediumorchid1]Dynamic Adaptive Streaming over HTTP[/]",
-                zhTW: "內容匹配: [white on mediumorchid1]Dynamic Adaptive Streaming over HTTP[/]",
-                enUS: "Content Matched: [white on mediumorchid1]Dynamic Adaptive Streaming over HTTP[/]"
-            ),
-            ["matchMSS"] = new TextContainer
-            (
-                zhCN: "内容匹配: [white on steelblue1]Microsoft Smooth Streaming[/]",
-                zhTW: "內容匹配: [white on steelblue1]Microsoft Smooth Streaming[/]",
-                enUS: "Content Matched: [white on steelblue1]Microsoft Smooth Streaming[/]"
-            ),
-            ["matchHLS"] = new TextContainer
-            (
-                zhCN: "内容匹配: [white on deepskyblue1]HTTP Live Streaming[/]",
-                zhTW: "內容匹配: [white on deepskyblue1]HTTP Live Streaming[/]",
-                enUS: "Content Matched: [white on deepskyblue1]HTTP Live Streaming[/]"
-            ),
-            ["partMerge"] = new TextContainer
-            (
-                zhCN: "分片数量大于1800个,开始分块合并...",
-                zhTW: "分片數量大於1800個,開始分塊合併...",
-                enUS: "Segments more than 1800, start partial merge..."
-            ),
-            ["notSupported"] = new TextContainer
-            (
-                zhCN: "当前输入不受支持: ",
-                zhTW: "當前輸入不受支援: ",
-                enUS: "Input not supported: "
-            ),
-            ["parsingStream"] = new TextContainer
-            (
-                zhCN: "正在解析媒体信息...",
-                zhTW: "正在解析媒體信息...",
-                enUS: "Parsing streams..."
-            ),
-            ["promptChoiceText"] = new TextContainer
-            (
-                zhCN: "[grey](按键盘上下键以浏览更多内容)[/]",
-                zhTW: "[grey](按鍵盤上下鍵以瀏覽更多內容)[/]",
-                enUS: "[grey](Move up and down to reveal more streams)[/]"
-            ),
-            ["promptInfo"] = new TextContainer
-            (
-                zhCN: "(按 [blue]空格键[/] 选择流, [green]回车键[/] 完成选择)",
-                zhTW: "(按 [blue]空格鍵[/] 選擇流, [green]確認鍵[/] 完成選擇)",
-                enUS: "(Press [blue]<space>[/] to toggle a stream, [green]<enter>[/] to accept)"
-            ),
-            ["promptTitle"] = new TextContainer
-            (
-                zhCN: "请选择 [green]你要下载的内容[/]:",
-                zhTW: "請選擇 [green]你要下載的內容[/]:",
-                enUS: "Please select [green]what you want to download[/]:"
-            ),
-            ["readingInfo"] = new TextContainer
-            (
-                zhCN: "读取媒体信息...",
-                zhTW: "讀取媒體訊息...",
-                enUS: "Reading media info..."
-            ),
-            ["searchKey"] = new TextContainer
-            (
-                zhCN: "正在尝试从文本文件搜索KEY...",
-                zhTW: "正在嘗試從文本文件搜尋KEY...",
-                enUS: "Trying to search for KEY from text file..."
-            ),
-            ["segmentCountCheckNotPass"] = new TextContainer
-            (
-                zhCN: "分片数量校验不通过, 共{}个,已下载{}.",
-                zhTW: "分片數量校驗不通過, 共{}個,已下載{}.",
-                enUS: "Segment count check not pass, total: {}, downloaded: {}."
-            ),
-            ["selectedStream"] = new TextContainer
-            (
-                zhCN: "已选择的流:",
-                zhTW: "已選擇的流:",
-                enUS: "Selected streams:"
-            ),
-            ["startDownloading"] = new TextContainer
-            (
-                zhCN: "开始下载...",
-                zhTW: "開始下載...",
-                enUS: "Start downloading..."
-            ),
-            ["streamsInfo"] = new TextContainer
-            (
-                zhCN: "已解析, 共计 {} 条媒体流, 基本流 {} 条, 可选音频流 {} 条, 可选字幕流 {} 条",
-                zhTW: "已解析, 共計 {} 條媒體流, 基本流 {} 條, 可選音頻流 {} 條, 可選字幕流 {} 條",
-                enUS: "Extracted, there are {} streams, with {} basic streams, {} audio streams, {} subtitle streams"
-            ),
-            ["writeJson"] = new TextContainer
-            (
-                zhCN: "写出meta json",
-                zhTW: "寫出meta json",
-                enUS: "Writing meta json"
-            ),
-            ["noStreamsToDownload"] = new TextContainer
-            (
-                zhCN: "没有找到需要下载的流",
-                zhTW: "沒有找到需要下載的流",
-                enUS: "No stream found to download"
-            ),
+namespace N_m3u8DL_RE.Common.Resource;
 
-        };
-    }
-}
+internal class StaticText
+{
+    public static Dictionary<string, TextContainer> LANG_DIC = new()
+    {
+        ["singleFileSplitWarn"] = new TextContainer
+        (
+            zhCN: "整段文件已被自动切割为小分片以加速下载",
+            zhTW: "整段文件已被自動切割為小分片以加速下載",
+            enUS: "The entire file has been cut into small segments to accelerate"
+        ),
+        ["singleFileRealtimeDecryptWarn"] = new TextContainer
+        (
+            zhCN: "实时解密已被强制关闭",
+            zhTW: "即時解密已被強制關閉",
+            enUS: "Real-time decryption has been disabled"
+        ),
+        ["cmd_forceAnsiConsole"] = new TextContainer
+        (
+            zhCN: "强制认定终端为支持ANSI且可交互的终端",
+            zhTW: "強制認定終端為支援ANSI且可交往的終端",
+            enUS: "Force assuming the terminal is ANSI-compatible and interactive"
+        ),
+        ["cmd_noAnsiColor"] = new TextContainer
+        (
+            zhCN: "去除ANSI颜色",
+            zhTW: "關閉ANSI顏色",
+            enUS: "Remove ANSI colors"
+        ),
+        ["customRangeWarn"] = new TextContainer
+        (
+            zhCN: "请注意,自定义下载范围有时会导致音画不同步",
+            zhTW: "請注意,自定義下載範圍有時會導致音畫不同步",
+            enUS: "Please note that custom range may sometimes result in audio and video being out of sync"
+        ),
+        ["customRangeInvalid"] = new TextContainer
+        (
+            zhCN: "自定义下载范围无效",
+            zhTW: "自定義下載範圍無效",
+            enUS: "User customed range invalid"
+        ),
+        ["customAdKeywordsFound"] = new TextContainer
+        (
+            zhCN: "用户自定义广告分片URL关键字:",
+            zhTW: "用戶自定義廣告分片URL關鍵字:",
+            enUS: "User customed Ad keyword: "
+        ),
+        ["customRangeFound"] = new TextContainer
+        (
+            zhCN: "用户自定义下载范围:",
+            zhTW: "用戶自定義下載範圍:",
+            enUS: "User customed range: "
+        ),
+        ["consoleRedirected"] = new TextContainer
+        (
+            zhCN: "输出被重定向, 将清除ANSI颜色",
+            zhTW: "輸出被重定向, 將清除ANSI顏色",
+            enUS: "Output is redirected, ANSI colors are cleared."
+        ),
+        ["processImageSub"] = new TextContainer
+        (
+            zhCN: "正在处理图形字幕",
+            zhTW: "正在處理圖形字幕",
+            enUS: "Processing Image Sub"
+        ),
+        ["newVersionFound"] = new TextContainer
+        (
+            zhCN: "检测到新版本,请尽快升级!",
+            zhTW: "檢測到新版本,請盡快升級!",
+            enUS: "New version detected!"
+        ),
+        ["namedPipeCreated"] = new TextContainer
+        (
+            zhCN: "已创建命名管道:",
+            zhTW: "已創建命名管道:",
+            enUS: "Named pipe created: "
+        ),
+        ["namedPipeMux"] = new TextContainer
+        (
+            zhCN: "通过命名管道混流到",
+            zhTW: "通過命名管道混流到",
+            enUS: "Mux with named pipe, to"
+        ),
+        ["taskStartAt"] = new TextContainer
+        (
+            zhCN: "程序将等待,直到:",
+            zhTW: "程序將等待,直到:",
+            enUS: "The program will wait until: "
+        ),
+        ["autoBinaryMerge"] = new TextContainer
+        (
+            zhCN: "检测到fMP4,自动开启二进制合并",
+            zhTW: "檢測到fMP4,自動開啟二進位制合併",
+            enUS: "fMP4 is detected, binary merging is automatically enabled"
+        ),
+        ["autoBinaryMerge2"] = new TextContainer
+        (
+            zhCN: "检测到杜比视界内容,自动开启二进制合并",
+            zhTW: "檢測到杜比視界內容,自動開啟二進位制合併",
+            enUS: "Dolby Vision content is detected, binary merging is automatically enabled"
+        ),
+        ["autoBinaryMerge3"] = new TextContainer
+        (
+            zhCN: "检测到无法识别的加密方式,自动开启二进制合并",
+            zhTW: "檢測到無法識別的加密方式,自動開啟二進位制合併",
+            enUS: "An unrecognized encryption method is detected, binary merging is automatically enabled"
+        ),
+        ["autoBinaryMerge4"] = new TextContainer
+        (
+            zhCN: "检测到CENC加密方式,自动开启二进制合并",
+            zhTW: "檢測到CENC加密方式,自動開啟二進位制合併",
+            enUS: "When CENC encryption is detected, binary merging is automatically enabled"
+        ),
+        ["autoBinaryMerge5"] = new TextContainer
+        (
+            zhCN: "检测到杜比视界内容,混流功能已禁用",
+            zhTW: "檢測到杜比視界內容,混流功能已禁用",
+            enUS: "Dolby Vision content is detected, mux after done is automatically disabled"
+        ),
+        ["autoBinaryMerge6"] = new TextContainer
+        (
+            zhCN: "你已开启下载完成后混流,自动开启二进制合并",
+            zhTW: "你已開啟下載完成後混流,自動開啟二進制合併",
+            enUS: "MuxAfterDone is detected, binary merging is automatically enabled"
+        ),
+        ["badM3u8"] = new TextContainer
+        (
+            zhCN: "错误的m3u8",
+            zhTW: "錯誤的m3u8",
+            enUS: "Bad m3u8"
+        ),
+        ["binaryMerge"] = new TextContainer
+        (
+            zhCN: "二进制合并中...",
+            zhTW: "二進位制合併中...",
+            enUS: "Binary merging..."
+        ),
+        ["checkingLast"] = new TextContainer
+        (
+            zhCN: "验证最后一个分片有效性",
+            zhTW: "驗證最後一個分片有效性",
+            enUS: "Verifying the validity of the last segment"
+        ),
+        ["cmd_baseUrl"] = new TextContainer
+        (
+            zhCN: "设置BaseURL",
+            zhTW: "設置BaseURL",
+            enUS: "Set BaseURL"
+        ),
+        ["cmd_maxSpeed"] = new TextContainer
+        (
+            zhCN: "设置限速,单位支持 Mbps 或 Kbps,如:15M 100K",
+            zhTW: "設置限速,單位支持 Mbps 或 Kbps,如:15M 100K",
+            enUS: "Set speed limit, Mbps or Kbps, for example: 15M 100K."
+        ),
+        ["cmd_noDateInfo"] = new TextContainer
+        (
+            zhCN: "混流时不写入日期信息",
+            zhTW: "混流時不寫入日期訊息",
+            enUS: "Date information is not written during muxing"
+        ),
+        ["cmd_noLog"] = new TextContainer
+        (
+            zhCN: "关闭日志文件输出",
+            zhTW: "關閉日誌文件輸出",
+            enUS: "Disable log file output"
+        ),
+        ["cmd_appendUrlParams"] = new TextContainer
+        (
+            zhCN: "将输入Url的Params添加至分片, 对某些网站很有用, 例如 kakao.com",
+            zhTW: "將輸入Url的Params添加至分片, 對某些網站很有用, 例如 kakao.com",
+            enUS: "Add Params of input Url to segments, useful for some websites, such as kakao.com"
+        ),
+        ["cmd_autoSelect"] = new TextContainer
+        (
+            zhCN: "自动选择所有类型的最佳轨道",
+            zhTW: "自動選擇所有類型的最佳軌道",
+            enUS: "Automatically selects the best tracks of all types"
+        ),
+        ["cmd_binaryMerge"] = new TextContainer
+        (
+            zhCN: "二进制合并",
+            zhTW: "二進位制合併",
+            enUS: "Binary merge"
+        ),
+        ["cmd_useFFmpegConcatDemuxer"] = new TextContainer
+        (
+            zhCN: "使用 ffmpeg 合并时,使用 concat 分离器而非 concat 协议",
+            zhTW: "使用 ffmpeg 合併時,使用 concat 分離器而非 concat 協議",
+            enUS: "When merging with ffmpeg, use the concat demuxer instead of the concat protocol"
+        ),
+        ["cmd_checkSegmentsCount"] = new TextContainer
+        (
+            zhCN: "检测实际下载的分片数量和预期数量是否匹配",
+            zhTW: "檢測實際下載的分片數量和預期數量是否匹配",
+            enUS: "Check if the actual number of segments downloaded matches the expected number"
+        ),
+        ["cmd_downloadRetryCount"] = new TextContainer
+        (
+            zhCN: "每个分片下载异常时的重试次数",
+            zhTW: "每個分片下載異常時的重試次數",
+            enUS: "The number of retries when download segment error"
+        ),
+        ["cmd_decryptionBinaryPath"] = new TextContainer
+        (
+            zhCN: "MP4解密所用工具的全路径, 例如 C:\\Tools\\mp4decrypt.exe",
+            zhTW: "MP4解密所用工具的全路徑, 例如 C:\\Tools\\mp4decrypt.exe",
+            enUS: "Full path to the tool used for MP4 decryption, like C:\\Tools\\mp4decrypt.exe"
+        ),
+        ["cmd_delAfterDone"] = new TextContainer
+        (
+            zhCN: "完成后删除临时文件",
+            zhTW: "完成後刪除臨時文件",
+            enUS: "Delete temporary files when done"
+        ),
+        ["cmd_ffmpegBinaryPath"] = new TextContainer
+        (
+            zhCN: "ffmpeg可执行程序全路径, 例如 C:\\Tools\\ffmpeg.exe",
+            zhTW: "ffmpeg可執行程序全路徑, 例如 C:\\Tools\\ffmpeg.exe",
+            enUS: "Full path to the ffmpeg binary, like C:\\Tools\\ffmpeg.exe"
+        ),
+        ["cmd_mkvmergeBinaryPath"] = new TextContainer
+        (
+            zhCN: "mkvmerge可执行程序全路径, 例如 C:\\Tools\\mkvmerge.exe",
+            zhTW: "mkvmerge可執行程序全路徑, 例如 C:\\Tools\\mkvmerge.exe",
+            enUS: "Full path to the mkvmerge binary, like C:\\Tools\\mkvmerge.exe"
+        ),
+        ["cmd_liveFixVttByAudio"] = new TextContainer
+        (
+            zhCN: "通过读取音频文件的起始时间修正VTT字幕",
+            zhTW: "透過讀取音訊檔案的起始時間修正VTT字幕",
+            enUS: "Correct VTT sub by reading the start time of the audio file"
+        ),
+        ["cmd_header"] = new TextContainer
+        (
+            zhCN: "为HTTP请求设置特定的请求头, 例如:\r\n-H \"Cookie: mycookie\" -H \"User-Agent: iOS\"",
+            zhTW: "為HTTP請求設置特定的請求頭, 例如:\r\n-H \"Cookie: mycookie\" -H \"User-Agent: iOS\"",
+            enUS: "Pass custom header(s) to server, Example:\r\n-H \"Cookie: mycookie\" -H \"User-Agent: iOS\""
+        ),
+        ["cmd_Input"] = new TextContainer
+        (
+            zhCN: "链接或文件",
+            zhTW: "連結或文件",
+            enUS: "Input Url or File"
+        ),
+        ["cmd_keys"] = new TextContainer
+        (
+            zhCN: "设置解密密钥, 程序调用mp4decrpyt/shaka-packager进行解密. 格式:\r\n--key KID1:KEY1 --key KID2:KEY2\r\n对于KEY相同的情况可以直接输入 --key KEY",
+            zhTW: "設置解密密鑰, 程序調用mp4decrpyt/shaka-packager進行解密. 格式:\r\n--key KID1:KEY1 --key KID2:KEY2\r\n對於KEY相同的情況可以直接輸入 --key KEY",
+            enUS: "Set decryption key(s) to mp4decrypt/shaka-packager. format:\r\n--key KID1:KEY1 --key KID2:KEY2\r\nor use --key KEY if all tracks share the same key."
+        ),
+        ["cmd_keyText"] = new TextContainer
+        (
+            zhCN: "设置密钥文件,程序将从文件中按KID搜寻KEY以解密.(不建议使用特大文件)",
+            zhTW: "設置密鑰文件,程序將從文件中按KID搜尋KEY以解密.(不建議使用特大文件)",
+            enUS: "Set the kid-key file, the program will search the KEY with KID from the file.(Very large file are not recommended)"
+        ),
+        ["cmd_loadKeyFailed"] = new TextContainer
+        (
+            zhCN: "获取KEY失败,忽略读取.",
+            zhTW: "獲取KEY失敗,忽略讀取.",
+            enUS: "Failed to get KEY, ignore."
+        ),
+        ["cmd_logLevel"] = new TextContainer
+        (
+            zhCN: "设置日志级别",
+            zhTW: "設置日誌級別",
+            enUS: "Set log level"
+        ),
+        ["cmd_MP4RealTimeDecryption"] = new TextContainer
+        (
+            zhCN: "实时解密MP4分片",
+            zhTW: "即時解密MP4分片",
+            enUS: "Decrypt MP4 segments in real time"
+        ),
+        ["cmd_saveDir"] = new TextContainer
+        (
+            zhCN: "设置输出目录",
+            zhTW: "設置輸出目錄",
+            enUS: "Set output directory"
+        ),
+        ["cmd_saveName"] = new TextContainer
+        (
+            zhCN: "设置保存文件名",
+            zhTW: "設置保存檔案名",
+            enUS: "Set output filename"
+        ),
+        ["cmd_savePattern"] = new TextContainer
+        (
+            zhCN: "设置保存文件命名模板, 支持使用变量",
+            zhTW: "",
+            enUS: ""
+        ),
+        ["cmd_skipDownload"] = new TextContainer
+        (
+            zhCN: "跳过下载",
+            zhTW: "跳過下載",
+            enUS: "Skip download"
+        ),
+        ["cmd_skipMerge"] = new TextContainer
+        (
+            zhCN: "跳过合并分片",
+            zhTW: "跳過合併分片",
+            enUS: "Skip segments merge"
+        ),
+        ["cmd_subFormat"] = new TextContainer
+        (
+            zhCN: "字幕输出类型",
+            zhTW: "字幕輸出類型",
+            enUS: "Subtitle output format"
+        ),
+        ["cmd_subOnly"] = new TextContainer
+        (
+            zhCN: "只选取字幕轨道",
+            zhTW: "只選取字幕軌道",
+            enUS: "Select only subtitle tracks"
+        ),
+        ["cmd_subtitleFix"] = new TextContainer
+        (
+            zhCN: "自动修正字幕",
+            zhTW: "自動修正字幕",
+            enUS: "Automatically fix subtitles"
+        ),
+        ["cmd_threadCount"] = new TextContainer
+        (
+            zhCN: "设置下载线程数",
+            zhTW: "設置下載執行緒數",
+            enUS: "Set download thread count"
+        ),
+        ["cmd_tmpDir"] = new TextContainer
+        (
+            zhCN: "设置临时文件存储目录",
+            zhTW: "設置臨時文件儲存目錄",
+            enUS: "Set temporary file directory"
+        ),
+        ["cmd_uiLanguage"] = new TextContainer
+        (
+            zhCN: "设置UI语言",
+            zhTW: "設置UI語言",
+            enUS: "Set UI language"
+        ),
+        ["cmd_moreHelp"] = new TextContainer
+        (
+            zhCN: "查看某个选项的详细帮助信息",
+            zhTW: "查看某個選項的詳細幫助訊息",
+            enUS: "Set more help info about one option"
+        ),
+        ["cmd_urlProcessorArgs"] = new TextContainer
+        (
+            zhCN: "此字符串将直接传递给URL Processor",
+            zhTW: "此字符串將直接傳遞給URL Processor",
+            enUS: "Give these arguments to the URL Processors."
+        ),
+        ["cmd_liveRealTimeMerge"] = new TextContainer
+        (
+            zhCN: "录制直播时实时合并",
+            zhTW: "錄製直播時即時合併",
+            enUS: "Real-time merge into file when recording live"
+        ),
+        ["cmd_customProxy"] = new TextContainer
+        (
+            zhCN: "设置请求代理, 如 http://127.0.0.1:8888",
+            zhTW: "設置請求代理, 如 http://127.0.0.1:8888",
+            enUS: "Set web request proxy, like http://127.0.0.1:8888"
+        ),
+        ["cmd_customRange"] = new TextContainer
+        (
+            zhCN: "仅下载部分分片. 输入 \"--morehelp custom-range\" 以查看详细信息",
+            zhTW: "僅下載部分分片. 輸入 \"--morehelp custom-range\" 以查看詳細訊息",
+            enUS: "Download only part of the segments. Use \"--morehelp custom-range\" for more details"
+        ),
+        ["cmd_useSystemProxy"] = new TextContainer
+        (
+            zhCN: "使用系统默认代理",
+            zhTW: "使用系統默認代理",
+            enUS: "Use system default proxy"
+        ),
+        ["cmd_livePerformAsVod"] = new TextContainer
+        (
+            zhCN: "以点播方式下载直播流",
+            zhTW: "以點播方式下載直播流",
+            enUS: "Download live streams as vod"
+        ),
+        ["cmd_liveWaitTime"] = new TextContainer
+        (
+            zhCN: "手动设置直播列表刷新间隔",
+            zhTW: "手動設置直播列表刷新間隔",
+            enUS: "Manually set the live playlist refresh interval"
+        ),
+        ["cmd_adKeyword"] = new TextContainer
+        (
+            zhCN: "设置广告分片的URL关键字(正则表达式)",
+            zhTW: "設置廣告分片的URL關鍵字(正則表達式)",
+            enUS: "Set URL keywords (regular expressions) for AD segments"
+        ),
+        ["cmd_liveTakeCount"] = new TextContainer
+        (
+            zhCN: "手动设置录制直播时首次获取分片的数量",
+            zhTW: "手動設置錄製直播時首次獲取分片的數量",
+            enUS: "Manually set the number of segments downloaded for the first time when recording live"
+        ),
+        ["cmd_customHLSMethod"] = new TextContainer
+        (
+            zhCN: "指定HLS加密方式 (AES_128|AES_128_ECB|CENC|CHACHA20|NONE|SAMPLE_AES|SAMPLE_AES_CTR|UNKNOWN)",
+            zhTW: "指定HLS加密方式 (AES_128|AES_128_ECB|CENC|CHACHA20|NONE|SAMPLE_AES|SAMPLE_AES_CTR|UNKNOWN)",
+            enUS: "Set HLS encryption method (AES_128|AES_128_ECB|CENC|CHACHA20|NONE|SAMPLE_AES|SAMPLE_AES_CTR|UNKNOWN)"
+        ),
+        ["cmd_customHLSKey"] = new TextContainer
+        (
+            zhCN: "指定HLS解密KEY. 可以是文件, HEX或Base64",
+            zhTW: "指定HLS解密KEY. 可以是文件, HEX或Base64",
+            enUS: "Set the HLS decryption key. Can be file, HEX or Base64"
+        ),
+        ["cmd_customHLSIv"] = new TextContainer
+        (
+            zhCN: "指定HLS解密IV. 可以是文件, HEX或Base64",
+            zhTW: "指定HLS解密IV. 可以是文件, HEX或Base64",
+            enUS: "Set the HLS decryption iv. Can be file, HEX or Base64"
+        ),
+        ["cmd_livePipeMux"] = new TextContainer
+        (
+            zhCN: "录制直播并开启实时合并时通过管道+ffmpeg实时混流到TS文件",
+            zhTW: "錄製直播並開啟即時合併時通過管道+ffmpeg即時混流到TS文件",
+            enUS: "Real-time muxing to TS file through pipeline + ffmpeg (liveRealTimeMerge enabled)"
+        ),
+        ["cmd_liveKeepSegments"] = new TextContainer
+        (
+            zhCN: "录制直播并开启实时合并时依然保留分片",
+            zhTW: "錄製直播並開啟即時合併時依然保留分片",
+            enUS: "Keep segments when recording a live (liveRealTimeMerge enabled)"
+        ),
+        ["cmd_liveRecordLimit"] = new TextContainer
+        (
+            zhCN: "录制直播时的录制时长限制",
+            zhTW: "錄製直播時的錄製時長限制",
+            enUS: "Recording time limit when recording live"
+        ),
+        ["cmd_taskStartAt"] = new TextContainer
+        (
+            zhCN: "在此时间之前不会开始执行任务",
+            zhTW: "在此時間之前不會開始執行任務",
+            enUS: "Task execution will not start before this time"
+        ),
+        ["cmd_useShakaPackager"] = new TextContainer
+        (
+            zhCN: "解密时使用shaka-packager替代mp4decrypt",
+            zhTW: "解密時使用shaka-packager替代mp4decrypt",
+            enUS: "Use shaka-packager instead of mp4decrypt to decrypt"
+        ),
+        ["cmd_concurrentDownload"] = new TextContainer
+        (
+            zhCN: "并发下载已选择的音频、视频和字幕",
+            zhTW: "並發下載已選擇的音訊、影片和字幕",
+            enUS: "Concurrently download the selected audio, video and subtitles"
+        ),
+        ["cmd_selectVideo"] = new TextContainer
+        (
+            zhCN: "通过正则表达式选择符合要求的视频流. 输入 \"--morehelp select-video\" 以查看详细信息",
+            zhTW: "通過正則表達式選擇符合要求的影片軌. 輸入 \"--morehelp select-video\" 以查看詳細訊息",
+            enUS: "Select video streams by regular expressions. Use \"--morehelp select-video\" for more details"
+        ),
+        ["cmd_dropVideo"] = new TextContainer
+        (
+            zhCN: "通过正则表达式去除符合要求的视频流.",
+            zhTW: "通過正則表達式去除符合要求的影片串流.",
+            enUS: "Drop video streams by regular expressions."
+        ),
+        ["cmd_selectVideo_more"] = new TextContainer
+        (
+            zhCN: "通过正则表达式选择符合要求的视频流. 你能够以:分隔形式指定如下参数:\r\n\r\n" +
+                  "id=REGEX:lang=REGEX:name=REGEX:codecs=REGEX:res=REGEX:frame=REGEX\r\n" +
+                  "segsMin=number:segsMax=number:ch=REGEX:range=REGEX:url=REGEX\r\n" +
+                  "plistDurMin=hms:plistDurMax=hms:bwMin=int:bwMax=int:role=string:for=FOR\r\n\r\n" +
+                  "* for=FOR: 选择方式. best[number], worst[number], all (默认: best)\r\n\r\n" +
+                  "例如: \r\n" +
+                  "# 选择最佳视频\r\n" +
+                  "-sv best\r\n" +
+                  "# 选择4K+HEVC视频\r\n" +
+                  "-sv res=\"3840*\":codecs=hvc1:for=best\r\n" +
+                  "# 选择长度大于1小时20分钟30秒的视频\r\n" +
+                  "-sv plistDurMin=\"1h20m30s\":for=best\r\n" +
+                  "-sv role=\"main\":for=best\r\n" +
+                  "# 选择码率在800Kbps至1Mbps之间的视频\r\n" +
+                  "-sv bwMin=800:bwMax=1000\r\n",
+            zhTW: "通過正則表達式選擇符合要求的影片軌. 你能夠以:分隔形式指定如下參數:\r\n\r\n" +
+                  "id=REGEX:lang=REGEX:name=REGEX:codecs=REGEX:res=REGEX:frame=REGEX\r\n" +
+                  "segsMin=number:segsMax=number:ch=REGEX:range=REGEX:url=REGEX\r\n" +
+                  "plistDurMin=hms:plistDurMax=hms:bwMin=int:bwMax=int:role=string:for=FOR\r\n\r\n" +
+                  "* for=FOR: 選擇方式. best[number], worst[number], all (默認: best)\r\n\r\n" +
+                  "例如: \r\n" +
+                  "# 選擇最佳影片\r\n" +
+                  "-sv best\r\n" +
+                  "# 選擇4K+HEVC影片\r\n" +
+                  "-sv res=\"3840*\":codecs=hvc1:for=best\r\n" +
+                  "# 選擇長度大於1小時20分鐘30秒的影片\r\n" +
+                  "-sv plistDurMin=\"1h20m30s\":for=best\r\n" +
+                  "-sv role=\"main\":for=best\r\n" +
+                  "# 選擇碼率在800Kbps至1Mbps之間的影片\r\n" +
+                  "-sv bwMin=800:bwMax=1000\r\n",
+            enUS: "Select video streams by regular expressions. OPTIONS is a colon separated list of:\r\n\r\n" +
+                  "id=REGEX:lang=REGEX:name=REGEX:codecs=REGEX:res=REGEX:frame=REGEX\r\n" +
+                  "segsMin=number:segsMax=number:ch=REGEX:range=REGEX:url=REGEX\r\n" +
+                  "plistDurMin=hms:plistDurMax=hms:bwMin=int:bwMax=int:role=string:for=FOR\r\n\r\n" +
+                  "* for=FOR: Select type. best[number], worst[number], all (Default: best)\r\n\r\n" +
+                  "Examples: \r\n" +
+                  "# select best video\r\n" +
+                  "-sv best\r\n" +
+                  "# select 4K+HEVC video\r\n" +
+                  "-sv res=\"3840*\":codecs=hvc1:for=best\r\n" +
+                  "# Select best video with duration longer than 1 hour 20 minutes 30 seconds\r\n" +
+                  "-sv plistDurMin=\"1h20m30s\":for=best\r\n" +
+                  "-sv role=\"main\":for=best\r\n" +
+                  "# Select video with bandwidth between 800Kbps and 1Mbps\r\n" +
+                  "-sv bwMin=800:bwMax=1000\r\n"
+        ),
+        ["cmd_selectAudio"] = new TextContainer
+        (
+            zhCN: "通过正则表达式选择符合要求的音频流. 输入 \"--morehelp select-audio\" 以查看详细信息",
+            zhTW: "通過正則表達式選擇符合要求的音軌. 輸入 \"--morehelp select-audio\" 以查看詳細訊息",
+            enUS: "Select audio streams by regular expressions. Use \"--morehelp select-audio\" for more details"
+        ),
+        ["cmd_dropAudio"] = new TextContainer
+        (
+            zhCN: "通过正则表达式去除符合要求的音频流.",
+            zhTW: "通過正則表達式去除符合要求的音軌.",
+            enUS: "Drop audio streams by regular expressions."
+        ),
+        ["cmd_selectAudio_more"] = new TextContainer
+        (
+            zhCN: "通过正则表达式选择符合要求的音频流. 参考 --select-video\r\n\r\n" +
+                  "例如: \r\n" +
+                  "# 选择所有音频\r\n" +
+                  "-sa all\r\n" +
+                  "# 选择最佳英语音轨\r\n" +
+                  "-sa lang=en:for=best\r\n" +
+                  "# 选择最佳的2条英语(或日语)音轨\r\n" +
+                  "-sa lang=\"ja|en\":for=best2\r\n" +
+                  "-sa role=\"main\":for=best\r\n",
+            zhTW: "通過正則表達式選擇符合要求的音軌. 參考 --select-video\r\n\r\n" +
+                  "例如: \r\n" +
+                  "# 選擇所有音訊\r\n" +
+                  "-sa all\r\n" +
+                  "# 選擇最佳英語音軌\r\n" +
+                  "-sa lang=en:for=best\r\n" +
+                  "# 選擇最佳的2條英語(或日語)音軌\r\n" +
+                  "-sa lang=\"ja|en\":for=best2\r\n" +
+                  "-sa role=\"main\":for=best\r\n",
+            enUS: "Select audio streams by regular expressions. ref --select-video\r\n\r\n" +
+                  "Examples: \r\n" +
+                  "# select all\r\n" +
+                  "-sa all\r\n" +
+                  "# select best eng audio\r\n" +
+                  "-sa lang=en:for=best\r\n" +
+                  "# select best 2, and language is ja or en\r\n" +
+                  "-sa lang=\"ja|en\":for=best2\r\n" +
+                  "-sa role=\"main\":for=best\r\n"
+        ),
+        ["cmd_selectSubtitle"] = new TextContainer
+        (
+            zhCN: "通过正则表达式选择符合要求的字幕流. 输入 \"--morehelp select-subtitle\" 以查看详细信息",
+            zhTW: "通過正則表達式選擇符合要求的字幕流. 輸入 \"--morehelp select-subtitle\" 以查看詳細訊息",
+            enUS: "Select subtitle streams by regular expressions. Use \"--morehelp select-subtitle\" for more details"
+        ),
+        ["cmd_dropSubtitle"] = new TextContainer
+        (
+            zhCN: "通过正则表达式去除符合要求的字幕流.",
+            zhTW: "通過正則表達式去除符合要求的字幕流.",
+            enUS: "Drop subtitle streams by regular expressions."
+        ),
+        ["cmd_custom_range"] = new TextContainer
+        (
+            zhCN: "下载点播内容时, 仅下载部分分片.\r\n\r\n" +
+                  "例如: \r\n" +
+                  "# 下载[0,10]共11个分片\r\n" +
+                  "--custom-range 0-10\r\n" +
+                  "# 下载从序号10开始的后续分片\r\n" +
+                  "--custom-range 10-\r\n" +
+                  "# 下载前100个分片\r\n" +
+                  "--custom-range -99\r\n" +
+                  "# 下载第5分钟到20分钟的内容\r\n" +
+                  "--custom-range 05:00-20:00\r\n",
+            zhTW: "下載點播內容時, 僅下載部分分片.\r\n\r\n" +
+                  "例如: \r\n" +
+                  "# 下載[0,10]共11個分片\r\n" +
+                  "--custom-range 0-10\r\n" +
+                  "# 下載從序號10開始的後續分片\r\n" +
+                  "--custom-range 10-\r\n" +
+                  "# 下載前100個分片\r\n" +
+                  "--custom-range -99\r\n" +
+                  "# 下載第5分鐘到20分鐘的內容\r\n" +
+                  "--custom-range 05:00-20:00\r\n",
+            enUS: "Download only part of the segments when downloading vod content.\r\n\r\n" +
+                  "Examples: \r\n" +
+                  "# Download [0,10], a total of 11 segments\r\n" +
+                  "--custom-range 0-10\r\n" +
+                  "# Download subsequent segments starting from index 10\r\n" +
+                  "--custom-range 10-\r\n" +
+                  "# Download the first 100 segments\r\n" +
+                  "--custom-range -99\r\n" +
+                  "# Download content from the 05:00 to 20:00\r\n" +
+                  "--custom-range 05:00-20:00\r\n"
+        ),
+        ["cmd_selectSubtitle_more"] = new TextContainer
+        (
+            zhCN: "通过正则表达式选择符合要求的字幕流. 参考 --select-video\r\n\r\n" +
+                  "例如: \r\n" +
+                  "# 选择所有字幕\r\n" +
+                  "-ss all\r\n" +
+                  "# 选择所有带有\"中文\"的字幕\r\n" +
+                  "-ss name=\"中文\":for=all\r\n",
+            zhTW: "通過正則表達式選擇符合要求的字幕流. 參考 --select-video\r\n\r\n" +
+                  "例如: \r\n" +
+                  "# 選擇所有字幕\r\n" +
+                  "-ss all\r\n" +
+                  "# 選擇所有帶有\"中文\"的字幕\r\n" +
+                  "-ss name=\"中文\":for=all\r\n",
+            enUS: "Select subtitle streams by regular expressions. ref --select-video\r\n\r\n" +
+                  "Examples: \r\n" +
+                  "# select all subs\r\n" +
+                  "-ss all\r\n" +
+                  "# select all subs containing \"English\"\r\n" +
+                  "-ss name=\"English\":for=all\r\n"
+        ),
+        ["cmd_muxAfterDone_more"] = new TextContainer
+        (
+            zhCN: "所有工作完成时尝试混流分离的音视频. 你能够以:分隔形式指定如下参数:\r\n\r\n" +
+                  "* format=FORMAT: 指定混流容器 mkv, mp4, ts\r\n" +
+                  "* muxer=MUXER: 指定混流程序 ffmpeg, mkvmerge (默认: ffmpeg)\r\n" +
+                  "* bin_path=PATH: 指定程序路径 (默认: 自动寻找)\r\n" +
+                  "* skip_sub=BOOL: 是否忽略字幕文件 (默认: false)\r\n" +
+                  "* keep=BOOL: 混流完成是否保留文件 true, false (默认: false)\r\n\r\n" +
+                  "例如: \r\n" +
+                  "# 混流为mp4容器\r\n" +
+                  "-M format=mp4\r\n" +
+                  "# 使用mkvmerge, 自动寻找程序\r\n" +
+                  "-M format=mkv:muxer=mkvmerge\r\n" +
+                  "# 使用mkvmerge, 自定义程序路径\r\n" +
+                  "-M format=mkv:muxer=mkvmerge:bin_path=\"C\\:\\Program Files\\MKVToolNix\\mkvmerge.exe\"\r\n",
+            zhTW: "所有工作完成時嘗試混流分離的影音. 你能夠以:分隔形式指定如下參數:\r\n\r\n" +
+                  "* format=FORMAT: 指定混流容器 mkv, mp4, ts\r\n" +
+                  "* muxer=MUXER: 指定混流程序 ffmpeg, mkvmerge (默認: ffmpeg)\r\n" +
+                  "* bin_path=PATH: 指定程序路徑 (默認: 自動尋找)\r\n" +
+                  "* skip_sub=BOOL: 是否忽略字幕文件 (默認: false)\r\n" +
+                  "* keep=BOOL: 混流完成是否保留文件 true, false (默認: false)\r\n\r\n" +
+                  "例如: \r\n" +
+                  "# 混流為mp4容器\r\n" +
+                  "-M format=mp4\r\n" +
+                  "# 使用mkvmerge, 自動尋找程序\r\n" +
+                  "-M format=mkv:muxer=mkvmerge\r\n" +
+                  "# 使用mkvmerge, 自訂程序路徑\r\n" +
+                  "-M format=mkv:muxer=mkvmerge:bin_path=\"C\\:\\Program Files\\MKVToolNix\\mkvmerge.exe\"\r\n",
+            enUS: "When all works is done, try to mux the downloaded streams. OPTIONS is a colon separated list of:\r\n\r\n" +
+                  "* format=FORMAT: set container. mkv, mp4, ts\r\n" +
+                  "* muxer=MUXER: set muxer. ffmpeg, mkvmerge (Default: ffmpeg)\r\n" +
+                  "* bin_path=PATH: set binary file path. (Default: auto)\r\n" +
+                  "* skip_sub=BOOL: set whether or not skip subtitle files (Default: false)\r\n" +
+                  "* keep=BOOL: set whether or not keep files. true, false (Default: false)\r\n\r\n" +
+                  "Examples: \r\n" +
+                  "# mux to mp4\r\n" +
+                  "-M format=mp4\r\n" +
+                  "# use mkvmerge, auto detect bin path\r\n" +
+                  "-M format=mkv:muxer=mkvmerge\r\n" +
+                  "# use mkvmerge, set bin path\r\n" +
+                  "-M format=mkv:muxer=mkvmerge:bin_path=\"C\\:\\Program Files\\MKVToolNix\\mkvmerge.exe\"\r\n"
+        ),
+        ["cmd_muxAfterDone"] = new TextContainer
+        (
+            zhCN: "所有工作完成时尝试混流分离的音视频. 输入 \"--morehelp mux-after-done\" 以查看详细信息",
+            zhTW: "所有工作完成時嘗試混流分離的影音. 輸入 \"--morehelp mux-after-done\" 以查看詳細訊息",
+            enUS: "When all works is done, try to mux the downloaded streams. Use \"--morehelp mux-after-done\" for more details"
+        ),
+        ["cmd_muxImport"] = new TextContainer
+        (
+            zhCN: "混流时引入外部媒体文件. 输入 \"--morehelp mux-import\" 以查看详细信息",
+            zhTW: "混流時引入外部媒體檔案. 輸入 \"--morehelp mux-import\" 以查看詳細訊息",
+            enUS: "When MuxAfterDone enabled, allow to import local media files. Use \"--morehelp mux-import\" for more details"
+        ),
+        ["cmd_muxImport_more"] = new TextContainer
+        (
+            zhCN: "混流时引入外部媒体文件. 你能够以:分隔形式指定如下参数:\r\n\r\n" +
+                  "* path=PATH: 指定媒体文件路径\r\n" +
+                  "* lang=CODE: 指定媒体文件语言代码 (非必须)\r\n" +
+                  "* name=NAME: 指定媒体文件描述信息 (非必须)\r\n\r\n" +
+                  "例如: \r\n" +
+                  "# 引入外部字幕\r\n" +
+                  "--mux-import path=zh-Hans.srt:lang=chi:name=\"中文 (简体)\"\r\n" +
+                  "# 引入外部音轨+字幕\r\n" +
+                  "--mux-import path=\"D\\:\\media\\atmos.m4a\":lang=eng:name=\"English Description Audio\" --mux-import path=\"D\\:\\media\\eng.vtt\":lang=eng:name=\"English (Description)\"",
+            zhTW: "混流時引入外部媒體檔案. 你能夠以:分隔形式指定如下參數:\r\n\r\n" +
+                  "* path=PATH: 指定媒體檔案路徑\r\n" +
+                  "* lang=CODE: 指定媒體檔案語言代碼 (非必須)\r\n" +
+                  "* name=NAME: 指定媒體檔案描述訊息 (非必須)\r\n\r\n" +
+                  "例如: \r\n" +
+                  "# 引入外部字幕\r\n" +
+                  "--mux-import path=zh-Hant.srt:lang=chi:name=\"中文 (繁體)\"\r\n" +
+                  "# 引入外部音軌+字幕\r\n" +
+                  "--mux-import path=\"D\\:\\media\\atmos.m4a\":lang=eng:name=\"English Description Audio\" --mux-import path=\"D\\:\\media\\eng.vtt\":lang=eng:name=\"English (Description)\"",
+            enUS: "When MuxAfterDone enabled, allow to import local media files. OPTIONS is a colon separated list of:\r\n\r\n" +
+                  "* path=PATH: set file path\r\n" +
+                  "* lang=CODE: set media language code (not required)\r\n" +
+                  "* name=NAME: set description (not required)\r\n\r\n" +
+                  "Examples: \r\n" +
+                  "# import subtitle\r\n" +
+                  "--mux-import path=en-US.srt:lang=eng:name=\"English (Original)\"\r\n" +
+                  "# import audio and subtitle\r\n" +
+                  "--mux-import path=\"D\\:\\media\\atmos.m4a\":lang=eng:name=\"English Description Audio\" --mux-import path=\"D\\:\\media\\eng.vtt\":lang=eng:name=\"English (Description)\""
+        ),
+        ["cmd_writeMetaJson"] = new TextContainer
+        (
+            zhCN: "解析后的信息是否输出json文件",
+            zhTW: "解析後的訊息是否輸出json文件",
+            enUS: "Write meta json after parsed"
+        ),
+        ["liveLimit"] = new TextContainer
+        (
+            zhCN: "本次直播录制时长上限: ",
+            zhTW: "本次直播錄製時長上限: ",
+            enUS: "Live recording duration limit: "
+        ),
+        ["realTimeDecMessage"] = new TextContainer
+        (
+            zhCN: "启用实时解密时,建议用shaka-packager而非mp4decrypt",
+            zhTW: "啟用即時解密時,建議用shaka-packager而非mp4decrypt",
+            enUS: "When enabling real-time decryption, it is recommended to use shaka-packager instead of mp4decrypt"
+        ),
+        ["liveLimitReached"] = new TextContainer
+        (
+            zhCN: "到达直播录制上限,即将停止录制",
+            zhTW: "到達直播錄製上限,即將停止錄製",
+            enUS: "Live recording limit reached, will stop recording soon"
+        ),
+        ["saveName"] = new TextContainer
+        (
+            zhCN: "保存文件名: ",
+            zhTW: "保存檔案名: ",
+            enUS: "Save Name: "
+        ),
+        ["fetch"] = new TextContainer
+        (
+            zhCN: "获取: ",
+            zhTW: "獲取: ",
+            enUS: "Fetch: "
+        ),
+        ["ffmpegMerge"] = new TextContainer
+        (
+            zhCN: "调用ffmpeg合并中...",
+            zhTW: "調用ffmpeg合併中...",
+            enUS: "ffmpeg merging..."
+        ),
+        ["ffmpegNotFound"] = new TextContainer
+        (
+            zhCN: "找不到ffmpeg,请自行下载:https://ffmpeg.org/download.html",
+            zhTW: "找不到ffmpeg,請自行下載:https://ffmpeg.org/download.html",
+            enUS: "ffmpeg not found, please download at: https://ffmpeg.org/download.html"
+        ),
+        ["fixingTTML"] = new TextContainer
+        (
+            zhCN: "正在提取TTML(raw)字幕...",
+            zhTW: "正在提取TTML(raw)字幕...",
+            enUS: "Extracting TTML(raw) subtitle..."
+        ),
+        ["fixingTTMLmp4"] = new TextContainer
+        (
+            zhCN: "正在提取TTML(mp4)字幕...",
+            zhTW: "正在提取TTML(mp4)字幕...",
+            enUS: "Extracting TTML(mp4) subtitle..."
+        ),
+        ["fixingVTT"] = new TextContainer
+        (
+            zhCN: "正在提取VTT(raw)字幕...",
+            zhTW: "正在提取VTT(raw)字幕...",
+            enUS: "Extracting VTT(raw) subtitle..."
+        ),
+        ["fixingVTTmp4"] = new TextContainer
+        (
+            zhCN: "正在提取VTT(mp4)字幕...",
+            zhTW: "正在提取VTT(mp4)字幕...",
+            enUS: "Extracting VTT(mp4) subtitle..."
+        ),
+        ["keyProcessorNotFound"] = new TextContainer
+        (
+            zhCN: "找不到支持的Processor",
+            zhTW: "找不到支持的Processor",
+            enUS: "No Processor matched"
+        ),
+        ["liveFound"] = new TextContainer
+        (
+            zhCN: "检测到直播流",
+            zhTW: "檢測到直播流",
+            enUS: "Live stream found"
+        ),
+        ["loadingUrl"] = new TextContainer
+        (
+            zhCN: "加载URL: ",
+            zhTW: "載入URL: ",
+            enUS: "Loading URL: "
+        ),
+        ["masterM3u8Found"] = new TextContainer
+        (
+            zhCN: "检测到Master列表,开始解析全部流信息",
+            zhTW: "檢測到Master列表,開始解析全部流訊息",
+            enUS: "Master List detected, try parse all streams"
+        ),
+        ["matchTS"] = new TextContainer
+        (
+            zhCN: "内容匹配: [white on green3]HTTP Live MPEG2-TS[/]",
+            zhTW: "內容匹配: [white on green3]HTTP Live MPEG2-TS[/]",
+            enUS: "Content Matched: [white on green3]HTTP Live MPEG2-TS[/]"
+        ),
+        ["matchDASH"] = new TextContainer
+        (
+            zhCN: "内容匹配: [white on mediumorchid1]Dynamic Adaptive Streaming over HTTP[/]",
+            zhTW: "內容匹配: [white on mediumorchid1]Dynamic Adaptive Streaming over HTTP[/]",
+            enUS: "Content Matched: [white on mediumorchid1]Dynamic Adaptive Streaming over HTTP[/]"
+        ),
+        ["matchMSS"] = new TextContainer
+        (
+            zhCN: "内容匹配: [white on steelblue1]Microsoft Smooth Streaming[/]",
+            zhTW: "內容匹配: [white on steelblue1]Microsoft Smooth Streaming[/]",
+            enUS: "Content Matched: [white on steelblue1]Microsoft Smooth Streaming[/]"
+        ),
+        ["matchHLS"] = new TextContainer
+        (
+            zhCN: "内容匹配: [white on deepskyblue1]HTTP Live Streaming[/]",
+            zhTW: "內容匹配: [white on deepskyblue1]HTTP Live Streaming[/]",
+            enUS: "Content Matched: [white on deepskyblue1]HTTP Live Streaming[/]"
+        ),
+        ["partMerge"] = new TextContainer
+        (
+            zhCN: "分片数量大于1800个,开始分块合并...",
+            zhTW: "分片數量大於1800個,開始分塊合併...",
+            enUS: "Segments more than 1800, start partial merge..."
+        ),
+        ["notSupported"] = new TextContainer
+        (
+            zhCN: "当前输入不受支持: ",
+            zhTW: "當前輸入不受支援: ",
+            enUS: "Input not supported: "
+        ),
+        ["parsingStream"] = new TextContainer
+        (
+            zhCN: "正在解析媒体信息...",
+            zhTW: "正在解析媒體信息...",
+            enUS: "Parsing streams..."
+        ),
+        ["promptChoiceText"] = new TextContainer
+        (
+            zhCN: "[grey](按键盘上下键以浏览更多内容)[/]",
+            zhTW: "[grey](按鍵盤上下鍵以瀏覽更多內容)[/]",
+            enUS: "[grey](Move up and down to reveal more streams)[/]"
+        ),
+        ["promptInfo"] = new TextContainer
+        (
+            zhCN: "(按 [blue]空格键[/] 选择流, [green]回车键[/] 完成选择)",
+            zhTW: "(按 [blue]空格鍵[/] 選擇流, [green]確認鍵[/] 完成選擇)",
+            enUS: "(Press [blue]<space>[/] to toggle a stream, [green]<enter>[/] to accept)"
+        ),
+        ["promptTitle"] = new TextContainer
+        (
+            zhCN: "请选择 [green]你要下载的内容[/]:",
+            zhTW: "請選擇 [green]你要下載的內容[/]:",
+            enUS: "Please select [green]what you want to download[/]:"
+        ),
+        ["readingInfo"] = new TextContainer
+        (
+            zhCN: "读取媒体信息...",
+            zhTW: "讀取媒體訊息...",
+            enUS: "Reading media info..."
+        ),
+        ["searchKey"] = new TextContainer
+        (
+            zhCN: "正在尝试从文本文件搜索KEY...",
+            zhTW: "正在嘗試從文本文件搜尋KEY...",
+            enUS: "Trying to search for KEY from text file..."
+        ),
+        ["segmentCountCheckNotPass"] = new TextContainer
+        (
+            zhCN: "分片数量校验不通过, 共{}个,已下载{}.",
+            zhTW: "分片數量校驗不通過, 共{}個,已下載{}.",
+            enUS: "Segment count check not pass, total: {}, downloaded: {}."
+        ),
+        ["selectedStream"] = new TextContainer
+        (
+            zhCN: "已选择的流:",
+            zhTW: "已選擇的流:",
+            enUS: "Selected streams:"
+        ),
+        ["startDownloading"] = new TextContainer
+        (
+            zhCN: "开始下载...",
+            zhTW: "開始下載...",
+            enUS: "Start downloading..."
+        ),
+        ["streamsInfo"] = new TextContainer
+        (
+            zhCN: "已解析, 共计 {} 条媒体流, 基本流 {} 条, 可选音频流 {} 条, 可选字幕流 {} 条",
+            zhTW: "已解析, 共計 {} 條媒體流, 基本流 {} 條, 可選音頻流 {} 條, 可選字幕流 {} 條",
+            enUS: "Extracted, there are {} streams, with {} basic streams, {} audio streams, {} subtitle streams"
+        ),
+        ["writeJson"] = new TextContainer
+        (
+            zhCN: "写出meta json",
+            zhTW: "寫出meta json",
+            enUS: "Writing meta json"
+        ),
+        ["noStreamsToDownload"] = new TextContainer
+        (
+            zhCN: "没有找到需要下载的流",
+            zhTW: "沒有找到需要下載的流",
+            enUS: "No stream found to download"
+        ),
+
+    };
+}
\ No newline at end of file
diff --git a/src/N_m3u8DL-RE.Common/Resource/TextContainer.cs b/src/N_m3u8DL-RE.Common/Resource/TextContainer.cs
index 13ae7cc..d4a24c0 100644
--- a/src/N_m3u8DL-RE.Common/Resource/TextContainer.cs
+++ b/src/N_m3u8DL-RE.Common/Resource/TextContainer.cs
@@ -1,22 +1,15 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
+namespace N_m3u8DL_RE.Common.Resource;
 
-namespace N_m3u8DL_RE.Common.Resource
+internal class TextContainer
 {
-    internal class TextContainer
-    {
-        public string ZH_CN { get; set; }
-        public string ZH_TW { get; set; }
-        public string EN_US { get; set; }
+    public string ZH_CN { get; }
+    public string ZH_TW { get; }
+    public string EN_US { get; }
 
-        public TextContainer(string zhCN, string zhTW, string enUS)
-        {
-            ZH_CN = zhCN;
-            ZH_TW = zhTW;
-            EN_US = enUS;
-        }
+    public TextContainer(string zhCN, string zhTW, string enUS)
+    {
+        ZH_CN = zhCN;
+        ZH_TW = zhTW;
+        EN_US = enUS;
     }
-}
+}
\ No newline at end of file
diff --git a/src/N_m3u8DL-RE.Common/Util/GlobalUtil.cs b/src/N_m3u8DL-RE.Common/Util/GlobalUtil.cs
index f41dce9..b8e3ba7 100644
--- a/src/N_m3u8DL-RE.Common/Util/GlobalUtil.cs
+++ b/src/N_m3u8DL-RE.Common/Util/GlobalUtil.cs
@@ -1,18 +1,13 @@
 using N_m3u8DL_RE.Common.Entity;
 using N_m3u8DL_RE.Common.JsonConverter;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
 using System.Text.Json;
 using System.Text.Json.Serialization;
-using System.Threading.Tasks;
 
 namespace N_m3u8DL_RE.Common.Util;
 
 public static class GlobalUtil
 {
-    private static readonly JsonSerializerOptions Options = new JsonSerializerOptions
+    private static readonly JsonSerializerOptions Options = new()
     {
         Encoder = System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
         WriteIndented = true,
@@ -27,15 +22,15 @@ public static class GlobalUtil
         {
             return JsonSerializer.Serialize(s, Context.StreamSpec);
         }
-        else if (o is IOrderedEnumerable<StreamSpec> ss)
+        if (o is IOrderedEnumerable<StreamSpec> ss)
         {
             return JsonSerializer.Serialize(ss, Context.IOrderedEnumerableStreamSpec);
         }
-        else if (o is List<StreamSpec> sList)
+        if (o is List<StreamSpec> sList)
         {
             return JsonSerializer.Serialize(sList, Context.ListStreamSpec);
         }
-        else if (o is IEnumerable<MediaSegment> mList)
+        if (o is IEnumerable<MediaSegment> mList)
         {
             return JsonSerializer.Serialize(mList, Context.IEnumerableMediaSegment);
         }
@@ -47,14 +42,14 @@ public static class GlobalUtil
         return fileSize switch
         {
             < 0 => throw new ArgumentOutOfRangeException(nameof(fileSize)),
-            >= 1024 * 1024 * 1024 => string.Format("{0:########0.00}GB", (double)fileSize / (1024 * 1024 * 1024)),
-            >= 1024 * 1024 => string.Format("{0:####0.00}MB", (double)fileSize / (1024 * 1024)),
-            >= 1024 => string.Format("{0:####0.00}KB", (double)fileSize / 1024),
-            _ => string.Format("{0:####0.00}B", fileSize)
+            >= 1024 * 1024 * 1024 => $"{fileSize / (1024 * 1024 * 1024):########0.00}GB",
+            >= 1024 * 1024 => $"{fileSize / (1024 * 1024):####0.00}MB",
+            >= 1024 => $"{fileSize / 1024:####0.00}KB",
+            _ => $"{fileSize:####0.00}B"
         };
     }
 
-    //此函数用于格式化输出时长  
+    // 此函数用于格式化输出时长  
     public static string FormatTime(int time)
     {
         TimeSpan ts = new TimeSpan(0, 0, time);
@@ -74,6 +69,6 @@ public static class GlobalUtil
         var searchPath = new[] { Environment.CurrentDirectory, Path.GetDirectoryName(Environment.ProcessPath) };
         var envPath = Environment.GetEnvironmentVariable("PATH")?.Split(Path.PathSeparator) ??
                       Array.Empty<string>();
-        return searchPath.Concat(envPath).Select(p => Path.Combine(p, name + fileExt)).FirstOrDefault(File.Exists);
+        return searchPath.Concat(envPath).Select(p => Path.Combine(p!, name + fileExt)).FirstOrDefault(File.Exists);
     }
 }
\ No newline at end of file
diff --git a/src/N_m3u8DL-RE.Common/Util/HTTPUtil.cs b/src/N_m3u8DL-RE.Common/Util/HTTPUtil.cs
index 973f5ee..c04fdb4 100644
--- a/src/N_m3u8DL-RE.Common/Util/HTTPUtil.cs
+++ b/src/N_m3u8DL-RE.Common/Util/HTTPUtil.cs
@@ -37,13 +37,13 @@ public static class HTTPUtil
             }
         }
         Logger.Debug(webRequest.Headers.ToString());
-        //手动处理跳转,以免自定义Headers丢失
+        // 手动处理跳转,以免自定义Headers丢失
         var webResponse = await AppHttpClient.SendAsync(webRequest, HttpCompletionOption.ResponseHeadersRead);
         if (((int)webResponse.StatusCode).ToString().StartsWith("30"))
         {
             HttpResponseHeaders respHeaders = webResponse.Headers;
             Logger.Debug(respHeaders.ToString());
-            if (respHeaders != null && respHeaders.Location != null)
+            if (respHeaders.Location != null)
             {
                 var redirectedUrl = "";
                 if (!respHeaders.Location.IsAbsoluteUri)
@@ -64,7 +64,7 @@ public static class HTTPUtil
                 }
             }
         }
-        //手动将跳转后的URL设置进去, 用于后续取用
+        // 手动将跳转后的URL设置进去, 用于后续取用
         webResponse.Headers.Location = new Uri(url);
         webResponse.EnsureSuccessStatusCode();
         return webResponse;
@@ -76,9 +76,8 @@ public static class HTTPUtil
         {
             return await File.ReadAllBytesAsync(new Uri(url).LocalPath);
         }
-        byte[] bytes = new byte[0];
         var webResponse = await DoGetAsync(url, headers);
-        bytes = await webResponse.Content.ReadAsByteArrayAsync();
+        var bytes = await webResponse.Content.ReadAsByteArrayAsync();
         Logger.Debug(HexUtil.BytesToHex(bytes, " "));
         return bytes;
     }
@@ -91,9 +90,8 @@ public static class HTTPUtil
     /// <returns></returns>
     public static async Task<string> GetWebSourceAsync(string url, Dictionary<string, string>? headers = null)
     {
-        string htmlCode = string.Empty;
         var webResponse = await DoGetAsync(url, headers);
-        htmlCode = await webResponse.Content.ReadAsStringAsync();
+        string htmlCode = await webResponse.Content.ReadAsStringAsync();
         Logger.Debug(htmlCode);
         return htmlCode;
     }
@@ -112,7 +110,7 @@ public static class HTTPUtil
     /// <returns>(Source Code, RedirectedUrl)</returns>
     public static async Task<(string, string)> GetWebSourceAndNewUrlAsync(string url, Dictionary<string, string>? headers = null)
     {
-        string htmlCode = string.Empty;
+        string htmlCode;
         var webResponse = await DoGetAsync(url, headers);
         if (CheckMPEG2TS(webResponse))
         {
@@ -128,7 +126,7 @@ public static class HTTPUtil
 
     public static async Task<string> GetPostResponseAsync(string Url, byte[] postData)
     {
-        string htmlCode = string.Empty;
+        string htmlCode;
         using HttpRequestMessage request = new(HttpMethod.Post, Url);
         request.Headers.TryAddWithoutValidation("Content-Type", "application/json");
         request.Headers.TryAddWithoutValidation("Content-Length", postData.Length.ToString());
diff --git a/src/N_m3u8DL-RE.Common/Util/HexUtil.cs b/src/N_m3u8DL-RE.Common/Util/HexUtil.cs
index 2aea255..a2506ad 100644
--- a/src/N_m3u8DL-RE.Common/Util/HexUtil.cs
+++ b/src/N_m3u8DL-RE.Common/Util/HexUtil.cs
@@ -31,7 +31,7 @@ public static class HexUtil
         var hexSpan = hex.AsSpan().Trim();
         if (hexSpan.StartsWith("0x") || hexSpan.StartsWith("0X"))
         {
-            hexSpan = hexSpan.Slice(2);
+            hexSpan = hexSpan[2..];
         }
 
         return Convert.FromHexString(hexSpan);
diff --git a/src/N_m3u8DL-RE.Parser/Config/ParserConfig.cs b/src/N_m3u8DL-RE.Parser/Config/ParserConfig.cs
index 52e2831..fc21386 100644
--- a/src/N_m3u8DL-RE.Parser/Config/ParserConfig.cs
+++ b/src/N_m3u8DL-RE.Parser/Config/ParserConfig.cs
@@ -3,66 +3,65 @@ using N_m3u8DL_RE.Parser.Processor;
 using N_m3u8DL_RE.Parser.Processor.DASH;
 using N_m3u8DL_RE.Parser.Processor.HLS;
 
-namespace N_m3u8DL_RE.Parser.Config
+namespace N_m3u8DL_RE.Parser.Config;
+
+public class ParserConfig
 {
-    public class ParserConfig
-    {
-        public string Url { get; set; }
+    public string Url { get; set; }
 
-        public string OriginalUrl { get; set; }
+    public string OriginalUrl { get; set; }
 
-        public string BaseUrl { get; set; }
+    public string BaseUrl { get; set; }
 
-        public Dictionary<string, string> Headers { get; set; } = new Dictionary<string, string>();
+    public Dictionary<string, string> Headers { get; set; } = new Dictionary<string, string>();
 
-        /// <summary>
-        /// 内容前置处理器. 调用顺序与列表顺序相同
-        /// </summary>
-        public IList<ContentProcessor> ContentProcessors { get; } = new List<ContentProcessor>() { new DefaultHLSContentProcessor(), new DefaultDASHContentProcessor() };
+    /// <summary>
+    /// 内容前置处理器. 调用顺序与列表顺序相同
+    /// </summary>
+    public IList<ContentProcessor> ContentProcessors { get; } = new List<ContentProcessor>() { new DefaultHLSContentProcessor(), new DefaultDASHContentProcessor() };
 
-        /// <summary>
-        /// 添加分片URL前置处理器. 调用顺序与列表顺序相同
-        /// </summary>
-        public IList<UrlProcessor> UrlProcessors { get; } = new List<UrlProcessor>() { new DefaultUrlProcessor() };
+    /// <summary>
+    /// 添加分片URL前置处理器. 调用顺序与列表顺序相同
+    /// </summary>
+    public IList<UrlProcessor> UrlProcessors { get; } = new List<UrlProcessor>() { new DefaultUrlProcessor() };
 
-        /// <summary>
-        /// KEY解析器. 调用顺序与列表顺序相同
-        /// </summary>
-        public IList<KeyProcessor> KeyProcessors { get; } = new List<KeyProcessor>() { new DefaultHLSKeyProcessor() };
+    /// <summary>
+    /// KEY解析器. 调用顺序与列表顺序相同
+    /// </summary>
+    public IList<KeyProcessor> KeyProcessors { get; } = new List<KeyProcessor>() { new DefaultHLSKeyProcessor() };
 
 
-        /// <summary>
-        /// 自定义的加密方式
-        /// </summary>
-        public EncryptMethod? CustomMethod { get; set; }
+    /// <summary>
+    /// 自定义的加密方式
+    /// </summary>
+    public EncryptMethod? CustomMethod { get; set; }
 
-        /// <summary>
-        /// 自定义的解密KEY
-        /// </summary>
-        public byte[]? CustomeKey { get; set; }
+    /// <summary>
+    /// 自定义的解密KEY
+    /// </summary>
+    public byte[]? CustomeKey { get; set; }
 
-        /// <summary>
-        /// 自定义的解密IV
-        /// </summary>
-        public byte[]? CustomeIV { get; set; }
+    /// <summary>
+    /// 自定义的解密IV
+    /// </summary>
+    public byte[]? CustomeIV { get; set; }
 
-        /// <summary>
-        /// 组装视频分段的URL时,是否要把原本URL后的参数也加上去
-        /// 如 Base URL = "http://xxx.com/playlist.m3u8?hmac=xxx&token=xxx"
-        /// 相对路径 = clip_01.ts
-        /// 如果 AppendUrlParams=false,得 http://xxx.com/clip_01.ts
-        /// 如果 AppendUrlParams=true,得 http://xxx.com/clip_01.ts?hmac=xxx&token=xxx
-        /// </summary>
-        public bool AppendUrlParams { get; set; } = false;
+    /// <summary>
+    /// 组装视频分段的URL时,是否要把原本URL后的参数也加上去
+    /// 如 Base URL = "http://xxx.com/playlist.m3u8?hmac=xxx&token=xxx"
+    /// 相对路径 = clip_01.ts
+    /// 如果 AppendUrlParams=false,得 http://xxx.com/clip_01.ts
+    /// 如果 AppendUrlParams=true,得 http://xxx.com/clip_01.ts?hmac=xxx&token=xxx
+    /// </summary>
+    public bool AppendUrlParams { get; set; } = false;
 
-        /// <summary>
-        /// 此参数将会传递给URL Processor中
-        /// </summary>
-        public string? UrlProcessorArgs { get; set; }
+    /// <summary>
+    /// 此参数将会传递给URL Processor中
+    /// </summary>
+    public string? UrlProcessorArgs { get; set; }
 
-        /// <summary>
-        /// KEY重试次数
-        /// </summary>
-        public int KeyRetryCount { get; set; } = 3;
-    }
-}
+    /// <summary>
+    /// KEY重试次数
+    /// </summary>
+    public int KeyRetryCount { get; set; } = 3;
+}
\ No newline at end of file
diff --git a/src/N_m3u8DL-RE.Parser/Constants/DASHTags.cs b/src/N_m3u8DL-RE.Parser/Constants/DASHTags.cs
index 4a1ebbe..3bba8fb 100644
--- a/src/N_m3u8DL-RE.Parser/Constants/DASHTags.cs
+++ b/src/N_m3u8DL-RE.Parser/Constants/DASHTags.cs
@@ -4,13 +4,12 @@ using System.Linq;
 using System.Text;
 using System.Threading.Tasks;
 
-namespace N_m3u8DL_RE.Parser.Constants
+namespace N_m3u8DL_RE.Parser.Constants;
+
+internal class DASHTags
 {
-    internal class DASHTags
-    {
-        public static string TemplateRepresentationID = "$RepresentationID$";
-        public static string TemplateBandwidth = "$Bandwidth$";
-        public static string TemplateNumber = "$Number$";
-        public static string TemplateTime = "$Time$";
-    }
-}
+    public static string TemplateRepresentationID = "$RepresentationID$";
+    public static string TemplateBandwidth = "$Bandwidth$";
+    public static string TemplateNumber = "$Number$";
+    public static string TemplateTime = "$Time$";
+}
\ No newline at end of file
diff --git a/src/N_m3u8DL-RE.Parser/Constants/HLSTags.cs b/src/N_m3u8DL-RE.Parser/Constants/HLSTags.cs
index d27d3c8..ef59755 100644
--- a/src/N_m3u8DL-RE.Parser/Constants/HLSTags.cs
+++ b/src/N_m3u8DL-RE.Parser/Constants/HLSTags.cs
@@ -4,35 +4,34 @@ using System.Linq;
 using System.Text;
 using System.Threading.Tasks;
 
-namespace N_m3u8DL_RE.Parser.Constants
+namespace N_m3u8DL_RE.Parser.Constants;
+
+internal class HLSTags
 {
-    internal class HLSTags
-    {
-        public static string ext_m3u = "#EXTM3U";
-        public static string ext_x_targetduration = "#EXT-X-TARGETDURATION";
-        public static string ext_x_media_sequence = "#EXT-X-MEDIA-SEQUENCE";
-        public static string ext_x_discontinuity_sequence = "#EXT-X-DISCONTINUITY-SEQUENCE";
-        public static string ext_x_program_date_time = "#EXT-X-PROGRAM-DATE-TIME";
-        public static string ext_x_media = "#EXT-X-MEDIA";
-        public static string ext_x_playlist_type = "#EXT-X-PLAYLIST-TYPE";
-        public static string ext_x_key = "#EXT-X-KEY";
-        public static string ext_x_stream_inf = "#EXT-X-STREAM-INF";
-        public static string ext_x_version = "#EXT-X-VERSION";
-        public static string ext_x_allow_cache = "#EXT-X-ALLOW-CACHE";
-        public static string ext_x_endlist = "#EXT-X-ENDLIST";
-        public static string extinf = "#EXTINF";
-        public static string ext_i_frames_only = "#EXT-X-I-FRAMES-ONLY";
-        public static string ext_x_byterange = "#EXT-X-BYTERANGE";
-        public static string ext_x_i_frame_stream_inf = "#EXT-X-I-FRAME-STREAM-INF";
-        public static string ext_x_discontinuity = "#EXT-X-DISCONTINUITY";
-        public static string ext_x_cue_out_start = "#EXT-X-CUE-OUT";
-        public static string ext_x_cue_out = "#EXT-X-CUE-OUT-CONT";
-        public static string ext_is_independent_segments = "#EXT-X-INDEPENDENT-SEGMENTS";
-        public static string ext_x_scte35 = "#EXT-OATCLS-SCTE35";
-        public static string ext_x_cue_start = "#EXT-X-CUE-OUT";
-        public static string ext_x_cue_end = "#EXT-X-CUE-IN";
-        public static string ext_x_cue_span = "#EXT-X-CUE-SPAN";
-        public static string ext_x_map = "#EXT-X-MAP";
-        public static string ext_x_start = "#EXT-X-START";
-    }
-}
+    public static string ext_m3u = "#EXTM3U";
+    public static string ext_x_targetduration = "#EXT-X-TARGETDURATION";
+    public static string ext_x_media_sequence = "#EXT-X-MEDIA-SEQUENCE";
+    public static string ext_x_discontinuity_sequence = "#EXT-X-DISCONTINUITY-SEQUENCE";
+    public static string ext_x_program_date_time = "#EXT-X-PROGRAM-DATE-TIME";
+    public static string ext_x_media = "#EXT-X-MEDIA";
+    public static string ext_x_playlist_type = "#EXT-X-PLAYLIST-TYPE";
+    public static string ext_x_key = "#EXT-X-KEY";
+    public static string ext_x_stream_inf = "#EXT-X-STREAM-INF";
+    public static string ext_x_version = "#EXT-X-VERSION";
+    public static string ext_x_allow_cache = "#EXT-X-ALLOW-CACHE";
+    public static string ext_x_endlist = "#EXT-X-ENDLIST";
+    public static string extinf = "#EXTINF";
+    public static string ext_i_frames_only = "#EXT-X-I-FRAMES-ONLY";
+    public static string ext_x_byterange = "#EXT-X-BYTERANGE";
+    public static string ext_x_i_frame_stream_inf = "#EXT-X-I-FRAME-STREAM-INF";
+    public static string ext_x_discontinuity = "#EXT-X-DISCONTINUITY";
+    public static string ext_x_cue_out_start = "#EXT-X-CUE-OUT";
+    public static string ext_x_cue_out = "#EXT-X-CUE-OUT-CONT";
+    public static string ext_is_independent_segments = "#EXT-X-INDEPENDENT-SEGMENTS";
+    public static string ext_x_scte35 = "#EXT-OATCLS-SCTE35";
+    public static string ext_x_cue_start = "#EXT-X-CUE-OUT";
+    public static string ext_x_cue_end = "#EXT-X-CUE-IN";
+    public static string ext_x_cue_span = "#EXT-X-CUE-SPAN";
+    public static string ext_x_map = "#EXT-X-MAP";
+    public static string ext_x_start = "#EXT-X-START";
+}
\ No newline at end of file
diff --git a/src/N_m3u8DL-RE.Parser/Constants/MSSTags.cs b/src/N_m3u8DL-RE.Parser/Constants/MSSTags.cs
index bf7e32c..35b894b 100644
--- a/src/N_m3u8DL-RE.Parser/Constants/MSSTags.cs
+++ b/src/N_m3u8DL-RE.Parser/Constants/MSSTags.cs
@@ -4,13 +4,12 @@ using System.Linq;
 using System.Text;
 using System.Threading.Tasks;
 
-namespace N_m3u8DL_RE.Parser.Constants
+namespace N_m3u8DL_RE.Parser.Constants;
+
+internal class MSSTags
 {
-    internal class MSSTags
-    {
-        public static string Bitrate = "{Bitrate}";
-        public static string Bitrate_BK = "{bitrate}";
-        public static string StartTime = "{start_time}";
-        public static string StartTime_BK = "{start time}";
-    }
-}
+    public static string Bitrate = "{Bitrate}";
+    public static string Bitrate_BK = "{bitrate}";
+    public static string StartTime = "{start_time}";
+    public static string StartTime_BK = "{start time}";
+}
\ No newline at end of file
diff --git a/src/N_m3u8DL-RE.Parser/Extractor/DASHExtractor2.cs b/src/N_m3u8DL-RE.Parser/Extractor/DASHExtractor2.cs
index b84021d..f9f8c4f 100644
--- a/src/N_m3u8DL-RE.Parser/Extractor/DASHExtractor2.cs
+++ b/src/N_m3u8DL-RE.Parser/Extractor/DASHExtractor2.cs
@@ -13,266 +13,242 @@ using System.Threading.Tasks;
 using System.Xml;
 using System.Xml.Linq;
 
-namespace N_m3u8DL_RE.Parser.Extractor
+namespace N_m3u8DL_RE.Parser.Extractor;
+
+// https://blog.csdn.net/leek5533/article/details/117750191
+internal class DASHExtractor2 : IExtractor
 {
-    //https://blog.csdn.net/leek5533/article/details/117750191
-    internal class DASHExtractor2 : IExtractor
+    private static EncryptMethod DEFAULT_METHOD = EncryptMethod.CENC;
+
+    public ExtractorType ExtractorType => ExtractorType.MPEG_DASH;
+
+    private string MpdUrl = string.Empty;
+    private string BaseUrl = string.Empty;
+    private string MpdContent = string.Empty;
+    public ParserConfig ParserConfig { get; set; }
+
+    public DASHExtractor2(ParserConfig parserConfig)
     {
-        private static EncryptMethod DEFAULT_METHOD = EncryptMethod.CENC;
+        this.ParserConfig = parserConfig;
+        SetInitUrl();
+    }
 
-        public ExtractorType ExtractorType => ExtractorType.MPEG_DASH;
 
-        private string MpdUrl = string.Empty;
-        private string BaseUrl = string.Empty;
-        private string MpdContent = string.Empty;
-        public ParserConfig ParserConfig { get; set; }
+    private void SetInitUrl()
+    {
+        this.MpdUrl = ParserConfig.Url ?? string.Empty;
+        if (!string.IsNullOrEmpty(ParserConfig.BaseUrl))
+            this.BaseUrl = ParserConfig.BaseUrl;
+        else
+            this.BaseUrl = this.MpdUrl;
+    }
 
-        public DASHExtractor2(ParserConfig parserConfig)
+    private string ExtendBaseUrl(XElement element, string oriBaseUrl)
+    {
+        var target = element.Elements().Where(e => e.Name.LocalName == "BaseURL").FirstOrDefault();
+        if (target != null)
         {
-            this.ParserConfig = parserConfig;
-            SetInitUrl();
+            oriBaseUrl = ParserUtil.CombineURL(oriBaseUrl, target.Value);
         }
 
+        return oriBaseUrl;
+    }
 
-        private void SetInitUrl()
+    private double? GetFrameRate(XElement element)
+    {
+        var frameRate = element.Attribute("frameRate")?.Value;
+        if (frameRate != null && frameRate.Contains("/"))
         {
-            this.MpdUrl = ParserConfig.Url ?? string.Empty;
-            if (!string.IsNullOrEmpty(ParserConfig.BaseUrl))
-                this.BaseUrl = ParserConfig.BaseUrl;
-            else
-                this.BaseUrl = this.MpdUrl;
+            var d = Convert.ToDouble(frameRate.Split('/')[0]) / Convert.ToDouble(frameRate.Split('/')[1]);
+            frameRate = d.ToString("0.000");
+            return Convert.ToDouble(frameRate);
+        }
+        return null;
+    }
+
+    public Task<List<StreamSpec>> ExtractStreamsAsync(string rawText)
+    {
+        var streamList = new List<StreamSpec>();
+
+        this.MpdContent = rawText;
+        this.PreProcessContent();
+
+
+        var xmlDocument = XDocument.Parse(MpdContent);
+
+        // 选中第一个MPD节点
+        var mpdElement = xmlDocument.Elements().First(e => e.Name.LocalName == "MPD");
+
+        // 类型 static点播, dynamic直播
+        var type = mpdElement.Attribute("type")?.Value;
+        bool isLive = type == "dynamic";
+
+        // 分片最大时长
+        var maxSegmentDuration = mpdElement.Attribute("maxSegmentDuration")?.Value;
+        // 分片从该时间起可用
+        var availabilityStartTime = mpdElement.Attribute("availabilityStartTime")?.Value;
+        // 在availabilityStartTime的前XX段时间,分片有效
+        var timeShiftBufferDepth = mpdElement.Attribute("timeShiftBufferDepth")?.Value;
+        if (string.IsNullOrEmpty(timeShiftBufferDepth))
+        {
+            // 如果没有 默认一分钟有效
+            timeShiftBufferDepth = "PT1M";
+        }
+        // MPD发布时间
+        var publishTime = mpdElement.Attribute("publishTime")?.Value;
+        // MPD总时长
+        var mediaPresentationDuration = mpdElement.Attribute("mediaPresentationDuration")?.Value;
+
+        // 读取在MPD开头定义的<BaseURL>,并替换本身的URL
+        var baseUrlElement = mpdElement.Elements().Where(e => e.Name.LocalName == "BaseURL").FirstOrDefault();
+        if (baseUrlElement != null)
+        {
+            var baseUrl = baseUrlElement.Value;
+            if (baseUrl.Contains("kkbox.com.tw/")) baseUrl = baseUrl.Replace("//https:%2F%2F", "//");
+            this.BaseUrl = ParserUtil.CombineURL(this.MpdUrl, baseUrl);
         }
 
-        private string ExtendBaseUrl(XElement element, string oriBaseUrl)
+        // 全部Period
+        var periods = mpdElement.Elements().Where(e => e.Name.LocalName == "Period");
+        foreach (var period in periods)
         {
-            var target = element.Elements().Where(e => e.Name.LocalName == "BaseURL").FirstOrDefault();
-            if (target != null)
+            // 本Period时长
+            var periodDuration = period.Attribute("duration")?.Value;
+
+            // 本Period ID
+            var periodId = period.Attribute("id")?.Value;
+
+            // 最终分片会使用的baseurl
+            var segBaseUrl = this.BaseUrl;
+
+            // 处理baseurl嵌套
+            segBaseUrl = ExtendBaseUrl(period, segBaseUrl);
+
+            var adaptationSetsBaseUrl = segBaseUrl;
+
+            // 本Period中的全部AdaptationSet
+            var adaptationSets = period.Elements().Where(e => e.Name.LocalName == "AdaptationSet");
+            foreach (var adaptationSet in adaptationSets)
             {
-                oriBaseUrl = ParserUtil.CombineURL(oriBaseUrl, target.Value);
-            }
+                // 处理baseurl嵌套
+                segBaseUrl = ExtendBaseUrl(adaptationSet, segBaseUrl);
 
-            return oriBaseUrl;
-        }
+                var representationsBaseUrl = segBaseUrl;
 
-        private double? GetFrameRate(XElement element)
-        {
-            var frameRate = element.Attribute("frameRate")?.Value;
-            if (frameRate != null && frameRate.Contains("/"))
-            {
-                var d = Convert.ToDouble(frameRate.Split('/')[0]) / Convert.ToDouble(frameRate.Split('/')[1]);
-                frameRate = d.ToString("0.000");
-                return Convert.ToDouble(frameRate);
-            }
-            return null;
-        }
-
-        public async Task<List<StreamSpec>> ExtractStreamsAsync(string rawText)
-        {
-            var streamList = new List<StreamSpec>();
-
-            this.MpdContent = rawText;
-            this.PreProcessContent();
-
-
-            var xmlDocument = XDocument.Parse(MpdContent);
-
-            //选中第一个MPD节点
-            var mpdElement = xmlDocument.Elements().First(e => e.Name.LocalName == "MPD");
-
-            //类型 static点播, dynamic直播
-            var type = mpdElement.Attribute("type")?.Value;
-            bool isLive = type == "dynamic";
-
-            //分片最大时长
-            var maxSegmentDuration = mpdElement.Attribute("maxSegmentDuration")?.Value;
-            //分片从该时间起可用
-            var availabilityStartTime = mpdElement.Attribute("availabilityStartTime")?.Value;
-            //在availabilityStartTime的前XX段时间,分片有效
-            var timeShiftBufferDepth = mpdElement.Attribute("timeShiftBufferDepth")?.Value;
-            if (string.IsNullOrEmpty(timeShiftBufferDepth))
-            {
-                //如果没有 默认一分钟有效
-                timeShiftBufferDepth = "PT1M";
-            }
-            //MPD发布时间
-            var publishTime = mpdElement.Attribute("publishTime")?.Value;
-            //MPD总时长
-            var mediaPresentationDuration = mpdElement.Attribute("mediaPresentationDuration")?.Value;
-
-            //读取在MPD开头定义的<BaseURL>,并替换本身的URL
-            var baseUrlElement = mpdElement.Elements().Where(e => e.Name.LocalName == "BaseURL").FirstOrDefault();
-            if (baseUrlElement != null)
-            {
-                var baseUrl = baseUrlElement.Value;
-                if (baseUrl.Contains("kkbox.com.tw/")) baseUrl = baseUrl.Replace("//https:%2F%2F", "//");
-                this.BaseUrl = ParserUtil.CombineURL(this.MpdUrl, baseUrl);
-            }
-
-            //全部Period
-            var periods = mpdElement.Elements().Where(e => e.Name.LocalName == "Period");
-            foreach (var period in periods)
-            {
-                //本Period时长
-                var periodDuration = period.Attribute("duration")?.Value;
-
-                //本Period ID
-                var periodId = period.Attribute("id")?.Value;
-
-                //最终分片会使用的baseurl
-                var segBaseUrl = this.BaseUrl;
-
-                //处理baseurl嵌套
-                segBaseUrl = ExtendBaseUrl(period, segBaseUrl);
-
-                var adaptationSetsBaseUrl = segBaseUrl;
-
-                //本Period中的全部AdaptationSet
-                var adaptationSets = period.Elements().Where(e => e.Name.LocalName == "AdaptationSet");
-                foreach (var adaptationSet in adaptationSets)
+                var mimeType = adaptationSet.Attribute("contentType")?.Value ?? adaptationSet.Attribute("mimeType")?.Value;
+                var frameRate = GetFrameRate(adaptationSet);
+                // 本AdaptationSet中的全部Representation
+                var representations = adaptationSet.Elements().Where(e => e.Name.LocalName == "Representation");
+                foreach (var representation in representations)
                 {
-                    //处理baseurl嵌套
-                    segBaseUrl = ExtendBaseUrl(adaptationSet, segBaseUrl);
+                    // 处理baseurl嵌套
+                    segBaseUrl = ExtendBaseUrl(representation, segBaseUrl);
 
-                    var representationsBaseUrl = segBaseUrl;
-
-                    var mimeType = adaptationSet.Attribute("contentType")?.Value ?? adaptationSet.Attribute("mimeType")?.Value;
-                    var frameRate = GetFrameRate(adaptationSet);
-                    //本AdaptationSet中的全部Representation
-                    var representations = adaptationSet.Elements().Where(e => e.Name.LocalName == "Representation");
-                    foreach (var representation in representations)
+                    if (mimeType == null)
                     {
-                        //处理baseurl嵌套
-                        segBaseUrl = ExtendBaseUrl(representation, segBaseUrl);
+                        mimeType = representation.Attribute("contentType")?.Value ?? representation.Attribute("mimeType")?.Value ?? "";
+                    }
+                    var bandwidth = representation.Attribute("bandwidth");
+                    StreamSpec streamSpec = new();
+                    streamSpec.OriginalUrl = ParserConfig.OriginalUrl;
+                    streamSpec.PeriodId = periodId;
+                    streamSpec.Playlist = new Playlist();
+                    streamSpec.Playlist.MediaParts.Add(new MediaPart());
+                    streamSpec.GroupId = representation.Attribute("id")?.Value;
+                    streamSpec.Bandwidth = Convert.ToInt32(bandwidth?.Value ?? "0");
+                    streamSpec.Codecs = representation.Attribute("codecs")?.Value ?? adaptationSet.Attribute("codecs")?.Value;
+                    streamSpec.Language = FilterLanguage(representation.Attribute("lang")?.Value ?? adaptationSet.Attribute("lang")?.Value);
+                    streamSpec.FrameRate = frameRate ?? GetFrameRate(representation);
+                    streamSpec.Resolution = representation.Attribute("width")?.Value != null ? $"{representation.Attribute("width")?.Value}x{representation.Attribute("height")?.Value}" : null;
+                    streamSpec.Url = MpdUrl;
+                    streamSpec.MediaType = mimeType.Split('/')[0] switch
+                    {
+                        "text" => MediaType.SUBTITLES,
+                        "audio" => MediaType.AUDIO,
+                        _ => null
+                    };
+                    // 特殊处理
+                    if (representation.Attribute("volumeAdjust") != null)
+                    {
+                        streamSpec.GroupId += "-" + representation.Attribute("volumeAdjust")?.Value;
+                    }
+                    // 推测后缀名
+                    var mType = representation.Attribute("mimeType")?.Value ?? adaptationSet.Attribute("mimeType")?.Value;
+                    if (mType != null)
+                    {
+                        var mTypeSplit = mType.Split('/');
+                        streamSpec.Extension = mTypeSplit.Length == 2 ? mTypeSplit[1] : null;
+                    }
+                    // 优化字幕场景识别
+                    if (streamSpec.Codecs == "stpp" || streamSpec.Codecs == "wvtt")
+                    {
+                        streamSpec.MediaType = MediaType.SUBTITLES;
+                    }
+                    // 优化字幕场景识别
+                    var role = representation.Elements().Where(e => e.Name.LocalName == "Role").FirstOrDefault() ?? adaptationSet.Elements().Where(e => e.Name.LocalName == "Role").FirstOrDefault();
+                    if (role != null)
+                    {
+                        var v = role.Attribute("value")?.Value;
+                        if (Enum.TryParse(v, true, out RoleType roleType))
+                        {
+                            streamSpec.Role = roleType;
 
-                        if (mimeType == null)
-                        {
-                            mimeType = representation.Attribute("contentType")?.Value ?? representation.Attribute("mimeType")?.Value ?? "";
-                        }
-                        var bandwidth = representation.Attribute("bandwidth");
-                        StreamSpec streamSpec = new();
-                        streamSpec.OriginalUrl = ParserConfig.OriginalUrl;
-                        streamSpec.PeriodId = periodId;
-                        streamSpec.Playlist = new Playlist();
-                        streamSpec.Playlist.MediaParts.Add(new MediaPart());
-                        streamSpec.GroupId = representation.Attribute("id")?.Value;
-                        streamSpec.Bandwidth = Convert.ToInt32(bandwidth?.Value ?? "0");
-                        streamSpec.Codecs = representation.Attribute("codecs")?.Value ?? adaptationSet.Attribute("codecs")?.Value;
-                        streamSpec.Language = FilterLanguage(representation.Attribute("lang")?.Value ?? adaptationSet.Attribute("lang")?.Value);
-                        streamSpec.FrameRate = frameRate ?? GetFrameRate(representation);
-                        streamSpec.Resolution = representation.Attribute("width")?.Value != null ? $"{representation.Attribute("width")?.Value}x{representation.Attribute("height")?.Value}" : null;
-                        streamSpec.Url = MpdUrl;
-                        streamSpec.MediaType = mimeType.Split('/')[0] switch
-                        {
-                            "text" => MediaType.SUBTITLES,
-                            "audio" => MediaType.AUDIO,
-                            _ => null
-                        };
-                        //特殊处理
-                        if (representation.Attribute("volumeAdjust") != null)
-                        {
-                            streamSpec.GroupId += "-" + representation.Attribute("volumeAdjust")?.Value;
-                        }
-                        //推测后缀名
-                        var mType = representation.Attribute("mimeType")?.Value ?? adaptationSet.Attribute("mimeType")?.Value;
-                        if (mType != null)
-                        {
-                            var mTypeSplit = mType.Split('/');
-                            streamSpec.Extension = mTypeSplit.Length == 2 ? mTypeSplit[1] : null;
-                        }
-                        //优化字幕场景识别
-                        if (streamSpec.Codecs == "stpp" || streamSpec.Codecs == "wvtt")
-                        {
-                            streamSpec.MediaType = MediaType.SUBTITLES;
-                        }
-                        //优化字幕场景识别
-                        var role = representation.Elements().Where(e => e.Name.LocalName == "Role").FirstOrDefault() ?? adaptationSet.Elements().Where(e => e.Name.LocalName == "Role").FirstOrDefault();
-                        if (role != null)
-                        {
-                            var v = role.Attribute("value")?.Value;
-                            if (Enum.TryParse(v, true, out RoleType roleType))
+                            if (roleType == RoleType.Subtitle)
                             {
-                                streamSpec.Role = roleType;
-
-                                if (roleType == RoleType.Subtitle)
-                                {
-                                    streamSpec.MediaType = MediaType.SUBTITLES;
-                                    if (mType != null && mType.Contains("ttml"))
-                                        streamSpec.Extension = "ttml";
-                                }
+                                streamSpec.MediaType = MediaType.SUBTITLES;
+                                if (mType != null && mType.Contains("ttml"))
+                                    streamSpec.Extension = "ttml";
                             }
                         }
-                        streamSpec.Playlist.IsLive = isLive;
-                        //设置刷新间隔 timeShiftBufferDepth / 2
-                        if (timeShiftBufferDepth != null)
-                        {
-                            streamSpec.Playlist.RefreshIntervalMs = XmlConvert.ToTimeSpan(timeShiftBufferDepth).TotalMilliseconds / 2;
-                        }
+                    }
+                    streamSpec.Playlist.IsLive = isLive;
+                    // 设置刷新间隔 timeShiftBufferDepth / 2
+                    if (timeShiftBufferDepth != null)
+                    {
+                        streamSpec.Playlist.RefreshIntervalMs = XmlConvert.ToTimeSpan(timeShiftBufferDepth).TotalMilliseconds / 2;
+                    }
 
-                        //读取声道数量
-                        var audioChannelConfiguration = adaptationSet.Elements().Concat(representation.Elements()).Where(e => e.Name.LocalName == "AudioChannelConfiguration").FirstOrDefault();
-                        if (audioChannelConfiguration != null)
-                        {
-                            streamSpec.Channels = audioChannelConfiguration.Attribute("value")?.Value;
-                        }
+                    // 读取声道数量
+                    var audioChannelConfiguration = adaptationSet.Elements().Concat(representation.Elements()).Where(e => e.Name.LocalName == "AudioChannelConfiguration").FirstOrDefault();
+                    if (audioChannelConfiguration != null)
+                    {
+                        streamSpec.Channels = audioChannelConfiguration.Attribute("value")?.Value;
+                    }
 
-                        //发布时间
-                        if (!string.IsNullOrEmpty(publishTime))
-                        {
-                            streamSpec.PublishTime = DateTime.Parse(publishTime);
-                        }
+                    // 发布时间
+                    if (!string.IsNullOrEmpty(publishTime))
+                    {
+                        streamSpec.PublishTime = DateTime.Parse(publishTime);
+                    }
 
 
-                        //第一种形式 SegmentBase
-                        var segmentBaseElement = representation.Elements().Where(e => e.Name.LocalName == "SegmentBase").FirstOrDefault();
-                        if (segmentBaseElement != null)
+                    // 第一种形式 SegmentBase
+                    var segmentBaseElement = representation.Elements().Where(e => e.Name.LocalName == "SegmentBase").FirstOrDefault();
+                    if (segmentBaseElement != null)
+                    {
+                        // 处理init url
+                        var initialization = segmentBaseElement.Elements().Where(e => e.Name.LocalName == "Initialization").FirstOrDefault();
+                        if (initialization != null)
                         {
-                            //处理init url
-                            var initialization = segmentBaseElement.Elements().Where(e => e.Name.LocalName == "Initialization").FirstOrDefault();
-                            if (initialization != null)
+                            var sourceURL = initialization.Attribute("sourceURL")?.Value;
+                            if (sourceURL == null)
                             {
-                                var sourceURL = initialization.Attribute("sourceURL")?.Value;
-                                if (sourceURL == null)
-                                {
-                                    streamSpec.Playlist.MediaParts[0].MediaSegments.Add
-                                    (
-                                        new MediaSegment()
-                                        {
-                                            Index = 0,
-                                            Url = segBaseUrl,
-                                            Duration = XmlConvert.ToTimeSpan(periodDuration ?? mediaPresentationDuration ?? "PT0S").TotalSeconds
-                                        }
-                                    );
-                                }
-                                else
-                                {
-                                    var initUrl = ParserUtil.CombineURL(segBaseUrl, initialization.Attribute("sourceURL")?.Value!);
-                                    var initRange = initialization.Attribute("range")?.Value;
-                                    streamSpec.Playlist.MediaInit = new MediaSegment();
-                                    streamSpec.Playlist.MediaInit.Index = -1; //便于排序
-                                    streamSpec.Playlist.MediaInit.Url = initUrl;
-                                    if (initRange != null)
+                                streamSpec.Playlist.MediaParts[0].MediaSegments.Add
+                                (
+                                    new MediaSegment()
                                     {
-                                        var (start, expect) = ParserUtil.ParseRange(initRange);
-                                        streamSpec.Playlist.MediaInit.StartRange = start;
-                                        streamSpec.Playlist.MediaInit.ExpectLength = expect;
+                                        Index = 0,
+                                        Url = segBaseUrl,
+                                        Duration = XmlConvert.ToTimeSpan(periodDuration ?? mediaPresentationDuration ?? "PT0S").TotalSeconds
                                     }
-                                }
+                                );
                             }
-                        }
-
-                        //第二种形式 SegmentList.SegmentList
-                        var segmentList = representation.Elements().Where(e => e.Name.LocalName == "SegmentList").FirstOrDefault();
-                        if (segmentList != null)
-                        {
-                            var durationStr = segmentList.Attribute("duration")?.Value;
-                            //处理init url
-                            var initialization = segmentList.Elements().Where(e => e.Name.LocalName == "Initialization").FirstOrDefault();
-                            if (initialization != null)
+                            else
                             {
                                 var initUrl = ParserUtil.CombineURL(segBaseUrl, initialization.Attribute("sourceURL")?.Value!);
                                 var initRange = initialization.Attribute("range")?.Value;
                                 streamSpec.Playlist.MediaInit = new MediaSegment();
-                                streamSpec.Playlist.MediaInit.Index = -1; //便于排序
+                                streamSpec.Playlist.MediaInit.Index = -1; // 便于排序
                                 streamSpec.Playlist.MediaInit.Url = initUrl;
                                 if (initRange != null)
                                 {
@@ -281,355 +257,380 @@ namespace N_m3u8DL_RE.Parser.Extractor
                                     streamSpec.Playlist.MediaInit.ExpectLength = expect;
                                 }
                             }
-                            //处理分片
-                            var segmentURLs = segmentList.Elements().Where(e => e.Name.LocalName == "SegmentURL");
-                            var timescaleStr = segmentList.Attribute("timescale")?.Value ?? "1";
-                            for (int segmentIndex = 0; segmentIndex < segmentURLs.Count(); segmentIndex++)
+                        }
+                    }
+
+                    // 第二种形式 SegmentList.SegmentList
+                    var segmentList = representation.Elements().Where(e => e.Name.LocalName == "SegmentList").FirstOrDefault();
+                    if (segmentList != null)
+                    {
+                        var durationStr = segmentList.Attribute("duration")?.Value;
+                        // 处理init url
+                        var initialization = segmentList.Elements().Where(e => e.Name.LocalName == "Initialization").FirstOrDefault();
+                        if (initialization != null)
+                        {
+                            var initUrl = ParserUtil.CombineURL(segBaseUrl, initialization.Attribute("sourceURL")?.Value!);
+                            var initRange = initialization.Attribute("range")?.Value;
+                            streamSpec.Playlist.MediaInit = new MediaSegment();
+                            streamSpec.Playlist.MediaInit.Index = -1; // 便于排序
+                            streamSpec.Playlist.MediaInit.Url = initUrl;
+                            if (initRange != null)
                             {
-                                var segmentURL = segmentURLs.ElementAt(segmentIndex);
-                                var mediaUrl = ParserUtil.CombineURL(segBaseUrl, segmentURL.Attribute("media")?.Value!);
-                                var mediaRange = segmentURL.Attribute("mediaRange")?.Value;
-                                var timesacle = Convert.ToInt32(timescaleStr);
-                                var duration = Convert.ToInt64(durationStr);
-                                MediaSegment mediaSegment = new();
-                                mediaSegment.Duration = duration / (double)timesacle;
-                                mediaSegment.Url = mediaUrl;
-                                mediaSegment.Index = segmentIndex;
-                                if (mediaRange != null)
-                                {
-                                    var (start, expect) = ParserUtil.ParseRange(mediaRange);
-                                    mediaSegment.StartRange = start;
-                                    mediaSegment.ExpectLength = expect;
-                                }
-                                streamSpec.Playlist.MediaParts[0].MediaSegments.Add(mediaSegment);
+                                var (start, expect) = ParserUtil.ParseRange(initRange);
+                                streamSpec.Playlist.MediaInit.StartRange = start;
+                                streamSpec.Playlist.MediaInit.ExpectLength = expect;
                             }
                         }
-
-                        //第三种形式 SegmentTemplate+SegmentTimeline
-                        //通配符有$RepresentationID$ $Bandwidth$ $Number$ $Time$
-
-                        //adaptationSets中的segmentTemplate
-                        var segmentTemplateElementsOuter = adaptationSet.Elements().Where(e => e.Name.LocalName == "SegmentTemplate");
-                        //representation中的segmentTemplate
-                        var segmentTemplateElements = representation.Elements().Where(e => e.Name.LocalName == "SegmentTemplate");
-                        if (segmentTemplateElements.Any() || segmentTemplateElementsOuter.Any())
+                        // 处理分片
+                        var segmentURLs = segmentList.Elements().Where(e => e.Name.LocalName == "SegmentURL");
+                        var timescaleStr = segmentList.Attribute("timescale")?.Value ?? "1";
+                        for (int segmentIndex = 0; segmentIndex < segmentURLs.Count(); segmentIndex++)
                         {
-                            //优先使用最近的元素
-                            var segmentTemplate = (segmentTemplateElements.FirstOrDefault() ?? segmentTemplateElementsOuter.FirstOrDefault())!;
-                            var segmentTemplateOuter = (segmentTemplateElementsOuter.FirstOrDefault() ?? segmentTemplateElements.FirstOrDefault())!;
-                            var varDic = new Dictionary<string, object?>();
-                            varDic[DASHTags.TemplateRepresentationID] = streamSpec.GroupId;
-                            varDic[DASHTags.TemplateBandwidth] = bandwidth?.Value;
-                            //presentationTimeOffset
-                            var presentationTimeOffsetStr = segmentTemplate.Attribute("presentationTimeOffset")?.Value ?? segmentTemplateOuter.Attribute("presentationTimeOffset")?.Value ?? "0";
-                            //timesacle
-                            var timescaleStr = segmentTemplate.Attribute("timescale")?.Value ?? segmentTemplateOuter.Attribute("timescale")?.Value ?? "1";
-                            var durationStr = segmentTemplate.Attribute("duration")?.Value ?? segmentTemplateOuter.Attribute("duration")?.Value;
-                            var startNumberStr = segmentTemplate.Attribute("startNumber")?.Value ?? segmentTemplateOuter.Attribute("startNumber")?.Value ?? "1";
-                            //处理init url
-                            var initialization = segmentTemplate.Attribute("initialization")?.Value ?? segmentTemplateOuter.Attribute("initialization")?.Value;
-                            if (initialization != null)
+                            var segmentURL = segmentURLs.ElementAt(segmentIndex);
+                            var mediaUrl = ParserUtil.CombineURL(segBaseUrl, segmentURL.Attribute("media")?.Value!);
+                            var mediaRange = segmentURL.Attribute("mediaRange")?.Value;
+                            var timesacle = Convert.ToInt32(timescaleStr);
+                            var duration = Convert.ToInt64(durationStr);
+                            MediaSegment mediaSegment = new();
+                            mediaSegment.Duration = duration / (double)timesacle;
+                            mediaSegment.Url = mediaUrl;
+                            mediaSegment.Index = segmentIndex;
+                            if (mediaRange != null)
                             {
-                                var _init = ParserUtil.ReplaceVars(initialization, varDic);
-                                var initUrl = ParserUtil.CombineURL(segBaseUrl, _init);
-                                streamSpec.Playlist.MediaInit = new MediaSegment();
-                                streamSpec.Playlist.MediaInit.Index = -1; //便于排序
-                                streamSpec.Playlist.MediaInit.Url = initUrl;
+                                var (start, expect) = ParserUtil.ParseRange(mediaRange);
+                                mediaSegment.StartRange = start;
+                                mediaSegment.ExpectLength = expect;
                             }
-                            //处理分片
-                            var mediaTemplate = segmentTemplate.Attribute("media")?.Value ?? segmentTemplateOuter.Attribute("media")?.Value;
-                            var segmentTimeline = segmentTemplate.Elements().Where(e => e.Name.LocalName == "SegmentTimeline").FirstOrDefault();
-                            if (segmentTimeline != null)
-                            {
-                                //使用了SegmentTimeline 结果精确
-                                var segNumber = Convert.ToInt64(startNumberStr);
-                                var Ss = segmentTimeline.Elements().Where(e => e.Name.LocalName == "S");
-                                var currentTime = 0L;
-                                var segIndex = 0;
-                                foreach (var S in Ss)
-                                {
-                                    //每个S元素包含三个属性:@t(start time)\@r(repeat count)\@d(duration)
-                                    var _startTimeStr = S.Attribute("t")?.Value;
-                                    var _durationStr = S.Attribute("d")?.Value;
-                                    var _repeatCountStr = S.Attribute("r")?.Value;
+                            streamSpec.Playlist.MediaParts[0].MediaSegments.Add(mediaSegment);
+                        }
+                    }
 
-                                    if (_startTimeStr != null) currentTime = Convert.ToInt64(_startTimeStr);
-                                    var _duration = Convert.ToInt64(_durationStr);
-                                    var timescale = Convert.ToInt32(timescaleStr);
-                                    var _repeatCount = Convert.ToInt64(_repeatCountStr);
+                    // 第三种形式 SegmentTemplate+SegmentTimeline
+                    // 通配符有$RepresentationID$ $Bandwidth$ $Number$ $Time$
+
+                    // adaptationSets中的segmentTemplate
+                    var segmentTemplateElementsOuter = adaptationSet.Elements().Where(e => e.Name.LocalName == "SegmentTemplate");
+                    // representation中的segmentTemplate
+                    var segmentTemplateElements = representation.Elements().Where(e => e.Name.LocalName == "SegmentTemplate");
+                    if (segmentTemplateElements.Any() || segmentTemplateElementsOuter.Any())
+                    {
+                        // 优先使用最近的元素
+                        var segmentTemplate = (segmentTemplateElements.FirstOrDefault() ?? segmentTemplateElementsOuter.FirstOrDefault())!;
+                        var segmentTemplateOuter = (segmentTemplateElementsOuter.FirstOrDefault() ?? segmentTemplateElements.FirstOrDefault())!;
+                        var varDic = new Dictionary<string, object?>();
+                        varDic[DASHTags.TemplateRepresentationID] = streamSpec.GroupId;
+                        varDic[DASHTags.TemplateBandwidth] = bandwidth?.Value;
+                        // presentationTimeOffset
+                        var presentationTimeOffsetStr = segmentTemplate.Attribute("presentationTimeOffset")?.Value ?? segmentTemplateOuter.Attribute("presentationTimeOffset")?.Value ?? "0";
+                        // timesacle
+                        var timescaleStr = segmentTemplate.Attribute("timescale")?.Value ?? segmentTemplateOuter.Attribute("timescale")?.Value ?? "1";
+                        var durationStr = segmentTemplate.Attribute("duration")?.Value ?? segmentTemplateOuter.Attribute("duration")?.Value;
+                        var startNumberStr = segmentTemplate.Attribute("startNumber")?.Value ?? segmentTemplateOuter.Attribute("startNumber")?.Value ?? "1";
+                        // 处理init url
+                        var initialization = segmentTemplate.Attribute("initialization")?.Value ?? segmentTemplateOuter.Attribute("initialization")?.Value;
+                        if (initialization != null)
+                        {
+                            var _init = ParserUtil.ReplaceVars(initialization, varDic);
+                            var initUrl = ParserUtil.CombineURL(segBaseUrl, _init);
+                            streamSpec.Playlist.MediaInit = new MediaSegment();
+                            streamSpec.Playlist.MediaInit.Index = -1; // 便于排序
+                            streamSpec.Playlist.MediaInit.Url = initUrl;
+                        }
+                        // 处理分片
+                        var mediaTemplate = segmentTemplate.Attribute("media")?.Value ?? segmentTemplateOuter.Attribute("media")?.Value;
+                        var segmentTimeline = segmentTemplate.Elements().Where(e => e.Name.LocalName == "SegmentTimeline").FirstOrDefault();
+                        if (segmentTimeline != null)
+                        {
+                            // 使用了SegmentTimeline 结果精确
+                            var segNumber = Convert.ToInt64(startNumberStr);
+                            var Ss = segmentTimeline.Elements().Where(e => e.Name.LocalName == "S");
+                            var currentTime = 0L;
+                            var segIndex = 0;
+                            foreach (var S in Ss)
+                            {
+                                // 每个S元素包含三个属性:@t(start time)\@r(repeat count)\@d(duration)
+                                var _startTimeStr = S.Attribute("t")?.Value;
+                                var _durationStr = S.Attribute("d")?.Value;
+                                var _repeatCountStr = S.Attribute("r")?.Value;
+
+                                if (_startTimeStr != null) currentTime = Convert.ToInt64(_startTimeStr);
+                                var _duration = Convert.ToInt64(_durationStr);
+                                var timescale = Convert.ToInt32(timescaleStr);
+                                var _repeatCount = Convert.ToInt64(_repeatCountStr);
+                                varDic[DASHTags.TemplateTime] = currentTime;
+                                varDic[DASHTags.TemplateNumber] = segNumber++;
+                                var hasTime = mediaTemplate!.Contains(DASHTags.TemplateTime);
+                                var media = ParserUtil.ReplaceVars(mediaTemplate!, varDic);
+                                var mediaUrl = ParserUtil.CombineURL(segBaseUrl, media!);
+                                MediaSegment mediaSegment = new();
+                                mediaSegment.Url = mediaUrl;
+                                if (hasTime)
+                                    mediaSegment.NameFromVar = currentTime.ToString();
+                                mediaSegment.Duration = _duration / (double)timescale;
+                                mediaSegment.Index = segIndex++;
+                                streamSpec.Playlist.MediaParts[0].MediaSegments.Add(mediaSegment);
+                                if (_repeatCount < 0)
+                                {
+                                    // 负数表示一直重复 直到period结束 注意减掉已经加入的1个片段
+                                    _repeatCount = (long)Math.Ceiling(XmlConvert.ToTimeSpan(periodDuration ?? mediaPresentationDuration ?? "PT0S").TotalSeconds * timescale / _duration) - 1;
+                                }
+                                for (long i = 0; i < _repeatCount; i++)
+                                {
+                                    currentTime += _duration;
+                                    MediaSegment _mediaSegment = new();
                                     varDic[DASHTags.TemplateTime] = currentTime;
                                     varDic[DASHTags.TemplateNumber] = segNumber++;
-                                    var hasTime = mediaTemplate!.Contains(DASHTags.TemplateTime);
-                                    var media = ParserUtil.ReplaceVars(mediaTemplate!, varDic);
-                                    var mediaUrl = ParserUtil.CombineURL(segBaseUrl, media!);
-                                    MediaSegment mediaSegment = new();
-                                    mediaSegment.Url = mediaUrl;
-                                    if (hasTime)
-                                        mediaSegment.NameFromVar = currentTime.ToString();
-                                    mediaSegment.Duration = _duration / (double)timescale;
-                                    mediaSegment.Index = segIndex++;
-                                    streamSpec.Playlist.MediaParts[0].MediaSegments.Add(mediaSegment);
-                                    if (_repeatCount < 0)
-                                    {
-                                        //负数表示一直重复 直到period结束 注意减掉已经加入的1个片段
-                                        _repeatCount = (long)Math.Ceiling(XmlConvert.ToTimeSpan(periodDuration ?? mediaPresentationDuration ?? "PT0S").TotalSeconds * timescale / _duration) - 1;
-                                    }
-                                    for (long i = 0; i < _repeatCount; i++)
-                                    {
-                                        currentTime += _duration;
-                                        MediaSegment _mediaSegment = new();
-                                        varDic[DASHTags.TemplateTime] = currentTime;
-                                        varDic[DASHTags.TemplateNumber] = segNumber++;
-                                        var _hashTime = mediaTemplate!.Contains(DASHTags.TemplateTime);
-                                        var _media = ParserUtil.ReplaceVars(mediaTemplate!, varDic);
-                                        var _mediaUrl = ParserUtil.CombineURL(segBaseUrl, _media);
-                                        _mediaSegment.Url = _mediaUrl;
-                                        _mediaSegment.Index = segIndex++;
-                                        _mediaSegment.Duration = _duration / (double)timescale;
-                                        if (_hashTime)
-                                            _mediaSegment.NameFromVar = currentTime.ToString();
-                                        streamSpec.Playlist.MediaParts[0].MediaSegments.Add(_mediaSegment);
-                                    }
-                                    currentTime += _duration;
-                                }
-                            }
-                            else
-                            {
-                                //没用SegmentTimeline 需要计算总分片数量 不精确
-                                var timescale = Convert.ToInt32(timescaleStr);
-                                var startNumber = Convert.ToInt64(startNumberStr);
-                                var duration = Convert.ToInt32(durationStr);
-                                var totalNumber = (long)Math.Ceiling(XmlConvert.ToTimeSpan(periodDuration ?? mediaPresentationDuration ?? "PT0S").TotalSeconds * timescale / duration);
-                                //直播的情况,需要自己计算totalNumber
-                                if (totalNumber == 0 && isLive)
-                                {
-                                    var now = DateTime.Now;
-                                    var availableTime = DateTime.Parse(availabilityStartTime!);
-                                    //可用时间+偏移量
-                                    var offsetMs = TimeSpan.FromMilliseconds(Convert.ToInt64(presentationTimeOffsetStr) / 1000);
-                                    availableTime = availableTime.Add(offsetMs);
-                                    var ts = now - availableTime;
-                                    var updateTs = XmlConvert.ToTimeSpan(timeShiftBufferDepth!);
-                                    //(当前时间到发布时间的时间差 - 最小刷新间隔) / 分片时长
-                                    startNumber += (long)((ts.TotalSeconds - updateTs.TotalSeconds) * timescale / duration);
-                                    totalNumber = (long)(updateTs.TotalSeconds * timescale / duration);
-                                }
-                                for (long index = startNumber, segIndex = 0; index < startNumber + totalNumber; index++, segIndex++)
-                                {
-                                    varDic[DASHTags.TemplateNumber] = index;
-                                    var hasNumber = mediaTemplate!.Contains(DASHTags.TemplateNumber);
-                                    var media = ParserUtil.ReplaceVars(mediaTemplate!, varDic);
-                                    var mediaUrl = ParserUtil.CombineURL(segBaseUrl, media!);
-                                    MediaSegment mediaSegment = new();
-                                    mediaSegment.Url = mediaUrl;
-                                    if (hasNumber)
-                                        mediaSegment.NameFromVar = index.ToString();
-                                    mediaSegment.Index = isLive ? index : segIndex; //直播直接用startNumber
-                                    mediaSegment.Duration = duration / (double)timescale;
-                                    streamSpec.Playlist.MediaParts[0].MediaSegments.Add(mediaSegment);
-                                }
-                            }
-                        }
-
-                        //如果依旧没被添加分片,直接把BaseUrl塞进去就好
-                        if (streamSpec.Playlist.MediaParts[0].MediaSegments.Count == 0)
-                        {
-                            streamSpec.Playlist.MediaParts[0].MediaSegments.Add
-                                    (
-                                        new MediaSegment()
-                                        {
-                                            Index = 0,
-                                            Url = segBaseUrl,
-                                            Duration = XmlConvert.ToTimeSpan(periodDuration ?? mediaPresentationDuration ?? "PT0S").TotalSeconds
-                                        }
-                                    );
-                        }
-
-                        //判断加密情况
-                        if (adaptationSet.Elements().Concat(representation.Elements()).Any(e => e.Name.LocalName == "ContentProtection"))
-                        {
-                            if (streamSpec.Playlist.MediaInit != null)
-                            {
-                                streamSpec.Playlist.MediaInit.EncryptInfo.Method = DEFAULT_METHOD;
-                            }
-                            foreach (var item in streamSpec.Playlist.MediaParts[0].MediaSegments)
-                            {
-                                item.EncryptInfo.Method = DEFAULT_METHOD;
-                            }
-                        }
-
-                        //处理同一ID分散在不同Period的情况
-                        var _index = streamList.FindIndex(_f => _f.PeriodId != streamSpec.PeriodId && _f.GroupId == streamSpec.GroupId && _f.Resolution == streamSpec.Resolution && _f.MediaType == streamSpec.MediaType);
-                        if (_index > -1)
-                        {
-                            if (isLive)
-                            {
-                                //直播,这种情况直接略过新的
-                            }
-                            else
-                            {
-                                //点播,这种情况如果URL不同则作为新的part出现,否则仅把时间加起来
-                                var url1 = streamList[_index].Playlist!.MediaParts.Last().MediaSegments.Last().Url;
-                                var url2 = streamSpec.Playlist.MediaParts[0].MediaSegments.LastOrDefault()?.Url;
-                                if (url1 != url2)
-                                {
-                                    var startIndex = streamList[_index].Playlist!.MediaParts.Last().MediaSegments.Last().Index + 1;
-                                    var enumerator = streamSpec.Playlist.MediaParts[0].MediaSegments.GetEnumerator();
-                                    while (enumerator.MoveNext())
-                                    {
-                                        enumerator.Current.Index += startIndex;
-                                    }
-                                    streamList[_index].Playlist!.MediaParts.Add(new MediaPart()
-                                    {
-                                        MediaSegments = streamSpec.Playlist.MediaParts[0].MediaSegments
-                                    });
-                                }
-                                else
-                                {
-                                    streamList[_index].Playlist!.MediaParts.Last().MediaSegments.Last().Duration += streamSpec.Playlist.MediaParts[0].MediaSegments.Sum(x => x.Duration);
+                                    var _hashTime = mediaTemplate!.Contains(DASHTags.TemplateTime);
+                                    var _media = ParserUtil.ReplaceVars(mediaTemplate!, varDic);
+                                    var _mediaUrl = ParserUtil.CombineURL(segBaseUrl, _media);
+                                    _mediaSegment.Url = _mediaUrl;
+                                    _mediaSegment.Index = segIndex++;
+                                    _mediaSegment.Duration = _duration / (double)timescale;
+                                    if (_hashTime)
+                                        _mediaSegment.NameFromVar = currentTime.ToString();
+                                    streamSpec.Playlist.MediaParts[0].MediaSegments.Add(_mediaSegment);
                                 }
+                                currentTime += _duration;
                             }
                         }
                         else
                         {
-                            //修复mp4类型字幕
-                            if (streamSpec.MediaType == MediaType.SUBTITLES && streamSpec.Extension == "mp4")
+                            // 没用SegmentTimeline 需要计算总分片数量 不精确
+                            var timescale = Convert.ToInt32(timescaleStr);
+                            var startNumber = Convert.ToInt64(startNumberStr);
+                            var duration = Convert.ToInt32(durationStr);
+                            var totalNumber = (long)Math.Ceiling(XmlConvert.ToTimeSpan(periodDuration ?? mediaPresentationDuration ?? "PT0S").TotalSeconds * timescale / duration);
+                            // 直播的情况,需要自己计算totalNumber
+                            if (totalNumber == 0 && isLive)
                             {
-                                streamSpec.Extension = "m4s";
+                                var now = DateTime.Now;
+                                var availableTime = DateTime.Parse(availabilityStartTime!);
+                                // 可用时间+偏移量
+                                var offsetMs = TimeSpan.FromMilliseconds(Convert.ToInt64(presentationTimeOffsetStr) / 1000);
+                                availableTime = availableTime.Add(offsetMs);
+                                var ts = now - availableTime;
+                                var updateTs = XmlConvert.ToTimeSpan(timeShiftBufferDepth!);
+                                // (当前时间到发布时间的时间差 - 最小刷新间隔) / 分片时长
+                                startNumber += (long)((ts.TotalSeconds - updateTs.TotalSeconds) * timescale / duration);
+                                totalNumber = (long)(updateTs.TotalSeconds * timescale / duration);
                             }
-                            //分片默认后缀m4s
-                            if (streamSpec.MediaType != MediaType.SUBTITLES && (streamSpec.Extension == null || streamSpec.Playlist.MediaParts.Sum(x => x.MediaSegments.Count) > 1))
+                            for (long index = startNumber, segIndex = 0; index < startNumber + totalNumber; index++, segIndex++)
                             {
-                                streamSpec.Extension = "m4s";
+                                varDic[DASHTags.TemplateNumber] = index;
+                                var hasNumber = mediaTemplate!.Contains(DASHTags.TemplateNumber);
+                                var media = ParserUtil.ReplaceVars(mediaTemplate!, varDic);
+                                var mediaUrl = ParserUtil.CombineURL(segBaseUrl, media!);
+                                MediaSegment mediaSegment = new();
+                                mediaSegment.Url = mediaUrl;
+                                if (hasNumber)
+                                    mediaSegment.NameFromVar = index.ToString();
+                                mediaSegment.Index = isLive ? index : segIndex; // 直播直接用startNumber
+                                mediaSegment.Duration = duration / (double)timescale;
+                                streamSpec.Playlist.MediaParts[0].MediaSegments.Add(mediaSegment);
                             }
-                            streamList.Add(streamSpec);
                         }
-                        //恢复BaseURL相对位置
-                        segBaseUrl = representationsBaseUrl;
                     }
-                    //恢复BaseURL相对位置
-                    segBaseUrl = adaptationSetsBaseUrl;
-                }
-            }
 
-            //为视频设置默认轨道
-            var aL = streamList.Where(s => s.MediaType == MediaType.AUDIO);
-            var sL = streamList.Where(s => s.MediaType == MediaType.SUBTITLES);
-            foreach (var item in streamList)
-            {
-                if (!string.IsNullOrEmpty(item.Resolution))
-                {
-                    if (aL.Any())
+                    // 如果依旧没被添加分片,直接把BaseUrl塞进去就好
+                    if (streamSpec.Playlist.MediaParts[0].MediaSegments.Count == 0)
                     {
-                        item.AudioId = aL.OrderByDescending(x => x.Bandwidth).First().GroupId;
+                        streamSpec.Playlist.MediaParts[0].MediaSegments.Add
+                        (
+                            new MediaSegment()
+                            {
+                                Index = 0,
+                                Url = segBaseUrl,
+                                Duration = XmlConvert.ToTimeSpan(periodDuration ?? mediaPresentationDuration ?? "PT0S").TotalSeconds
+                            }
+                        );
                     }
-                    if (sL.Any())
+
+                    // 判断加密情况
+                    if (adaptationSet.Elements().Concat(representation.Elements()).Any(e => e.Name.LocalName == "ContentProtection"))
                     {
-                        item.SubtitleId = sL.OrderByDescending(x => x.Bandwidth).First().GroupId;
-                    }
-                }
-            }
-
-            return streamList;
-        }
-
-        /// <summary>
-        /// 如果有非法字符 返回und
-        /// </summary>
-        /// <param name="v"></param>
-        /// <returns></returns>
-        private string? FilterLanguage(string? v)
-        {
-            if (v == null) return null;
-            if (Regex.IsMatch(v, "^[\\w_\\-\\d]+$")) return v;
-            return "und";
-        }
-
-        public async Task RefreshPlayListAsync(List<StreamSpec> streamSpecs)
-        {
-            if (streamSpecs.Count == 0) return;
-
-            var (rawText, url) = ("", ParserConfig.Url);
-            try
-            {
-                (rawText, url) = await HTTPUtil.GetWebSourceAndNewUrlAsync(ParserConfig.Url, ParserConfig.Headers);
-            }
-            catch (HttpRequestException) when (ParserConfig.Url!= ParserConfig.OriginalUrl)
-            {
-                //当URL无法访问时,再请求原始URL
-                (rawText, url) = await HTTPUtil.GetWebSourceAndNewUrlAsync(ParserConfig.OriginalUrl, ParserConfig.Headers);
-            }
-
-            ParserConfig.Url = url;
-            SetInitUrl();
-
-            var newStreams = await ExtractStreamsAsync(rawText);
-            foreach (var streamSpec in streamSpecs)
-            {
-                //有的网站每次请求MPD返回的码率不一致,导致ToShortString()无法匹配 无法更新playlist
-                //故增加通过init url来匹配 (如果有的话)
-                var match = newStreams.Where(n => n.ToShortString() == streamSpec.ToShortString());
-                if (!match.Any())
-                    match = newStreams.Where(n => n.Playlist?.MediaInit?.Url == streamSpec.Playlist?.MediaInit?.Url);
-
-                if (match.Any())
-                    streamSpec.Playlist!.MediaParts = match.First().Playlist!.MediaParts; //不更新init
-            }
-            //这里才调用URL预处理器,节省开销
-            await ProcessUrlAsync(streamSpecs);
-        }
-
-        private async Task ProcessUrlAsync(List<StreamSpec> streamSpecs)
-        {
-            for (int i = 0; i < streamSpecs.Count; i++)
-            {
-                var playlist = streamSpecs[i].Playlist;
-                if (playlist != null)
-                {
-                    if (playlist.MediaInit != null)
-                    {
-                        playlist.MediaInit!.Url = PreProcessUrl(playlist.MediaInit!.Url);
-                    }
-                    for (int ii = 0; ii < playlist!.MediaParts.Count; ii++)
-                    {
-                        var part = playlist.MediaParts[ii];
-                        for (int iii = 0; iii < part.MediaSegments.Count; iii++)
+                        if (streamSpec.Playlist.MediaInit != null)
                         {
-                            part.MediaSegments[iii].Url = PreProcessUrl(part.MediaSegments[iii].Url);
+                            streamSpec.Playlist.MediaInit.EncryptInfo.Method = DEFAULT_METHOD;
                         }
+                        foreach (var item in streamSpec.Playlist.MediaParts[0].MediaSegments)
+                        {
+                            item.EncryptInfo.Method = DEFAULT_METHOD;
+                        }
+                    }
+
+                    // 处理同一ID分散在不同Period的情况
+                    var _index = streamList.FindIndex(_f => _f.PeriodId != streamSpec.PeriodId && _f.GroupId == streamSpec.GroupId && _f.Resolution == streamSpec.Resolution && _f.MediaType == streamSpec.MediaType);
+                    if (_index > -1)
+                    {
+                        if (isLive)
+                        {
+                            // 直播,这种情况直接略过新的
+                        }
+                        else
+                        {
+                            // 点播,这种情况如果URL不同则作为新的part出现,否则仅把时间加起来
+                            var url1 = streamList[_index].Playlist!.MediaParts.Last().MediaSegments.Last().Url;
+                            var url2 = streamSpec.Playlist.MediaParts[0].MediaSegments.LastOrDefault()?.Url;
+                            if (url1 != url2)
+                            {
+                                var startIndex = streamList[_index].Playlist!.MediaParts.Last().MediaSegments.Last().Index + 1;
+                                var enumerator = streamSpec.Playlist.MediaParts[0].MediaSegments.GetEnumerator();
+                                while (enumerator.MoveNext())
+                                {
+                                    enumerator.Current.Index += startIndex;
+                                }
+                                streamList[_index].Playlist!.MediaParts.Add(new MediaPart()
+                                {
+                                    MediaSegments = streamSpec.Playlist.MediaParts[0].MediaSegments
+                                });
+                            }
+                            else
+                            {
+                                streamList[_index].Playlist!.MediaParts.Last().MediaSegments.Last().Duration += streamSpec.Playlist.MediaParts[0].MediaSegments.Sum(x => x.Duration);
+                            }
+                        }
+                    }
+                    else
+                    {
+                        // 修复mp4类型字幕
+                        if (streamSpec.MediaType == MediaType.SUBTITLES && streamSpec.Extension == "mp4")
+                        {
+                            streamSpec.Extension = "m4s";
+                        }
+                        // 分片默认后缀m4s
+                        if (streamSpec.MediaType != MediaType.SUBTITLES && (streamSpec.Extension == null || streamSpec.Playlist.MediaParts.Sum(x => x.MediaSegments.Count) > 1))
+                        {
+                            streamSpec.Extension = "m4s";
+                        }
+                        streamList.Add(streamSpec);
+                    }
+                    // 恢复BaseURL相对位置
+                    segBaseUrl = representationsBaseUrl;
+                }
+                // 恢复BaseURL相对位置
+                segBaseUrl = adaptationSetsBaseUrl;
+            }
+        }
+
+        // 为视频设置默认轨道
+        var aL = streamList.Where(s => s.MediaType == MediaType.AUDIO);
+        var sL = streamList.Where(s => s.MediaType == MediaType.SUBTITLES);
+        foreach (var item in streamList)
+        {
+            if (!string.IsNullOrEmpty(item.Resolution))
+            {
+                if (aL.Any())
+                {
+                    item.AudioId = aL.OrderByDescending(x => x.Bandwidth).First().GroupId;
+                }
+                if (sL.Any())
+                {
+                    item.SubtitleId = sL.OrderByDescending(x => x.Bandwidth).First().GroupId;
+                }
+            }
+        }
+
+        return Task.FromResult(streamList);
+    }
+
+    /// <summary>
+    /// 如果有非法字符 返回und
+    /// </summary>
+    /// <param name="v"></param>
+    /// <returns></returns>
+    private string? FilterLanguage(string? v)
+    {
+        if (v == null) return null;
+        if (Regex.IsMatch(v, "^[\\w_\\-\\d]+$")) return v;
+        return "und";
+    }
+
+    public async Task RefreshPlayListAsync(List<StreamSpec> streamSpecs)
+    {
+        if (streamSpecs.Count == 0) return;
+
+        var (rawText, url) = ("", ParserConfig.Url);
+        try
+        {
+            (rawText, url) = await HTTPUtil.GetWebSourceAndNewUrlAsync(ParserConfig.Url, ParserConfig.Headers);
+        }
+        catch (HttpRequestException) when (ParserConfig.Url!= ParserConfig.OriginalUrl)
+        {
+            // 当URL无法访问时,再请求原始URL
+            (rawText, url) = await HTTPUtil.GetWebSourceAndNewUrlAsync(ParserConfig.OriginalUrl, ParserConfig.Headers);
+        }
+
+        ParserConfig.Url = url;
+        SetInitUrl();
+
+        var newStreams = await ExtractStreamsAsync(rawText);
+        foreach (var streamSpec in streamSpecs)
+        {
+            // 有的网站每次请求MPD返回的码率不一致,导致ToShortString()无法匹配 无法更新playlist
+            // 故增加通过init url来匹配 (如果有的话)
+            var match = newStreams.Where(n => n.ToShortString() == streamSpec.ToShortString());
+            if (!match.Any())
+                match = newStreams.Where(n => n.Playlist?.MediaInit?.Url == streamSpec.Playlist?.MediaInit?.Url);
+
+            if (match.Any())
+                streamSpec.Playlist!.MediaParts = match.First().Playlist!.MediaParts; // 不更新init
+        }
+        // 这里才调用URL预处理器,节省开销
+        await ProcessUrlAsync(streamSpecs);
+    }
+
+    private Task ProcessUrlAsync(List<StreamSpec> streamSpecs)
+    {
+        for (int i = 0; i < streamSpecs.Count; i++)
+        {
+            var playlist = streamSpecs[i].Playlist;
+            if (playlist != null)
+            {
+                if (playlist.MediaInit != null)
+                {
+                    playlist.MediaInit!.Url = PreProcessUrl(playlist.MediaInit!.Url);
+                }
+                for (int ii = 0; ii < playlist!.MediaParts.Count; ii++)
+                {
+                    var part = playlist.MediaParts[ii];
+                    for (int iii = 0; iii < part.MediaSegments.Count; iii++)
+                    {
+                        part.MediaSegments[iii].Url = PreProcessUrl(part.MediaSegments[iii].Url);
                     }
                 }
             }
         }
 
-        public async Task FetchPlayListAsync(List<StreamSpec> streamSpecs)
-        {
-            //这里才调用URL预处理器,节省开销
-            await ProcessUrlAsync(streamSpecs);
-        }
+        return Task.CompletedTask;
+    }
 
-        public string PreProcessUrl(string url)
+    public async Task FetchPlayListAsync(List<StreamSpec> streamSpecs)
+    {
+        // 这里才调用URL预处理器,节省开销
+        await ProcessUrlAsync(streamSpecs);
+    }
+
+    public string PreProcessUrl(string url)
+    {
+        foreach (var p in ParserConfig.UrlProcessors)
         {
-            foreach (var p in ParserConfig.UrlProcessors)
+            if (p.CanProcess(ExtractorType, url, ParserConfig))
             {
-                if (p.CanProcess(ExtractorType, url, ParserConfig))
-                {
-                    url = p.Process(url, ParserConfig);
-                }
+                url = p.Process(url, ParserConfig);
             }
-
-            return url;
         }
 
-        public void PreProcessContent()
+        return url;
+    }
+
+    public void PreProcessContent()
+    {
+        foreach (var p in ParserConfig.ContentProcessors)
         {
-            foreach (var p in ParserConfig.ContentProcessors)
+            if (p.CanProcess(ExtractorType, MpdContent, ParserConfig))
             {
-                if (p.CanProcess(ExtractorType, MpdContent, ParserConfig))
-                {
-                    MpdContent = p.Process(MpdContent, ParserConfig);
-                }
+                MpdContent = p.Process(MpdContent, ParserConfig);
             }
         }
     }
-}
+}
\ No newline at end of file
diff --git a/src/N_m3u8DL-RE.Parser/Extractor/HLSExtractor.cs b/src/N_m3u8DL-RE.Parser/Extractor/HLSExtractor.cs
index 4f49da6..3f60fe5 100644
--- a/src/N_m3u8DL-RE.Parser/Extractor/HLSExtractor.cs
+++ b/src/N_m3u8DL-RE.Parser/Extractor/HLSExtractor.cs
@@ -13,341 +13,379 @@ using System.Text.RegularExpressions;
 using System.Threading.Tasks;
 using N_m3u8DL_RE.Common.Util;
 
-namespace N_m3u8DL_RE.Parser.Extractor
+namespace N_m3u8DL_RE.Parser.Extractor;
+
+internal class HLSExtractor : IExtractor
 {
-    internal class HLSExtractor : IExtractor
+    public ExtractorType ExtractorType => ExtractorType.HLS;
+
+    private string M3u8Url = string.Empty;
+    private string BaseUrl = string.Empty;
+    private string M3u8Content = string.Empty;
+    private bool MasterM3u8Flag = false;
+
+    public ParserConfig ParserConfig { get; set; }
+
+    private HLSExtractor() { }
+
+    public HLSExtractor(ParserConfig parserConfig)
     {
-        public ExtractorType ExtractorType => ExtractorType.HLS;
+        this.ParserConfig = parserConfig;
+        this.M3u8Url = parserConfig.Url ?? string.Empty;
+        this.SetBaseUrl();
+    }
 
-        private string M3u8Url = string.Empty;
-        private string BaseUrl = string.Empty;
-        private string M3u8Content = string.Empty;
-        private bool MasterM3u8Flag = false;
-
-        public ParserConfig ParserConfig { get; set; }
-
-        private HLSExtractor() { }
-
-        public HLSExtractor(ParserConfig parserConfig)
+    private void SetBaseUrl()
+    {
+        if (!string.IsNullOrEmpty(ParserConfig.BaseUrl))
         {
-            this.ParserConfig = parserConfig;
-            this.M3u8Url = parserConfig.Url ?? string.Empty;
-            this.SetBaseUrl();
+            this.BaseUrl = ParserConfig.BaseUrl;
+        }
+        else
+        {
+            this.BaseUrl = this.M3u8Url;
+        }
+    }
+
+    /// <summary>
+    /// 预处理m3u8内容
+    /// </summary>
+    public void PreProcessContent()
+    {
+        M3u8Content = M3u8Content.Trim();
+        if (!M3u8Content.StartsWith(HLSTags.ext_m3u))
+        {
+            throw new Exception(ResString.badM3u8);
         }
 
-        private void SetBaseUrl()
+        foreach (var p in ParserConfig.ContentProcessors)
         {
-            if (!string.IsNullOrEmpty(ParserConfig.BaseUrl))
+            if (p.CanProcess(ExtractorType, M3u8Content, ParserConfig))
             {
-                this.BaseUrl = ParserConfig.BaseUrl;
+                M3u8Content = p.Process(M3u8Content, ParserConfig);
             }
-            else
+        }
+    }
+
+    /// <summary>
+    /// 预处理URL
+    /// </summary>
+    public string PreProcessUrl(string url)
+    {
+        foreach (var p in ParserConfig.UrlProcessors)
+        {
+            if (p.CanProcess(ExtractorType, url, ParserConfig))
             {
-                this.BaseUrl = this.M3u8Url;
+                url = p.Process(url, ParserConfig);
             }
         }
 
-        /// <summary>
-        /// 预处理m3u8内容
-        /// </summary>
-        public void PreProcessContent()
+        return url;
+    }
+
+    private Task<List<StreamSpec>> ParseMasterListAsync()
+    {
+        MasterM3u8Flag = true;
+
+        List<StreamSpec> streams = new List<StreamSpec>();
+
+        using StringReader sr = new StringReader(M3u8Content);
+        string? line;
+        bool expectPlaylist = false;
+        StreamSpec streamSpec = new();
+
+        while ((line = sr.ReadLine()) != null)
         {
-            M3u8Content = M3u8Content.Trim();
-            if (!M3u8Content.StartsWith(HLSTags.ext_m3u))
+            if (string.IsNullOrEmpty(line))
+                continue;
+
+            if (line.StartsWith(HLSTags.ext_x_stream_inf))
             {
-                throw new Exception(ResString.badM3u8);
+                streamSpec = new();
+                streamSpec.OriginalUrl = ParserConfig.OriginalUrl;
+                var bandwidth = string.IsNullOrEmpty(ParserUtil.GetAttribute(line, "AVERAGE-BANDWIDTH")) ? ParserUtil.GetAttribute(line, "BANDWIDTH") : ParserUtil.GetAttribute(line, "AVERAGE-BANDWIDTH");
+                streamSpec.Bandwidth = Convert.ToInt32(bandwidth);
+                streamSpec.Codecs = ParserUtil.GetAttribute(line, "CODECS");
+                streamSpec.Resolution = ParserUtil.GetAttribute(line, "RESOLUTION");
+
+                var frameRate = ParserUtil.GetAttribute(line, "FRAME-RATE");
+                if (!string.IsNullOrEmpty(frameRate))
+                    streamSpec.FrameRate = Convert.ToDouble(frameRate);
+
+                var audioId = ParserUtil.GetAttribute(line, "AUDIO");
+                if (!string.IsNullOrEmpty(audioId))
+                    streamSpec.AudioId = audioId;
+
+                var videoId = ParserUtil.GetAttribute(line, "VIDEO");
+                if (!string.IsNullOrEmpty(videoId))
+                    streamSpec.VideoId = videoId;
+
+                var subtitleId = ParserUtil.GetAttribute(line, "SUBTITLES");
+                if (!string.IsNullOrEmpty(subtitleId))
+                    streamSpec.SubtitleId = subtitleId;
+
+                var videoRange = ParserUtil.GetAttribute(line, "VIDEO-RANGE");
+                if (!string.IsNullOrEmpty(videoRange))
+                    streamSpec.VideoRange = videoRange;
+
+                // 清除多余的编码信息 dvh1.05.06,ec-3 => dvh1.05.06
+                if (!string.IsNullOrEmpty(streamSpec.Codecs) && !string.IsNullOrEmpty(streamSpec.AudioId))
+                {
+                    streamSpec.Codecs = streamSpec.Codecs.Split(',')[0];
+                }
+
+                expectPlaylist = true;
             }
-
-            foreach (var p in ParserConfig.ContentProcessors)
+            else if (line.StartsWith(HLSTags.ext_x_media))
             {
-                if (p.CanProcess(ExtractorType, M3u8Content, ParserConfig))
+                streamSpec = new();
+                var type = ParserUtil.GetAttribute(line, "TYPE").Replace("-", "_");
+                if (Enum.TryParse<MediaType>(type, out var mediaType))
                 {
-                    M3u8Content = p.Process(M3u8Content, ParserConfig);
+                    streamSpec.MediaType = mediaType;
                 }
-            }
-        }
 
-        /// <summary>
-        /// 预处理URL
-        /// </summary>
-        public string PreProcessUrl(string url)
-        {
-            foreach (var p in ParserConfig.UrlProcessors)
-            {
-                if (p.CanProcess(ExtractorType, url, ParserConfig))
-                {
-                    url = p.Process(url, ParserConfig);
-                }
-            }
-
-            return url;
-        }
-
-        private async Task<List<StreamSpec>> ParseMasterListAsync()
-        {
-            MasterM3u8Flag = true;
-
-            List<StreamSpec> streams = new List<StreamSpec>();
-
-            using StringReader sr = new StringReader(M3u8Content);
-            string? line;
-            bool expectPlaylist = false;
-            StreamSpec streamSpec = new();
-
-            while ((line = sr.ReadLine()) != null)
-            {
-                if (string.IsNullOrEmpty(line))
-                    continue;
-
-                if (line.StartsWith(HLSTags.ext_x_stream_inf))
-                {
-                    streamSpec = new();
-                    streamSpec.OriginalUrl = ParserConfig.OriginalUrl;
-                    var bandwidth = string.IsNullOrEmpty(ParserUtil.GetAttribute(line, "AVERAGE-BANDWIDTH")) ? ParserUtil.GetAttribute(line, "BANDWIDTH") : ParserUtil.GetAttribute(line, "AVERAGE-BANDWIDTH");
-                    streamSpec.Bandwidth = Convert.ToInt32(bandwidth);
-                    streamSpec.Codecs = ParserUtil.GetAttribute(line, "CODECS");
-                    streamSpec.Resolution = ParserUtil.GetAttribute(line, "RESOLUTION");
-
-                    var frameRate = ParserUtil.GetAttribute(line, "FRAME-RATE");
-                    if (!string.IsNullOrEmpty(frameRate))
-                        streamSpec.FrameRate = Convert.ToDouble(frameRate);
-
-                    var audioId = ParserUtil.GetAttribute(line, "AUDIO");
-                    if (!string.IsNullOrEmpty(audioId))
-                        streamSpec.AudioId = audioId;
-
-                    var videoId = ParserUtil.GetAttribute(line, "VIDEO");
-                    if (!string.IsNullOrEmpty(videoId))
-                        streamSpec.VideoId = videoId;
-
-                    var subtitleId = ParserUtil.GetAttribute(line, "SUBTITLES");
-                    if (!string.IsNullOrEmpty(subtitleId))
-                        streamSpec.SubtitleId = subtitleId;
-
-                    var videoRange = ParserUtil.GetAttribute(line, "VIDEO-RANGE");
-                    if (!string.IsNullOrEmpty(videoRange))
-                        streamSpec.VideoRange = videoRange;
-
-                    //清除多余的编码信息 dvh1.05.06,ec-3 => dvh1.05.06
-                    if (!string.IsNullOrEmpty(streamSpec.Codecs) && !string.IsNullOrEmpty(streamSpec.AudioId))
-                    {
-                        streamSpec.Codecs = streamSpec.Codecs.Split(',')[0];
-                    }
-
-                    expectPlaylist = true;
-                }
-                else if (line.StartsWith(HLSTags.ext_x_media))
-                {
-                    streamSpec = new();
-                    var type = ParserUtil.GetAttribute(line, "TYPE").Replace("-", "_");
-                    if (Enum.TryParse<MediaType>(type, out var mediaType))
-                    {
-                        streamSpec.MediaType = mediaType;
-                    }
-
-                    //跳过CLOSED_CAPTIONS类型(目前不支持)
-                    if (streamSpec.MediaType == MediaType.CLOSED_CAPTIONS)
-                    {
-                        continue;
-                    }
-
-                    var url = ParserUtil.GetAttribute(line, "URI");
-
-                    /**
-                     *    The URI attribute of the EXT-X-MEDIA tag is REQUIRED if the media
-                          type is SUBTITLES, but OPTIONAL if the media type is VIDEO or AUDIO.
-                          If the media type is VIDEO or AUDIO, a missing URI attribute
-                          indicates that the media data for this Rendition is included in the
-                          Media Playlist of any EXT-X-STREAM-INF tag referencing this EXT-
-                          X-MEDIA tag.  If the media TYPE is AUDIO and the URI attribute is
-                          missing, clients MUST assume that the audio data for this Rendition
-                          is present in every video Rendition specified by the EXT-X-STREAM-INF
-                          tag.
-                          
-                          此处直接忽略URI属性为空的情况
-                     */
-                    if (string.IsNullOrEmpty(url))
-                    {
-                        continue;
-                    }
-
-                    url = ParserUtil.CombineURL(BaseUrl, url);
-                    streamSpec.Url = PreProcessUrl(url);
-
-                    var groupId = ParserUtil.GetAttribute(line, "GROUP-ID");
-                    streamSpec.GroupId = groupId;
-
-                    var lang = ParserUtil.GetAttribute(line, "LANGUAGE");
-                    if (!string.IsNullOrEmpty(lang))
-                        streamSpec.Language = lang;
-
-                    var name = ParserUtil.GetAttribute(line, "NAME");
-                    if (!string.IsNullOrEmpty(name))
-                        streamSpec.Name = name;
-
-                    var def = ParserUtil.GetAttribute(line, "DEFAULT");
-                    if (Enum.TryParse<Choise>(type, out var defaultChoise))
-                    {
-                        streamSpec.Default = defaultChoise;
-                    }
-
-                    var channels = ParserUtil.GetAttribute(line, "CHANNELS");
-                    if (!string.IsNullOrEmpty(channels))
-                        streamSpec.Channels = channels;
-
-                    var characteristics = ParserUtil.GetAttribute(line, "CHARACTERISTICS");
-                    if (!string.IsNullOrEmpty(characteristics))
-                        streamSpec.Characteristics = characteristics.Split(',').Last().Split('.').Last();
-
-                    streams.Add(streamSpec);
-                }
-                else if (line.StartsWith("#"))
+                // 跳过CLOSED_CAPTIONS类型(目前不支持)
+                if (streamSpec.MediaType == MediaType.CLOSED_CAPTIONS)
                 {
                     continue;
                 }
-                else if (expectPlaylist)
-                {
-                    var url = ParserUtil.CombineURL(BaseUrl, line);
-                    streamSpec.Url = PreProcessUrl(url);
-                    expectPlaylist = false;
-                    streams.Add(streamSpec);
-                }
-            }
 
-            return streams;
+                var url = ParserUtil.GetAttribute(line, "URI");
+
+                /**
+                 *    The URI attribute of the EXT-X-MEDIA tag is REQUIRED if the media
+                      type is SUBTITLES, but OPTIONAL if the media type is VIDEO or AUDIO.
+                      If the media type is VIDEO or AUDIO, a missing URI attribute
+                      indicates that the media data for this Rendition is included in the
+                      Media Playlist of any EXT-X-STREAM-INF tag referencing this EXT-
+                      X-MEDIA tag.  If the media TYPE is AUDIO and the URI attribute is
+                      missing, clients MUST assume that the audio data for this Rendition
+                      is present in every video Rendition specified by the EXT-X-STREAM-INF
+                      tag.
+
+                      此处直接忽略URI属性为空的情况
+                 */
+                if (string.IsNullOrEmpty(url))
+                {
+                    continue;
+                }
+
+                url = ParserUtil.CombineURL(BaseUrl, url);
+                streamSpec.Url = PreProcessUrl(url);
+
+                var groupId = ParserUtil.GetAttribute(line, "GROUP-ID");
+                streamSpec.GroupId = groupId;
+
+                var lang = ParserUtil.GetAttribute(line, "LANGUAGE");
+                if (!string.IsNullOrEmpty(lang))
+                    streamSpec.Language = lang;
+
+                var name = ParserUtil.GetAttribute(line, "NAME");
+                if (!string.IsNullOrEmpty(name))
+                    streamSpec.Name = name;
+
+                var def = ParserUtil.GetAttribute(line, "DEFAULT");
+                if (Enum.TryParse<Choise>(type, out var defaultChoise))
+                {
+                    streamSpec.Default = defaultChoise;
+                }
+
+                var channels = ParserUtil.GetAttribute(line, "CHANNELS");
+                if (!string.IsNullOrEmpty(channels))
+                    streamSpec.Channels = channels;
+
+                var characteristics = ParserUtil.GetAttribute(line, "CHARACTERISTICS");
+                if (!string.IsNullOrEmpty(characteristics))
+                    streamSpec.Characteristics = characteristics.Split(',').Last().Split('.').Last();
+
+                streams.Add(streamSpec);
+            }
+            else if (line.StartsWith("#"))
+            {
+                continue;
+            }
+            else if (expectPlaylist)
+            {
+                var url = ParserUtil.CombineURL(BaseUrl, line);
+                streamSpec.Url = PreProcessUrl(url);
+                expectPlaylist = false;
+                streams.Add(streamSpec);
+            }
         }
 
-        private async Task<Playlist> ParseListAsync()
+        return Task.FromResult(streams);
+    }
+
+    private Task<Playlist> ParseListAsync()
+    {
+        // 标记是否已清除广告分片
+        bool hasAd = false;
+
+        using StringReader sr = new StringReader(M3u8Content);
+        string? line;
+        bool expectSegment = false;
+        bool isEndlist = false;
+        long segIndex = 0;
+        bool isAd = false;
+        long startIndex;
+
+        Playlist playlist = new();
+        List<MediaPart> mediaParts = new();
+
+        // 当前的加密信息
+        EncryptInfo currentEncryptInfo = new();
+        if (ParserConfig.CustomMethod != null)
+            currentEncryptInfo.Method = ParserConfig.CustomMethod.Value;
+        if (ParserConfig.CustomeKey != null && ParserConfig.CustomeKey.Length > 0) 
+            currentEncryptInfo.Key = ParserConfig.CustomeKey;
+        if (ParserConfig.CustomeIV != null && ParserConfig.CustomeIV.Length > 0)
+            currentEncryptInfo.IV = ParserConfig.CustomeIV;
+        // 上次读取到的加密行,#EXT-X-KEY:……
+        string lastKeyLine = "";
+
+        MediaPart mediaPart = new();
+        MediaSegment segment = new();
+        List<MediaSegment> segments = new();
+
+
+        while ((line = sr.ReadLine()) != null)
         {
-            //标记是否已清除优酷广告分片
-            bool hasAd = false;
+            if (string.IsNullOrEmpty(line))
+                continue;
 
-            using StringReader sr = new StringReader(M3u8Content);
-            string? line;
-            bool expectSegment = false;
-            bool isEndlist = false;
-            long segIndex = 0;
-            bool isAd = false;
-            long startIndex;
-
-            Playlist playlist = new();
-            List<MediaPart> mediaParts = new();
-
-            //当前的加密信息
-            EncryptInfo currentEncryptInfo = new();
-            if (ParserConfig.CustomMethod != null)
-                currentEncryptInfo.Method = ParserConfig.CustomMethod.Value;
-            if (ParserConfig.CustomeKey != null && ParserConfig.CustomeKey.Length > 0) 
-                currentEncryptInfo.Key = ParserConfig.CustomeKey;
-            if (ParserConfig.CustomeIV != null && ParserConfig.CustomeIV.Length > 0)
-                currentEncryptInfo.IV = ParserConfig.CustomeIV;
-            //上次读取到的加密行,#EXT-X-KEY:……
-            string lastKeyLine = "";
-
-            MediaPart mediaPart = new();
-            MediaSegment segment = new();
-            List<MediaSegment> segments = new();
-
-
-            while ((line = sr.ReadLine()) != null)
+            // 只下载部分字节
+            if (line.StartsWith(HLSTags.ext_x_byterange))
             {
-                if (string.IsNullOrEmpty(line))
-                    continue;
-
-                //只下载部分字节
-                if (line.StartsWith(HLSTags.ext_x_byterange))
-                {
-                    var p = ParserUtil.GetAttribute(line);
-                    var (n, o) = ParserUtil.GetRange(p);
-                    segment.ExpectLength = n;
-                    segment.StartRange = o ?? segments.Last().StartRange + segments.Last().ExpectLength;
-                    expectSegment = true;
-                }
-                //国家地理去广告
-                else if (line.StartsWith("#UPLYNK-SEGMENT"))
-                {
-                    if (line.Contains(",ad"))
-                        isAd = true;
-                    else if (line.Contains(",segment"))
-                        isAd = false;
-                }
-                //国家地理去广告
-                else if (isAd)
+                var p = ParserUtil.GetAttribute(line);
+                var (n, o) = ParserUtil.GetRange(p);
+                segment.ExpectLength = n;
+                segment.StartRange = o ?? segments.Last().StartRange + segments.Last().ExpectLength;
+                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))
+            {
+                playlist.TargetDuration = Convert.ToDouble(ParserUtil.GetAttribute(line));
+            }
+            // 解析起始编号
+            else if (line.StartsWith(HLSTags.ext_x_media_sequence))
+            {
+                segIndex = Convert.ToInt64(ParserUtil.GetAttribute(line));
+                startIndex = segIndex;
+            }
+            // program date time
+            else if (line.StartsWith(HLSTags.ext_x_program_date_time))
+            {
+                segment.DateTime = DateTime.Parse(ParserUtil.GetAttribute(line));
+            }
+            // 解析不连续标记,需要单独合并(timestamp不同)
+            else if (line.StartsWith(HLSTags.ext_x_discontinuity))
+            {
+                // 修复YK去除广告后的遗留问题
+                if (hasAd && mediaParts.Count > 0)
                 {
+                    segments = mediaParts[mediaParts.Count - 1].MediaSegments;
+                    mediaParts.RemoveAt(mediaParts.Count - 1);
+                    hasAd = false;
                     continue;
                 }
-                //解析定义的分段长度
-                else if (line.StartsWith(HLSTags.ext_x_targetduration))
+                // 常规情况的#EXT-X-DISCONTINUITY标记,新建part
+                if (!hasAd && segments.Count >= 1)
                 {
-                    playlist.TargetDuration = Convert.ToDouble(ParserUtil.GetAttribute(line));
-                }
-                //解析起始编号
-                else if (line.StartsWith(HLSTags.ext_x_media_sequence))
-                {
-                    segIndex = Convert.ToInt64(ParserUtil.GetAttribute(line));
-                    startIndex = segIndex;
-                }
-                //program date time
-                else if (line.StartsWith(HLSTags.ext_x_program_date_time))
-                {
-                    segment.DateTime = DateTime.Parse(ParserUtil.GetAttribute(line));
-                }
-                //解析不连续标记,需要单独合并(timestamp不同)
-                else if (line.StartsWith(HLSTags.ext_x_discontinuity))
-                {
-                    //修复优酷去除广告后的遗留问题
-                    if (hasAd && mediaParts.Count > 0)
+                    mediaParts.Add(new MediaPart()
                     {
-                        segments = mediaParts[mediaParts.Count - 1].MediaSegments;
-                        mediaParts.RemoveAt(mediaParts.Count - 1);
-                        hasAd = false;
-                        continue;
-                    }
-                    //常规情况的#EXT-X-DISCONTINUITY标记,新建part
-                    if (!hasAd && segments.Count >= 1)
-                    {
-                        mediaParts.Add(new MediaPart()
-                        {
-                            MediaSegments = segments,
-                        });
-                        segments = new();
-                    }
+                        MediaSegments = segments,
+                    });
+                    segments = new();
                 }
-                //解析KEY
-                else if (line.StartsWith(HLSTags.ext_x_key))
-                {
-                    var uri = ParserUtil.GetAttribute(line, "URI");
-                    var uri_last = ParserUtil.GetAttribute(lastKeyLine, "URI");
+            }
+            // 解析KEY
+            else if (line.StartsWith(HLSTags.ext_x_key))
+            {
+                var uri = ParserUtil.GetAttribute(line, "URI");
+                var uri_last = ParserUtil.GetAttribute(lastKeyLine, "URI");
                     
-                    //如果KEY URL相同,不进行重复解析
-                    if (uri != uri_last)
-                    {
-                        //调用处理器进行解析
-                        var parsedInfo = ParseKey(line);
-                        currentEncryptInfo.Method = parsedInfo.Method;
-                        currentEncryptInfo.Key = parsedInfo.Key;
-                        currentEncryptInfo.IV = parsedInfo.IV;
-                    }
-                    lastKeyLine = line;
-                }
-                //解析分片时长
-                else if (line.StartsWith(HLSTags.extinf))
+                // 如果KEY URL相同,不进行重复解析
+                if (uri != uri_last)
                 {
-                    string[] tmp = ParserUtil.GetAttribute(line).Split(',');
-                    segment.Duration = Convert.ToDouble(tmp[0]);
-                    segment.Index = segIndex;
-                    //是否有加密,有的话写入KEY和IV
+                    // 调用处理器进行解析
+                    var parsedInfo = ParseKey(line);
+                    currentEncryptInfo.Method = parsedInfo.Method;
+                    currentEncryptInfo.Key = parsedInfo.Key;
+                    currentEncryptInfo.IV = parsedInfo.IV;
+                }
+                lastKeyLine = line;
+            }
+            // 解析分片时长
+            else if (line.StartsWith(HLSTags.extinf))
+            {
+                string[] tmp = ParserUtil.GetAttribute(line).Split(',');
+                segment.Duration = Convert.ToDouble(tmp[0]);
+                segment.Index = segIndex;
+                // 是否有加密,有的话写入KEY和IV
+                if (currentEncryptInfo.Method != EncryptMethod.NONE)
+                {
+                    segment.EncryptInfo.Method = currentEncryptInfo.Method;
+                    segment.EncryptInfo.Key = currentEncryptInfo.Key;
+                    segment.EncryptInfo.IV = currentEncryptInfo.IV ?? HexUtil.HexToBytes(Convert.ToString(segIndex, 16).PadLeft(32, '0'));
+                }
+                expectSegment = true;
+                segIndex++;
+            }
+            // m3u8主体结束
+            else if (line.StartsWith(HLSTags.ext_x_endlist))
+            {
+                if (segments.Count > 0)
+                {
+                    mediaParts.Add(new MediaPart()
+                    {
+                        MediaSegments = segments
+                    });
+                }
+                segments = new();
+                isEndlist = true;
+            }
+            // #EXT-X-MAP
+            else if (line.StartsWith(HLSTags.ext_x_map))
+            {
+                if (playlist.MediaInit == null) 
+                {
+                    playlist.MediaInit = new MediaSegment()
+                    {
+                        Url = PreProcessUrl(ParserUtil.CombineURL(BaseUrl, ParserUtil.GetAttribute(line, "URI"))),
+                        Index = -1, // 便于排序
+                    };
+                    if (line.Contains("BYTERANGE"))
+                    {
+                        var p = ParserUtil.GetAttribute(line, "BYTERANGE");
+                        var (n, o) = ParserUtil.GetRange(p);
+                        playlist.MediaInit.ExpectLength = n;
+                        playlist.MediaInit.StartRange = o ?? 0L;
+                    }
+                    // 是否有加密,有的话写入KEY和IV
                     if (currentEncryptInfo.Method != EncryptMethod.NONE)
                     {
-                        segment.EncryptInfo.Method = currentEncryptInfo.Method;
-                        segment.EncryptInfo.Key = currentEncryptInfo.Key;
-                        segment.EncryptInfo.IV = currentEncryptInfo.IV ?? HexUtil.HexToBytes(Convert.ToString(segIndex, 16).PadLeft(32, '0'));
+                        playlist.MediaInit.EncryptInfo.Method = currentEncryptInfo.Method;
+                        playlist.MediaInit.EncryptInfo.Key = currentEncryptInfo.Key;
+                        playlist.MediaInit.EncryptInfo.IV = currentEncryptInfo.IV ?? HexUtil.HexToBytes(Convert.ToString(segIndex, 16).PadLeft(32, '0'));
                     }
-                    expectSegment = true;
-                    segIndex++;
                 }
-                //m3u8主体结束
-                else if (line.StartsWith(HLSTags.ext_x_endlist))
+                // 遇到了其他的map,说明已经不是一个视频了,全部丢弃即可
+                else
                 {
                     if (segments.Count > 0)
                     {
@@ -358,228 +396,187 @@ namespace N_m3u8DL_RE.Parser.Extractor
                     }
                     segments = new();
                     isEndlist = true;
-                }
-                //#EXT-X-MAP
-                else if (line.StartsWith(HLSTags.ext_x_map))
-                {
-                    if (playlist.MediaInit == null) 
-                    {
-                        playlist.MediaInit = new MediaSegment()
-                        {
-                            Url = PreProcessUrl(ParserUtil.CombineURL(BaseUrl, ParserUtil.GetAttribute(line, "URI"))),
-                            Index = -1, //便于排序
-                        };
-                        if (line.Contains("BYTERANGE"))
-                        {
-                            var p = ParserUtil.GetAttribute(line, "BYTERANGE");
-                            var (n, o) = ParserUtil.GetRange(p);
-                            playlist.MediaInit.ExpectLength = n;
-                            playlist.MediaInit.StartRange = o ?? 0L;
-                        }
-                        //是否有加密,有的话写入KEY和IV
-                        if (currentEncryptInfo.Method != EncryptMethod.NONE)
-                        {
-                            playlist.MediaInit.EncryptInfo.Method = currentEncryptInfo.Method;
-                            playlist.MediaInit.EncryptInfo.Key = currentEncryptInfo.Key;
-                            playlist.MediaInit.EncryptInfo.IV = currentEncryptInfo.IV ?? HexUtil.HexToBytes(Convert.ToString(segIndex, 16).PadLeft(32, '0'));
-                        }
-                    }
-                    //遇到了其他的map,说明已经不是一个视频了,全部丢弃即可
-                    else
-                    {
-                        if (segments.Count > 0)
-                        {
-                            mediaParts.Add(new MediaPart()
-                            {
-                                MediaSegments = segments
-                            });
-                        }
-                        segments = new();
-                        isEndlist = true;
-                        break;
-                    }
-                }
-                //评论行不解析
-                else if (line.StartsWith("#")) continue;
-                //空白行不解析
-                else if (line.StartsWith("\r\n")) continue;
-                //解析分片的地址
-                else if (expectSegment)
-                {
-                    var segUrl = PreProcessUrl(ParserUtil.CombineURL(BaseUrl, line));
-                    segment.Url = segUrl;
-                    segments.Add(segment);
-                    segment = new();
-                    //优酷的广告分段则清除此分片
-                    //需要注意,遇到广告说明程序对上文的#EXT-X-DISCONTINUITY做出的动作是不必要的,
-                    //其实上下文是同一种编码,需要恢复到原先的part上
-                    if (segUrl.Contains("ccode=") && segUrl.Contains("/ad/") && segUrl.Contains("duration="))
-                    {
-                        segments.RemoveAt(segments.Count - 1);
-                        segIndex--;
-                        hasAd = true;
-                    }
-                    //优酷广告(4K分辨率测试)
-                    if (segUrl.Contains("ccode=0902") && segUrl.Contains("duration="))
-                    {
-                        segments.RemoveAt(segments.Count - 1);
-                        segIndex--;
-                        hasAd = true;
-                    }
-                    expectSegment = false;
+                    break;
                 }
             }
-
-            //直播的情况,无法遇到m3u8结束标记,需要手动将segments加入parts
-            if (!isEndlist)
+            // 评论行不解析
+            else if (line.StartsWith("#")) continue;
+            // 空白行不解析
+            else if (line.StartsWith("\r\n")) continue;
+            // 解析分片的地址
+            else if (expectSegment)
             {
-                mediaParts.Add(new MediaPart()
+                var segUrl = PreProcessUrl(ParserUtil.CombineURL(BaseUrl, line));
+                segment.Url = segUrl;
+                segments.Add(segment);
+                segment = new();
+                // YK的广告分段则清除此分片
+                // 需要注意,遇到广告说明程序对上文的#EXT-X-DISCONTINUITY做出的动作是不必要的,
+                // 其实上下文是同一种编码,需要恢复到原先的part上
+                if (segUrl.Contains("ccode=") && segUrl.Contains("/ad/") && segUrl.Contains("duration="))
                 {
-                    MediaSegments = segments
-                });
+                    segments.RemoveAt(segments.Count - 1);
+                    segIndex--;
+                    hasAd = true;
+                }
+                // YK广告(4K分辨率测试)
+                if (segUrl.Contains("ccode=0902") && segUrl.Contains("duration="))
+                {
+                    segments.RemoveAt(segments.Count - 1);
+                    segIndex--;
+                    hasAd = true;
+                }
+                expectSegment = false;
             }
-
-            playlist.MediaParts = mediaParts;
-            playlist.IsLive = !isEndlist;
-
-            //直播刷新间隔
-            if (playlist.IsLive)
-            {
-                //由于播放器默认从最后3个分片开始播放 此处设置刷新间隔为TargetDuration的2倍
-                playlist.RefreshIntervalMs = (int)((playlist.TargetDuration ?? 5) * 2 * 1000);
-            }
-
-            return playlist;
         }
 
-        private EncryptInfo ParseKey(string keyLine)
+        // 直播的情况,无法遇到m3u8结束标记,需要手动将segments加入parts
+        if (!isEndlist)
         {
-            foreach (var p in ParserConfig.KeyProcessors)
+            mediaParts.Add(new MediaPart()
             {
-                if (p.CanProcess(ExtractorType, keyLine, M3u8Url, M3u8Content, ParserConfig))
-                {
-                    //匹配到对应处理器后不再继续
-                    return p.Process(keyLine, M3u8Url, M3u8Content, ParserConfig);
-                }
-            }
-
-            throw new Exception(ResString.keyProcessorNotFound);
+                MediaSegments = segments
+            });
         }
 
-        public async Task<List<StreamSpec>> ExtractStreamsAsync(string rawText)
+        playlist.MediaParts = mediaParts;
+        playlist.IsLive = !isEndlist;
+
+        // 直播刷新间隔
+        if (playlist.IsLive)
         {
-            this.M3u8Content = rawText;
-            this.PreProcessContent();
-            if (M3u8Content.Contains(HLSTags.ext_x_stream_inf))
+            // 由于播放器默认从最后3个分片开始播放 此处设置刷新间隔为TargetDuration的2倍
+            playlist.RefreshIntervalMs = (int)((playlist.TargetDuration ?? 5) * 2 * 1000);
+        }
+
+        return Task.FromResult(playlist);
+    }
+
+    private EncryptInfo ParseKey(string keyLine)
+    {
+        foreach (var p in ParserConfig.KeyProcessors)
+        {
+            if (p.CanProcess(ExtractorType, keyLine, M3u8Url, M3u8Content, ParserConfig))
             {
-                Logger.Warn(ResString.masterM3u8Found);
-                var lists = await ParseMasterListAsync();
-                lists = lists.DistinctBy(p => p.Url).ToList();
-                return lists;
+                // 匹配到对应处理器后不再继续
+                return p.Process(keyLine, M3u8Url, M3u8Content, ParserConfig);
+            }
+        }
+
+        throw new Exception(ResString.keyProcessorNotFound);
+    }
+
+    public async Task<List<StreamSpec>> ExtractStreamsAsync(string rawText)
+    {
+        this.M3u8Content = rawText;
+        this.PreProcessContent();
+        if (M3u8Content.Contains(HLSTags.ext_x_stream_inf))
+        {
+            Logger.Warn(ResString.masterM3u8Found);
+            var lists = await ParseMasterListAsync();
+            lists = lists.DistinctBy(p => p.Url).ToList();
+            return lists;
+        }
+
+        var playlist = await ParseListAsync();
+        return new List<StreamSpec>
+        {
+            new()
+            {
+                Url = ParserConfig.Url,
+                Playlist = playlist,
+                Extension = playlist.MediaInit != null ? "mp4" : "ts"
+            }
+        };
+    }
+
+    private async Task LoadM3u8FromUrlAsync(string url)
+    {
+        // Logger.Info(ResString.loadingUrl + url);
+        if (url.StartsWith("file:"))
+        {
+            var uri = new Uri(url);
+            this.M3u8Content = File.ReadAllText(uri.LocalPath);
+        }
+        else if (url.StartsWith("http"))
+        {
+            try
+            {
+                (this.M3u8Content, url) = await HTTPUtil.GetWebSourceAndNewUrlAsync(url, ParserConfig.Headers);
+            }
+            catch (HttpRequestException) when (url != ParserConfig.OriginalUrl)
+            {
+                // 当URL无法访问时,再请求原始URL
+                (this.M3u8Content, url) = await HTTPUtil.GetWebSourceAndNewUrlAsync(ParserConfig.OriginalUrl, ParserConfig.Headers);
+            }
+        }
+
+        this.M3u8Url = url;
+        this.SetBaseUrl();
+        this.PreProcessContent();
+    }
+
+    /// <summary>
+    /// 从Master链接中刷新各个流的URL
+    /// </summary>
+    /// <param name="lists"></param>
+    /// <returns></returns>
+    private async Task RefreshUrlFromMaster(List<StreamSpec> lists)
+    {
+        // 重新加载master m3u8, 刷新选中流的URL
+        await LoadM3u8FromUrlAsync(ParserConfig.Url);
+        var newStreams = await ParseMasterListAsync();
+        newStreams = newStreams.DistinctBy(p => p.Url).ToList();
+        foreach (var l in lists)
+        {
+            var match = newStreams.Where(n => n.ToShortString() == l.ToShortString()).ToList();
+            if (match.Any())
+            {
+                Logger.DebugMarkUp($"{l.Url} => {match.First().Url}");
+                l.Url = match.First().Url;
+            }
+        }
+    }
+
+    public async Task FetchPlayListAsync(List<StreamSpec> lists)
+    {
+        for (int i = 0; i < lists.Count; i++)
+        {
+            try
+            {
+                // 直接重新加载m3u8
+                await LoadM3u8FromUrlAsync(lists[i].Url!);
+            }
+            catch (HttpRequestException) when (MasterM3u8Flag == true)
+            {
+                Logger.WarnMarkUp("Can not load m3u8. Try refreshing url from master url...");
+                // 当前URL无法加载 尝试从Master链接中刷新URL
+                await RefreshUrlFromMaster(lists);
+                await LoadM3u8FromUrlAsync(lists[i].Url!);
+            }
+
+            var newPlaylist = await ParseListAsync();
+            if (lists[i].Playlist?.MediaInit != null)
+                lists[i].Playlist!.MediaParts = newPlaylist.MediaParts; // 不更新init
+            else
+                lists[i].Playlist = newPlaylist;
+
+            if (lists[i].MediaType == MediaType.SUBTITLES)
+            {
+                var a = lists[i].Playlist!.MediaParts.Any(p => p.MediaSegments.Any(m => m.Url.Contains(".ttml")));
+                var b = lists[i].Playlist!.MediaParts.Any(p => p.MediaSegments.Any(m => m.Url.Contains(".vtt") || m.Url.Contains(".webvtt")));
+                if (a) lists[i].Extension = "ttml";
+                if (b) lists[i].Extension = "vtt";
             }
             else
             {
-                var playlist = await ParseListAsync();
-                return new List<StreamSpec>()
-                {
-                    new StreamSpec()
-                    {
-                        Url = ParserConfig.Url,
-                        Playlist = playlist,
-                        Extension = playlist.MediaInit != null ? "mp4" : "ts"
-                    }
-                };
+                lists[i].Extension = lists[i].Playlist!.MediaInit != null ? "m4s" : "ts";
             }
         }
-
-        private async Task LoadM3u8FromUrlAsync(string url)
-        {
-            //Logger.Info(ResString.loadingUrl + url);
-            if (url.StartsWith("file:"))
-            {
-                var uri = new Uri(url);
-                this.M3u8Content = File.ReadAllText(uri.LocalPath);
-            }
-            else if (url.StartsWith("http"))
-            {
-                try
-                {
-                    (this.M3u8Content, url) = await HTTPUtil.GetWebSourceAndNewUrlAsync(url, ParserConfig.Headers);
-                }
-                catch (HttpRequestException) when (url != ParserConfig.OriginalUrl)
-                {
-                    //当URL无法访问时,再请求原始URL
-                    (this.M3u8Content, url) = await HTTPUtil.GetWebSourceAndNewUrlAsync(ParserConfig.OriginalUrl, ParserConfig.Headers);
-                }
-            }
-
-            this.M3u8Url = url;
-            this.SetBaseUrl();
-            this.PreProcessContent();
-        }
-
-        /// <summary>
-        /// 从Master链接中刷新各个流的URL
-        /// </summary>
-        /// <param name="lists"></param>
-        /// <returns></returns>
-        private async Task RefreshUrlFromMaster(List<StreamSpec> lists)
-        {
-            //重新加载master m3u8, 刷新选中流的URL
-            await LoadM3u8FromUrlAsync(ParserConfig.Url);
-            var newStreams = await ParseMasterListAsync();
-            newStreams = newStreams.DistinctBy(p => p.Url).ToList();
-            foreach (var l in lists)
-            {
-                var match = newStreams.Where(n => n.ToShortString() == l.ToShortString());
-                if (match.Any())
-                {
-                    Logger.DebugMarkUp($"{l.Url} => {match.First().Url}");
-                    l.Url = match.First().Url;
-                }
-            }
-        }
-
-        public async Task FetchPlayListAsync(List<StreamSpec> lists)
-        {
-            for (int i = 0; i < lists.Count; i++)
-            {
-                try
-                {
-                    //直接重新加载m3u8
-                    await LoadM3u8FromUrlAsync(lists[i].Url!);
-                }
-                catch (HttpRequestException) when (MasterM3u8Flag == true)
-                {
-                    Logger.WarnMarkUp("Can not load m3u8. Try refreshing url from master url...");
-                    //当前URL无法加载 尝试从Master链接中刷新URL
-                    await RefreshUrlFromMaster(lists);
-                    await LoadM3u8FromUrlAsync(lists[i].Url!);
-                }
-
-                var newPlaylist = await ParseListAsync();
-                if (lists[i].Playlist?.MediaInit != null)
-                    lists[i].Playlist!.MediaParts = newPlaylist.MediaParts; //不更新init
-                else
-                    lists[i].Playlist = newPlaylist;
-
-                if (lists[i].MediaType == MediaType.SUBTITLES)
-                {
-                    var a = lists[i].Playlist!.MediaParts.Any(p => p.MediaSegments.Any(m => m.Url.Contains(".ttml")));
-                    var b = lists[i].Playlist!.MediaParts.Any(p => p.MediaSegments.Any(m => m.Url.Contains(".vtt") || m.Url.Contains(".webvtt")));
-                    if (a) lists[i].Extension = "ttml";
-                    if (b) lists[i].Extension = "vtt";
-                }
-                else
-                {
-                    lists[i].Extension = lists[i].Playlist!.MediaInit != null ? "m4s" : "ts";
-                }
-            }
-        }
-
-        public async Task RefreshPlayListAsync(List<StreamSpec> streamSpecs)
-        {
-            await FetchPlayListAsync(streamSpecs);
-        }
     }
-}
+
+    public async Task RefreshPlayListAsync(List<StreamSpec> streamSpecs)
+    {
+        await FetchPlayListAsync(streamSpecs);
+    }
+}
\ No newline at end of file
diff --git a/src/N_m3u8DL-RE.Parser/Extractor/IExtractor.cs b/src/N_m3u8DL-RE.Parser/Extractor/IExtractor.cs
index ad6fb73..708f5b9 100644
--- a/src/N_m3u8DL-RE.Parser/Extractor/IExtractor.cs
+++ b/src/N_m3u8DL-RE.Parser/Extractor/IExtractor.cs
@@ -7,21 +7,20 @@ using System.Text;
 using System.Threading.Tasks;
 using N_m3u8DL_RE.Common.Enum;
 
-namespace N_m3u8DL_RE.Parser.Extractor
+namespace N_m3u8DL_RE.Parser.Extractor;
+
+public interface IExtractor
 {
-    public interface IExtractor
-    {
-        ExtractorType ExtractorType { get; }
+    ExtractorType ExtractorType { get; }
 
-        ParserConfig ParserConfig { get; set; }
+    ParserConfig ParserConfig { get; set; }
 
-        Task<List<StreamSpec>> ExtractStreamsAsync(string rawText);
+    Task<List<StreamSpec>> ExtractStreamsAsync(string rawText);
 
-        Task FetchPlayListAsync(List<StreamSpec> streamSpecs);
-        Task RefreshPlayListAsync(List<StreamSpec> streamSpecs);
+    Task FetchPlayListAsync(List<StreamSpec> streamSpecs);
+    Task RefreshPlayListAsync(List<StreamSpec> streamSpecs);
 
-        string PreProcessUrl(string url);
+    string PreProcessUrl(string url);
 
-        void PreProcessContent();
-    }
-}
+    void PreProcessContent();
+}
\ No newline at end of file
diff --git a/src/N_m3u8DL-RE.Parser/Extractor/LiveTSExtractor.cs b/src/N_m3u8DL-RE.Parser/Extractor/LiveTSExtractor.cs
index eae7cc0..7e36fd5 100644
--- a/src/N_m3u8DL-RE.Parser/Extractor/LiveTSExtractor.cs
+++ b/src/N_m3u8DL-RE.Parser/Extractor/LiveTSExtractor.cs
@@ -3,51 +3,50 @@ using N_m3u8DL_RE.Common.Enum;
 using N_m3u8DL_RE.Common.Resource;
 using N_m3u8DL_RE.Parser.Config;
 
-namespace N_m3u8DL_RE.Parser.Extractor
+namespace N_m3u8DL_RE.Parser.Extractor;
+
+internal class LiveTSExtractor : IExtractor
 {
-    internal class LiveTSExtractor : IExtractor
+    public ExtractorType ExtractorType => ExtractorType.HTTP_LIVE;
+
+    public ParserConfig ParserConfig {get; set;}
+
+    public LiveTSExtractor(ParserConfig parserConfig)
     {
-        public ExtractorType ExtractorType => ExtractorType.HTTP_LIVE;
-
-        public ParserConfig ParserConfig {get; set;}
-
-        public LiveTSExtractor(ParserConfig parserConfig)
-        {
-            this.ParserConfig = parserConfig;
-        }
-
-        public async Task<List<StreamSpec>> ExtractStreamsAsync(string rawText)
-        {
-            return new List<StreamSpec>()
-            {
-                new StreamSpec()
-                {
-                    OriginalUrl = ParserConfig.OriginalUrl,
-                    Url = ParserConfig.Url,
-                    Playlist = new Playlist(),
-                    GroupId = ResString.ReLiveTs
-                }
-            };
-        }
-
-        public async Task FetchPlayListAsync(List<StreamSpec> streamSpecs)
-        {
-            throw new NotImplementedException();
-        }
-
-        public async void PreProcessContent()
-        {
-            throw new NotImplementedException();
-        }
-
-        public string PreProcessUrl(string url)
-        {
-            throw new NotImplementedException();
-        }
-
-        public Task RefreshPlayListAsync(List<StreamSpec> streamSpecs)
-        {
-            throw new NotImplementedException();
-        }
+        this.ParserConfig = parserConfig;
     }
-}
+
+    public Task<List<StreamSpec>> ExtractStreamsAsync(string rawText)
+    {
+        return Task.FromResult(new List<StreamSpec>
+        {
+            new()
+            {
+                OriginalUrl = ParserConfig.OriginalUrl,
+                Url = ParserConfig.Url,
+                Playlist = new Playlist(),
+                GroupId = ResString.ReLiveTs
+            }
+        });
+    }
+
+    public Task FetchPlayListAsync(List<StreamSpec> streamSpecs)
+    {
+        throw new NotImplementedException();
+    }
+
+    public void PreProcessContent()
+    {
+        throw new NotImplementedException();
+    }
+
+    public string PreProcessUrl(string url)
+    {
+        throw new NotImplementedException();
+    }
+
+    public Task RefreshPlayListAsync(List<StreamSpec> streamSpecs)
+    {
+        throw new NotImplementedException();
+    }
+}
\ No newline at end of file
diff --git a/src/N_m3u8DL-RE.Parser/Extractor/MSSExtractor.cs b/src/N_m3u8DL-RE.Parser/Extractor/MSSExtractor.cs
index e6639a8..9c2b5b4 100644
--- a/src/N_m3u8DL-RE.Parser/Extractor/MSSExtractor.cs
+++ b/src/N_m3u8DL-RE.Parser/Extractor/MSSExtractor.cs
@@ -16,386 +16,386 @@ using System.Threading.Tasks;
 using System.Xml;
 using System.Xml.Linq;
 
-namespace N_m3u8DL_RE.Parser.Extractor
+namespace N_m3u8DL_RE.Parser.Extractor;
+
+// Microsoft Smooth Streaming
+// https://test.playready.microsoft.com/smoothstreaming/SSWSS720H264/SuperSpeedway_720.ism/manifest
+// file:///C:/Users/nilaoda/Downloads/[MS-SSTR]-180316.pdf
+internal partial class MSSExtractor : IExtractor
 {
-    //Microsoft Smooth Streaming
-    //https://test.playready.microsoft.com/smoothstreaming/SSWSS720H264/SuperSpeedway_720.ism/manifest
-    //file:///C:/Users/nilaoda/Downloads/[MS-SSTR]-180316.pdf
-    internal partial class MSSExtractor : IExtractor
-    {
-        [GeneratedRegex("00000001\\d7([0-9a-fA-F]{6})")]
-        private static partial Regex VCodecsRegex();
+    [GeneratedRegex("00000001\\d7([0-9a-fA-F]{6})")]
+    private static partial Regex VCodecsRegex();
 
-        ////////////////////////////////////////
+    ////////////////////////////////////////
         
-        private static EncryptMethod DEFAULT_METHOD = EncryptMethod.CENC;
+    private static EncryptMethod DEFAULT_METHOD = EncryptMethod.CENC;
 
-        public ExtractorType ExtractorType => ExtractorType.MSS;
+    public ExtractorType ExtractorType => ExtractorType.MSS;
 
-        private string IsmUrl = string.Empty;
-        private string BaseUrl = string.Empty;
-        private string IsmContent = string.Empty;
-        public ParserConfig ParserConfig { get; set; }
+    private string IsmUrl = string.Empty;
+    private string BaseUrl = string.Empty;
+    private string IsmContent = string.Empty;
+    public ParserConfig ParserConfig { get; set; }
 
-        public MSSExtractor(ParserConfig parserConfig)
+    public MSSExtractor(ParserConfig parserConfig)
+    {
+        this.ParserConfig = parserConfig;
+        SetInitUrl();
+    }
+
+    private void SetInitUrl()
+    {
+        this.IsmUrl = ParserConfig.Url ?? string.Empty;
+        if (!string.IsNullOrEmpty(ParserConfig.BaseUrl))
+            this.BaseUrl = ParserConfig.BaseUrl;
+        else
+            this.BaseUrl = this.IsmUrl;
+    }
+
+    public Task<List<StreamSpec>> ExtractStreamsAsync(string rawText)
+    {
+        var streamList = new List<StreamSpec>();
+        this.IsmContent = rawText;
+        this.PreProcessContent();
+
+        var xmlDocument = XDocument.Parse(IsmContent);
+
+        // 选中第一个SmoothStreamingMedia节点
+        var ssmElement = xmlDocument.Elements().First(e => e.Name.LocalName == "SmoothStreamingMedia");
+        var timeScaleStr = ssmElement.Attribute("TimeScale")?.Value ?? "10000000";
+        var durationStr = ssmElement.Attribute("Duration")?.Value;
+        var timescale = Convert.ToInt32(timeScaleStr);
+        var isLiveStr = ssmElement.Attribute("IsLive")?.Value;
+        bool isLive = Convert.ToBoolean(isLiveStr ?? "FALSE");
+
+        var isProtection = false;
+        var protectionSystemId = "";
+        var protectionData = "";
+
+        // 加密检测
+        var protectElement = ssmElement.Elements().FirstOrDefault(e => e.Name.LocalName == "Protection");
+        if (protectElement != null)
         {
-            this.ParserConfig = parserConfig;
-            SetInitUrl();
-        }
-
-        private void SetInitUrl()
-        {
-            this.IsmUrl = ParserConfig.Url ?? string.Empty;
-            if (!string.IsNullOrEmpty(ParserConfig.BaseUrl))
-                this.BaseUrl = ParserConfig.BaseUrl;
-            else
-                this.BaseUrl = this.IsmUrl;
-        }
-
-        public async Task<List<StreamSpec>> ExtractStreamsAsync(string rawText)
-        {
-            var streamList = new List<StreamSpec>();
-            this.IsmContent = rawText;
-            this.PreProcessContent();
-
-            var xmlDocument = XDocument.Parse(IsmContent);
-
-            //选中第一个SmoothStreamingMedia节点
-            var ssmElement = xmlDocument.Elements().First(e => e.Name.LocalName == "SmoothStreamingMedia");
-            var timeScaleStr = ssmElement.Attribute("TimeScale")?.Value ?? "10000000";
-            var durationStr = ssmElement.Attribute("Duration")?.Value;
-            var timescale = Convert.ToInt32(timeScaleStr);
-            var isLiveStr = ssmElement.Attribute("IsLive")?.Value;
-            bool isLive = Convert.ToBoolean(isLiveStr ?? "FALSE");
-
-            var isProtection = false;
-            var protectionSystemId = "";
-            var protectionData = "";
-
-            //加密检测
-            var protectElement = ssmElement.Elements().FirstOrDefault(e => e.Name.LocalName == "Protection");
-            if (protectElement != null)
+            var protectionHeader = protectElement.Element("ProtectionHeader");
+            if (protectionHeader != null)
             {
-                var protectionHeader = protectElement.Element("ProtectionHeader");
-                if (protectionHeader != null)
-                {
-                    isProtection = true;
-                    protectionSystemId = protectionHeader.Attribute("SystemID")?.Value ?? "9A04F079-9840-4286-AB92-E65BE0885F95";
-                    protectionData = HexUtil.BytesToHex(Convert.FromBase64String(protectionHeader.Value));
-                }
+                isProtection = true;
+                protectionSystemId = protectionHeader.Attribute("SystemID")?.Value ?? "9A04F079-9840-4286-AB92-E65BE0885F95";
+                protectionData = HexUtil.BytesToHex(Convert.FromBase64String(protectionHeader.Value));
             }
+        }
 
-            //所有StreamIndex节点
-            var streamIndexElements = ssmElement.Elements().Where(e => e.Name.LocalName == "StreamIndex");
+        // 所有StreamIndex节点
+        var streamIndexElements = ssmElement.Elements().Where(e => e.Name.LocalName == "StreamIndex");
 
-            foreach (var streamIndex in streamIndexElements)
+        foreach (var streamIndex in streamIndexElements)
+        {
+            var type = streamIndex.Attribute("Type")?.Value; // "video" / "audio" / "text"
+            var name = streamIndex.Attribute("Name")?.Value;
+            var subType = streamIndex.Attribute("Subtype")?.Value; // text track
+            // 如果有则不从QualityLevel读取
+            // Bitrate = "{bitrate}" / "{Bitrate}"
+            // StartTimeSubstitution = "{start time}" / "{start_time}"
+            var urlPattern = streamIndex.Attribute("Url")?.Value;
+            var language = streamIndex.Attribute("Language")?.Value;
+            // 去除不规范的语言标签
+            if (language?.Length != 3) language = null;
+
+            // 所有c节点
+            var cElements = streamIndex.Elements().Where(e => e.Name.LocalName == "c");
+
+            // 所有QualityLevel节点
+            var qualityLevelElements = streamIndex.Elements().Where(e => e.Name.LocalName == "QualityLevel");
+
+            foreach (var qualityLevel in qualityLevelElements)
             {
-                var type = streamIndex.Attribute("Type")?.Value; //"video" / "audio" / "text"
-                var name = streamIndex.Attribute("Name")?.Value;
-                var subType = streamIndex.Attribute("Subtype")?.Value; //text track
-                //如果有则不从QualityLevel读取
-                //Bitrate = "{bitrate}" / "{Bitrate}"
-                //StartTimeSubstitution = "{start time}" / "{start_time}"
-                var urlPattern = streamIndex.Attribute("Url")?.Value;
-                var language = streamIndex.Attribute("Language")?.Value;
-                //去除不规范的语言标签
-                if (language?.Length != 3) language = null;
+                urlPattern = (qualityLevel.Attribute("Url")?.Value ?? urlPattern)!
+                    .Replace(MSSTags.Bitrate_BK, MSSTags.Bitrate).Replace(MSSTags.StartTime_BK, MSSTags.StartTime);
+                var fourCC = qualityLevel.Attribute("FourCC")!.Value.ToUpper();
+                var samplingRateStr = qualityLevel.Attribute("SamplingRate")?.Value;
+                var bitsPerSampleStr = qualityLevel.Attribute("BitsPerSample")?.Value;
+                var nalUnitLengthFieldStr = qualityLevel.Attribute("NALUnitLengthField")?.Value;
+                var indexStr = qualityLevel.Attribute("Index")?.Value;
+                var codecPrivateData = qualityLevel.Attribute("CodecPrivateData")?.Value ?? "";
+                var audioTag = qualityLevel.Attribute("AudioTag")?.Value;
+                var bitrate = Convert.ToInt32(qualityLevel.Attribute("Bitrate")?.Value ?? "0");
+                var width = Convert.ToInt32(qualityLevel.Attribute("MaxWidth")?.Value ?? "0");
+                var height = Convert.ToInt32(qualityLevel.Attribute("MaxHeight")?.Value ?? "0");
+                var channels = qualityLevel.Attribute("Channels")?.Value;
 
-                //所有c节点
-                var cElements = streamIndex.Elements().Where(e => e.Name.LocalName == "c");
-
-                //所有QualityLevel节点
-                var qualityLevelElements = streamIndex.Elements().Where(e => e.Name.LocalName == "QualityLevel");
-
-                foreach (var qualityLevel in qualityLevelElements)
+                StreamSpec streamSpec = new();
+                streamSpec.PublishTime = DateTime.Now; // 发布时间默认现在
+                streamSpec.Extension = "m4s";
+                streamSpec.OriginalUrl = ParserConfig.OriginalUrl;
+                streamSpec.PeriodId = indexStr;
+                streamSpec.Playlist = new Playlist();
+                streamSpec.Playlist.IsLive = isLive;
+                streamSpec.Playlist.MediaParts.Add(new MediaPart());
+                streamSpec.GroupId = name ?? indexStr;
+                streamSpec.Bandwidth = bitrate;
+                streamSpec.Codecs = ParseCodecs(fourCC, codecPrivateData);
+                streamSpec.Language = language;
+                streamSpec.Resolution = width == 0 ? null : $"{width}x{height}";
+                streamSpec.Url = IsmUrl;
+                streamSpec.Channels = channels;
+                streamSpec.MediaType = type switch
                 {
-                    urlPattern = (qualityLevel.Attribute("Url")?.Value ?? urlPattern)!
-                        .Replace(MSSTags.Bitrate_BK, MSSTags.Bitrate).Replace(MSSTags.StartTime_BK, MSSTags.StartTime);
-                    var fourCC = qualityLevel.Attribute("FourCC")!.Value.ToUpper();
-                    var samplingRateStr = qualityLevel.Attribute("SamplingRate")?.Value;
-                    var bitsPerSampleStr = qualityLevel.Attribute("BitsPerSample")?.Value;
-                    var nalUnitLengthFieldStr = qualityLevel.Attribute("NALUnitLengthField")?.Value;
-                    var indexStr = qualityLevel.Attribute("Index")?.Value;
-                    var codecPrivateData = qualityLevel.Attribute("CodecPrivateData")?.Value ?? "";
-                    var audioTag = qualityLevel.Attribute("AudioTag")?.Value;
-                    var bitrate = Convert.ToInt32(qualityLevel.Attribute("Bitrate")?.Value ?? "0");
-                    var width = Convert.ToInt32(qualityLevel.Attribute("MaxWidth")?.Value ?? "0");
-                    var height = Convert.ToInt32(qualityLevel.Attribute("MaxHeight")?.Value ?? "0");
-                    var channels = qualityLevel.Attribute("Channels")?.Value;
+                    "text" => MediaType.SUBTITLES,
+                    "audio" => MediaType.AUDIO,
+                    _ => null
+                };
 
-                    StreamSpec streamSpec = new();
-                    streamSpec.PublishTime = DateTime.Now; //发布时间默认现在
-                    streamSpec.Extension = "m4s";
-                    streamSpec.OriginalUrl = ParserConfig.OriginalUrl;
-                    streamSpec.PeriodId = indexStr;
-                    streamSpec.Playlist = new Playlist();
-                    streamSpec.Playlist.IsLive = isLive;
-                    streamSpec.Playlist.MediaParts.Add(new MediaPart());
-                    streamSpec.GroupId = name ?? indexStr;
-                    streamSpec.Bandwidth = bitrate;
-                    streamSpec.Codecs = ParseCodecs(fourCC, codecPrivateData);
-                    streamSpec.Language = language;
-                    streamSpec.Resolution = width == 0 ? null : $"{width}x{height}";
-                    streamSpec.Url = IsmUrl;
-                    streamSpec.Channels = channels;
-                    streamSpec.MediaType = type switch
-                    {
-                        "text" => MediaType.SUBTITLES,
-                        "audio" => MediaType.AUDIO,
-                        _ => null
-                    };
+                streamSpec.Playlist.MediaInit = new MediaSegment();
+                if (!string.IsNullOrEmpty(codecPrivateData))
+                {
+                    streamSpec.Playlist.MediaInit.Index = -1; // 便于排序
+                    streamSpec.Playlist.MediaInit.Url = $"hex://{codecPrivateData}";
+                }
 
-                    streamSpec.Playlist.MediaInit = new MediaSegment();
-                    if (!string.IsNullOrEmpty(codecPrivateData))
+                var currentTime = 0L;
+                var segIndex = 0;
+                var varDic = new Dictionary<string, object?>();
+                varDic[MSSTags.Bitrate] = bitrate;
+
+                foreach (var c in cElements)
+                {
+                    // 每个C元素包含三个属性:@t(start time)\@r(repeat count)\@d(duration)
+                    var _startTimeStr = c.Attribute("t")?.Value;
+                    var _durationStr = c.Attribute("d")?.Value;
+                    var _repeatCountStr = c.Attribute("r")?.Value;
+
+                    if (_startTimeStr != null) currentTime = Convert.ToInt64(_startTimeStr);
+                    var _duration = Convert.ToInt64(_durationStr);
+                    var _repeatCount = Convert.ToInt64(_repeatCountStr);
+                    if (_repeatCount > 0)
                     {
-                        streamSpec.Playlist.MediaInit.Index = -1; //便于排序
-                        streamSpec.Playlist.MediaInit.Url = $"hex://{codecPrivateData}";
+                        // This value is one-based. (A value of 2 means two fragments in the contiguous series).
+                        _repeatCount -= 1;
                     }
 
-                    var currentTime = 0L;
-                    var segIndex = 0;
-                    var varDic = new Dictionary<string, object?>();
-                    varDic[MSSTags.Bitrate] = bitrate;
-
-                    foreach (var c in cElements)
+                    varDic[MSSTags.StartTime] = currentTime;
+                    var oriUrl = ParserUtil.CombineURL(this.BaseUrl, urlPattern!);
+                    var mediaUrl = ParserUtil.ReplaceVars(oriUrl, varDic);
+                    MediaSegment mediaSegment = new();
+                    mediaSegment.Url = mediaUrl;
+                    if (oriUrl.Contains(MSSTags.StartTime))
+                        mediaSegment.NameFromVar = currentTime.ToString();
+                    mediaSegment.Duration = _duration / (double)timescale;
+                    mediaSegment.Index = segIndex++;
+                    streamSpec.Playlist.MediaParts[0].MediaSegments.Add(mediaSegment);
+                    if (_repeatCount < 0)
+                    {
+                        // 负数表示一直重复 直到period结束 注意减掉已经加入的1个片段
+                        _repeatCount = (long)Math.Ceiling(Convert.ToInt64(durationStr) / (double)_duration) - 1;
+                    }
+                    for (long i = 0; i < _repeatCount; i++)
                     {
-                        //每个C元素包含三个属性:@t(start time)\@r(repeat count)\@d(duration)
-                        var _startTimeStr = c.Attribute("t")?.Value;
-                        var _durationStr = c.Attribute("d")?.Value;
-                        var _repeatCountStr = c.Attribute("r")?.Value;
-
-                        if (_startTimeStr != null) currentTime = Convert.ToInt64(_startTimeStr);
-                        var _duration = Convert.ToInt64(_durationStr);
-                        var _repeatCount = Convert.ToInt64(_repeatCountStr);
-                        if (_repeatCount > 0)
-                        {
-                            // This value is one-based. (A value of 2 means two fragments in the contiguous series).
-                            _repeatCount -= 1;
-                        }
-
-                        varDic[MSSTags.StartTime] = currentTime;
-                        var oriUrl = ParserUtil.CombineURL(this.BaseUrl, urlPattern!);
-                        var mediaUrl = ParserUtil.ReplaceVars(oriUrl, varDic);
-                        MediaSegment mediaSegment = new();
-                        mediaSegment.Url = mediaUrl;
-                        if (oriUrl.Contains(MSSTags.StartTime))
-                            mediaSegment.NameFromVar = currentTime.ToString();
-                        mediaSegment.Duration = _duration / (double)timescale;
-                        mediaSegment.Index = segIndex++;
-                        streamSpec.Playlist.MediaParts[0].MediaSegments.Add(mediaSegment);
-                        if (_repeatCount < 0)
-                        {
-                            //负数表示一直重复 直到period结束 注意减掉已经加入的1个片段
-                            _repeatCount = (long)Math.Ceiling(Convert.ToInt64(durationStr) / (double)_duration) - 1;
-                        }
-                        for (long i = 0; i < _repeatCount; i++)
-                        {
-                            currentTime += _duration;
-                            MediaSegment _mediaSegment = new();
-                            varDic[MSSTags.StartTime] = currentTime;
-                            var _oriUrl = ParserUtil.CombineURL(this.BaseUrl, urlPattern!);
-                            var _mediaUrl = ParserUtil.ReplaceVars(_oriUrl, varDic);
-                            _mediaSegment.Url = _mediaUrl;
-                            _mediaSegment.Index = segIndex++;
-                            _mediaSegment.Duration = _duration / (double)timescale;
-                            if (_oriUrl.Contains(MSSTags.StartTime))
-                                _mediaSegment.NameFromVar = currentTime.ToString();
-                            streamSpec.Playlist.MediaParts[0].MediaSegments.Add(_mediaSegment);
-                        }
                         currentTime += _duration;
+                        MediaSegment _mediaSegment = new();
+                        varDic[MSSTags.StartTime] = currentTime;
+                        var _oriUrl = ParserUtil.CombineURL(this.BaseUrl, urlPattern!);
+                        var _mediaUrl = ParserUtil.ReplaceVars(_oriUrl, varDic);
+                        _mediaSegment.Url = _mediaUrl;
+                        _mediaSegment.Index = segIndex++;
+                        _mediaSegment.Duration = _duration / (double)timescale;
+                        if (_oriUrl.Contains(MSSTags.StartTime))
+                            _mediaSegment.NameFromVar = currentTime.ToString();
+                        streamSpec.Playlist.MediaParts[0].MediaSegments.Add(_mediaSegment);
                     }
+                    currentTime += _duration;
+                }
 
-                    //生成MOOV数据
-                    if (MSSMoovProcessor.CanHandle(fourCC!))
+                // 生成MOOV数据
+                if (MSSMoovProcessor.CanHandle(fourCC!))
+                {
+                    streamSpec.MSSData = new MSSData()
                     {
-                        streamSpec.MSSData = new MSSData()
+                        FourCC = fourCC!,
+                        CodecPrivateData = codecPrivateData,
+                        Type = type!,
+                        Timesacle = Convert.ToInt32(timeScaleStr),
+                        Duration = Convert.ToInt64(durationStr),
+                        SamplingRate = Convert.ToInt32(samplingRateStr ?? "48000"),
+                        Channels = Convert.ToInt32(channels ?? "2"),
+                        BitsPerSample = Convert.ToInt32(bitsPerSampleStr ?? "16"),
+                        NalUnitLengthField = Convert.ToInt32(nalUnitLengthFieldStr ?? "4"),
+                        IsProtection = isProtection,
+                        ProtectionData = protectionData,
+                        ProtectionSystemID = protectionSystemId,
+                    };
+                    var processor = new MSSMoovProcessor(streamSpec);
+                    var header = processor.GenHeader(); // trackId可能不正确
+                    streamSpec.Playlist!.MediaInit!.Url = $"base64://{Convert.ToBase64String(header)}";
+                    // 为音视频写入加密信息
+                    if (isProtection && type != "text") 
+                    {
+                        if (streamSpec.Playlist.MediaInit != null)
                         {
-                            FourCC = fourCC!,
-                            CodecPrivateData = codecPrivateData,
-                            Type = type!,
-                            Timesacle = Convert.ToInt32(timeScaleStr),
-                            Duration = Convert.ToInt64(durationStr),
-                            SamplingRate = Convert.ToInt32(samplingRateStr ?? "48000"),
-                            Channels = Convert.ToInt32(channels ?? "2"),
-                            BitsPerSample = Convert.ToInt32(bitsPerSampleStr ?? "16"),
-                            NalUnitLengthField = Convert.ToInt32(nalUnitLengthFieldStr ?? "4"),
-                            IsProtection = isProtection,
-                            ProtectionData = protectionData,
-                            ProtectionSystemID = protectionSystemId,
-                        };
-                        var processor = new MSSMoovProcessor(streamSpec);
-                        var header = processor.GenHeader(); //trackId可能不正确
-                        streamSpec.Playlist!.MediaInit!.Url = $"base64://{Convert.ToBase64String(header)}";
-                        //为音视频写入加密信息
-                        if (isProtection && type != "text") 
-                        {
-                            if (streamSpec.Playlist.MediaInit != null)
-                            {
-                                streamSpec.Playlist.MediaInit.EncryptInfo.Method = DEFAULT_METHOD;
-                            }
-                            foreach (var item in streamSpec.Playlist.MediaParts[0].MediaSegments)
-                            {
-                                item.EncryptInfo.Method = DEFAULT_METHOD;
-                            }
+                            streamSpec.Playlist.MediaInit.EncryptInfo.Method = DEFAULT_METHOD;
                         }
-                        streamList.Add(streamSpec);
-                    }
-                    else
-                    {
-                        Logger.WarnMarkUp($"[green]{fourCC}[/] not supported! Skiped.");
-                        continue;
-                    }
-                }
-            }
-
-            //为视频设置默认轨道
-            var aL = streamList.Where(s => s.MediaType == MediaType.AUDIO);
-            var sL = streamList.Where(s => s.MediaType == MediaType.SUBTITLES);
-            foreach (var item in streamList)
-            {
-                if (!string.IsNullOrEmpty(item.Resolution))
-                {
-                    if (aL.Any())
-                    {
-                        item.AudioId = aL.First().GroupId;
-                    }
-                    if (sL.Any())
-                    {
-                        item.SubtitleId = sL.First().GroupId;
-                    }
-                }
-            }
-
-            return streamList;
-        }
-
-        /// <summary>
-        /// 解析编码
-        /// </summary>
-        /// <param name="fourCC"></param>
-        /// <returns></returns>
-        private static string? ParseCodecs(string fourCC, string? privateData)
-        {
-            if (fourCC == "TTML") return "stpp";
-            if (string.IsNullOrEmpty(privateData)) return null;
-
-            return fourCC switch
-            {
-                //AVC视频
-                "H264" or "X264" or "DAVC" or "AVC1" => ParseAVCCodecs(privateData),
-                //AAC音频
-                "AAC" or "AACL" or "AACH" or "AACP" => ParseAACCodecs(fourCC, privateData),
-                //默认返回fourCC本身
-                _ => fourCC.ToLower()
-            };
-        }
-
-        private static string ParseAVCCodecs(string privateData)
-        {
-            var result = VCodecsRegex().Match(privateData).Groups[1].Value;
-            return string.IsNullOrEmpty(result) ? "avc1.4D401E" : $"avc1.{result}";
-        }
-
-        private static string ParseAACCodecs(string fourCC, string privateData)
-        {
-            var mpProfile = 2;
-            if (fourCC == "AACH")
-            {
-                mpProfile = 5; // High Efficiency AAC Profile
-            }
-            else if (!string.IsNullOrEmpty(privateData)) 
-            {
-                mpProfile = (Convert.ToByte(privateData[..2], 16) & 0xF8) >> 3;
-            }
-
-            return $"mp4a.40.{mpProfile}";
-        }
-
-        public async Task FetchPlayListAsync(List<StreamSpec> streamSpecs)
-        {
-            //这里才调用URL预处理器,节省开销
-            await ProcessUrlAsync(streamSpecs);
-        }
-
-        private async Task ProcessUrlAsync(List<StreamSpec> streamSpecs)
-        {
-            for (int i = 0; i < streamSpecs.Count; i++)
-            {
-                var playlist = streamSpecs[i].Playlist;
-                if (playlist != null)
-                {
-                    if (playlist.MediaInit != null)
-                    {
-                        playlist.MediaInit!.Url = PreProcessUrl(playlist.MediaInit!.Url);
-                    }
-                    for (int ii = 0; ii < playlist!.MediaParts.Count; ii++)
-                    {
-                        var part = playlist.MediaParts[ii];
-                        for (int iii = 0; iii < part.MediaSegments.Count; iii++)
+                        foreach (var item in streamSpec.Playlist.MediaParts[0].MediaSegments)
                         {
-                            part.MediaSegments[iii].Url = PreProcessUrl(part.MediaSegments[iii].Url);
+                            item.EncryptInfo.Method = DEFAULT_METHOD;
                         }
                     }
+                    streamList.Add(streamSpec);
                 }
-            }
-        }
-
-        public string PreProcessUrl(string url)
-        {
-            foreach (var p in ParserConfig.UrlProcessors)
-            {
-                if (p.CanProcess(ExtractorType, url, ParserConfig))
+                else
                 {
-                    url = p.Process(url, ParserConfig);
+                    Logger.WarnMarkUp($"[green]{fourCC}[/] not supported! Skiped.");
                 }
             }
-
-            return url;
         }
 
-        public void PreProcessContent()
+        // 为视频设置默认轨道
+        var aL = streamList.Where(s => s.MediaType == MediaType.AUDIO);
+        var sL = streamList.Where(s => s.MediaType == MediaType.SUBTITLES);
+        foreach (var item in streamList)
         {
-            foreach (var p in ParserConfig.ContentProcessors)
+            if (!string.IsNullOrEmpty(item.Resolution))
             {
-                if (p.CanProcess(ExtractorType, IsmContent, ParserConfig))
+                if (aL.Any())
                 {
-                    IsmContent = p.Process(IsmContent, ParserConfig);
+                    item.AudioId = aL.First().GroupId;
+                }
+                if (sL.Any())
+                {
+                    item.SubtitleId = sL.First().GroupId;
                 }
             }
         }
 
-        public async Task RefreshPlayListAsync(List<StreamSpec> streamSpecs)
+        return Task.FromResult(streamList);
+    }
+
+    /// <summary>
+    /// 解析编码
+    /// </summary>
+    /// <param name="fourCC"></param>
+    /// <returns></returns>
+    private static string? ParseCodecs(string fourCC, string? privateData)
+    {
+        if (fourCC == "TTML") return "stpp";
+        if (string.IsNullOrEmpty(privateData)) return null;
+
+        return fourCC switch
         {
-            if (streamSpecs.Count == 0) return;
+            // AVC视频
+            "H264" or "X264" or "DAVC" or "AVC1" => ParseAVCCodecs(privateData),
+            // AAC音频
+            "AAC" or "AACL" or "AACH" or "AACP" => ParseAACCodecs(fourCC, privateData),
+            // 默认返回fourCC本身
+            _ => fourCC.ToLower()
+        };
+    }
 
-            var (rawText, url) = ("", ParserConfig.Url);
-            try
+    private static string ParseAVCCodecs(string privateData)
+    {
+        var result = VCodecsRegex().Match(privateData).Groups[1].Value;
+        return string.IsNullOrEmpty(result) ? "avc1.4D401E" : $"avc1.{result}";
+    }
+
+    private static string ParseAACCodecs(string fourCC, string privateData)
+    {
+        var mpProfile = 2;
+        if (fourCC == "AACH")
+        {
+            mpProfile = 5; // High Efficiency AAC Profile
+        }
+        else if (!string.IsNullOrEmpty(privateData)) 
+        {
+            mpProfile = (Convert.ToByte(privateData[..2], 16) & 0xF8) >> 3;
+        }
+
+        return $"mp4a.40.{mpProfile}";
+    }
+
+    public async Task FetchPlayListAsync(List<StreamSpec> streamSpecs)
+    {
+        // 这里才调用URL预处理器,节省开销
+        await ProcessUrlAsync(streamSpecs);
+    }
+
+    private Task ProcessUrlAsync(List<StreamSpec> streamSpecs)
+    {
+        for (int i = 0; i < streamSpecs.Count; i++)
+        {
+            var playlist = streamSpecs[i].Playlist;
+            if (playlist != null)
             {
-                (rawText, url) = await HTTPUtil.GetWebSourceAndNewUrlAsync(ParserConfig.Url, ParserConfig.Headers);
+                if (playlist.MediaInit != null)
+                {
+                    playlist.MediaInit!.Url = PreProcessUrl(playlist.MediaInit!.Url);
+                }
+                for (int ii = 0; ii < playlist!.MediaParts.Count; ii++)
+                {
+                    var part = playlist.MediaParts[ii];
+                    for (int iii = 0; iii < part.MediaSegments.Count; iii++)
+                    {
+                        part.MediaSegments[iii].Url = PreProcessUrl(part.MediaSegments[iii].Url);
+                    }
+                }
             }
-            catch (HttpRequestException) when (ParserConfig.Url != ParserConfig.OriginalUrl)
+        }
+
+        return Task.CompletedTask;
+    }
+
+    public string PreProcessUrl(string url)
+    {
+        foreach (var p in ParserConfig.UrlProcessors)
+        {
+            if (p.CanProcess(ExtractorType, url, ParserConfig))
             {
-                //当URL无法访问时,再请求原始URL
-                (rawText, url) = await HTTPUtil.GetWebSourceAndNewUrlAsync(ParserConfig.OriginalUrl, ParserConfig.Headers);
+                url = p.Process(url, ParserConfig);
             }
+        }
 
-            ParserConfig.Url = url;
-            SetInitUrl();
+        return url;
+    }
 
-            var newStreams = await ExtractStreamsAsync(rawText);
-            foreach (var streamSpec in streamSpecs)
+    public void PreProcessContent()
+    {
+        foreach (var p in ParserConfig.ContentProcessors)
+        {
+            if (p.CanProcess(ExtractorType, IsmContent, ParserConfig))
             {
-                //有的网站每次请求MPD返回的码率不一致,导致ToShortString()无法匹配 无法更新playlist
-                //故增加通过init url来匹配 (如果有的话)
-                var match = newStreams.Where(n => n.ToShortString() == streamSpec.ToShortString());
-                if (!match.Any())
-                    match = newStreams.Where(n => n.Playlist?.MediaInit?.Url == streamSpec.Playlist?.MediaInit?.Url);
-
-                if (match.Any())
-                    streamSpec.Playlist!.MediaParts = match.First().Playlist!.MediaParts; //不更新init
+                IsmContent = p.Process(IsmContent, ParserConfig);
             }
-            //这里才调用URL预处理器,节省开销
-            await ProcessUrlAsync(streamSpecs);
         }
     }
-}
+
+    public async Task RefreshPlayListAsync(List<StreamSpec> streamSpecs)
+    {
+        if (streamSpecs.Count == 0) return;
+
+        var (rawText, url) = ("", ParserConfig.Url);
+        try
+        {
+            (rawText, url) = await HTTPUtil.GetWebSourceAndNewUrlAsync(ParserConfig.Url, ParserConfig.Headers);
+        }
+        catch (HttpRequestException) when (ParserConfig.Url != ParserConfig.OriginalUrl)
+        {
+            // 当URL无法访问时,再请求原始URL
+            (rawText, url) = await HTTPUtil.GetWebSourceAndNewUrlAsync(ParserConfig.OriginalUrl, ParserConfig.Headers);
+        }
+
+        ParserConfig.Url = url;
+        SetInitUrl();
+
+        var newStreams = await ExtractStreamsAsync(rawText);
+        foreach (var streamSpec in streamSpecs)
+        {
+            // 有的网站每次请求MPD返回的码率不一致,导致ToShortString()无法匹配 无法更新playlist
+            // 故增加通过init url来匹配 (如果有的话)
+            var match = newStreams.Where(n => n.ToShortString() == streamSpec.ToShortString());
+            if (!match.Any())
+                match = newStreams.Where(n => n.Playlist?.MediaInit?.Url == streamSpec.Playlist?.MediaInit?.Url);
+
+            if (match.Any())
+                streamSpec.Playlist!.MediaParts = match.First().Playlist!.MediaParts; // 不更新init
+        }
+        // 这里才调用URL预处理器,节省开销
+        await ProcessUrlAsync(streamSpecs);
+    }
+}
\ No newline at end of file
diff --git a/src/N_m3u8DL-RE.Parser/Mp4/BinaryReader2.cs b/src/N_m3u8DL-RE.Parser/Mp4/BinaryReader2.cs
index e0eceda..4d66979 100644
--- a/src/N_m3u8DL-RE.Parser/Mp4/BinaryReader2.cs
+++ b/src/N_m3u8DL-RE.Parser/Mp4/BinaryReader2.cs
@@ -7,7 +7,7 @@ using System.Threading.Tasks;
 
 namespace Mp4SubtitleParser
 {
-    //make BinaryReader in Big Endian
+    // make BinaryReader in Big Endian
     class BinaryReader2 : BinaryReader
     {
         public BinaryReader2(System.IO.Stream stream) : base(stream) { }
diff --git a/src/N_m3u8DL-RE.Parser/Mp4/BinaryWriter2.cs b/src/N_m3u8DL-RE.Parser/Mp4/BinaryWriter2.cs
index 3ddd3ea..f185779 100644
--- a/src/N_m3u8DL-RE.Parser/Mp4/BinaryWriter2.cs
+++ b/src/N_m3u8DL-RE.Parser/Mp4/BinaryWriter2.cs
@@ -7,7 +7,7 @@ using System.Threading.Tasks;
 
 namespace Mp4SubtitleParser
 {
-    //make BinaryWriter in Big Endian
+    // make BinaryWriter in Big Endian
     class BinaryWriter2 : BinaryWriter
     {
         private static bool IsLittleEndian = BitConverter.IsLittleEndian;
diff --git a/src/N_m3u8DL-RE.Parser/Mp4/MP4InitUtil.cs b/src/N_m3u8DL-RE.Parser/Mp4/MP4InitUtil.cs
index 3e5ea62..864672e 100644
--- a/src/N_m3u8DL-RE.Parser/Mp4/MP4InitUtil.cs
+++ b/src/N_m3u8DL-RE.Parser/Mp4/MP4InitUtil.cs
@@ -20,7 +20,7 @@ namespace Mp4SubtitleParser
         {
             var info = new ParsedMP4Info();
 
-            //parse init
+            // parse init
             new MP4Parser()
                 .Box("moov", MP4Parser.Children)
                 .Box("trak", MP4Parser.Children)
@@ -56,7 +56,7 @@ namespace Mp4SubtitleParser
 
         private static void ReadBox(byte[] data, ParsedMP4Info info)
         {
-            //find schm 
+            // find schm 
             var schmBytes = new byte[4] { 0x73, 0x63, 0x68, 0x6d };
             var schmIndex = 0;
             for (int i = 0; i < data.Length - 4; i++) 
@@ -72,9 +72,9 @@ namespace Mp4SubtitleParser
                 info.Scheme = System.Text.Encoding.UTF8.GetString(data[schmIndex..][8..12]);
             }
 
-            //if (info.Scheme != "cenc") return;
+            // if (info.Scheme != "cenc") return;
 
-            //find KID
+            // find KID
             var tencBytes = new byte[4] { 0x74, 0x65, 0x6E, 0x63 };
             var tencIndex = -1;
             for (int i = 0; i < data.Length - 4; i++)
diff --git a/src/N_m3u8DL-RE.Parser/Mp4/MP4Parser.cs b/src/N_m3u8DL-RE.Parser/Mp4/MP4Parser.cs
index fd1ef78..e5ae4b5 100644
--- a/src/N_m3u8DL-RE.Parser/Mp4/MP4Parser.cs
+++ b/src/N_m3u8DL-RE.Parser/Mp4/MP4Parser.cs
@@ -111,7 +111,7 @@ namespace Mp4SubtitleParser
             var name = TypeToString(type);
             var has64BitSize = false;
 
-            //Console.WriteLine($"Parsing MP4 box: {name}");
+            // Console.WriteLine($"Parsing MP4 box: {name}");
 
             switch (size)
             {
@@ -129,8 +129,7 @@ namespace Mp4SubtitleParser
                     break;
             }
 
-            BoxHandler boxDefinition = null;
-            this.BoxDefinitions.TryGetValue(type, out boxDefinition);
+            this.BoxDefinitions.TryGetValue(type, out BoxHandler? boxDefinition);
 
             if (boxDefinition != null)
             {
diff --git a/src/N_m3u8DL-RE.Parser/Mp4/MP4TtmlUtil.cs b/src/N_m3u8DL-RE.Parser/Mp4/MP4TtmlUtil.cs
index 427da8d..378a085 100644
--- a/src/N_m3u8DL-RE.Parser/Mp4/MP4TtmlUtil.cs
+++ b/src/N_m3u8DL-RE.Parser/Mp4/MP4TtmlUtil.cs
@@ -10,8 +10,8 @@ namespace Mp4SubtitleParser
         public string Begin { get; set; }
         public string End { get; set; }
         public string Region { get; set; }
-        public List<XmlElement> Contents { get; set; } = new List<XmlElement>();
-        public List<string> ContentStrings { get; set; } = new List<string>();
+        public List<XmlElement> Contents { get; set; } = new();
+        public List<string> ContentStrings { get; set; } = new();
 
         public override bool Equals(object? obj)
         {
@@ -43,7 +43,7 @@ namespace Mp4SubtitleParser
         {
             bool sawSTPP = false;
 
-            //parse init
+            // parse init
             new MP4Parser()
                 .Box("moov", MP4Parser.Children)
                 .Box("trak", MP4Parser.Children)
@@ -85,12 +85,12 @@ namespace Mp4SubtitleParser
                 return xmlSrc;
 
             var _div = bodyNode.SelectSingleNode("ns:div", nsMgr);
-            //Parse <p> label
+            // Parse <p> label
             foreach (XmlElement _p in _div!.SelectNodes("ns:p", nsMgr)!)
             {
                 var _begin = _p.GetAttribute("begin");
                 var _end = _p.GetAttribute("end");
-                //Handle namespace
+                // Handle namespace
                 foreach (XmlAttribute attr in _p.Attributes)
                 {
                     if (attr.LocalName == "begin") _begin = attr.Value;
@@ -98,8 +98,8 @@ namespace Mp4SubtitleParser
                 }
                 _p.SetAttribute("begin", Add(_begin));
                 _p.SetAttribute("end", Add(_end));
-                //Console.WriteLine($"{_begin} {_p.GetAttribute("begin")}");
-                //Console.WriteLine($"{_end} {_p.GetAttribute("begin")}");
+                // Console.WriteLine($"{_begin} {_p.GetAttribute("begin")}");
+                // Console.WriteLine($"{_end} {_p.GetAttribute("begin")}");
             }
 
             return xmlDoc.OuterXml;
@@ -135,7 +135,7 @@ namespace Mp4SubtitleParser
 
         public static WebVttSub ExtractFromMp4s(IEnumerable<string> items, long segTimeMs, long baseTimestamp = 0L)
         {
-            //read ttmls
+            // read ttmls
             List<string> xmls = new List<string>();
             int segIndex = 0;
             foreach (var item in items)
@@ -143,7 +143,7 @@ namespace Mp4SubtitleParser
                 var dataSeg = File.ReadAllBytes(item);
 
                 var sawMDAT = false;
-                //parse media
+                // parse media
                 new MP4Parser()
                     .Box("mdat", MP4Parser.AllData((data) =>
                     {
@@ -161,10 +161,7 @@ namespace Mp4SubtitleParser
                         else
                         {
                             var datas = SplitMultipleRootElements(Encoding.UTF8.GetString(data));
-                            foreach (var item in datas)
-                            {
-                                xmls.Add(item);
-                            }
+                            xmls.AddRange(datas);
                         }
                     }))
                     .Parse(dataSeg,/* partialOkay= */ false);
@@ -181,7 +178,7 @@ namespace Mp4SubtitleParser
 
         public static WebVttSub ExtractFromTTMLs(IEnumerable<string> items, long segTimeMs, long baseTimestamp = 0L)
         {
-            //read ttmls
+            // read ttmls
             List<string> xmls = new List<string>();
             int segIndex = 0;
             foreach (var item in items)
@@ -203,7 +200,7 @@ namespace Mp4SubtitleParser
 
         private static WebVttSub ExtractSub(List<string> xmls, long baseTimestamp)
         {
-            //parsing
+            // parsing
             var xmlDoc = new XmlDocument();
             var finalSubs = new List<SubEntity>();
             XmlNode? headNode = null;
@@ -215,7 +212,7 @@ namespace Mp4SubtitleParser
                 var xmlContent = item;
                 if (!xmlContent.Contains("<tt")) continue;
 
-                //fix non-standard xml 
+                // fix non-standard xml 
                 var xmlContentFix = xmlContent;
                 if (regex.IsMatch(xmlContent))
                 {
@@ -256,8 +253,8 @@ namespace Mp4SubtitleParser
                     continue;
 
 
-                //PNG Subs
-                var imageDic = new Dictionary<string, string>(); //id, Base64
+                // PNG Subs
+                var imageDic = new Dictionary<string, string>(); // id, Base64
                 if (ImageRegex().IsMatch(xmlDoc.InnerXml))
                 {
                     foreach (Match img in ImageRegex().Matches(xmlDoc.InnerXml))
@@ -266,7 +263,7 @@ namespace Mp4SubtitleParser
                     }
                 }
 
-                //convert <div> to <p>
+                // convert <div> to <p>
                 if (_div!.SelectNodes("ns:p", nsMgr) == null || _div!.SelectNodes("ns:p", nsMgr)!.Count == 0)
                 {
                     foreach (XmlElement _tDiv in bodyNode.SelectNodes("ns:div", nsMgr)!)
@@ -277,14 +274,14 @@ namespace Mp4SubtitleParser
                     }
                 }
 
-                //Parse <p> label
+                // Parse <p> label
                 foreach (XmlElement _p in _div!.SelectNodes("ns:p", nsMgr)!)
                 {
                     var _begin = _p.GetAttribute("begin");
                     var _end = _p.GetAttribute("end");
                     var _region = _p.GetAttribute("region");
                     var _bgImg = _p.GetAttribute("smpte:backgroundImage");
-                    //Handle namespace
+                    // Handle namespace
                     foreach (XmlAttribute attr in _p.Attributes)
                     {
                         if (attr.LocalName == "begin") _begin = attr.Value;
@@ -301,7 +298,7 @@ namespace Mp4SubtitleParser
                     if (string.IsNullOrEmpty(_bgImg))
                     {
                         var _spans = _p.ChildNodes;
-                        //Collect <span>
+                        // Collect <span>
                         foreach (XmlNode _node in _spans)
                         {
                             if (_node.NodeType == XmlNodeType.Element)
@@ -333,12 +330,12 @@ namespace Mp4SubtitleParser
                         }
                     }
                     
-                    //Check if one <p> has been splitted
+                    // Check if one <p> has been splitted
                     var index = finalSubs.FindLastIndex(s => s.End == _begin && s.Region == _region && s.ContentStrings.SequenceEqual(sub.ContentStrings));
-                    //Skip empty lines
+                    // Skip empty lines
                     if (sub.ContentStrings.Count > 0)
                     {
-                        //Extend <p> duration
+                        // Extend <p> duration
                         if (index != -1)
                             finalSubs[index].End = sub.End;
                         else if (!finalSubs.Contains(sub))
@@ -372,7 +369,7 @@ namespace Mp4SubtitleParser
             }
 
 
-            StringBuilder vtt = new StringBuilder();
+            var vtt = new StringBuilder();
             vtt.AppendLine("WEBVTT");
             foreach (var item in dic)
             {
diff --git a/src/N_m3u8DL-RE.Parser/Mp4/MP4VttUtil.cs b/src/N_m3u8DL-RE.Parser/Mp4/MP4VttUtil.cs
index fdb62e3..d51842f 100644
--- a/src/N_m3u8DL-RE.Parser/Mp4/MP4VttUtil.cs
+++ b/src/N_m3u8DL-RE.Parser/Mp4/MP4VttUtil.cs
@@ -1,216 +1,215 @@
 using N_m3u8DL_RE.Common.Entity;
 using System.Text;
 
-namespace Mp4SubtitleParser
+namespace Mp4SubtitleParser;
+
+public class MP4VttUtil
 {
-    public class MP4VttUtil
+    public static (bool, uint) CheckInit(byte[] data)
     {
-        public static (bool, uint) CheckInit(byte[] data)
-        {
-            uint timescale = 0;
-            bool sawWVTT = false;
+        uint timescale = 0;
+        bool sawWVTT = false;
 
-            //parse init
-            new MP4Parser()
-                .Box("moov", MP4Parser.Children)
-                .Box("trak", MP4Parser.Children)
-                .Box("mdia", MP4Parser.Children)
-                .FullBox("mdhd", (box) =>
-                {
-                    if (!(box.Version == 0 || box.Version == 1))
-                        throw new Exception("MDHD version can only be 0 or 1");
-                    timescale = MP4Parser.ParseMDHD(box.Reader, box.Version);
-                })
-                .Box("minf", MP4Parser.Children)
-                .Box("stbl", MP4Parser.Children)
-                .FullBox("stsd", MP4Parser.SampleDescription)
-                .Box("wvtt", (box) => {
-                    // A valid vtt init segment, though we have no actual subtitles yet.
-                    sawWVTT = true;
-                })
-                .Parse(data);
-
-            return (sawWVTT, timescale);
-        }
-
-        public static WebVttSub ExtractSub(IEnumerable<string> files, uint timescale)
-        {
-            if (timescale == 0)
-                throw new Exception("Missing timescale for VTT content!");
-
-            List<SubCue> cues = new();
-
-            foreach (var item in files)
+        // parse init
+        new MP4Parser()
+            .Box("moov", MP4Parser.Children)
+            .Box("trak", MP4Parser.Children)
+            .Box("mdia", MP4Parser.Children)
+            .FullBox("mdhd", (box) =>
             {
-                var dataSeg = File.ReadAllBytes(item);
+                if (!(box.Version == 0 || box.Version == 1))
+                    throw new Exception("MDHD version can only be 0 or 1");
+                timescale = MP4Parser.ParseMDHD(box.Reader, box.Version);
+            })
+            .Box("minf", MP4Parser.Children)
+            .Box("stbl", MP4Parser.Children)
+            .FullBox("stsd", MP4Parser.SampleDescription)
+            .Box("wvtt", (box) => {
+                // A valid vtt init segment, though we have no actual subtitles yet.
+                sawWVTT = true;
+            })
+            .Parse(data);
 
-                bool sawTFDT = false;
-                bool sawTRUN = false;
-                bool sawMDAT = false;
-                byte[]? rawPayload = null;
-                ulong baseTime = 0;
-                ulong defaultDuration = 0;
-                List<Sample> presentations = new();
+        return (sawWVTT, timescale);
+    }
+
+    public static WebVttSub ExtractSub(IEnumerable<string> files, uint timescale)
+    {
+        if (timescale == 0)
+            throw new Exception("Missing timescale for VTT content!");
+
+        List<SubCue> cues = new();
+
+        foreach (var item in files)
+        {
+            var dataSeg = File.ReadAllBytes(item);
+
+            bool sawTFDT = false;
+            bool sawTRUN = false;
+            bool sawMDAT = false;
+            byte[]? rawPayload = null;
+            ulong baseTime = 0;
+            ulong defaultDuration = 0;
+            List<Sample> presentations = new();
 
 
-                //parse media
-                new MP4Parser()
-                    .Box("moof", MP4Parser.Children)
-                    .Box("traf", MP4Parser.Children)
-                    .FullBox("tfdt", (box) =>
-                    {
-                        sawTFDT = true;
-                        if (!(box.Version == 0 || box.Version == 1))
-                            throw new Exception("TFDT version can only be 0 or 1");
-                        baseTime = MP4Parser.ParseTFDT(box.Reader, box.Version);
-                    })
-                    .FullBox("tfhd", (box) =>
-                    {
-                        if (box.Flags == 1000)
-                            throw new Exception("A TFHD box should have a valid flags value");
-                        defaultDuration = MP4Parser.ParseTFHD(box.Reader, box.Flags).DefaultSampleDuration;
-                    })
-                    .FullBox("trun", (box) =>
-                    {
-                        sawTRUN = true;
-                        if (box.Version == 1000)
-                            throw new Exception("A TRUN box should have a valid version value");
-                        if (box.Flags == 1000)
-                            throw new Exception("A TRUN box should have a valid flags value");
-                        presentations = MP4Parser.ParseTRUN(box.Reader, box.Version, box.Flags).SampleData;
-                    })
-                    .Box("mdat", MP4Parser.AllData((data) =>
-                    {
-                        if (sawMDAT)
-                            throw new Exception("VTT cues in mp4 with multiple MDAT are not currently supported");
-                        sawMDAT = true;
-                        rawPayload = data;
-                    }))
-                    .Parse(dataSeg,/* partialOkay= */ false);
-
-                if (!sawMDAT && !sawTFDT && !sawTRUN)
+            // parse media
+            new MP4Parser()
+                .Box("moof", MP4Parser.Children)
+                .Box("traf", MP4Parser.Children)
+                .FullBox("tfdt", (box) =>
                 {
-                    throw new Exception("A required box is missing");
-                }
-
-                var currentTime = baseTime;
-                var reader = new BinaryReader2(new MemoryStream(rawPayload!));
-
-                foreach (var presentation in presentations)
+                    sawTFDT = true;
+                    if (!(box.Version == 0 || box.Version == 1))
+                        throw new Exception("TFDT version can only be 0 or 1");
+                    baseTime = MP4Parser.ParseTFDT(box.Reader, box.Version);
+                })
+                .FullBox("tfhd", (box) =>
                 {
-                    var duration = presentation.SampleDuration == 0 ? defaultDuration : presentation.SampleDuration;
-                    var startTime = presentation.SampleCompositionTimeOffset != 0 ?
-                          baseTime + presentation.SampleCompositionTimeOffset :
-                          currentTime;
-                    currentTime = startTime + duration;
-                    var totalSize = 0;
-                    do
+                    if (box.Flags == 1000)
+                        throw new Exception("A TFHD box should have a valid flags value");
+                    defaultDuration = MP4Parser.ParseTFHD(box.Reader, box.Flags).DefaultSampleDuration;
+                })
+                .FullBox("trun", (box) =>
+                {
+                    sawTRUN = true;
+                    if (box.Version == 1000)
+                        throw new Exception("A TRUN box should have a valid version value");
+                    if (box.Flags == 1000)
+                        throw new Exception("A TRUN box should have a valid flags value");
+                    presentations = MP4Parser.ParseTRUN(box.Reader, box.Version, box.Flags).SampleData;
+                })
+                .Box("mdat", MP4Parser.AllData((data) =>
+                {
+                    if (sawMDAT)
+                        throw new Exception("VTT cues in mp4 with multiple MDAT are not currently supported");
+                    sawMDAT = true;
+                    rawPayload = data;
+                }))
+                .Parse(dataSeg,/* partialOkay= */ false);
+
+            if (!sawMDAT && !sawTFDT && !sawTRUN)
+            {
+                throw new Exception("A required box is missing");
+            }
+
+            var currentTime = baseTime;
+            var reader = new BinaryReader2(new MemoryStream(rawPayload!));
+
+            foreach (var presentation in presentations)
+            {
+                var duration = presentation.SampleDuration == 0 ? defaultDuration : presentation.SampleDuration;
+                var startTime = presentation.SampleCompositionTimeOffset != 0 ?
+                    baseTime + presentation.SampleCompositionTimeOffset :
+                    currentTime;
+                currentTime = startTime + duration;
+                var totalSize = 0;
+                do
+                {
+                    // Read the payload size.
+                    var payloadSize = (int)reader.ReadUInt32();
+                    totalSize += payloadSize;
+
+                    // Skip the type.
+                    var payloadType = reader.ReadUInt32();
+                    var payloadName = MP4Parser.TypeToString(payloadType);
+
+                    // Read the data payload.
+                    byte[]? payload = null;
+                    if (payloadName == "vttc")
                     {
-                        // Read the payload size.
-                        var payloadSize = (int)reader.ReadUInt32();
-                        totalSize += payloadSize;
-
-                        // Skip the type.
-                        var payloadType = reader.ReadUInt32();
-                        var payloadName = MP4Parser.TypeToString(payloadType);
-
-                        // Read the data payload.
-                        byte[]? payload = null;
-                        if (payloadName == "vttc")
+                        if (payloadSize > 8)
                         {
-                            if (payloadSize > 8)
+                            payload = reader.ReadBytes(payloadSize - 8);
+                        }
+                    }
+                    else if (payloadName == "vtte")
+                    {
+                        // It's a vtte, which is a vtt cue that is empty. Ignore any data that
+                        // does exist.
+                        reader.ReadBytes(payloadSize - 8);
+                    }
+                    else
+                    {
+                        Console.WriteLine($"Unknown box {payloadName}! Skipping!");
+                        reader.ReadBytes(payloadSize - 8);
+                    }
+
+                    if (duration != 0)
+                    {
+                        if (payload != null)
+                        {
+                            if (timescale == 0)
+                                throw new Exception("Timescale should not be zero!");
+                            var cue = ParseVTTC(
+                                payload,
+                                0 + (double)startTime / timescale,
+                                0 + (double)currentTime / timescale);
+                            // Check if same subtitle has been splitted
+                            if (cue != null)
                             {
-                                payload = reader.ReadBytes(payloadSize - 8);
-                            }
-                        }
-                        else if (payloadName == "vtte")
-                        {
-                            // It's a vtte, which is a vtt cue that is empty. Ignore any data that
-                            // does exist.
-                            reader.ReadBytes(payloadSize - 8);
-                        }
-                        else
-                        {
-                            Console.WriteLine($"Unknown box {payloadName}! Skipping!");
-                            reader.ReadBytes(payloadSize - 8);
-                        }
-
-                        if (duration != 0)
-                        {
-                            if (payload != null)
-                            {
-                                if (timescale == 0)
-                                    throw new Exception("Timescale should not be zero!");
-                                var cue = ParseVTTC(
-                                    payload,
-                                    0 + (double)startTime / timescale,
-                                    0 + (double)currentTime / timescale);
-                                //Check if same subtitle has been splitted
-                                if (cue != null)
+                                var index = cues.FindLastIndex(s => s.EndTime == cue.StartTime && s.Settings == cue.Settings && s.Payload == cue.Payload);
+                                if (index != -1)
                                 {
-                                    var index = cues.FindLastIndex(s => s.EndTime == cue.StartTime && s.Settings == cue.Settings && s.Payload == cue.Payload);
-                                    if (index != -1)
-                                    {
-                                        cues[index].EndTime = cue.EndTime;
-                                    }
-                                    else
-                                    {
-                                        cues.Add(cue);
-                                    }
+                                    cues[index].EndTime = cue.EndTime;
+                                }
+                                else
+                                {
+                                    cues.Add(cue);
                                 }
                             }
                         }
-                        else
-                        {
-                            throw new Exception("WVTT sample duration unknown, and no default found!");
-                        }
-
-                        if (!(presentation.SampleSize == 0 || totalSize <= presentation.SampleSize))
-                        {
-                            throw new Exception("The samples do not fit evenly into the sample sizes given in the TRUN box!");
-                        }
-
-                    } while (presentation.SampleSize != 0 && (totalSize < presentation.SampleSize));
-
-                    if (reader.HasMoreData())
-                    {
-                        //throw new Exception("MDAT which contain VTT cues and non-VTT data are not currently supported!");
                     }
+                    else
+                    {
+                        throw new Exception("WVTT sample duration unknown, and no default found!");
+                    }
+
+                    if (!(presentation.SampleSize == 0 || totalSize <= presentation.SampleSize))
+                    {
+                        throw new Exception("The samples do not fit evenly into the sample sizes given in the TRUN box!");
+                    }
+
+                } while (presentation.SampleSize != 0 && (totalSize < presentation.SampleSize));
+
+                if (reader.HasMoreData())
+                {
+                    // throw new Exception("MDAT which contain VTT cues and non-VTT data are not currently supported!");
                 }
             }
-
-            if (cues.Count > 0)
-            {
-                return new WebVttSub() { Cues = cues };
-            }
-            return new WebVttSub();
         }
 
-        private static SubCue? ParseVTTC(byte[] data, double startTime, double endTime)
+        if (cues.Count > 0)
         {
-            string payload = string.Empty;
-            string id = string.Empty;
-            string settings = string.Empty;
-            new MP4Parser()
-                .Box("payl", MP4Parser.AllData((data) =>
-                {
-                    payload = Encoding.UTF8.GetString(data);
-                }))
-                .Box("iden", MP4Parser.AllData((data) =>
-                {
-                    id = Encoding.UTF8.GetString(data);
-                }))
-                .Box("sttg", MP4Parser.AllData((data) =>
-                {
-                    settings = Encoding.UTF8.GetString(data);
-                }))
-                .Parse(data);
-
-            if (!string.IsNullOrEmpty(payload))
-            {
-                return new SubCue() { StartTime = TimeSpan.FromSeconds(startTime), EndTime = TimeSpan.FromSeconds(endTime), Payload = payload, Settings = settings };
-            }
-            return null;
+            return new WebVttSub() { Cues = cues };
         }
+        return new WebVttSub();
     }
-}
+
+    private static SubCue? ParseVTTC(byte[] data, double startTime, double endTime)
+    {
+        string payload = string.Empty;
+        string id = string.Empty;
+        string settings = string.Empty;
+        new MP4Parser()
+            .Box("payl", MP4Parser.AllData((data) =>
+            {
+                payload = Encoding.UTF8.GetString(data);
+            }))
+            .Box("iden", MP4Parser.AllData((data) =>
+            {
+                id = Encoding.UTF8.GetString(data);
+            }))
+            .Box("sttg", MP4Parser.AllData((data) =>
+            {
+                settings = Encoding.UTF8.GetString(data);
+            }))
+            .Parse(data);
+
+        if (!string.IsNullOrEmpty(payload))
+        {
+            return new SubCue() { StartTime = TimeSpan.FromSeconds(startTime), EndTime = TimeSpan.FromSeconds(endTime), Payload = payload, Settings = settings };
+        }
+        return null;
+    }
+}
\ No newline at end of file
diff --git a/src/N_m3u8DL-RE.Parser/Mp4/MSSMoovProcessor.cs b/src/N_m3u8DL-RE.Parser/Mp4/MSSMoovProcessor.cs
index 82f6428..e8b6571 100644
--- a/src/N_m3u8DL-RE.Parser/Mp4/MSSMoovProcessor.cs
+++ b/src/N_m3u8DL-RE.Parser/Mp4/MSSMoovProcessor.cs
@@ -4,888 +4,887 @@ using N_m3u8DL_RE.Common.Util;
 using System.Text;
 using System.Text.RegularExpressions;
 
-//https://github.com/canalplus/rx-player/blob/48d1f845064cea5c5a3546d2c53b1855c2be149d/src/parsers/manifest/smooth/get_codecs.ts
-//https://github.dev/Dash-Industry-Forum/dash.js/blob/2aad3e79079b4de0bcd961ce6b4957103d98a621/src/mss/MssFragmentMoovProcessor.js
-//https://github.com/yt-dlp/yt-dlp/blob/3639df54c3298e35b5ae2a96a25bc4d3c38950d0/yt_dlp/downloader/ism.py
-//https://github.com/google/ExoPlayer/blob/a9444c880230d2c2c79097e89259ce0b9f80b87d/library/extractor/src/main/java/com/google/android/exoplayer2/video/HevcConfig.java#L38
-//https://github.com/sannies/mp4parser/blob/master/isoparser/src/main/java/org/mp4parser/boxes/iso14496/part15/HevcDecoderConfigurationRecord.java
-namespace N_m3u8DL_RE.Parser.Mp4
+// https://github.com/canalplus/rx-player/blob/48d1f845064cea5c5a3546d2c53b1855c2be149d/src/parsers/manifest/smooth/get_codecs.ts
+// https://github.dev/Dash-Industry-Forum/dash.js/blob/2aad3e79079b4de0bcd961ce6b4957103d98a621/src/mss/MssFragmentMoovProcessor.js
+// https://github.com/yt-dlp/yt-dlp/blob/3639df54c3298e35b5ae2a96a25bc4d3c38950d0/yt_dlp/downloader/ism.py
+// https://github.com/google/ExoPlayer/blob/a9444c880230d2c2c79097e89259ce0b9f80b87d/library/extractor/src/main/java/com/google/android/exoplayer2/video/HevcConfig.java#L38
+// https://github.com/sannies/mp4parser/blob/master/isoparser/src/main/java/org/mp4parser/boxes/iso14496/part15/HevcDecoderConfigurationRecord.java
+namespace N_m3u8DL_RE.Parser.Mp4;
+
+public partial class MSSMoovProcessor
 {
-    public partial class MSSMoovProcessor
+    [GeneratedRegex("\\<KID\\>(.*?)\\<")]
+    private static partial Regex KIDRegex();
+
+    private static string StartCode = "00000001";
+    private StreamSpec StreamSpec;
+    private int TrackId = 2;
+    private string FourCC;
+    private string CodecPrivateData;
+    private int Timesacle;
+    private long Duration;
+    private string Language { get => StreamSpec.Language ?? "und"; }
+    private int Width => int.Parse((StreamSpec.Resolution ?? "0x0").Split('x').First());
+    private int Height => int.Parse((StreamSpec.Resolution ?? "0x0").Split('x').Last());
+    private string StreamType;
+    private int Channels;
+    private int BitsPerSample;
+    private int SamplingRate;
+    private int NalUnitLengthField;
+    private long CreationTime = DateTimeOffset.UtcNow.ToUnixTimeSeconds();
+
+    private bool IsProtection;
+    private string ProtectionSystemId;
+    private string ProtectionData;
+    private string ProtecitonKID;
+    private string ProtecitonKID_PR;
+    private byte[] UnityMatrix
     {
-        [GeneratedRegex("\\<KID\\>(.*?)\\<")]
-        private static partial Regex KIDRegex();
-
-        private static string StartCode = "00000001";
-        private StreamSpec StreamSpec;
-        private int TrackId = 2;
-        private string FourCC;
-        private string CodecPrivateData;
-        private int Timesacle;
-        private long Duration;
-        private string Language { get => StreamSpec.Language ?? "und"; }
-        private int Width { get => int.Parse((StreamSpec.Resolution ?? "0x0").Split('x').First()); }
-        private int Height { get => int.Parse((StreamSpec.Resolution ?? "0x0").Split('x').Last()); }
-        private string StreamType;
-        private int Channels;
-        private int BitsPerSample;
-        private int SamplingRate;
-        private int NalUnitLengthField;
-        private long CreationTime = DateTimeOffset.UtcNow.ToUnixTimeSeconds();
-
-        private bool IsProtection;
-        private string ProtectionSystemId;
-        private string ProtectionData;
-        private string ProtecitonKID;
-        private string ProtecitonKID_PR;
-        private byte[] UnityMatrix
-        {
-            get
-            {
-                using var stream = new MemoryStream();
-                using var writer = new BinaryWriter2(stream);
-                writer.WriteInt(0x10000);
-                writer.WriteInt(0);
-                writer.WriteInt(0);
-                writer.WriteInt(0);
-                writer.WriteInt(0x10000);
-                writer.WriteInt(0);
-                writer.WriteInt(0);
-                writer.WriteInt(0);
-                writer.WriteInt(0x40000000);
-                return stream.ToArray();
-            }
-        }
-        private static byte TRACK_ENABLED = 0x1;
-        private static byte TRACK_IN_MOVIE = 0x2;
-        private static byte TRACK_IN_PREVIEW = 0x4;
-        private static byte SELF_CONTAINED = 0x1;
-        private static List<string> SupportedFourCC = new List<string>()
-        {
-            "HVC1","HEV1","AACL","AACH","EC-3","H264","AVC1","DAVC","AVC1","TTML","DVHE","DVH1"
-        };
-
-        public MSSMoovProcessor(StreamSpec streamSpec)
-        {
-            this.StreamSpec = streamSpec;
-            var data = streamSpec.MSSData!;
-            this.NalUnitLengthField = data.NalUnitLengthField;
-            this.CodecPrivateData = data.CodecPrivateData;
-            this.FourCC = data.FourCC;
-            this.Timesacle = data.Timesacle;
-            this.Duration = data.Duration;
-            this.StreamType = data.Type;
-            this.Channels = data.Channels;
-            this.SamplingRate = data.SamplingRate;
-            this.BitsPerSample = data.BitsPerSample;
-            this.IsProtection = data.IsProtection;
-            this.ProtectionData = data.ProtectionData;
-            this.ProtectionSystemId = data.ProtectionSystemID;
-
-            //需要手动生成CodecPrivateData
-            if (string.IsNullOrEmpty(CodecPrivateData))
-            {
-                GenCodecPrivateDataForAAC();
-            }
-
-            //解析KID
-            if (IsProtection)
-            {
-                ExtractKID();
-            }
-        }
-
-        private static string[] HEVC_GENERAL_PROFILE_SPACE_STRINGS = new string[] { "", "A", "B", "C" };
-        private int SamplingFrequencyIndex(int samplingRate) => samplingRate switch
-        {
-            96000 => 0x0,
-            88200 => 0x1,
-            64000 => 0x2,
-            48000 => 0x3,
-            44100 => 0x4,
-            32000 => 0x5,
-            24000 => 0x6,
-            22050 => 0x7,
-            16000 => 0x8,
-            12000 => 0x9,
-            11025 => 0xA,
-            8000 => 0xB,
-            7350 => 0xC,
-            _ => 0x0
-        };
-
-        private void GenCodecPrivateDataForAAC()
-        {
-            var objectType = 0x02; //AAC Main Low Complexity => object Type = 2
-            var indexFreq = SamplingFrequencyIndex(SamplingRate);
-
-            if (FourCC == "AACH")
-            {
-                // 4 bytes :     XXXXX         XXXX          XXXX             XXXX                  XXXXX      XXX   XXXXXXX
-                //           ' ObjectType' 'Freq Index' 'Channels value'   'Extens Sampl Freq'  'ObjectType'  'GAS' 'alignment = 0'
-                objectType = 0x05; // High Efficiency AAC Profile = object Type = 5 SBR
-                var codecPrivateData = new byte[4];
-                var extensionSamplingFrequencyIndex = SamplingFrequencyIndex(SamplingRate * 2); // in HE AAC Extension Sampling frequence
-                // equals to SamplingRate*2
-                //Freq Index is present for 3 bits in the first byte, last bit is in the second
-                codecPrivateData[0] = (byte)((objectType << 3) | (indexFreq >> 1));
-                codecPrivateData[1] = (byte)((indexFreq << 7) | (Channels << 3) | (extensionSamplingFrequencyIndex >> 1));
-                codecPrivateData[2] = (byte)((extensionSamplingFrequencyIndex << 7) | (0x02 << 2)); // origin object type equals to 2 => AAC Main Low Complexity
-                codecPrivateData[3] = 0x0; //alignment bits
-
-                var arr16 = new ushort[2];
-                arr16[0] = (ushort)((codecPrivateData[0] << 8) + codecPrivateData[1]);
-                arr16[1] = (ushort)((codecPrivateData[2] << 8) + codecPrivateData[3]);
-
-                //convert decimal to hex value
-                this.CodecPrivateData = HexUtil.BytesToHex(BitConverter.GetBytes(arr16[0])).PadLeft(16, '0');
-                this.CodecPrivateData += HexUtil.BytesToHex(BitConverter.GetBytes(arr16[1])).PadLeft(16, '0');
-            }
-            else if (FourCC.StartsWith("AAC")) 
-            {
-                // 2 bytes :     XXXXX         XXXX          XXXX              XXX
-                //           ' ObjectType' 'Freq Index' 'Channels value'   'GAS = 000'
-                var codecPrivateData = new byte[2];
-                //Freq Index is present for 3 bits in the first byte, last bit is in the second
-                codecPrivateData[0] = (byte)((objectType << 3) | (indexFreq >> 1));
-                codecPrivateData[1] = (byte)((indexFreq << 7) | Channels << 3);
-                // put the 2 bytes in an 16 bits array
-                var arr16 = new ushort[1];
-                arr16[0] = (ushort)((codecPrivateData[0] << 8) + codecPrivateData[1]);
-
-                //convert decimal to hex value
-                this.CodecPrivateData = HexUtil.BytesToHex(BitConverter.GetBytes(arr16[0])).PadLeft(16, '0');
-            }
-        }
-
-        private void ExtractKID()
-        {
-            //playready
-            if (ProtectionSystemId.ToUpper() == "9A04F079-9840-4286-AB92-E65BE0885F95")
-            {
-                var bytes = HexUtil.HexToBytes(ProtectionData.Replace("00", ""));
-                var text = Encoding.ASCII.GetString(bytes);
-                var kidBytes = Convert.FromBase64String(KIDRegex().Match(text).Groups[1].Value);
-                //save kid for playready
-                this.ProtecitonKID_PR = HexUtil.BytesToHex(kidBytes);
-                //fix byte order
-                var reverse1 = new byte[4] { kidBytes[3], kidBytes[2], kidBytes[1], kidBytes[0] };
-                var reverse2 = new byte[4] { kidBytes[5], kidBytes[4], kidBytes[7], kidBytes[6] };
-                Array.Copy(reverse1, 0, kidBytes, 0, reverse1.Length);
-                Array.Copy(reverse2, 0, kidBytes, 4, reverse1.Length);
-                this.ProtecitonKID = HexUtil.BytesToHex(kidBytes);
-            }
-            //widevine
-            else if (ProtectionSystemId.ToUpper() == "EDEF8BA9-79D6-4ACE-A3C8-27DCD51D21ED")
-            {
-                throw new NotSupportedException();
-            }
-        }
-
-        public static bool CanHandle(string fourCC) => SupportedFourCC.Contains(fourCC);
-
-        private byte[] Box(string boxType, byte[] payload)
+        get
         {
             using var stream = new MemoryStream();
             using var writer = new BinaryWriter2(stream);
-
-            writer.WriteUInt(8 + (uint)payload.Length);
-            writer.Write(boxType);
-            writer.Write(payload);
-
-            return stream.ToArray();
-        }
-
-        private byte[] FullBox(string boxType, byte version, uint flags, byte[] payload)
-        {
-            using var stream = new MemoryStream();
-            using var writer = new BinaryWriter2(stream);
-
-            writer.Write(version);
-            writer.WriteUInt(flags, offset: 1);
-            writer.Write(payload);
-
-            return Box(boxType, stream.ToArray());
-        }
-
-        private byte[] GenSinf(string codec)
-        {
-            var frmaBox = Box("frma", Encoding.ASCII.GetBytes(codec));
-
-            var sinfPayload = new List<byte>();
-            sinfPayload.AddRange(frmaBox);
-
-            var schmPayload = new List<byte>();
-            schmPayload.AddRange(Encoding.ASCII.GetBytes("cenc")); //scheme_type 'cenc' => common encryption
-            schmPayload.AddRange(new byte[] { 0, 1, 0, 0 }); //scheme_version Major version 1, Minor version 0
-            var schmBox = FullBox("schm", 0, 0, schmPayload.ToArray());
-
-            sinfPayload.AddRange(schmBox);
-
-            var tencPayload = new List<byte>();
-            tencPayload.AddRange(new byte[] { 0, 0 });
-            tencPayload.Add(0x1); //default_IsProtected
-            tencPayload.Add(0x8); //default_Per_Sample_IV_size
-            tencPayload.AddRange(HexUtil.HexToBytes(ProtecitonKID)); //default_KID
-            //tencPayload.Add(0x8);//default_constant_IV_size
-            //tencPayload.AddRange(new byte[8]);//default_constant_IV
-            var tencBox = FullBox("tenc", 0, 0, tencPayload.ToArray());
-
-            var schiBox = Box("schi", tencBox);
-            sinfPayload.AddRange(schiBox);
-
-            var sinfBox = Box("sinf", sinfPayload.ToArray());
-
-            return sinfBox;
-        }
-
-        private byte[] GenFtyp()
-        {
-            using var stream = new MemoryStream();
-            using var writer = new BinaryWriter2(stream);
-
-            writer.Write("isml"); //major brand
-            writer.WriteUInt(1); //minor version
-            writer.Write("iso5"); //compatible brand
-            writer.Write("iso6"); //compatible brand
-            writer.Write("piff"); //compatible brand
-            writer.Write("msdh"); //compatible brand
-
-            return Box("ftyp", stream.ToArray());
-        }
-
-        private byte[] GenMvhd()
-        {
-            using var stream = new MemoryStream();
-            using var writer = new BinaryWriter2(stream);
-
-            writer.WriteULong(CreationTime); //creation_time
-            writer.WriteULong(CreationTime); //modification_time
-            writer.WriteUInt(Timesacle); //timescale
-            writer.WriteULong(Duration); //duration
-            writer.WriteUShort(1, padding: 2); //rate
-            writer.WriteByte(1, padding: 1); //volume
-            writer.WriteUShort(0); //reserved
-            writer.WriteUInt(0);
-            writer.WriteUInt(0);
-
-            writer.Write(UnityMatrix);
-
-            writer.WriteUInt(0); //pre defined
-            writer.WriteUInt(0);
-            writer.WriteUInt(0);
-            writer.WriteUInt(0);
-            writer.WriteUInt(0);
-            writer.WriteUInt(0);
-
-            writer.WriteUInt(0xffffffff); //next track id
-
-
-            return FullBox("mvhd", 1, 0, stream.ToArray());
-        }
-
-        private byte[] GenTkhd()
-        {
-            using var stream = new MemoryStream();
-            using var writer = new BinaryWriter2(stream);
-
-            writer.WriteULong(CreationTime); //creation_time
-            writer.WriteULong(CreationTime); //modification_time
-            writer.WriteUInt(TrackId); //track id
-            writer.WriteUInt(0); //reserved
-            writer.WriteULong(Duration); //duration
-            writer.WriteUInt(0); //reserved
-            writer.WriteUInt(0);
-            writer.WriteShort(0); //layer
-            writer.WriteShort(0); //alternate group
-            writer.WriteByte(StreamType == "audio" ? (byte)1 : (byte)0, padding: 1); //volume
-            writer.WriteUShort(0); //reserved
-
-            writer.Write(UnityMatrix);
-
-            writer.WriteUShort(Width, padding: 2); //width
-            writer.WriteUShort(Height, padding: 2); //height
-
-            return FullBox("tkhd", 1, (uint)TRACK_ENABLED | TRACK_IN_MOVIE | TRACK_IN_PREVIEW, stream.ToArray());
-        }
-
-
-        private byte[] GenMdhd()
-        {
-            using var stream = new MemoryStream();
-            using var writer = new BinaryWriter2(stream);
-
-            writer.WriteULong(CreationTime); //creation_time
-            writer.WriteULong(CreationTime); //modification_time
-            writer.WriteUInt(Timesacle); //timescale
-            writer.WriteULong(Duration); //duration
-            writer.WriteUShort((Language[0] - 0x60) << 10 | (Language[1] - 0x60) << 5 | (Language[2] - 0x60)); //language
-            writer.WriteUShort(0); //pre defined
-
-            return FullBox("mdhd", 1, 0, stream.ToArray());
-        }
-
-        private byte[] GenHdlr()
-        {
-            using var stream = new MemoryStream();
-            using var writer = new BinaryWriter2(stream);
-
-            writer.WriteUInt(0); //pre defined
-            if (StreamType == "audio") writer.Write("soun");
-            else if (StreamType == "video") writer.Write("vide");
-            else if (StreamType == "text") writer.Write("subt");
-            else throw new NotSupportedException();
-
-            writer.WriteUInt(0); //reserved
-            writer.WriteUInt(0);
-            writer.WriteUInt(0);
-            writer.Write($"{StreamSpec.GroupId ?? "RE Handler"}\0"); //name
-
-            return FullBox("hdlr", 0, 0, stream.ToArray());
-        }
-
-        private byte[] GenMinf()
-        {
-            using var stream = new MemoryStream();
-            using var writer = new BinaryWriter2(stream);
-
-            var minfPayload = new List<byte>();
-            if (StreamType == "audio")
-            {
-                var smhd = new List<byte>();
-                smhd.Add(0); smhd.Add(0); //balance
-                smhd.Add(0); smhd.Add(0); //reserved
-
-                minfPayload.AddRange(FullBox("smhd", 0, 0, smhd.ToArray())); //Sound Media Header
-            }
-            else if (StreamType == "video")
-            {
-                var vmhd = new List<byte>();
-                vmhd.Add(0); vmhd.Add(0); //graphics mode
-                vmhd.Add(0); vmhd.Add(0); vmhd.Add(0); vmhd.Add(0); vmhd.Add(0); vmhd.Add(0);//opcolor
-
-                minfPayload.AddRange(FullBox("vmhd", 0, 1, vmhd.ToArray())); //Video Media Header
-            }
-            else if (StreamType == "text")
-            {
-                minfPayload.AddRange(FullBox("sthd", 0, 0, new byte[0])); //Subtitle Media Header
-            }
-            else
-            {
-                throw new NotSupportedException();
-            }
-
-            var drefPayload = new List<byte>();
-            drefPayload.Add(0); drefPayload.Add(0); drefPayload.Add(0); drefPayload.Add(1); //entry count
-            drefPayload.AddRange(FullBox("url ", 0, SELF_CONTAINED, new byte[0])); //Data Entry URL Box
-
-            var dinfPayload = FullBox("dref", 0, 0, drefPayload.ToArray()); //Data Reference Box
-            minfPayload.AddRange(Box("dinf", dinfPayload.ToArray())); //Data Information Box
-
-            return minfPayload.ToArray();
-        }
-
-        private byte[] GenEsds(byte[] audioSpecificConfig)
-        {
-            using var stream = new MemoryStream();
-            using var writer = new BinaryWriter2(stream);
-
-            // ESDS length = esds box header length (= 12) +
-            //               ES_Descriptor header length (= 5) +
-            //               DecoderConfigDescriptor header length (= 15) +
-            //               decoderSpecificInfo header length (= 2) +
-            //               AudioSpecificConfig length (= codecPrivateData length)
-            // esdsLength = 34 + len(audioSpecificConfig)
-
-            // ES_Descriptor (see ISO/IEC 14496-1 (Systems))
-            writer.WriteByte(0x03); //tag = 0x03 (ES_DescrTag)
-            writer.WriteByte((byte)(20 + audioSpecificConfig.Length)); //size
-            writer.WriteByte((byte)((TrackId & 0xFF00) >> 8)); //ES_ID = track_id
-            writer.WriteByte((byte)(TrackId & 0x00FF));
-            writer.WriteByte(0); //flags and streamPriority
-
-            // DecoderConfigDescriptor (see ISO/IEC 14496-1 (Systems))
-            writer.WriteByte(0x04); //tag = 0x04 (DecoderConfigDescrTag)
-            writer.WriteByte((byte)(15 + audioSpecificConfig.Length)); //size
-            writer.WriteByte(0x40); //objectTypeIndication = 0x40 (MPEG-4 AAC)
-            writer.WriteByte((0x05 << 2) | (0 << 1) | 1); //reserved = 1
-            writer.WriteByte(0xFF); //buffersizeDB = undefined
-            writer.WriteByte(0xFF); 
-            writer.WriteByte(0xFF);
-
-            var bandwidth = StreamSpec.Bandwidth!;
-            writer.WriteByte((byte)((bandwidth & 0xFF000000) >> 24)); //maxBitrate
-            writer.WriteByte((byte)((bandwidth & 0x00FF0000) >> 16));
-            writer.WriteByte((byte)((bandwidth & 0x0000FF00) >> 8));
-            writer.WriteByte((byte)(bandwidth  & 0x000000FF));
-            writer.WriteByte((byte)((bandwidth & 0xFF000000) >> 24)); //avgbitrate
-            writer.WriteByte((byte)((bandwidth & 0x00FF0000) >> 16)); 
-            writer.WriteByte((byte)((bandwidth & 0x0000FF00) >> 8));
-            writer.WriteByte((byte)(bandwidth  & 0x000000FF));
-
-            // DecoderSpecificInfo (see ISO/IEC 14496-1 (Systems))
-            writer.WriteByte(0x05); //tag = 0x05 (DecSpecificInfoTag)
-            writer.WriteByte((byte)audioSpecificConfig.Length); //size
-            writer.Write(audioSpecificConfig); //AudioSpecificConfig bytes
-
-            return FullBox("esds", 0, 0, stream.ToArray());
-        }
-
-        private byte[] GetSampleEntryBox()
-        {
-            using var stream = new MemoryStream();
-            using var writer = new BinaryWriter2(stream);
-
-            writer.WriteByte(0); //reserved
-            writer.WriteByte(0);
-            writer.WriteByte(0);
-            writer.WriteByte(0);
-            writer.WriteByte(0);
-            writer.WriteByte(0);
-            writer.WriteUShort(1); //data reference index
-
-            if (StreamType == "audio")
-            {
-                writer.WriteUInt(0); //reserved2
-                writer.WriteUInt(0);
-                writer.WriteUShort(Channels); //channels
-                writer.WriteUShort(BitsPerSample); //bits_per_sample
-                writer.WriteUShort(0); //pre defined
-                writer.WriteUShort(0); //reserved3
-                writer.WriteUShort(SamplingRate, padding: 2); //sampling_rate
-
-                var audioSpecificConfig = HexUtil.HexToBytes(CodecPrivateData);
-                var esdsBox = GenEsds(audioSpecificConfig);
-                writer.Write(esdsBox);
-
-                if (FourCC.StartsWith("AAC")) 
-                {
-                    if (IsProtection)
-                    {
-                        var sinfBox = GenSinf("mp4a");
-                        writer.Write(sinfBox);
-                        return Box("enca", stream.ToArray()); //Encrypted Audio
-                    }
-                    else
-                    {
-                        return Box("mp4a", stream.ToArray());
-                    }
-                }
-                if (FourCC == "EC-3")
-                {
-                    if (IsProtection)
-                    {
-                        var sinfBox = GenSinf("ec-3");
-                        writer.Write(sinfBox);
-                        return Box("enca", stream.ToArray()); //Encrypted Audio
-                    }
-                    else
-                    {
-                        return Box("ec-3", stream.ToArray());
-                    }
-                }
-            }
-            else if (StreamType == "video")
-            {
-                writer.WriteUShort(0); //pre defined
-                writer.WriteUShort(0); //reserved
-                writer.WriteUInt(0); //pre defined
-                writer.WriteUInt(0);
-                writer.WriteUInt(0);
-                writer.WriteUShort(Width); //width
-                writer.WriteUShort(Height); //height
-                writer.WriteUShort(0x48, padding: 2); //horiz resolution 72 dpi
-                writer.WriteUShort(0x48, padding: 2); //vert resolution 72 dpi
-                writer.WriteUInt(0); //reserved
-                writer.WriteUShort(1); //frame count
-                for (int i = 0; i < 32; i++) //compressor name
-                {
-                    writer.WriteByte(0);
-                }
-                writer.WriteUShort(0x18); //depth
-                writer.WriteUShort(65535); //pre defined
-
-                var codecPrivateData = HexUtil.HexToBytes(CodecPrivateData);
-
-                if (FourCC == "H264" || FourCC == "AVC1" || FourCC == "DAVC" || FourCC == "AVC1")
-                {
-                    var arr = CodecPrivateData.Split(new[] { StartCode }, StringSplitOptions.RemoveEmptyEntries);
-                    var sps = HexUtil.HexToBytes(arr.Where(x => (HexUtil.HexToBytes(x[0..2])[0] & 0x1F) == 7).First());
-                    var pps = HexUtil.HexToBytes(arr.Where(x => (HexUtil.HexToBytes(x[0..2])[0] & 0x1F) == 8).First());
-                    //make avcC
-                    var avcC = GetAvcC(sps, pps);
-                    writer.Write(avcC);
-                    if (IsProtection)
-                    {
-                        var sinfBox = GenSinf("avc1");
-                        writer.Write(sinfBox);
-                        return Box("encv", stream.ToArray()); //Encrypted Video
-                    }
-                    else
-                    {
-                        return Box("avc1", stream.ToArray()); //AVC Simple Entry
-                    }
-                }
-                else if (FourCC == "HVC1" || FourCC == "HEV1")
-                {
-                    var arr = CodecPrivateData.Split(new[] { StartCode }, StringSplitOptions.RemoveEmptyEntries);
-                    var vps = HexUtil.HexToBytes(arr.Where(x => (HexUtil.HexToBytes(x[0..2])[0] >> 1) == 0x20).First());
-                    var sps = HexUtil.HexToBytes(arr.Where(x => (HexUtil.HexToBytes(x[0..2])[0] >> 1) == 0x21).First());
-                    var pps = HexUtil.HexToBytes(arr.Where(x => (HexUtil.HexToBytes(x[0..2])[0] >> 1) == 0x22).First());
-                    //make hvcC
-                    var hvcC = GetHvcC(sps, pps, vps);
-                    writer.Write(hvcC);
-                    if (IsProtection)
-                    {
-                        var sinfBox = GenSinf("hvc1");
-                        writer.Write(sinfBox);
-                        return Box("encv", stream.ToArray()); //Encrypted Video
-                    }
-                    else
-                    {
-                        return Box("hvc1", stream.ToArray()); //HEVC Simple Entry
-                    }
-                }
-                // 杜比视界也按照hevc处理
-                else if (FourCC == "DVHE" || FourCC == "DVH1")
-                {
-                    var arr = CodecPrivateData.Split(new[] { StartCode }, StringSplitOptions.RemoveEmptyEntries);
-                    var vps = HexUtil.HexToBytes(arr.Where(x => (HexUtil.HexToBytes(x[0..2])[0] >> 1) == 0x20).First());
-                    var sps = HexUtil.HexToBytes(arr.Where(x => (HexUtil.HexToBytes(x[0..2])[0] >> 1) == 0x21).First());
-                    var pps = HexUtil.HexToBytes(arr.Where(x => (HexUtil.HexToBytes(x[0..2])[0] >> 1) == 0x22).First());
-                    //make hvcC
-                    var hvcC = GetHvcC(sps, pps, vps, "dvh1");
-                    writer.Write(hvcC);
-                    if (IsProtection)
-                    {
-                        var sinfBox = GenSinf("dvh1");
-                        writer.Write(sinfBox);
-                        return Box("encv", stream.ToArray()); //Encrypted Video
-                    }
-                    else
-                    {
-                        return Box("dvh1", stream.ToArray()); //HEVC Simple Entry
-                    }
-                }
-                else
-                {
-                    throw new NotSupportedException();
-                }
-            }
-            else if (StreamType == "text")
-            {
-                if (FourCC == "TTML")
-                {
-                    writer.Write("http://www.w3.org/ns/ttml\0"); //namespace
-                    writer.Write("\0"); //schema location
-                    writer.Write("\0"); //auxilary mime types(??)
-                    return Box("stpp", stream.ToArray()); //TTML Simple Entry
-                }
-                else
-                {
-                    throw new NotSupportedException();
-                }
-            }
-            else
-            {
-                throw new NotSupportedException();
-            }
-
-            throw new NotSupportedException();
-        }
-
-        private byte[] GetAvcC(byte[] sps, byte[] pps)
-        {
-            using var stream = new MemoryStream();
-            using var writer = new BinaryWriter2(stream);
-
-            writer.WriteByte(1); //configuration version
-            writer.Write(sps[1..4]); //avc profile indication + profile compatibility + avc level indication
-            writer.WriteByte((byte)(0xfc | (NalUnitLengthField - 1))); //complete representation (1) + reserved (11111) + length size minus one
-            writer.WriteByte(1); //reserved (0) + number of sps (0000001)
-            writer.WriteUShort(sps.Length);
-            writer.Write(sps);
-            writer.WriteByte(1); //number of pps
-            writer.WriteUShort(pps.Length);
-            writer.Write(pps);
-
-            return Box("avcC", stream.ToArray()); //AVC Decoder Configuration Record
-        }
-
-        private byte[] GetHvcC(byte[] sps, byte[] pps, byte[] vps, string code = "hvc1")
-        {
-            var oriSps = new List<byte>(sps);
-            //https://www.itu.int/rec/dologin.asp?lang=f&id=T-REC-H.265-201504-S!!PDF-E&type=items
-            //Read generalProfileSpace, generalTierFlag, generalProfileIdc,
-            //generalProfileCompatibilityFlags, constraintBytes, generalLevelIdc
-            //from sps
-            var encList = new List<byte>();
-            /**
-             * 处理payload, 有00 00 03 0,1,2,3的情况 统一换成00 00 XX 即丢弃03
-             * 注意:此处采用的逻辑是直接简单粗暴地判断列表末尾3字节,如果是0x000003就删掉最后的0x03,可能会导致以下情况
-             * 00 00 03 03 03 03 03 01 会被直接处理成 => 00 00 01
-             * 此处经过测试只有直接跳过才正常,如果处理成 00 00 03 03 03 03 01 是有问题的
-             * 
-             * 测试的数据如下:
-             *   原始:42 01 01 01 60 00 00 03 00 90 00 00 03 00 00 03 00 96 a0 01 e0 20 06 61 65 95 9a 49 30 bf fc 0c 7c 0c 81 a8 08 08 08 20 00 00 03 00 20 00 00 03 03 01
-             * 处理后:42 01 01 01 60 00 00 00 90 00 00 00 00 00 96 A0 01 E0 20 06 61 65 95 9A 49 30 BF FC 0C 7C 0C 81 A8 08 08 08 20 00 00 00 20 00 00 01
-             */
-            using (var _reader = new BinaryReader(new MemoryStream(sps)))
-            {
-                while (_reader.BaseStream.Position < _reader.BaseStream.Length)
-                {
-                    encList.Add(_reader.ReadByte());
-                    if (encList.Count >= 3 && encList[encList.Count - 3] == 0x00 && encList[encList.Count - 2] == 0x00 && encList[encList.Count - 1] == 0x03)
-                    {
-                        encList.RemoveAt(encList.Count - 1);
-                    }
-                }
-            }
-            sps = encList.ToArray();
-
-            using var reader = new BinaryReader2(new MemoryStream(sps));
-            reader.ReadBytes(2); //Skip 2 bytes unit header
-            var firstByte = reader.ReadByte();
-            var maxSubLayersMinus1 = (firstByte & 0xe) >> 1;
-            var nextByte = reader.ReadByte();
-            var generalProfileSpace = (nextByte & 0xc0) >> 6;
-            var generalTierFlag = (nextByte & 0x20) >> 5;
-            var generalProfileIdc = nextByte & 0x1f;
-            var generalProfileCompatibilityFlags = reader.ReadUInt32();
-            var constraintBytes = reader.ReadBytes(6);
-            var generalLevelIdc = reader.ReadByte();
-
-            /*var skipBit = 0;
-            for (int i = 0; i < maxSubLayersMinus1; i++)
-            {
-                skipBit += 2; //sub_layer_profile_present_flag sub_layer_level_present_flag
-            }
-            if (maxSubLayersMinus1 > 0)
-            {
-                for (int i = maxSubLayersMinus1; i < 8; i++)
-                {
-                    skipBit += 2; //reserved_zero_2bits
-                }
-            }
-            for (int i = 0; i < maxSubLayersMinus1; i++)
-            {
-                skipBit += 2; //sub_layer_profile_present_flag sub_layer_level_present_flag
-            }*/
-
-            //生成编码信息
-            var codecs = code +
-                $".{HEVC_GENERAL_PROFILE_SPACE_STRINGS[generalProfileSpace]}{generalProfileIdc}" +
-                $".{Convert.ToString(generalProfileCompatibilityFlags, 16)}" +
-                $".{(generalTierFlag == 1 ? 'H' : 'L')}{generalLevelIdc}" +
-                $".{HexUtil.BytesToHex(constraintBytes.Where(b => b != 0).ToArray())}";
-            StreamSpec.Codecs = codecs;
-
-
-            ///////////////////////
-
-
-            using var stream = new MemoryStream();
-            using var writer = new BinaryWriter2(stream);
-
-            //var reserved1 = 0xF;
-
-            writer.WriteByte(1); //configuration version
-            writer.WriteByte((byte)((generalProfileSpace << 6) + (generalTierFlag == 1 ? 0x20 : 0) | generalProfileIdc)); //general_profile_space + general_tier_flag + general_profile_idc
-            writer.WriteUInt(generalProfileCompatibilityFlags); //general_profile_compatibility_flags
-            writer.Write(constraintBytes); //general_constraint_indicator_flags
-            writer.WriteByte((byte)generalProfileIdc); //general_level_idc
-            writer.WriteUShort(0xf000); //reserved + min_spatial_segmentation_idc
-            writer.WriteByte(0xfc); //reserved + parallelismType
-            writer.WriteByte(0 | 0xfc); //reserved + chromaFormat 
-            writer.WriteByte(0 | 0xf8); //reserved + bitDepthLumaMinus8
-            writer.WriteByte(0 | 0xf8); //reserved + bitDepthChromaMinus8
-            writer.WriteUShort(0); //avgFrameRate
-            writer.WriteByte((byte)(0 << 6 | 0 << 3 | 0 << 2 | (NalUnitLengthField - 1))); //constantFrameRate + numTemporalLayers + temporalIdNested + lengthSizeMinusOne
-            writer.WriteByte(0x03); //numOfArrays (vps sps pps)
-            
-            sps = oriSps.ToArray();
-            writer.WriteByte(0x20); //array_completeness + reserved + NAL_unit_type
-            writer.WriteUShort(1); //numNalus 
-            writer.WriteUShort(vps.Length);
-            writer.Write(vps);
-            writer.WriteByte(0x21);
-            writer.WriteUShort(1); //numNalus
-            writer.WriteUShort(sps.Length);
-            writer.Write(sps);
-            writer.WriteByte(0x22); 
-            writer.WriteUShort(1); //numNalus
-            writer.WriteUShort(pps.Length);
-            writer.Write(pps);
-
-            return Box("hvcC", stream.ToArray()); //HEVC Decoder Configuration Record
-        }
-
-        private byte[] GetStsd()
-        {
-            using var stream = new MemoryStream();
-            using var writer = new BinaryWriter2(stream);
-
-            writer.WriteUInt(1); //entry count
-            var sampleEntryData = GetSampleEntryBox();
-            writer.Write(sampleEntryData);
-
-            return stream.ToArray();
-        }
-
-        private byte[] GetMehd()
-        {
-            using var stream = new MemoryStream();
-            using var writer = new BinaryWriter2(stream);
-
-            writer.WriteULong(Duration);
-
-            return FullBox("mehd", 1, 0, stream.ToArray()); //Movie Extends Header Box
-        }
-        private byte[] GetTrex()
-        {
-            using var stream = new MemoryStream();
-            using var writer = new BinaryWriter2(stream);
-
-            writer.WriteUInt(TrackId); //track id
-            writer.WriteUInt(1); //default sample description index
-            writer.WriteUInt(0); //default sample duration
-            writer.WriteUInt(0); //default sample size
-            writer.WriteUInt(0); //default sample flags
-
-            return FullBox("trex", 0, 0, stream.ToArray()); //Track Extends Box
-        }
-
-        private byte[] GenPsshBoxForPlayReady()
-        {
-            using var _stream = new MemoryStream();
-            using var _writer = new BinaryWriter2(_stream);
-            var sysIdData = HexUtil.HexToBytes(ProtectionSystemId.Replace("-", ""));
-            var psshData = HexUtil.HexToBytes(ProtectionData);
-
-            _writer.Write(sysIdData);  // SystemID 16 bytes
-            _writer.WriteUInt(psshData.Length); //Size of Data 4 bytes
-            _writer.Write(psshData); //Data
-            var psshBox = FullBox("pssh", 0, 0, _stream.ToArray());
-            return psshBox;
-        }
-
-        private byte[] GenPsshBoxForWideVine()
-        {
-            using var _stream = new MemoryStream();
-            using var _writer = new BinaryWriter2(_stream);
-            var sysIdData = HexUtil.HexToBytes("edef8ba9-79d6-4ace-a3c8-27dcd51d21ed".Replace("-", ""));
-            //var kid = HexUtil.HexToBytes(ProtecitonKID);
-
-            _writer.Write(sysIdData);  // SystemID 16 bytes
-            var psshData = HexUtil.HexToBytes($"08011210{ProtecitonKID}1A046E647265220400000000");
-            _writer.WriteUInt(psshData.Length); //Size of Data 4 bytes
-            _writer.Write(psshData); //Data
-            var psshBox = FullBox("pssh", 0, 0, _stream.ToArray());
-            return psshBox;
-        }
-
-        private byte[] GenMoof()
-        {
-            using var stream = new MemoryStream();
-            using var writer = new BinaryWriter2(stream);
-
-            //make senc
-            writer.WriteUInt(1); //sample_count
-            writer.Write(new byte[8]); //8 bytes IV
-
-            var sencBox = FullBox("senc", 1, 0, stream.ToArray());
-
-            var moofBox = Box("moof", sencBox); //Movie Extends Box
-
-            return moofBox;
-        }
-
-        public byte[] GenHeader(byte[] firstSegment)
-        {
-            new MP4Parser()
-                .Box("moof", MP4Parser.Children)
-                .Box("traf", MP4Parser.Children)
-                .FullBox("tfhd", (box) =>
-                {
-                    TrackId = (int)box.Reader.ReadUInt32();
-                })
-                .Parse(firstSegment);
-
-            return GenHeader();
-        }
-
-        public byte[] GenHeader()
-        {
-            using var stream = new MemoryStream();
-
-            var ftyp = GenFtyp(); // File Type Box
-            stream.Write(ftyp);
-
-            var moovPayload = GenMvhd(); // Movie Header Box
-
-            var trakPayload = GenTkhd(); // Track Header Box
-
-            var mdhdPayload = GenMdhd(); // Media Header Box
-
-            var hdlrPayload = GenHdlr(); // Handler Reference Box
-
-            var mdiaPayload = mdhdPayload.Concat(hdlrPayload).ToArray();
-
-            var minfPayload = GenMinf();
-
-
-            var sttsPayload = new byte[] { 0, 0, 0, 0 }; //entry count
-            var stblPayload = FullBox("stts", 0, 0, sttsPayload); //Decoding Time to Sample Box
-
-            var stscPayload = new byte[] { 0, 0, 0, 0 }; //entry count
-            var stscBox = FullBox("stsc", 0, 0, stscPayload); //Sample To Chunk Box
-
-            var stcoPayload = new byte[] { 0, 0, 0, 0 }; //entry count
-            var stcoBox = FullBox("stco", 0, 0, stcoPayload); //Chunk Offset Box
-
-            var stszPayload = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 }; //sample size, sample count
-            var stszBox = FullBox("stsz", 0, 0, stszPayload); //Sample Size Box
-
-            var stsdPayload = GetStsd();
-            var stsdBox = FullBox("stsd", 0, 0, stsdPayload); //Sample Description Box
-
-            stblPayload = stblPayload.Concat(stscBox).Concat(stcoBox).Concat(stszBox).Concat(stsdBox).ToArray();
-
-
-            var stblBox = Box("stbl", stblPayload); //Sample Table Box
-            minfPayload = minfPayload.Concat(stblBox).ToArray();
-
-            var minfBox = Box("minf", minfPayload); //Media Information Box
-            mdiaPayload = mdiaPayload.Concat(minfBox).ToArray();
-
-            var mdiaBox = Box("mdia", mdiaPayload); //Media Box
-            trakPayload = trakPayload.Concat(mdiaBox).ToArray();
-
-            var trakBox = Box("trak", trakPayload); //Track Box
-            moovPayload = moovPayload.Concat(trakBox).ToArray();
-
-            var mvexPayload = GetMehd();
-            var trexBox = GetTrex();
-            mvexPayload = mvexPayload.Concat(trexBox).ToArray();
-
-            var mvexBox = Box("mvex", mvexPayload); //Movie Extends Box
-            moovPayload = moovPayload.Concat(mvexBox).ToArray();
-
-            if (IsProtection)
-            {
-                var psshBox1 = GenPsshBoxForPlayReady();
-                var psshBox2 = GenPsshBoxForWideVine();
-                moovPayload = moovPayload.Concat(psshBox1).Concat(psshBox2).ToArray();
-            }
-
-            var moovBox = Box("moov", moovPayload); //Movie Box
-
-            stream.Write(moovBox);
-
-            //var moofBox = GenMoof(); //Movie Extends Box
-            //stream.Write(moofBox);
-
+            writer.WriteInt(0x10000);
+            writer.WriteInt(0);
+            writer.WriteInt(0);
+            writer.WriteInt(0);
+            writer.WriteInt(0x10000);
+            writer.WriteInt(0);
+            writer.WriteInt(0);
+            writer.WriteInt(0);
+            writer.WriteInt(0x40000000);
             return stream.ToArray();
         }
     }
-}
+    private static byte TRACK_ENABLED = 0x1;
+    private static byte TRACK_IN_MOVIE = 0x2;
+    private static byte TRACK_IN_PREVIEW = 0x4;
+    private static byte SELF_CONTAINED = 0x1;
+    private static List<string> SupportedFourCC = new()
+    {
+        "HVC1","HEV1","AACL","AACH","EC-3","H264","AVC1","DAVC","AVC1","TTML","DVHE","DVH1"
+    };
+
+    public MSSMoovProcessor(StreamSpec streamSpec)
+    {
+        this.StreamSpec = streamSpec;
+        var data = streamSpec.MSSData!;
+        this.NalUnitLengthField = data.NalUnitLengthField;
+        this.CodecPrivateData = data.CodecPrivateData;
+        this.FourCC = data.FourCC;
+        this.Timesacle = data.Timesacle;
+        this.Duration = data.Duration;
+        this.StreamType = data.Type;
+        this.Channels = data.Channels;
+        this.SamplingRate = data.SamplingRate;
+        this.BitsPerSample = data.BitsPerSample;
+        this.IsProtection = data.IsProtection;
+        this.ProtectionData = data.ProtectionData;
+        this.ProtectionSystemId = data.ProtectionSystemID;
+
+        // 需要手动生成CodecPrivateData
+        if (string.IsNullOrEmpty(CodecPrivateData))
+        {
+            GenCodecPrivateDataForAAC();
+        }
+
+        // 解析KID
+        if (IsProtection)
+        {
+            ExtractKID();
+        }
+    }
+
+    private static string[] HEVC_GENERAL_PROFILE_SPACE_STRINGS = new string[] { "", "A", "B", "C" };
+    private int SamplingFrequencyIndex(int samplingRate) => samplingRate switch
+    {
+        96000 => 0x0,
+        88200 => 0x1,
+        64000 => 0x2,
+        48000 => 0x3,
+        44100 => 0x4,
+        32000 => 0x5,
+        24000 => 0x6,
+        22050 => 0x7,
+        16000 => 0x8,
+        12000 => 0x9,
+        11025 => 0xA,
+        8000 => 0xB,
+        7350 => 0xC,
+        _ => 0x0
+    };
+
+    private void GenCodecPrivateDataForAAC()
+    {
+        var objectType = 0x02; // AAC Main Low Complexity => object Type = 2
+        var indexFreq = SamplingFrequencyIndex(SamplingRate);
+
+        if (FourCC == "AACH")
+        {
+            // 4 bytes :     XXXXX         XXXX          XXXX             XXXX                  XXXXX      XXX   XXXXXXX
+            //           ' ObjectType' 'Freq Index' 'Channels value'   'Extens Sampl Freq'  'ObjectType'  'GAS' 'alignment = 0'
+            objectType = 0x05; // High Efficiency AAC Profile = object Type = 5 SBR
+            var codecPrivateData = new byte[4];
+            var extensionSamplingFrequencyIndex = SamplingFrequencyIndex(SamplingRate * 2); // in HE AAC Extension Sampling frequence
+            // equals to SamplingRate*2
+            // Freq Index is present for 3 bits in the first byte, last bit is in the second
+            codecPrivateData[0] = (byte)((objectType << 3) | (indexFreq >> 1));
+            codecPrivateData[1] = (byte)((indexFreq << 7) | (Channels << 3) | (extensionSamplingFrequencyIndex >> 1));
+            codecPrivateData[2] = (byte)((extensionSamplingFrequencyIndex << 7) | (0x02 << 2)); // origin object type equals to 2 => AAC Main Low Complexity
+            codecPrivateData[3] = 0x0; // alignment bits
+
+            var arr16 = new ushort[2];
+            arr16[0] = (ushort)((codecPrivateData[0] << 8) + codecPrivateData[1]);
+            arr16[1] = (ushort)((codecPrivateData[2] << 8) + codecPrivateData[3]);
+
+            // convert decimal to hex value
+            this.CodecPrivateData = HexUtil.BytesToHex(BitConverter.GetBytes(arr16[0])).PadLeft(16, '0');
+            this.CodecPrivateData += HexUtil.BytesToHex(BitConverter.GetBytes(arr16[1])).PadLeft(16, '0');
+        }
+        else if (FourCC.StartsWith("AAC")) 
+        {
+            // 2 bytes :     XXXXX         XXXX          XXXX              XXX
+            //           ' ObjectType' 'Freq Index' 'Channels value'   'GAS = 000'
+            var codecPrivateData = new byte[2];
+            // Freq Index is present for 3 bits in the first byte, last bit is in the second
+            codecPrivateData[0] = (byte)((objectType << 3) | (indexFreq >> 1));
+            codecPrivateData[1] = (byte)((indexFreq << 7) | Channels << 3);
+            // put the 2 bytes in an 16 bits array
+            var arr16 = new ushort[1];
+            arr16[0] = (ushort)((codecPrivateData[0] << 8) + codecPrivateData[1]);
+
+            // convert decimal to hex value
+            this.CodecPrivateData = HexUtil.BytesToHex(BitConverter.GetBytes(arr16[0])).PadLeft(16, '0');
+        }
+    }
+
+    private void ExtractKID()
+    {
+        // playready
+        if (ProtectionSystemId.ToUpper() == "9A04F079-9840-4286-AB92-E65BE0885F95")
+        {
+            var bytes = HexUtil.HexToBytes(ProtectionData.Replace("00", ""));
+            var text = Encoding.ASCII.GetString(bytes);
+            var kidBytes = Convert.FromBase64String(KIDRegex().Match(text).Groups[1].Value);
+            // save kid for playready
+            this.ProtecitonKID_PR = HexUtil.BytesToHex(kidBytes);
+            // fix byte order
+            var reverse1 = new byte[4] { kidBytes[3], kidBytes[2], kidBytes[1], kidBytes[0] };
+            var reverse2 = new byte[4] { kidBytes[5], kidBytes[4], kidBytes[7], kidBytes[6] };
+            Array.Copy(reverse1, 0, kidBytes, 0, reverse1.Length);
+            Array.Copy(reverse2, 0, kidBytes, 4, reverse1.Length);
+            this.ProtecitonKID = HexUtil.BytesToHex(kidBytes);
+        }
+        // widevine
+        else if (ProtectionSystemId.ToUpper() == "EDEF8BA9-79D6-4ACE-A3C8-27DCD51D21ED")
+        {
+            throw new NotSupportedException();
+        }
+    }
+
+    public static bool CanHandle(string fourCC) => SupportedFourCC.Contains(fourCC);
+
+    private byte[] Box(string boxType, byte[] payload)
+    {
+        using var stream = new MemoryStream();
+        using var writer = new BinaryWriter2(stream);
+
+        writer.WriteUInt(8 + (uint)payload.Length);
+        writer.Write(boxType);
+        writer.Write(payload);
+
+        return stream.ToArray();
+    }
+
+    private byte[] FullBox(string boxType, byte version, uint flags, byte[] payload)
+    {
+        using var stream = new MemoryStream();
+        using var writer = new BinaryWriter2(stream);
+
+        writer.Write(version);
+        writer.WriteUInt(flags, offset: 1);
+        writer.Write(payload);
+
+        return Box(boxType, stream.ToArray());
+    }
+
+    private byte[] GenSinf(string codec)
+    {
+        var frmaBox = Box("frma", Encoding.ASCII.GetBytes(codec));
+
+        var sinfPayload = new List<byte>();
+        sinfPayload.AddRange(frmaBox);
+
+        var schmPayload = new List<byte>();
+        schmPayload.AddRange(Encoding.ASCII.GetBytes("cenc")); // scheme_type 'cenc' => common encryption
+        schmPayload.AddRange(new byte[] { 0, 1, 0, 0 }); // scheme_version Major version 1, Minor version 0
+        var schmBox = FullBox("schm", 0, 0, schmPayload.ToArray());
+
+        sinfPayload.AddRange(schmBox);
+
+        var tencPayload = new List<byte>();
+        tencPayload.AddRange(new byte[] { 0, 0 });
+        tencPayload.Add(0x1); // default_IsProtected
+        tencPayload.Add(0x8); // default_Per_Sample_IV_size
+        tencPayload.AddRange(HexUtil.HexToBytes(ProtecitonKID)); // default_KID
+        // tencPayload.Add(0x8);// default_constant_IV_size
+        // tencPayload.AddRange(new byte[8]);// default_constant_IV
+        var tencBox = FullBox("tenc", 0, 0, tencPayload.ToArray());
+
+        var schiBox = Box("schi", tencBox);
+        sinfPayload.AddRange(schiBox);
+
+        var sinfBox = Box("sinf", sinfPayload.ToArray());
+
+        return sinfBox;
+    }
+
+    private byte[] GenFtyp()
+    {
+        using var stream = new MemoryStream();
+        using var writer = new BinaryWriter2(stream);
+
+        writer.Write("isml"); // major brand
+        writer.WriteUInt(1); // minor version
+        writer.Write("iso5"); // compatible brand
+        writer.Write("iso6"); // compatible brand
+        writer.Write("piff"); // compatible brand
+        writer.Write("msdh"); // compatible brand
+
+        return Box("ftyp", stream.ToArray());
+    }
+
+    private byte[] GenMvhd()
+    {
+        using var stream = new MemoryStream();
+        using var writer = new BinaryWriter2(stream);
+
+        writer.WriteULong(CreationTime); // creation_time
+        writer.WriteULong(CreationTime); // modification_time
+        writer.WriteUInt(Timesacle); // timescale
+        writer.WriteULong(Duration); // duration
+        writer.WriteUShort(1, padding: 2); // rate
+        writer.WriteByte(1, padding: 1); // volume
+        writer.WriteUShort(0); // reserved
+        writer.WriteUInt(0);
+        writer.WriteUInt(0);
+
+        writer.Write(UnityMatrix);
+
+        writer.WriteUInt(0); // pre defined
+        writer.WriteUInt(0);
+        writer.WriteUInt(0);
+        writer.WriteUInt(0);
+        writer.WriteUInt(0);
+        writer.WriteUInt(0);
+
+        writer.WriteUInt(0xffffffff); // next track id
+
+
+        return FullBox("mvhd", 1, 0, stream.ToArray());
+    }
+
+    private byte[] GenTkhd()
+    {
+        using var stream = new MemoryStream();
+        using var writer = new BinaryWriter2(stream);
+
+        writer.WriteULong(CreationTime); // creation_time
+        writer.WriteULong(CreationTime); // modification_time
+        writer.WriteUInt(TrackId); // track id
+        writer.WriteUInt(0); // reserved
+        writer.WriteULong(Duration); // duration
+        writer.WriteUInt(0); // reserved
+        writer.WriteUInt(0);
+        writer.WriteShort(0); // layer
+        writer.WriteShort(0); // alternate group
+        writer.WriteByte(StreamType == "audio" ? (byte)1 : (byte)0, padding: 1); // volume
+        writer.WriteUShort(0); // reserved
+
+        writer.Write(UnityMatrix);
+
+        writer.WriteUShort(Width, padding: 2); // width
+        writer.WriteUShort(Height, padding: 2); // height
+
+        return FullBox("tkhd", 1, (uint)TRACK_ENABLED | TRACK_IN_MOVIE | TRACK_IN_PREVIEW, stream.ToArray());
+    }
+
+
+    private byte[] GenMdhd()
+    {
+        using var stream = new MemoryStream();
+        using var writer = new BinaryWriter2(stream);
+
+        writer.WriteULong(CreationTime); // creation_time
+        writer.WriteULong(CreationTime); // modification_time
+        writer.WriteUInt(Timesacle); // timescale
+        writer.WriteULong(Duration); // duration
+        writer.WriteUShort((Language[0] - 0x60) << 10 | (Language[1] - 0x60) << 5 | (Language[2] - 0x60)); // language
+        writer.WriteUShort(0); // pre defined
+
+        return FullBox("mdhd", 1, 0, stream.ToArray());
+    }
+
+    private byte[] GenHdlr()
+    {
+        using var stream = new MemoryStream();
+        using var writer = new BinaryWriter2(stream);
+
+        writer.WriteUInt(0); // pre defined
+        if (StreamType == "audio") writer.Write("soun");
+        else if (StreamType == "video") writer.Write("vide");
+        else if (StreamType == "text") writer.Write("subt");
+        else throw new NotSupportedException();
+
+        writer.WriteUInt(0); // reserved
+        writer.WriteUInt(0);
+        writer.WriteUInt(0);
+        writer.Write($"{StreamSpec.GroupId ?? "RE Handler"}\0"); // name
+
+        return FullBox("hdlr", 0, 0, stream.ToArray());
+    }
+
+    private byte[] GenMinf()
+    {
+        using var stream = new MemoryStream();
+        using var writer = new BinaryWriter2(stream);
+
+        var minfPayload = new List<byte>();
+        if (StreamType == "audio")
+        {
+            var smhd = new List<byte>();
+            smhd.Add(0); smhd.Add(0); // balance
+            smhd.Add(0); smhd.Add(0); // reserved
+
+            minfPayload.AddRange(FullBox("smhd", 0, 0, smhd.ToArray())); // Sound Media Header
+        }
+        else if (StreamType == "video")
+        {
+            var vmhd = new List<byte>();
+            vmhd.Add(0); vmhd.Add(0); // graphics mode
+            vmhd.Add(0); vmhd.Add(0); vmhd.Add(0); vmhd.Add(0); vmhd.Add(0); vmhd.Add(0);// opcolor
+
+            minfPayload.AddRange(FullBox("vmhd", 0, 1, vmhd.ToArray())); // Video Media Header
+        }
+        else if (StreamType == "text")
+        {
+            minfPayload.AddRange(FullBox("sthd", 0, 0, new byte[0])); // Subtitle Media Header
+        }
+        else
+        {
+            throw new NotSupportedException();
+        }
+
+        var drefPayload = new List<byte>();
+        drefPayload.Add(0); drefPayload.Add(0); drefPayload.Add(0); drefPayload.Add(1); // entry count
+        drefPayload.AddRange(FullBox("url ", 0, SELF_CONTAINED, new byte[0])); // Data Entry URL Box
+
+        var dinfPayload = FullBox("dref", 0, 0, drefPayload.ToArray()); // Data Reference Box
+        minfPayload.AddRange(Box("dinf", dinfPayload.ToArray())); // Data Information Box
+
+        return minfPayload.ToArray();
+    }
+
+    private byte[] GenEsds(byte[] audioSpecificConfig)
+    {
+        using var stream = new MemoryStream();
+        using var writer = new BinaryWriter2(stream);
+
+        // ESDS length = esds box header length (= 12) +
+        //               ES_Descriptor header length (= 5) +
+        //               DecoderConfigDescriptor header length (= 15) +
+        //               decoderSpecificInfo header length (= 2) +
+        //               AudioSpecificConfig length (= codecPrivateData length)
+        // esdsLength = 34 + len(audioSpecificConfig)
+
+        // ES_Descriptor (see ISO/IEC 14496-1 (Systems))
+        writer.WriteByte(0x03); // tag = 0x03 (ES_DescrTag)
+        writer.WriteByte((byte)(20 + audioSpecificConfig.Length)); // size
+        writer.WriteByte((byte)((TrackId & 0xFF00) >> 8)); // ES_ID = track_id
+        writer.WriteByte((byte)(TrackId & 0x00FF));
+        writer.WriteByte(0); // flags and streamPriority
+
+        // DecoderConfigDescriptor (see ISO/IEC 14496-1 (Systems))
+        writer.WriteByte(0x04); // tag = 0x04 (DecoderConfigDescrTag)
+        writer.WriteByte((byte)(15 + audioSpecificConfig.Length)); // size
+        writer.WriteByte(0x40); // objectTypeIndication = 0x40 (MPEG-4 AAC)
+        writer.WriteByte((0x05 << 2) | (0 << 1) | 1); // reserved = 1
+        writer.WriteByte(0xFF); // buffersizeDB = undefined
+        writer.WriteByte(0xFF); 
+        writer.WriteByte(0xFF);
+
+        var bandwidth = StreamSpec.Bandwidth!;
+        writer.WriteByte((byte)((bandwidth & 0xFF000000) >> 24)); // maxBitrate
+        writer.WriteByte((byte)((bandwidth & 0x00FF0000) >> 16));
+        writer.WriteByte((byte)((bandwidth & 0x0000FF00) >> 8));
+        writer.WriteByte((byte)(bandwidth  & 0x000000FF));
+        writer.WriteByte((byte)((bandwidth & 0xFF000000) >> 24)); // avgbitrate
+        writer.WriteByte((byte)((bandwidth & 0x00FF0000) >> 16)); 
+        writer.WriteByte((byte)((bandwidth & 0x0000FF00) >> 8));
+        writer.WriteByte((byte)(bandwidth  & 0x000000FF));
+
+        // DecoderSpecificInfo (see ISO/IEC 14496-1 (Systems))
+        writer.WriteByte(0x05); // tag = 0x05 (DecSpecificInfoTag)
+        writer.WriteByte((byte)audioSpecificConfig.Length); // size
+        writer.Write(audioSpecificConfig); // AudioSpecificConfig bytes
+
+        return FullBox("esds", 0, 0, stream.ToArray());
+    }
+
+    private byte[] GetSampleEntryBox()
+    {
+        using var stream = new MemoryStream();
+        using var writer = new BinaryWriter2(stream);
+
+        writer.WriteByte(0); // reserved
+        writer.WriteByte(0);
+        writer.WriteByte(0);
+        writer.WriteByte(0);
+        writer.WriteByte(0);
+        writer.WriteByte(0);
+        writer.WriteUShort(1); // data reference index
+
+        if (StreamType == "audio")
+        {
+            writer.WriteUInt(0); // reserved2
+            writer.WriteUInt(0);
+            writer.WriteUShort(Channels); // channels
+            writer.WriteUShort(BitsPerSample); // bits_per_sample
+            writer.WriteUShort(0); // pre defined
+            writer.WriteUShort(0); // reserved3
+            writer.WriteUShort(SamplingRate, padding: 2); // sampling_rate
+
+            var audioSpecificConfig = HexUtil.HexToBytes(CodecPrivateData);
+            var esdsBox = GenEsds(audioSpecificConfig);
+            writer.Write(esdsBox);
+
+            if (FourCC.StartsWith("AAC")) 
+            {
+                if (IsProtection)
+                {
+                    var sinfBox = GenSinf("mp4a");
+                    writer.Write(sinfBox);
+                    return Box("enca", stream.ToArray()); // Encrypted Audio
+                }
+                else
+                {
+                    return Box("mp4a", stream.ToArray());
+                }
+            }
+            if (FourCC == "EC-3")
+            {
+                if (IsProtection)
+                {
+                    var sinfBox = GenSinf("ec-3");
+                    writer.Write(sinfBox);
+                    return Box("enca", stream.ToArray()); // Encrypted Audio
+                }
+                else
+                {
+                    return Box("ec-3", stream.ToArray());
+                }
+            }
+        }
+        else if (StreamType == "video")
+        {
+            writer.WriteUShort(0); // pre defined
+            writer.WriteUShort(0); // reserved
+            writer.WriteUInt(0); // pre defined
+            writer.WriteUInt(0);
+            writer.WriteUInt(0);
+            writer.WriteUShort(Width); // width
+            writer.WriteUShort(Height); // height
+            writer.WriteUShort(0x48, padding: 2); // horiz resolution 72 dpi
+            writer.WriteUShort(0x48, padding: 2); // vert resolution 72 dpi
+            writer.WriteUInt(0); // reserved
+            writer.WriteUShort(1); // frame count
+            for (int i = 0; i < 32; i++) // compressor name
+            {
+                writer.WriteByte(0);
+            }
+            writer.WriteUShort(0x18); // depth
+            writer.WriteUShort(65535); // pre defined
+
+            var codecPrivateData = HexUtil.HexToBytes(CodecPrivateData);
+
+            if (FourCC == "H264" || FourCC == "AVC1" || FourCC == "DAVC" || FourCC == "AVC1")
+            {
+                var arr = CodecPrivateData.Split(new[] { StartCode }, StringSplitOptions.RemoveEmptyEntries);
+                var sps = HexUtil.HexToBytes(arr.Where(x => (HexUtil.HexToBytes(x[0..2])[0] & 0x1F) == 7).First());
+                var pps = HexUtil.HexToBytes(arr.Where(x => (HexUtil.HexToBytes(x[0..2])[0] & 0x1F) == 8).First());
+                // make avcC
+                var avcC = GetAvcC(sps, pps);
+                writer.Write(avcC);
+                if (IsProtection)
+                {
+                    var sinfBox = GenSinf("avc1");
+                    writer.Write(sinfBox);
+                    return Box("encv", stream.ToArray()); // Encrypted Video
+                }
+                else
+                {
+                    return Box("avc1", stream.ToArray()); // AVC Simple Entry
+                }
+            }
+            else if (FourCC == "HVC1" || FourCC == "HEV1")
+            {
+                var arr = CodecPrivateData.Split(new[] { StartCode }, StringSplitOptions.RemoveEmptyEntries);
+                var vps = HexUtil.HexToBytes(arr.Where(x => (HexUtil.HexToBytes(x[0..2])[0] >> 1) == 0x20).First());
+                var sps = HexUtil.HexToBytes(arr.Where(x => (HexUtil.HexToBytes(x[0..2])[0] >> 1) == 0x21).First());
+                var pps = HexUtil.HexToBytes(arr.Where(x => (HexUtil.HexToBytes(x[0..2])[0] >> 1) == 0x22).First());
+                // make hvcC
+                var hvcC = GetHvcC(sps, pps, vps);
+                writer.Write(hvcC);
+                if (IsProtection)
+                {
+                    var sinfBox = GenSinf("hvc1");
+                    writer.Write(sinfBox);
+                    return Box("encv", stream.ToArray()); // Encrypted Video
+                }
+                else
+                {
+                    return Box("hvc1", stream.ToArray()); // HEVC Simple Entry
+                }
+            }
+            // 杜比视界也按照hevc处理
+            else if (FourCC == "DVHE" || FourCC == "DVH1")
+            {
+                var arr = CodecPrivateData.Split(new[] { StartCode }, StringSplitOptions.RemoveEmptyEntries);
+                var vps = HexUtil.HexToBytes(arr.Where(x => (HexUtil.HexToBytes(x[0..2])[0] >> 1) == 0x20).First());
+                var sps = HexUtil.HexToBytes(arr.Where(x => (HexUtil.HexToBytes(x[0..2])[0] >> 1) == 0x21).First());
+                var pps = HexUtil.HexToBytes(arr.Where(x => (HexUtil.HexToBytes(x[0..2])[0] >> 1) == 0x22).First());
+                // make hvcC
+                var hvcC = GetHvcC(sps, pps, vps, "dvh1");
+                writer.Write(hvcC);
+                if (IsProtection)
+                {
+                    var sinfBox = GenSinf("dvh1");
+                    writer.Write(sinfBox);
+                    return Box("encv", stream.ToArray()); // Encrypted Video
+                }
+                else
+                {
+                    return Box("dvh1", stream.ToArray()); // HEVC Simple Entry
+                }
+            }
+            else
+            {
+                throw new NotSupportedException();
+            }
+        }
+        else if (StreamType == "text")
+        {
+            if (FourCC == "TTML")
+            {
+                writer.Write("http://www.w3.org/ns/ttml\0"); // namespace
+                writer.Write("\0"); // schema location
+                writer.Write("\0"); // auxilary mime types(??)
+                return Box("stpp", stream.ToArray()); // TTML Simple Entry
+            }
+            else
+            {
+                throw new NotSupportedException();
+            }
+        }
+        else
+        {
+            throw new NotSupportedException();
+        }
+
+        throw new NotSupportedException();
+    }
+
+    private byte[] GetAvcC(byte[] sps, byte[] pps)
+    {
+        using var stream = new MemoryStream();
+        using var writer = new BinaryWriter2(stream);
+
+        writer.WriteByte(1); // configuration version
+        writer.Write(sps[1..4]); // avc profile indication + profile compatibility + avc level indication
+        writer.WriteByte((byte)(0xfc | (NalUnitLengthField - 1))); // complete representation (1) + reserved (11111) + length size minus one
+        writer.WriteByte(1); // reserved (0) + number of sps (0000001)
+        writer.WriteUShort(sps.Length);
+        writer.Write(sps);
+        writer.WriteByte(1); // number of pps
+        writer.WriteUShort(pps.Length);
+        writer.Write(pps);
+
+        return Box("avcC", stream.ToArray()); // AVC Decoder Configuration Record
+    }
+
+    private byte[] GetHvcC(byte[] sps, byte[] pps, byte[] vps, string code = "hvc1")
+    {
+        var oriSps = new List<byte>(sps);
+        // https://www.itu.int/rec/dologin.asp?lang=f&id=T-REC-H.265-201504-S!!PDF-E&type=items
+        // Read generalProfileSpace, generalTierFlag, generalProfileIdc,
+        // generalProfileCompatibilityFlags, constraintBytes, generalLevelIdc
+        // from sps
+        var encList = new List<byte>();
+        /**
+         * 处理payload, 有00 00 03 0,1,2,3的情况 统一换成00 00 XX 即丢弃03
+         * 注意:此处采用的逻辑是直接简单粗暴地判断列表末尾3字节,如果是0x000003就删掉最后的0x03,可能会导致以下情况
+         * 00 00 03 03 03 03 03 01 会被直接处理成 => 00 00 01
+         * 此处经过测试只有直接跳过才正常,如果处理成 00 00 03 03 03 03 01 是有问题的
+         *
+         * 测试的数据如下:
+         *   原始:42 01 01 01 60 00 00 03 00 90 00 00 03 00 00 03 00 96 a0 01 e0 20 06 61 65 95 9a 49 30 bf fc 0c 7c 0c 81 a8 08 08 08 20 00 00 03 00 20 00 00 03 03 01
+         * 处理后:42 01 01 01 60 00 00 00 90 00 00 00 00 00 96 A0 01 E0 20 06 61 65 95 9A 49 30 BF FC 0C 7C 0C 81 A8 08 08 08 20 00 00 00 20 00 00 01
+         */
+        using (var _reader = new BinaryReader(new MemoryStream(sps)))
+        {
+            while (_reader.BaseStream.Position < _reader.BaseStream.Length)
+            {
+                encList.Add(_reader.ReadByte());
+                if (encList.Count >= 3 && encList[encList.Count - 3] == 0x00 && encList[encList.Count - 2] == 0x00 && encList[encList.Count - 1] == 0x03)
+                {
+                    encList.RemoveAt(encList.Count - 1);
+                }
+            }
+        }
+        sps = encList.ToArray();
+
+        using var reader = new BinaryReader2(new MemoryStream(sps));
+        reader.ReadBytes(2); // Skip 2 bytes unit header
+        var firstByte = reader.ReadByte();
+        var maxSubLayersMinus1 = (firstByte & 0xe) >> 1;
+        var nextByte = reader.ReadByte();
+        var generalProfileSpace = (nextByte & 0xc0) >> 6;
+        var generalTierFlag = (nextByte & 0x20) >> 5;
+        var generalProfileIdc = nextByte & 0x1f;
+        var generalProfileCompatibilityFlags = reader.ReadUInt32();
+        var constraintBytes = reader.ReadBytes(6);
+        var generalLevelIdc = reader.ReadByte();
+
+        /*var skipBit = 0;
+        for (int i = 0; i < maxSubLayersMinus1; i++)
+        {
+            skipBit += 2; // sub_layer_profile_present_flag sub_layer_level_present_flag
+        }
+        if (maxSubLayersMinus1 > 0)
+        {
+            for (int i = maxSubLayersMinus1; i < 8; i++)
+            {
+                skipBit += 2; // reserved_zero_2bits
+            }
+        }
+        for (int i = 0; i < maxSubLayersMinus1; i++)
+        {
+            skipBit += 2; // sub_layer_profile_present_flag sub_layer_level_present_flag
+        }*/
+
+        // 生成编码信息
+        var codecs = code +
+                     $".{HEVC_GENERAL_PROFILE_SPACE_STRINGS[generalProfileSpace]}{generalProfileIdc}" +
+                     $".{Convert.ToString(generalProfileCompatibilityFlags, 16)}" +
+                     $".{(generalTierFlag == 1 ? 'H' : 'L')}{generalLevelIdc}" +
+                     $".{HexUtil.BytesToHex(constraintBytes.Where(b => b != 0).ToArray())}";
+        StreamSpec.Codecs = codecs;
+
+
+        ///////////////////////
+
+
+        using var stream = new MemoryStream();
+        using var writer = new BinaryWriter2(stream);
+
+        // var reserved1 = 0xF;
+
+        writer.WriteByte(1); // configuration version
+        writer.WriteByte((byte)((generalProfileSpace << 6) + (generalTierFlag == 1 ? 0x20 : 0) | generalProfileIdc)); // general_profile_space + general_tier_flag + general_profile_idc
+        writer.WriteUInt(generalProfileCompatibilityFlags); // general_profile_compatibility_flags
+        writer.Write(constraintBytes); // general_constraint_indicator_flags
+        writer.WriteByte((byte)generalProfileIdc); // general_level_idc
+        writer.WriteUShort(0xf000); // reserved + min_spatial_segmentation_idc
+        writer.WriteByte(0xfc); // reserved + parallelismType
+        writer.WriteByte(0 | 0xfc); // reserved + chromaFormat 
+        writer.WriteByte(0 | 0xf8); // reserved + bitDepthLumaMinus8
+        writer.WriteByte(0 | 0xf8); // reserved + bitDepthChromaMinus8
+        writer.WriteUShort(0); // avgFrameRate
+        writer.WriteByte((byte)(0 << 6 | 0 << 3 | 0 << 2 | (NalUnitLengthField - 1))); // constantFrameRate + numTemporalLayers + temporalIdNested + lengthSizeMinusOne
+        writer.WriteByte(0x03); // numOfArrays (vps sps pps)
+            
+        sps = oriSps.ToArray();
+        writer.WriteByte(0x20); // array_completeness + reserved + NAL_unit_type
+        writer.WriteUShort(1); // numNalus 
+        writer.WriteUShort(vps.Length);
+        writer.Write(vps);
+        writer.WriteByte(0x21);
+        writer.WriteUShort(1); // numNalus
+        writer.WriteUShort(sps.Length);
+        writer.Write(sps);
+        writer.WriteByte(0x22); 
+        writer.WriteUShort(1); // numNalus
+        writer.WriteUShort(pps.Length);
+        writer.Write(pps);
+
+        return Box("hvcC", stream.ToArray()); // HEVC Decoder Configuration Record
+    }
+
+    private byte[] GetStsd()
+    {
+        using var stream = new MemoryStream();
+        using var writer = new BinaryWriter2(stream);
+
+        writer.WriteUInt(1); // entry count
+        var sampleEntryData = GetSampleEntryBox();
+        writer.Write(sampleEntryData);
+
+        return stream.ToArray();
+    }
+
+    private byte[] GetMehd()
+    {
+        using var stream = new MemoryStream();
+        using var writer = new BinaryWriter2(stream);
+
+        writer.WriteULong(Duration);
+
+        return FullBox("mehd", 1, 0, stream.ToArray()); // Movie Extends Header Box
+    }
+    private byte[] GetTrex()
+    {
+        using var stream = new MemoryStream();
+        using var writer = new BinaryWriter2(stream);
+
+        writer.WriteUInt(TrackId); // track id
+        writer.WriteUInt(1); // default sample description index
+        writer.WriteUInt(0); // default sample duration
+        writer.WriteUInt(0); // default sample size
+        writer.WriteUInt(0); // default sample flags
+
+        return FullBox("trex", 0, 0, stream.ToArray()); // Track Extends Box
+    }
+
+    private byte[] GenPsshBoxForPlayReady()
+    {
+        using var _stream = new MemoryStream();
+        using var _writer = new BinaryWriter2(_stream);
+        var sysIdData = HexUtil.HexToBytes(ProtectionSystemId.Replace("-", ""));
+        var psshData = HexUtil.HexToBytes(ProtectionData);
+
+        _writer.Write(sysIdData);  // SystemID 16 bytes
+        _writer.WriteUInt(psshData.Length); // Size of Data 4 bytes
+        _writer.Write(psshData); // Data
+        var psshBox = FullBox("pssh", 0, 0, _stream.ToArray());
+        return psshBox;
+    }
+
+    private byte[] GenPsshBoxForWideVine()
+    {
+        using var _stream = new MemoryStream();
+        using var _writer = new BinaryWriter2(_stream);
+        var sysIdData = HexUtil.HexToBytes("edef8ba9-79d6-4ace-a3c8-27dcd51d21ed".Replace("-", ""));
+        // var kid = HexUtil.HexToBytes(ProtecitonKID);
+
+        _writer.Write(sysIdData);  // SystemID 16 bytes
+        var psshData = HexUtil.HexToBytes($"08011210{ProtecitonKID}1A046E647265220400000000");
+        _writer.WriteUInt(psshData.Length); // Size of Data 4 bytes
+        _writer.Write(psshData); // Data
+        var psshBox = FullBox("pssh", 0, 0, _stream.ToArray());
+        return psshBox;
+    }
+
+    private byte[] GenMoof()
+    {
+        using var stream = new MemoryStream();
+        using var writer = new BinaryWriter2(stream);
+
+        // make senc
+        writer.WriteUInt(1); // sample_count
+        writer.Write(new byte[8]); // 8 bytes IV
+
+        var sencBox = FullBox("senc", 1, 0, stream.ToArray());
+
+        var moofBox = Box("moof", sencBox); // Movie Extends Box
+
+        return moofBox;
+    }
+
+    public byte[] GenHeader(byte[] firstSegment)
+    {
+        new MP4Parser()
+            .Box("moof", MP4Parser.Children)
+            .Box("traf", MP4Parser.Children)
+            .FullBox("tfhd", (box) =>
+            {
+                TrackId = (int)box.Reader.ReadUInt32();
+            })
+            .Parse(firstSegment);
+
+        return GenHeader();
+    }
+
+    public byte[] GenHeader()
+    {
+        using var stream = new MemoryStream();
+
+        var ftyp = GenFtyp(); // File Type Box
+        stream.Write(ftyp);
+
+        var moovPayload = GenMvhd(); // Movie Header Box
+
+        var trakPayload = GenTkhd(); // Track Header Box
+
+        var mdhdPayload = GenMdhd(); // Media Header Box
+
+        var hdlrPayload = GenHdlr(); // Handler Reference Box
+
+        var mdiaPayload = mdhdPayload.Concat(hdlrPayload).ToArray();
+
+        var minfPayload = GenMinf();
+
+
+        var sttsPayload = new byte[] { 0, 0, 0, 0 }; // entry count
+        var stblPayload = FullBox("stts", 0, 0, sttsPayload); // Decoding Time to Sample Box
+
+        var stscPayload = new byte[] { 0, 0, 0, 0 }; // entry count
+        var stscBox = FullBox("stsc", 0, 0, stscPayload); // Sample To Chunk Box
+
+        var stcoPayload = new byte[] { 0, 0, 0, 0 }; // entry count
+        var stcoBox = FullBox("stco", 0, 0, stcoPayload); // Chunk Offset Box
+
+        var stszPayload = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 }; // sample size, sample count
+        var stszBox = FullBox("stsz", 0, 0, stszPayload); // Sample Size Box
+
+        var stsdPayload = GetStsd();
+        var stsdBox = FullBox("stsd", 0, 0, stsdPayload); // Sample Description Box
+
+        stblPayload = stblPayload.Concat(stscBox).Concat(stcoBox).Concat(stszBox).Concat(stsdBox).ToArray();
+
+
+        var stblBox = Box("stbl", stblPayload); // Sample Table Box
+        minfPayload = minfPayload.Concat(stblBox).ToArray();
+
+        var minfBox = Box("minf", minfPayload); // Media Information Box
+        mdiaPayload = mdiaPayload.Concat(minfBox).ToArray();
+
+        var mdiaBox = Box("mdia", mdiaPayload); // Media Box
+        trakPayload = trakPayload.Concat(mdiaBox).ToArray();
+
+        var trakBox = Box("trak", trakPayload); // Track Box
+        moovPayload = moovPayload.Concat(trakBox).ToArray();
+
+        var mvexPayload = GetMehd();
+        var trexBox = GetTrex();
+        mvexPayload = mvexPayload.Concat(trexBox).ToArray();
+
+        var mvexBox = Box("mvex", mvexPayload); // Movie Extends Box
+        moovPayload = moovPayload.Concat(mvexBox).ToArray();
+
+        if (IsProtection)
+        {
+            var psshBox1 = GenPsshBoxForPlayReady();
+            var psshBox2 = GenPsshBoxForWideVine();
+            moovPayload = moovPayload.Concat(psshBox1).Concat(psshBox2).ToArray();
+        }
+
+        var moovBox = Box("moov", moovPayload); // Movie Box
+
+        stream.Write(moovBox);
+
+        // var moofBox = GenMoof(); // Movie Extends Box
+        // stream.Write(moofBox);
+
+        return stream.ToArray();
+    }
+}
\ No newline at end of file
diff --git a/src/N_m3u8DL-RE.Parser/Processor/ContentProcessor.cs b/src/N_m3u8DL-RE.Parser/Processor/ContentProcessor.cs
index 60b0a87..ba1a6ea 100644
--- a/src/N_m3u8DL-RE.Parser/Processor/ContentProcessor.cs
+++ b/src/N_m3u8DL-RE.Parser/Processor/ContentProcessor.cs
@@ -6,11 +6,10 @@ using System.Linq;
 using System.Text;
 using System.Threading.Tasks;
 
-namespace N_m3u8DL_RE.Parser.Processor
+namespace N_m3u8DL_RE.Parser.Processor;
+
+public abstract class ContentProcessor
 {
-    public abstract class ContentProcessor
-    {
-        public abstract bool CanProcess(ExtractorType extractorType, string rawText, ParserConfig parserConfig);
-        public abstract string Process(string rawText, ParserConfig parserConfig);
-    }
-}
+    public abstract bool CanProcess(ExtractorType extractorType, string rawText, ParserConfig parserConfig);
+    public abstract string Process(string rawText, ParserConfig parserConfig);
+}
\ No newline at end of file
diff --git a/src/N_m3u8DL-RE.Parser/Processor/DASH/DefaultDASHContentProcessor.cs b/src/N_m3u8DL-RE.Parser/Processor/DASH/DefaultDASHContentProcessor.cs
index aa69a94..97ede43 100644
--- a/src/N_m3u8DL-RE.Parser/Processor/DASH/DefaultDASHContentProcessor.cs
+++ b/src/N_m3u8DL-RE.Parser/Processor/DASH/DefaultDASHContentProcessor.cs
@@ -7,30 +7,29 @@ using System.Linq;
 using System.Text;
 using System.Threading.Tasks;
 
-namespace N_m3u8DL_RE.Parser.Processor.DASH
+namespace N_m3u8DL_RE.Parser.Processor.DASH;
+
+/// <summary>
+/// XG视频处理
+/// </summary>
+public class DefaultDASHContentProcessor : ContentProcessor
 {
-    /// <summary>
-    /// 西瓜视频处理
-    /// </summary>
-    public class DefaultDASHContentProcessor : ContentProcessor
+    public override bool CanProcess(ExtractorType extractorType, string mpdContent, ParserConfig parserConfig)
     {
-        public override bool CanProcess(ExtractorType extractorType, string mpdContent, ParserConfig parserConfig)
+        if (extractorType != ExtractorType.MPEG_DASH) return false;
+
+        if (mpdContent.Contains("<mas:") && !mpdContent.Contains("xmlns:mas"))
         {
-            if (extractorType != ExtractorType.MPEG_DASH) return false;
-
-            if (mpdContent.Contains("<mas:") && !mpdContent.Contains("xmlns:mas"))
-            {
-                return true;
-            }
-            return false;
-        }
-
-        public override string Process(string mpdContent, ParserConfig parserConfig)
-        {
-            Logger.Debug("Fix xigua mpd...");
-            mpdContent = mpdContent.Replace("<MPD ", "<MPD xmlns:mas=\"urn:marlin:mas:1-0:services:schemas:mpd\" ");
-
-            return mpdContent;
+            return true;
         }
+        return false;
     }
-}
+
+    public override string Process(string mpdContent, ParserConfig parserConfig)
+    {
+        Logger.Debug("Fix xigua mpd...");
+        mpdContent = mpdContent.Replace("<MPD ", "<MPD xmlns:mas=\"urn:marlin:mas:1-0:services:schemas:mpd\" ");
+
+        return mpdContent;
+    }
+}
\ No newline at end of file
diff --git a/src/N_m3u8DL-RE.Parser/Processor/DefaultUrlProcessor.cs b/src/N_m3u8DL-RE.Parser/Processor/DefaultUrlProcessor.cs
index ee518f3..e2bd061 100644
--- a/src/N_m3u8DL-RE.Parser/Processor/DefaultUrlProcessor.cs
+++ b/src/N_m3u8DL-RE.Parser/Processor/DefaultUrlProcessor.cs
@@ -9,38 +9,37 @@ using System.Text.RegularExpressions;
 using System.Threading.Tasks;
 using System.Web;
 
-namespace N_m3u8DL_RE.Parser.Processor
+namespace N_m3u8DL_RE.Parser.Processor;
+
+public class DefaultUrlProcessor : UrlProcessor
 {
-    public class DefaultUrlProcessor : UrlProcessor
+    public override bool CanProcess(ExtractorType extractorType, string oriUrl, ParserConfig paserConfig) => paserConfig.AppendUrlParams;
+
+    public override string Process(string oriUrl, ParserConfig paserConfig)
     {
-        public override bool CanProcess(ExtractorType extractorType, string oriUrl, ParserConfig paserConfig) => paserConfig.AppendUrlParams;
-
-        public override string Process(string oriUrl, ParserConfig paserConfig)
+        if (oriUrl.StartsWith("http")) 
         {
-            if (oriUrl.StartsWith("http")) 
+            var uriFromConfig = new Uri(paserConfig.Url);
+            var uriFromConfigQuery = HttpUtility.ParseQueryString(uriFromConfig.Query);
+
+            var oldUri = new Uri(oriUrl);
+            var newQuery = HttpUtility.ParseQueryString(oldUri.Query);
+            foreach (var item in uriFromConfigQuery.AllKeys)
             {
-                var uriFromConfig = new Uri(paserConfig.Url);
-                var uriFromConfigQuery = HttpUtility.ParseQueryString(uriFromConfig.Query);
-
-                var oldUri = new Uri(oriUrl);
-                var newQuery = HttpUtility.ParseQueryString(oldUri.Query);
-                foreach (var item in uriFromConfigQuery.AllKeys)
-                {
-                    if (newQuery.AllKeys.Contains(item))
-                        newQuery.Set(item, uriFromConfigQuery.Get(item));
-                    else
-                        newQuery.Add(item, uriFromConfigQuery.Get(item));
-                }
-
-                if (!string.IsNullOrEmpty(newQuery.ToString()))
-                {
-                    Logger.Debug("Before: " + oriUrl);
-                    oriUrl = (oldUri.GetLeftPart(UriPartial.Path) + "?" + newQuery.ToString()).TrimEnd('?');
-                    Logger.Debug("After: " + oriUrl);
-                }
+                if (newQuery.AllKeys.Contains(item))
+                    newQuery.Set(item, uriFromConfigQuery.Get(item));
+                else
+                    newQuery.Add(item, uriFromConfigQuery.Get(item));
             }
 
-            return oriUrl;
+            if (!string.IsNullOrEmpty(newQuery.ToString()))
+            {
+                Logger.Debug("Before: " + oriUrl);
+                oriUrl = (oldUri.GetLeftPart(UriPartial.Path) + "?" + newQuery.ToString()).TrimEnd('?');
+                Logger.Debug("After: " + oriUrl);
+            }
         }
+
+        return oriUrl;
     }
-}
+}
\ No newline at end of file
diff --git a/src/N_m3u8DL-RE.Parser/Processor/HLS/DefaultHLSContentProcessor.cs b/src/N_m3u8DL-RE.Parser/Processor/HLS/DefaultHLSContentProcessor.cs
index 3e69ee0..2e99e61 100644
--- a/src/N_m3u8DL-RE.Parser/Processor/HLS/DefaultHLSContentProcessor.cs
+++ b/src/N_m3u8DL-RE.Parser/Processor/HLS/DefaultHLSContentProcessor.cs
@@ -8,101 +8,94 @@ using System.Text;
 using System.Text.RegularExpressions;
 using System.Threading.Tasks;
 
-namespace N_m3u8DL_RE.Parser.Processor.HLS
+namespace N_m3u8DL_RE.Parser.Processor.HLS;
+
+public partial class DefaultHLSContentProcessor : ContentProcessor
 {
-    public partial class DefaultHLSContentProcessor : ContentProcessor
+    [GeneratedRegex("#EXT-X-DISCONTINUITY\\s+#EXT-X-MAP:URI=\\\"(.*?)\\\",BYTERANGE=\\\"(.*?)\\\"")]
+    private static partial Regex YkDVRegex();
+    [GeneratedRegex("#EXT-X-MAP:URI=\\\".*?BUMPER/[\\s\\S]+?#EXT-X-DISCONTINUITY")]
+    private static partial Regex DNSPRegex();
+    [GeneratedRegex("#EXTINF:.*?,\\s+.*BUMPER.*\\s+?#EXT-X-DISCONTINUITY")]
+    private static partial Regex DNSPSubRegex();
+    [GeneratedRegex("(#EXTINF.*)(\\s+)(#EXT-X-KEY.*)")]
+    private static partial Regex OrderFixRegex();
+    [GeneratedRegex("#EXT-X-MAP.*\\.apple\\.com/")]
+    private static partial Regex ATVRegex();
+    [GeneratedRegex("(#EXT-X-KEY:[\\s\\S]*?)(#EXT-X-DISCONTINUITY|#EXT-X-ENDLIST)")]
+    private static partial Regex ATVRegex2();
+
+    public override bool CanProcess(ExtractorType extractorType, string rawText, ParserConfig parserConfig) => extractorType == ExtractorType.HLS;
+
+    public override string Process(string m3u8Content, ParserConfig parserConfig)
     {
-        [GeneratedRegex("#EXT-X-DISCONTINUITY\\s+#EXT-X-MAP:URI=\\\"(.*?)\\\",BYTERANGE=\\\"(.*?)\\\"")]
-        private static partial Regex YkDVRegex();
-        [GeneratedRegex("#EXT-X-MAP:URI=\\\".*?BUMPER/[\\s\\S]+?#EXT-X-DISCONTINUITY")]
-        private static partial Regex DNSPRegex();
-        [GeneratedRegex("#EXTINF:.*?,\\s+.*BUMPER.*\\s+?#EXT-X-DISCONTINUITY")]
-        private static partial Regex DNSPSubRegex();
-        [GeneratedRegex("(#EXTINF.*)(\\s+)(#EXT-X-KEY.*)")]
-        private static partial Regex OrderFixRegex();
-        [GeneratedRegex("#EXT-X-MAP.*\\.apple\\.com/")]
-        private static partial Regex ATVRegex();
-        [GeneratedRegex("(#EXT-X-KEY:[\\s\\S]*?)(#EXT-X-DISCONTINUITY|#EXT-X-ENDLIST)")]
-        private static partial Regex ATVRegex2();
-
-        public override bool CanProcess(ExtractorType extractorType, string rawText, ParserConfig parserConfig) => extractorType == ExtractorType.HLS;
-
-        public override string Process(string m3u8Content, ParserConfig parserConfig)
+        // 处理content以\r作为换行符的情况
+        if (m3u8Content.Contains("\r") && !m3u8Content.Contains("\n"))
         {
-            //处理content以\r作为换行符的情况
-            if (m3u8Content.Contains("\r") && !m3u8Content.Contains("\n"))
-            {
-                m3u8Content = m3u8Content.Replace("\r", Environment.NewLine);
-            }
-
-            var m3u8Url = parserConfig.Url;
-            //央视频回放
-            if (m3u8Url.Contains("tlivecloud-playback-cdn.ysp.cctv.cn") && m3u8Url.Contains("endtime="))
-            {
-                m3u8Content += Environment.NewLine + HLSTags.ext_x_endlist;
-            }
-
-            //IMOOC
-            if (m3u8Url.Contains("imooc.com/"))
-            {
-                //M3u8Content = DecodeImooc.DecodeM3u8(M3u8Content);
-            }
-
-            //iqy
-            if (m3u8Content.StartsWith("{\"payload\""))
-            {
-                //
-            }
-
-            //针对优酷#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 = YkDVRegex();
-                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}");
-                }
-            }
-
-            //针对Disney+修正
-            if (m3u8Content.Contains("#EXT-X-DISCONTINUITY") && m3u8Content.Contains("#EXT-X-MAP") && m3u8Url.Contains("media.dssott.com/"))
-            {
-                Regex ykmap = DNSPRegex();
-                if (ykmap.IsMatch(m3u8Content))
-                {
-                    m3u8Content = m3u8Content.Replace(ykmap.Match(m3u8Content).Value, "#XXX");
-                }
-            }
-
-            //针对Disney+字幕修正
-            if (m3u8Content.Contains("#EXT-X-DISCONTINUITY") && m3u8Content.Contains("seg_00000.vtt") && m3u8Url.Contains("media.dssott.com/"))
-            {
-                Regex ykmap = DNSPSubRegex();
-                if (ykmap.IsMatch(m3u8Content))
-                {
-                    m3u8Content = m3u8Content.Replace(ykmap.Match(m3u8Content).Value, "#XXX");
-                }
-            }
-
-            //针对AppleTv修正
-            if (m3u8Content.Contains("#EXT-X-DISCONTINUITY") && m3u8Content.Contains("#EXT-X-MAP") && (m3u8Url.Contains(".apple.com/") || ATVRegex().IsMatch(m3u8Content)))
-            {
-                //只取加密部分即可
-                Regex ykmap = ATVRegex2();
-                if (ykmap.IsMatch(m3u8Content))
-                {
-                    m3u8Content = "#EXTM3U\r\n" + ykmap.Match(m3u8Content).Groups[1].Value + "\r\n#EXT-X-ENDLIST";
-                }
-            }
-
-            //修复#EXT-X-KEY与#EXTINF出现次序异常问题
-            var regex = OrderFixRegex();
-            if (regex.IsMatch(m3u8Content))
-            {
-                m3u8Content = regex.Replace(m3u8Content, "$3$2$1");
-            }
-
-            return m3u8Content;
+            m3u8Content = m3u8Content.Replace("\r", Environment.NewLine);
         }
+
+        var m3u8Url = parserConfig.Url;
+        // YSP回放
+        if (m3u8Url.Contains("tlivecloud-playback-cdn.ysp.cctv.cn") && m3u8Url.Contains("endtime="))
+        {
+            m3u8Content += Environment.NewLine + HLSTags.ext_x_endlist;
+        }
+
+        // IMOOC
+        if (m3u8Url.Contains("imooc.com/"))
+        {
+            // M3u8Content = DecodeImooc.DecodeM3u8(M3u8Content);
+        }
+
+        // 针对YK #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 = YkDVRegex();
+            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}");
+            }
+        }
+
+        // 针对Disney+修正
+        if (m3u8Content.Contains("#EXT-X-DISCONTINUITY") && m3u8Content.Contains("#EXT-X-MAP") && m3u8Url.Contains("media.dssott.com/"))
+        {
+            Regex ykmap = DNSPRegex();
+            if (ykmap.IsMatch(m3u8Content))
+            {
+                m3u8Content = m3u8Content.Replace(ykmap.Match(m3u8Content).Value, "#XXX");
+            }
+        }
+
+        // 针对Disney+字幕修正
+        if (m3u8Content.Contains("#EXT-X-DISCONTINUITY") && m3u8Content.Contains("seg_00000.vtt") && m3u8Url.Contains("media.dssott.com/"))
+        {
+            Regex ykmap = DNSPSubRegex();
+            if (ykmap.IsMatch(m3u8Content))
+            {
+                m3u8Content = m3u8Content.Replace(ykmap.Match(m3u8Content).Value, "#XXX");
+            }
+        }
+
+        // 针对AppleTv修正
+        if (m3u8Content.Contains("#EXT-X-DISCONTINUITY") && m3u8Content.Contains("#EXT-X-MAP") && (m3u8Url.Contains(".apple.com/") || ATVRegex().IsMatch(m3u8Content)))
+        {
+            // 只取加密部分即可
+            Regex ykmap = ATVRegex2();
+            if (ykmap.IsMatch(m3u8Content))
+            {
+                m3u8Content = "#EXTM3U\r\n" + ykmap.Match(m3u8Content).Groups[1].Value + "\r\n#EXT-X-ENDLIST";
+            }
+        }
+
+        // 修复#EXT-X-KEY与#EXTINF出现次序异常问题
+        var regex = OrderFixRegex();
+        if (regex.IsMatch(m3u8Content))
+        {
+            m3u8Content = regex.Replace(m3u8Content, "$3$2$1");
+        }
+
+        return m3u8Content;
     }
-}
+}
\ No newline at end of file
diff --git a/src/N_m3u8DL-RE.Parser/Processor/HLS/DefaultHLSKeyProcessor.cs b/src/N_m3u8DL-RE.Parser/Processor/HLS/DefaultHLSKeyProcessor.cs
index 556bdcc..9f8826c 100644
--- a/src/N_m3u8DL-RE.Parser/Processor/HLS/DefaultHLSKeyProcessor.cs
+++ b/src/N_m3u8DL-RE.Parser/Processor/HLS/DefaultHLSKeyProcessor.cs
@@ -6,112 +6,106 @@ using N_m3u8DL_RE.Common.Util;
 using N_m3u8DL_RE.Parser.Config;
 using N_m3u8DL_RE.Parser.Util;
 using Spectre.Console;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
 
-namespace N_m3u8DL_RE.Parser.Processor.HLS
+namespace N_m3u8DL_RE.Parser.Processor.HLS;
+
+public class DefaultHLSKeyProcessor : KeyProcessor
 {
-    public class DefaultHLSKeyProcessor : KeyProcessor
+    public override bool CanProcess(ExtractorType extractorType, string m3u8Url, string keyLine, string m3u8Content, ParserConfig paserConfig) => extractorType == ExtractorType.HLS;
+
+
+    public override EncryptInfo Process(string keyLine, string m3u8Url, string m3u8Content, ParserConfig parserConfig)
     {
-        public override bool CanProcess(ExtractorType extractorType, string m3u8Url, string keyLine, string m3u8Content, ParserConfig paserConfig) => extractorType == ExtractorType.HLS;
+        var iv = ParserUtil.GetAttribute(keyLine, "IV");
+        var method = ParserUtil.GetAttribute(keyLine, "METHOD");
+        var uri = ParserUtil.GetAttribute(keyLine, "URI");
 
+        Logger.Debug("METHOD:{},URI:{},IV:{}", method, uri, iv);
 
-        public override EncryptInfo Process(string keyLine, string m3u8Url, string m3u8Content, ParserConfig parserConfig)
+        var encryptInfo = new EncryptInfo(method);
+
+        // IV
+        if (!string.IsNullOrEmpty(iv))
         {
-            var iv = ParserUtil.GetAttribute(keyLine, "IV");
-            var method = ParserUtil.GetAttribute(keyLine, "METHOD");
-            var uri = ParserUtil.GetAttribute(keyLine, "URI");
+            encryptInfo.IV = HexUtil.HexToBytes(iv);
+        }
+        // 自定义IV
+        if (parserConfig.CustomeIV is { Length: > 0 }) 
+        {
+            encryptInfo.IV = parserConfig.CustomeIV;
+        }
 
-            Logger.Debug("METHOD:{},URI:{},IV:{}", method, uri, iv);
-
-            var encryptInfo = new EncryptInfo(method);
-
-            //IV
-            if (!string.IsNullOrEmpty(iv))
+        // KEY
+        try
+        {
+            if (parserConfig.CustomeKey is { Length: > 0 })
             {
-                encryptInfo.IV = HexUtil.HexToBytes(iv);
+                encryptInfo.Key = parserConfig.CustomeKey;
             }
-            //自定义IV
-            if (parserConfig.CustomeIV != null && parserConfig.CustomeIV.Length > 0) 
+            else if (uri.ToLower().StartsWith("base64:"))
             {
-                encryptInfo.IV = parserConfig.CustomeIV;
+                encryptInfo.Key = Convert.FromBase64String(uri[7..]);
             }
-
-            //KEY
-            try
+            else if (uri.ToLower().StartsWith("data:;base64,"))
             {
-                if (parserConfig.CustomeKey != null && parserConfig.CustomeKey.Length > 0)
-                {
-                    encryptInfo.Key = parserConfig.CustomeKey;
-                }
-                else if (uri.ToLower().StartsWith("base64:"))
-                {
-                    encryptInfo.Key = Convert.FromBase64String(uri[7..]);
-                }
-                else if (uri.ToLower().StartsWith("data:;base64,"))
-                {
-                    encryptInfo.Key = Convert.FromBase64String(uri[13..]);
-                }
-                else if (uri.ToLower().StartsWith("data:text/plain;base64,"))
-                {
-                    encryptInfo.Key = Convert.FromBase64String(uri[23..]);
-                }
-                else if (File.Exists(uri))
-                {
-                    encryptInfo.Key = File.ReadAllBytes(uri);
-                }
-                else if (!string.IsNullOrEmpty(uri))
-                {
-                    var retryCount = parserConfig.KeyRetryCount;
-                    var segUrl = PreProcessUrl(ParserUtil.CombineURL(m3u8Url, uri), parserConfig);
+                encryptInfo.Key = Convert.FromBase64String(uri[13..]);
+            }
+            else if (uri.ToLower().StartsWith("data:text/plain;base64,"))
+            {
+                encryptInfo.Key = Convert.FromBase64String(uri[23..]);
+            }
+            else if (File.Exists(uri))
+            {
+                encryptInfo.Key = File.ReadAllBytes(uri);
+            }
+            else if (!string.IsNullOrEmpty(uri))
+            {
+                var retryCount = parserConfig.KeyRetryCount;
+                var segUrl = PreProcessUrl(ParserUtil.CombineURL(m3u8Url, uri), parserConfig);
                 getHttpKey:
-                    try
-                    {
-                        var bytes = HTTPUtil.GetBytesAsync(segUrl, parserConfig.Headers).Result;
-                        encryptInfo.Key = bytes;
-                    }
-                    catch (Exception _ex) when (!_ex.Message.Contains("scheme is not supported."))
-                    {
-                        Logger.WarnMarkUp($"[grey]{_ex.Message.EscapeMarkup()} retryCount: {retryCount}[/]");
-                        Thread.Sleep(1000);
-                        if (retryCount-- > 0) goto getHttpKey;
-                        else throw;
-                    }
-                }
-            }
-            catch (Exception ex)
-            {
-                Logger.Error(ResString.cmd_loadKeyFailed + ": " + ex.Message);
-                encryptInfo.Method = EncryptMethod.UNKNOWN;
-            }
-
-            //处理自定义加密方式
-            if (parserConfig.CustomMethod != null)
-            {
-                encryptInfo.Method = parserConfig.CustomMethod.Value;
-                Logger.Warn("METHOD changed from {} to {}", method, encryptInfo.Method);
-            }
-
-            return encryptInfo;
-        }
-
-        /// <summary>
-        /// 预处理URL
-        /// </summary>
-        private string PreProcessUrl(string url, ParserConfig parserConfig)
-        {
-            foreach (var p in parserConfig.UrlProcessors)
-            {
-                if (p.CanProcess(ExtractorType.HLS, url, parserConfig))
+                try
                 {
-                    url = p.Process(url, parserConfig);
+                    var bytes = HTTPUtil.GetBytesAsync(segUrl, parserConfig.Headers).Result;
+                    encryptInfo.Key = bytes;
+                }
+                catch (Exception _ex) when (!_ex.Message.Contains("scheme is not supported."))
+                {
+                    Logger.WarnMarkUp($"[grey]{_ex.Message.EscapeMarkup()} retryCount: {retryCount}[/]");
+                    Thread.Sleep(1000);
+                    if (retryCount-- > 0) goto getHttpKey;
+                    throw;
                 }
             }
-
-            return url;
         }
+        catch (Exception ex)
+        {
+            Logger.Error(ResString.cmd_loadKeyFailed + ": " + ex.Message);
+            encryptInfo.Method = EncryptMethod.UNKNOWN;
+        }
+
+        // 处理自定义加密方式
+        if (parserConfig.CustomMethod != null)
+        {
+            encryptInfo.Method = parserConfig.CustomMethod.Value;
+            Logger.Warn("METHOD changed from {} to {}", method, encryptInfo.Method);
+        }
+
+        return encryptInfo;
     }
-}
+
+    /// <summary>
+    /// 预处理URL
+    /// </summary>
+    private string PreProcessUrl(string url, ParserConfig parserConfig)
+    {
+        foreach (var p in parserConfig.UrlProcessors)
+        {
+            if (p.CanProcess(ExtractorType.HLS, url, parserConfig))
+            {
+                url = p.Process(url, parserConfig);
+            }
+        }
+
+        return url;
+    }
+}
\ No newline at end of file
diff --git a/src/N_m3u8DL-RE.Parser/Processor/KeyProcessor.cs b/src/N_m3u8DL-RE.Parser/Processor/KeyProcessor.cs
index ec1438d..66dd612 100644
--- a/src/N_m3u8DL-RE.Parser/Processor/KeyProcessor.cs
+++ b/src/N_m3u8DL-RE.Parser/Processor/KeyProcessor.cs
@@ -7,11 +7,10 @@ using System.Linq;
 using System.Text;
 using System.Threading.Tasks;
 
-namespace N_m3u8DL_RE.Parser.Processor
+namespace N_m3u8DL_RE.Parser.Processor;
+
+public abstract class KeyProcessor
 {
-    public abstract class KeyProcessor
-    {
-        public abstract bool CanProcess(ExtractorType extractorType, string keyLine, string m3u8Url, string m3u8Content, ParserConfig parserConfig);
-        public abstract EncryptInfo Process(string keyLine, string m3u8Url, string m3u8Content, ParserConfig parserConfig);
-    }
-}
+    public abstract bool CanProcess(ExtractorType extractorType, string keyLine, string m3u8Url, string m3u8Content, ParserConfig parserConfig);
+    public abstract EncryptInfo Process(string keyLine, string m3u8Url, string m3u8Content, ParserConfig parserConfig);
+}
\ No newline at end of file
diff --git a/src/N_m3u8DL-RE.Parser/Processor/UrlProcessor.cs b/src/N_m3u8DL-RE.Parser/Processor/UrlProcessor.cs
index 6f3818e..1fa2f62 100644
--- a/src/N_m3u8DL-RE.Parser/Processor/UrlProcessor.cs
+++ b/src/N_m3u8DL-RE.Parser/Processor/UrlProcessor.cs
@@ -1,16 +1,10 @@
 using N_m3u8DL_RE.Common.Enum;
 using N_m3u8DL_RE.Parser.Config;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
 
-namespace N_m3u8DL_RE.Parser.Processor
+namespace N_m3u8DL_RE.Parser.Processor;
+
+public abstract class UrlProcessor
 {
-    public abstract class UrlProcessor
-    {
-        public abstract bool CanProcess(ExtractorType extractorType, string oriUrl, ParserConfig parserConfig);
-        public abstract string Process(string oriUrl, ParserConfig parserConfig);
-    }
-}
+    public abstract bool CanProcess(ExtractorType extractorType, string oriUrl, ParserConfig parserConfig);
+    public abstract string Process(string oriUrl, ParserConfig parserConfig);
+}
\ No newline at end of file
diff --git a/src/N_m3u8DL-RE.Parser/StreamExtractor.cs b/src/N_m3u8DL-RE.Parser/StreamExtractor.cs
index 81215ca..a7ef235 100644
--- a/src/N_m3u8DL-RE.Parser/StreamExtractor.cs
+++ b/src/N_m3u8DL-RE.Parser/StreamExtractor.cs
@@ -6,144 +6,142 @@ using N_m3u8DL_RE.Parser.Constants;
 using N_m3u8DL_RE.Parser.Extractor;
 using N_m3u8DL_RE.Common.Util;
 using N_m3u8DL_RE.Common.Enum;
-using Spectre.Console;
 
-namespace N_m3u8DL_RE.Parser
+namespace N_m3u8DL_RE.Parser;
+
+public class StreamExtractor
 {
-    public class StreamExtractor
+    public ExtractorType ExtractorType => extractor.ExtractorType;
+    private IExtractor extractor;
+    private ParserConfig parserConfig = new();
+    private string rawText;
+    private static SemaphoreSlim semaphore = new(1, 1);
+
+    public Dictionary<string, string> RawFiles { get; set; } = new(); // 存储(文件名,文件内容)
+
+    public StreamExtractor()
     {
-        public ExtractorType ExtractorType { get => extractor.ExtractorType; }
-        private IExtractor extractor;
-        private ParserConfig parserConfig = new ParserConfig();
-        private string rawText;
-        private static SemaphoreSlim semaphore = new SemaphoreSlim(1, 1);
 
-        public Dictionary<string, string> RawFiles { get; set; } = new(); //存储(文件名,文件内容)
+    }
 
-        public StreamExtractor()
+    public StreamExtractor(ParserConfig parserConfig)
+    {
+        this.parserConfig = parserConfig;
+    }
+
+    public async Task LoadSourceFromUrlAsync(string url)
+    {
+        Logger.Info(ResString.loadingUrl + url);
+        if (url.StartsWith("file:"))
         {
+            var uri = new Uri(url);
+            this.rawText = await File.ReadAllTextAsync(uri.LocalPath);
+            parserConfig.OriginalUrl = parserConfig.Url = url;
+        }
+        else if (url.StartsWith("http"))
+        {
+            parserConfig.OriginalUrl = url;
+            (this.rawText, url) = await HTTPUtil.GetWebSourceAndNewUrlAsync(url, parserConfig.Headers);
+            parserConfig.Url = url;
+        }
+        else if (File.Exists(url))
+        {
+            url = Path.GetFullPath(url);
+            this.rawText = await File.ReadAllTextAsync(url);
+            parserConfig.OriginalUrl = parserConfig.Url = new Uri(url).AbsoluteUri;
+        }
+        this.rawText = rawText.Trim();
+        LoadSourceFromText(this.rawText);
+    }
 
+    public void LoadSourceFromText(string rawText)
+    {
+        var rawType = "txt";
+        rawText = rawText.Trim();
+        this.rawText = rawText;
+        if (rawText.StartsWith(HLSTags.ext_m3u))
+        {
+            Logger.InfoMarkUp(ResString.matchHLS);
+            extractor = new HLSExtractor(parserConfig);
+            rawType = "m3u8";
+        }
+        else if (rawText.Contains("</MPD>") && rawText.Contains("<MPD"))
+        {
+            Logger.InfoMarkUp(ResString.matchDASH);
+            // extractor = new DASHExtractor(parserConfig);
+            extractor = new DASHExtractor2(parserConfig);
+            rawType = "mpd";
+        }
+        else if (rawText.Contains("</SmoothStreamingMedia>") && rawText.Contains("<SmoothStreamingMedia"))
+        {
+            Logger.InfoMarkUp(ResString.matchMSS);
+            // extractor = new DASHExtractor(parserConfig);
+            extractor = new MSSExtractor(parserConfig);
+            rawType = "ism";
+        }
+        else if (rawText == ResString.ReLiveTs)
+        {
+            Logger.InfoMarkUp(ResString.matchTS);
+            extractor = new LiveTSExtractor(parserConfig);
+        }
+        else
+        {
+            throw new NotSupportedException(ResString.notSupported);
         }
 
-        public StreamExtractor(ParserConfig parserConfig)
+        RawFiles[$"raw.{rawType}"] = rawText;
+    }
+
+    /// <summary>
+    /// 开始解析流媒体信息
+    /// </summary>
+    /// <returns></returns>
+    public async Task<List<StreamSpec>> ExtractStreamsAsync()
+    {
+        try
         {
-            this.parserConfig = parserConfig;
+            await semaphore.WaitAsync();
+            Logger.Info(ResString.parsingStream);
+            return await extractor.ExtractStreamsAsync(rawText);
         }
-
-        public async Task LoadSourceFromUrlAsync(string url)
+        finally
         {
-            Logger.Info(ResString.loadingUrl + url);
-            if (url.StartsWith("file:"))
-            {
-                var uri = new Uri(url);
-                this.rawText = await File.ReadAllTextAsync(uri.LocalPath);
-                parserConfig.OriginalUrl = parserConfig.Url = url;
-            }
-            else if (url.StartsWith("http"))
-            {
-                parserConfig.OriginalUrl = url;
-                (this.rawText, url) = await HTTPUtil.GetWebSourceAndNewUrlAsync(url, parserConfig.Headers);
-                parserConfig.Url = url;
-            }
-            else if (File.Exists(url))
-            {
-                url = Path.GetFullPath(url);
-                this.rawText = await File.ReadAllTextAsync(url);
-                parserConfig.OriginalUrl = parserConfig.Url = new Uri(url).AbsoluteUri;
-            }
-            this.rawText = rawText.Trim();
-            LoadSourceFromText(this.rawText);
-        }
-
-        public void LoadSourceFromText(string rawText)
-        {
-            var rawType = "txt";
-            rawText = rawText.Trim();
-            this.rawText = rawText;
-            if (rawText.StartsWith(HLSTags.ext_m3u))
-            {
-                Logger.InfoMarkUp(ResString.matchHLS);
-                extractor = new HLSExtractor(parserConfig);
-                rawType = "m3u8";
-            }
-            else if (rawText.Contains("</MPD>") && rawText.Contains("<MPD"))
-            {
-                Logger.InfoMarkUp(ResString.matchDASH);
-                //extractor = new DASHExtractor(parserConfig);
-                extractor = new DASHExtractor2(parserConfig);
-                rawType = "mpd";
-            }
-            else if (rawText.Contains("</SmoothStreamingMedia>") && rawText.Contains("<SmoothStreamingMedia"))
-            {
-                Logger.InfoMarkUp(ResString.matchMSS);
-                //extractor = new DASHExtractor(parserConfig);
-                extractor = new MSSExtractor(parserConfig);
-                rawType = "ism";
-            }
-            else if (rawText == ResString.ReLiveTs)
-            {
-                Logger.InfoMarkUp(ResString.matchTS);
-                extractor = new LiveTSExtractor(parserConfig);
-            }
-            else
-            {
-                throw new NotSupportedException(ResString.notSupported);
-            }
-
-            RawFiles[$"raw.{rawType}"] = rawText;
-        }
-
-        /// <summary>
-        /// 开始解析流媒体信息
-        /// </summary>
-        /// <returns></returns>
-        public async Task<List<StreamSpec>> ExtractStreamsAsync()
-        {
-            try
-            {
-                await semaphore.WaitAsync();
-                Logger.Info(ResString.parsingStream);
-                return await extractor.ExtractStreamsAsync(rawText);
-            }
-            finally
-            {
-                semaphore.Release();
-            }
-        }
-
-        /// <summary>
-        /// 根据规格说明填充媒体播放列表信息
-        /// </summary>
-        /// <param name="streamSpecs"></param>
-        public async Task FetchPlayListAsync(List<StreamSpec> streamSpecs)
-        {
-            try
-            {
-                await semaphore.WaitAsync();
-                Logger.Info(ResString.parsingStream);
-                await extractor.FetchPlayListAsync(streamSpecs);
-            }
-            finally
-            {
-                semaphore.Release();
-            }
-        }
-
-        public async Task RefreshPlayListAsync(List<StreamSpec> streamSpecs)
-        {
-            try
-            {
-                await semaphore.WaitAsync();
-                await RetryUtil.WebRequestRetryAsync(async () =>
-                {
-                    await extractor.RefreshPlayListAsync(streamSpecs);
-                    return true;
-                }, retryDelayMilliseconds: 1000, maxRetries: 5);
-            }
-            finally
-            {
-                semaphore.Release();
-            }
+            semaphore.Release();
         }
     }
-}
+
+    /// <summary>
+    /// 根据规格说明填充媒体播放列表信息
+    /// </summary>
+    /// <param name="streamSpecs"></param>
+    public async Task FetchPlayListAsync(List<StreamSpec> streamSpecs)
+    {
+        try
+        {
+            await semaphore.WaitAsync();
+            Logger.Info(ResString.parsingStream);
+            await extractor.FetchPlayListAsync(streamSpecs);
+        }
+        finally
+        {
+            semaphore.Release();
+        }
+    }
+
+    public async Task RefreshPlayListAsync(List<StreamSpec> streamSpecs)
+    {
+        try
+        {
+            await semaphore.WaitAsync();
+            await RetryUtil.WebRequestRetryAsync(async () =>
+            {
+                await extractor.RefreshPlayListAsync(streamSpecs);
+                return true;
+            }, retryDelayMilliseconds: 1000, maxRetries: 5);
+        }
+        finally
+        {
+            semaphore.Release();
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/N_m3u8DL-RE.Parser/Util/ParserUtil.cs b/src/N_m3u8DL-RE.Parser/Util/ParserUtil.cs
index 0215c77..4186e9b 100644
--- a/src/N_m3u8DL-RE.Parser/Util/ParserUtil.cs
+++ b/src/N_m3u8DL-RE.Parser/Util/ParserUtil.cs
@@ -1,126 +1,120 @@
 using N_m3u8DL_RE.Parser.Constants;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
 using System.Text.RegularExpressions;
-using System.Threading.Tasks;
 
-namespace N_m3u8DL_RE.Parser.Util
+namespace N_m3u8DL_RE.Parser.Util;
+
+public partial class ParserUtil
 {
-    public partial class ParserUtil
+    [GeneratedRegex("\\$Number%([^$]+)d\\$")]
+    private static partial Regex VarsNumberRegex();
+
+    /// <summary>
+    /// 从以下文本中获取参数
+    /// #EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=2149280,CODECS="mp4a.40.2,avc1.64001f",RESOLUTION=1280x720,NAME="720"
+    /// </summary>
+    /// <param name="line">等待被解析的一行文本</param>
+    /// <param name="key">留空则获取第一个英文冒号后的全部字符</param>
+    /// <returns></returns>
+    public static string GetAttribute(string line, string key = "")
     {
-        [GeneratedRegex("\\$Number%([^$]+)d\\$")]
-        private static partial Regex VarsNumberRegex();
+        line = line.Trim();
+        if (key == "")
+            return line[(line.IndexOf(':') + 1)..];
 
-        /// <summary>
-        /// 从以下文本中获取参数
-        /// #EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=2149280,CODECS="mp4a.40.2,avc1.64001f",RESOLUTION=1280x720,NAME="720"
-        /// </summary>
-        /// <param name="line">等待被解析的一行文本</param>
-        /// <param name="key">留空则获取第一个英文冒号后的全部字符</param>
-        /// <returns></returns>
-        public static string GetAttribute(string line, string key = "")
+        var index = -1;
+        var result = string.Empty;
+        if ((index = line.IndexOf(key + "=\"", StringComparison.Ordinal)) > -1)
         {
-            line = line.Trim();
-            if (key == "")
-                return line[(line.IndexOf(':') + 1)..];
-
-            var index = -1;
-            var result = string.Empty;
-            if ((index = line.IndexOf(key + "=\"")) > -1)
-            {
-                var startIndex = index + (key + "=\"").Length;
-                var endIndex = startIndex + line[startIndex..].IndexOf('\"');
-                result = line[startIndex..endIndex];
-            }
-            else if ((index = line.IndexOf(key + "=")) > -1)
-            {
-                var startIndex = index + (key + "=").Length;
-                var endIndex = startIndex + line[startIndex..].IndexOf(',');
-                if (endIndex >= startIndex) result = line[startIndex..endIndex];
-                else result = line[startIndex..];
-            }
-
-            return result;
+            var startIndex = index + (key + "=\"").Length;
+            var endIndex = startIndex + line[startIndex..].IndexOf('\"');
+            result = line[startIndex..endIndex];
+        }
+        else if ((index = line.IndexOf(key + "=", StringComparison.Ordinal)) > -1)
+        {
+            var startIndex = index + (key + "=").Length;
+            var endIndex = startIndex + line[startIndex..].IndexOf(',');
+            if (endIndex >= startIndex) result = line[startIndex..endIndex];
+            else result = line[startIndex..];
         }
 
-        /// <summary>
-        /// 从如下文本中提取
-        /// <n>[@<o>]
-        /// </summary>
-        /// <param name="input"></param>
-        /// <returns>n(length) o(start)</returns>
-        public static (long, long?) GetRange(string input)
-        {
-            var t = input.Split('@');
-            if (t.Length > 0)
-            {
-                if (t.Length == 1)
-                {
-                    return (Convert.ToInt64(t[0]), null);
-                }
-                if (t.Length == 2)
-                {
-                    return (Convert.ToInt64(t[0]), Convert.ToInt64(t[1]));
-                }
-            }
-            return (0, null);
-        }
-
-        /// <summary>
-        /// 从100-300这种字符串中获取StartRange, ExpectLength信息
-        /// </summary>
-        /// <param name="range"></param>
-        /// <returns>StartRange, ExpectLength</returns>
-        public static (long, long) ParseRange(string range)
-        {
-            var start = Convert.ToInt64(range.Split('-')[0]);
-            var end = Convert.ToInt64(range.Split('-')[1]);
-            return (start, end - start + 1);
-        }
-
-        /// <summary>
-        /// MPD SegmentTemplate替换
-        /// </summary>
-        /// <param name="text"></param>
-        /// <param name="keyValuePairs"></param>
-        /// <returns></returns>
-        public static string ReplaceVars(string text, Dictionary<string, object?> keyValuePairs)
-        {
-            foreach (var item in keyValuePairs)
-                if (text.Contains(item.Key))
-                    text = text.Replace(item.Key, item.Value!.ToString());
-
-            //处理特殊形式数字 如 $Number%05d$
-            var regex = VarsNumberRegex();
-            if (regex.IsMatch(text) && keyValuePairs.ContainsKey(DASHTags.TemplateNumber)) 
-            {
-                foreach (Match m in regex.Matches(text))
-                {
-                    text = text.Replace(m.Value, keyValuePairs[DASHTags.TemplateNumber]?.ToString()?.PadLeft(Convert.ToInt32(m.Groups[1].Value), '0'));
-                }
-            }
-
-            return text;
-        }
-
-        /// <summary>
-        /// 拼接Baseurl和RelativeUrl
-        /// </summary>
-        /// <param name="baseurl">Baseurl</param>
-        /// <param name="url">RelativeUrl</param>
-        /// <returns></returns>
-        public static string CombineURL(string baseurl, string url)
-        {
-            if (string.IsNullOrEmpty(baseurl))
-                return url;
-
-            Uri uri1 = new Uri(baseurl);  //这里直接传完整的URL即可
-            Uri uri2 = new Uri(uri1, url);
-            url = uri2.ToString();
-
-            return url;
-        }
+        return result;
     }
-}
+
+    /// <summary>
+    /// 从如下文本中提取
+    /// <n>[@<o>]
+    /// </summary>
+    /// <param name="input"></param>
+    /// <returns>n(length) o(start)</returns>
+    public static (long, long?) GetRange(string input)
+    {
+        var t = input.Split('@');
+        if (t.Length > 0)
+        {
+            if (t.Length == 1)
+            {
+                return (Convert.ToInt64(t[0]), null);
+            }
+            if (t.Length == 2)
+            {
+                return (Convert.ToInt64(t[0]), Convert.ToInt64(t[1]));
+            }
+        }
+        return (0, null);
+    }
+
+    /// <summary>
+    /// 从100-300这种字符串中获取StartRange, ExpectLength信息
+    /// </summary>
+    /// <param name="range"></param>
+    /// <returns>StartRange, ExpectLength</returns>
+    public static (long, long) ParseRange(string range)
+    {
+        var start = Convert.ToInt64(range.Split('-')[0]);
+        var end = Convert.ToInt64(range.Split('-')[1]);
+        return (start, end - start + 1);
+    }
+
+    /// <summary>
+    /// MPD SegmentTemplate替换
+    /// </summary>
+    /// <param name="text"></param>
+    /// <param name="keyValuePairs"></param>
+    /// <returns></returns>
+    public static string ReplaceVars(string text, Dictionary<string, object?> keyValuePairs)
+    {
+        foreach (var item in keyValuePairs)
+            if (text.Contains(item.Key))
+                text = text.Replace(item.Key, item.Value!.ToString());
+
+        // 处理特殊形式数字 如 $Number%05d$
+        var regex = VarsNumberRegex();
+        if (regex.IsMatch(text) && keyValuePairs.TryGetValue(DASHTags.TemplateNumber, out var keyValuePair)) 
+        {
+            foreach (Match m in regex.Matches(text))
+            {
+                text = text.Replace(m.Value, keyValuePair?.ToString()?.PadLeft(Convert.ToInt32(m.Groups[1].Value), '0'));
+            }
+        }
+
+        return text;
+    }
+
+    /// <summary>
+    /// 拼接Baseurl和RelativeUrl
+    /// </summary>
+    /// <param name="baseurl">Baseurl</param>
+    /// <param name="url">RelativeUrl</param>
+    /// <returns></returns>
+    public static string CombineURL(string baseurl, string url)
+    {
+        if (string.IsNullOrEmpty(baseurl))
+            return url;
+
+        Uri uri1 = new Uri(baseurl);  // 这里直接传完整的URL即可
+        Uri uri2 = new Uri(uri1, url);
+        url = uri2.ToString();
+
+        return url;
+    }
+}
\ No newline at end of file
diff --git a/src/N_m3u8DL-RE/Column/DownloadSpeedColumn.cs b/src/N_m3u8DL-RE/Column/DownloadSpeedColumn.cs
index cc0f522..565eef6 100644
--- a/src/N_m3u8DL-RE/Column/DownloadSpeedColumn.cs
+++ b/src/N_m3u8DL-RE/Column/DownloadSpeedColumn.cs
@@ -1,66 +1,48 @@
-using N_m3u8DL_RE.Common.Log;
-using N_m3u8DL_RE.Entity;
+using N_m3u8DL_RE.Entity;
 using Spectre.Console;
 using Spectre.Console.Rendering;
-using System;
 using System.Collections.Concurrent;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
+using N_m3u8DL_RE.Common.Util;
 
-namespace N_m3u8DL_RE.Column
+namespace N_m3u8DL_RE.Column;
+
+internal sealed class DownloadSpeedColumn : ProgressColumn
 {
-    internal sealed class DownloadSpeedColumn : ProgressColumn
+    private long _stopSpeed = 0;
+    private ConcurrentDictionary<int, string> DateTimeStringDic = new();
+    protected override bool NoWrap => true;
+    private ConcurrentDictionary<int, SpeedContainer> SpeedContainerDic { get; set; }
+
+    public DownloadSpeedColumn(ConcurrentDictionary<int, SpeedContainer> SpeedContainerDic)
     {
-        private long _stopSpeed = 0;
-        private ConcurrentDictionary<int, string> DateTimeStringDic = new();
-        protected override bool NoWrap => true;
-        private ConcurrentDictionary<int, SpeedContainer> SpeedContainerDic { get; set; }
-
-        public DownloadSpeedColumn(ConcurrentDictionary<int, SpeedContainer> SpeedContainerDic)
-        {
-            this.SpeedContainerDic = SpeedContainerDic;
-        }
-
-        public Style MyStyle { get; set; } = new Style(foreground: Color.Green);
-
-        public override IRenderable Render(RenderOptions options, ProgressTask task, TimeSpan deltaTime)
-        {
-            var taskId = task.Id;
-            var speedContainer = SpeedContainerDic[taskId];
-            var now = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
-            var flag = task.IsFinished || !task.IsStarted;
-            //单文件下载汇报进度
-            if (!flag && speedContainer.SingleSegment && speedContainer.ResponseLength != null)
-            {
-                task.MaxValue = (double)speedContainer.ResponseLength;
-                task.Value = speedContainer.RDownloaded;
-            }
-            //一秒汇报一次即可
-            if (DateTimeStringDic.TryGetValue(taskId, out var oldTime) && oldTime != now && !flag)
-            {
-                speedContainer.NowSpeed = speedContainer.Downloaded;
-                //速度为0,计数增加
-                if (speedContainer.Downloaded <= _stopSpeed) { speedContainer.AddLowSpeedCount(); }
-                else speedContainer.ResetLowSpeedCount();
-                speedContainer.Reset();
-            }
-            DateTimeStringDic[taskId] = now;
-            var style = flag ? Style.Plain : MyStyle;
-            return flag ? new Text("-", style).Centered() : new Text(FormatFileSize(speedContainer.NowSpeed) + (speedContainer.LowSpeedCount > 0 ? $"({speedContainer.LowSpeedCount})" : ""), style).Centered();
-        }
-
-        private static string FormatFileSize(double fileSize)
-        {
-            return fileSize switch
-            {
-                < 0 => throw new ArgumentOutOfRangeException(nameof(fileSize)),
-                >= 1024 * 1024 * 1024 => string.Format("{0:########0.00}GBps", (double)fileSize / (1024 * 1024 * 1024)),
-                >= 1024 * 1024 => string.Format("{0:####0.00}MBps", (double)fileSize / (1024 * 1024)),
-                >= 1024 => string.Format("{0:####0.00}KBps", (double)fileSize / 1024),
-                _ => string.Format("{0:####0.00}Bps", fileSize)
-            };
-        }
+        this.SpeedContainerDic = SpeedContainerDic;
     }
-}
+
+    public Style MyStyle { get; set; } = new Style(foreground: Color.Green);
+
+    public override IRenderable Render(RenderOptions options, ProgressTask task, TimeSpan deltaTime)
+    {
+        var taskId = task.Id;
+        var speedContainer = SpeedContainerDic[taskId];
+        var now = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
+        var flag = task.IsFinished || !task.IsStarted;
+        // 单文件下载汇报进度
+        if (!flag && speedContainer.SingleSegment && speedContainer.ResponseLength != null)
+        {
+            task.MaxValue = (double)speedContainer.ResponseLength;
+            task.Value = speedContainer.RDownloaded;
+        }
+        // 一秒汇报一次即可
+        if (DateTimeStringDic.TryGetValue(taskId, out var oldTime) && oldTime != now && !flag)
+        {
+            speedContainer.NowSpeed = speedContainer.Downloaded;
+            // 速度为0,计数增加
+            if (speedContainer.Downloaded <= _stopSpeed) { speedContainer.AddLowSpeedCount(); }
+            else speedContainer.ResetLowSpeedCount();
+            speedContainer.Reset();
+        }
+        DateTimeStringDic[taskId] = now;
+        var style = flag ? Style.Plain : MyStyle;
+        return flag ? new Text("-", style).Centered() : new Text(GlobalUtil.FormatFileSize(speedContainer.NowSpeed) + (speedContainer.LowSpeedCount > 0 ? $"({speedContainer.LowSpeedCount})" : ""), style).Centered();
+    }
+}
\ No newline at end of file
diff --git a/src/N_m3u8DL-RE/Column/DownloadStatusColumn.cs b/src/N_m3u8DL-RE/Column/DownloadStatusColumn.cs
index 60edcc4..cbedb9d 100644
--- a/src/N_m3u8DL-RE/Column/DownloadStatusColumn.cs
+++ b/src/N_m3u8DL-RE/Column/DownloadStatusColumn.cs
@@ -2,48 +2,42 @@
 using N_m3u8DL_RE.Entity;
 using Spectre.Console;
 using Spectre.Console.Rendering;
-using System;
 using System.Collections.Concurrent;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
 
-namespace N_m3u8DL_RE.Column
+namespace N_m3u8DL_RE.Column;
+
+internal class DownloadStatusColumn : ProgressColumn
 {
-    internal class DownloadStatusColumn : ProgressColumn
+    private ConcurrentDictionary<int, SpeedContainer> SpeedContainerDic { get; set; }
+    private ConcurrentDictionary<int, string> DateTimeStringDic = new();
+    private ConcurrentDictionary<int, string> SizeDic = new();
+    public Style MyStyle { get; set; } = new Style(foreground: Color.DarkCyan);
+    public Style FinishedStyle { get; set; } = new Style(foreground: Color.Green);
+
+    public DownloadStatusColumn(ConcurrentDictionary<int, SpeedContainer> speedContainerDic)
     {
-        private ConcurrentDictionary<int, SpeedContainer> SpeedContainerDic { get; set; }
-        private ConcurrentDictionary<int, string> DateTimeStringDic = new();
-        private ConcurrentDictionary<int, string> SizeDic = new();
-        public Style MyStyle { get; set; } = new Style(foreground: Color.DarkCyan);
-        public Style FinishedStyle { get; set; } = new Style(foreground: Color.Green);
-
-        public DownloadStatusColumn(ConcurrentDictionary<int, SpeedContainer> speedContainerDic)
-        {
-            this.SpeedContainerDic = speedContainerDic;
-        }
-
-        public override IRenderable Render(RenderOptions options, ProgressTask task, TimeSpan deltaTime)
-        {
-            if (task.Value == 0) return new Text("-", MyStyle).RightJustified();
-            var now = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
-
-            var speedContainer = SpeedContainerDic[task.Id];
-            var size = speedContainer.RDownloaded;
-
-            //一秒汇报一次即可
-            if (DateTimeStringDic.TryGetValue(task.Id, out var oldTime) && oldTime != now)
-            {
-                var totalSize = speedContainer.SingleSegment ? (speedContainer.ResponseLength ?? 0) : (long)(size / (task.Value / task.MaxValue));
-                SizeDic[task.Id] = $"{GlobalUtil.FormatFileSize(size)}/{GlobalUtil.FormatFileSize(totalSize)}";
-            }
-            DateTimeStringDic[task.Id] = now;
-            SizeDic.TryGetValue(task.Id, out var sizeStr);
-
-            if (task.IsFinished) sizeStr = GlobalUtil.FormatFileSize(size);
-
-            return new Text(sizeStr ?? "-", MyStyle).RightJustified();
-        }
+        this.SpeedContainerDic = speedContainerDic;
     }
-}
+
+    public override IRenderable Render(RenderOptions options, ProgressTask task, TimeSpan deltaTime)
+    {
+        if (task.Value == 0) return new Text("-", MyStyle).RightJustified();
+        var now = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
+
+        var speedContainer = SpeedContainerDic[task.Id];
+        var size = speedContainer.RDownloaded;
+
+        // 一秒汇报一次即可
+        if (DateTimeStringDic.TryGetValue(task.Id, out var oldTime) && oldTime != now)
+        {
+            var totalSize = speedContainer.SingleSegment ? (speedContainer.ResponseLength ?? 0) : (long)(size / (task.Value / task.MaxValue));
+            SizeDic[task.Id] = $"{GlobalUtil.FormatFileSize(size)}/{GlobalUtil.FormatFileSize(totalSize)}";
+        }
+        DateTimeStringDic[task.Id] = now;
+        SizeDic.TryGetValue(task.Id, out var sizeStr);
+
+        if (task.IsFinished) sizeStr = GlobalUtil.FormatFileSize(size);
+
+        return new Text(sizeStr ?? "-", MyStyle).RightJustified();
+    }
+}
\ No newline at end of file
diff --git a/src/N_m3u8DL-RE/Column/MyPercentageColumn.cs b/src/N_m3u8DL-RE/Column/MyPercentageColumn.cs
index 849821b..2c74aae 100644
--- a/src/N_m3u8DL-RE/Column/MyPercentageColumn.cs
+++ b/src/N_m3u8DL-RE/Column/MyPercentageColumn.cs
@@ -1,31 +1,25 @@
 using Spectre.Console.Rendering;
 using Spectre.Console;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
 
-namespace N_m3u8DL_RE.Column
+namespace N_m3u8DL_RE.Column;
+
+internal class MyPercentageColumn : ProgressColumn
 {
-    internal class MyPercentageColumn : ProgressColumn
+    /// <summary>
+    /// Gets or sets the style for a non-complete task.
+    /// </summary>
+    public Style Style { get; set; } = Style.Plain;
+
+    /// <summary>
+    /// Gets or sets the style for a completed task.
+    /// </summary>
+    public Style CompletedStyle { get; set; } = new Style(foreground: Color.Green);
+
+    /// <inheritdoc/>
+    public override IRenderable Render(RenderOptions options, ProgressTask task, TimeSpan deltaTime)
     {
-        /// <summary>
-        /// Gets or sets the style for a non-complete task.
-        /// </summary>
-        public Style Style { get; set; } = Style.Plain;
-
-        /// <summary>
-        /// Gets or sets the style for a completed task.
-        /// </summary>
-        public Style CompletedStyle { get; set; } = new Style(foreground: Color.Green);
-
-        /// <inheritdoc/>
-        public override IRenderable Render(RenderOptions options, ProgressTask task, TimeSpan deltaTime)
-        {
-            var percentage = task.Percentage;
-            var style = percentage == 100 ? CompletedStyle : Style ?? Style.Plain;
-            return new Text($"{task.Value}/{task.MaxValue} {percentage:F2}%", style).RightJustified();
-        }
+        var percentage = task.Percentage;
+        var style = percentage == 100 ? CompletedStyle : Style ?? Style.Plain;
+        return new Text($"{task.Value}/{task.MaxValue} {percentage:F2}%", style).RightJustified();
     }
-}
+}
\ No newline at end of file
diff --git a/src/N_m3u8DL-RE/Column/RecordingDurationColumn.cs b/src/N_m3u8DL-RE/Column/RecordingDurationColumn.cs
index 461e834..4bb3190 100644
--- a/src/N_m3u8DL-RE/Column/RecordingDurationColumn.cs
+++ b/src/N_m3u8DL-RE/Column/RecordingDurationColumn.cs
@@ -1,39 +1,33 @@
 using N_m3u8DL_RE.Common.Util;
 using Spectre.Console;
 using Spectre.Console.Rendering;
-using System;
 using System.Collections.Concurrent;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
 
-namespace N_m3u8DL_RE.Column
+namespace N_m3u8DL_RE.Column;
+
+internal class RecordingDurationColumn : ProgressColumn
 {
-    internal class RecordingDurationColumn : ProgressColumn
+    protected override bool NoWrap => true;
+    private ConcurrentDictionary<int, int> _recodingDurDic;
+    private ConcurrentDictionary<int, int>? _refreshedDurDic;
+    public Style GreyStyle { get; set; } = new Style(foreground: Color.Grey);
+    public Style MyStyle { get; set; } = new Style(foreground: Color.DarkGreen);
+    public RecordingDurationColumn(ConcurrentDictionary<int, int> recodingDurDic)
     {
-        protected override bool NoWrap => true;
-        private ConcurrentDictionary<int, int> _recodingDurDic;
-        private ConcurrentDictionary<int, int>? _refreshedDurDic;
-        public Style GreyStyle { get; set; } = new Style(foreground: Color.Grey);
-        public Style MyStyle { get; set; } = new Style(foreground: Color.DarkGreen);
-        public RecordingDurationColumn(ConcurrentDictionary<int, int> recodingDurDic)
-        {
-            _recodingDurDic = recodingDurDic;
-        }
-        public RecordingDurationColumn(ConcurrentDictionary<int, int> recodingDurDic, ConcurrentDictionary<int, int> refreshedDurDic)
-        {
-            _recodingDurDic = recodingDurDic;
-            _refreshedDurDic = refreshedDurDic;
-        }
-        public override IRenderable Render(RenderOptions options, ProgressTask task, TimeSpan deltaTime)
-        {
-            if (_refreshedDurDic == null)
-                return new Text($"{GlobalUtil.FormatTime(_recodingDurDic[task.Id])}", MyStyle).LeftJustified();
-            else
-            {                
-                return new Text($"{GlobalUtil.FormatTime(_recodingDurDic[task.Id])}/{GlobalUtil.FormatTime(_refreshedDurDic[task.Id])}", GreyStyle);
-            }
+        _recodingDurDic = recodingDurDic;
+    }
+    public RecordingDurationColumn(ConcurrentDictionary<int, int> recodingDurDic, ConcurrentDictionary<int, int> refreshedDurDic)
+    {
+        _recodingDurDic = recodingDurDic;
+        _refreshedDurDic = refreshedDurDic;
+    }
+    public override IRenderable Render(RenderOptions options, ProgressTask task, TimeSpan deltaTime)
+    {
+        if (_refreshedDurDic == null)
+            return new Text($"{GlobalUtil.FormatTime(_recodingDurDic[task.Id])}", MyStyle).LeftJustified();
+        else
+        {                
+            return new Text($"{GlobalUtil.FormatTime(_recodingDurDic[task.Id])}/{GlobalUtil.FormatTime(_refreshedDurDic[task.Id])}", GreyStyle);
         }
     }
-}
+}
\ No newline at end of file
diff --git a/src/N_m3u8DL-RE/Column/RecordingSizeColumn.cs b/src/N_m3u8DL-RE/Column/RecordingSizeColumn.cs
index 21b76f4..9d56934 100644
--- a/src/N_m3u8DL-RE/Column/RecordingSizeColumn.cs
+++ b/src/N_m3u8DL-RE/Column/RecordingSizeColumn.cs
@@ -1,39 +1,32 @@
 using N_m3u8DL_RE.Common.Util;
-using N_m3u8DL_RE.Entity;
 using Spectre.Console;
 using Spectre.Console.Rendering;
-using System;
 using System.Collections.Concurrent;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
 
-namespace N_m3u8DL_RE.Column
+namespace N_m3u8DL_RE.Column;
+
+internal class RecordingSizeColumn : ProgressColumn
 {
-    internal class RecordingSizeColumn : ProgressColumn
+    protected override bool NoWrap => true;
+    private ConcurrentDictionary<int, double> RecodingSizeDic = new(); // 临时的大小 每秒刷新用
+    private ConcurrentDictionary<int, double> _recodingSizeDic;
+    private ConcurrentDictionary<int, string> DateTimeStringDic = new();
+    public Style MyStyle { get; set; } = new Style(foreground: Color.DarkCyan);
+    public RecordingSizeColumn(ConcurrentDictionary<int, double> recodingSizeDic)
     {
-        protected override bool NoWrap => true;
-        private ConcurrentDictionary<int, double> RecodingSizeDic = new(); //临时的大小 每秒刷新用
-        private ConcurrentDictionary<int, double> _recodingSizeDic;
-        private ConcurrentDictionary<int, string> DateTimeStringDic = new();
-        public Style MyStyle { get; set; } = new Style(foreground: Color.DarkCyan);
-        public RecordingSizeColumn(ConcurrentDictionary<int, double> recodingSizeDic)
-        {
-            _recodingSizeDic = recodingSizeDic;
-        }
-        public override IRenderable Render(RenderOptions options, ProgressTask task, TimeSpan deltaTime)
-        {
-            var now = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
-            var taskId = task.Id;
-            //一秒汇报一次即可
-            if (DateTimeStringDic.TryGetValue(taskId, out var oldTime) && oldTime != now)
-            {
-                RecodingSizeDic[task.Id] = _recodingSizeDic[task.Id];
-            }
-            DateTimeStringDic[taskId] = now;
-            var flag = RecodingSizeDic.TryGetValue(taskId, out var size);
-            return new Text(GlobalUtil.FormatFileSize(flag ? size : 0), MyStyle).LeftJustified();
-        }
+        _recodingSizeDic = recodingSizeDic;
     }
-}
+    public override IRenderable Render(RenderOptions options, ProgressTask task, TimeSpan deltaTime)
+    {
+        var now = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
+        var taskId = task.Id;
+        // 一秒汇报一次即可
+        if (DateTimeStringDic.TryGetValue(taskId, out var oldTime) && oldTime != now)
+        {
+            RecodingSizeDic[task.Id] = _recodingSizeDic[task.Id];
+        }
+        DateTimeStringDic[taskId] = now;
+        var flag = RecodingSizeDic.TryGetValue(taskId, out var size);
+        return new Text(GlobalUtil.FormatFileSize(flag ? size : 0), MyStyle).LeftJustified();
+    }
+}
\ No newline at end of file
diff --git a/src/N_m3u8DL-RE/Column/RecordingStatusColumn.cs b/src/N_m3u8DL-RE/Column/RecordingStatusColumn.cs
index aa0a057..2925d22 100644
--- a/src/N_m3u8DL-RE/Column/RecordingStatusColumn.cs
+++ b/src/N_m3u8DL-RE/Column/RecordingStatusColumn.cs
@@ -1,23 +1,17 @@
 using Spectre.Console;
 using Spectre.Console.Rendering;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
 
-namespace N_m3u8DL_RE.Column
+namespace N_m3u8DL_RE.Column;
+
+internal class RecordingStatusColumn : ProgressColumn
 {
-    internal class RecordingStatusColumn : ProgressColumn
+    protected override bool NoWrap => true;
+    public Style MyStyle { get; set; } = new Style(foreground: Color.Default);
+    public Style FinishedStyle { get; set; } = new Style(foreground: Color.Yellow);
+    public override IRenderable Render(RenderOptions options, ProgressTask task, TimeSpan deltaTime)
     {
-        protected override bool NoWrap => true;
-        public Style MyStyle { get; set; } = new Style(foreground: Color.Default);
-        public Style FinishedStyle { get; set; } = new Style(foreground: Color.Yellow);
-        public override IRenderable Render(RenderOptions options, ProgressTask task, TimeSpan deltaTime)
-        {
-            if (task.IsFinished)
-                return new Text($"{task.Value}/{task.MaxValue} Waiting  ", FinishedStyle).LeftJustified();
-            return new Text($"{task.Value}/{task.MaxValue} Recording", MyStyle).LeftJustified();
-        }
+        if (task.IsFinished)
+            return new Text($"{task.Value}/{task.MaxValue} Waiting  ", FinishedStyle).LeftJustified();
+        return new Text($"{task.Value}/{task.MaxValue} Recording", MyStyle).LeftJustified();
     }
-}
+}
\ No newline at end of file
diff --git a/src/N_m3u8DL-RE/CommandLine/CommandInvoker.cs b/src/N_m3u8DL-RE/CommandLine/CommandInvoker.cs
index fbb78b8..23915ef 100644
--- a/src/N_m3u8DL-RE/CommandLine/CommandInvoker.cs
+++ b/src/N_m3u8DL-RE/CommandLine/CommandInvoker.cs
@@ -5,7 +5,6 @@ using N_m3u8DL_RE.Common.Util;
 using N_m3u8DL_RE.Entity;
 using N_m3u8DL_RE.Enum;
 using N_m3u8DL_RE.Util;
-using NiL.JS.Expressions;
 using System.CommandLine;
 using System.CommandLine.Binding;
 using System.CommandLine.Builder;
@@ -14,630 +13,629 @@ using System.Globalization;
 using System.Net;
 using System.Text.RegularExpressions;
 
-namespace N_m3u8DL_RE.CommandLine
+namespace N_m3u8DL_RE.CommandLine;
+
+internal partial class CommandInvoker
 {
-    internal partial class CommandInvoker
+    public const string VERSION_INFO = "N_m3u8DL-RE (Beta version) 20241110";
+
+    [GeneratedRegex("((best|worst)\\d*|all)")]
+    private static partial Regex ForStrRegex();
+    [GeneratedRegex("(\\d*)-(\\d*)")]
+    private static partial Regex RangeRegex();
+
+    private static readonly Argument<string> Input = new(name: "input", description: ResString.cmd_Input);
+    private static readonly Option<string?> TmpDir = new(["--tmp-dir"], description: ResString.cmd_tmpDir);
+    private static readonly Option<string?> SaveDir = new(["--save-dir"], description: ResString.cmd_saveDir);
+    private static readonly Option<string?> SaveName = new(["--save-name"], description: ResString.cmd_saveName, parseArgument: ParseSaveName);
+    private static readonly Option<string?> SavePattern = new(["--save-pattern"], description: ResString.cmd_savePattern, getDefaultValue: () => "<SaveName>_<Id>_<Codecs>_<Language>_<Ext>");
+    private static readonly Option<string?> UILanguage = new Option<string?>(["--ui-language"], description: ResString.cmd_uiLanguage).FromAmong("en-US", "zh-CN", "zh-TW");
+    private static readonly Option<string?> UrlProcessorArgs = new(["--urlprocessor-args"], description: ResString.cmd_urlProcessorArgs);
+    private static readonly Option<string[]?> Keys = new(["--key"], description: ResString.cmd_keys) { Arity = ArgumentArity.OneOrMore, AllowMultipleArgumentsPerToken = false };
+    private static readonly Option<string> KeyTextFile = new(["--key-text-file"], description: ResString.cmd_keyText);
+    private static readonly Option<Dictionary<string, string>> Headers = new(["-H", "--header"], description: ResString.cmd_header, parseArgument: ParseHeaders) { Arity = ArgumentArity.OneOrMore, AllowMultipleArgumentsPerToken = false };
+    private static readonly Option<LogLevel> LogLevel = new(name: "--log-level", description: ResString.cmd_logLevel, getDefaultValue: () => Common.Log.LogLevel.INFO);
+    private static readonly Option<SubtitleFormat> SubtitleFormat = new(name: "--sub-format", description: ResString.cmd_subFormat, getDefaultValue: () => Enum.SubtitleFormat.SRT);
+    private static readonly Option<bool> AutoSelect = new(["--auto-select"], description: ResString.cmd_autoSelect, getDefaultValue: () => false);
+    private static readonly Option<bool> SubOnly = new(["--sub-only"], description: ResString.cmd_subOnly, getDefaultValue: () => false);
+    private static readonly Option<int> ThreadCount = new(["--thread-count"], description: ResString.cmd_threadCount, getDefaultValue: () => Environment.ProcessorCount) { ArgumentHelpName = "number" };
+    private static readonly Option<int> DownloadRetryCount = new(["--download-retry-count"], description: ResString.cmd_downloadRetryCount, getDefaultValue: () => 3) { ArgumentHelpName = "number" };
+    private static readonly Option<bool> SkipMerge = new(["--skip-merge"], description: ResString.cmd_skipMerge, getDefaultValue: () => false);
+    private static readonly Option<bool> SkipDownload = new(["--skip-download"], description: ResString.cmd_skipDownload, getDefaultValue: () => false);
+    private static readonly Option<bool> NoDateInfo = new(["--no-date-info"], description: ResString.cmd_noDateInfo, getDefaultValue: () => false);
+    private static readonly Option<bool> BinaryMerge = new(["--binary-merge"], description: ResString.cmd_binaryMerge, getDefaultValue: () => false);
+    private static readonly Option<bool> UseFFmpegConcatDemuxer = new(["--use-ffmpeg-concat-demuxer"], description: ResString.cmd_useFFmpegConcatDemuxer, getDefaultValue: () => false);
+    private static readonly Option<bool> DelAfterDone = new(["--del-after-done"], description: ResString.cmd_delAfterDone, getDefaultValue: () => true);
+    private static readonly Option<bool> AutoSubtitleFix = new(["--auto-subtitle-fix"], description: ResString.cmd_subtitleFix, getDefaultValue: () => true);
+    private static readonly Option<bool> CheckSegmentsCount = new(["--check-segments-count"], description: ResString.cmd_checkSegmentsCount, getDefaultValue: () => true);
+    private static readonly Option<bool> WriteMetaJson = new(["--write-meta-json"], description: ResString.cmd_writeMetaJson, getDefaultValue: () => true);
+    private static readonly Option<bool> AppendUrlParams = new(["--append-url-params"], description: ResString.cmd_appendUrlParams, getDefaultValue: () => false);
+    private static readonly Option<bool> MP4RealTimeDecryption = new (["--mp4-real-time-decryption"], description: ResString.cmd_MP4RealTimeDecryption, getDefaultValue: () => false);
+    private static readonly Option<bool> UseShakaPackager = new (["--use-shaka-packager"], description: ResString.cmd_useShakaPackager, getDefaultValue: () => false);
+    private static readonly Option<bool> ForceAnsiConsole = new(["--force-ansi-console"], description: ResString.cmd_forceAnsiConsole);
+    private static readonly Option<bool> NoAnsiColor = new(["--no-ansi-color"], description: ResString.cmd_noAnsiColor);
+    private static readonly Option<string?> DecryptionBinaryPath = new(["--decryption-binary-path"], description: ResString.cmd_decryptionBinaryPath) { ArgumentHelpName = "PATH" };
+    private static readonly Option<string?> FFmpegBinaryPath = new(["--ffmpeg-binary-path"], description: ResString.cmd_ffmpegBinaryPath) { ArgumentHelpName = "PATH" };
+    private static readonly Option<string?> BaseUrl = new(["--base-url"], description: ResString.cmd_baseUrl);
+    private static readonly Option<bool> ConcurrentDownload = new(["-mt", "--concurrent-download"], description: ResString.cmd_concurrentDownload, getDefaultValue: () => false);
+    private static readonly Option<bool> NoLog = new(["--no-log"], description: ResString.cmd_noLog, getDefaultValue: () => false);
+    private static readonly Option<string[]?> AdKeywords = new(["--ad-keyword"], description: ResString.cmd_adKeyword) { ArgumentHelpName = "REG" };
+    private static readonly Option<long?> MaxSpeed = new(["-R", "--max-speed"], description: ResString.cmd_maxSpeed, parseArgument: ParseSpeedLimit) { ArgumentHelpName = "SPEED" };
+
+
+    // 代理选项
+    private static readonly Option<bool> UseSystemProxy = new(["--use-system-proxy"], description: ResString.cmd_useSystemProxy, getDefaultValue: () => true);
+    private static readonly Option<WebProxy?> CustomProxy = new(["--custom-proxy"], description: ResString.cmd_customProxy, parseArgument: ParseProxy) { ArgumentHelpName = "URL" };
+
+    // 只下载部分分片
+    private static readonly Option<CustomRange?> CustomRange = new(["--custom-range"], description: ResString.cmd_customRange, parseArgument: ParseCustomRange) { ArgumentHelpName = "RANGE" };
+
+
+    // morehelp
+    private static readonly Option<string?> MoreHelp = new(["--morehelp"], description: ResString.cmd_moreHelp) { ArgumentHelpName = "OPTION" };
+
+    // 自定义KEY等
+    private static readonly Option<EncryptMethod?> CustomHLSMethod = new(name: "--custom-hls-method", description: ResString.cmd_customHLSMethod) { ArgumentHelpName = "METHOD" };
+    private static readonly Option<byte[]?> CustomHLSKey = new(name: "--custom-hls-key", description: ResString.cmd_customHLSKey, parseArgument: ParseHLSCustomKey) { ArgumentHelpName = "FILE|HEX|BASE64" };
+    private static readonly Option<byte[]?> CustomHLSIv = new(name: "--custom-hls-iv", description: ResString.cmd_customHLSIv, parseArgument: ParseHLSCustomKey) { ArgumentHelpName = "FILE|HEX|BASE64" };
+
+    // 任务开始时间
+    private static readonly Option<DateTime?> TaskStartAt = new(["--task-start-at"], description: ResString.cmd_taskStartAt, parseArgument: ParseStartTime) { ArgumentHelpName = "yyyyMMddHHmmss" };
+
+
+    // 直播相关
+    private static readonly Option<bool> LivePerformAsVod = new(["--live-perform-as-vod"], description: ResString.cmd_livePerformAsVod, getDefaultValue: () => false);
+    private static readonly Option<bool> LiveRealTimeMerge = new(["--live-real-time-merge"], description: ResString.cmd_liveRealTimeMerge, getDefaultValue: () => false);
+    private static readonly Option<bool> LiveKeepSegments = new(["--live-keep-segments"], description: ResString.cmd_liveKeepSegments, getDefaultValue: () => true);
+    private static readonly Option<bool> LivePipeMux = new(["--live-pipe-mux"], description: ResString.cmd_livePipeMux, getDefaultValue: () => false);
+    private static readonly Option<TimeSpan?> LiveRecordLimit = new(["--live-record-limit"], description: ResString.cmd_liveRecordLimit, parseArgument: ParseLiveLimit) { ArgumentHelpName = "HH:mm:ss" };
+    private static readonly Option<int?> LiveWaitTime = new(["--live-wait-time"], description: ResString.cmd_liveWaitTime) { ArgumentHelpName = "SEC" };
+    private static readonly Option<int> LiveTakeCount = new(["--live-take-count"], description: ResString.cmd_liveTakeCount, getDefaultValue: () => 16) { ArgumentHelpName = "NUM" };
+    private static readonly Option<bool> LiveFixVttByAudio = new(["--live-fix-vtt-by-audio"], description: ResString.cmd_liveFixVttByAudio, getDefaultValue: () => false);
+
+
+    // 复杂命令行如下
+    private static readonly Option<MuxOptions?> MuxAfterDone = new(["-M", "--mux-after-done"], description: ResString.cmd_muxAfterDone, parseArgument: ParseMuxAfterDone) { ArgumentHelpName = "OPTIONS" };
+    private static readonly Option<List<OutputFile>> MuxImports = new("--mux-import", description: ResString.cmd_muxImport, parseArgument: ParseImports) { Arity = ArgumentArity.OneOrMore, AllowMultipleArgumentsPerToken = false, ArgumentHelpName = "OPTIONS" };
+    private static readonly Option<StreamFilter?> VideoFilter = new(["-sv", "--select-video"], description: ResString.cmd_selectVideo, parseArgument: ParseStreamFilter) { ArgumentHelpName = "OPTIONS" };
+    private static readonly Option<StreamFilter?> AudioFilter = new(["-sa", "--select-audio"], description: ResString.cmd_selectAudio, parseArgument: ParseStreamFilter) { ArgumentHelpName = "OPTIONS" };
+    private static readonly Option<StreamFilter?> SubtitleFilter = new(["-ss", "--select-subtitle"], description: ResString.cmd_selectSubtitle, parseArgument: ParseStreamFilter) { ArgumentHelpName = "OPTIONS" };
+
+    private static readonly Option<StreamFilter?> DropVideoFilter = new(["-dv", "--drop-video"], description: ResString.cmd_dropVideo, parseArgument: ParseStreamFilter) { ArgumentHelpName = "OPTIONS" };
+    private static readonly Option<StreamFilter?> DropAudioFilter = new(["-da", "--drop-audio"], description: ResString.cmd_dropAudio, parseArgument: ParseStreamFilter) { ArgumentHelpName = "OPTIONS" };
+    private static readonly Option<StreamFilter?> DropSubtitleFilter = new(["-ds", "--drop-subtitle"], description: ResString.cmd_dropSubtitle, parseArgument: ParseStreamFilter) { ArgumentHelpName = "OPTIONS" };
+
+    /// <summary>
+    /// 解析录制直播时长限制
+    /// </summary>
+    /// <param name="result"></param>
+    /// <returns></returns>
+    private static long? ParseSpeedLimit(ArgumentResult result)
     {
-        public const string VERSION_INFO = "N_m3u8DL-RE (Beta version) 20241020";
-
-        [GeneratedRegex("((best|worst)\\d*|all)")]
-        private static partial Regex ForStrRegex();
-        [GeneratedRegex("(\\d*)-(\\d*)")]
-        private static partial Regex RangeRegex();
-
-        private readonly static Argument<string> Input = new(name: "input", description: ResString.cmd_Input);
-        private readonly static Option<string?> TmpDir = new(new string[] { "--tmp-dir" }, description: ResString.cmd_tmpDir);
-        private readonly static Option<string?> SaveDir = new(new string[] { "--save-dir" }, description: ResString.cmd_saveDir);
-        private readonly static Option<string?> SaveName = new(new string[] { "--save-name" }, description: ResString.cmd_saveName, parseArgument: ParseSaveName);
-        private readonly static Option<string?> SavePattern = new(new string[] { "--save-pattern" }, description: ResString.cmd_savePattern, getDefaultValue: () => "<SaveName>_<Id>_<Codecs>_<Language>_<Ext>");
-        private readonly static Option<string?> UILanguage = new Option<string?>(new string[] { "--ui-language" }, description: ResString.cmd_uiLanguage).FromAmong("en-US", "zh-CN", "zh-TW");
-        private readonly static Option<string?> UrlProcessorArgs = new(new string[] { "--urlprocessor-args" }, description: ResString.cmd_urlProcessorArgs);
-        private readonly static Option<string[]?> Keys = new(new string[] { "--key" }, description: ResString.cmd_keys) { Arity = ArgumentArity.OneOrMore, AllowMultipleArgumentsPerToken = false };
-        private readonly static Option<string> KeyTextFile = new(new string[] { "--key-text-file" }, description: ResString.cmd_keyText);
-        private readonly static Option<Dictionary<string, string>> Headers = new(new string[] { "-H", "--header" }, description: ResString.cmd_header, parseArgument: ParseHeaders) { Arity = ArgumentArity.OneOrMore, AllowMultipleArgumentsPerToken = false };
-        private readonly static Option<LogLevel> LogLevel = new(name: "--log-level", description: ResString.cmd_logLevel, getDefaultValue: () => Common.Log.LogLevel.INFO);
-        private readonly static Option<SubtitleFormat> SubtitleFormat = new(name: "--sub-format", description: ResString.cmd_subFormat, getDefaultValue: () => Enum.SubtitleFormat.SRT);
-        private readonly static Option<bool> AutoSelect = new(new string[] { "--auto-select" }, description: ResString.cmd_autoSelect, getDefaultValue: () => false);
-        private readonly static Option<bool> SubOnly = new(new string[] { "--sub-only" }, description: ResString.cmd_subOnly, getDefaultValue: () => false);
-        private readonly static Option<int> ThreadCount = new(new string[] { "--thread-count" }, description: ResString.cmd_threadCount, getDefaultValue: () => Environment.ProcessorCount) { ArgumentHelpName = "number" };
-        private readonly static Option<int> DownloadRetryCount = new(new string[] { "--download-retry-count" }, description: ResString.cmd_downloadRetryCount, getDefaultValue: () => 3) { ArgumentHelpName = "number" };
-        private readonly static Option<bool> SkipMerge = new(new string[] { "--skip-merge" }, description: ResString.cmd_skipMerge, getDefaultValue: () => false);
-        private readonly static Option<bool> SkipDownload = new(new string[] { "--skip-download" }, description: ResString.cmd_skipDownload, getDefaultValue: () => false);
-        private readonly static Option<bool> NoDateInfo = new(new string[] { "--no-date-info" }, description: ResString.cmd_noDateInfo, getDefaultValue: () => false);
-        private readonly static Option<bool> BinaryMerge = new(new string[] { "--binary-merge" }, description: ResString.cmd_binaryMerge, getDefaultValue: () => false);
-        private readonly static Option<bool> UseFFmpegConcatDemuxer = new(new string[] { "--use-ffmpeg-concat-demuxer" }, description: ResString.cmd_useFFmpegConcatDemuxer, getDefaultValue: () => false);
-        private readonly static Option<bool> DelAfterDone = new(new string[] { "--del-after-done" }, description: ResString.cmd_delAfterDone, getDefaultValue: () => true);
-        private readonly static Option<bool> AutoSubtitleFix = new(new string[] { "--auto-subtitle-fix" }, description: ResString.cmd_subtitleFix, getDefaultValue: () => true);
-        private readonly static Option<bool> CheckSegmentsCount = new(new string[] { "--check-segments-count" }, description: ResString.cmd_checkSegmentsCount, getDefaultValue: () => true);
-        private readonly static Option<bool> WriteMetaJson = new(new string[] { "--write-meta-json" }, description: ResString.cmd_writeMetaJson, getDefaultValue: () => true);
-        private readonly static Option<bool> AppendUrlParams = new(new string[] { "--append-url-params" }, description: ResString.cmd_appendUrlParams, getDefaultValue: () => false);
-        private readonly static Option<bool> MP4RealTimeDecryption = new (new string[] { "--mp4-real-time-decryption" }, description: ResString.cmd_MP4RealTimeDecryption, getDefaultValue: () => false);
-        private readonly static Option<bool> UseShakaPackager = new (new string[] { "--use-shaka-packager" }, description: ResString.cmd_useShakaPackager, getDefaultValue: () => false);
-        private readonly static Option<bool> ForceAnsiConsole = new(new string[] { "--force-ansi-console" }, description: ResString.cmd_forceAnsiConsole);
-        private readonly static Option<bool> NoAnsiColor = new(new string[] { "--no-ansi-color" }, description: ResString.cmd_noAnsiColor);
-        private readonly static Option<string?> DecryptionBinaryPath = new(new string[] { "--decryption-binary-path" }, description: ResString.cmd_decryptionBinaryPath) { ArgumentHelpName = "PATH" };
-        private readonly static Option<string?> FFmpegBinaryPath = new(new string[] { "--ffmpeg-binary-path" }, description: ResString.cmd_ffmpegBinaryPath) { ArgumentHelpName = "PATH" };
-        private readonly static Option<string?> BaseUrl = new(new string[] { "--base-url" }, description: ResString.cmd_baseUrl);
-        private readonly static Option<bool> ConcurrentDownload = new(new string[] { "-mt", "--concurrent-download" }, description: ResString.cmd_concurrentDownload, getDefaultValue: () => false);
-        private readonly static Option<bool> NoLog = new(new string[] { "--no-log" }, description: ResString.cmd_noLog, getDefaultValue: () => false);
-        private readonly static Option<string[]?> AdKeywords = new(new string[] { "--ad-keyword" }, description: ResString.cmd_adKeyword) { ArgumentHelpName = "REG" };
-        private readonly static Option<long?> MaxSpeed = new(new string[] { "-R", "--max-speed" }, description: ResString.cmd_maxSpeed, parseArgument: ParseSpeedLimit) { ArgumentHelpName = "SPEED" };
-
-
-        //代理选项
-        private readonly static Option<bool> UseSystemProxy = new(new string[] { "--use-system-proxy" }, description: ResString.cmd_useSystemProxy, getDefaultValue: () => true);
-        private readonly static Option<WebProxy?> CustomProxy = new(new string[] { "--custom-proxy" }, description: ResString.cmd_customProxy, parseArgument: ParseProxy) { ArgumentHelpName = "URL" };
-
-        //只下载部分分片
-        private readonly static Option<CustomRange?> CustomRange = new(new string[] { "--custom-range" }, description: ResString.cmd_customRange, parseArgument: ParseCustomRange) { ArgumentHelpName = "RANGE" };
-
-
-        //morehelp
-        private readonly static Option<string?> MoreHelp = new(new string[] { "--morehelp" }, description: ResString.cmd_moreHelp) { ArgumentHelpName = "OPTION" };
-
-        //自定义KEY等
-        private readonly static Option<EncryptMethod?> CustomHLSMethod = new(name: "--custom-hls-method", description: ResString.cmd_customHLSMethod) { ArgumentHelpName = "METHOD" };
-        private readonly static Option<byte[]?> CustomHLSKey = new(name: "--custom-hls-key", description: ResString.cmd_customHLSKey, parseArgument: ParseHLSCustomKey) { ArgumentHelpName = "FILE|HEX|BASE64" };
-        private readonly static Option<byte[]?> CustomHLSIv = new(name: "--custom-hls-iv", description: ResString.cmd_customHLSIv, parseArgument: ParseHLSCustomKey) { ArgumentHelpName = "FILE|HEX|BASE64" };
-
-        //任务开始时间
-        private readonly static Option<DateTime?> TaskStartAt = new(new string[] { "--task-start-at" }, description: ResString.cmd_taskStartAt, parseArgument: ParseStartTime) { ArgumentHelpName = "yyyyMMddHHmmss" };
-
-
-        //直播相关
-        private readonly static Option<bool> LivePerformAsVod = new(new string[] { "--live-perform-as-vod" }, description: ResString.cmd_livePerformAsVod, getDefaultValue: () => false);
-        private readonly static Option<bool> LiveRealTimeMerge = new(new string[] { "--live-real-time-merge" }, description: ResString.cmd_liveRealTimeMerge, getDefaultValue: () => false);
-        private readonly static Option<bool> LiveKeepSegments = new(new string[] { "--live-keep-segments" }, description: ResString.cmd_liveKeepSegments, getDefaultValue: () => true);
-        private readonly static Option<bool> LivePipeMux = new(new string[] { "--live-pipe-mux" }, description: ResString.cmd_livePipeMux, getDefaultValue: () => false);
-        private readonly static Option<TimeSpan?> LiveRecordLimit = new(new string[] { "--live-record-limit" }, description: ResString.cmd_liveRecordLimit, parseArgument: ParseLiveLimit) { ArgumentHelpName = "HH:mm:ss" };
-        private readonly static Option<int?> LiveWaitTime = new(new string[] { "--live-wait-time" }, description: ResString.cmd_liveWaitTime) { ArgumentHelpName = "SEC" };
-        private readonly static Option<int> LiveTakeCount = new(new string[] { "--live-take-count" }, description: ResString.cmd_liveTakeCount, getDefaultValue: () => 16) { ArgumentHelpName = "NUM" };
-        private readonly static Option<bool> LiveFixVttByAudio = new(new string[] { "--live-fix-vtt-by-audio" }, description: ResString.cmd_liveFixVttByAudio, getDefaultValue: () => false);
-
-
-        //复杂命令行如下
-        private readonly static Option<MuxOptions?> MuxAfterDone = new(new string[] { "-M", "--mux-after-done" }, description: ResString.cmd_muxAfterDone, parseArgument: ParseMuxAfterDone) { ArgumentHelpName = "OPTIONS" };
-        private readonly static Option<List<OutputFile>> MuxImports = new("--mux-import", description: ResString.cmd_muxImport, parseArgument: ParseImports) { Arity = ArgumentArity.OneOrMore, AllowMultipleArgumentsPerToken = false, ArgumentHelpName = "OPTIONS" };
-        private readonly static Option<StreamFilter?> VideoFilter = new(new string[] { "-sv", "--select-video" }, description: ResString.cmd_selectVideo, parseArgument: ParseStreamFilter) { ArgumentHelpName = "OPTIONS" };
-        private readonly static Option<StreamFilter?> AudioFilter = new(new string[] { "-sa", "--select-audio" }, description: ResString.cmd_selectAudio, parseArgument: ParseStreamFilter) { ArgumentHelpName = "OPTIONS" };
-        private readonly static Option<StreamFilter?> SubtitleFilter = new(new string[] { "-ss", "--select-subtitle" }, description: ResString.cmd_selectSubtitle, parseArgument: ParseStreamFilter) { ArgumentHelpName = "OPTIONS" };
-
-        private readonly static Option<StreamFilter?> DropVideoFilter = new(new string[] { "-dv", "--drop-video" }, description: ResString.cmd_dropVideo, parseArgument: ParseStreamFilter) { ArgumentHelpName = "OPTIONS" };
-        private readonly static Option<StreamFilter?> DropAudioFilter = new(new string[] { "-da", "--drop-audio" }, description: ResString.cmd_dropAudio, parseArgument: ParseStreamFilter) { ArgumentHelpName = "OPTIONS" };
-        private readonly static Option<StreamFilter?> DropSubtitleFilter = new(new string[] { "-ds", "--drop-subtitle" }, description: ResString.cmd_dropSubtitle, parseArgument: ParseStreamFilter) { ArgumentHelpName = "OPTIONS" };
-
-        /// <summary>
-        /// 解析录制直播时长限制
-        /// </summary>
-        /// <param name="result"></param>
-        /// <returns></returns>
-        private static long? ParseSpeedLimit(ArgumentResult result)
+        var input = result.Tokens.First().Value.ToUpper();
+        try
         {
-            var input = result.Tokens.First().Value.ToUpper();
-            try
-            {
-                var reg = new Regex("([\\d\\\\.]+)(M|K)");
-                if (!reg.IsMatch(input)) throw new ArgumentException();
+            var reg = new Regex("([\\d\\\\.]+)(M|K)");
+            if (!reg.IsMatch(input)) throw new ArgumentException();
 
-                var number = double.Parse(reg.Match(input).Groups[1].Value);
-                if (reg.Match(input).Groups[2].Value == "M")
-                    return (long)(number * 1024 * 1024);
-                else
-                    return (long)(number * 1024);
-            }
-            catch (Exception)
-            {
-                result.ErrorMessage = "error in parse SpeedLimit: " + input;
-                return null;
-            }
-        }
-
-        /// <summary>
-        /// 解析用户定义的下载范围
-        /// </summary>
-        /// <param name="result"></param>
-        /// <returns></returns>
-        /// <exception cref="ArgumentException"></exception>
-        private static CustomRange? ParseCustomRange(ArgumentResult result)
-        {
-            var input = result.Tokens.First().Value;
-            //支持的种类 0-100; 01:00:00-02:30:00; -300; 300-; 05:00-; -03:00;
-            try
-            {
-                if (string.IsNullOrEmpty(input))
-                    return null;
-
-                var arr = input.Split('-');
-                if (arr.Length != 2)
-                    throw new ArgumentException("Bad format!");
-
-                if (input.Contains(":"))
-                {
-                    return new CustomRange()
-                    {
-                        InputStr = input,
-                        StartSec = arr[0] == "" ? 0 : OtherUtil.ParseDur(arr[0]).TotalSeconds,
-                        EndSec = arr[1] == "" ? double.MaxValue : OtherUtil.ParseDur(arr[1]).TotalSeconds,
-                    };
-                }
-                else if (RangeRegex().IsMatch(input))
-                {
-                    var left = RangeRegex().Match(input).Groups[1].Value;
-                    var right = RangeRegex().Match(input).Groups[2].Value;
-                    return new CustomRange()
-                    {
-                        InputStr = input,
-                        StartSegIndex = left == "" ? 0 : long.Parse(left),
-                        EndSegIndex = right == "" ? long.MaxValue : long.Parse(right),
-                    };
-                }
-
-                throw new ArgumentException("Bad format!");
-            }
-            catch (Exception ex)
-            {
-                result.ErrorMessage = $"error in parse CustomRange: " + ex.Message;
-                return null;
-            }
-        }
-
-        /// <summary>
-        /// 解析用户代理
-        /// </summary>
-        /// <param name="result"></param>
-        /// <returns></returns>
-        /// <exception cref="ArgumentException"></exception>
-        private static WebProxy? ParseProxy(ArgumentResult result)
-        {
-            var input = result.Tokens.First().Value;
-            try
-            {
-                if (string.IsNullOrEmpty(input))
-                    return null;
-
-                var uri = new Uri(input);
-                var proxy = new WebProxy(uri, true);
-                if (!string.IsNullOrEmpty(uri.UserInfo))
-                {
-                    var infos = uri.UserInfo.Split(':');
-                    proxy.Credentials = new NetworkCredential(infos.First(), infos.Last());
-                }
-                return proxy;
-            }
-            catch (Exception ex)
-            {
-                result.ErrorMessage = $"error in parse proxy: " + ex.Message;
-                return null;
-            }
-        }
-
-        /// <summary>
-        /// 解析自定义KEY
-        /// </summary>
-        /// <param name="result"></param>
-        /// <returns></returns>
-        private static byte[]? ParseHLSCustomKey(ArgumentResult result)
-        {
-            var input = result.Tokens.First().Value;
-            try
-            {
-                if (string.IsNullOrEmpty(input))
-                    return null;
-                if (File.Exists(input))
-                    return File.ReadAllBytes(input);
-                else if (HexUtil.TryParseHexString(input, out byte[]? bytes))
-                    return bytes;
-                else
-                    return Convert.FromBase64String(input);
-            }
-            catch (Exception)
-            {
-                result.ErrorMessage = "error in parse hls custom key: " + input;
-                return null;
-            }
-        }
-
-        /// <summary>
-        /// 解析录制直播时长限制
-        /// </summary>
-        /// <param name="result"></param>
-        /// <returns></returns>
-        private static TimeSpan? ParseLiveLimit(ArgumentResult result)
-        {
-            var input = result.Tokens.First().Value;
-            try
-            {
-                return OtherUtil.ParseDur(input);
-            }
-            catch (Exception)
-            {
-                result.ErrorMessage = "error in parse LiveRecordLimit: " + input;
-                return null;
-            }
-        }
-
-        /// <summary>
-        /// 解析任务开始时间
-        /// </summary>
-        /// <param name="result"></param>
-        /// <returns></returns>
-        private static DateTime? ParseStartTime(ArgumentResult result)
-        {
-            var input = result.Tokens.First().Value;
-            try
-            {
-                CultureInfo provider = CultureInfo.InvariantCulture;
-                return DateTime.ParseExact(input, "yyyyMMddHHmmss", provider);
-            }
-            catch (Exception)
-            {
-                result.ErrorMessage = "error in parse TaskStartTime: " + input;
-                return null;
-            }
-        }
-
-        private static string? ParseSaveName(ArgumentResult result)
-        {
-            var input = result.Tokens.First().Value;
-            var newName = OtherUtil.GetValidFileName(input);
-            if (string.IsNullOrEmpty(newName))
-            {
-                result.ErrorMessage = "Invalid save name!";
-                return null;
-            }
-            return newName;
-        }
-
-        /// <summary>
-        /// 流过滤器
-        /// </summary>
-        /// <param name="result"></param>
-        /// <returns></returns>
-        private static StreamFilter? ParseStreamFilter(ArgumentResult result)
-        {
-            var streamFilter = new StreamFilter();
-            var input = result.Tokens.First().Value;
-            var p = new ComplexParamParser(input);
-
-
-            //目标范围
-            var forStr = "";
-            if (input == ForStrRegex().Match(input).Value)
-            {
-                forStr = input;
-            }
+            var number = double.Parse(reg.Match(input).Groups[1].Value);
+            if (reg.Match(input).Groups[2].Value == "M")
+                return (long)(number * 1024 * 1024);
             else
-            {
-                forStr = p.GetValue("for") ?? "best";
-                if (forStr != ForStrRegex().Match(forStr).Value)
-                {
-                    result.ErrorMessage = $"for={forStr} not valid";
-                    return null;
-                }
-            }
-            streamFilter.For = forStr;
-
-            var id = p.GetValue("id");
-            if (!string.IsNullOrEmpty(id))
-                streamFilter.GroupIdReg = new Regex(id);
-
-            var lang = p.GetValue("lang");
-            if (!string.IsNullOrEmpty(lang))
-                streamFilter.LanguageReg = new Regex(lang);
-
-            var name = p.GetValue("name");
-            if (!string.IsNullOrEmpty(name))
-                streamFilter.NameReg = new Regex(name);
-
-            var codecs = p.GetValue("codecs");
-            if (!string.IsNullOrEmpty(codecs))
-                streamFilter.CodecsReg = new Regex(codecs);
-
-            var res = p.GetValue("res");
-            if (!string.IsNullOrEmpty(res))
-                streamFilter.ResolutionReg = new Regex(res);
-
-            var frame = p.GetValue("frame");
-            if (!string.IsNullOrEmpty(frame))
-                streamFilter.FrameRateReg = new Regex(frame);
-
-            var channel = p.GetValue("channel");
-            if (!string.IsNullOrEmpty(channel))
-                streamFilter.ChannelsReg = new Regex(channel);
-
-            var range = p.GetValue("range");
-            if (!string.IsNullOrEmpty(range))
-                streamFilter.VideoRangeReg = new Regex(range);
-
-            var url = p.GetValue("url");
-            if (!string.IsNullOrEmpty(url))
-                streamFilter.UrlReg = new Regex(url);
-
-            var segsMin = p.GetValue("segsMin");
-            if (!string.IsNullOrEmpty(segsMin))
-                streamFilter.SegmentsMinCount = long.Parse(segsMin);
-
-            var segsMax = p.GetValue("segsMax");
-            if (!string.IsNullOrEmpty(segsMax))
-                streamFilter.SegmentsMaxCount = long.Parse(segsMax);
-
-            var plistDurMin = p.GetValue("plistDurMin");
-            if (!string.IsNullOrEmpty(plistDurMin))
-                streamFilter.PlaylistMinDur = OtherUtil.ParseSeconds(plistDurMin);
-
-            var plistDurMax = p.GetValue("plistDurMax");
-            if (!string.IsNullOrEmpty(plistDurMax))
-                streamFilter.PlaylistMaxDur = OtherUtil.ParseSeconds(plistDurMax);
-
-            var bwMin = p.GetValue("bwMin");
-            if (!string.IsNullOrEmpty(bwMin))
-                streamFilter.BandwidthMin = int.Parse(bwMin) * 1000;
-
-            var bwMax = p.GetValue("bwMax");
-            if (!string.IsNullOrEmpty(bwMax))
-                streamFilter.BandwidthMax = int.Parse(bwMax) * 1000;
-
-            var role = p.GetValue("role");
-            if (System.Enum.TryParse(role, true, out RoleType roleType))
-                streamFilter.Role = roleType;
-
-            return streamFilter;
+                return (long)(number * 1024);
         }
-
-        /// <summary>
-        /// 分割Header
-        /// </summary>
-        /// <param name="result"></param>
-        /// <returns></returns>
-        private static Dictionary<string, string> ParseHeaders(ArgumentResult result)
+        catch (Exception)
         {
-            var array = result.Tokens.Select(t => t.Value).ToArray();
-            return OtherUtil.SplitHeaderArrayToDic(array);
-        }
-
-        /// <summary>
-        /// 解析混流引入的外部文件
-        /// </summary>
-        /// <param name="result"></param>
-        /// <returns></returns>
-        private static List<OutputFile> ParseImports(ArgumentResult result)
-        {
-            var imports = new List<OutputFile>();
-
-            foreach (var item in result.Tokens)
-            {
-                var p = new ComplexParamParser(item.Value);
-                var path = p.GetValue("path") ?? item.Value; //若未获取到,直接整个字符串作为path
-                var lang = p.GetValue("lang");
-                var name = p.GetValue("name");
-                if (string.IsNullOrEmpty(path) || !File.Exists(path))
-                {
-                    result.ErrorMessage = "path empty or file not exists!";
-                    return imports;
-                }
-                imports.Add(new OutputFile()
-                {
-                    Index = 999,
-                    FilePath = path,
-                    LangCode = lang,
-                    Description = name
-                });
-            }
-
-            return imports;
-        }
-
-        /// <summary>
-        /// 解析混流选项
-        /// </summary>
-        /// <param name="result"></param>
-        /// <returns></returns>
-        private static MuxOptions? ParseMuxAfterDone(ArgumentResult result)
-        {
-            var v = result.Tokens.First().Value;
-            var p = new ComplexParamParser(v);
-            //混流格式
-            var format = p.GetValue("format") ?? v.Split(':')[0]; //若未获取到,直接:前的字符串作为format解析
-            var parseResult = System.Enum.TryParse(format.ToUpperInvariant(), out MuxFormat muxFormat);
-            if (!parseResult)
-            {
-                result.ErrorMessage = $"format={format} not valid";
-                return null;
-            }
-            //混流器
-            var muxer = p.GetValue("muxer") ?? "ffmpeg";
-            if (muxer != "ffmpeg" && muxer != "mkvmerge")
-            {
-                result.ErrorMessage = $"muxer={muxer} not valid";
-                return null;
-            }
-            //混流器路径
-            var bin_path = p.GetValue("bin_path") ?? "auto";
-            if (string.IsNullOrEmpty(bin_path))
-            {
-                result.ErrorMessage = $"bin_path={bin_path} not valid";
-                return null;
-            }
-            //是否删除
-            var keep = p.GetValue("keep") ?? "false";
-            if (keep != "true" && keep != "false")
-            {
-                result.ErrorMessage = $"keep={keep} not valid";
-                return null;
-            }
-            //是否忽略字幕
-            var skipSub = p.GetValue("skip_sub") ?? "false";
-            if (skipSub != "true" && skipSub != "false")
-            {
-                result.ErrorMessage = $"skip_sub={keep} not valid";
-                return null;
-            }
-            //冲突检测
-            if (muxer == "mkvmerge" && format == "mp4")
-            {
-                result.ErrorMessage = $"mkvmerge can not do mp4";
-                return null;
-            }
-            return new MuxOptions()
-            {
-                UseMkvmerge = muxer == "mkvmerge",
-                MuxFormat = muxFormat,
-                KeepFiles = keep == "true",
-                SkipSubtitle = skipSub == "true",
-                BinPath = bin_path == "auto" ? null : bin_path
-            };
-        }
-
-        class MyOptionBinder : BinderBase<MyOption>
-        {
-            protected override MyOption GetBoundValue(BindingContext bindingContext)
-            {
-                var option = new MyOption
-                {
-                    Input = bindingContext.ParseResult.GetValueForArgument(Input),
-                    ForceAnsiConsole = bindingContext.ParseResult.GetValueForOption(ForceAnsiConsole),
-                    NoAnsiColor = bindingContext.ParseResult.GetValueForOption(NoAnsiColor),
-                    LogLevel = bindingContext.ParseResult.GetValueForOption(LogLevel),
-                    AutoSelect = bindingContext.ParseResult.GetValueForOption(AutoSelect),
-                    SkipMerge = bindingContext.ParseResult.GetValueForOption(SkipMerge),
-                    BinaryMerge = bindingContext.ParseResult.GetValueForOption(BinaryMerge),
-                    UseFFmpegConcatDemuxer = bindingContext.ParseResult.GetValueForOption(UseFFmpegConcatDemuxer),
-                    DelAfterDone = bindingContext.ParseResult.GetValueForOption(DelAfterDone),
-                    AutoSubtitleFix = bindingContext.ParseResult.GetValueForOption(AutoSubtitleFix),
-                    CheckSegmentsCount = bindingContext.ParseResult.GetValueForOption(CheckSegmentsCount),
-                    SubtitleFormat = bindingContext.ParseResult.GetValueForOption(SubtitleFormat),
-                    SubOnly = bindingContext.ParseResult.GetValueForOption(SubOnly),
-                    TmpDir = bindingContext.ParseResult.GetValueForOption(TmpDir),
-                    SaveDir = bindingContext.ParseResult.GetValueForOption(SaveDir),
-                    SaveName = bindingContext.ParseResult.GetValueForOption(SaveName),
-                    ThreadCount = bindingContext.ParseResult.GetValueForOption(ThreadCount),
-                    UILanguage = bindingContext.ParseResult.GetValueForOption(UILanguage),
-                    SkipDownload = bindingContext.ParseResult.GetValueForOption(SkipDownload),
-                    WriteMetaJson = bindingContext.ParseResult.GetValueForOption(WriteMetaJson),
-                    AppendUrlParams = bindingContext.ParseResult.GetValueForOption(AppendUrlParams),
-                    SavePattern = bindingContext.ParseResult.GetValueForOption(SavePattern),
-                    Keys = bindingContext.ParseResult.GetValueForOption(Keys),
-                    UrlProcessorArgs = bindingContext.ParseResult.GetValueForOption(UrlProcessorArgs),
-                    MP4RealTimeDecryption = bindingContext.ParseResult.GetValueForOption(MP4RealTimeDecryption),
-                    UseShakaPackager = bindingContext.ParseResult.GetValueForOption(UseShakaPackager),
-                    DecryptionBinaryPath = bindingContext.ParseResult.GetValueForOption(DecryptionBinaryPath),
-                    FFmpegBinaryPath = bindingContext.ParseResult.GetValueForOption(FFmpegBinaryPath),
-                    KeyTextFile = bindingContext.ParseResult.GetValueForOption(KeyTextFile),
-                    DownloadRetryCount = bindingContext.ParseResult.GetValueForOption(DownloadRetryCount),
-                    BaseUrl = bindingContext.ParseResult.GetValueForOption(BaseUrl),
-                    MuxImports = bindingContext.ParseResult.GetValueForOption(MuxImports),
-                    ConcurrentDownload = bindingContext.ParseResult.GetValueForOption(ConcurrentDownload),
-                    VideoFilter = bindingContext.ParseResult.GetValueForOption(VideoFilter),
-                    AudioFilter = bindingContext.ParseResult.GetValueForOption(AudioFilter),
-                    SubtitleFilter = bindingContext.ParseResult.GetValueForOption(SubtitleFilter),
-                    DropVideoFilter = bindingContext.ParseResult.GetValueForOption(DropVideoFilter),
-                    DropAudioFilter = bindingContext.ParseResult.GetValueForOption(DropAudioFilter),
-                    DropSubtitleFilter = bindingContext.ParseResult.GetValueForOption(DropSubtitleFilter),
-                    LiveRealTimeMerge = bindingContext.ParseResult.GetValueForOption(LiveRealTimeMerge),
-                    LiveKeepSegments = bindingContext.ParseResult.GetValueForOption(LiveKeepSegments),
-                    LiveRecordLimit = bindingContext.ParseResult.GetValueForOption(LiveRecordLimit),
-                    TaskStartAt = bindingContext.ParseResult.GetValueForOption(TaskStartAt),
-                    LivePerformAsVod = bindingContext.ParseResult.GetValueForOption(LivePerformAsVod),
-                    LivePipeMux = bindingContext.ParseResult.GetValueForOption(LivePipeMux),
-                    LiveFixVttByAudio = bindingContext.ParseResult.GetValueForOption(LiveFixVttByAudio),
-                    UseSystemProxy = bindingContext.ParseResult.GetValueForOption(UseSystemProxy),
-                    CustomProxy = bindingContext.ParseResult.GetValueForOption(CustomProxy),
-                    CustomRange = bindingContext.ParseResult.GetValueForOption(CustomRange),
-                    LiveWaitTime = bindingContext.ParseResult.GetValueForOption(LiveWaitTime),
-                    LiveTakeCount = bindingContext.ParseResult.GetValueForOption(LiveTakeCount),
-                    NoDateInfo = bindingContext.ParseResult.GetValueForOption(NoDateInfo),
-                    NoLog = bindingContext.ParseResult.GetValueForOption(NoLog),
-                    AdKeywords = bindingContext.ParseResult.GetValueForOption(AdKeywords),
-                    MaxSpeed = bindingContext.ParseResult.GetValueForOption(MaxSpeed),
-                };
-
-                if (bindingContext.ParseResult.HasOption(CustomHLSMethod)) option.CustomHLSMethod = bindingContext.ParseResult.GetValueForOption(CustomHLSMethod);
-                if (bindingContext.ParseResult.HasOption(CustomHLSKey)) option.CustomHLSKey = bindingContext.ParseResult.GetValueForOption(CustomHLSKey);
-                if (bindingContext.ParseResult.HasOption(CustomHLSIv)) option.CustomHLSIv = bindingContext.ParseResult.GetValueForOption(CustomHLSIv);
-
-                var parsedHeaders = bindingContext.ParseResult.GetValueForOption(Headers);
-                if (parsedHeaders != null)
-                    option.Headers = parsedHeaders;
-
-
-                //以用户选择语言为准优先
-                if (option.UILanguage != null)
-                {
-                    CultureInfo.DefaultThreadCurrentCulture = new CultureInfo(option.UILanguage);
-                    Thread.CurrentThread.CurrentUICulture = CultureInfo.GetCultureInfo(option.UILanguage);
-                    Thread.CurrentThread.CurrentCulture = CultureInfo.GetCultureInfo(option.UILanguage);
-                }
-
-                //混流设置
-                var muxAfterDoneValue = bindingContext.ParseResult.GetValueForOption(MuxAfterDone);
-                if (muxAfterDoneValue != null)
-                {
-                    option.MuxAfterDone = true;
-                    option.MuxOptions = muxAfterDoneValue;
-                    if (muxAfterDoneValue.UseMkvmerge) option.MkvmergeBinaryPath = muxAfterDoneValue.BinPath;
-                    else option.FFmpegBinaryPath ??= muxAfterDoneValue.BinPath;
-                }
-
-
-                return option;
-            }
-        }
-
-
-        public static async Task<int> InvokeArgs(string[] args, Func<MyOption, Task> action)
-        {
-            var argList = new List<string>(args);
-            var index = -1;
-            if ((index = argList.IndexOf("--morehelp")) >= 0 && argList.Count > index + 1)
-            {
-                var option = argList[index + 1];
-                var msg = option switch
-                {
-                    "mux-after-done" => ResString.cmd_muxAfterDone_more,
-                    "mux-import" => ResString.cmd_muxImport_more,
-                    "select-video" => ResString.cmd_selectVideo_more,
-                    "select-audio" => ResString.cmd_selectAudio_more,
-                    "select-subtitle" => ResString.cmd_selectSubtitle_more,
-                    "custom-range" => ResString.cmd_custom_range,
-                    _ => $"Option=\"{option}\" not found"
-                };
-                Console.WriteLine($"More Help:\r\n\r\n  --{option}\r\n\r\n" + msg);
-                Environment.Exit(0);
-            }
-
-            var rootCommand = new RootCommand(VERSION_INFO)
-            {
-                Input, TmpDir, SaveDir, SaveName, BaseUrl, ThreadCount, DownloadRetryCount, ForceAnsiConsole, NoAnsiColor,AutoSelect, SkipMerge, SkipDownload, CheckSegmentsCount,
-                BinaryMerge, UseFFmpegConcatDemuxer, DelAfterDone, NoDateInfo, NoLog, WriteMetaJson, AppendUrlParams, ConcurrentDownload, Headers, /**SavePattern,**/ SubOnly, SubtitleFormat, AutoSubtitleFix,
-                FFmpegBinaryPath,
-                LogLevel, UILanguage, UrlProcessorArgs, Keys, KeyTextFile, DecryptionBinaryPath, UseShakaPackager, MP4RealTimeDecryption,
-                MaxSpeed,
-                MuxAfterDone,
-                CustomHLSMethod, CustomHLSKey, CustomHLSIv, UseSystemProxy, CustomProxy, CustomRange, TaskStartAt,
-                LivePerformAsVod, LiveRealTimeMerge, LiveKeepSegments, LivePipeMux, LiveFixVttByAudio, LiveRecordLimit, LiveWaitTime, LiveTakeCount,
-                MuxImports, VideoFilter, AudioFilter, SubtitleFilter, DropVideoFilter, DropAudioFilter, DropSubtitleFilter, AdKeywords, MoreHelp
-            };
-
-            rootCommand.TreatUnmatchedTokensAsErrors = true;
-            rootCommand.SetHandler(async (myOption) => await action(myOption), new MyOptionBinder());
-
-            var parser = new CommandLineBuilder(rootCommand)
-                .UseDefaults()
-                .EnablePosixBundling(false)
-                .UseExceptionHandler((ex, context) =>
-                {
-                    try { Console.CursorVisible = true; } catch { }
-                    string msg = Logger.LogLevel == Common.Log.LogLevel.DEBUG ? ex.ToString() : ex.Message;
-#if DEBUG
-                    msg = ex.ToString();
-#endif
-                    Logger.Error(msg);
-                    Thread.Sleep(3000);
-                    Environment.Exit(1);
-                }, 1)
-                .Build();
-
-            return await parser.InvokeAsync(args);
+            result.ErrorMessage = "error in parse SpeedLimit: " + input;
+            return null;
         }
     }
-}
+
+    /// <summary>
+    /// 解析用户定义的下载范围
+    /// </summary>
+    /// <param name="result"></param>
+    /// <returns></returns>
+    /// <exception cref="ArgumentException"></exception>
+    private static CustomRange? ParseCustomRange(ArgumentResult result)
+    {
+        var input = result.Tokens.First().Value;
+        // 支持的种类 0-100; 01:00:00-02:30:00; -300; 300-; 05:00-; -03:00;
+        try
+        {
+            if (string.IsNullOrEmpty(input))
+                return null;
+
+            var arr = input.Split('-');
+            if (arr.Length != 2)
+                throw new ArgumentException("Bad format!");
+
+            if (input.Contains(":"))
+            {
+                return new CustomRange()
+                {
+                    InputStr = input,
+                    StartSec = arr[0] == "" ? 0 : OtherUtil.ParseDur(arr[0]).TotalSeconds,
+                    EndSec = arr[1] == "" ? double.MaxValue : OtherUtil.ParseDur(arr[1]).TotalSeconds,
+                };
+            }
+            else if (RangeRegex().IsMatch(input))
+            {
+                var left = RangeRegex().Match(input).Groups[1].Value;
+                var right = RangeRegex().Match(input).Groups[2].Value;
+                return new CustomRange()
+                {
+                    InputStr = input,
+                    StartSegIndex = left == "" ? 0 : long.Parse(left),
+                    EndSegIndex = right == "" ? long.MaxValue : long.Parse(right),
+                };
+            }
+
+            throw new ArgumentException("Bad format!");
+        }
+        catch (Exception ex)
+        {
+            result.ErrorMessage = $"error in parse CustomRange: " + ex.Message;
+            return null;
+        }
+    }
+
+    /// <summary>
+    /// 解析用户代理
+    /// </summary>
+    /// <param name="result"></param>
+    /// <returns></returns>
+    /// <exception cref="ArgumentException"></exception>
+    private static WebProxy? ParseProxy(ArgumentResult result)
+    {
+        var input = result.Tokens.First().Value;
+        try
+        {
+            if (string.IsNullOrEmpty(input))
+                return null;
+
+            var uri = new Uri(input);
+            var proxy = new WebProxy(uri, true);
+            if (!string.IsNullOrEmpty(uri.UserInfo))
+            {
+                var infos = uri.UserInfo.Split(':');
+                proxy.Credentials = new NetworkCredential(infos.First(), infos.Last());
+            }
+            return proxy;
+        }
+        catch (Exception ex)
+        {
+            result.ErrorMessage = $"error in parse proxy: " + ex.Message;
+            return null;
+        }
+    }
+
+    /// <summary>
+    /// 解析自定义KEY
+    /// </summary>
+    /// <param name="result"></param>
+    /// <returns></returns>
+    private static byte[]? ParseHLSCustomKey(ArgumentResult result)
+    {
+        var input = result.Tokens.First().Value;
+        try
+        {
+            if (string.IsNullOrEmpty(input))
+                return null;
+            if (File.Exists(input))
+                return File.ReadAllBytes(input);
+            else if (HexUtil.TryParseHexString(input, out byte[]? bytes))
+                return bytes;
+            else
+                return Convert.FromBase64String(input);
+        }
+        catch (Exception)
+        {
+            result.ErrorMessage = "error in parse hls custom key: " + input;
+            return null;
+        }
+    }
+
+    /// <summary>
+    /// 解析录制直播时长限制
+    /// </summary>
+    /// <param name="result"></param>
+    /// <returns></returns>
+    private static TimeSpan? ParseLiveLimit(ArgumentResult result)
+    {
+        var input = result.Tokens.First().Value;
+        try
+        {
+            return OtherUtil.ParseDur(input);
+        }
+        catch (Exception)
+        {
+            result.ErrorMessage = "error in parse LiveRecordLimit: " + input;
+            return null;
+        }
+    }
+
+    /// <summary>
+    /// 解析任务开始时间
+    /// </summary>
+    /// <param name="result"></param>
+    /// <returns></returns>
+    private static DateTime? ParseStartTime(ArgumentResult result)
+    {
+        var input = result.Tokens.First().Value;
+        try
+        {
+            CultureInfo provider = CultureInfo.InvariantCulture;
+            return DateTime.ParseExact(input, "yyyyMMddHHmmss", provider);
+        }
+        catch (Exception)
+        {
+            result.ErrorMessage = "error in parse TaskStartTime: " + input;
+            return null;
+        }
+    }
+
+    private static string? ParseSaveName(ArgumentResult result)
+    {
+        var input = result.Tokens.First().Value;
+        var newName = OtherUtil.GetValidFileName(input);
+        if (string.IsNullOrEmpty(newName))
+        {
+            result.ErrorMessage = "Invalid save name!";
+            return null;
+        }
+        return newName;
+    }
+
+    /// <summary>
+    /// 流过滤器
+    /// </summary>
+    /// <param name="result"></param>
+    /// <returns></returns>
+    private static StreamFilter? ParseStreamFilter(ArgumentResult result)
+    {
+        var streamFilter = new StreamFilter();
+        var input = result.Tokens.First().Value;
+        var p = new ComplexParamParser(input);
+
+
+        // 目标范围
+        var forStr = "";
+        if (input == ForStrRegex().Match(input).Value)
+        {
+            forStr = input;
+        }
+        else
+        {
+            forStr = p.GetValue("for") ?? "best";
+            if (forStr != ForStrRegex().Match(forStr).Value)
+            {
+                result.ErrorMessage = $"for={forStr} not valid";
+                return null;
+            }
+        }
+        streamFilter.For = forStr;
+
+        var id = p.GetValue("id");
+        if (!string.IsNullOrEmpty(id))
+            streamFilter.GroupIdReg = new Regex(id);
+
+        var lang = p.GetValue("lang");
+        if (!string.IsNullOrEmpty(lang))
+            streamFilter.LanguageReg = new Regex(lang);
+
+        var name = p.GetValue("name");
+        if (!string.IsNullOrEmpty(name))
+            streamFilter.NameReg = new Regex(name);
+
+        var codecs = p.GetValue("codecs");
+        if (!string.IsNullOrEmpty(codecs))
+            streamFilter.CodecsReg = new Regex(codecs);
+
+        var res = p.GetValue("res");
+        if (!string.IsNullOrEmpty(res))
+            streamFilter.ResolutionReg = new Regex(res);
+
+        var frame = p.GetValue("frame");
+        if (!string.IsNullOrEmpty(frame))
+            streamFilter.FrameRateReg = new Regex(frame);
+
+        var channel = p.GetValue("channel");
+        if (!string.IsNullOrEmpty(channel))
+            streamFilter.ChannelsReg = new Regex(channel);
+
+        var range = p.GetValue("range");
+        if (!string.IsNullOrEmpty(range))
+            streamFilter.VideoRangeReg = new Regex(range);
+
+        var url = p.GetValue("url");
+        if (!string.IsNullOrEmpty(url))
+            streamFilter.UrlReg = new Regex(url);
+
+        var segsMin = p.GetValue("segsMin");
+        if (!string.IsNullOrEmpty(segsMin))
+            streamFilter.SegmentsMinCount = long.Parse(segsMin);
+
+        var segsMax = p.GetValue("segsMax");
+        if (!string.IsNullOrEmpty(segsMax))
+            streamFilter.SegmentsMaxCount = long.Parse(segsMax);
+
+        var plistDurMin = p.GetValue("plistDurMin");
+        if (!string.IsNullOrEmpty(plistDurMin))
+            streamFilter.PlaylistMinDur = OtherUtil.ParseSeconds(plistDurMin);
+
+        var plistDurMax = p.GetValue("plistDurMax");
+        if (!string.IsNullOrEmpty(plistDurMax))
+            streamFilter.PlaylistMaxDur = OtherUtil.ParseSeconds(plistDurMax);
+
+        var bwMin = p.GetValue("bwMin");
+        if (!string.IsNullOrEmpty(bwMin))
+            streamFilter.BandwidthMin = int.Parse(bwMin) * 1000;
+
+        var bwMax = p.GetValue("bwMax");
+        if (!string.IsNullOrEmpty(bwMax))
+            streamFilter.BandwidthMax = int.Parse(bwMax) * 1000;
+
+        var role = p.GetValue("role");
+        if (System.Enum.TryParse(role, true, out RoleType roleType))
+            streamFilter.Role = roleType;
+
+        return streamFilter;
+    }
+
+    /// <summary>
+    /// 分割Header
+    /// </summary>
+    /// <param name="result"></param>
+    /// <returns></returns>
+    private static Dictionary<string, string> ParseHeaders(ArgumentResult result)
+    {
+        var array = result.Tokens.Select(t => t.Value).ToArray();
+        return OtherUtil.SplitHeaderArrayToDic(array);
+    }
+
+    /// <summary>
+    /// 解析混流引入的外部文件
+    /// </summary>
+    /// <param name="result"></param>
+    /// <returns></returns>
+    private static List<OutputFile> ParseImports(ArgumentResult result)
+    {
+        var imports = new List<OutputFile>();
+
+        foreach (var item in result.Tokens)
+        {
+            var p = new ComplexParamParser(item.Value);
+            var path = p.GetValue("path") ?? item.Value; // 若未获取到,直接整个字符串作为path
+            var lang = p.GetValue("lang");
+            var name = p.GetValue("name");
+            if (string.IsNullOrEmpty(path) || !File.Exists(path))
+            {
+                result.ErrorMessage = "path empty or file not exists!";
+                return imports;
+            }
+            imports.Add(new OutputFile()
+            {
+                Index = 999,
+                FilePath = path,
+                LangCode = lang,
+                Description = name
+            });
+        }
+
+        return imports;
+    }
+
+    /// <summary>
+    /// 解析混流选项
+    /// </summary>
+    /// <param name="result"></param>
+    /// <returns></returns>
+    private static MuxOptions? ParseMuxAfterDone(ArgumentResult result)
+    {
+        var v = result.Tokens.First().Value;
+        var p = new ComplexParamParser(v);
+        // 混流格式
+        var format = p.GetValue("format") ?? v.Split(':')[0]; // 若未获取到,直接:前的字符串作为format解析
+        var parseResult = System.Enum.TryParse(format.ToUpperInvariant(), out MuxFormat muxFormat);
+        if (!parseResult)
+        {
+            result.ErrorMessage = $"format={format} not valid";
+            return null;
+        }
+        // 混流器
+        var muxer = p.GetValue("muxer") ?? "ffmpeg";
+        if (muxer != "ffmpeg" && muxer != "mkvmerge")
+        {
+            result.ErrorMessage = $"muxer={muxer} not valid";
+            return null;
+        }
+        // 混流器路径
+        var bin_path = p.GetValue("bin_path") ?? "auto";
+        if (string.IsNullOrEmpty(bin_path))
+        {
+            result.ErrorMessage = $"bin_path={bin_path} not valid";
+            return null;
+        }
+        // 是否删除
+        var keep = p.GetValue("keep") ?? "false";
+        if (keep != "true" && keep != "false")
+        {
+            result.ErrorMessage = $"keep={keep} not valid";
+            return null;
+        }
+        // 是否忽略字幕
+        var skipSub = p.GetValue("skip_sub") ?? "false";
+        if (skipSub != "true" && skipSub != "false")
+        {
+            result.ErrorMessage = $"skip_sub={keep} not valid";
+            return null;
+        }
+        // 冲突检测
+        if (muxer == "mkvmerge" && format == "mp4")
+        {
+            result.ErrorMessage = $"mkvmerge can not do mp4";
+            return null;
+        }
+        return new MuxOptions()
+        {
+            UseMkvmerge = muxer == "mkvmerge",
+            MuxFormat = muxFormat,
+            KeepFiles = keep == "true",
+            SkipSubtitle = skipSub == "true",
+            BinPath = bin_path == "auto" ? null : bin_path
+        };
+    }
+
+    class MyOptionBinder : BinderBase<MyOption>
+    {
+        protected override MyOption GetBoundValue(BindingContext bindingContext)
+        {
+            var option = new MyOption
+            {
+                Input = bindingContext.ParseResult.GetValueForArgument(Input),
+                ForceAnsiConsole = bindingContext.ParseResult.GetValueForOption(ForceAnsiConsole),
+                NoAnsiColor = bindingContext.ParseResult.GetValueForOption(NoAnsiColor),
+                LogLevel = bindingContext.ParseResult.GetValueForOption(LogLevel),
+                AutoSelect = bindingContext.ParseResult.GetValueForOption(AutoSelect),
+                SkipMerge = bindingContext.ParseResult.GetValueForOption(SkipMerge),
+                BinaryMerge = bindingContext.ParseResult.GetValueForOption(BinaryMerge),
+                UseFFmpegConcatDemuxer = bindingContext.ParseResult.GetValueForOption(UseFFmpegConcatDemuxer),
+                DelAfterDone = bindingContext.ParseResult.GetValueForOption(DelAfterDone),
+                AutoSubtitleFix = bindingContext.ParseResult.GetValueForOption(AutoSubtitleFix),
+                CheckSegmentsCount = bindingContext.ParseResult.GetValueForOption(CheckSegmentsCount),
+                SubtitleFormat = bindingContext.ParseResult.GetValueForOption(SubtitleFormat),
+                SubOnly = bindingContext.ParseResult.GetValueForOption(SubOnly),
+                TmpDir = bindingContext.ParseResult.GetValueForOption(TmpDir),
+                SaveDir = bindingContext.ParseResult.GetValueForOption(SaveDir),
+                SaveName = bindingContext.ParseResult.GetValueForOption(SaveName),
+                ThreadCount = bindingContext.ParseResult.GetValueForOption(ThreadCount),
+                UILanguage = bindingContext.ParseResult.GetValueForOption(UILanguage),
+                SkipDownload = bindingContext.ParseResult.GetValueForOption(SkipDownload),
+                WriteMetaJson = bindingContext.ParseResult.GetValueForOption(WriteMetaJson),
+                AppendUrlParams = bindingContext.ParseResult.GetValueForOption(AppendUrlParams),
+                SavePattern = bindingContext.ParseResult.GetValueForOption(SavePattern),
+                Keys = bindingContext.ParseResult.GetValueForOption(Keys),
+                UrlProcessorArgs = bindingContext.ParseResult.GetValueForOption(UrlProcessorArgs),
+                MP4RealTimeDecryption = bindingContext.ParseResult.GetValueForOption(MP4RealTimeDecryption),
+                UseShakaPackager = bindingContext.ParseResult.GetValueForOption(UseShakaPackager),
+                DecryptionBinaryPath = bindingContext.ParseResult.GetValueForOption(DecryptionBinaryPath),
+                FFmpegBinaryPath = bindingContext.ParseResult.GetValueForOption(FFmpegBinaryPath),
+                KeyTextFile = bindingContext.ParseResult.GetValueForOption(KeyTextFile),
+                DownloadRetryCount = bindingContext.ParseResult.GetValueForOption(DownloadRetryCount),
+                BaseUrl = bindingContext.ParseResult.GetValueForOption(BaseUrl),
+                MuxImports = bindingContext.ParseResult.GetValueForOption(MuxImports),
+                ConcurrentDownload = bindingContext.ParseResult.GetValueForOption(ConcurrentDownload),
+                VideoFilter = bindingContext.ParseResult.GetValueForOption(VideoFilter),
+                AudioFilter = bindingContext.ParseResult.GetValueForOption(AudioFilter),
+                SubtitleFilter = bindingContext.ParseResult.GetValueForOption(SubtitleFilter),
+                DropVideoFilter = bindingContext.ParseResult.GetValueForOption(DropVideoFilter),
+                DropAudioFilter = bindingContext.ParseResult.GetValueForOption(DropAudioFilter),
+                DropSubtitleFilter = bindingContext.ParseResult.GetValueForOption(DropSubtitleFilter),
+                LiveRealTimeMerge = bindingContext.ParseResult.GetValueForOption(LiveRealTimeMerge),
+                LiveKeepSegments = bindingContext.ParseResult.GetValueForOption(LiveKeepSegments),
+                LiveRecordLimit = bindingContext.ParseResult.GetValueForOption(LiveRecordLimit),
+                TaskStartAt = bindingContext.ParseResult.GetValueForOption(TaskStartAt),
+                LivePerformAsVod = bindingContext.ParseResult.GetValueForOption(LivePerformAsVod),
+                LivePipeMux = bindingContext.ParseResult.GetValueForOption(LivePipeMux),
+                LiveFixVttByAudio = bindingContext.ParseResult.GetValueForOption(LiveFixVttByAudio),
+                UseSystemProxy = bindingContext.ParseResult.GetValueForOption(UseSystemProxy),
+                CustomProxy = bindingContext.ParseResult.GetValueForOption(CustomProxy),
+                CustomRange = bindingContext.ParseResult.GetValueForOption(CustomRange),
+                LiveWaitTime = bindingContext.ParseResult.GetValueForOption(LiveWaitTime),
+                LiveTakeCount = bindingContext.ParseResult.GetValueForOption(LiveTakeCount),
+                NoDateInfo = bindingContext.ParseResult.GetValueForOption(NoDateInfo),
+                NoLog = bindingContext.ParseResult.GetValueForOption(NoLog),
+                AdKeywords = bindingContext.ParseResult.GetValueForOption(AdKeywords),
+                MaxSpeed = bindingContext.ParseResult.GetValueForOption(MaxSpeed),
+            };
+
+            if (bindingContext.ParseResult.HasOption(CustomHLSMethod)) option.CustomHLSMethod = bindingContext.ParseResult.GetValueForOption(CustomHLSMethod);
+            if (bindingContext.ParseResult.HasOption(CustomHLSKey)) option.CustomHLSKey = bindingContext.ParseResult.GetValueForOption(CustomHLSKey);
+            if (bindingContext.ParseResult.HasOption(CustomHLSIv)) option.CustomHLSIv = bindingContext.ParseResult.GetValueForOption(CustomHLSIv);
+
+            var parsedHeaders = bindingContext.ParseResult.GetValueForOption(Headers);
+            if (parsedHeaders != null)
+                option.Headers = parsedHeaders;
+
+
+            // 以用户选择语言为准优先
+            if (option.UILanguage != null)
+            {
+                CultureInfo.DefaultThreadCurrentCulture = new CultureInfo(option.UILanguage);
+                Thread.CurrentThread.CurrentUICulture = CultureInfo.GetCultureInfo(option.UILanguage);
+                Thread.CurrentThread.CurrentCulture = CultureInfo.GetCultureInfo(option.UILanguage);
+            }
+
+            // 混流设置
+            var muxAfterDoneValue = bindingContext.ParseResult.GetValueForOption(MuxAfterDone);
+            if (muxAfterDoneValue != null)
+            {
+                option.MuxAfterDone = true;
+                option.MuxOptions = muxAfterDoneValue;
+                if (muxAfterDoneValue.UseMkvmerge) option.MkvmergeBinaryPath = muxAfterDoneValue.BinPath;
+                else option.FFmpegBinaryPath ??= muxAfterDoneValue.BinPath;
+            }
+
+
+            return option;
+        }
+    }
+
+
+    public static async Task<int> InvokeArgs(string[] args, Func<MyOption, Task> action)
+    {
+        var argList = new List<string>(args);
+        var index = -1;
+        if ((index = argList.IndexOf("--morehelp")) >= 0 && argList.Count > index + 1)
+        {
+            var option = argList[index + 1];
+            var msg = option switch
+            {
+                "mux-after-done" => ResString.cmd_muxAfterDone_more,
+                "mux-import" => ResString.cmd_muxImport_more,
+                "select-video" => ResString.cmd_selectVideo_more,
+                "select-audio" => ResString.cmd_selectAudio_more,
+                "select-subtitle" => ResString.cmd_selectSubtitle_more,
+                "custom-range" => ResString.cmd_custom_range,
+                _ => $"Option=\"{option}\" not found"
+            };
+            Console.WriteLine($"More Help:\r\n\r\n  --{option}\r\n\r\n" + msg);
+            Environment.Exit(0);
+        }
+
+        var rootCommand = new RootCommand(VERSION_INFO)
+        {
+            Input, TmpDir, SaveDir, SaveName, BaseUrl, ThreadCount, DownloadRetryCount, ForceAnsiConsole, NoAnsiColor,AutoSelect, SkipMerge, SkipDownload, CheckSegmentsCount,
+            BinaryMerge, UseFFmpegConcatDemuxer, DelAfterDone, NoDateInfo, NoLog, WriteMetaJson, AppendUrlParams, ConcurrentDownload, Headers, /**SavePattern,**/ SubOnly, SubtitleFormat, AutoSubtitleFix,
+            FFmpegBinaryPath,
+            LogLevel, UILanguage, UrlProcessorArgs, Keys, KeyTextFile, DecryptionBinaryPath, UseShakaPackager, MP4RealTimeDecryption,
+            MaxSpeed,
+            MuxAfterDone,
+            CustomHLSMethod, CustomHLSKey, CustomHLSIv, UseSystemProxy, CustomProxy, CustomRange, TaskStartAt,
+            LivePerformAsVod, LiveRealTimeMerge, LiveKeepSegments, LivePipeMux, LiveFixVttByAudio, LiveRecordLimit, LiveWaitTime, LiveTakeCount,
+            MuxImports, VideoFilter, AudioFilter, SubtitleFilter, DropVideoFilter, DropAudioFilter, DropSubtitleFilter, AdKeywords, MoreHelp
+        };
+
+        rootCommand.TreatUnmatchedTokensAsErrors = true;
+        rootCommand.SetHandler(async (myOption) => await action(myOption), new MyOptionBinder());
+
+        var parser = new CommandLineBuilder(rootCommand)
+            .UseDefaults()
+            .EnablePosixBundling(false)
+            .UseExceptionHandler((ex, context) =>
+            {
+                try { Console.CursorVisible = true; } catch { }
+                string msg = Logger.LogLevel == Common.Log.LogLevel.DEBUG ? ex.ToString() : ex.Message;
+#if DEBUG
+                msg = ex.ToString();
+#endif
+                Logger.Error(msg);
+                Thread.Sleep(3000);
+                Environment.Exit(1);
+            }, 1)
+            .Build();
+
+        return await parser.InvokeAsync(args);
+    }
+}
\ No newline at end of file
diff --git a/src/N_m3u8DL-RE/CommandLine/ComplexParamParser.cs b/src/N_m3u8DL-RE/CommandLine/ComplexParamParser.cs
index 8674dd4..4ee32f6 100644
--- a/src/N_m3u8DL-RE/CommandLine/ComplexParamParser.cs
+++ b/src/N_m3u8DL-RE/CommandLine/ComplexParamParser.cs
@@ -1,61 +1,56 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
+using System.Text;
 
-namespace N_m3u8DL_RE.CommandLine
+namespace N_m3u8DL_RE.CommandLine;
+
+internal class ComplexParamParser
 {
-    internal class ComplexParamParser
+    private string _arg;
+    public ComplexParamParser(string arg)
     {
-        private string _arg;
-        public ComplexParamParser(string arg)
-        {
-            _arg = arg;
-        }
+        _arg = arg;
+    }
 
-        public string? GetValue(string key)
-        {
-            if (string.IsNullOrEmpty(key) || string.IsNullOrEmpty(_arg)) return null;
+    public string? GetValue(string key)
+    {
+        if (string.IsNullOrEmpty(key) || string.IsNullOrEmpty(_arg)) return null;
 
-            try
+        try
+        {
+            var index = _arg.IndexOf(key + "=");
+            if (index == -1) return (_arg.Contains(key) && _arg.EndsWith(key)) ? "true" : null;
+
+            var chars = _arg[(index + key.Length + 1)..].ToCharArray();
+            var result = new StringBuilder();
+            char last = '\0';
+            for (int i = 0; i < chars.Length; i++)
             {
-                var index = _arg.IndexOf(key + "=");
-                if (index == -1) return (_arg.Contains(key) && _arg.EndsWith(key)) ? "true" : null;
-
-                var chars = _arg[(index + key.Length + 1)..].ToCharArray();
-                var result = new StringBuilder();
-                char last = '\0';
-                for (int i = 0; i < chars.Length; i++)
+                if (chars[i] == ':')
                 {
-                    if (chars[i] == ':')
-                    {
-                        if (last == '\\')
-                        {
-                            result.Replace("\\", "");
-                            last = chars[i];
-                            result.Append(chars[i]);
-                        }
-                        else break;
-                    }
-                    else
+                    if (last == '\\')
                     {
+                        result.Replace("\\", "");
                         last = chars[i];
                         result.Append(chars[i]);
                     }
+                    else break;
+                }
+                else
+                {
+                    last = chars[i];
+                    result.Append(chars[i]);
                 }
-
-                var resultStr = result.ToString().Trim().Trim('\"').Trim('\'');
-
-                //不应该有引号出现
-                if (resultStr.Contains('\"') || resultStr.Contains('\'')) throw new Exception();
-
-                return resultStr;
-            }
-            catch (Exception)
-            {
-                throw new ArgumentException($"Parse Argument [{key}] failed!");
             }
+
+            var resultStr = result.ToString().Trim().Trim('\"').Trim('\'');
+
+            // 不应该有引号出现
+            if (resultStr.Contains('\"') || resultStr.Contains('\'')) throw new Exception();
+
+            return resultStr;
+        }
+        catch (Exception)
+        {
+            throw new ArgumentException($"Parse Argument [{key}] failed!");
         }
     }
-}
+}
\ No newline at end of file
diff --git a/src/N_m3u8DL-RE/CommandLine/MyOption.cs b/src/N_m3u8DL-RE/CommandLine/MyOption.cs
index 9b926ce..0cb77e4 100644
--- a/src/N_m3u8DL-RE/CommandLine/MyOption.cs
+++ b/src/N_m3u8DL-RE/CommandLine/MyOption.cs
@@ -4,255 +4,254 @@ using N_m3u8DL_RE.Entity;
 using N_m3u8DL_RE.Enum;
 using System.Net;
 
-namespace N_m3u8DL_RE.CommandLine
+namespace N_m3u8DL_RE.CommandLine;
+
+internal class MyOption
 {
-    internal class MyOption
-    {
-        /// <summary>
-        /// See: <see cref="CommandInvoker.Input"/>.
-        /// </summary>
-        public string Input { get; set; } = default!;
-        /// <summary>
-        /// See: <see cref="CommandInvoker.Headers"/>.
-        /// </summary>
-        public Dictionary<string, string> Headers { get; set; } = new Dictionary<string, string>();
-        /// <summary>
-        /// See: <see cref="CommandInvoker.AdKeywords"/>.
-        /// </summary>
-        public string[]? AdKeywords { get; set; }
-        /// <summary>
-        /// See: <see cref="CommandInvoker.MaxSpeed"/>.
-        /// </summary>
-        public long? MaxSpeed { get; set; }
-        /// <summary>
-        /// See: <see cref="CommandInvoker.Keys"/>.
-        /// </summary>
-        public string[]? Keys { get; set; }
-        /// <summary>
-        /// See: <see cref="CommandInvoker.BaseUrl"/>.
-        /// </summary>
-        public string? BaseUrl { get; set; }
-        /// <summary>
-        /// See: <see cref="CommandInvoker.KeyTextFile"/>.
-        /// </summary>
-        public string? KeyTextFile { get; set; }
-        /// <summary>
-        /// See: <see cref="CommandInvoker.UrlProcessorArgs"/>.
-        /// </summary>
-        public string? UrlProcessorArgs { get; set; }
-        /// <summary>
-        /// See: <see cref="CommandInvoker.LogLevel"/>.
-        /// </summary>
-        public LogLevel LogLevel { get; set; }
-        /// <summary>
-        /// See: <see cref="CommandInvoker.NoDateInfo"/>.
-        /// </summary>
-        public bool NoDateInfo { get; set; }
-        /// <summary>
-        /// See: <see cref="CommandInvoker.NoLog"/>.
-        /// </summary>
-        public bool NoLog { get; set; }
-        /// <summary>
-        /// See: <see cref="CommandInvoker.AutoSelect"/>.
-        /// </summary>
-        public bool AutoSelect { get; set; }
-        /// <summary>
-        /// See: <see cref="CommandInvoker.SubOnly"/>.
-        /// </summary>
-        public bool SubOnly { get; set; }
-        /// <summary>
-        /// See: <see cref="CommandInvoker.ThreadCount"/>.
-        /// </summary>
-        public int ThreadCount { get; set; }
-        /// <summary>
-        /// See: <see cref="CommandInvoker.DownloadRetryCount"/>.
-        /// </summary>
-        public int DownloadRetryCount { get; set; }
-        /// <summary>
-        /// See: <see cref="CommandInvoker.LiveRecordLimit"/>.
-        /// </summary>
-        public TimeSpan? LiveRecordLimit { get; set; }
-        /// <summary>
-        /// See: <see cref="CommandInvoker.TaskStartAt"/>.
-        /// </summary>
-        public DateTime? TaskStartAt { get; set; }
-        /// <summary>
-        /// See: <see cref="CommandInvoker.SkipMerge"/>.
-        /// </summary>
-        public bool SkipMerge { get; set; }
-        /// <summary>
-        /// See: <see cref="CommandInvoker.BinaryMerge"/>.
-        /// </summary>
-        public bool BinaryMerge { get; set; }
-        /// <summary>
-        /// See: <see cref="CommandInvoker.ForceAnsiConsole"/>.
-        /// </summary>
-        public bool ForceAnsiConsole { get; set; }
-        /// <summary>
-        /// See: <see cref="CommandInvoker.NoAnsiColor"/>.
-        /// </summary>
-        public bool NoAnsiColor { get; set; }
-        /// <summary>
-        /// See: <see cref="CommandInvoker.UseFFmpegConcatDemuxer"/>.
-        /// </summary>
-        public bool UseFFmpegConcatDemuxer { get; set; }
-        /// <summary>
-        /// See: <see cref="CommandInvoker.DelAfterDone"/>.
-        /// </summary>
-        public bool DelAfterDone { get; set; }
-        /// <summary>
-        /// See: <see cref="CommandInvoker.AutoSubtitleFix"/>.
-        /// </summary>
-        public bool AutoSubtitleFix { get; set; }
-        /// <summary>
-        /// See: <see cref="CommandInvoker.CheckSegmentsCount"/>.
-        /// </summary>
-        public bool CheckSegmentsCount { get; set; }
-        /// <summary>
-        /// See: <see cref="CommandInvoker.SkipDownload"/>.
-        /// </summary>
-        public bool SkipDownload { get; set; }
-        /// <summary>
-        /// See: <see cref="CommandInvoker.WriteMetaJson"/>.
-        /// </summary>
-        public bool WriteMetaJson { get; set; }
-        /// <summary>
-        /// See: <see cref="CommandInvoker.AppendUrlParams"/>.
-        /// </summary>
-        public bool AppendUrlParams { get; set; }
-        /// <summary>
-        /// See: <see cref="CommandInvoker.MP4RealTimeDecryption"/>.
-        /// </summary>
-        public bool MP4RealTimeDecryption { get; set; }
-        /// <summary>
-        /// See: <see cref="CommandInvoker.UseShakaPackager"/>.
-        /// </summary>
-        public bool UseShakaPackager { get; set; }
-        /// <summary>
-        /// See: <see cref="CommandInvoker.MuxAfterDone"/>.
-        /// </summary>
-        public bool MuxAfterDone { get; set; }
-        /// <summary>
-        /// See: <see cref="CommandInvoker.ConcurrentDownload"/>.
-        /// </summary>
-        public bool ConcurrentDownload { get; set; }
-        /// <summary>
-        /// See: <see cref="CommandInvoker.LiveRealTimeMerge"/>.
-        /// </summary>
-        public bool LiveRealTimeMerge { get; set; }
-        /// <summary>
-        /// See: <see cref="CommandInvoker.LiveKeepSegments"/>.
-        /// </summary>
-        public bool LiveKeepSegments { get; set; }
-        /// <summary>
-        /// See: <see cref="CommandInvoker.LivePerformAsVod"/>.
-        /// </summary>
-        public bool LivePerformAsVod { get; set; }
-        /// <summary>
-        /// See: <see cref="CommandInvoker.UseSystemProxy"/>.
-        /// </summary>
-        public bool UseSystemProxy { get; set; }
-        /// <summary>
-        /// See: <see cref="CommandInvoker.SubtitleFormat"/>.
-        /// </summary>
-        public SubtitleFormat SubtitleFormat { get; set; }
-        /// <summary>
-        /// See: <see cref="CommandInvoker.TmpDir"/>.
-        /// </summary>
-        public string? TmpDir { get; set; }
-        /// <summary>
-        /// See: <see cref="CommandInvoker.SaveDir"/>.
-        /// </summary>
-        public string? SaveDir { get; set; }
-        /// <summary>
-        /// See: <see cref="CommandInvoker.SaveName"/>.
-        /// </summary>
-        public string? SaveName { get; set; }
-        /// <summary>
-        /// See: <see cref="CommandInvoker.SavePattern"/>.
-        /// </summary>
-        public string? SavePattern { get; set; }
-        /// <summary>
-        /// See: <see cref="CommandInvoker.UILanguage"/>.
-        /// </summary>
-        public string? UILanguage { get; set; }
-        /// <summary>
-        /// See: <see cref="CommandInvoker.DecryptionBinaryPath"/>.
-        /// </summary>
-        public string? DecryptionBinaryPath { get; set; }
-        /// <summary>
-        /// See: <see cref="CommandInvoker.FFmpegBinaryPath"/>.
-        /// </summary>
-        public string? FFmpegBinaryPath { get; set; }
-        /// <summary>
-        /// See: <see cref="CommandInvoker.MkvmergeBinaryPath"/>.
-        /// </summary>
-        public string? MkvmergeBinaryPath { get; set; }
-        /// <summary>
-        /// See: <see cref="CommandInvoker.MuxImports"/>.
-        /// </summary>
-        public List<OutputFile>? MuxImports { get; set; }
-        /// <summary>
-        /// See: <see cref="CommandInvoker.VideoFilter"/>.
-        /// </summary>
-        public StreamFilter? VideoFilter { get; set; }
-        /// <summary>
-        /// See: <see cref="CommandInvoker.DropVideoFilter"/>.
-        /// </summary>
-        public StreamFilter? DropVideoFilter { get; set; }
-        /// <summary>
-        /// See: <see cref="CommandInvoker.AudioFilter"/>.
-        /// </summary>
-        public StreamFilter? AudioFilter { get; set; }
-        /// <summary>
-        /// See: <see cref="CommandInvoker.DropAudioFilter"/>.
-        /// </summary>
-        public StreamFilter? DropAudioFilter { get; set; }
-        /// <summary>
-        /// See: <see cref="CommandInvoker.SubtitleFilter"/>.
-        /// </summary>
-        public StreamFilter? SubtitleFilter { get; set; }
-        /// <summary>
-        /// See: <see cref="CommandInvoker.DropSubtitleFilter"/>.
-        /// </summary>
-        public StreamFilter? DropSubtitleFilter { get; set; }
-        /// <summary>
-        /// See: <see cref="CommandInvoker.CustomHLSMethod"/>.
-        /// </summary>
-        public EncryptMethod? CustomHLSMethod { get; set; }
-        /// <summary>
-        /// See: <see cref="CommandInvoker.CustomHLSKey"/>.
-        /// </summary>
-        public byte[]? CustomHLSKey { get; set; }
-        /// <summary>
-        /// See: <see cref="CommandInvoker.CustomHLSIv"/>.
-        /// </summary>
-        public byte[]? CustomHLSIv { get; set; }
-        /// <summary>
-        /// See: <see cref="CommandInvoker.CustomProxy"/>.
-        /// </summary>
-        public WebProxy? CustomProxy { get; set; }
-        /// <summary>
-        /// See: <see cref="CommandInvoker.CustomRange"/>.
-        /// </summary>
-        public CustomRange? CustomRange { get; set; }
-        /// <summary>
-        /// See: <see cref="CommandInvoker.LiveWaitTime"/>.
-        /// </summary>
-        public int? LiveWaitTime { get; set; }
-        /// <summary>
-        /// See: <see cref="CommandInvoker.LiveTakeCount"/>.
-        /// </summary>
-        public int LiveTakeCount { get; set; }
-        public MuxOptions MuxOptions { get; set; }
-        //public bool LiveWriteHLS { get; set; } = true;
-        /// <summary>
-        /// See: <see cref="CommandInvoker.LivePipeMux"/>.
-        /// </summary>
-        public bool LivePipeMux { get; set; }
-        /// <summary>
-        /// See: <see cref="CommandInvoker.LiveFixVttByAudio"/>.
-        /// </summary>
-        public bool LiveFixVttByAudio { get; set; }
-    }
+    /// <summary>
+    /// See: <see cref="CommandInvoker.Input"/>.
+    /// </summary>
+    public string Input { get; set; } = default!;
+    /// <summary>
+    /// See: <see cref="CommandInvoker.Headers"/>.
+    /// </summary>
+    public Dictionary<string, string> Headers { get; set; } = new Dictionary<string, string>();
+    /// <summary>
+    /// See: <see cref="CommandInvoker.AdKeywords"/>.
+    /// </summary>
+    public string[]? AdKeywords { get; set; }
+    /// <summary>
+    /// See: <see cref="CommandInvoker.MaxSpeed"/>.
+    /// </summary>
+    public long? MaxSpeed { get; set; }
+    /// <summary>
+    /// See: <see cref="CommandInvoker.Keys"/>.
+    /// </summary>
+    public string[]? Keys { get; set; }
+    /// <summary>
+    /// See: <see cref="CommandInvoker.BaseUrl"/>.
+    /// </summary>
+    public string? BaseUrl { get; set; }
+    /// <summary>
+    /// See: <see cref="CommandInvoker.KeyTextFile"/>.
+    /// </summary>
+    public string? KeyTextFile { get; set; }
+    /// <summary>
+    /// See: <see cref="CommandInvoker.UrlProcessorArgs"/>.
+    /// </summary>
+    public string? UrlProcessorArgs { get; set; }
+    /// <summary>
+    /// See: <see cref="CommandInvoker.LogLevel"/>.
+    /// </summary>
+    public LogLevel LogLevel { get; set; }
+    /// <summary>
+    /// See: <see cref="CommandInvoker.NoDateInfo"/>.
+    /// </summary>
+    public bool NoDateInfo { get; set; }
+    /// <summary>
+    /// See: <see cref="CommandInvoker.NoLog"/>.
+    /// </summary>
+    public bool NoLog { get; set; }
+    /// <summary>
+    /// See: <see cref="CommandInvoker.AutoSelect"/>.
+    /// </summary>
+    public bool AutoSelect { get; set; }
+    /// <summary>
+    /// See: <see cref="CommandInvoker.SubOnly"/>.
+    /// </summary>
+    public bool SubOnly { get; set; }
+    /// <summary>
+    /// See: <see cref="CommandInvoker.ThreadCount"/>.
+    /// </summary>
+    public int ThreadCount { get; set; }
+    /// <summary>
+    /// See: <see cref="CommandInvoker.DownloadRetryCount"/>.
+    /// </summary>
+    public int DownloadRetryCount { get; set; }
+    /// <summary>
+    /// See: <see cref="CommandInvoker.LiveRecordLimit"/>.
+    /// </summary>
+    public TimeSpan? LiveRecordLimit { get; set; }
+    /// <summary>
+    /// See: <see cref="CommandInvoker.TaskStartAt"/>.
+    /// </summary>
+    public DateTime? TaskStartAt { get; set; }
+    /// <summary>
+    /// See: <see cref="CommandInvoker.SkipMerge"/>.
+    /// </summary>
+    public bool SkipMerge { get; set; }
+    /// <summary>
+    /// See: <see cref="CommandInvoker.BinaryMerge"/>.
+    /// </summary>
+    public bool BinaryMerge { get; set; }
+    /// <summary>
+    /// See: <see cref="CommandInvoker.ForceAnsiConsole"/>.
+    /// </summary>
+    public bool ForceAnsiConsole { get; set; }
+    /// <summary>
+    /// See: <see cref="CommandInvoker.NoAnsiColor"/>.
+    /// </summary>
+    public bool NoAnsiColor { get; set; }
+    /// <summary>
+    /// See: <see cref="CommandInvoker.UseFFmpegConcatDemuxer"/>.
+    /// </summary>
+    public bool UseFFmpegConcatDemuxer { get; set; }
+    /// <summary>
+    /// See: <see cref="CommandInvoker.DelAfterDone"/>.
+    /// </summary>
+    public bool DelAfterDone { get; set; }
+    /// <summary>
+    /// See: <see cref="CommandInvoker.AutoSubtitleFix"/>.
+    /// </summary>
+    public bool AutoSubtitleFix { get; set; }
+    /// <summary>
+    /// See: <see cref="CommandInvoker.CheckSegmentsCount"/>.
+    /// </summary>
+    public bool CheckSegmentsCount { get; set; }
+    /// <summary>
+    /// See: <see cref="CommandInvoker.SkipDownload"/>.
+    /// </summary>
+    public bool SkipDownload { get; set; }
+    /// <summary>
+    /// See: <see cref="CommandInvoker.WriteMetaJson"/>.
+    /// </summary>
+    public bool WriteMetaJson { get; set; }
+    /// <summary>
+    /// See: <see cref="CommandInvoker.AppendUrlParams"/>.
+    /// </summary>
+    public bool AppendUrlParams { get; set; }
+    /// <summary>
+    /// See: <see cref="CommandInvoker.MP4RealTimeDecryption"/>.
+    /// </summary>
+    public bool MP4RealTimeDecryption { get; set; }
+    /// <summary>
+    /// See: <see cref="CommandInvoker.UseShakaPackager"/>.
+    /// </summary>
+    public bool UseShakaPackager { get; set; }
+    /// <summary>
+    /// See: <see cref="CommandInvoker.MuxAfterDone"/>.
+    /// </summary>
+    public bool MuxAfterDone { get; set; }
+    /// <summary>
+    /// See: <see cref="CommandInvoker.ConcurrentDownload"/>.
+    /// </summary>
+    public bool ConcurrentDownload { get; set; }
+    /// <summary>
+    /// See: <see cref="CommandInvoker.LiveRealTimeMerge"/>.
+    /// </summary>
+    public bool LiveRealTimeMerge { get; set; }
+    /// <summary>
+    /// See: <see cref="CommandInvoker.LiveKeepSegments"/>.
+    /// </summary>
+    public bool LiveKeepSegments { get; set; }
+    /// <summary>
+    /// See: <see cref="CommandInvoker.LivePerformAsVod"/>.
+    /// </summary>
+    public bool LivePerformAsVod { get; set; }
+    /// <summary>
+    /// See: <see cref="CommandInvoker.UseSystemProxy"/>.
+    /// </summary>
+    public bool UseSystemProxy { get; set; }
+    /// <summary>
+    /// See: <see cref="CommandInvoker.SubtitleFormat"/>.
+    /// </summary>
+    public SubtitleFormat SubtitleFormat { get; set; }
+    /// <summary>
+    /// See: <see cref="CommandInvoker.TmpDir"/>.
+    /// </summary>
+    public string? TmpDir { get; set; }
+    /// <summary>
+    /// See: <see cref="CommandInvoker.SaveDir"/>.
+    /// </summary>
+    public string? SaveDir { get; set; }
+    /// <summary>
+    /// See: <see cref="CommandInvoker.SaveName"/>.
+    /// </summary>
+    public string? SaveName { get; set; }
+    /// <summary>
+    /// See: <see cref="CommandInvoker.SavePattern"/>.
+    /// </summary>
+    public string? SavePattern { get; set; }
+    /// <summary>
+    /// See: <see cref="CommandInvoker.UILanguage"/>.
+    /// </summary>
+    public string? UILanguage { get; set; }
+    /// <summary>
+    /// See: <see cref="CommandInvoker.DecryptionBinaryPath"/>.
+    /// </summary>
+    public string? DecryptionBinaryPath { get; set; }
+    /// <summary>
+    /// See: <see cref="CommandInvoker.FFmpegBinaryPath"/>.
+    /// </summary>
+    public string? FFmpegBinaryPath { get; set; }
+    /// <summary>
+    /// See: <see cref="CommandInvoker.MkvmergeBinaryPath"/>.
+    /// </summary>
+    public string? MkvmergeBinaryPath { get; set; }
+    /// <summary>
+    /// See: <see cref="CommandInvoker.MuxImports"/>.
+    /// </summary>
+    public List<OutputFile>? MuxImports { get; set; }
+    /// <summary>
+    /// See: <see cref="CommandInvoker.VideoFilter"/>.
+    /// </summary>
+    public StreamFilter? VideoFilter { get; set; }
+    /// <summary>
+    /// See: <see cref="CommandInvoker.DropVideoFilter"/>.
+    /// </summary>
+    public StreamFilter? DropVideoFilter { get; set; }
+    /// <summary>
+    /// See: <see cref="CommandInvoker.AudioFilter"/>.
+    /// </summary>
+    public StreamFilter? AudioFilter { get; set; }
+    /// <summary>
+    /// See: <see cref="CommandInvoker.DropAudioFilter"/>.
+    /// </summary>
+    public StreamFilter? DropAudioFilter { get; set; }
+    /// <summary>
+    /// See: <see cref="CommandInvoker.SubtitleFilter"/>.
+    /// </summary>
+    public StreamFilter? SubtitleFilter { get; set; }
+    /// <summary>
+    /// See: <see cref="CommandInvoker.DropSubtitleFilter"/>.
+    /// </summary>
+    public StreamFilter? DropSubtitleFilter { get; set; }
+    /// <summary>
+    /// See: <see cref="CommandInvoker.CustomHLSMethod"/>.
+    /// </summary>
+    public EncryptMethod? CustomHLSMethod { get; set; }
+    /// <summary>
+    /// See: <see cref="CommandInvoker.CustomHLSKey"/>.
+    /// </summary>
+    public byte[]? CustomHLSKey { get; set; }
+    /// <summary>
+    /// See: <see cref="CommandInvoker.CustomHLSIv"/>.
+    /// </summary>
+    public byte[]? CustomHLSIv { get; set; }
+    /// <summary>
+    /// See: <see cref="CommandInvoker.CustomProxy"/>.
+    /// </summary>
+    public WebProxy? CustomProxy { get; set; }
+    /// <summary>
+    /// See: <see cref="CommandInvoker.CustomRange"/>.
+    /// </summary>
+    public CustomRange? CustomRange { get; set; }
+    /// <summary>
+    /// See: <see cref="CommandInvoker.LiveWaitTime"/>.
+    /// </summary>
+    public int? LiveWaitTime { get; set; }
+    /// <summary>
+    /// See: <see cref="CommandInvoker.LiveTakeCount"/>.
+    /// </summary>
+    public int LiveTakeCount { get; set; }
+    public MuxOptions? MuxOptions { get; set; }
+    // public bool LiveWriteHLS { get; set; } = true;
+    /// <summary>
+    /// See: <see cref="CommandInvoker.LivePipeMux"/>.
+    /// </summary>
+    public bool LivePipeMux { get; set; }
+    /// <summary>
+    /// See: <see cref="CommandInvoker.LiveFixVttByAudio"/>.
+    /// </summary>
+    public bool LiveFixVttByAudio { get; set; }
 }
\ No newline at end of file
diff --git a/src/N_m3u8DL-RE/Config/DownloaderConfig.cs b/src/N_m3u8DL-RE/Config/DownloaderConfig.cs
index c953878..ca1716e 100644
--- a/src/N_m3u8DL-RE/Config/DownloaderConfig.cs
+++ b/src/N_m3u8DL-RE/Config/DownloaderConfig.cs
@@ -1,33 +1,25 @@
 using N_m3u8DL_RE.CommandLine;
-using N_m3u8DL_RE.Enum;
-using N_m3u8DL_RE.Parser.Config;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
 
-namespace N_m3u8DL_RE.Config
+namespace N_m3u8DL_RE.Config;
+
+internal class DownloaderConfig
 {
-    internal class DownloaderConfig
-    {
-        public required MyOption MyOptions { get; set; }
+    public required MyOption MyOptions { get; set; }
 
-        /// <summary>
-        /// 前置阶段生成的文件夹名
-        /// </summary>
-        public required string DirPrefix { get; set; }
-        /// <summary>
-        /// 文件名模板
-        /// </summary>
-        public string? SavePattern { get; set; }
-        /// <summary>
-        /// 校验响应头的文件大小和实际大小
-        /// </summary>
-        public bool CheckContentLength { get; set; } = true;
-        /// <summary>
-        /// 请求头
-        /// </summary>
-        public Dictionary<string, string> Headers { get; set; } = new Dictionary<string, string>();
-    }
-}
+    /// <summary>
+    /// 前置阶段生成的文件夹名
+    /// </summary>
+    public required string DirPrefix { get; set; }
+    /// <summary>
+    /// 文件名模板
+    /// </summary>
+    public string? SavePattern { get; set; }
+    /// <summary>
+    /// 校验响应头的文件大小和实际大小
+    /// </summary>
+    public bool CheckContentLength { get; set; } = true;
+    /// <summary>
+    /// 请求头
+    /// </summary>
+    public Dictionary<string, string> Headers { get; set; } = new Dictionary<string, string>();
+}
\ No newline at end of file
diff --git a/src/N_m3u8DL-RE/Crypto/AESUtil.cs b/src/N_m3u8DL-RE/Crypto/AESUtil.cs
index b2b270d..3b9d299 100644
--- a/src/N_m3u8DL-RE/Crypto/AESUtil.cs
+++ b/src/N_m3u8DL-RE/Crypto/AESUtil.cs
@@ -1,44 +1,38 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Security.Cryptography;
-using System.Text;
-using System.Threading.Tasks;
+using System.Security.Cryptography;
 
-namespace N_m3u8DL_RE.Crypto
+namespace N_m3u8DL_RE.Crypto;
+
+internal class AESUtil
 {
-    internal class AESUtil
+    /// <summary>
+    /// AES-128解密,解密后原地替换文件
+    /// </summary>
+    /// <param name="filePath"></param>
+    /// <param name="keyByte"></param>
+    /// <param name="ivByte"></param>
+    /// <param name="mode"></param>
+    /// <param name="padding"></param>
+    public static void AES128Decrypt(string filePath, byte[] keyByte, byte[] ivByte, CipherMode mode = CipherMode.CBC, PaddingMode padding = PaddingMode.PKCS7)
     {
-        /// <summary>
-        /// AES-128解密,解密后原地替换文件
-        /// </summary>
-        /// <param name="filePath"></param>
-        /// <param name="keyByte"></param>
-        /// <param name="ivByte"></param>
-        /// <param name="mode"></param>
-        /// <param name="padding"></param>
-        public static void AES128Decrypt(string filePath, byte[] keyByte, byte[] ivByte, CipherMode mode = CipherMode.CBC, PaddingMode padding = PaddingMode.PKCS7)
-        {
-            var fileBytes = File.ReadAllBytes(filePath);
-            var decrypted = AES128Decrypt(fileBytes, keyByte, ivByte, mode, padding);
-            File.WriteAllBytes(filePath, decrypted);
-        }
-
-        public static byte[] AES128Decrypt(byte[] encryptedBuff, byte[] keyByte, byte[] ivByte, CipherMode mode = CipherMode.CBC, PaddingMode padding = PaddingMode.PKCS7)
-        {
-            byte[] inBuff = encryptedBuff;
-
-            Aes dcpt = Aes.Create();
-            dcpt.BlockSize = 128;
-            dcpt.KeySize = 128;
-            dcpt.Key = keyByte;
-            dcpt.IV = ivByte;
-            dcpt.Mode = mode;
-            dcpt.Padding = padding;
-
-            ICryptoTransform cTransform = dcpt.CreateDecryptor();
-            byte[] resultArray = cTransform.TransformFinalBlock(inBuff, 0, inBuff.Length);
-            return resultArray;
-        }
+        var fileBytes = File.ReadAllBytes(filePath);
+        var decrypted = AES128Decrypt(fileBytes, keyByte, ivByte, mode, padding);
+        File.WriteAllBytes(filePath, decrypted);
     }
-}
+
+    public static byte[] AES128Decrypt(byte[] encryptedBuff, byte[] keyByte, byte[] ivByte, CipherMode mode = CipherMode.CBC, PaddingMode padding = PaddingMode.PKCS7)
+    {
+        byte[] inBuff = encryptedBuff;
+
+        Aes dcpt = Aes.Create();
+        dcpt.BlockSize = 128;
+        dcpt.KeySize = 128;
+        dcpt.Key = keyByte;
+        dcpt.IV = ivByte;
+        dcpt.Mode = mode;
+        dcpt.Padding = padding;
+
+        ICryptoTransform cTransform = dcpt.CreateDecryptor();
+        byte[] resultArray = cTransform.TransformFinalBlock(inBuff, 0, inBuff.Length);
+        return resultArray;
+    }
+}
\ No newline at end of file
diff --git a/src/N_m3u8DL-RE/Crypto/ChaCha20Util.cs b/src/N_m3u8DL-RE/Crypto/ChaCha20Util.cs
index 0c02179..2edab26 100644
--- a/src/N_m3u8DL-RE/Crypto/ChaCha20Util.cs
+++ b/src/N_m3u8DL-RE/Crypto/ChaCha20Util.cs
@@ -1,26 +1,21 @@
 using CSChaCha20;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
 
-namespace N_m3u8DL_RE.Crypto
+namespace N_m3u8DL_RE.Crypto;
+
+internal class ChaCha20Util
 {
-    internal class ChaCha20Util
+    public static byte[] DecryptPer1024Bytes(byte[] encryptedBuff, byte[] keyBytes, byte[] nonceBytes)
     {
-        public static byte[] DecryptPer1024Bytes(byte[] encryptedBuff, byte[] keyBytes, byte[] nonceBytes)
-        {
-            if (keyBytes.Length != 32)
-                throw new Exception("Key must be 32 bytes!");
-            if (nonceBytes.Length != 12 && nonceBytes.Length != 8)
-                throw new Exception("Key must be 12 or 8 bytes!");
-            if (nonceBytes.Length == 8)
-                nonceBytes = (new byte[4] { 0, 0, 0, 0 }).Concat(nonceBytes).ToArray();
+        if (keyBytes.Length != 32)
+            throw new Exception("Key must be 32 bytes!");
+        if (nonceBytes.Length != 12 && nonceBytes.Length != 8)
+            throw new Exception("Key must be 12 or 8 bytes!");
+        if (nonceBytes.Length == 8)
+            nonceBytes = (new byte[4] { 0, 0, 0, 0 }).Concat(nonceBytes).ToArray();
 
-            var decStream = new MemoryStream();
-            using BinaryReader reader = new BinaryReader(new MemoryStream(encryptedBuff));
-            using (BinaryWriter writer = new BinaryWriter(decStream))
+        var decStream = new MemoryStream();
+        using BinaryReader reader = new BinaryReader(new MemoryStream(encryptedBuff));
+        using (BinaryWriter writer = new BinaryWriter(decStream))
             while (true)
             {
                 var buffer = reader.ReadBytes(1024);
@@ -37,7 +32,6 @@ namespace N_m3u8DL_RE.Crypto
                 }
             }
 
-            return decStream.ToArray();
-        }
+        return decStream.ToArray();
     }
-}
+}
\ No newline at end of file
diff --git a/src/N_m3u8DL-RE/DownloadManager/HTTPLiveRecordManager.cs b/src/N_m3u8DL-RE/DownloadManager/HTTPLiveRecordManager.cs
index be6c39d..206b78e 100644
--- a/src/N_m3u8DL-RE/DownloadManager/HTTPLiveRecordManager.cs
+++ b/src/N_m3u8DL-RE/DownloadManager/HTTPLiveRecordManager.cs
@@ -1,7 +1,5 @@
-using Mp4SubtitleParser;
-using N_m3u8DL_RE.Column;
+using N_m3u8DL_RE.Column;
 using N_m3u8DL_RE.Common.Entity;
-using N_m3u8DL_RE.Common.Enum;
 using N_m3u8DL_RE.Common.Log;
 using N_m3u8DL_RE.Common.Resource;
 using N_m3u8DL_RE.Common.Util;
@@ -11,244 +9,234 @@ using N_m3u8DL_RE.Entity;
 using N_m3u8DL_RE.Parser;
 using N_m3u8DL_RE.Util;
 using Spectre.Console;
-using Spectre.Console.Rendering;
-using System;
 using System.Collections.Concurrent;
-using System.IO;
-using System.Net.Http.Headers;
-using System.Reflection.PortableExecutable;
 using System.Text;
-using System.Threading;
-using System.Threading.Tasks;
-using System.Threading.Tasks.Dataflow;
-using System.Xml.Linq;
 
-namespace N_m3u8DL_RE.DownloadManager
+namespace N_m3u8DL_RE.DownloadManager;
+
+internal class HTTPLiveRecordManager
 {
-    internal class HTTPLiveRecordManager
+    IDownloader Downloader;
+    DownloaderConfig DownloaderConfig;
+    StreamExtractor StreamExtractor;
+    List<StreamSpec> SelectedSteams;
+    List<OutputFile> OutputFiles = new();
+    DateTime NowDateTime;
+    DateTime? PublishDateTime;
+    bool STOP_FLAG = false;
+    bool READ_IFO = false;
+    ConcurrentDictionary<int, int> RecordingDurDic = new(); // 已录制时长
+    ConcurrentDictionary<int, double> RecordingSizeDic = new(); // 已录制大小
+    CancellationTokenSource CancellationTokenSource = new(); // 取消Wait
+    List<byte> InfoBuffer = new List<byte>(188 * 5000); // 5000个分包中解析信息,没有就算了
+
+    public HTTPLiveRecordManager(DownloaderConfig downloaderConfig, List<StreamSpec> selectedSteams, StreamExtractor streamExtractor)
     {
-        IDownloader Downloader;
-        DownloaderConfig DownloaderConfig;
-        StreamExtractor StreamExtractor;
-        List<StreamSpec> SelectedSteams;
-        List<OutputFile> OutputFiles = new();
-        DateTime NowDateTime;
-        DateTime? PublishDateTime;
-        bool STOP_FLAG = false;
-        bool READ_IFO = false;
-        ConcurrentDictionary<int, int> RecordingDurDic = new(); //已录制时长
-        ConcurrentDictionary<int, double> RecordingSizeDic = new(); //已录制大小
-        CancellationTokenSource CancellationTokenSource = new(); //取消Wait
-        List<byte> InfoBuffer = new List<byte>(188 * 5000); //5000个分包中解析信息,没有就算了
+        this.DownloaderConfig = downloaderConfig;
+        Downloader = new SimpleDownloader(DownloaderConfig);
+        NowDateTime = DateTime.Now;
+        PublishDateTime = selectedSteams.FirstOrDefault()?.PublishTime;
+        StreamExtractor = streamExtractor;
+        SelectedSteams = selectedSteams;
+    }
 
-        public HTTPLiveRecordManager(DownloaderConfig downloaderConfig, List<StreamSpec> selectedSteams, StreamExtractor streamExtractor)
+    private async Task<bool> RecordStreamAsync(StreamSpec streamSpec, ProgressTask task, SpeedContainer speedContainer)
+    {
+        task.MaxValue = 1;
+        task.StartTask();
+
+        var name = streamSpec.ToShortString();
+        var dirName = $"{DownloaderConfig.MyOptions.SaveName ?? NowDateTime.ToString("yyyy-MM-dd_HH-mm-ss")}_{task.Id}_{OtherUtil.GetValidFileName(streamSpec.GroupId ?? "", "-")}_{streamSpec.Codecs}_{streamSpec.Bandwidth}_{streamSpec.Language}";
+        var saveDir = DownloaderConfig.MyOptions.SaveDir ?? Environment.CurrentDirectory;
+        var saveName = DownloaderConfig.MyOptions.SaveName != null ? $"{DownloaderConfig.MyOptions.SaveName}.{streamSpec.Language}".TrimEnd('.') : dirName;
+
+        Logger.Debug($"dirName: {dirName}; saveDir: {saveDir}; saveName: {saveName}");
+
+        // 创建文件夹
+        if (!Directory.Exists(saveDir)) Directory.CreateDirectory(saveDir);
+
+        using var request = new HttpRequestMessage(HttpMethod.Get, new Uri(streamSpec.Url));
+        request.Headers.ConnectionClose = false;
+        foreach (var item in DownloaderConfig.Headers)
         {
-            this.DownloaderConfig = downloaderConfig;
-            Downloader = new SimpleDownloader(DownloaderConfig);
-            NowDateTime = DateTime.Now;
-            PublishDateTime = selectedSteams.FirstOrDefault()?.PublishTime;
-            StreamExtractor = streamExtractor;
-            SelectedSteams = selectedSteams;
+            request.Headers.TryAddWithoutValidation(item.Key, item.Value);
+        }
+        Logger.Debug(request.Headers.ToString());
+
+        using var response = await HTTPUtil.AppHttpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, CancellationTokenSource.Token);
+        response.EnsureSuccessStatusCode();
+
+        var output = Path.Combine(saveDir, saveName + ".ts");
+        using var stream = new FileStream(output, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.Read);
+        using var responseStream = await response.Content.ReadAsStreamAsync(CancellationTokenSource.Token);
+        var buffer = new byte[16 * 1024];
+        var size = 0;
+
+        // 计时器
+        _ = TimeCounterAsync();
+        // 读取INFO
+        _ = ReadInfoAsync();
+
+        try
+        {
+            while ((size = await responseStream.ReadAsync(buffer, CancellationTokenSource.Token)) > 0)
+            {
+                if (!READ_IFO && InfoBuffer.Count < 188 * 5000)
+                {
+                    InfoBuffer.AddRange(buffer);
+                }
+                speedContainer.Add(size);
+                RecordingSizeDic[task.Id] += size;
+                await stream.WriteAsync(buffer, 0, size);
+            }
+        }
+        catch (OperationCanceledException oce) when (oce.CancellationToken == CancellationTokenSource.Token)
+        {
+            ;
         }
 
-        private async Task<bool> RecordStreamAsync(StreamSpec streamSpec, ProgressTask task, SpeedContainer speedContainer)
+        Logger.InfoMarkUp("File Size: " + GlobalUtil.FormatFileSize(RecordingSizeDic[task.Id]));
+
+        return true;
+    }
+
+    public async Task ReadInfoAsync()
+    {
+        while (!STOP_FLAG && !READ_IFO)
         {
-            task.MaxValue = 1;
-            task.StartTask();
+            await Task.Delay(200);
+            if (InfoBuffer.Count < 188 * 5000) continue;
 
-            var name = streamSpec.ToShortString();
-            var dirName = $"{DownloaderConfig.MyOptions.SaveName ?? NowDateTime.ToString("yyyy-MM-dd_HH-mm-ss")}_{task.Id}_{OtherUtil.GetValidFileName(streamSpec.GroupId ?? "", "-")}_{streamSpec.Codecs}_{streamSpec.Bandwidth}_{streamSpec.Language}";
-            var saveDir = DownloaderConfig.MyOptions.SaveDir ?? Environment.CurrentDirectory;
-            var saveName = DownloaderConfig.MyOptions.SaveName != null ? $"{DownloaderConfig.MyOptions.SaveName}.{streamSpec.Language}".TrimEnd('.') : dirName;
-
-            Logger.Debug($"dirName: {dirName}; saveDir: {saveDir}; saveName: {saveName}");
-
-            //创建文件夹
-            if (!Directory.Exists(saveDir)) Directory.CreateDirectory(saveDir);
-
-            using var request = new HttpRequestMessage(HttpMethod.Get, new Uri(streamSpec.Url));
-            request.Headers.ConnectionClose = false;
-            foreach (var item in DownloaderConfig.Headers)
+            UInt16 ConvertToUint16(IEnumerable<byte> bytes)
             {
-                request.Headers.TryAddWithoutValidation(item.Key, item.Value);
+                if (BitConverter.IsLittleEndian)
+                    bytes = bytes.Reverse();
+                return BitConverter.ToUInt16(bytes.ToArray());
             }
-            Logger.Debug(request.Headers.ToString());
 
-            using var response = await HTTPUtil.AppHttpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, CancellationTokenSource.Token);
-            response.EnsureSuccessStatusCode();
-
-            var output = Path.Combine(saveDir, saveName + ".ts");
-            using var stream = new FileStream(output, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.Read);
-            using var responseStream = await response.Content.ReadAsStreamAsync(CancellationTokenSource.Token);
-            var buffer = new byte[16 * 1024];
-            var size = 0;
-
-            //计时器
-            TimeCounterAsync();
-            //读取INFO
-            ReadInfoAsync();
-
-            try
+            var data = InfoBuffer.ToArray();
+            var programId = "";
+            var serviceProvider = "";
+            var serviceName = "";
+            for (int i = 0; i < data.Length; i++)
             {
-                while ((size = await responseStream.ReadAsync(buffer, CancellationTokenSource.Token)) > 0)
+                if (data[i] == 0x47 && (i + 188) < data.Length && data[i + 188] == 0x47)
                 {
-                    if (!READ_IFO && InfoBuffer.Count < 188 * 5000)
+                    var tsData = data.Skip(i).Take(188);
+                    var tsHeaderInt = BitConverter.ToUInt32(BitConverter.IsLittleEndian ? tsData.Take(4).Reverse().ToArray() : tsData.Take(4).ToArray(), 0);
+                    var pid = (tsHeaderInt & 0x1fff00) >> 8;
+                    var tsPayload = tsData.Skip(4);
+                    // PAT
+                    if (pid == 0x0000)
                     {
-                        InfoBuffer.AddRange(buffer);
+                        programId = ConvertToUint16(tsPayload.Skip(9).Take(2)).ToString();
                     }
-                    speedContainer.Add(size);
-                    RecordingSizeDic[task.Id] += size;
-                    await stream.WriteAsync(buffer, 0, size);
-                }
-            }
-            catch (OperationCanceledException oce) when (oce.CancellationToken == CancellationTokenSource.Token)
-            {
-                ;
-            }
-
-            Logger.InfoMarkUp("File Size: " + GlobalUtil.FormatFileSize(RecordingSizeDic[task.Id]));
-
-            return true;
-        }
-
-        public async Task ReadInfoAsync()
-        {
-            while (!STOP_FLAG && !READ_IFO)
-            {
-                await Task.Delay(200);
-                if (InfoBuffer.Count < 188 * 5000) continue;
-
-                UInt16 ConvertToUint16(IEnumerable<byte> bytes)
-                {
-                    if (BitConverter.IsLittleEndian)
-                        bytes = bytes.Reverse();
-                    return BitConverter.ToUInt16(bytes.ToArray());
-                }
-
-                var data = InfoBuffer.ToArray();
-                var programId = "";
-                var serviceProvider = "";
-                var serviceName = "";
-                for (int i = 0; i < data.Length; i++)
-                {
-                    if (data[i] == 0x47 && (i + 188) < data.Length && data[i + 188] == 0x47)
+                    // SDT, BAT, ST
+                    else if (pid == 0x0011)
                     {
-                        var tsData = data.Skip(i).Take(188);
-                        var tsHeaderInt = BitConverter.ToUInt32(BitConverter.IsLittleEndian ? tsData.Take(4).Reverse().ToArray() : tsData.Take(4).ToArray(), 0);
-                        var pid = (tsHeaderInt & 0x1fff00) >> 8;
-                        var tsPayload = tsData.Skip(4);
-                        //PAT
-                        if (pid == 0x0000)
+                        var tableId = (int)tsPayload.Skip(1).First();
+                        // Current TS Info
+                        if (tableId == 0x42)
                         {
-                            programId = ConvertToUint16(tsPayload.Skip(9).Take(2)).ToString();
+                            var sectionLength = ConvertToUint16(tsPayload.Skip(2).Take(2)) & 0xfff;
+                            var sectionData = tsPayload.Skip(4).Take(sectionLength);
+                            var dscripData = sectionData.Skip(8);
+                            var descriptorsLoopLength = (ConvertToUint16(dscripData.Skip(3).Take(2))) & 0xfff;
+                            var descriptorsData = dscripData.Skip(5).Take(descriptorsLoopLength);
+                            var serviceProviderLength = (int)descriptorsData.Skip(3).First();
+                            serviceProvider = Encoding.UTF8.GetString(descriptorsData.Skip(4).Take(serviceProviderLength).ToArray());
+                            var serviceNameLength = (int)descriptorsData.Skip(4 + serviceProviderLength).First();
+                            serviceName = Encoding.UTF8.GetString(descriptorsData.Skip(5 + serviceProviderLength).Take(serviceNameLength).ToArray());
                         }
-                        //SDT, BAT, ST
-                        else if (pid == 0x0011)
-                        {
-                            var tableId = (int)tsPayload.Skip(1).First();
-                            //Current TS Info
-                            if (tableId == 0x42)
-                            {
-                                var sectionLength = ConvertToUint16(tsPayload.Skip(2).Take(2)) & 0xfff;
-                                var sectionData = tsPayload.Skip(4).Take(sectionLength);
-                                var dscripData = sectionData.Skip(8);
-                                var descriptorsLoopLength = (ConvertToUint16(dscripData.Skip(3).Take(2))) & 0xfff;
-                                var descriptorsData = dscripData.Skip(5).Take(descriptorsLoopLength);
-                                var serviceProviderLength = (int)descriptorsData.Skip(3).First();
-                                serviceProvider = Encoding.UTF8.GetString(descriptorsData.Skip(4).Take(serviceProviderLength).ToArray());
-                                var serviceNameLength = (int)descriptorsData.Skip(4 + serviceProviderLength).First();
-                                serviceName = Encoding.UTF8.GetString(descriptorsData.Skip(5 + serviceProviderLength).Take(serviceNameLength).ToArray());
-                            }
-                        }
-                        if (programId != "" && (serviceName != "" || serviceProvider != ""))
-                            break;
                     }
-                }
-
-                if (!string.IsNullOrEmpty(programId))
-                {
-                    Logger.InfoMarkUp($"Program Id: [cyan]{programId.EscapeMarkup()}[/]");
-                    if (!string.IsNullOrEmpty(serviceName)) Logger.InfoMarkUp($"Service Name: [cyan]{serviceName.EscapeMarkup()}[/]");
-                    if (!string.IsNullOrEmpty(serviceProvider)) Logger.InfoMarkUp($"Service Provider: [cyan]{serviceProvider.EscapeMarkup()}[/]");
-                    READ_IFO = true;
+                    if (programId != "" && (serviceName != "" || serviceProvider != ""))
+                        break;
                 }
             }
-        }
 
-        public async Task TimeCounterAsync()
-        {
-            while (!STOP_FLAG)
+            if (!string.IsNullOrEmpty(programId))
             {
-                await Task.Delay(1000);
-                RecordingDurDic[0]++;
-
-                //检测时长限制
-                if (RecordingDurDic.All(d => d.Value >= DownloaderConfig.MyOptions.LiveRecordLimit?.TotalSeconds))
-                {
-                    Logger.WarnMarkUp($"[darkorange3_1]{ResString.liveLimitReached}[/]");
-                    STOP_FLAG = true;
-                    CancellationTokenSource.Cancel();
-                }
+                Logger.InfoMarkUp($"Program Id: [cyan]{programId.EscapeMarkup()}[/]");
+                if (!string.IsNullOrEmpty(serviceName)) Logger.InfoMarkUp($"Service Name: [cyan]{serviceName.EscapeMarkup()}[/]");
+                if (!string.IsNullOrEmpty(serviceProvider)) Logger.InfoMarkUp($"Service Provider: [cyan]{serviceProvider.EscapeMarkup()}[/]");
+                READ_IFO = true;
             }
         }
-
-        public async Task<bool> StartRecordAsync()
-        {
-            ConcurrentDictionary<int, SpeedContainer> SpeedContainerDic = new(); //速度计算
-            ConcurrentDictionary<StreamSpec, bool?> Results = new();
-
-            var progress = CustomAnsiConsole.Console.Progress().AutoClear(true);
-            progress.AutoRefresh = DownloaderConfig.MyOptions.LogLevel != LogLevel.OFF;
-
-            //进度条的列定义
-            var progressColumns = new ProgressColumn[]
-            {
-                new TaskDescriptionColumn() { Alignment = Justify.Left },
-                new RecordingDurationColumn(RecordingDurDic), //时长显示
-                new RecordingSizeColumn(RecordingSizeDic), //大小显示
-                new RecordingStatusColumn(),
-                new DownloadSpeedColumn(SpeedContainerDic), //速度计算
-                new SpinnerColumn(),
-            };
-            if (DownloaderConfig.MyOptions.NoAnsiColor)
-            {
-                progressColumns = progressColumns.SkipLast(1).ToArray();
-            }
-            progress.Columns(progressColumns);
-
-            await progress.StartAsync(async ctx =>
-            {
-                //创建任务
-                var dic = SelectedSteams.Select(item =>
-                {
-                    var task = ctx.AddTask(item.ToShortString(), autoStart: false, maxValue: 0);
-                    SpeedContainerDic[task.Id] = new SpeedContainer(); //速度计算
-                    RecordingDurDic[task.Id] = 0;
-                    RecordingSizeDic[task.Id] = 0;
-                    return (item, task);
-                }).ToDictionary(item => item.item, item => item.task);
-
-                DownloaderConfig.MyOptions.LiveRecordLimit = DownloaderConfig.MyOptions.LiveRecordLimit ?? TimeSpan.MaxValue;
-                var limit = DownloaderConfig.MyOptions.LiveRecordLimit;
-                if (limit != TimeSpan.MaxValue)
-                    Logger.WarnMarkUp($"[darkorange3_1]{ResString.liveLimit}{GlobalUtil.FormatTime((int)limit.Value.TotalSeconds)}[/]");
-                //录制直播时,用户选了几个流就并发录几个
-                var options = new ParallelOptions()
-                {
-                    MaxDegreeOfParallelism = SelectedSteams.Count
-                };
-                //并发下载
-                await Parallel.ForEachAsync(dic, options, async (kp, _) =>
-                {
-                    var task = kp.Value;
-                    var consumerTask = RecordStreamAsync(kp.Key, task, SpeedContainerDic[task.Id]);
-                    Results[kp.Key] = await consumerTask;
-                });
-            });
-
-            var success = Results.Values.All(v => v == true);
-
-            return success;
-        }
     }
-}
+
+    public async Task TimeCounterAsync()
+    {
+        while (!STOP_FLAG)
+        {
+            await Task.Delay(1000);
+            RecordingDurDic[0]++;
+
+            // 检测时长限制
+            if (RecordingDurDic.All(d => d.Value >= DownloaderConfig.MyOptions.LiveRecordLimit?.TotalSeconds))
+            {
+                Logger.WarnMarkUp($"[darkorange3_1]{ResString.liveLimitReached}[/]");
+                STOP_FLAG = true;
+                CancellationTokenSource.Cancel();
+            }
+        }
+    }
+
+    public async Task<bool> StartRecordAsync()
+    {
+        ConcurrentDictionary<int, SpeedContainer> SpeedContainerDic = new(); // 速度计算
+        ConcurrentDictionary<StreamSpec, bool?> Results = new();
+
+        var progress = CustomAnsiConsole.Console.Progress().AutoClear(true);
+        progress.AutoRefresh = DownloaderConfig.MyOptions.LogLevel != LogLevel.OFF;
+
+        // 进度条的列定义
+        var progressColumns = new ProgressColumn[]
+        {
+            new TaskDescriptionColumn() { Alignment = Justify.Left },
+            new RecordingDurationColumn(RecordingDurDic), // 时长显示
+            new RecordingSizeColumn(RecordingSizeDic), // 大小显示
+            new RecordingStatusColumn(),
+            new DownloadSpeedColumn(SpeedContainerDic), // 速度计算
+            new SpinnerColumn(),
+        };
+        if (DownloaderConfig.MyOptions.NoAnsiColor)
+        {
+            progressColumns = progressColumns.SkipLast(1).ToArray();
+        }
+        progress.Columns(progressColumns);
+
+        await progress.StartAsync(async ctx =>
+        {
+            // 创建任务
+            var dic = SelectedSteams.Select(item =>
+            {
+                var task = ctx.AddTask(item.ToShortString(), autoStart: false, maxValue: 0);
+                SpeedContainerDic[task.Id] = new SpeedContainer(); // 速度计算
+                RecordingDurDic[task.Id] = 0;
+                RecordingSizeDic[task.Id] = 0;
+                return (item, task);
+            }).ToDictionary(item => item.item, item => item.task);
+
+            DownloaderConfig.MyOptions.LiveRecordLimit = DownloaderConfig.MyOptions.LiveRecordLimit ?? TimeSpan.MaxValue;
+            var limit = DownloaderConfig.MyOptions.LiveRecordLimit;
+            if (limit != TimeSpan.MaxValue)
+                Logger.WarnMarkUp($"[darkorange3_1]{ResString.liveLimit}{GlobalUtil.FormatTime((int)limit.Value.TotalSeconds)}[/]");
+            // 录制直播时,用户选了几个流就并发录几个
+            var options = new ParallelOptions()
+            {
+                MaxDegreeOfParallelism = SelectedSteams.Count
+            };
+            // 并发下载
+            await Parallel.ForEachAsync(dic, options, async (kp, _) =>
+            {
+                var task = kp.Value;
+                var consumerTask = RecordStreamAsync(kp.Key, task, SpeedContainerDic[task.Id]);
+                Results[kp.Key] = await consumerTask;
+            });
+        });
+
+        var success = Results.Values.All(v => v == true);
+
+        return success;
+    }
+}
\ No newline at end of file
diff --git a/src/N_m3u8DL-RE/DownloadManager/SimpleDownloadManager.cs b/src/N_m3u8DL-RE/DownloadManager/SimpleDownloadManager.cs
index 118b86a..abd4c17 100644
--- a/src/N_m3u8DL-RE/DownloadManager/SimpleDownloadManager.cs
+++ b/src/N_m3u8DL-RE/DownloadManager/SimpleDownloadManager.cs
@@ -14,279 +14,241 @@ using Spectre.Console;
 using System.Collections.Concurrent;
 using System.Text;
 
-namespace N_m3u8DL_RE.DownloadManager
+namespace N_m3u8DL_RE.DownloadManager;
+
+internal class SimpleDownloadManager
 {
-    internal class SimpleDownloadManager
+    IDownloader Downloader;
+    DownloaderConfig DownloaderConfig;
+    StreamExtractor StreamExtractor;
+    List<StreamSpec> SelectedSteams;
+    List<OutputFile> OutputFiles = new();
+
+    public SimpleDownloadManager(DownloaderConfig downloaderConfig, List<StreamSpec> selectedSteams, StreamExtractor streamExtractor) 
+    { 
+        this.DownloaderConfig = downloaderConfig;
+        this.SelectedSteams = selectedSteams;
+        this.StreamExtractor = streamExtractor;
+        Downloader = new SimpleDownloader(DownloaderConfig);
+    }
+
+    // 从文件读取KEY
+    private async Task SearchKeyAsync(string? currentKID)
     {
-        IDownloader Downloader;
-        DownloaderConfig DownloaderConfig;
-        StreamExtractor StreamExtractor;
-        List<StreamSpec> SelectedSteams;
-        List<OutputFile> OutputFiles = new();
+        var _key = await MP4DecryptUtil.SearchKeyFromFileAsync(DownloaderConfig.MyOptions.KeyTextFile, currentKID);
+        if (_key != null)
+        {
+            if (DownloaderConfig.MyOptions.Keys == null)
+                DownloaderConfig.MyOptions.Keys = new string[] { _key };
+            else
+                DownloaderConfig.MyOptions.Keys = DownloaderConfig.MyOptions.Keys.Concat(new string[] { _key }).ToArray();
+        }
+    }
 
-        public SimpleDownloadManager(DownloaderConfig downloaderConfig, List<StreamSpec> selectedSteams, StreamExtractor streamExtractor) 
-        { 
-            this.DownloaderConfig = downloaderConfig;
-            this.SelectedSteams = selectedSteams;
-            this.StreamExtractor = streamExtractor;
-            Downloader = new SimpleDownloader(DownloaderConfig);
+    private void ChangeSpecInfo(StreamSpec streamSpec, List<Mediainfo> mediainfos, ref bool useAACFilter)
+    {
+        if (!DownloaderConfig.MyOptions.BinaryMerge && mediainfos.Any(m => m.DolbyVison == true))
+        {
+            DownloaderConfig.MyOptions.BinaryMerge = true;
+            Logger.WarnMarkUp($"[darkorange3_1]{ResString.autoBinaryMerge2}[/]");
         }
 
-        //从文件读取KEY
-        private async Task SearchKeyAsync(string? currentKID)
+        if (DownloaderConfig.MyOptions.MuxAfterDone && mediainfos.Any(m => m.DolbyVison == true))
         {
-            var _key = await MP4DecryptUtil.SearchKeyFromFileAsync(DownloaderConfig.MyOptions.KeyTextFile, currentKID);
-            if (_key != null)
+            DownloaderConfig.MyOptions.MuxAfterDone = false;
+            Logger.WarnMarkUp($"[darkorange3_1]{ResString.autoBinaryMerge5}[/]");
+        }
+
+        if (mediainfos.Where(m => m.Type == "Audio").All(m => m.BaseInfo!.Contains("aac")))
+        {
+            useAACFilter = true;
+        }
+
+        if (mediainfos.All(m => m.Type == "Audio"))
+        {
+            streamSpec.MediaType = MediaType.AUDIO;
+        }
+        else if (mediainfos.All(m => m.Type == "Subtitle"))
+        {
+            streamSpec.MediaType = MediaType.SUBTITLES;
+            if (streamSpec.Extension == null || streamSpec.Extension == "ts")
+                streamSpec.Extension = "vtt";
+        }
+    }
+
+    private async Task<bool> DownloadStreamAsync(StreamSpec streamSpec, ProgressTask task, SpeedContainer speedContainer)
+    {
+        speedContainer.ResetVars();
+        bool useAACFilter = false; // ffmpeg合并flag
+        List<Mediainfo> mediaInfos = new();
+        ConcurrentDictionary<MediaSegment, DownloadResult?> FileDic = new();
+
+        var segments = streamSpec.Playlist?.MediaParts.SelectMany(m => m.MediaSegments);
+        if (segments == null || !segments.Any()) return false;
+        // 单分段尝试切片并行下载
+        if (segments.Count() == 1)
+        {
+            var splitSegments = await LargeSingleFileSplitUtil.SplitUrlAsync(segments.First(), DownloaderConfig.Headers);
+            if (splitSegments != null)
             {
-                if (DownloaderConfig.MyOptions.Keys == null)
-                    DownloaderConfig.MyOptions.Keys = new string[] { _key };
-                else
-                    DownloaderConfig.MyOptions.Keys = DownloaderConfig.MyOptions.Keys.Concat(new string[] { _key }).ToArray();
+                segments = splitSegments;
+                Logger.WarnMarkUp($"[darkorange3_1]{ResString.singleFileSplitWarn}[/]");
+                if (DownloaderConfig.MyOptions.MP4RealTimeDecryption)
+                {
+                    DownloaderConfig.MyOptions.MP4RealTimeDecryption = false;
+                    Logger.WarnMarkUp($"[darkorange3_1]{ResString.singleFileRealtimeDecryptWarn}[/]");
+                }
             }
+            else speedContainer.SingleSegment = true;
         }
 
-        private void ChangeSpecInfo(StreamSpec streamSpec, List<Mediainfo> mediainfos, ref bool useAACFilter)
+        var type = streamSpec.MediaType ?? Common.Enum.MediaType.VIDEO;
+        var dirName = $"{task.Id}_{OtherUtil.GetValidFileName(streamSpec.GroupId ?? "", "-")}_{streamSpec.Codecs}_{streamSpec.Bandwidth}_{streamSpec.Language}";
+        var tmpDir = Path.Combine(DownloaderConfig.DirPrefix, dirName);
+        var saveDir = DownloaderConfig.MyOptions.SaveDir ?? Environment.CurrentDirectory;
+        var saveName = DownloaderConfig.MyOptions.SaveName != null ? $"{DownloaderConfig.MyOptions.SaveName}.{streamSpec.Language}".TrimEnd('.') : dirName;
+        var headers = DownloaderConfig.Headers;
+
+        // mp4decrypt
+        var mp4decrypt = DownloaderConfig.MyOptions.DecryptionBinaryPath!;
+        var mp4InitFile = "";
+        var currentKID = "";
+        var readInfo = false; // 是否读取过
+        var mp4Info = new ParsedMP4Info();
+
+        // 用户自定义范围导致被跳过的时长 计算字幕偏移使用
+        var skippedDur = streamSpec.SkippedDuration ?? 0d;
+
+        Logger.Debug($"dirName: {dirName}; tmpDir: {tmpDir}; saveDir: {saveDir}; saveName: {saveName}");
+
+        // 创建文件夹
+        if (!Directory.Exists(tmpDir)) Directory.CreateDirectory(tmpDir);
+        if (!Directory.Exists(saveDir)) Directory.CreateDirectory(saveDir);
+
+        var totalCount = segments.Count();
+        if (streamSpec.Playlist?.MediaInit != null)
         {
-            if (!DownloaderConfig.MyOptions.BinaryMerge && mediainfos.Any(m => m.DolbyVison == true))
+            totalCount++;
+        }
+
+        task.MaxValue = totalCount;
+        task.StartTask();
+
+        // 开始下载
+        Logger.InfoMarkUp(ResString.startDownloading + streamSpec.ToShortString());
+
+        // 对于CENC,全部自动开启二进制合并
+        if (!DownloaderConfig.MyOptions.BinaryMerge && totalCount >= 1 && streamSpec.Playlist!.MediaParts.First().MediaSegments.First().EncryptInfo.Method == Common.Enum.EncryptMethod.CENC)
+        {
+            DownloaderConfig.MyOptions.BinaryMerge = true;
+            Logger.WarnMarkUp($"[darkorange3_1]{ResString.autoBinaryMerge4}[/]");
+        }
+
+        // 下载init
+        if (streamSpec.Playlist?.MediaInit != null)
+        {
+            // 对于fMP4,自动开启二进制合并
+            if (!DownloaderConfig.MyOptions.BinaryMerge && streamSpec.MediaType != MediaType.SUBTITLES)
             {
                 DownloaderConfig.MyOptions.BinaryMerge = true;
-                Logger.WarnMarkUp($"[darkorange3_1]{ResString.autoBinaryMerge2}[/]");
+                Logger.WarnMarkUp($"[darkorange3_1]{ResString.autoBinaryMerge}[/]");
             }
 
-            if (DownloaderConfig.MyOptions.MuxAfterDone && mediainfos.Any(m => m.DolbyVison == true))
+            var path = Path.Combine(tmpDir, "_init.mp4.tmp");
+            var result = await Downloader.DownloadSegmentAsync(streamSpec.Playlist.MediaInit, path, speedContainer, headers);
+            FileDic[streamSpec.Playlist.MediaInit] = result;
+            if (result == null || !result.Success)
             {
-                DownloaderConfig.MyOptions.MuxAfterDone = false;
-                Logger.WarnMarkUp($"[darkorange3_1]{ResString.autoBinaryMerge5}[/]");
+                throw new Exception("Download init file failed!");
             }
+            mp4InitFile = result.ActualFilePath;
+            task.Increment(1);
 
-            if (mediainfos.Where(m => m.Type == "Audio").All(m => m.BaseInfo!.Contains("aac")))
+            // 读取mp4信息
+            if (result != null && result.Success) 
             {
-                useAACFilter = true;
-            }
-
-            if (mediainfos.All(m => m.Type == "Audio"))
-            {
-                streamSpec.MediaType = MediaType.AUDIO;
-            }
-            else if (mediainfos.All(m => m.Type == "Subtitle"))
-            {
-                streamSpec.MediaType = MediaType.SUBTITLES;
-                if (streamSpec.Extension == null || streamSpec.Extension == "ts")
-                    streamSpec.Extension = "vtt";
+                mp4Info = MP4DecryptUtil.GetMP4Info(result.ActualFilePath);
+                currentKID = mp4Info.KID;
+                // try shaka packager, which can handle WebM
+                if (string.IsNullOrEmpty(currentKID) &&  DownloaderConfig.MyOptions.UseShakaPackager) {
+                    currentKID = MP4DecryptUtil.ReadInitShaka(result.ActualFilePath, mp4decrypt);
+                }
+                // 从文件读取KEY
+                await SearchKeyAsync(currentKID);
+                // 实时解密
+                if (DownloaderConfig.MyOptions.MP4RealTimeDecryption && !string.IsNullOrEmpty(currentKID) && StreamExtractor.ExtractorType != ExtractorType.MSS)
+                {
+                    var enc = result.ActualFilePath;
+                    var dec = Path.Combine(Path.GetDirectoryName(enc)!, Path.GetFileNameWithoutExtension(enc) + "_dec" + Path.GetExtension(enc));
+                    var dResult = await MP4DecryptUtil.DecryptAsync(DownloaderConfig.MyOptions.UseShakaPackager, mp4decrypt, DownloaderConfig.MyOptions.Keys, enc, dec, currentKID, isMultiDRM: mp4Info.isMultiDRM);
+                    if (dResult)
+                    {
+                        FileDic[streamSpec.Playlist.MediaInit]!.ActualFilePath = dec;
+                    }
+                }
+                // ffmpeg读取信息
+                if (!readInfo)
+                {
+                    Logger.WarnMarkUp(ResString.readingInfo);
+                    mediaInfos = await MediainfoUtil.ReadInfoAsync(DownloaderConfig.MyOptions.FFmpegBinaryPath!, result.ActualFilePath);
+                    mediaInfos.ForEach(info => Logger.InfoMarkUp(info.ToStringMarkUp()));
+                    ChangeSpecInfo(streamSpec, mediaInfos, ref useAACFilter);
+                    readInfo = true;
+                }
             }
         }
 
-        private async Task<bool> DownloadStreamAsync(StreamSpec streamSpec, ProgressTask task, SpeedContainer speedContainer)
+        // 计算填零个数
+        var pad = "0".PadLeft(segments.Count().ToString().Length, '0');
+
+        // 下载第一个分片
+        if (!readInfo || StreamExtractor.ExtractorType == ExtractorType.MSS)
         {
-            speedContainer.ResetVars();
-            bool useAACFilter = false; //ffmpeg合并flag
-            List<Mediainfo> mediaInfos = new();
-            ConcurrentDictionary<MediaSegment, DownloadResult?> FileDic = new();
+            var seg = segments.First();
+            segments = segments.Skip(1);
 
-            var segments = streamSpec.Playlist?.MediaParts.SelectMany(m => m.MediaSegments);
-            if (segments == null || !segments.Any()) return false;
-            //单分段尝试切片并行下载
-            if (segments.Count() == 1)
+            var index = seg.Index;
+            var path = Path.Combine(tmpDir, index.ToString(pad) + $".{streamSpec.Extension ?? "clip"}.tmp");
+            var result = await Downloader.DownloadSegmentAsync(seg, path, speedContainer, headers);
+            FileDic[seg] = result;
+            if (result == null || !result.Success)
             {
-                var splitSegments = await LargeSingleFileSplitUtil.SplitUrlAsync(segments.First(), DownloaderConfig.Headers);
-                if (splitSegments != null)
-                {
-                    segments = splitSegments;
-                    Logger.WarnMarkUp($"[darkorange3_1]{ResString.singleFileSplitWarn}[/]");
-                    if (DownloaderConfig.MyOptions.MP4RealTimeDecryption)
-                    {
-                        DownloaderConfig.MyOptions.MP4RealTimeDecryption = false;
-                        Logger.WarnMarkUp($"[darkorange3_1]{ResString.singleFileRealtimeDecryptWarn}[/]");
-                    }
-                }
-                else speedContainer.SingleSegment = true;
+                throw new Exception("Download first segment failed!");
             }
-
-            var type = streamSpec.MediaType ?? Common.Enum.MediaType.VIDEO;
-            var dirName = $"{task.Id}_{OtherUtil.GetValidFileName(streamSpec.GroupId ?? "", "-")}_{streamSpec.Codecs}_{streamSpec.Bandwidth}_{streamSpec.Language}";
-            var tmpDir = Path.Combine(DownloaderConfig.DirPrefix, dirName);
-            var saveDir = DownloaderConfig.MyOptions.SaveDir ?? Environment.CurrentDirectory;
-            var saveName = DownloaderConfig.MyOptions.SaveName != null ? $"{DownloaderConfig.MyOptions.SaveName}.{streamSpec.Language}".TrimEnd('.') : dirName;
-            var headers = DownloaderConfig.Headers;
-
-            //mp4decrypt
-            var mp4decrypt = DownloaderConfig.MyOptions.DecryptionBinaryPath!;
-            var mp4InitFile = "";
-            var currentKID = "";
-            var readInfo = false; //是否读取过
-            var mp4Info = new ParsedMP4Info();
-
-            //用户自定义范围导致被跳过的时长 计算字幕偏移使用
-            var skippedDur = streamSpec.SkippedDuration ?? 0d;
-
-            Logger.Debug($"dirName: {dirName}; tmpDir: {tmpDir}; saveDir: {saveDir}; saveName: {saveName}");
-
-            //创建文件夹
-            if (!Directory.Exists(tmpDir)) Directory.CreateDirectory(tmpDir);
-            if (!Directory.Exists(saveDir)) Directory.CreateDirectory(saveDir);
-
-            var totalCount = segments.Count();
-            if (streamSpec.Playlist?.MediaInit != null)
+            task.Increment(1);
+            if (result != null && result.Success)
             {
-                totalCount++;
-            }
-
-            task.MaxValue = totalCount;
-            task.StartTask();
-
-            //开始下载
-            Logger.InfoMarkUp(ResString.startDownloading + streamSpec.ToShortString());
-
-            //对于CENC,全部自动开启二进制合并
-            if (!DownloaderConfig.MyOptions.BinaryMerge && totalCount >= 1 && streamSpec.Playlist!.MediaParts.First().MediaSegments.First().EncryptInfo.Method == Common.Enum.EncryptMethod.CENC)
-            {
-                DownloaderConfig.MyOptions.BinaryMerge = true;
-                Logger.WarnMarkUp($"[darkorange3_1]{ResString.autoBinaryMerge4}[/]");
-            }
-
-            //下载init
-            if (streamSpec.Playlist?.MediaInit != null)
-            {
-                //对于fMP4,自动开启二进制合并
-                if (!DownloaderConfig.MyOptions.BinaryMerge && streamSpec.MediaType != MediaType.SUBTITLES)
+                // 修复MSS init
+                if (StreamExtractor.ExtractorType == ExtractorType.MSS)
                 {
-                    DownloaderConfig.MyOptions.BinaryMerge = true;
-                    Logger.WarnMarkUp($"[darkorange3_1]{ResString.autoBinaryMerge}[/]");
-                }
-
-                var path = Path.Combine(tmpDir, "_init.mp4.tmp");
-                var result = await Downloader.DownloadSegmentAsync(streamSpec.Playlist.MediaInit, path, speedContainer, headers);
-                FileDic[streamSpec.Playlist.MediaInit] = result;
-                if (result == null || !result.Success)
-                {
-                    throw new Exception("Download init file failed!");
-                }
-                mp4InitFile = result.ActualFilePath;
-                task.Increment(1);
-
-                //读取mp4信息
-                if (result != null && result.Success) 
-                {
-                    mp4Info = MP4DecryptUtil.GetMP4Info(result.ActualFilePath);
-                    currentKID = mp4Info.KID;
-                    // try shaka packager, which can handle WebM
-                    if (string.IsNullOrEmpty(currentKID) &&  DownloaderConfig.MyOptions.UseShakaPackager) {
-                        currentKID = MP4DecryptUtil.ReadInitShaka(result.ActualFilePath, mp4decrypt);
-                    }
-                    //从文件读取KEY
-                    await SearchKeyAsync(currentKID);
-                    //实时解密
-                    if (DownloaderConfig.MyOptions.MP4RealTimeDecryption && !string.IsNullOrEmpty(currentKID) && StreamExtractor.ExtractorType != ExtractorType.MSS)
-                    {
-                        var enc = result.ActualFilePath;
-                        var dec = Path.Combine(Path.GetDirectoryName(enc)!, Path.GetFileNameWithoutExtension(enc) + "_dec" + Path.GetExtension(enc));
-                        var dResult = await MP4DecryptUtil.DecryptAsync(DownloaderConfig.MyOptions.UseShakaPackager, mp4decrypt, DownloaderConfig.MyOptions.Keys, enc, dec, currentKID, isMultiDRM: mp4Info.isMultiDRM);
-                        if (dResult)
-                        {
-                            FileDic[streamSpec.Playlist.MediaInit]!.ActualFilePath = dec;
-                        }
-                    }
-                    //ffmpeg读取信息
-                    if (!readInfo)
-                    {
-                        Logger.WarnMarkUp(ResString.readingInfo);
-                        mediaInfos = await MediainfoUtil.ReadInfoAsync(DownloaderConfig.MyOptions.FFmpegBinaryPath!, result.ActualFilePath);
-                        mediaInfos.ForEach(info => Logger.InfoMarkUp(info.ToStringMarkUp()));
-                        ChangeSpecInfo(streamSpec, mediaInfos, ref useAACFilter);
-                        readInfo = true;
-                    }
-                }
-            }
-
-            //计算填零个数
-            var pad = "0".PadLeft(segments.Count().ToString().Length, '0');
-
-            //下载第一个分片
-            if (!readInfo || StreamExtractor.ExtractorType == ExtractorType.MSS)
-            {
-                var seg = segments.First();
-                segments = segments.Skip(1);
-
-                var index = seg.Index;
-                var path = Path.Combine(tmpDir, index.ToString(pad) + $".{streamSpec.Extension ?? "clip"}.tmp");
-                var result = await Downloader.DownloadSegmentAsync(seg, path, speedContainer, headers);
-                FileDic[seg] = result;
-                if (result == null || !result.Success)
-                {
-                    throw new Exception("Download first segment failed!");
-                }
-                task.Increment(1);
-                if (result != null && result.Success)
-                {
-                    //修复MSS init
-                    if (StreamExtractor.ExtractorType == ExtractorType.MSS)
-                    {
-                        var processor = new MSSMoovProcessor(streamSpec);
-                        var header = processor.GenHeader(File.ReadAllBytes(result.ActualFilePath));
-                        await File.WriteAllBytesAsync(FileDic[streamSpec.Playlist!.MediaInit!]!.ActualFilePath, header);
-                        if (DownloaderConfig.MyOptions.MP4RealTimeDecryption && !string.IsNullOrEmpty(currentKID))
-                        {
-                            //需要重新解密init
-                            var enc = FileDic[streamSpec.Playlist!.MediaInit!]!.ActualFilePath;
-                            var dec = Path.Combine(Path.GetDirectoryName(enc)!, Path.GetFileNameWithoutExtension(enc) + "_dec" + Path.GetExtension(enc));
-                            var dResult = await MP4DecryptUtil.DecryptAsync(DownloaderConfig.MyOptions.UseShakaPackager, mp4decrypt, DownloaderConfig.MyOptions.Keys, enc, dec, currentKID);
-                            if (dResult)
-                            {
-                                FileDic[streamSpec.Playlist!.MediaInit!]!.ActualFilePath = dec;
-                            }
-                        }
-                    }
-                    //读取init信息
-                    if (string.IsNullOrEmpty(currentKID))
-                    {
-                        currentKID = MP4DecryptUtil.GetMP4Info(result.ActualFilePath).KID;
-                    }
-                    // try shaka packager, which can handle WebM
-                    if (string.IsNullOrEmpty(currentKID) &&  DownloaderConfig.MyOptions.UseShakaPackager) {
-                        currentKID = MP4DecryptUtil.ReadInitShaka(result.ActualFilePath, mp4decrypt);
-                    }
-                    //从文件读取KEY
-                    await SearchKeyAsync(currentKID);
-                    //实时解密
+                    var processor = new MSSMoovProcessor(streamSpec);
+                    var header = processor.GenHeader(File.ReadAllBytes(result.ActualFilePath));
+                    await File.WriteAllBytesAsync(FileDic[streamSpec.Playlist!.MediaInit!]!.ActualFilePath, header);
                     if (DownloaderConfig.MyOptions.MP4RealTimeDecryption && !string.IsNullOrEmpty(currentKID))
                     {
-                        var enc = result.ActualFilePath;
+                        // 需要重新解密init
+                        var enc = FileDic[streamSpec.Playlist!.MediaInit!]!.ActualFilePath;
                         var dec = Path.Combine(Path.GetDirectoryName(enc)!, Path.GetFileNameWithoutExtension(enc) + "_dec" + Path.GetExtension(enc));
-                        mp4Info = MP4DecryptUtil.GetMP4Info(enc);
-                        var dResult = await MP4DecryptUtil.DecryptAsync(DownloaderConfig.MyOptions.UseShakaPackager, mp4decrypt, DownloaderConfig.MyOptions.Keys, enc, dec, currentKID, mp4InitFile, isMultiDRM: mp4Info.isMultiDRM);
+                        var dResult = await MP4DecryptUtil.DecryptAsync(DownloaderConfig.MyOptions.UseShakaPackager, mp4decrypt, DownloaderConfig.MyOptions.Keys, enc, dec, currentKID);
                         if (dResult)
                         {
-                            File.Delete(enc);
-                            result.ActualFilePath = dec;
+                            FileDic[streamSpec.Playlist!.MediaInit!]!.ActualFilePath = dec;
                         }
                     }
-                    if (!readInfo)
-                    {
-                        //ffmpeg读取信息
-                        Logger.WarnMarkUp(ResString.readingInfo);
-                        mediaInfos = await MediainfoUtil.ReadInfoAsync(DownloaderConfig.MyOptions.FFmpegBinaryPath!, result!.ActualFilePath);
-                        mediaInfos.ForEach(info => Logger.InfoMarkUp(info.ToStringMarkUp()));
-                        ChangeSpecInfo(streamSpec, mediaInfos, ref useAACFilter);
-                        readInfo = true;
-                    }
                 }
-            }
-
-            //开始下载
-            var options = new ParallelOptions()
-            {
-                MaxDegreeOfParallelism = DownloaderConfig.MyOptions.ThreadCount
-            };
-            await Parallel.ForEachAsync(segments, options, async (seg, _) =>
-            {
-                var index = seg.Index;
-                var path = Path.Combine(tmpDir, index.ToString(pad) + $".{streamSpec.Extension ?? "clip"}.tmp");
-                var result = await Downloader.DownloadSegmentAsync(seg, path, speedContainer, headers);
-                FileDic[seg] = result;
-                if (result != null && result.Success)
-                    task.Increment(1);
-                //实时解密
-                if (DownloaderConfig.MyOptions.MP4RealTimeDecryption && result != null && result.Success && !string.IsNullOrEmpty(currentKID)) 
+                // 读取init信息
+                if (string.IsNullOrEmpty(currentKID))
+                {
+                    currentKID = MP4DecryptUtil.GetMP4Info(result.ActualFilePath).KID;
+                }
+                // try shaka packager, which can handle WebM
+                if (string.IsNullOrEmpty(currentKID) &&  DownloaderConfig.MyOptions.UseShakaPackager) {
+                    currentKID = MP4DecryptUtil.ReadInitShaka(result.ActualFilePath, mp4decrypt);
+                }
+                // 从文件读取KEY
+                await SearchKeyAsync(currentKID);
+                // 实时解密
+                if (DownloaderConfig.MyOptions.MP4RealTimeDecryption && !string.IsNullOrEmpty(currentKID))
                 {
                     var enc = result.ActualFilePath;
                     var dec = Path.Combine(Path.GetDirectoryName(enc)!, Path.GetFileNameWithoutExtension(enc) + "_dec" + Path.GetExtension(enc));
@@ -298,467 +260,504 @@ namespace N_m3u8DL_RE.DownloadManager
                         result.ActualFilePath = dec;
                     }
                 }
-            });
-
-            //修改输出后缀
-            var outputExt = "." + streamSpec.Extension;
-            if (streamSpec.Extension == null) outputExt = ".ts";
-            else if (streamSpec.MediaType == MediaType.AUDIO && (streamSpec.Extension == "m4s" || streamSpec.Extension == "mp4")) outputExt = ".m4a";
-            else if (streamSpec.MediaType != MediaType.SUBTITLES && (streamSpec.Extension == "m4s" || streamSpec.Extension == "mp4")) outputExt = ".mp4";
-
-            if (DownloaderConfig.MyOptions.AutoSubtitleFix && streamSpec.MediaType == MediaType.SUBTITLES)
-            {
-                if (DownloaderConfig.MyOptions.SubtitleFormat == Enum.SubtitleFormat.SRT) outputExt = ".srt";
-                else outputExt = ".vtt";
-            }
-            var output = Path.Combine(saveDir, saveName + outputExt);
-
-            //检测目标文件是否存在
-            while (File.Exists(output))
-            {
-                Logger.WarnMarkUp($"{Path.GetFileName(output)} => {Path.GetFileName(output = Path.ChangeExtension(output, $"copy" + Path.GetExtension(output)))}");
-            }
-
-            if (!string.IsNullOrEmpty(currentKID) && DownloaderConfig.MyOptions.MP4RealTimeDecryption && DownloaderConfig.MyOptions.Keys != null && DownloaderConfig.MyOptions.Keys.Length > 0 && mp4InitFile != "")
-            {
-                File.Delete(mp4InitFile);
-                //shaka实时解密不需要init文件用于合并
-                if (DownloaderConfig.MyOptions.UseShakaPackager)
+                if (!readInfo)
                 {
-                    FileDic!.Remove(streamSpec.Playlist!.MediaInit, out _);
+                    // ffmpeg读取信息
+                    Logger.WarnMarkUp(ResString.readingInfo);
+                    mediaInfos = await MediainfoUtil.ReadInfoAsync(DownloaderConfig.MyOptions.FFmpegBinaryPath!, result!.ActualFilePath);
+                    mediaInfos.ForEach(info => Logger.InfoMarkUp(info.ToStringMarkUp()));
+                    ChangeSpecInfo(streamSpec, mediaInfos, ref useAACFilter);
+                    readInfo = true;
                 }
             }
+        }
 
-            //校验分片数量
-            if (DownloaderConfig.MyOptions.CheckSegmentsCount && FileDic.Values.Any(s => s == null))
+        // 开始下载
+        var options = new ParallelOptions()
+        {
+            MaxDegreeOfParallelism = DownloaderConfig.MyOptions.ThreadCount
+        };
+        await Parallel.ForEachAsync(segments, options, async (seg, _) =>
+        {
+            var index = seg.Index;
+            var path = Path.Combine(tmpDir, index.ToString(pad) + $".{streamSpec.Extension ?? "clip"}.tmp");
+            var result = await Downloader.DownloadSegmentAsync(seg, path, speedContainer, headers);
+            FileDic[seg] = result;
+            if (result != null && result.Success)
+                task.Increment(1);
+            // 实时解密
+            if (DownloaderConfig.MyOptions.MP4RealTimeDecryption && result != null && result.Success && !string.IsNullOrEmpty(currentKID)) 
             {
-                Logger.ErrorMarkUp(ResString.segmentCountCheckNotPass, totalCount, FileDic.Values.Where(s => s != null).Count());
-                return false;
-            }
-
-            //移除无效片段
-            var badKeys = FileDic.Where(i => i.Value == null).Select(i => i.Key);
-            foreach (var badKey in badKeys)
-            {
-                FileDic!.Remove(badKey, out _);
-            }
-
-            //校验完整性
-            if (DownloaderConfig.CheckContentLength && FileDic.Values.Any(a => a!.Success == false)) 
-            {
-                return false;
-            }
-
-            //自动修复VTT raw字幕
-            if (DownloaderConfig.MyOptions.AutoSubtitleFix && streamSpec.MediaType == Common.Enum.MediaType.SUBTITLES 
-                && streamSpec.Extension != null && streamSpec.Extension.Contains("vtt")) 
-            {
-                Logger.WarnMarkUp(ResString.fixingVTT);
-                //排序字幕并修正时间戳
-                bool first = true;
-                var finalVtt = new WebVttSub();
-                var keys = FileDic.Keys.OrderBy(k => k.Index);
-                foreach (var seg in keys)
-                {
-                    var vttContent = File.ReadAllText(FileDic[seg]!.ActualFilePath);
-                    var vtt = WebVttSub.Parse(vttContent);
-                    //手动计算MPEGTS
-                    if (finalVtt.MpegtsTimestamp == 0 && vtt.MpegtsTimestamp == 0)
-                    {
-                        vtt.MpegtsTimestamp = 90000 * (long)(skippedDur + keys.Where(s => s.Index < seg.Index).Sum(s => s.Duration));
-                    }
-                    if (first) { finalVtt = vtt; first = false; }
-                    else finalVtt.AddCuesFromOne(vtt);
-                }
-                //写出字幕
-                var files = FileDic.OrderBy(s => s.Key.Index).Select(s => s.Value).Select(v => v!.ActualFilePath).ToArray();
-                foreach (var item in files) File.Delete(item);
-                FileDic.Clear();
-                var index = 0;
-                var path = Path.Combine(tmpDir, index.ToString(pad) + ".fix.vtt");
-                //设置字幕偏移
-                finalVtt.LeftShiftTime(TimeSpan.FromSeconds(skippedDur));
-                var subContentFixed = finalVtt.ToVtt();
-                //转换字幕格式
-                if (DownloaderConfig.MyOptions.SubtitleFormat != Enum.SubtitleFormat.VTT)
-                {
-                    path = Path.ChangeExtension(path, ".srt");
-                    subContentFixed = finalVtt.ToSrt();
-                }
-                await File.WriteAllTextAsync(path, subContentFixed, Encoding.UTF8);
-                FileDic[keys.First()] = new DownloadResult()
-                {
-                    ActualContentLength = subContentFixed.Length,
-                    ActualFilePath = path
-                };
-            }
-
-            //自动修复VTT mp4字幕
-            if (DownloaderConfig.MyOptions.AutoSubtitleFix && streamSpec.MediaType == Common.Enum.MediaType.SUBTITLES
-                && streamSpec.Codecs != "stpp" && streamSpec.Extension != null && streamSpec.Extension.Contains("m4s"))
-            {
-                var initFile = FileDic.Values.Where(v => Path.GetFileName(v!.ActualFilePath).StartsWith("_init")).FirstOrDefault();
-                var iniFileBytes = File.ReadAllBytes(initFile!.ActualFilePath);
-                var (sawVtt, timescale) = MP4VttUtil.CheckInit(iniFileBytes);
-                if (sawVtt)
-                {
-                    Logger.WarnMarkUp(ResString.fixingVTTmp4);
-                    var mp4s = FileDic.OrderBy(s => s.Key.Index).Select(s => s.Value).Select(v => v!.ActualFilePath).Where(p => p.EndsWith(".m4s")).ToArray();
-                    var finalVtt = MP4VttUtil.ExtractSub(mp4s, timescale);
-                    //写出字幕
-                    var firstKey = FileDic.Keys.First();
-                    var files = FileDic.OrderBy(s => s.Key.Index).Select(s => s.Value).Select(v => v!.ActualFilePath).ToArray();
-                    foreach (var item in files) File.Delete(item);
-                    FileDic.Clear();
-                    var index = 0;
-                    var path = Path.Combine(tmpDir, index.ToString(pad) + ".fix.vtt");
-                    //设置字幕偏移
-                    finalVtt.LeftShiftTime(TimeSpan.FromSeconds(skippedDur));
-                    var subContentFixed = finalVtt.ToVtt();
-                    //转换字幕格式
-                    if (DownloaderConfig.MyOptions.SubtitleFormat != Enum.SubtitleFormat.VTT)
-                    {
-                        path = Path.ChangeExtension(path, ".srt");
-                        subContentFixed = finalVtt.ToSrt();
-                    }
-                    await File.WriteAllTextAsync(path, subContentFixed, Encoding.UTF8);
-                    FileDic[firstKey] = new DownloadResult()
-                    {
-                        ActualContentLength = subContentFixed.Length,
-                        ActualFilePath = path
-                    };
-                }
-            }
-
-            //自动修复TTML raw字幕
-            if (DownloaderConfig.MyOptions.AutoSubtitleFix && streamSpec.MediaType == Common.Enum.MediaType.SUBTITLES
-                && streamSpec.Extension != null && streamSpec.Extension.Contains("ttml"))
-            {
-                Logger.WarnMarkUp(ResString.fixingTTML);
-                var first = true;
-                var finalVtt = new WebVttSub();
-                var keys = FileDic.OrderBy(s => s.Key.Index).Select(s => s.Key);
-                foreach (var seg in keys)
-                {
-                    var vtt = MP4TtmlUtil.ExtractFromTTML(FileDic[seg]!.ActualFilePath, 0);
-                    //手动计算MPEGTS
-                    if (finalVtt.MpegtsTimestamp == 0 && vtt.MpegtsTimestamp == 0)
-                    {
-                        vtt.MpegtsTimestamp = 90000 * (long)(skippedDur + keys.Where(s => s.Index < seg.Index).Sum(s => s.Duration));
-                    }
-                    if (first) { finalVtt = vtt; first = false; }
-                    else finalVtt.AddCuesFromOne(vtt);
-                }
-                //写出字幕
-                var firstKey = FileDic.Keys.First();
-                var files = FileDic.OrderBy(s => s.Key.Index).Select(s => s.Value).Select(v => v!.ActualFilePath).ToArray();
-
-                //处理图形字幕
-                await SubtitleUtil.TryWriteImagePngsAsync(finalVtt, tmpDir);
-
-                foreach (var item in files) File.Delete(item);
-                FileDic.Clear();
-                var index = 0;
-                var path = Path.Combine(tmpDir, index.ToString(pad) + ".fix.vtt");
-                //设置字幕偏移
-                finalVtt.LeftShiftTime(TimeSpan.FromSeconds(skippedDur));
-                var subContentFixed = finalVtt.ToVtt();
-                //转换字幕格式
-                if (DownloaderConfig.MyOptions.SubtitleFormat != Enum.SubtitleFormat.VTT)
-                {
-                    path = Path.ChangeExtension(path, ".srt");
-                    subContentFixed = finalVtt.ToSrt();
-                }
-                await File.WriteAllTextAsync(path, subContentFixed, Encoding.UTF8);
-                FileDic[firstKey] = new DownloadResult()
-                {
-                    ActualContentLength = subContentFixed.Length,
-                    ActualFilePath = path
-                };
-            }
-
-            //自动修复TTML mp4字幕
-            if (DownloaderConfig.MyOptions.AutoSubtitleFix && streamSpec.MediaType == Common.Enum.MediaType.SUBTITLES
-                && streamSpec.Extension != null && streamSpec.Extension.Contains("m4s")
-                && streamSpec.Codecs != null && streamSpec.Codecs.Contains("stpp")) 
-            {
-                Logger.WarnMarkUp(ResString.fixingTTMLmp4);
-                //sawTtml暂时不判断
-                //var initFile = FileDic.Values.Where(v => Path.GetFileName(v!.ActualFilePath).StartsWith("_init")).FirstOrDefault();
-                //var iniFileBytes = File.ReadAllBytes(initFile!.ActualFilePath);
-                //var sawTtml = MP4TtmlUtil.CheckInit(iniFileBytes);
-                var first = true;
-                var finalVtt = new WebVttSub();
-                var keys = FileDic.OrderBy(s => s.Key.Index).Where(v => v.Value!.ActualFilePath.EndsWith(".m4s")).Select(s => s.Key);
-                foreach (var seg in keys)
-                {
-                    var vtt = MP4TtmlUtil.ExtractFromMp4(FileDic[seg]!.ActualFilePath, 0);
-                    //手动计算MPEGTS
-                    if (finalVtt.MpegtsTimestamp == 0 && vtt.MpegtsTimestamp == 0)
-                    {
-                        vtt.MpegtsTimestamp = 90000 * (long)(skippedDur + keys.Where(s => s.Index < seg.Index).Sum(s => s.Duration));
-                    }
-                    if (first) { finalVtt = vtt; first = false; }
-                    else finalVtt.AddCuesFromOne(vtt);
-                }
-
-                //写出字幕
-                var firstKey = FileDic.Keys.First();
-                var files = FileDic.OrderBy(s => s.Key.Index).Select(s => s.Value).Select(v => v!.ActualFilePath).ToArray();
-
-                //处理图形字幕
-                await SubtitleUtil.TryWriteImagePngsAsync(finalVtt, tmpDir);
-
-                foreach (var item in files) File.Delete(item);
-                FileDic.Clear();
-                var index = 0;
-                var path = Path.Combine(tmpDir, index.ToString(pad) + ".fix.vtt");
-                //设置字幕偏移
-                finalVtt.LeftShiftTime(TimeSpan.FromSeconds(skippedDur));
-                var subContentFixed = finalVtt.ToVtt();
-                //转换字幕格式
-                if (DownloaderConfig.MyOptions.SubtitleFormat != Enum.SubtitleFormat.VTT)
-                {
-                    path = Path.ChangeExtension(path, ".srt");
-                    subContentFixed = finalVtt.ToSrt();
-                }
-                await File.WriteAllTextAsync(path, subContentFixed, Encoding.UTF8);
-                FileDic[firstKey] = new DownloadResult()
-                {
-                    ActualContentLength = subContentFixed.Length,
-                    ActualFilePath = path
-                };
-            }
-
-            bool mergeSuccess = false;
-            //合并
-            if (!DownloaderConfig.MyOptions.SkipMerge)
-            {
-                //字幕也使用二进制合并
-                if (DownloaderConfig.MyOptions.BinaryMerge || streamSpec.MediaType == MediaType.SUBTITLES)
-                {
-                    Logger.InfoMarkUp(ResString.binaryMerge);
-                    var files = FileDic.OrderBy(s => s.Key.Index).Select(s => s.Value).Select(v => v!.ActualFilePath).ToArray();
-                    MergeUtil.CombineMultipleFilesIntoSingleFile(files, output);
-                    mergeSuccess = true;
-                }
-                else
-                {
-                    //ffmpeg合并
-                    var files = FileDic.OrderBy(s => s.Key.Index).Select(s => s.Value).Select(v => v!.ActualFilePath).ToArray();
-                    Logger.InfoMarkUp(ResString.ffmpegMerge);
-                    var ext = streamSpec.MediaType == MediaType.AUDIO ? "m4a" : "mp4";
-                    var ffOut = Path.Combine(Path.GetDirectoryName(output)!, Path.GetFileNameWithoutExtension(output) + $".{ext}");
-                    //检测目标文件是否存在
-                    while (File.Exists(ffOut))
-                    {
-                        Logger.WarnMarkUp($"{Path.GetFileName(ffOut)} => {Path.GetFileName(ffOut = Path.ChangeExtension(ffOut, $"copy" + Path.GetExtension(ffOut)))}");
-                    }
-                    //大于1800分片,需要分步骤合并
-                    if (files.Length >= 1800)
-                    {
-                        Logger.WarnMarkUp(ResString.partMerge);
-                        files = MergeUtil.PartialCombineMultipleFiles(files);
-                        FileDic.Clear();
-                        foreach (var item in files)
-                        {
-                            FileDic[new MediaSegment() { Url = item }] = new DownloadResult()
-                            {
-                                ActualFilePath = item
-                            };
-                        }
-                    }
-                    mergeSuccess = MergeUtil.MergeByFFmpeg(DownloaderConfig.MyOptions.FFmpegBinaryPath!, files, Path.ChangeExtension(ffOut, null), ext, useAACFilter, writeDate: !DownloaderConfig.MyOptions.NoDateInfo, useConcatDemuxer: DownloaderConfig.MyOptions.UseFFmpegConcatDemuxer);
-                    if (mergeSuccess) output = ffOut;
-                }
-            }
-
-            //删除临时文件夹
-            if (!DownloaderConfig.MyOptions.SkipMerge && DownloaderConfig.MyOptions.DelAfterDone && mergeSuccess)
-            {
-                var files = FileDic.Values.Select(v => v!.ActualFilePath);
-                foreach (var file in files)
-                {
-                    File.Delete(file);
-                }
-                OtherUtil.SafeDeleteDir(tmpDir);
-            }
-
-            //重新读取init信息
-            if (mergeSuccess && totalCount >= 1 && string.IsNullOrEmpty(currentKID) && streamSpec.Playlist!.MediaParts.First().MediaSegments.First().EncryptInfo.Method != Common.Enum.EncryptMethod.NONE)
-            {
-                currentKID = MP4DecryptUtil.GetMP4Info(output).KID;
-                // try shaka packager, which can handle WebM
-                if (string.IsNullOrEmpty(currentKID) &&  DownloaderConfig.MyOptions.UseShakaPackager) {
-                    currentKID = MP4DecryptUtil.ReadInitShaka(output, mp4decrypt);
-                }
-                //从文件读取KEY
-                await SearchKeyAsync(currentKID);
-            }
-
-            //调用mp4decrypt解密
-             if (mergeSuccess && File.Exists(output) && !string.IsNullOrEmpty(currentKID) && !DownloaderConfig.MyOptions.MP4RealTimeDecryption && DownloaderConfig.MyOptions.Keys != null && DownloaderConfig.MyOptions.Keys.Length > 0)
-            {
-                var enc = output;
+                var enc = result.ActualFilePath;
                 var dec = Path.Combine(Path.GetDirectoryName(enc)!, Path.GetFileNameWithoutExtension(enc) + "_dec" + Path.GetExtension(enc));
                 mp4Info = MP4DecryptUtil.GetMP4Info(enc);
-                Logger.InfoMarkUp($"[grey]Decrypting...[/]");
-                var result = await MP4DecryptUtil.DecryptAsync(DownloaderConfig.MyOptions.UseShakaPackager, mp4decrypt, DownloaderConfig.MyOptions.Keys, enc, dec, currentKID, isMultiDRM: mp4Info.isMultiDRM);
-                if (result)
+                var dResult = await MP4DecryptUtil.DecryptAsync(DownloaderConfig.MyOptions.UseShakaPackager, mp4decrypt, DownloaderConfig.MyOptions.Keys, enc, dec, currentKID, mp4InitFile, isMultiDRM: mp4Info.isMultiDRM);
+                if (dResult)
                 {
                     File.Delete(enc);
-                    File.Move(dec, enc);
+                    result.ActualFilePath = dec;
                 }
             }
+        });
 
-            //记录所有文件信息
-            if (File.Exists(output))
+        // 修改输出后缀
+        var outputExt = "." + streamSpec.Extension;
+        if (streamSpec.Extension == null) outputExt = ".ts";
+        else if (streamSpec.MediaType == MediaType.AUDIO && (streamSpec.Extension == "m4s" || streamSpec.Extension == "mp4")) outputExt = ".m4a";
+        else if (streamSpec.MediaType != MediaType.SUBTITLES && (streamSpec.Extension == "m4s" || streamSpec.Extension == "mp4")) outputExt = ".mp4";
+
+        if (DownloaderConfig.MyOptions.AutoSubtitleFix && streamSpec.MediaType == MediaType.SUBTITLES)
+        {
+            if (DownloaderConfig.MyOptions.SubtitleFormat == Enum.SubtitleFormat.SRT) outputExt = ".srt";
+            else outputExt = ".vtt";
+        }
+        var output = Path.Combine(saveDir, saveName + outputExt);
+
+        // 检测目标文件是否存在
+        while (File.Exists(output))
+        {
+            Logger.WarnMarkUp($"{Path.GetFileName(output)} => {Path.GetFileName(output = Path.ChangeExtension(output, $"copy" + Path.GetExtension(output)))}");
+        }
+
+        if (!string.IsNullOrEmpty(currentKID) && DownloaderConfig.MyOptions.MP4RealTimeDecryption && DownloaderConfig.MyOptions.Keys != null && DownloaderConfig.MyOptions.Keys.Length > 0 && mp4InitFile != "")
+        {
+            File.Delete(mp4InitFile);
+            // shaka实时解密不需要init文件用于合并
+            if (DownloaderConfig.MyOptions.UseShakaPackager)
             {
-                OutputFiles.Add(new OutputFile()
+                FileDic!.Remove(streamSpec.Playlist!.MediaInit, out _);
+            }
+        }
+
+        // 校验分片数量
+        if (DownloaderConfig.MyOptions.CheckSegmentsCount && FileDic.Values.Any(s => s == null))
+        {
+            Logger.ErrorMarkUp(ResString.segmentCountCheckNotPass, totalCount, FileDic.Values.Where(s => s != null).Count());
+            return false;
+        }
+
+        // 移除无效片段
+        var badKeys = FileDic.Where(i => i.Value == null).Select(i => i.Key);
+        foreach (var badKey in badKeys)
+        {
+            FileDic!.Remove(badKey, out _);
+        }
+
+        // 校验完整性
+        if (DownloaderConfig.CheckContentLength && FileDic.Values.Any(a => a!.Success == false)) 
+        {
+            return false;
+        }
+
+        // 自动修复VTT raw字幕
+        if (DownloaderConfig.MyOptions.AutoSubtitleFix && streamSpec.MediaType == Common.Enum.MediaType.SUBTITLES 
+                                                       && streamSpec.Extension != null && streamSpec.Extension.Contains("vtt")) 
+        {
+            Logger.WarnMarkUp(ResString.fixingVTT);
+            // 排序字幕并修正时间戳
+            bool first = true;
+            var finalVtt = new WebVttSub();
+            var keys = FileDic.Keys.OrderBy(k => k.Index);
+            foreach (var seg in keys)
+            {
+                var vttContent = File.ReadAllText(FileDic[seg]!.ActualFilePath);
+                var vtt = WebVttSub.Parse(vttContent);
+                // 手动计算MPEGTS
+                if (finalVtt.MpegtsTimestamp == 0 && vtt.MpegtsTimestamp == 0)
                 {
-                    Index = task.Id,
-                    FilePath = output,
-                    LangCode = streamSpec.Language,
-                    Description = streamSpec.Name,
-                    Mediainfos = mediaInfos,
-                    MediaType = streamSpec.MediaType,
+                    vtt.MpegtsTimestamp = 90000 * (long)(skippedDur + keys.Where(s => s.Index < seg.Index).Sum(s => s.Duration));
+                }
+                if (first) { finalVtt = vtt; first = false; }
+                else finalVtt.AddCuesFromOne(vtt);
+            }
+            // 写出字幕
+            var files = FileDic.OrderBy(s => s.Key.Index).Select(s => s.Value).Select(v => v!.ActualFilePath).ToArray();
+            foreach (var item in files) File.Delete(item);
+            FileDic.Clear();
+            var index = 0;
+            var path = Path.Combine(tmpDir, index.ToString(pad) + ".fix.vtt");
+            // 设置字幕偏移
+            finalVtt.LeftShiftTime(TimeSpan.FromSeconds(skippedDur));
+            var subContentFixed = finalVtt.ToVtt();
+            // 转换字幕格式
+            if (DownloaderConfig.MyOptions.SubtitleFormat != Enum.SubtitleFormat.VTT)
+            {
+                path = Path.ChangeExtension(path, ".srt");
+                subContentFixed = finalVtt.ToSrt();
+            }
+            await File.WriteAllTextAsync(path, subContentFixed, Encoding.UTF8);
+            FileDic[keys.First()] = new DownloadResult()
+            {
+                ActualContentLength = subContentFixed.Length,
+                ActualFilePath = path
+            };
+        }
+
+        // 自动修复VTT mp4字幕
+        if (DownloaderConfig.MyOptions.AutoSubtitleFix && streamSpec.MediaType == Common.Enum.MediaType.SUBTITLES
+                                                       && streamSpec.Codecs != "stpp" && streamSpec.Extension != null && streamSpec.Extension.Contains("m4s"))
+        {
+            var initFile = FileDic.Values.Where(v => Path.GetFileName(v!.ActualFilePath).StartsWith("_init")).FirstOrDefault();
+            var iniFileBytes = File.ReadAllBytes(initFile!.ActualFilePath);
+            var (sawVtt, timescale) = MP4VttUtil.CheckInit(iniFileBytes);
+            if (sawVtt)
+            {
+                Logger.WarnMarkUp(ResString.fixingVTTmp4);
+                var mp4s = FileDic.OrderBy(s => s.Key.Index).Select(s => s.Value).Select(v => v!.ActualFilePath).Where(p => p.EndsWith(".m4s")).ToArray();
+                var finalVtt = MP4VttUtil.ExtractSub(mp4s, timescale);
+                // 写出字幕
+                var firstKey = FileDic.Keys.First();
+                var files = FileDic.OrderBy(s => s.Key.Index).Select(s => s.Value).Select(v => v!.ActualFilePath).ToArray();
+                foreach (var item in files) File.Delete(item);
+                FileDic.Clear();
+                var index = 0;
+                var path = Path.Combine(tmpDir, index.ToString(pad) + ".fix.vtt");
+                // 设置字幕偏移
+                finalVtt.LeftShiftTime(TimeSpan.FromSeconds(skippedDur));
+                var subContentFixed = finalVtt.ToVtt();
+                // 转换字幕格式
+                if (DownloaderConfig.MyOptions.SubtitleFormat != Enum.SubtitleFormat.VTT)
+                {
+                    path = Path.ChangeExtension(path, ".srt");
+                    subContentFixed = finalVtt.ToSrt();
+                }
+                await File.WriteAllTextAsync(path, subContentFixed, Encoding.UTF8);
+                FileDic[firstKey] = new DownloadResult()
+                {
+                    ActualContentLength = subContentFixed.Length,
+                    ActualFilePath = path
+                };
+            }
+        }
+
+        // 自动修复TTML raw字幕
+        if (DownloaderConfig.MyOptions.AutoSubtitleFix && streamSpec.MediaType == Common.Enum.MediaType.SUBTITLES
+                                                       && streamSpec.Extension != null && streamSpec.Extension.Contains("ttml"))
+        {
+            Logger.WarnMarkUp(ResString.fixingTTML);
+            var first = true;
+            var finalVtt = new WebVttSub();
+            var keys = FileDic.OrderBy(s => s.Key.Index).Select(s => s.Key);
+            foreach (var seg in keys)
+            {
+                var vtt = MP4TtmlUtil.ExtractFromTTML(FileDic[seg]!.ActualFilePath, 0);
+                // 手动计算MPEGTS
+                if (finalVtt.MpegtsTimestamp == 0 && vtt.MpegtsTimestamp == 0)
+                {
+                    vtt.MpegtsTimestamp = 90000 * (long)(skippedDur + keys.Where(s => s.Index < seg.Index).Sum(s => s.Duration));
+                }
+                if (first) { finalVtt = vtt; first = false; }
+                else finalVtt.AddCuesFromOne(vtt);
+            }
+            // 写出字幕
+            var firstKey = FileDic.Keys.First();
+            var files = FileDic.OrderBy(s => s.Key.Index).Select(s => s.Value).Select(v => v!.ActualFilePath).ToArray();
+
+            // 处理图形字幕
+            await SubtitleUtil.TryWriteImagePngsAsync(finalVtt, tmpDir);
+
+            foreach (var item in files) File.Delete(item);
+            FileDic.Clear();
+            var index = 0;
+            var path = Path.Combine(tmpDir, index.ToString(pad) + ".fix.vtt");
+            // 设置字幕偏移
+            finalVtt.LeftShiftTime(TimeSpan.FromSeconds(skippedDur));
+            var subContentFixed = finalVtt.ToVtt();
+            // 转换字幕格式
+            if (DownloaderConfig.MyOptions.SubtitleFormat != Enum.SubtitleFormat.VTT)
+            {
+                path = Path.ChangeExtension(path, ".srt");
+                subContentFixed = finalVtt.ToSrt();
+            }
+            await File.WriteAllTextAsync(path, subContentFixed, Encoding.UTF8);
+            FileDic[firstKey] = new DownloadResult()
+            {
+                ActualContentLength = subContentFixed.Length,
+                ActualFilePath = path
+            };
+        }
+
+        // 自动修复TTML mp4字幕
+        if (DownloaderConfig.MyOptions.AutoSubtitleFix && streamSpec.MediaType == Common.Enum.MediaType.SUBTITLES
+                                                       && streamSpec.Extension != null && streamSpec.Extension.Contains("m4s")
+                                                       && streamSpec.Codecs != null && streamSpec.Codecs.Contains("stpp")) 
+        {
+            Logger.WarnMarkUp(ResString.fixingTTMLmp4);
+            // sawTtml暂时不判断
+            // var initFile = FileDic.Values.Where(v => Path.GetFileName(v!.ActualFilePath).StartsWith("_init")).FirstOrDefault();
+            // var iniFileBytes = File.ReadAllBytes(initFile!.ActualFilePath);
+            // var sawTtml = MP4TtmlUtil.CheckInit(iniFileBytes);
+            var first = true;
+            var finalVtt = new WebVttSub();
+            var keys = FileDic.OrderBy(s => s.Key.Index).Where(v => v.Value!.ActualFilePath.EndsWith(".m4s")).Select(s => s.Key);
+            foreach (var seg in keys)
+            {
+                var vtt = MP4TtmlUtil.ExtractFromMp4(FileDic[seg]!.ActualFilePath, 0);
+                // 手动计算MPEGTS
+                if (finalVtt.MpegtsTimestamp == 0 && vtt.MpegtsTimestamp == 0)
+                {
+                    vtt.MpegtsTimestamp = 90000 * (long)(skippedDur + keys.Where(s => s.Index < seg.Index).Sum(s => s.Duration));
+                }
+                if (first) { finalVtt = vtt; first = false; }
+                else finalVtt.AddCuesFromOne(vtt);
+            }
+
+            // 写出字幕
+            var firstKey = FileDic.Keys.First();
+            var files = FileDic.OrderBy(s => s.Key.Index).Select(s => s.Value).Select(v => v!.ActualFilePath).ToArray();
+
+            // 处理图形字幕
+            await SubtitleUtil.TryWriteImagePngsAsync(finalVtt, tmpDir);
+
+            foreach (var item in files) File.Delete(item);
+            FileDic.Clear();
+            var index = 0;
+            var path = Path.Combine(tmpDir, index.ToString(pad) + ".fix.vtt");
+            // 设置字幕偏移
+            finalVtt.LeftShiftTime(TimeSpan.FromSeconds(skippedDur));
+            var subContentFixed = finalVtt.ToVtt();
+            // 转换字幕格式
+            if (DownloaderConfig.MyOptions.SubtitleFormat != Enum.SubtitleFormat.VTT)
+            {
+                path = Path.ChangeExtension(path, ".srt");
+                subContentFixed = finalVtt.ToSrt();
+            }
+            await File.WriteAllTextAsync(path, subContentFixed, Encoding.UTF8);
+            FileDic[firstKey] = new DownloadResult()
+            {
+                ActualContentLength = subContentFixed.Length,
+                ActualFilePath = path
+            };
+        }
+
+        bool mergeSuccess = false;
+        // 合并
+        if (!DownloaderConfig.MyOptions.SkipMerge)
+        {
+            // 字幕也使用二进制合并
+            if (DownloaderConfig.MyOptions.BinaryMerge || streamSpec.MediaType == MediaType.SUBTITLES)
+            {
+                Logger.InfoMarkUp(ResString.binaryMerge);
+                var files = FileDic.OrderBy(s => s.Key.Index).Select(s => s.Value).Select(v => v!.ActualFilePath).ToArray();
+                MergeUtil.CombineMultipleFilesIntoSingleFile(files, output);
+                mergeSuccess = true;
+            }
+            else
+            {
+                // ffmpeg合并
+                var files = FileDic.OrderBy(s => s.Key.Index).Select(s => s.Value).Select(v => v!.ActualFilePath).ToArray();
+                Logger.InfoMarkUp(ResString.ffmpegMerge);
+                var ext = streamSpec.MediaType == MediaType.AUDIO ? "m4a" : "mp4";
+                var ffOut = Path.Combine(Path.GetDirectoryName(output)!, Path.GetFileNameWithoutExtension(output) + $".{ext}");
+                // 检测目标文件是否存在
+                while (File.Exists(ffOut))
+                {
+                    Logger.WarnMarkUp($"{Path.GetFileName(ffOut)} => {Path.GetFileName(ffOut = Path.ChangeExtension(ffOut, $"copy" + Path.GetExtension(ffOut)))}");
+                }
+                // 大于1800分片,需要分步骤合并
+                if (files.Length >= 1800)
+                {
+                    Logger.WarnMarkUp(ResString.partMerge);
+                    files = MergeUtil.PartialCombineMultipleFiles(files);
+                    FileDic.Clear();
+                    foreach (var item in files)
+                    {
+                        FileDic[new MediaSegment() { Url = item }] = new DownloadResult()
+                        {
+                            ActualFilePath = item
+                        };
+                    }
+                }
+                mergeSuccess = MergeUtil.MergeByFFmpeg(DownloaderConfig.MyOptions.FFmpegBinaryPath!, files, Path.ChangeExtension(ffOut, null), ext, useAACFilter, writeDate: !DownloaderConfig.MyOptions.NoDateInfo, useConcatDemuxer: DownloaderConfig.MyOptions.UseFFmpegConcatDemuxer);
+                if (mergeSuccess) output = ffOut;
+            }
+        }
+
+        // 删除临时文件夹
+        if (!DownloaderConfig.MyOptions.SkipMerge && DownloaderConfig.MyOptions.DelAfterDone && mergeSuccess)
+        {
+            var files = FileDic.Values.Select(v => v!.ActualFilePath);
+            foreach (var file in files)
+            {
+                File.Delete(file);
+            }
+            OtherUtil.SafeDeleteDir(tmpDir);
+        }
+
+        // 重新读取init信息
+        if (mergeSuccess && totalCount >= 1 && string.IsNullOrEmpty(currentKID) && streamSpec.Playlist!.MediaParts.First().MediaSegments.First().EncryptInfo.Method != Common.Enum.EncryptMethod.NONE)
+        {
+            currentKID = MP4DecryptUtil.GetMP4Info(output).KID;
+            // try shaka packager, which can handle WebM
+            if (string.IsNullOrEmpty(currentKID) &&  DownloaderConfig.MyOptions.UseShakaPackager) {
+                currentKID = MP4DecryptUtil.ReadInitShaka(output, mp4decrypt);
+            }
+            // 从文件读取KEY
+            await SearchKeyAsync(currentKID);
+        }
+
+        // 调用mp4decrypt解密
+        if (mergeSuccess && File.Exists(output) && !string.IsNullOrEmpty(currentKID) && !DownloaderConfig.MyOptions.MP4RealTimeDecryption && DownloaderConfig.MyOptions.Keys != null && DownloaderConfig.MyOptions.Keys.Length > 0)
+        {
+            var enc = output;
+            var dec = Path.Combine(Path.GetDirectoryName(enc)!, Path.GetFileNameWithoutExtension(enc) + "_dec" + Path.GetExtension(enc));
+            mp4Info = MP4DecryptUtil.GetMP4Info(enc);
+            Logger.InfoMarkUp($"[grey]Decrypting...[/]");
+            var result = await MP4DecryptUtil.DecryptAsync(DownloaderConfig.MyOptions.UseShakaPackager, mp4decrypt, DownloaderConfig.MyOptions.Keys, enc, dec, currentKID, isMultiDRM: mp4Info.isMultiDRM);
+            if (result)
+            {
+                File.Delete(enc);
+                File.Move(dec, enc);
+            }
+        }
+
+        // 记录所有文件信息
+        if (File.Exists(output))
+        {
+            OutputFiles.Add(new OutputFile()
+            {
+                Index = task.Id,
+                FilePath = output,
+                LangCode = streamSpec.Language,
+                Description = streamSpec.Name,
+                Mediainfos = mediaInfos,
+                MediaType = streamSpec.MediaType,
+            });
+        }
+
+        return true;
+    }
+
+    public async Task<bool> StartDownloadAsync()
+    {
+        ConcurrentDictionary<int, SpeedContainer> SpeedContainerDic = new(); // 速度计算
+        ConcurrentDictionary<StreamSpec, bool?> Results = new();
+            
+        var progress = CustomAnsiConsole.Console.Progress().AutoClear(true);
+        progress.AutoRefresh = DownloaderConfig.MyOptions.LogLevel != LogLevel.OFF;
+
+        // 进度条的列定义
+        var progressColumns = new ProgressColumn[]
+        {
+            new TaskDescriptionColumn() { Alignment = Justify.Left },
+            new ProgressBarColumn(){ Width = 30 },
+            new MyPercentageColumn(),
+            new DownloadStatusColumn(SpeedContainerDic),
+            new DownloadSpeedColumn(SpeedContainerDic), // 速度计算
+            new RemainingTimeColumn(),
+            new SpinnerColumn(),
+        };
+        if (DownloaderConfig.MyOptions.NoAnsiColor)
+        {
+            progressColumns = progressColumns.SkipLast(1).ToArray();
+        }
+        progress.Columns(progressColumns);
+
+        if (DownloaderConfig.MyOptions.MP4RealTimeDecryption && !DownloaderConfig.MyOptions.UseShakaPackager
+                                                             && DownloaderConfig.MyOptions.Keys != null && DownloaderConfig.MyOptions.Keys.Length > 0)
+            Logger.WarnMarkUp($"[darkorange3_1]{ResString.realTimeDecMessage}[/]");
+
+        await progress.StartAsync(async ctx =>
+        {
+            // 创建任务
+            var dic = SelectedSteams.Select(item =>
+            {
+                var description = item.ToShortShortString();
+                var task = ctx.AddTask(description, autoStart: false);
+                SpeedContainerDic[task.Id] = new SpeedContainer(); // 速度计算
+                // 限速设置
+                if (DownloaderConfig.MyOptions.MaxSpeed != null)
+                {
+                    SpeedContainerDic[task.Id].SpeedLimit = DownloaderConfig.MyOptions.MaxSpeed.Value;
+                }
+                return (item, task);
+            }).ToDictionary(item => item.item, item => item.task);
+
+            if (!DownloaderConfig.MyOptions.ConcurrentDownload)
+            {
+                // 遍历,顺序下载
+                foreach (var kp in dic)
+                {
+                    var task = kp.Value;
+                    var result = await DownloadStreamAsync(kp.Key, task, SpeedContainerDic[task.Id]);
+                    Results[kp.Key] = result;
+                    // 失败不再下载后续
+                    if (!result) break;
+                }
+            }
+            else
+            {
+                // 并发下载
+                await Parallel.ForEachAsync(dic, async (kp, _) =>
+                {
+                    var task = kp.Value;
+                    var result = await DownloadStreamAsync(kp.Key, task, SpeedContainerDic[task.Id]);
+                    Results[kp.Key] = result;
                 });
             }
+        });
 
-            return true;
-        }
+        var success = Results.Values.All(v => v == true);
 
-        public async Task<bool> StartDownloadAsync()
+        // 删除临时文件夹
+        if (!DownloaderConfig.MyOptions.SkipMerge && DownloaderConfig.MyOptions.DelAfterDone && success)
         {
-            ConcurrentDictionary<int, SpeedContainer> SpeedContainerDic = new(); //速度计算
-            ConcurrentDictionary<StreamSpec, bool?> Results = new();
-            
-            var progress = CustomAnsiConsole.Console.Progress().AutoClear(true);
-            progress.AutoRefresh = DownloaderConfig.MyOptions.LogLevel != LogLevel.OFF;
-
-            //进度条的列定义
-            var progressColumns = new ProgressColumn[]
+            foreach (var item in StreamExtractor.RawFiles)
             {
-                new TaskDescriptionColumn() { Alignment = Justify.Left },
-                new ProgressBarColumn(){ Width = 30 },
-                new MyPercentageColumn(),
-                new DownloadStatusColumn(SpeedContainerDic),
-                new DownloadSpeedColumn(SpeedContainerDic), //速度计算
-                new RemainingTimeColumn(),
-                new SpinnerColumn(),
-            };
-            if (DownloaderConfig.MyOptions.NoAnsiColor)
-            {
-                progressColumns = progressColumns.SkipLast(1).ToArray();
+                var file = Path.Combine(DownloaderConfig.DirPrefix, item.Key);
+                if (File.Exists(file)) File.Delete(file);
             }
-            progress.Columns(progressColumns);
-
-            if (DownloaderConfig.MyOptions.MP4RealTimeDecryption && !DownloaderConfig.MyOptions.UseShakaPackager
-                && DownloaderConfig.MyOptions.Keys != null && DownloaderConfig.MyOptions.Keys.Length > 0)
-                Logger.WarnMarkUp($"[darkorange3_1]{ResString.realTimeDecMessage}[/]");
-
-            await progress.StartAsync(async ctx =>
-            {
-                //创建任务
-                var dic = SelectedSteams.Select(item =>
-                {
-                    var description = item.ToShortShortString();
-                    var task = ctx.AddTask(description, autoStart: false);
-                    SpeedContainerDic[task.Id] = new SpeedContainer(); //速度计算
-                    //限速设置
-                    if (DownloaderConfig.MyOptions.MaxSpeed != null)
-                    {
-                        SpeedContainerDic[task.Id].SpeedLimit = DownloaderConfig.MyOptions.MaxSpeed.Value;
-                    }
-                    return (item, task);
-                }).ToDictionary(item => item.item, item => item.task);
-
-                if (!DownloaderConfig.MyOptions.ConcurrentDownload)
-                {
-                    //遍历,顺序下载
-                    foreach (var kp in dic)
-                    {
-                        var task = kp.Value;
-                        var result = await DownloadStreamAsync(kp.Key, task, SpeedContainerDic[task.Id]);
-                        Results[kp.Key] = result;
-                        //失败不再下载后续
-                        if (!result) break;
-                    }
-                }
-                else
-                {
-                    //并发下载
-                    await Parallel.ForEachAsync(dic, async (kp, _) =>
-                    {
-                        var task = kp.Value;
-                        var result = await DownloadStreamAsync(kp.Key, task, SpeedContainerDic[task.Id]);
-                        Results[kp.Key] = result;
-                    });
-                }
-            });
-
-            var success = Results.Values.All(v => v == true);
-
-            //删除临时文件夹
-            if (!DownloaderConfig.MyOptions.SkipMerge && DownloaderConfig.MyOptions.DelAfterDone && success)
-            {
-                foreach (var item in StreamExtractor.RawFiles)
-                {
-                    var file = Path.Combine(DownloaderConfig.DirPrefix, item.Key);
-                    if (File.Exists(file)) File.Delete(file);
-                }
-                OtherUtil.SafeDeleteDir(DownloaderConfig.DirPrefix);
-            }
-
-            //混流
-            if (success && DownloaderConfig.MyOptions.MuxAfterDone && OutputFiles.Count > 0) 
-            {
-                OutputFiles = OutputFiles.OrderBy(o => o.Index).ToList();
-                //是否跳过字幕
-                if (DownloaderConfig.MyOptions.MuxOptions.SkipSubtitle)
-                {
-                    OutputFiles = OutputFiles.Where(o => o.MediaType != MediaType.SUBTITLES).ToList();
-                }
-                if (DownloaderConfig.MyOptions.MuxImports != null)
-                {
-                    OutputFiles.AddRange(DownloaderConfig.MyOptions.MuxImports);
-                }
-                OutputFiles.ForEach(f => Logger.WarnMarkUp($"[grey]{Path.GetFileName(f.FilePath).EscapeMarkup()}[/]"));
-                var saveDir = DownloaderConfig.MyOptions.SaveDir ?? Environment.CurrentDirectory;
-                var ext = OtherUtil.GetMuxExtension(DownloaderConfig.MyOptions.MuxOptions.MuxFormat);
-                var dirName = Path.GetFileName(DownloaderConfig.DirPrefix);
-                var outName = $"{dirName}.MUX";
-                var outPath = Path.Combine(saveDir, outName);
-                Logger.WarnMarkUp($"Muxing to [grey]{outName.EscapeMarkup()}{ext}[/]");
-                var result = false;
-                if (DownloaderConfig.MyOptions.MuxOptions.UseMkvmerge) result = MergeUtil.MuxInputsByMkvmerge(DownloaderConfig.MyOptions.MkvmergeBinaryPath!, OutputFiles.ToArray(), outPath);
-                else result = MergeUtil.MuxInputsByFFmpeg(DownloaderConfig.MyOptions.FFmpegBinaryPath!, OutputFiles.ToArray(), outPath, DownloaderConfig.MyOptions.MuxOptions.MuxFormat, !DownloaderConfig.MyOptions.NoDateInfo);
-                //完成后删除各轨道文件
-                if (result)
-                {
-                    if (!DownloaderConfig.MyOptions.MuxOptions.KeepFiles)
-                    {
-                        Logger.WarnMarkUp("[grey]Cleaning files...[/]");
-                        OutputFiles.ForEach(f => File.Delete(f.FilePath));
-                        var tmpDir = DownloaderConfig.MyOptions.TmpDir ?? Environment.CurrentDirectory;
-                        OtherUtil.SafeDeleteDir(tmpDir);
-                    }
-                }
-                else
-                {
-                    success = false;
-                    Logger.ErrorMarkUp($"Mux failed");
-                }
-                //判断是否要改名
-                var newPath = Path.ChangeExtension(outPath, ext);
-                if (result && !File.Exists(newPath))
-                {
-                    Logger.WarnMarkUp($"Rename to [grey]{Path.GetFileName(newPath).EscapeMarkup()}[/]");
-                    File.Move(outPath + ext, newPath);
-                }
-            }
-
-            return success;
+            OtherUtil.SafeDeleteDir(DownloaderConfig.DirPrefix);
         }
+
+        // 混流
+        if (success && DownloaderConfig.MyOptions.MuxAfterDone && OutputFiles.Count > 0) 
+        {
+            OutputFiles = OutputFiles.OrderBy(o => o.Index).ToList();
+            // 是否跳过字幕
+            if (DownloaderConfig.MyOptions.MuxOptions!.SkipSubtitle)
+            {
+                OutputFiles = OutputFiles.Where(o => o.MediaType != MediaType.SUBTITLES).ToList();
+            }
+            if (DownloaderConfig.MyOptions.MuxImports != null)
+            {
+                OutputFiles.AddRange(DownloaderConfig.MyOptions.MuxImports);
+            }
+            OutputFiles.ForEach(f => Logger.WarnMarkUp($"[grey]{Path.GetFileName(f.FilePath).EscapeMarkup()}[/]"));
+            var saveDir = DownloaderConfig.MyOptions.SaveDir ?? Environment.CurrentDirectory;
+            var ext = OtherUtil.GetMuxExtension(DownloaderConfig.MyOptions.MuxOptions.MuxFormat);
+            var dirName = Path.GetFileName(DownloaderConfig.DirPrefix);
+            var outName = $"{dirName}.MUX";
+            var outPath = Path.Combine(saveDir, outName);
+            Logger.WarnMarkUp($"Muxing to [grey]{outName.EscapeMarkup()}{ext}[/]");
+            var result = false;
+            if (DownloaderConfig.MyOptions.MuxOptions.UseMkvmerge) result = MergeUtil.MuxInputsByMkvmerge(DownloaderConfig.MyOptions.MkvmergeBinaryPath!, OutputFiles.ToArray(), outPath);
+            else result = MergeUtil.MuxInputsByFFmpeg(DownloaderConfig.MyOptions.FFmpegBinaryPath!, OutputFiles.ToArray(), outPath, DownloaderConfig.MyOptions.MuxOptions.MuxFormat, !DownloaderConfig.MyOptions.NoDateInfo);
+            // 完成后删除各轨道文件
+            if (result)
+            {
+                if (!DownloaderConfig.MyOptions.MuxOptions.KeepFiles)
+                {
+                    Logger.WarnMarkUp("[grey]Cleaning files...[/]");
+                    OutputFiles.ForEach(f => File.Delete(f.FilePath));
+                    var tmpDir = DownloaderConfig.MyOptions.TmpDir ?? Environment.CurrentDirectory;
+                    OtherUtil.SafeDeleteDir(tmpDir);
+                }
+            }
+            else
+            {
+                success = false;
+                Logger.ErrorMarkUp($"Mux failed");
+            }
+            // 判断是否要改名
+            var newPath = Path.ChangeExtension(outPath, ext);
+            if (result && !File.Exists(newPath))
+            {
+                Logger.WarnMarkUp($"Rename to [grey]{Path.GetFileName(newPath).EscapeMarkup()}[/]");
+                File.Move(outPath + ext, newPath);
+            }
+        }
+
+        return success;
     }
-}
+}
\ No newline at end of file
diff --git a/src/N_m3u8DL-RE/DownloadManager/SimpleLiveRecordManager2.cs b/src/N_m3u8DL-RE/DownloadManager/SimpleLiveRecordManager2.cs
index 80e62e0..e5c07ef 100644
--- a/src/N_m3u8DL-RE/DownloadManager/SimpleLiveRecordManager2.cs
+++ b/src/N_m3u8DL-RE/DownloadManager/SimpleLiveRecordManager2.cs
@@ -12,333 +12,284 @@ using N_m3u8DL_RE.Parser;
 using N_m3u8DL_RE.Parser.Mp4;
 using N_m3u8DL_RE.Util;
 using Spectre.Console;
-using Spectre.Console.Rendering;
 using System.Collections.Concurrent;
-using System.Diagnostics;
-using System.IO;
 using System.IO.Pipes;
-using System.Linq;
 using System.Text;
-using System.Threading.Tasks;
 using System.Threading.Tasks.Dataflow;
-using System.Xml.Linq;
 
-namespace N_m3u8DL_RE.DownloadManager
+namespace N_m3u8DL_RE.DownloadManager;
+
+internal class SimpleLiveRecordManager2
 {
-    internal class SimpleLiveRecordManager2
+    IDownloader Downloader;
+    DownloaderConfig DownloaderConfig;
+    StreamExtractor StreamExtractor;
+    List<StreamSpec> SelectedSteams;
+    ConcurrentDictionary<int, string> PipeSteamNamesDic = new();
+    List<OutputFile> OutputFiles = new();
+    DateTime? PublishDateTime;
+    bool STOP_FLAG = false;
+    int WAIT_SEC = 0; // 刷新间隔
+    ConcurrentDictionary<int, int> RecordedDurDic = new(); // 已录制时长
+    ConcurrentDictionary<int, int> RefreshedDurDic = new(); // 已刷新出的时长
+    ConcurrentDictionary<int, BufferBlock<List<MediaSegment>>> BlockDic = new(); // 各流的Block
+    ConcurrentDictionary<int, bool> SamePathDic = new(); // 各流是否allSamePath
+    ConcurrentDictionary<int, bool> RecordLimitReachedDic = new(); // 各流是否达到上限
+    ConcurrentDictionary<int, string> LastFileNameDic = new(); // 上次下载的文件名
+    ConcurrentDictionary<int, long> MaxIndexDic = new(); // 最大Index
+    ConcurrentDictionary<int, long> DateTimeDic = new(); // 上次下载的dateTime
+    CancellationTokenSource CancellationTokenSource = new(); // 取消Wait
+
+    private readonly object lockObj = new object();
+    TimeSpan? audioStart = null;
+
+    public SimpleLiveRecordManager2(DownloaderConfig downloaderConfig, List<StreamSpec> selectedSteams, StreamExtractor streamExtractor)
     {
-        IDownloader Downloader;
-        DownloaderConfig DownloaderConfig;
-        StreamExtractor StreamExtractor;
-        List<StreamSpec> SelectedSteams;
-        ConcurrentDictionary<int, string> PipeSteamNamesDic = new();
-        List<OutputFile> OutputFiles = new();
-        DateTime? PublishDateTime;
-        bool STOP_FLAG = false;
-        int WAIT_SEC = 0; //刷新间隔
-        ConcurrentDictionary<int, int> RecordedDurDic = new(); //已录制时长
-        ConcurrentDictionary<int, int> RefreshedDurDic = new(); //已刷新出的时长
-        ConcurrentDictionary<int, BufferBlock<List<MediaSegment>>> BlockDic = new(); //各流的Block
-        ConcurrentDictionary<int, bool> SamePathDic = new(); //各流是否allSamePath
-        ConcurrentDictionary<int, bool> RecordLimitReachedDic = new(); //各流是否达到上限
-        ConcurrentDictionary<int, string> LastFileNameDic = new(); //上次下载的文件名
-        ConcurrentDictionary<int, long> MaxIndexDic = new(); //最大Index
-        ConcurrentDictionary<int, long> DateTimeDic = new(); //上次下载的dateTime
-        CancellationTokenSource CancellationTokenSource = new(); //取消Wait
+        this.DownloaderConfig = downloaderConfig;
+        Downloader = new SimpleDownloader(DownloaderConfig);
+        PublishDateTime = selectedSteams.FirstOrDefault()?.PublishTime;
+        StreamExtractor = streamExtractor;
+        SelectedSteams = selectedSteams;
+    }
 
-        private readonly object lockObj = new object();
-        TimeSpan? audioStart = null;
-
-        public SimpleLiveRecordManager2(DownloaderConfig downloaderConfig, List<StreamSpec> selectedSteams, StreamExtractor streamExtractor)
+    // 从文件读取KEY
+    private async Task SearchKeyAsync(string? currentKID)
+    {
+        var _key = await MP4DecryptUtil.SearchKeyFromFileAsync(DownloaderConfig.MyOptions.KeyTextFile, currentKID);
+        if (_key != null)
         {
-            this.DownloaderConfig = downloaderConfig;
-            Downloader = new SimpleDownloader(DownloaderConfig);
-            PublishDateTime = selectedSteams.FirstOrDefault()?.PublishTime;
-            StreamExtractor = streamExtractor;
-            SelectedSteams = selectedSteams;
+            if (DownloaderConfig.MyOptions.Keys == null)
+                DownloaderConfig.MyOptions.Keys = new string[] { _key };
+            else
+                DownloaderConfig.MyOptions.Keys = DownloaderConfig.MyOptions.Keys.Concat(new string[] { _key }).ToArray();
+        }
+    }
+
+    /// <summary>
+    /// 获取时间戳
+    /// </summary>
+    /// <param name="dateTime"></param>
+    /// <returns></returns>
+    private long GetUnixTimestamp(DateTime dateTime)
+    {
+        return new DateTimeOffset(dateTime.ToUniversalTime()).ToUnixTimeSeconds();
+    }
+
+    /// <summary>
+    /// 获取分段文件夹
+    /// </summary>
+    /// <param name="segment"></param>
+    /// <param name="allHasDatetime"></param>
+    /// <returns></returns>
+    private string GetSegmentName(MediaSegment segment, bool allHasDatetime, bool allSamePath)
+    {
+        if (!string.IsNullOrEmpty(segment.NameFromVar))
+        {
+            return segment.NameFromVar;
         }
 
-        //从文件读取KEY
-        private async Task SearchKeyAsync(string? currentKID)
+        bool hls = StreamExtractor.ExtractorType == ExtractorType.HLS;
+
+        string name = OtherUtil.GetFileNameFromInput(segment.Url, false);
+        if (allSamePath)
         {
-            var _key = await MP4DecryptUtil.SearchKeyFromFileAsync(DownloaderConfig.MyOptions.KeyTextFile, currentKID);
-            if (_key != null)
-            {
-                if (DownloaderConfig.MyOptions.Keys == null)
-                    DownloaderConfig.MyOptions.Keys = new string[] { _key };
-                else
-                    DownloaderConfig.MyOptions.Keys = DownloaderConfig.MyOptions.Keys.Concat(new string[] { _key }).ToArray();
-            }
+            name = OtherUtil.GetValidFileName(segment.Url.Split('?').Last(), "_");
         }
 
-        /// <summary>
-        /// 获取时间戳
-        /// </summary>
-        /// <param name="dateTime"></param>
-        /// <returns></returns>
-        private long GetUnixTimestamp(DateTime dateTime)
+        if (hls && allHasDatetime)
         {
-            return new DateTimeOffset(dateTime.ToUniversalTime()).ToUnixTimeSeconds();
+            name = GetUnixTimestamp(segment.DateTime!.Value).ToString();
+        }
+        else if (hls)
+        {
+            name = segment.Index.ToString();
         }
 
-        /// <summary>
-        /// 获取分段文件夹
-        /// </summary>
-        /// <param name="segment"></param>
-        /// <param name="allHasDatetime"></param>
-        /// <returns></returns>
-        private string GetSegmentName(MediaSegment segment, bool allHasDatetime, bool allSamePath)
+        return name;
+    }
+
+    private void ChangeSpecInfo(StreamSpec streamSpec, List<Mediainfo> mediainfos, ref bool useAACFilter)
+    {
+        if (!DownloaderConfig.MyOptions.BinaryMerge && mediainfos.Any(m => m.DolbyVison == true))
         {
-            if (!string.IsNullOrEmpty(segment.NameFromVar))
-            {
-                return segment.NameFromVar;
-            }
-
-            bool hls = StreamExtractor.ExtractorType == ExtractorType.HLS;
-
-            string name = OtherUtil.GetFileNameFromInput(segment.Url, false);
-            if (allSamePath)
-            {
-                name = OtherUtil.GetValidFileName(segment.Url.Split('?').Last(), "_");
-            }
-
-            if (hls && allHasDatetime)
-            {
-                name = GetUnixTimestamp(segment.DateTime!.Value).ToString();
-            }
-            else if (hls)
-            {
-                name = segment.Index.ToString();
-            }
-
-            return name;
+            DownloaderConfig.MyOptions.BinaryMerge = true;
+            Logger.WarnMarkUp($"[darkorange3_1]{ResString.autoBinaryMerge2}[/]");
         }
 
-        private void ChangeSpecInfo(StreamSpec streamSpec, List<Mediainfo> mediainfos, ref bool useAACFilter)
+        if (DownloaderConfig.MyOptions.MuxAfterDone && mediainfos.Any(m => m.DolbyVison == true))
         {
-            if (!DownloaderConfig.MyOptions.BinaryMerge && mediainfos.Any(m => m.DolbyVison == true))
-            {
-                DownloaderConfig.MyOptions.BinaryMerge = true;
-                Logger.WarnMarkUp($"[darkorange3_1]{ResString.autoBinaryMerge2}[/]");
-            }
-
-            if (DownloaderConfig.MyOptions.MuxAfterDone && mediainfos.Any(m => m.DolbyVison == true))
-            {
-                DownloaderConfig.MyOptions.MuxAfterDone = false;
-                Logger.WarnMarkUp($"[darkorange3_1]{ResString.autoBinaryMerge5}[/]");
-            }
-
-            if (mediainfos.Where(m => m.Type == "Audio").All(m => m.BaseInfo!.Contains("aac")))
-            {
-                useAACFilter = true;
-            }
-
-            if (mediainfos.All(m => m.Type == "Audio") && streamSpec.MediaType != MediaType.AUDIO)
-            {
-                streamSpec.MediaType = MediaType.AUDIO;
-            }
-            else if (mediainfos.All(m => m.Type == "Subtitle") && streamSpec.MediaType != MediaType.SUBTITLES)
-            {
-                streamSpec.MediaType = MediaType.SUBTITLES;
-
-                if (streamSpec.Extension == null || streamSpec.Extension == "ts")
-                    streamSpec.Extension = "vtt";
-            }
+            DownloaderConfig.MyOptions.MuxAfterDone = false;
+            Logger.WarnMarkUp($"[darkorange3_1]{ResString.autoBinaryMerge5}[/]");
         }
 
-        private async Task<bool> RecordStreamAsync(StreamSpec streamSpec, ProgressTask task, SpeedContainer speedContainer, BufferBlock<List<MediaSegment>> source)
+        if (mediainfos.Where(m => m.Type == "Audio").All(m => m.BaseInfo!.Contains("aac")))
         {
-            var baseTimestamp = PublishDateTime == null ? 0L : (long)(PublishDateTime.Value.ToUniversalTime() - new DateTime(1970, 1, 1, 0, 0, 0, 0)).TotalMilliseconds;
-            //mp4decrypt
-            var mp4decrypt = DownloaderConfig.MyOptions.DecryptionBinaryPath!;
-            var mp4InitFile = "";
-            var currentKID = "";
-            var readInfo = false; //是否读取过
-            bool useAACFilter = false; //ffmpeg合并flag
-            bool initDownloaded = false; //是否下载过init文件
-            ConcurrentDictionary<MediaSegment, DownloadResult?> FileDic = new();
-            List<Mediainfo> mediaInfos = new();
-            Stream? fileOutputStream = null;
-            WebVttSub currentVtt = new(); //字幕流始终维护一个实例
-            bool firstSub = true;
-            task.StartTask();
+            useAACFilter = true;
+        }
 
-            var name = streamSpec.ToShortString();
-            var type = streamSpec.MediaType ?? Common.Enum.MediaType.VIDEO;
-            var dirName = $"{task.Id}_{OtherUtil.GetValidFileName(streamSpec.GroupId ?? "", "-")}_{streamSpec.Codecs}_{streamSpec.Bandwidth}_{streamSpec.Language}";
-            var tmpDir = Path.Combine(DownloaderConfig.DirPrefix, dirName);
-            var saveDir = DownloaderConfig.MyOptions.SaveDir ?? Environment.CurrentDirectory;
-            var saveName = DownloaderConfig.MyOptions.SaveName != null ? $"{DownloaderConfig.MyOptions.SaveName}.{streamSpec.Language}".TrimEnd('.') : dirName;
-            var headers = DownloaderConfig.Headers;
+        if (mediainfos.All(m => m.Type == "Audio") && streamSpec.MediaType != MediaType.AUDIO)
+        {
+            streamSpec.MediaType = MediaType.AUDIO;
+        }
+        else if (mediainfos.All(m => m.Type == "Subtitle") && streamSpec.MediaType != MediaType.SUBTITLES)
+        {
+            streamSpec.MediaType = MediaType.SUBTITLES;
 
-            Logger.Debug($"dirName: {dirName}; tmpDir: {tmpDir}; saveDir: {saveDir}; saveName: {saveName}");
+            if (streamSpec.Extension == null || streamSpec.Extension == "ts")
+                streamSpec.Extension = "vtt";
+        }
+    }
 
-            //创建文件夹
-            if (!Directory.Exists(tmpDir)) Directory.CreateDirectory(tmpDir);
-            if (!Directory.Exists(saveDir)) Directory.CreateDirectory(saveDir);
+    private async Task<bool> RecordStreamAsync(StreamSpec streamSpec, ProgressTask task, SpeedContainer speedContainer, BufferBlock<List<MediaSegment>> source)
+    {
+        var baseTimestamp = PublishDateTime == null ? 0L : (long)(PublishDateTime.Value.ToUniversalTime() - new DateTime(1970, 1, 1, 0, 0, 0, 0)).TotalMilliseconds;
+        // mp4decrypt
+        var mp4decrypt = DownloaderConfig.MyOptions.DecryptionBinaryPath!;
+        var mp4InitFile = "";
+        var currentKID = "";
+        var readInfo = false; // 是否读取过
+        bool useAACFilter = false; // ffmpeg合并flag
+        bool initDownloaded = false; // 是否下载过init文件
+        ConcurrentDictionary<MediaSegment, DownloadResult?> FileDic = new();
+        List<Mediainfo> mediaInfos = new();
+        Stream? fileOutputStream = null;
+        WebVttSub currentVtt = new(); // 字幕流始终维护一个实例
+        bool firstSub = true;
+        task.StartTask();
 
-            while (true && await source.OutputAvailableAsync())
+        var name = streamSpec.ToShortString();
+        var type = streamSpec.MediaType ?? Common.Enum.MediaType.VIDEO;
+        var dirName = $"{task.Id}_{OtherUtil.GetValidFileName(streamSpec.GroupId ?? "", "-")}_{streamSpec.Codecs}_{streamSpec.Bandwidth}_{streamSpec.Language}";
+        var tmpDir = Path.Combine(DownloaderConfig.DirPrefix, dirName);
+        var saveDir = DownloaderConfig.MyOptions.SaveDir ?? Environment.CurrentDirectory;
+        var saveName = DownloaderConfig.MyOptions.SaveName != null ? $"{DownloaderConfig.MyOptions.SaveName}.{streamSpec.Language}".TrimEnd('.') : dirName;
+        var headers = DownloaderConfig.Headers;
+
+        Logger.Debug($"dirName: {dirName}; tmpDir: {tmpDir}; saveDir: {saveDir}; saveName: {saveName}");
+
+        // 创建文件夹
+        if (!Directory.Exists(tmpDir)) Directory.CreateDirectory(tmpDir);
+        if (!Directory.Exists(saveDir)) Directory.CreateDirectory(saveDir);
+
+        while (true && await source.OutputAvailableAsync())
+        {
+            // 接收新片段 且总是拿全部未处理的片段
+            // 有时每次只有很少的片段,但是之前的片段下载慢,导致后面还没下载的片段都失效了
+            // TryReceiveAll可以稍微缓解一下
+            source.TryReceiveAll(out IList<List<MediaSegment>>? segmentsList);
+            var segments = segmentsList!.SelectMany(s => s);
+            if (segments == null || !segments.Any()) continue;
+            var segmentsDuration = segments.Sum(s => s.Duration);
+            Logger.DebugMarkUp(string.Join(",", segments.Select(sss => GetSegmentName(sss, false, false))));
+
+            // 下载init
+            if (!initDownloaded && streamSpec.Playlist?.MediaInit != null) 
             {
-                //接收新片段 且总是拿全部未处理的片段
-                //有时每次只有很少的片段,但是之前的片段下载慢,导致后面还没下载的片段都失效了
-                //TryReceiveAll可以稍微缓解一下
-                source.TryReceiveAll(out IList<List<MediaSegment>>? segmentsList);
-                var segments = segmentsList!.SelectMany(s => s);
-                if (segments == null || !segments.Any()) continue;
-                var segmentsDuration = segments.Sum(s => s.Duration);
-                Logger.DebugMarkUp(string.Join(",", segments.Select(sss => GetSegmentName(sss, false, false))));
-
-                //下载init
-                if (!initDownloaded && streamSpec.Playlist?.MediaInit != null) 
+                task.MaxValue += 1;
+                // 对于fMP4,自动开启二进制合并
+                if (!DownloaderConfig.MyOptions.BinaryMerge && streamSpec.MediaType != MediaType.SUBTITLES)
                 {
-                    task.MaxValue += 1;
-                    //对于fMP4,自动开启二进制合并
-                    if (!DownloaderConfig.MyOptions.BinaryMerge && streamSpec.MediaType != MediaType.SUBTITLES)
-                    {
-                        DownloaderConfig.MyOptions.BinaryMerge = true;
-                        Logger.WarnMarkUp($"[darkorange3_1]{ResString.autoBinaryMerge}[/]");
-                    }
+                    DownloaderConfig.MyOptions.BinaryMerge = true;
+                    Logger.WarnMarkUp($"[darkorange3_1]{ResString.autoBinaryMerge}[/]");
+                }
 
-                    var path = Path.Combine(tmpDir, "_init.mp4.tmp");
-                    var result = await Downloader.DownloadSegmentAsync(streamSpec.Playlist.MediaInit, path, speedContainer, headers);
-                    FileDic[streamSpec.Playlist.MediaInit] = result;
-                    if (result == null || !result.Success)
-                    {
-                        throw new Exception("Download init file failed!");
-                    }
-                    mp4InitFile = result.ActualFilePath;
-                    task.Increment(1);
+                var path = Path.Combine(tmpDir, "_init.mp4.tmp");
+                var result = await Downloader.DownloadSegmentAsync(streamSpec.Playlist.MediaInit, path, speedContainer, headers);
+                FileDic[streamSpec.Playlist.MediaInit] = result;
+                if (result == null || !result.Success)
+                {
+                    throw new Exception("Download init file failed!");
+                }
+                mp4InitFile = result.ActualFilePath;
+                task.Increment(1);
 
-                    //读取mp4信息
-                    if (result != null && result.Success)
+                // 读取mp4信息
+                if (result != null && result.Success)
+                {
+                    currentKID = MP4DecryptUtil.GetMP4Info(result.ActualFilePath).KID;
+                    // 从文件读取KEY
+                    await SearchKeyAsync(currentKID);
+                    // 实时解密
+                    if (DownloaderConfig.MyOptions.MP4RealTimeDecryption && !string.IsNullOrEmpty(currentKID) && StreamExtractor.ExtractorType != ExtractorType.MSS)
                     {
-                        currentKID = MP4DecryptUtil.GetMP4Info(result.ActualFilePath).KID;
-                        //从文件读取KEY
-                        await SearchKeyAsync(currentKID);
-                        //实时解密
-                        if (DownloaderConfig.MyOptions.MP4RealTimeDecryption && !string.IsNullOrEmpty(currentKID) && StreamExtractor.ExtractorType != ExtractorType.MSS)
+                        var enc = result.ActualFilePath;
+                        var dec = Path.Combine(Path.GetDirectoryName(enc)!, Path.GetFileNameWithoutExtension(enc) + "_dec" + Path.GetExtension(enc));
+                        var dResult = await MP4DecryptUtil.DecryptAsync(DownloaderConfig.MyOptions.UseShakaPackager, mp4decrypt, DownloaderConfig.MyOptions.Keys, enc, dec, currentKID);
+                        if (dResult)
                         {
-                            var enc = result.ActualFilePath;
+                            FileDic[streamSpec.Playlist.MediaInit]!.ActualFilePath = dec;
+                        }
+                    }
+                    // ffmpeg读取信息
+                    if (!readInfo)
+                    {
+                        Logger.WarnMarkUp(ResString.readingInfo);
+                        mediaInfos = await MediainfoUtil.ReadInfoAsync(DownloaderConfig.MyOptions.FFmpegBinaryPath!, result.ActualFilePath);
+                        mediaInfos.ForEach(info => Logger.InfoMarkUp(info.ToStringMarkUp()));
+                        lock (lockObj)
+                        {
+                            if (audioStart == null) audioStart = mediaInfos.FirstOrDefault(x => x.Type == "Audio")?.StartTime;
+                        }
+                        ChangeSpecInfo(streamSpec, mediaInfos, ref useAACFilter);
+                        readInfo = true;
+                    }
+                    initDownloaded = true;
+                }
+            }
+
+            var allHasDatetime = segments.All(s => s.DateTime != null);
+            if (!SamePathDic.ContainsKey(task.Id))
+            {
+                var allName = segments.Select(s => OtherUtil.GetFileNameFromInput(s.Url, false));
+                var allSamePath = allName.Count() > 1 && allName.Distinct().Count() == 1;
+                SamePathDic[task.Id] = allSamePath;
+            }
+
+            // 下载第一个分片
+            if (!readInfo || StreamExtractor.ExtractorType == ExtractorType.MSS)
+            {
+                var seg = segments.First();
+                segments = segments.Skip(1);
+                // 获取文件名
+                var filename = GetSegmentName(seg, allHasDatetime, SamePathDic[task.Id]);
+                var index = seg.Index;
+                var path = Path.Combine(tmpDir, filename + $".{streamSpec.Extension ?? "clip"}.tmp");
+                var result = await Downloader.DownloadSegmentAsync(seg, path, speedContainer, headers);
+                FileDic[seg] = result;
+                if (result == null || !result.Success)
+                {
+                    throw new Exception("Download first segment failed!");
+                }
+                task.Increment(1);
+                if (result != null && result.Success)
+                {
+                    // 修复MSS init
+                    if (StreamExtractor.ExtractorType == ExtractorType.MSS)
+                    {
+                        var processor = new MSSMoovProcessor(streamSpec);
+                        var header = processor.GenHeader(File.ReadAllBytes(result.ActualFilePath));
+                        await File.WriteAllBytesAsync(FileDic[streamSpec.Playlist!.MediaInit!]!.ActualFilePath, header);
+                        if (DownloaderConfig.MyOptions.MP4RealTimeDecryption && !string.IsNullOrEmpty(currentKID))
+                        {
+                            // 需要重新解密init
+                            var enc = FileDic[streamSpec.Playlist!.MediaInit!]!.ActualFilePath;
                             var dec = Path.Combine(Path.GetDirectoryName(enc)!, Path.GetFileNameWithoutExtension(enc) + "_dec" + Path.GetExtension(enc));
                             var dResult = await MP4DecryptUtil.DecryptAsync(DownloaderConfig.MyOptions.UseShakaPackager, mp4decrypt, DownloaderConfig.MyOptions.Keys, enc, dec, currentKID);
                             if (dResult)
                             {
-                                FileDic[streamSpec.Playlist.MediaInit]!.ActualFilePath = dec;
+                                FileDic[streamSpec.Playlist!.MediaInit!]!.ActualFilePath = dec;
                             }
                         }
-                        //ffmpeg读取信息
-                        if (!readInfo)
-                        {
-                            Logger.WarnMarkUp(ResString.readingInfo);
-                            mediaInfos = await MediainfoUtil.ReadInfoAsync(DownloaderConfig.MyOptions.FFmpegBinaryPath!, result.ActualFilePath);
-                            mediaInfos.ForEach(info => Logger.InfoMarkUp(info.ToStringMarkUp()));
-                            lock (lockObj)
-                            {
-                                if (audioStart == null) audioStart = mediaInfos.FirstOrDefault(x => x.Type == "Audio")?.StartTime;
-                            }
-                            ChangeSpecInfo(streamSpec, mediaInfos, ref useAACFilter);
-                            readInfo = true;
-                        }
-                        initDownloaded = true;
                     }
-                }
-
-                var allHasDatetime = segments.All(s => s.DateTime != null);
-                if (!SamePathDic.ContainsKey(task.Id))
-                {
-                    var allName = segments.Select(s => OtherUtil.GetFileNameFromInput(s.Url, false));
-                    var allSamePath = allName.Count() > 1 && allName.Distinct().Count() == 1;
-                    SamePathDic[task.Id] = allSamePath;
-                }
-
-                //下载第一个分片
-                if (!readInfo || StreamExtractor.ExtractorType == ExtractorType.MSS)
-                {
-                    var seg = segments.First();
-                    segments = segments.Skip(1);
-                    //获取文件名
-                    var filename = GetSegmentName(seg, allHasDatetime, SamePathDic[task.Id]);
-                    var index = seg.Index;
-                    var path = Path.Combine(tmpDir, filename + $".{streamSpec.Extension ?? "clip"}.tmp");
-                    var result = await Downloader.DownloadSegmentAsync(seg, path, speedContainer, headers);
-                    FileDic[seg] = result;
-                    if (result == null || !result.Success)
+                    // 读取init信息
+                    if (string.IsNullOrEmpty(currentKID))
                     {
-                        throw new Exception("Download first segment failed!");
+                        currentKID = MP4DecryptUtil.GetMP4Info(result.ActualFilePath).KID;
                     }
-                    task.Increment(1);
-                    if (result != null && result.Success)
-                    {
-                        //修复MSS init
-                        if (StreamExtractor.ExtractorType == ExtractorType.MSS)
-                        {
-                            var processor = new MSSMoovProcessor(streamSpec);
-                            var header = processor.GenHeader(File.ReadAllBytes(result.ActualFilePath));
-                            await File.WriteAllBytesAsync(FileDic[streamSpec.Playlist!.MediaInit!]!.ActualFilePath, header);
-                            if (DownloaderConfig.MyOptions.MP4RealTimeDecryption && !string.IsNullOrEmpty(currentKID))
-                            {
-                                //需要重新解密init
-                                var enc = FileDic[streamSpec.Playlist!.MediaInit!]!.ActualFilePath;
-                                var dec = Path.Combine(Path.GetDirectoryName(enc)!, Path.GetFileNameWithoutExtension(enc) + "_dec" + Path.GetExtension(enc));
-                                var dResult = await MP4DecryptUtil.DecryptAsync(DownloaderConfig.MyOptions.UseShakaPackager, mp4decrypt, DownloaderConfig.MyOptions.Keys, enc, dec, currentKID);
-                                if (dResult)
-                                {
-                                    FileDic[streamSpec.Playlist!.MediaInit!]!.ActualFilePath = dec;
-                                }
-                            }
-                        }
-                        //读取init信息
-                        if (string.IsNullOrEmpty(currentKID))
-                        {
-                            currentKID = MP4DecryptUtil.GetMP4Info(result.ActualFilePath).KID;
-                        }
-                        //从文件读取KEY
-                        await SearchKeyAsync(currentKID);
-                        //实时解密
-                        if (DownloaderConfig.MyOptions.MP4RealTimeDecryption && !string.IsNullOrEmpty(currentKID))
-                        {
-                            var enc = result.ActualFilePath;
-                            var dec = Path.Combine(Path.GetDirectoryName(enc)!, Path.GetFileNameWithoutExtension(enc) + "_dec" + Path.GetExtension(enc));
-                            var dResult = await MP4DecryptUtil.DecryptAsync(DownloaderConfig.MyOptions.UseShakaPackager, mp4decrypt, DownloaderConfig.MyOptions.Keys, enc, dec, currentKID, mp4InitFile);
-                            if (dResult)
-                            {
-                                File.Delete(enc);
-                                result.ActualFilePath = dec;
-                            }
-                        }
-                        if (!readInfo)
-                        {
-                            //ffmpeg读取信息
-                            Logger.WarnMarkUp(ResString.readingInfo);
-                            mediaInfos = await MediainfoUtil.ReadInfoAsync(DownloaderConfig.MyOptions.FFmpegBinaryPath!, result!.ActualFilePath);
-                            mediaInfos.ForEach(info => Logger.InfoMarkUp(info.ToStringMarkUp()));
-                            lock (lockObj)
-                            {
-                                if (audioStart == null) audioStart = mediaInfos.FirstOrDefault(x => x.Type == "Audio")?.StartTime;
-                            }
-                            ChangeSpecInfo(streamSpec, mediaInfos, ref useAACFilter);
-                            readInfo = true;
-                        }
-                    }
-                }
-
-                //开始下载
-                var options = new ParallelOptions()
-                {
-                    MaxDegreeOfParallelism = DownloaderConfig.MyOptions.ThreadCount
-                };
-                await Parallel.ForEachAsync(segments, options, async (seg, _) =>
-                {
-                    //获取文件名
-                    var filename = GetSegmentName(seg, allHasDatetime, SamePathDic[task.Id]);
-                    var index = seg.Index;
-                    var path = Path.Combine(tmpDir, filename + $".{streamSpec.Extension ?? "clip"}.tmp");
-                    var result = await Downloader.DownloadSegmentAsync(seg, path, speedContainer, headers);
-                    FileDic[seg] = result;
-                    if (result != null && result.Success)
-                        task.Increment(1);
-                    //实时解密
-                    if (DownloaderConfig.MyOptions.MP4RealTimeDecryption && result != null && result.Success && !string.IsNullOrEmpty(currentKID))
+                    // 从文件读取KEY
+                    await SearchKeyAsync(currentKID);
+                    // 实时解密
+                    if (DownloaderConfig.MyOptions.MP4RealTimeDecryption && !string.IsNullOrEmpty(currentKID))
                     {
                         var enc = result.ActualFilePath;
                         var dec = Path.Combine(Path.GetDirectoryName(enc)!, Path.GetFileNameWithoutExtension(enc) + "_dec" + Path.GetExtension(enc));
@@ -349,587 +300,629 @@ namespace N_m3u8DL_RE.DownloadManager
                             result.ActualFilePath = dec;
                         }
                     }
-                });
+                    if (!readInfo)
+                    {
+                        // ffmpeg读取信息
+                        Logger.WarnMarkUp(ResString.readingInfo);
+                        mediaInfos = await MediainfoUtil.ReadInfoAsync(DownloaderConfig.MyOptions.FFmpegBinaryPath!, result!.ActualFilePath);
+                        mediaInfos.ForEach(info => Logger.InfoMarkUp(info.ToStringMarkUp()));
+                        lock (lockObj)
+                        {
+                            if (audioStart == null) audioStart = mediaInfos.FirstOrDefault(x => x.Type == "Audio")?.StartTime;
+                        }
+                        ChangeSpecInfo(streamSpec, mediaInfos, ref useAACFilter);
+                        readInfo = true;
+                    }
+                }
+            }
 
-                //自动修复VTT raw字幕
-                if (DownloaderConfig.MyOptions.AutoSubtitleFix && streamSpec.MediaType == Common.Enum.MediaType.SUBTITLES
-                    && streamSpec.Extension != null && streamSpec.Extension.Contains("vtt"))
+            // 开始下载
+            var options = new ParallelOptions()
+            {
+                MaxDegreeOfParallelism = DownloaderConfig.MyOptions.ThreadCount
+            };
+            await Parallel.ForEachAsync(segments, options, async (seg, _) =>
+            {
+                // 获取文件名
+                var filename = GetSegmentName(seg, allHasDatetime, SamePathDic[task.Id]);
+                var index = seg.Index;
+                var path = Path.Combine(tmpDir, filename + $".{streamSpec.Extension ?? "clip"}.tmp");
+                var result = await Downloader.DownloadSegmentAsync(seg, path, speedContainer, headers);
+                FileDic[seg] = result;
+                if (result != null && result.Success)
+                    task.Increment(1);
+                // 实时解密
+                if (DownloaderConfig.MyOptions.MP4RealTimeDecryption && result != null && result.Success && !string.IsNullOrEmpty(currentKID))
                 {
-                    //排序字幕并修正时间戳
-                    var keys = FileDic.Keys.OrderBy(k => k.Index);
+                    var enc = result.ActualFilePath;
+                    var dec = Path.Combine(Path.GetDirectoryName(enc)!, Path.GetFileNameWithoutExtension(enc) + "_dec" + Path.GetExtension(enc));
+                    var dResult = await MP4DecryptUtil.DecryptAsync(DownloaderConfig.MyOptions.UseShakaPackager, mp4decrypt, DownloaderConfig.MyOptions.Keys, enc, dec, currentKID, mp4InitFile);
+                    if (dResult)
+                    {
+                        File.Delete(enc);
+                        result.ActualFilePath = dec;
+                    }
+                }
+            });
+
+            // 自动修复VTT raw字幕
+            if (DownloaderConfig.MyOptions.AutoSubtitleFix && streamSpec.MediaType == Common.Enum.MediaType.SUBTITLES
+                                                           && streamSpec.Extension != null && streamSpec.Extension.Contains("vtt"))
+            {
+                // 排序字幕并修正时间戳
+                var keys = FileDic.Keys.OrderBy(k => k.Index);
+                foreach (var seg in keys)
+                {
+                    var vttContent = File.ReadAllText(FileDic[seg]!.ActualFilePath);
+                    var waitCount = 0;
+                    while (DownloaderConfig.MyOptions.LiveFixVttByAudio && audioStart == null && waitCount++ < 5)
+                    {
+                        await Task.Delay(1000);
+                    }
+                    var subOffset = audioStart != null ? (long)audioStart.Value.TotalMilliseconds : 0L;
+                    var vtt = WebVttSub.Parse(vttContent, subOffset);
+                    // 手动计算MPEGTS
+                    if (currentVtt.MpegtsTimestamp == 0 && vtt.MpegtsTimestamp == 0)
+                    {
+                        vtt.MpegtsTimestamp = 90000 * (long)keys.Where(s => s.Index < seg.Index).Sum(s => s.Duration);
+                    }
+                    if (firstSub) { currentVtt = vtt; firstSub = false; }
+                    else currentVtt.AddCuesFromOne(vtt);
+                }
+            }
+
+            // 自动修复VTT mp4字幕
+            if (DownloaderConfig.MyOptions.AutoSubtitleFix && streamSpec.MediaType == Common.Enum.MediaType.SUBTITLES
+                                                           && streamSpec.Codecs != "stpp" && streamSpec.Extension != null && streamSpec.Extension.Contains("m4s"))
+            {
+                var initFile = FileDic.Values.Where(v => Path.GetFileName(v!.ActualFilePath).StartsWith("_init")).FirstOrDefault();
+                var iniFileBytes = File.ReadAllBytes(initFile!.ActualFilePath);
+                var (sawVtt, timescale) = MP4VttUtil.CheckInit(iniFileBytes);
+                if (sawVtt)
+                {
+                    var mp4s = FileDic.OrderBy(s => s.Key.Index).Select(s => s.Value).Select(v => v!.ActualFilePath).Where(p => p.EndsWith(".m4s")).ToArray();
+                    if (firstSub)
+                    {
+                        currentVtt = MP4VttUtil.ExtractSub(mp4s, timescale);
+                        firstSub = false;
+                    }
+                    else
+                    {
+                        var vtt = MP4VttUtil.ExtractSub(mp4s, timescale);
+                        currentVtt.AddCuesFromOne(vtt);
+                    }
+                }
+            }
+
+            // 自动修复TTML raw字幕
+            if (DownloaderConfig.MyOptions.AutoSubtitleFix && streamSpec.MediaType == Common.Enum.MediaType.SUBTITLES
+                                                           && streamSpec.Extension != null && streamSpec.Extension.Contains("ttml"))
+            {
+                var keys = FileDic.OrderBy(s => s.Key.Index).Where(v => v.Value!.ActualFilePath.EndsWith(".m4s")).Select(s => s.Key);
+                if (firstSub)
+                {
+                    if (baseTimestamp != 0)
+                    {
+                        var total = segmentsDuration;
+                        baseTimestamp -= (long)TimeSpan.FromSeconds(total).TotalMilliseconds;
+                    }
+                    var first = true;
                     foreach (var seg in keys)
                     {
-                        var vttContent = File.ReadAllText(FileDic[seg]!.ActualFilePath);
-                        var waitCount = 0;
-                        while (DownloaderConfig.MyOptions.LiveFixVttByAudio && audioStart == null && waitCount++ < 5)
-                        {
-                            await Task.Delay(1000);
-                        }
-                        var subOffset = audioStart != null ? (long)audioStart.Value.TotalMilliseconds : 0L;
-                        var vtt = WebVttSub.Parse(vttContent, subOffset);
-                        //手动计算MPEGTS
+                        var vtt = MP4TtmlUtil.ExtractFromTTML(FileDic[seg]!.ActualFilePath, 0, baseTimestamp);
+                        // 手动计算MPEGTS
                         if (currentVtt.MpegtsTimestamp == 0 && vtt.MpegtsTimestamp == 0)
                         {
                             vtt.MpegtsTimestamp = 90000 * (long)keys.Where(s => s.Index < seg.Index).Sum(s => s.Duration);
                         }
-                        if (firstSub) { currentVtt = vtt; firstSub = false; }
+                        if (first) { currentVtt = vtt; first = false; }
                         else currentVtt.AddCuesFromOne(vtt);
                     }
+                    firstSub = false;
                 }
-
-                //自动修复VTT mp4字幕
-                if (DownloaderConfig.MyOptions.AutoSubtitleFix && streamSpec.MediaType == Common.Enum.MediaType.SUBTITLES
-                    && streamSpec.Codecs != "stpp" && streamSpec.Extension != null && streamSpec.Extension.Contains("m4s"))
+                else
                 {
-                    var initFile = FileDic.Values.Where(v => Path.GetFileName(v!.ActualFilePath).StartsWith("_init")).FirstOrDefault();
-                    var iniFileBytes = File.ReadAllBytes(initFile!.ActualFilePath);
-                    var (sawVtt, timescale) = MP4VttUtil.CheckInit(iniFileBytes);
-                    if (sawVtt)
+                    foreach (var seg in keys)
                     {
-                        var mp4s = FileDic.OrderBy(s => s.Key.Index).Select(s => s.Value).Select(v => v!.ActualFilePath).Where(p => p.EndsWith(".m4s")).ToArray();
-                        if (firstSub)
+                        var vtt = MP4TtmlUtil.ExtractFromTTML(FileDic[seg]!.ActualFilePath, 0, baseTimestamp);
+                        // 手动计算MPEGTS
+                        if (currentVtt.MpegtsTimestamp == 0 && vtt.MpegtsTimestamp == 0)
                         {
-                            currentVtt = MP4VttUtil.ExtractSub(mp4s, timescale);
-                            firstSub = false;
-                        }
-                        else
-                        {
-                            var vtt = MP4VttUtil.ExtractSub(mp4s, timescale);
-                            currentVtt.AddCuesFromOne(vtt);
+                            vtt.MpegtsTimestamp = 90000 * (RecordedDurDic[task.Id] + (long)keys.Where(s => s.Index < seg.Index).Sum(s => s.Duration));
                         }
+                        currentVtt.AddCuesFromOne(vtt);
                     }
                 }
-
-                //自动修复TTML raw字幕
-                if (DownloaderConfig.MyOptions.AutoSubtitleFix && streamSpec.MediaType == Common.Enum.MediaType.SUBTITLES
-                    && streamSpec.Extension != null && streamSpec.Extension.Contains("ttml"))
-                {
-                    var keys = FileDic.OrderBy(s => s.Key.Index).Where(v => v.Value!.ActualFilePath.EndsWith(".m4s")).Select(s => s.Key);
-                    if (firstSub)
-                    {
-                        if (baseTimestamp != 0)
-                        {
-                            var total = segmentsDuration;
-                            baseTimestamp -= (long)TimeSpan.FromSeconds(total).TotalMilliseconds;
-                        }
-                        var first = true;
-                        foreach (var seg in keys)
-                        {
-                            var vtt = MP4TtmlUtil.ExtractFromTTML(FileDic[seg]!.ActualFilePath, 0, baseTimestamp);
-                            //手动计算MPEGTS
-                            if (currentVtt.MpegtsTimestamp == 0 && vtt.MpegtsTimestamp == 0)
-                            {
-                                vtt.MpegtsTimestamp = 90000 * (long)keys.Where(s => s.Index < seg.Index).Sum(s => s.Duration);
-                            }
-                            if (first) { currentVtt = vtt; first = false; }
-                            else currentVtt.AddCuesFromOne(vtt);
-                        }
-                        firstSub = false;
-                    }
-                    else
-                    {
-                        foreach (var seg in keys)
-                        {
-                            var vtt = MP4TtmlUtil.ExtractFromTTML(FileDic[seg]!.ActualFilePath, 0, baseTimestamp);
-                            //手动计算MPEGTS
-                            if (currentVtt.MpegtsTimestamp == 0 && vtt.MpegtsTimestamp == 0)
-                            {
-                                vtt.MpegtsTimestamp = 90000 * (RecordedDurDic[task.Id] + (long)keys.Where(s => s.Index < seg.Index).Sum(s => s.Duration));
-                            }
-                            currentVtt.AddCuesFromOne(vtt);
-                        }
-                    }
-                }
-
-                //自动修复TTML mp4字幕
-                if (DownloaderConfig.MyOptions.AutoSubtitleFix && streamSpec.MediaType == Common.Enum.MediaType.SUBTITLES
-                    && streamSpec.Extension != null && streamSpec.Extension.Contains("m4s")
-                    && streamSpec.Codecs != null && streamSpec.Codecs.Contains("stpp"))
-                {
-                    //sawTtml暂时不判断
-                    //var initFile = FileDic.Values.Where(v => Path.GetFileName(v!.ActualFilePath).StartsWith("_init")).FirstOrDefault();
-                    //var iniFileBytes = File.ReadAllBytes(initFile!.ActualFilePath);
-                    //var sawTtml = MP4TtmlUtil.CheckInit(iniFileBytes);
-                    var keys = FileDic.OrderBy(s => s.Key.Index).Where(v => v.Value!.ActualFilePath.EndsWith(".m4s")).Select(s => s.Key);
-                    if (firstSub)
-                    {
-                        if (baseTimestamp != 0)
-                        {
-                            var total = segmentsDuration;
-                            baseTimestamp -= (long)TimeSpan.FromSeconds(total).TotalMilliseconds;
-                        }
-                        var first = true;
-                        foreach (var seg in keys)
-                        {
-                            var vtt = MP4TtmlUtil.ExtractFromMp4(FileDic[seg]!.ActualFilePath, 0, baseTimestamp);
-                            //手动计算MPEGTS
-                            if (currentVtt.MpegtsTimestamp == 0 && vtt.MpegtsTimestamp == 0)
-                            {
-                                vtt.MpegtsTimestamp = 90000 * (long)keys.Where(s => s.Index < seg.Index).Sum(s => s.Duration);
-                            }
-                            if (first) { currentVtt = vtt; first = false; }
-                            else currentVtt.AddCuesFromOne(vtt);
-                        }
-                        firstSub = false;
-                    }
-                    else
-                    {
-                        foreach (var seg in keys)
-                        {
-                            var vtt = MP4TtmlUtil.ExtractFromMp4(FileDic[seg]!.ActualFilePath, 0, baseTimestamp);
-                            //手动计算MPEGTS
-                            if (currentVtt.MpegtsTimestamp == 0 && vtt.MpegtsTimestamp == 0)
-                            {
-                                vtt.MpegtsTimestamp = 90000 * (RecordedDurDic[task.Id] + (long)keys.Where(s => s.Index < seg.Index).Sum(s => s.Duration));
-                            }
-                            currentVtt.AddCuesFromOne(vtt);
-                        }
-                    }
-                }
-
-                RecordedDurDic[task.Id] += (int)segmentsDuration;
-
-                /*//写出m3u8
-                if (DownloaderConfig.MyOptions.LiveWriteHLS)
-                {
-                    var _saveDir = DownloaderConfig.MyOptions.SaveDir ?? Environment.CurrentDirectory;
-                    var _saveName = DownloaderConfig.MyOptions.SaveName ?? DateTime.Now.ToString("yyyyMMddHHmmss");
-                    await StreamingUtil.WriteStreamListAsync(FileDic, task.Id, 0, _saveName, _saveDir);
-                }*/
-
-                //合并逻辑
-                if (DownloaderConfig.MyOptions.LiveRealTimeMerge)
-                {
-                    //合并
-                    var outputExt = "." + streamSpec.Extension;
-                    if (streamSpec.Extension == null) outputExt = ".ts";
-                    else if (streamSpec.MediaType == MediaType.AUDIO && streamSpec.Extension == "m4s") outputExt = ".m4a";
-                    else if (streamSpec.MediaType != MediaType.SUBTITLES && streamSpec.Extension == "m4s") outputExt = ".mp4";
-                    else if (streamSpec.MediaType == MediaType.SUBTITLES)
-                    {
-                        if (DownloaderConfig.MyOptions.SubtitleFormat == Enum.SubtitleFormat.SRT) outputExt = ".srt";
-                        else outputExt = ".vtt";
-                    }
-
-                    var output = Path.Combine(saveDir, saveName + outputExt);
-
-                    //移除无效片段
-                    var badKeys = FileDic.Where(i => i.Value == null).Select(i => i.Key);
-                    foreach (var badKey in badKeys)
-                    {
-                        FileDic!.Remove(badKey, out _);
-                    }
-
-                    //设置输出流
-                    if (fileOutputStream == null)
-                    {
-                        //检测目标文件是否存在
-                        while (File.Exists(output))
-                        {
-                            Logger.WarnMarkUp($"{Path.GetFileName(output)} => {Path.GetFileName(output = Path.ChangeExtension(output, $"copy" + Path.GetExtension(output)))}");
-                        }
-
-                        if (!DownloaderConfig.MyOptions.LivePipeMux || streamSpec.MediaType == MediaType.SUBTITLES)
-                        {
-                            fileOutputStream = new FileStream(output, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.Read);
-                        }
-                        else 
-                        {
-                            //创建管道
-                            output = Path.ChangeExtension(output, ".ts");
-                            var pipeName = $"RE_pipe_{Guid.NewGuid()}";
-                            fileOutputStream = PipeUtil.CreatePipe(pipeName);
-                            Logger.InfoMarkUp($"{ResString.namedPipeCreated} [cyan]{pipeName.EscapeMarkup()}[/]");
-                            PipeSteamNamesDic[task.Id] = pipeName;
-                            if (PipeSteamNamesDic.Count == SelectedSteams.Where(x => x.MediaType != MediaType.SUBTITLES).Count()) 
-                            {
-                                var names = PipeSteamNamesDic.OrderBy(i => i.Key).Select(k => k.Value).ToArray();
-                                Logger.WarnMarkUp($"{ResString.namedPipeMux} [deepskyblue1]{Path.GetFileName(output).EscapeMarkup()}[/]");
-                                var t = PipeUtil.StartPipeMuxAsync(DownloaderConfig.MyOptions.FFmpegBinaryPath!, names, output);
-                            }
-
-                            //Windows only
-                            if (OperatingSystem.IsWindows())
-                                await (fileOutputStream as NamedPipeServerStream)!.WaitForConnectionAsync();
-                        }
-                    }
-
-                    if (streamSpec.MediaType != MediaType.SUBTITLES)
-                    {
-                        var initResult = streamSpec.Playlist!.MediaInit != null ? FileDic[streamSpec.Playlist!.MediaInit!]! : null;
-                        var files = FileDic.Where(f => f.Key != streamSpec.Playlist!.MediaInit).OrderBy(s => s.Key.Index).Select(f => f.Value).Select(v => v!.ActualFilePath).ToArray();
-                        if (initResult != null && mp4InitFile != "")
-                        {
-                            //shaka实时解密不需要init文件用于合并,mp4decrpyt需要
-                            if (!DownloaderConfig.MyOptions.UseShakaPackager)
-                            {
-                                files = new string[] { initResult.ActualFilePath }.Concat(files).ToArray();
-                            }
-                        }
-                        foreach (var inputFilePath in files)
-                        {
-                            using (var inputStream = File.OpenRead(inputFilePath))
-                            {
-                                inputStream.CopyTo(fileOutputStream);
-                            }
-                        }
-                        if (!DownloaderConfig.MyOptions.LiveKeepSegments)
-                        {
-                            foreach (var inputFilePath in files.Where(x => !Path.GetFileName(x).StartsWith("_init")))
-                            {
-                                File.Delete(inputFilePath);
-                            }
-                        }
-                        FileDic.Clear();
-                        if (initResult != null)
-                        {
-                            FileDic[streamSpec.Playlist!.MediaInit!] = initResult;
-                        }
-                    }
-                    else
-                    {
-                        var initResult = streamSpec.Playlist!.MediaInit != null ? FileDic[streamSpec.Playlist!.MediaInit!]! : null;
-                        var files = FileDic.OrderBy(s => s.Key.Index).Select(f => f.Value).Select(v => v!.ActualFilePath).ToArray();
-                        foreach (var inputFilePath in files)
-                        {
-                            if (!DownloaderConfig.MyOptions.LiveKeepSegments && !Path.GetFileName(inputFilePath).StartsWith("_init"))
-                            {
-                                File.Delete(inputFilePath);
-                            }
-                        }
-
-                        //处理图形字幕
-                        await SubtitleUtil.TryWriteImagePngsAsync(currentVtt, tmpDir);
-
-                        var subText = currentVtt.ToVtt();
-                        if (outputExt == ".srt")
-                        {
-                            subText = currentVtt.ToSrt();
-                        }
-                        var subBytes = Encoding.UTF8.GetBytes(subText);
-                        fileOutputStream.Position = 0;
-                        fileOutputStream.Write(subBytes);
-                        FileDic.Clear();
-                        if (initResult != null)
-                        {
-                            FileDic[streamSpec.Playlist!.MediaInit!] = initResult;
-                        }
-                    }
-
-                    //刷新buffer
-                    if (fileOutputStream != null)
-                    {
-                        fileOutputStream.Flush();
-                    }
-                }
-
-                if (STOP_FLAG && source.Count == 0) 
-                    break;
             }
 
-            if (fileOutputStream != null)
+            // 自动修复TTML mp4字幕
+            if (DownloaderConfig.MyOptions.AutoSubtitleFix && streamSpec.MediaType == Common.Enum.MediaType.SUBTITLES
+                                                           && streamSpec.Extension != null && streamSpec.Extension.Contains("m4s")
+                                                           && streamSpec.Codecs != null && streamSpec.Codecs.Contains("stpp"))
             {
-                if (!DownloaderConfig.MyOptions.LivePipeMux)
+                // sawTtml暂时不判断
+                // var initFile = FileDic.Values.Where(v => Path.GetFileName(v!.ActualFilePath).StartsWith("_init")).FirstOrDefault();
+                // var iniFileBytes = File.ReadAllBytes(initFile!.ActualFilePath);
+                // var sawTtml = MP4TtmlUtil.CheckInit(iniFileBytes);
+                var keys = FileDic.OrderBy(s => s.Key.Index).Where(v => v.Value!.ActualFilePath.EndsWith(".m4s")).Select(s => s.Key);
+                if (firstSub)
                 {
-                    //记录所有文件信息
-                    OutputFiles.Add(new OutputFile()
+                    if (baseTimestamp != 0)
                     {
-                        Index = task.Id,
-                        FilePath = (fileOutputStream as FileStream)!.Name,
-                        LangCode = streamSpec.Language,
-                        Description = streamSpec.Name,
-                        Mediainfos = mediaInfos,
-                        MediaType = streamSpec.MediaType,
-                    });
+                        var total = segmentsDuration;
+                        baseTimestamp -= (long)TimeSpan.FromSeconds(total).TotalMilliseconds;
+                    }
+                    var first = true;
+                    foreach (var seg in keys)
+                    {
+                        var vtt = MP4TtmlUtil.ExtractFromMp4(FileDic[seg]!.ActualFilePath, 0, baseTimestamp);
+                        // 手动计算MPEGTS
+                        if (currentVtt.MpegtsTimestamp == 0 && vtt.MpegtsTimestamp == 0)
+                        {
+                            vtt.MpegtsTimestamp = 90000 * (long)keys.Where(s => s.Index < seg.Index).Sum(s => s.Duration);
+                        }
+                        if (first) { currentVtt = vtt; first = false; }
+                        else currentVtt.AddCuesFromOne(vtt);
+                    }
+                    firstSub = false;
                 }
-                fileOutputStream.Close();
-                fileOutputStream.Dispose();
-            }
-
-            return true;
-        }
-
-        private async Task PlayListProduceAsync(Dictionary<StreamSpec, ProgressTask> dic)
-        {
-            while (!STOP_FLAG)
-            {
-                if (WAIT_SEC != 0)
+                else
                 {
-                    //1. MPD 所有URL相同 单次请求即可获得所有轨道的信息
-                    //2. M3U8 所有URL不同 才需要多次请求
-
-                    await Parallel.ForEachAsync(dic, async (dic, _) =>
+                    foreach (var seg in keys)
                     {
-                        var streamSpec = dic.Key;
-                        var task = dic.Value;
-
-                        //达到上限时 不需要刷新了
-                        if (RecordLimitReachedDic[task.Id])
-                            return;
-
-                        var allHasDatetime = streamSpec.Playlist!.MediaParts[0].MediaSegments.All(s => s.DateTime != null);
-                        if (!SamePathDic.ContainsKey(task.Id))
+                        var vtt = MP4TtmlUtil.ExtractFromMp4(FileDic[seg]!.ActualFilePath, 0, baseTimestamp);
+                        // 手动计算MPEGTS
+                        if (currentVtt.MpegtsTimestamp == 0 && vtt.MpegtsTimestamp == 0)
                         {
-                            var allName = streamSpec.Playlist!.MediaParts[0].MediaSegments.Select(s => OtherUtil.GetFileNameFromInput(s.Url, false));
-                            var allSamePath = allName.Count() > 1 && allName.Distinct().Count() == 1;
-                            SamePathDic[task.Id] = allSamePath;
-                        }
-                        //过滤不需要下载的片段
-                        FilterMediaSegments(streamSpec, task, allHasDatetime, SamePathDic[task.Id]);
-                        var newList = streamSpec.Playlist!.MediaParts[0].MediaSegments;
-                        if (newList.Count > 0)
-                        {
-                            task.MaxValue += newList.Count;
-                            //推送给消费者
-                            await BlockDic[task.Id].SendAsync(newList);
-                            //更新最新链接
-                            LastFileNameDic[task.Id] = GetSegmentName(newList.Last(), allHasDatetime, SamePathDic[task.Id]);
-                            //尝试更新时间戳
-                            var dt = newList.Last().DateTime;
-                            DateTimeDic[task.Id] = dt != null ? GetUnixTimestamp(dt.Value) : 0L;
-                            //累加已获取到的时长
-                            RefreshedDurDic[task.Id] += (int)newList.Sum(s => s.Duration);
-                        }
-
-                        if (!STOP_FLAG && RefreshedDurDic[task.Id] >= DownloaderConfig.MyOptions.LiveRecordLimit?.TotalSeconds)
-                        {
-                            RecordLimitReachedDic[task.Id] = true;
-                        }
-
-                        //检测时长限制
-                        if (!STOP_FLAG && RecordLimitReachedDic.Values.All(x => x == true))
-                        {
-                            Logger.WarnMarkUp($"[darkorange3_1]{ResString.liveLimitReached}[/]");
-                            STOP_FLAG = true;
-                            CancellationTokenSource.Cancel();
-                        }
-                    });
-
-                    try
-                    {
-                        //Logger.WarnMarkUp($"wait {waitSec}s");
-                        if (!STOP_FLAG) await Task.Delay(WAIT_SEC * 1000, CancellationTokenSource.Token);
-                        //刷新列表
-                        if (!STOP_FLAG) await StreamExtractor.RefreshPlayListAsync(dic.Keys.ToList());
-                    }
-                    catch (OperationCanceledException oce) when (oce.CancellationToken == CancellationTokenSource.Token)
-                    {
-                        //不需要做事
-                    }
-                    catch (Exception e)
-                    {
-                        Logger.ErrorMarkUp(e);
-                        STOP_FLAG = true;
-                        //停止所有Block
-                        foreach (var target in BlockDic.Values)
-                        {
-                            target.Complete();
+                            vtt.MpegtsTimestamp = 90000 * (RecordedDurDic[task.Id] + (long)keys.Where(s => s.Index < seg.Index).Sum(s => s.Duration));
                         }
+                        currentVtt.AddCuesFromOne(vtt);
                     }
                 }
             }
-        }
 
-        private void FilterMediaSegments(StreamSpec streamSpec, ProgressTask task, bool allHasDatetime, bool allSamePath)
-        {
-            if (string.IsNullOrEmpty(LastFileNameDic[task.Id]) && DateTimeDic[task.Id] == 0) return;
+            RecordedDurDic[task.Id] += (int)segmentsDuration;
 
-            var index = -1;
-            var dateTime = DateTimeDic[task.Id];
-            var lastName = LastFileNameDic[task.Id];
-
-            //优先使用dateTime判断
-            if (dateTime != 0 && streamSpec.Playlist!.MediaParts[0].MediaSegments.All(s => s.DateTime != null)) 
-            {
-                index = streamSpec.Playlist!.MediaParts[0].MediaSegments.FindIndex(s => GetUnixTimestamp(s.DateTime!.Value) == dateTime);
-            }
-            else
-            {
-                index = streamSpec.Playlist!.MediaParts[0].MediaSegments.FindIndex(s => GetSegmentName(s, allHasDatetime, allSamePath) == lastName);
-            }
-
-            if (index > -1)
-            {
-                //修正Index
-                var list = streamSpec.Playlist!.MediaParts[0].MediaSegments.Skip(index + 1).ToList();
-                if (list.Count > 0)
-                {
-                    var newMin = list.Min(s => s.Index);
-                    var oldMax = MaxIndexDic[task.Id];
-                    if (newMin < oldMax)
-                    {
-                        var offset = oldMax - newMin + 1;
-                        foreach (var item in list)
-                        {
-                            item.Index += offset;
-                        }
-                    }
-                    MaxIndexDic[task.Id] = list.Max(s => s.Index);
-                }
-                streamSpec.Playlist!.MediaParts[0].MediaSegments = list;
-            }
-        }
-
-        public async Task<bool> StartRecordAsync()
-        {
-            var takeLastCount = DownloaderConfig.MyOptions.LiveTakeCount;
-            ConcurrentDictionary<int, SpeedContainer> SpeedContainerDic = new(); //速度计算
-            ConcurrentDictionary<StreamSpec, bool?> Results = new();
-            //同步流
-            FilterUtil.SyncStreams(SelectedSteams, takeLastCount);
-            //设置等待时间
-            if (WAIT_SEC == 0)
-            {
-                WAIT_SEC = (int)(SelectedSteams.Min(s => s.Playlist!.MediaParts[0].MediaSegments.Sum(s => s.Duration)) / 2);
-                WAIT_SEC -= 2; //再提前两秒吧 留出冗余
-                if (DownloaderConfig.MyOptions.LiveWaitTime != null)
-                    WAIT_SEC = DownloaderConfig.MyOptions.LiveWaitTime.Value;
-                if (WAIT_SEC <= 0) WAIT_SEC = 1;
-                Logger.WarnMarkUp($"set refresh interval to {WAIT_SEC} seconds");
-            }
-            //如果没有选中音频 取消通过音频修复vtt时间轴
-            if (!SelectedSteams.Any(x => x.MediaType == MediaType.AUDIO))
-            {
-                DownloaderConfig.MyOptions.LiveFixVttByAudio = false;
-            }
-
-            /*//写出master
+            /*// 写出m3u8
             if (DownloaderConfig.MyOptions.LiveWriteHLS)
             {
-                var saveDir = DownloaderConfig.MyOptions.SaveDir ?? Environment.CurrentDirectory;
-                var saveName = DownloaderConfig.MyOptions.SaveName ?? DateTime.Now.ToString("yyyyMMddHHmmss");
-                await StreamingUtil.WriteMasterListAsync(SelectedSteams, saveName, saveDir);
+                var _saveDir = DownloaderConfig.MyOptions.SaveDir ?? Environment.CurrentDirectory;
+                var _saveName = DownloaderConfig.MyOptions.SaveName ?? DateTime.Now.ToString("yyyyMMddHHmmss");
+                await StreamingUtil.WriteStreamListAsync(FileDic, task.Id, 0, _saveName, _saveDir);
             }*/
 
-            var progress = CustomAnsiConsole.Console.Progress().AutoClear(true);
-            progress.AutoRefresh = DownloaderConfig.MyOptions.LogLevel != LogLevel.OFF;
-            
-            //进度条的列定义
-            var progressColumns = new ProgressColumn[]
+            // 合并逻辑
+            if (DownloaderConfig.MyOptions.LiveRealTimeMerge)
             {
-                new TaskDescriptionColumn() { Alignment = Justify.Left },
-                new RecordingDurationColumn(RecordedDurDic, RefreshedDurDic), //时长显示
-                new RecordingStatusColumn(),
-                new PercentageColumn(),
-                new DownloadSpeedColumn(SpeedContainerDic), //速度计算
-                new SpinnerColumn(),
-            };
-            if (DownloaderConfig.MyOptions.NoAnsiColor)
-            {
-                progressColumns = progressColumns.SkipLast(1).ToArray();
-            }
-            progress.Columns(progressColumns);
-
-            await progress.StartAsync(async ctx =>
-            {
-                //创建任务
-                var dic = SelectedSteams.Select(item =>
+                // 合并
+                var outputExt = "." + streamSpec.Extension;
+                if (streamSpec.Extension == null) outputExt = ".ts";
+                else if (streamSpec.MediaType == MediaType.AUDIO && streamSpec.Extension == "m4s") outputExt = ".m4a";
+                else if (streamSpec.MediaType != MediaType.SUBTITLES && streamSpec.Extension == "m4s") outputExt = ".mp4";
+                else if (streamSpec.MediaType == MediaType.SUBTITLES)
                 {
-                    var task = ctx.AddTask(item.ToShortShortString(), autoStart: false, maxValue: 0);
-                    SpeedContainerDic[task.Id] = new SpeedContainer(); //速度计算
-                    //限速设置
-                    if (DownloaderConfig.MyOptions.MaxSpeed != null)
+                    if (DownloaderConfig.MyOptions.SubtitleFormat == Enum.SubtitleFormat.SRT) outputExt = ".srt";
+                    else outputExt = ".vtt";
+                }
+
+                var output = Path.Combine(saveDir, saveName + outputExt);
+
+                // 移除无效片段
+                var badKeys = FileDic.Where(i => i.Value == null).Select(i => i.Key);
+                foreach (var badKey in badKeys)
+                {
+                    FileDic!.Remove(badKey, out _);
+                }
+
+                // 设置输出流
+                if (fileOutputStream == null)
+                {
+                    // 检测目标文件是否存在
+                    while (File.Exists(output))
                     {
-                        SpeedContainerDic[task.Id].SpeedLimit = DownloaderConfig.MyOptions.MaxSpeed.Value;
+                        Logger.WarnMarkUp($"{Path.GetFileName(output)} => {Path.GetFileName(output = Path.ChangeExtension(output, $"copy" + Path.GetExtension(output)))}");
                     }
-                    LastFileNameDic[task.Id] = "";
-                    RecordLimitReachedDic[task.Id] = false;
-                    DateTimeDic[task.Id] = 0L;
-                    RecordedDurDic[task.Id] = 0;
-                    RefreshedDurDic[task.Id] = 0;
-                    MaxIndexDic[task.Id] = item.Playlist?.MediaParts[0].MediaSegments.LastOrDefault()?.Index ?? 0L; //最大Index
-                    BlockDic[task.Id] = new BufferBlock<List<MediaSegment>>();
-                    return (item, task);
-                }).ToDictionary(item => item.item, item => item.task);
 
-                DownloaderConfig.MyOptions.ConcurrentDownload = true;
-                DownloaderConfig.MyOptions.MP4RealTimeDecryption = true;
-                DownloaderConfig.MyOptions.LiveRecordLimit = DownloaderConfig.MyOptions.LiveRecordLimit ?? TimeSpan.MaxValue;
-                if (DownloaderConfig.MyOptions.MP4RealTimeDecryption && !DownloaderConfig.MyOptions.UseShakaPackager
-                    && DownloaderConfig.MyOptions.Keys != null && DownloaderConfig.MyOptions.Keys.Length > 0)
-                    Logger.WarnMarkUp($"[darkorange3_1]{ResString.realTimeDecMessage}[/]");
-                var limit = DownloaderConfig.MyOptions.LiveRecordLimit;
-                if (limit != TimeSpan.MaxValue)
-                    Logger.WarnMarkUp($"[darkorange3_1]{ResString.liveLimit}{GlobalUtil.FormatTime((int)limit.Value.TotalSeconds)}[/]");
-                //录制直播时,用户选了几个流就并发录几个
-                var options = new ParallelOptions()
-                {
-                    MaxDegreeOfParallelism = SelectedSteams.Count
-                };
-                //开始刷新
-                var producerTask = PlayListProduceAsync(dic);
-                await Task.Delay(200);
-                //并发下载
-                await Parallel.ForEachAsync(dic, options, async (kp, _) =>
-                {
-                    var task = kp.Value;
-                    var consumerTask = RecordStreamAsync(kp.Key, task, SpeedContainerDic[task.Id], BlockDic[task.Id]);
-                    Results[kp.Key] = await consumerTask;
-                });
-            });
-
-            var success = Results.Values.All(v => v == true);
-
-            //删除临时文件夹
-            if (!DownloaderConfig.MyOptions.SkipMerge && DownloaderConfig.MyOptions.DelAfterDone && success)
-            {
-                foreach (var item in StreamExtractor.RawFiles)
-                {
-                    var file = Path.Combine(DownloaderConfig.DirPrefix, item.Key);
-                    if (File.Exists(file)) File.Delete(file);
-                }
-                OtherUtil.SafeDeleteDir(DownloaderConfig.DirPrefix);
-            }
-
-            //混流
-            if (success && DownloaderConfig.MyOptions.MuxAfterDone && OutputFiles.Count > 0)
-            {
-                OutputFiles = OutputFiles.OrderBy(o => o.Index).ToList();
-                //是否跳过字幕
-                if (DownloaderConfig.MyOptions.MuxOptions.SkipSubtitle)
-                {
-                    OutputFiles = OutputFiles.Where(o => o.MediaType != MediaType.SUBTITLES).ToList();
-                }
-                if (DownloaderConfig.MyOptions.MuxImports != null)
-                {
-                    OutputFiles.AddRange(DownloaderConfig.MyOptions.MuxImports);
-                }
-                OutputFiles.ForEach(f => Logger.WarnMarkUp($"[grey]{Path.GetFileName(f.FilePath).EscapeMarkup()}[/]"));
-                var saveDir = DownloaderConfig.MyOptions.SaveDir ?? Environment.CurrentDirectory;
-                var ext = OtherUtil.GetMuxExtension(DownloaderConfig.MyOptions.MuxOptions.MuxFormat);
-                var dirName = Path.GetFileName(DownloaderConfig.DirPrefix);
-                var outName = $"{dirName}.MUX";
-                var outPath = Path.Combine(saveDir, outName);
-                Logger.WarnMarkUp($"Muxing to [grey]{outName.EscapeMarkup()}{ext}[/]");
-                var result = false;
-                if (DownloaderConfig.MyOptions.MuxOptions.UseMkvmerge) result = MergeUtil.MuxInputsByMkvmerge(DownloaderConfig.MyOptions.MkvmergeBinaryPath!, OutputFiles.ToArray(), outPath);
-                else result = MergeUtil.MuxInputsByFFmpeg(DownloaderConfig.MyOptions.FFmpegBinaryPath!, OutputFiles.ToArray(), outPath, DownloaderConfig.MyOptions.MuxOptions.MuxFormat, !DownloaderConfig.MyOptions.NoDateInfo);
-                //完成后删除各轨道文件
-                if (result)
-                {
-                    if (!DownloaderConfig.MyOptions.MuxOptions.KeepFiles)
+                    if (!DownloaderConfig.MyOptions.LivePipeMux || streamSpec.MediaType == MediaType.SUBTITLES)
                     {
-                        Logger.WarnMarkUp("[grey]Cleaning files...[/]");
-                        OutputFiles.ForEach(f => File.Delete(f.FilePath));
-                        var tmpDir = DownloaderConfig.MyOptions.TmpDir ?? Environment.CurrentDirectory;
-                        OtherUtil.SafeDeleteDir(tmpDir);
+                        fileOutputStream = new FileStream(output, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.Read);
+                    }
+                    else 
+                    {
+                        // 创建管道
+                        output = Path.ChangeExtension(output, ".ts");
+                        var pipeName = $"RE_pipe_{Guid.NewGuid()}";
+                        fileOutputStream = PipeUtil.CreatePipe(pipeName);
+                        Logger.InfoMarkUp($"{ResString.namedPipeCreated} [cyan]{pipeName.EscapeMarkup()}[/]");
+                        PipeSteamNamesDic[task.Id] = pipeName;
+                        if (PipeSteamNamesDic.Count == SelectedSteams.Where(x => x.MediaType != MediaType.SUBTITLES).Count()) 
+                        {
+                            var names = PipeSteamNamesDic.OrderBy(i => i.Key).Select(k => k.Value).ToArray();
+                            Logger.WarnMarkUp($"{ResString.namedPipeMux} [deepskyblue1]{Path.GetFileName(output).EscapeMarkup()}[/]");
+                            var t = PipeUtil.StartPipeMuxAsync(DownloaderConfig.MyOptions.FFmpegBinaryPath!, names, output);
+                        }
+
+                        // Windows only
+                        if (OperatingSystem.IsWindows())
+                            await (fileOutputStream as NamedPipeServerStream)!.WaitForConnectionAsync();
+                    }
+                }
+
+                if (streamSpec.MediaType != MediaType.SUBTITLES)
+                {
+                    var initResult = streamSpec.Playlist!.MediaInit != null ? FileDic[streamSpec.Playlist!.MediaInit!]! : null;
+                    var files = FileDic.Where(f => f.Key != streamSpec.Playlist!.MediaInit).OrderBy(s => s.Key.Index).Select(f => f.Value).Select(v => v!.ActualFilePath).ToArray();
+                    if (initResult != null && mp4InitFile != "")
+                    {
+                        // shaka实时解密不需要init文件用于合并,mp4decrpyt需要
+                        if (!DownloaderConfig.MyOptions.UseShakaPackager)
+                        {
+                            files = new string[] { initResult.ActualFilePath }.Concat(files).ToArray();
+                        }
+                    }
+                    foreach (var inputFilePath in files)
+                    {
+                        using (var inputStream = File.OpenRead(inputFilePath))
+                        {
+                            inputStream.CopyTo(fileOutputStream);
+                        }
+                    }
+                    if (!DownloaderConfig.MyOptions.LiveKeepSegments)
+                    {
+                        foreach (var inputFilePath in files.Where(x => !Path.GetFileName(x).StartsWith("_init")))
+                        {
+                            File.Delete(inputFilePath);
+                        }
+                    }
+                    FileDic.Clear();
+                    if (initResult != null)
+                    {
+                        FileDic[streamSpec.Playlist!.MediaInit!] = initResult;
                     }
                 }
                 else
                 {
-                    success = false;
-                    Logger.ErrorMarkUp($"Mux failed");
+                    var initResult = streamSpec.Playlist!.MediaInit != null ? FileDic[streamSpec.Playlist!.MediaInit!]! : null;
+                    var files = FileDic.OrderBy(s => s.Key.Index).Select(f => f.Value).Select(v => v!.ActualFilePath).ToArray();
+                    foreach (var inputFilePath in files)
+                    {
+                        if (!DownloaderConfig.MyOptions.LiveKeepSegments && !Path.GetFileName(inputFilePath).StartsWith("_init"))
+                        {
+                            File.Delete(inputFilePath);
+                        }
+                    }
+
+                    // 处理图形字幕
+                    await SubtitleUtil.TryWriteImagePngsAsync(currentVtt, tmpDir);
+
+                    var subText = currentVtt.ToVtt();
+                    if (outputExt == ".srt")
+                    {
+                        subText = currentVtt.ToSrt();
+                    }
+                    var subBytes = Encoding.UTF8.GetBytes(subText);
+                    fileOutputStream.Position = 0;
+                    fileOutputStream.Write(subBytes);
+                    FileDic.Clear();
+                    if (initResult != null)
+                    {
+                        FileDic[streamSpec.Playlist!.MediaInit!] = initResult;
+                    }
                 }
-                //判断是否要改名
-                var newPath = Path.ChangeExtension(outPath, ext);
-                if (result && !File.Exists(newPath))
+
+                // 刷新buffer
+                if (fileOutputStream != null)
                 {
-                    Logger.WarnMarkUp($"Rename to [grey]{Path.GetFileName(newPath).EscapeMarkup()}[/]");
-                    File.Move(outPath + ext, newPath);
+                    fileOutputStream.Flush();
                 }
             }
 
-            return success;
+            if (STOP_FLAG && source.Count == 0) 
+                break;
+        }
+
+        if (fileOutputStream != null)
+        {
+            if (!DownloaderConfig.MyOptions.LivePipeMux)
+            {
+                // 记录所有文件信息
+                OutputFiles.Add(new OutputFile()
+                {
+                    Index = task.Id,
+                    FilePath = (fileOutputStream as FileStream)!.Name,
+                    LangCode = streamSpec.Language,
+                    Description = streamSpec.Name,
+                    Mediainfos = mediaInfos,
+                    MediaType = streamSpec.MediaType,
+                });
+            }
+            fileOutputStream.Close();
+            fileOutputStream.Dispose();
+        }
+
+        return true;
+    }
+
+    private async Task PlayListProduceAsync(Dictionary<StreamSpec, ProgressTask> dic)
+    {
+        while (!STOP_FLAG)
+        {
+            if (WAIT_SEC != 0)
+            {
+                // 1. MPD 所有URL相同 单次请求即可获得所有轨道的信息
+                // 2. M3U8 所有URL不同 才需要多次请求
+
+                await Parallel.ForEachAsync(dic, async (dic, _) =>
+                {
+                    var streamSpec = dic.Key;
+                    var task = dic.Value;
+
+                    // 达到上限时 不需要刷新了
+                    if (RecordLimitReachedDic[task.Id])
+                        return;
+
+                    var allHasDatetime = streamSpec.Playlist!.MediaParts[0].MediaSegments.All(s => s.DateTime != null);
+                    if (!SamePathDic.ContainsKey(task.Id))
+                    {
+                        var allName = streamSpec.Playlist!.MediaParts[0].MediaSegments.Select(s => OtherUtil.GetFileNameFromInput(s.Url, false));
+                        var allSamePath = allName.Count() > 1 && allName.Distinct().Count() == 1;
+                        SamePathDic[task.Id] = allSamePath;
+                    }
+                    // 过滤不需要下载的片段
+                    FilterMediaSegments(streamSpec, task, allHasDatetime, SamePathDic[task.Id]);
+                    var newList = streamSpec.Playlist!.MediaParts[0].MediaSegments;
+                    if (newList.Count > 0)
+                    {
+                        task.MaxValue += newList.Count;
+                        // 推送给消费者
+                        await BlockDic[task.Id].SendAsync(newList);
+                        // 更新最新链接
+                        LastFileNameDic[task.Id] = GetSegmentName(newList.Last(), allHasDatetime, SamePathDic[task.Id]);
+                        // 尝试更新时间戳
+                        var dt = newList.Last().DateTime;
+                        DateTimeDic[task.Id] = dt != null ? GetUnixTimestamp(dt.Value) : 0L;
+                        // 累加已获取到的时长
+                        RefreshedDurDic[task.Id] += (int)newList.Sum(s => s.Duration);
+                    }
+
+                    if (!STOP_FLAG && RefreshedDurDic[task.Id] >= DownloaderConfig.MyOptions.LiveRecordLimit?.TotalSeconds)
+                    {
+                        RecordLimitReachedDic[task.Id] = true;
+                    }
+
+                    // 检测时长限制
+                    if (!STOP_FLAG && RecordLimitReachedDic.Values.All(x => x == true))
+                    {
+                        Logger.WarnMarkUp($"[darkorange3_1]{ResString.liveLimitReached}[/]");
+                        STOP_FLAG = true;
+                        CancellationTokenSource.Cancel();
+                    }
+                });
+
+                try
+                {
+                    // Logger.WarnMarkUp($"wait {waitSec}s");
+                    if (!STOP_FLAG) await Task.Delay(WAIT_SEC * 1000, CancellationTokenSource.Token);
+                    // 刷新列表
+                    if (!STOP_FLAG) await StreamExtractor.RefreshPlayListAsync(dic.Keys.ToList());
+                }
+                catch (OperationCanceledException oce) when (oce.CancellationToken == CancellationTokenSource.Token)
+                {
+                    // 不需要做事
+                }
+                catch (Exception e)
+                {
+                    Logger.ErrorMarkUp(e);
+                    STOP_FLAG = true;
+                    // 停止所有Block
+                    foreach (var target in BlockDic.Values)
+                    {
+                        target.Complete();
+                    }
+                }
+            }
         }
     }
-}
+
+    private void FilterMediaSegments(StreamSpec streamSpec, ProgressTask task, bool allHasDatetime, bool allSamePath)
+    {
+        if (string.IsNullOrEmpty(LastFileNameDic[task.Id]) && DateTimeDic[task.Id] == 0) return;
+
+        var index = -1;
+        var dateTime = DateTimeDic[task.Id];
+        var lastName = LastFileNameDic[task.Id];
+
+        // 优先使用dateTime判断
+        if (dateTime != 0 && streamSpec.Playlist!.MediaParts[0].MediaSegments.All(s => s.DateTime != null)) 
+        {
+            index = streamSpec.Playlist!.MediaParts[0].MediaSegments.FindIndex(s => GetUnixTimestamp(s.DateTime!.Value) == dateTime);
+        }
+        else
+        {
+            index = streamSpec.Playlist!.MediaParts[0].MediaSegments.FindIndex(s => GetSegmentName(s, allHasDatetime, allSamePath) == lastName);
+        }
+
+        if (index > -1)
+        {
+            // 修正Index
+            var list = streamSpec.Playlist!.MediaParts[0].MediaSegments.Skip(index + 1).ToList();
+            if (list.Count > 0)
+            {
+                var newMin = list.Min(s => s.Index);
+                var oldMax = MaxIndexDic[task.Id];
+                if (newMin < oldMax)
+                {
+                    var offset = oldMax - newMin + 1;
+                    foreach (var item in list)
+                    {
+                        item.Index += offset;
+                    }
+                }
+                MaxIndexDic[task.Id] = list.Max(s => s.Index);
+            }
+            streamSpec.Playlist!.MediaParts[0].MediaSegments = list;
+        }
+    }
+
+    public async Task<bool> StartRecordAsync()
+    {
+        var takeLastCount = DownloaderConfig.MyOptions.LiveTakeCount;
+        ConcurrentDictionary<int, SpeedContainer> SpeedContainerDic = new(); // 速度计算
+        ConcurrentDictionary<StreamSpec, bool?> Results = new();
+        // 同步流
+        FilterUtil.SyncStreams(SelectedSteams, takeLastCount);
+        // 设置等待时间
+        if (WAIT_SEC == 0)
+        {
+            WAIT_SEC = (int)(SelectedSteams.Min(s => s.Playlist!.MediaParts[0].MediaSegments.Sum(s => s.Duration)) / 2);
+            WAIT_SEC -= 2; // 再提前两秒吧 留出冗余
+            if (DownloaderConfig.MyOptions.LiveWaitTime != null)
+                WAIT_SEC = DownloaderConfig.MyOptions.LiveWaitTime.Value;
+            if (WAIT_SEC <= 0) WAIT_SEC = 1;
+            Logger.WarnMarkUp($"set refresh interval to {WAIT_SEC} seconds");
+        }
+        // 如果没有选中音频 取消通过音频修复vtt时间轴
+        if (!SelectedSteams.Any(x => x.MediaType == MediaType.AUDIO))
+        {
+            DownloaderConfig.MyOptions.LiveFixVttByAudio = false;
+        }
+
+        /*// 写出master
+        if (DownloaderConfig.MyOptions.LiveWriteHLS)
+        {
+            var saveDir = DownloaderConfig.MyOptions.SaveDir ?? Environment.CurrentDirectory;
+            var saveName = DownloaderConfig.MyOptions.SaveName ?? DateTime.Now.ToString("yyyyMMddHHmmss");
+            await StreamingUtil.WriteMasterListAsync(SelectedSteams, saveName, saveDir);
+        }*/
+
+        var progress = CustomAnsiConsole.Console.Progress().AutoClear(true);
+        progress.AutoRefresh = DownloaderConfig.MyOptions.LogLevel != LogLevel.OFF;
+            
+        // 进度条的列定义
+        var progressColumns = new ProgressColumn[]
+        {
+            new TaskDescriptionColumn() { Alignment = Justify.Left },
+            new RecordingDurationColumn(RecordedDurDic, RefreshedDurDic), // 时长显示
+            new RecordingStatusColumn(),
+            new PercentageColumn(),
+            new DownloadSpeedColumn(SpeedContainerDic), // 速度计算
+            new SpinnerColumn(),
+        };
+        if (DownloaderConfig.MyOptions.NoAnsiColor)
+        {
+            progressColumns = progressColumns.SkipLast(1).ToArray();
+        }
+        progress.Columns(progressColumns);
+
+        await progress.StartAsync(async ctx =>
+        {
+            // 创建任务
+            var dic = SelectedSteams.Select(item =>
+            {
+                var task = ctx.AddTask(item.ToShortShortString(), autoStart: false, maxValue: 0);
+                SpeedContainerDic[task.Id] = new SpeedContainer(); // 速度计算
+                // 限速设置
+                if (DownloaderConfig.MyOptions.MaxSpeed != null)
+                {
+                    SpeedContainerDic[task.Id].SpeedLimit = DownloaderConfig.MyOptions.MaxSpeed.Value;
+                }
+                LastFileNameDic[task.Id] = "";
+                RecordLimitReachedDic[task.Id] = false;
+                DateTimeDic[task.Id] = 0L;
+                RecordedDurDic[task.Id] = 0;
+                RefreshedDurDic[task.Id] = 0;
+                MaxIndexDic[task.Id] = item.Playlist?.MediaParts[0].MediaSegments.LastOrDefault()?.Index ?? 0L; // 最大Index
+                BlockDic[task.Id] = new BufferBlock<List<MediaSegment>>();
+                return (item, task);
+            }).ToDictionary(item => item.item, item => item.task);
+
+            DownloaderConfig.MyOptions.ConcurrentDownload = true;
+            DownloaderConfig.MyOptions.MP4RealTimeDecryption = true;
+            DownloaderConfig.MyOptions.LiveRecordLimit = DownloaderConfig.MyOptions.LiveRecordLimit ?? TimeSpan.MaxValue;
+            if (DownloaderConfig.MyOptions.MP4RealTimeDecryption && !DownloaderConfig.MyOptions.UseShakaPackager
+                                                                 && DownloaderConfig.MyOptions.Keys != null && DownloaderConfig.MyOptions.Keys.Length > 0)
+                Logger.WarnMarkUp($"[darkorange3_1]{ResString.realTimeDecMessage}[/]");
+            var limit = DownloaderConfig.MyOptions.LiveRecordLimit;
+            if (limit != TimeSpan.MaxValue)
+                Logger.WarnMarkUp($"[darkorange3_1]{ResString.liveLimit}{GlobalUtil.FormatTime((int)limit.Value.TotalSeconds)}[/]");
+            // 录制直播时,用户选了几个流就并发录几个
+            var options = new ParallelOptions()
+            {
+                MaxDegreeOfParallelism = SelectedSteams.Count
+            };
+            // 开始刷新
+            var producerTask = PlayListProduceAsync(dic);
+            await Task.Delay(200);
+            // 并发下载
+            await Parallel.ForEachAsync(dic, options, async (kp, _) =>
+            {
+                var task = kp.Value;
+                var consumerTask = RecordStreamAsync(kp.Key, task, SpeedContainerDic[task.Id], BlockDic[task.Id]);
+                Results[kp.Key] = await consumerTask;
+            });
+        });
+
+        var success = Results.Values.All(v => v == true);
+
+        // 删除临时文件夹
+        if (!DownloaderConfig.MyOptions.SkipMerge && DownloaderConfig.MyOptions.DelAfterDone && success)
+        {
+            foreach (var item in StreamExtractor.RawFiles)
+            {
+                var file = Path.Combine(DownloaderConfig.DirPrefix, item.Key);
+                if (File.Exists(file)) File.Delete(file);
+            }
+            OtherUtil.SafeDeleteDir(DownloaderConfig.DirPrefix);
+        }
+
+        // 混流
+        if (success && DownloaderConfig.MyOptions.MuxAfterDone && OutputFiles.Count > 0)
+        {
+            OutputFiles = OutputFiles.OrderBy(o => o.Index).ToList();
+            // 是否跳过字幕
+            if (DownloaderConfig.MyOptions.MuxOptions!.SkipSubtitle)
+            {
+                OutputFiles = OutputFiles.Where(o => o.MediaType != MediaType.SUBTITLES).ToList();
+            }
+            if (DownloaderConfig.MyOptions.MuxImports != null)
+            {
+                OutputFiles.AddRange(DownloaderConfig.MyOptions.MuxImports);
+            }
+            OutputFiles.ForEach(f => Logger.WarnMarkUp($"[grey]{Path.GetFileName(f.FilePath).EscapeMarkup()}[/]"));
+            var saveDir = DownloaderConfig.MyOptions.SaveDir ?? Environment.CurrentDirectory;
+            var ext = OtherUtil.GetMuxExtension(DownloaderConfig.MyOptions.MuxOptions.MuxFormat);
+            var dirName = Path.GetFileName(DownloaderConfig.DirPrefix);
+            var outName = $"{dirName}.MUX";
+            var outPath = Path.Combine(saveDir, outName);
+            Logger.WarnMarkUp($"Muxing to [grey]{outName.EscapeMarkup()}{ext}[/]");
+            var result = false;
+            if (DownloaderConfig.MyOptions.MuxOptions.UseMkvmerge) result = MergeUtil.MuxInputsByMkvmerge(DownloaderConfig.MyOptions.MkvmergeBinaryPath!, OutputFiles.ToArray(), outPath);
+            else result = MergeUtil.MuxInputsByFFmpeg(DownloaderConfig.MyOptions.FFmpegBinaryPath!, OutputFiles.ToArray(), outPath, DownloaderConfig.MyOptions.MuxOptions.MuxFormat, !DownloaderConfig.MyOptions.NoDateInfo);
+            // 完成后删除各轨道文件
+            if (result)
+            {
+                if (!DownloaderConfig.MyOptions.MuxOptions.KeepFiles)
+                {
+                    Logger.WarnMarkUp("[grey]Cleaning files...[/]");
+                    OutputFiles.ForEach(f => File.Delete(f.FilePath));
+                    var tmpDir = DownloaderConfig.MyOptions.TmpDir ?? Environment.CurrentDirectory;
+                    OtherUtil.SafeDeleteDir(tmpDir);
+                }
+            }
+            else
+            {
+                success = false;
+                Logger.ErrorMarkUp($"Mux failed");
+            }
+            // 判断是否要改名
+            var newPath = Path.ChangeExtension(outPath, ext);
+            if (result && !File.Exists(newPath))
+            {
+                Logger.WarnMarkUp($"Rename to [grey]{Path.GetFileName(newPath).EscapeMarkup()}[/]");
+                File.Move(outPath + ext, newPath);
+            }
+        }
+
+        return success;
+    }
+}
\ No newline at end of file
diff --git a/src/N_m3u8DL-RE/Downloader/IDownloader.cs b/src/N_m3u8DL-RE/Downloader/IDownloader.cs
index 9db7289..0d4e444 100644
--- a/src/N_m3u8DL-RE/Downloader/IDownloader.cs
+++ b/src/N_m3u8DL-RE/Downloader/IDownloader.cs
@@ -1,15 +1,9 @@
 using N_m3u8DL_RE.Common.Entity;
 using N_m3u8DL_RE.Entity;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
 
-namespace N_m3u8DL_RE.Downloader
+namespace N_m3u8DL_RE.Downloader;
+
+internal interface IDownloader
 {
-    internal interface IDownloader
-    {
-        Task<DownloadResult?> DownloadSegmentAsync(MediaSegment segment, string savePath, SpeedContainer speedContainer, Dictionary<string, string>? headers = null);
-    }
-}
+    Task<DownloadResult?> DownloadSegmentAsync(MediaSegment segment, string savePath, SpeedContainer speedContainer, Dictionary<string, string>? headers = null);
+}
\ No newline at end of file
diff --git a/src/N_m3u8DL-RE/Downloader/SimpleDownloader.cs b/src/N_m3u8DL-RE/Downloader/SimpleDownloader.cs
index b9405fc..f8a4c1c 100644
--- a/src/N_m3u8DL-RE/Downloader/SimpleDownloader.cs
+++ b/src/N_m3u8DL-RE/Downloader/SimpleDownloader.cs
@@ -6,156 +6,146 @@ using N_m3u8DL_RE.Crypto;
 using N_m3u8DL_RE.Entity;
 using N_m3u8DL_RE.Util;
 using Spectre.Console;
-using System;
-using System.Collections.Concurrent;
-using System.Collections.Generic;
-using System.Data;
-using System.Diagnostics;
-using System.Linq;
-using System.Text;
-using System.Threading;
-using System.Threading.Tasks;
 
-namespace N_m3u8DL_RE.Downloader
+namespace N_m3u8DL_RE.Downloader;
+
+/// <summary>
+/// 简单下载器
+/// </summary>
+internal class SimpleDownloader : IDownloader
 {
-    /// <summary>
-    /// 简单下载器
-    /// </summary>
-    internal class SimpleDownloader : IDownloader
+    DownloaderConfig DownloaderConfig;
+
+    public SimpleDownloader(DownloaderConfig config)
     {
-        DownloaderConfig DownloaderConfig;
+        DownloaderConfig = config;
+    }
 
-        public SimpleDownloader(DownloaderConfig config)
+    public async Task<DownloadResult?> DownloadSegmentAsync(MediaSegment segment, string savePath, SpeedContainer speedContainer, Dictionary<string, string>? headers = null)
+    {
+        var url = segment.Url;
+        var (des, dResult) = await DownClipAsync(url, savePath, speedContainer, segment.StartRange, segment.StopRange, headers, DownloaderConfig.MyOptions.DownloadRetryCount);
+        if (dResult != null && dResult.Success && dResult.ActualFilePath != des)
         {
-            DownloaderConfig = config;
-        }
-
-        public async Task<DownloadResult?> DownloadSegmentAsync(MediaSegment segment, string savePath, SpeedContainer speedContainer, Dictionary<string, string>? headers = null)
-        {
-            var url = segment.Url;
-            var (des, dResult) = await DownClipAsync(url, savePath, speedContainer, segment.StartRange, segment.StopRange, headers, DownloaderConfig.MyOptions.DownloadRetryCount);
-            if (dResult != null && dResult.Success && dResult.ActualFilePath != des)
+            if (segment.EncryptInfo != null)
             {
-                if (segment.EncryptInfo != null)
+                if (segment.EncryptInfo.Method == EncryptMethod.AES_128)
                 {
-                    if (segment.EncryptInfo.Method == EncryptMethod.AES_128)
-                    {
-                        var key = segment.EncryptInfo.Key;
-                        var iv = segment.EncryptInfo.IV;
-                        AESUtil.AES128Decrypt(dResult.ActualFilePath, key!, iv!);
-                    }
-                    else if (segment.EncryptInfo.Method == EncryptMethod.AES_128_ECB)
-                    {
-                        var key = segment.EncryptInfo.Key;
-                        var iv = segment.EncryptInfo.IV;
-                        AESUtil.AES128Decrypt(dResult.ActualFilePath, key!, iv!, System.Security.Cryptography.CipherMode.ECB);
-                    }
-                    else if (segment.EncryptInfo.Method == EncryptMethod.CHACHA20)
-                    {
-                        var key = segment.EncryptInfo.Key;
-                        var nonce = segment.EncryptInfo.IV;
+                    var key = segment.EncryptInfo.Key;
+                    var iv = segment.EncryptInfo.IV;
+                    AESUtil.AES128Decrypt(dResult.ActualFilePath, key!, iv!);
+                }
+                else if (segment.EncryptInfo.Method == EncryptMethod.AES_128_ECB)
+                {
+                    var key = segment.EncryptInfo.Key;
+                    var iv = segment.EncryptInfo.IV;
+                    AESUtil.AES128Decrypt(dResult.ActualFilePath, key!, iv!, System.Security.Cryptography.CipherMode.ECB);
+                }
+                else if (segment.EncryptInfo.Method == EncryptMethod.CHACHA20)
+                {
+                    var key = segment.EncryptInfo.Key;
+                    var nonce = segment.EncryptInfo.IV;
 
-                        var fileBytes = File.ReadAllBytes(dResult.ActualFilePath);
-                        var decrypted = ChaCha20Util.DecryptPer1024Bytes(fileBytes, key!, nonce!);
-                        await File.WriteAllBytesAsync(dResult.ActualFilePath, decrypted);
-                    }
-                    else if (segment.EncryptInfo.Method == EncryptMethod.SAMPLE_AES_CTR)
-                    {
-                        //throw new NotSupportedException("SAMPLE-AES-CTR");
-                    }
-
-                    //Image头处理
-                    if (dResult.ImageHeader)
-                    {
-                        await ImageHeaderUtil.ProcessAsync(dResult.ActualFilePath);
-                    }
-                    //Gzip解压
-                    if (dResult.GzipHeader)
-                    {
-                        await OtherUtil.DeGzipFileAsync(dResult.ActualFilePath);
-                    }
+                    var fileBytes = File.ReadAllBytes(dResult.ActualFilePath);
+                    var decrypted = ChaCha20Util.DecryptPer1024Bytes(fileBytes, key!, nonce!);
+                    await File.WriteAllBytesAsync(dResult.ActualFilePath, decrypted);
+                }
+                else if (segment.EncryptInfo.Method == EncryptMethod.SAMPLE_AES_CTR)
+                {
+                    // throw new NotSupportedException("SAMPLE-AES-CTR");
                 }
 
-                //处理完成后改名
-                File.Move(dResult.ActualFilePath, des);
-                dResult.ActualFilePath = des;
+                // Image头处理
+                if (dResult.ImageHeader)
+                {
+                    await ImageHeaderUtil.ProcessAsync(dResult.ActualFilePath);
+                }
+                // Gzip解压
+                if (dResult.GzipHeader)
+                {
+                    await OtherUtil.DeGzipFileAsync(dResult.ActualFilePath);
+                }
             }
-            return dResult;
-        }
 
-        private async Task<(string des, DownloadResult? dResult)> DownClipAsync(string url, string path, SpeedContainer speedContainer, long? fromPosition, long? toPosition, Dictionary<string, string>? headers = null, int retryCount = 3)
-        {
-            CancellationTokenSource? cancellationTokenSource = null;
+            // 处理完成后改名
+            File.Move(dResult.ActualFilePath, des);
+            dResult.ActualFilePath = des;
+        }
+        return dResult;
+    }
+
+    private async Task<(string des, DownloadResult? dResult)> DownClipAsync(string url, string path, SpeedContainer speedContainer, long? fromPosition, long? toPosition, Dictionary<string, string>? headers = null, int retryCount = 3)
+    {
+        CancellationTokenSource? cancellationTokenSource = null;
         retry:
-            try
+        try
+        {
+            cancellationTokenSource = new();
+            var des = Path.ChangeExtension(path, null);
+
+            // 已下载跳过
+            if (File.Exists(des))
             {
-                cancellationTokenSource = new();
-                var des = Path.ChangeExtension(path, null);
+                speedContainer.Add(new FileInfo(des).Length);
+                return (des, new DownloadResult() { ActualContentLength = 0, ActualFilePath = des });
+            }
 
-                //已下载跳过
-                if (File.Exists(des))
-                {
-                    speedContainer.Add(new FileInfo(des).Length);
-                    return (des, new DownloadResult() { ActualContentLength = 0, ActualFilePath = des });
-                }
+            // 已解密跳过
+            var dec = Path.Combine(Path.GetDirectoryName(des)!, Path.GetFileNameWithoutExtension(des) + "_dec" + Path.GetExtension(des));
+            if (File.Exists(dec))
+            {
+                speedContainer.Add(new FileInfo(dec).Length);
+                return (dec, new DownloadResult() { ActualContentLength = 0, ActualFilePath = dec });
+            }
 
-                //已解密跳过
-                var dec = Path.Combine(Path.GetDirectoryName(des)!, Path.GetFileNameWithoutExtension(des) + "_dec" + Path.GetExtension(des));
-                if (File.Exists(dec))
+            // 另起线程进行监控
+            using var watcher = Task.Factory.StartNew(async () =>
+            {
+                while (true)
                 {
-                    speedContainer.Add(new FileInfo(dec).Length);
-                    return (dec, new DownloadResult() { ActualContentLength = 0, ActualFilePath = dec });
-                }
-
-                //另起线程进行监控
-                using var watcher = Task.Factory.StartNew(async () =>
-                {
-                    while (true)
+                    if (cancellationTokenSource == null || cancellationTokenSource.IsCancellationRequested) break;
+                    if (speedContainer.ShouldStop)
                     {
-                        if (cancellationTokenSource == null || cancellationTokenSource.IsCancellationRequested) break;
-                        if (speedContainer.ShouldStop)
-                        {
-                            cancellationTokenSource.Cancel();
-                            Logger.DebugMarkUp("Cancel...");
-                            break;
-                        }
-                        await Task.Delay(500);
+                        cancellationTokenSource.Cancel();
+                        Logger.DebugMarkUp("Cancel...");
+                        break;
                     }
-                });
+                    await Task.Delay(500);
+                }
+            });
 
-                //调用下载
-                var result = await DownloadUtil.DownloadToFileAsync(url, path, speedContainer, cancellationTokenSource, headers, fromPosition, toPosition);
-                return (des, result);
+            // 调用下载
+            var result = await DownloadUtil.DownloadToFileAsync(url, path, speedContainer, cancellationTokenSource, headers, fromPosition, toPosition);
+            return (des, result);
 
-                throw new Exception("please retry");
-            }
-            catch (Exception ex)
+            throw new Exception("please retry");
+        }
+        catch (Exception ex)
+        {
+            Logger.DebugMarkUp($"[grey]{ex.Message.EscapeMarkup()} retryCount: {retryCount}[/]");
+            Logger.Debug(url + " " + ex.ToString());
+            Logger.Extra($"Ah oh!{Environment.NewLine}RetryCount => {retryCount}{Environment.NewLine}Exception  => {ex.Message}{Environment.NewLine}Url        => {url}");
+            if (retryCount-- > 0)
             {
-                Logger.DebugMarkUp($"[grey]{ex.Message.EscapeMarkup()} retryCount: {retryCount}[/]");
-                Logger.Debug(url + " " + ex.ToString());
-                Logger.Extra($"Ah oh!{Environment.NewLine}RetryCount => {retryCount}{Environment.NewLine}Exception  => {ex.Message}{Environment.NewLine}Url        => {url}");
-                if (retryCount-- > 0)
-                {
-                    await Task.Delay(1000);
-                    goto retry;
-                }
-                else
-                {
-                    Logger.Extra($"The retry attempts have been exhausted and the download of this segment has failed.{Environment.NewLine}Exception  => {ex.Message}{Environment.NewLine}Url        => {url}");
-                    Logger.WarnMarkUp($"[grey]{ex.Message.EscapeMarkup()}[/]");
-                }
-                //throw new Exception("download failed", ex);
-                return default;
+                await Task.Delay(1000);
+                goto retry;
             }
-            finally
+            else
             {
-                if (cancellationTokenSource != null)
-                {
-                    //调用后销毁
-                    cancellationTokenSource.Dispose();
-                    cancellationTokenSource = null;
-                }
+                Logger.Extra($"The retry attempts have been exhausted and the download of this segment has failed.{Environment.NewLine}Exception  => {ex.Message}{Environment.NewLine}Url        => {url}");
+                Logger.WarnMarkUp($"[grey]{ex.Message.EscapeMarkup()}[/]");
+            }
+            // throw new Exception("download failed", ex);
+            return default;
+        }
+        finally
+        {
+            if (cancellationTokenSource != null)
+            {
+                // 调用后销毁
+                cancellationTokenSource.Dispose();
+                cancellationTokenSource = null;
             }
         }
     }
-}
+}
\ No newline at end of file
diff --git a/src/N_m3u8DL-RE/Entity/CustomRange.cs b/src/N_m3u8DL-RE/Entity/CustomRange.cs
index e59b4c1..95c68a1 100644
--- a/src/N_m3u8DL-RE/Entity/CustomRange.cs
+++ b/src/N_m3u8DL-RE/Entity/CustomRange.cs
@@ -1,23 +1,16 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
+namespace N_m3u8DL_RE.Entity;
 
-namespace N_m3u8DL_RE.Entity
+public class CustomRange
 {
-    public class CustomRange
+    public required string InputStr { get; set; }
+    public double? StartSec { get; set; }
+    public double? EndSec { get; set; }
+
+    public long? StartSegIndex { get; set; }
+    public long? EndSegIndex { get; set;}
+
+    public override string? ToString()
     {
-        public required string InputStr { get; set; }
-        public double? StartSec { get; set; }
-        public double? EndSec { get; set; }
-
-        public long? StartSegIndex { get; set; }
-        public long? EndSegIndex { get; set;}
-
-        public override string? ToString()
-        {
-            return $"StartSec: {StartSec}, EndSec: {EndSec}, StartSegIndex: {StartSegIndex}, EndSegIndex: {EndSegIndex}";
-        }
+        return $"StartSec: {StartSec}, EndSec: {EndSec}, StartSegIndex: {StartSegIndex}, EndSegIndex: {EndSegIndex}";
     }
-}
+}
\ No newline at end of file
diff --git a/src/N_m3u8DL-RE/Entity/DownloadResult.cs b/src/N_m3u8DL-RE/Entity/DownloadResult.cs
index 04bdd79..ac085f8 100644
--- a/src/N_m3u8DL-RE/Entity/DownloadResult.cs
+++ b/src/N_m3u8DL-RE/Entity/DownloadResult.cs
@@ -5,15 +5,14 @@ using System.Linq;
 using System.Text;
 using System.Threading.Tasks;
 
-namespace N_m3u8DL_RE.Entity
+namespace N_m3u8DL_RE.Entity;
+
+internal class DownloadResult
 {
-    internal class DownloadResult
-    {
-        public bool Success { get => (ActualContentLength != null && RespContentLength != null) ? (RespContentLength == ActualContentLength) : (ActualContentLength == null ? false : true); }
-        public long? RespContentLength { get; set; }
-        public long? ActualContentLength { get; set; }
-        public bool ImageHeader { get; set; } = false; //图片伪装
-        public bool GzipHeader { get; set; } = false; //GZip压缩
-        public required string ActualFilePath { get; set; }
-    }
-}
+    public bool Success { get => (ActualContentLength != null && RespContentLength != null) ? (RespContentLength == ActualContentLength) : (ActualContentLength == null ? false : true); }
+    public long? RespContentLength { get; set; }
+    public long? ActualContentLength { get; set; }
+    public bool ImageHeader { get; set; } = false; // 图片伪装
+    public bool GzipHeader { get; set; } = false; // GZip压缩
+    public required string ActualFilePath { get; set; }
+}
\ No newline at end of file
diff --git a/src/N_m3u8DL-RE/Entity/Mediainfo.cs b/src/N_m3u8DL-RE/Entity/Mediainfo.cs
index 5bf440f..795becd 100644
--- a/src/N_m3u8DL-RE/Entity/Mediainfo.cs
+++ b/src/N_m3u8DL-RE/Entity/Mediainfo.cs
@@ -1,33 +1,27 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-using Spectre.Console;
+using Spectre.Console;
 
-namespace N_m3u8DL_RE.Entity
+namespace N_m3u8DL_RE.Entity;
+
+internal class Mediainfo
 {
-    internal class Mediainfo
+    public string? Id { get; set; }
+    public string? Text { get; set; }
+    public string? BaseInfo { get; set; }
+    public string? Bitrate { get; set; }
+    public string? Resolution { get; set; }
+    public string? Fps { get; set; }
+    public string? Type { get; set; }
+    public TimeSpan? StartTime { get; set; }
+    public bool DolbyVison { get; set; }
+    public bool HDR { get; set; }
+
+    public override string? ToString()
     {
-        public string? Id { get; set; }
-        public string? Text { get; set; }
-        public string? BaseInfo { get; set; }
-        public string? Bitrate { get; set; }
-        public string? Resolution { get; set; }
-        public string? Fps { get; set; }
-        public string? Type { get; set; }
-        public TimeSpan? StartTime { get; set; }
-        public bool DolbyVison { get; set; }
-        public bool HDR { get; set; }
-
-        public override string? ToString()
-        {
-            return $"{(string.IsNullOrEmpty(Id) ? "NaN" : Id)}: " + string.Join(", ", new List<string?> { Type, BaseInfo, Resolution, Fps, Bitrate }.Where(i => !string.IsNullOrEmpty(i)));
-        }
-
-        public string ToStringMarkUp()
-        {
-            return "[steelblue]" + ToString().EscapeMarkup() + ((HDR && !DolbyVison) ? " [darkorange3_1][[HDR]][/]" : "") + (DolbyVison ? " [darkorange3_1][[DOVI]][/]" : "") + "[/]";
-        }
+        return $"{(string.IsNullOrEmpty(Id) ? "NaN" : Id)}: " + string.Join(", ", new List<string?> { Type, BaseInfo, Resolution, Fps, Bitrate }.Where(i => !string.IsNullOrEmpty(i)));
     }
-}
+
+    public string ToStringMarkUp()
+    {
+        return "[steelblue]" + ToString().EscapeMarkup() + ((HDR && !DolbyVison) ? " [darkorange3_1][[HDR]][/]" : "") + (DolbyVison ? " [darkorange3_1][[DOVI]][/]" : "") + "[/]";
+    }
+}
\ No newline at end of file
diff --git a/src/N_m3u8DL-RE/Entity/MuxOptions.cs b/src/N_m3u8DL-RE/Entity/MuxOptions.cs
index 5e27134..163e0cf 100644
--- a/src/N_m3u8DL-RE/Entity/MuxOptions.cs
+++ b/src/N_m3u8DL-RE/Entity/MuxOptions.cs
@@ -1,13 +1,12 @@
 using N_m3u8DL_RE.Enum;
 
-namespace N_m3u8DL_RE.Entity
+namespace N_m3u8DL_RE.Entity;
+
+internal class MuxOptions
 {
-    internal class MuxOptions
-    {
-        public bool UseMkvmerge { get; set; } = false;
-        public MuxFormat MuxFormat { get; set; } = MuxFormat.MP4;
-        public bool KeepFiles { get; set; } = false;
-        public bool SkipSubtitle { get; set; } = false;
-        public string? BinPath { get; set; }
-    }
-}
+    public bool UseMkvmerge { get; set; } = false;
+    public MuxFormat MuxFormat { get; set; } = MuxFormat.MP4;
+    public bool KeepFiles { get; set; } = false;
+    public bool SkipSubtitle { get; set; } = false;
+    public string? BinPath { get; set; }
+}
\ No newline at end of file
diff --git a/src/N_m3u8DL-RE/Entity/OutputFile.cs b/src/N_m3u8DL-RE/Entity/OutputFile.cs
index cc37050..849ad78 100644
--- a/src/N_m3u8DL-RE/Entity/OutputFile.cs
+++ b/src/N_m3u8DL-RE/Entity/OutputFile.cs
@@ -1,19 +1,13 @@
 using N_m3u8DL_RE.Common.Enum;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
 
-namespace N_m3u8DL_RE.Entity
+namespace N_m3u8DL_RE.Entity;
+
+internal class OutputFile
 {
-    internal class OutputFile
-    {
-        public MediaType? MediaType { get; set; }
-        public required int Index { get; set; }
-        public required string FilePath { get; set; }
-        public string? LangCode { get; set; }
-        public string? Description { get; set; }
-        public List<Mediainfo> Mediainfos { get; set; } = new();
-    }
-}
+    public MediaType? MediaType { get; set; }
+    public required int Index { get; set; }
+    public required string FilePath { get; set; }
+    public string? LangCode { get; set; }
+    public string? Description { get; set; }
+    public List<Mediainfo> Mediainfos { get; set; } = new();
+}
\ No newline at end of file
diff --git a/src/N_m3u8DL-RE/Entity/SpeedContainer.cs b/src/N_m3u8DL-RE/Entity/SpeedContainer.cs
index 93ef2d5..13f4365 100644
--- a/src/N_m3u8DL-RE/Entity/SpeedContainer.cs
+++ b/src/N_m3u8DL-RE/Entity/SpeedContainer.cs
@@ -7,53 +7,52 @@ using System.Linq;
 using System.Text;
 using System.Threading.Tasks;
 
-namespace N_m3u8DL_RE.Entity
+namespace N_m3u8DL_RE.Entity;
+
+internal class SpeedContainer
 {
-    internal class SpeedContainer
+    public bool SingleSegment { get; set; } = false;
+    public long NowSpeed { get; set; } = 0L; // 当前每秒速度
+    public long SpeedLimit { get; set; } = long.MaxValue; // 限速设置
+    public long? ResponseLength { get; set; }
+    public long RDownloaded => _Rdownloaded;
+    private int _zeroSpeedCount = 0;
+    public int LowSpeedCount => _zeroSpeedCount;
+    public bool ShouldStop => LowSpeedCount >= 20;
+
+    ///////////////////////////////////////////////////
+
+    private long _downloaded = 0;
+    private long _Rdownloaded = 0;
+    public long Downloaded { get => _downloaded; }
+
+    public int AddLowSpeedCount()
     {
-        public bool SingleSegment { get; set; } = false;
-        public long NowSpeed { get; set; } = 0L; //当前每秒速度
-        public long SpeedLimit { get; set; } = long.MaxValue; //限速设置
-        public long? ResponseLength { get; set; }
-        public long RDownloaded { get => _Rdownloaded; }
-        private int _zeroSpeedCount = 0;
-        public int LowSpeedCount { get => _zeroSpeedCount; }
-        public bool ShouldStop { get => LowSpeedCount >= 20; }
-
-        ///////////////////////////////////////////////////
-
-        private long _downloaded = 0;
-        private long _Rdownloaded = 0;
-        public long Downloaded { get => _downloaded; }
-
-        public int AddLowSpeedCount()
-        {
-            return Interlocked.Add(ref _zeroSpeedCount, 1);
-        }
-
-        public int ResetLowSpeedCount()
-        {
-            return Interlocked.Exchange(ref _zeroSpeedCount, 0);
-        }
-
-        public long Add(long size)
-        {
-            Interlocked.Add(ref _Rdownloaded, size);
-            return Interlocked.Add(ref _downloaded, size);
-        }
-
-        public void Reset()
-        {
-            Interlocked.Exchange(ref _downloaded, 0);
-        }
-
-        public void ResetVars()
-        {
-            Reset();
-            ResetLowSpeedCount();
-            SingleSegment = false;
-            ResponseLength = null;
-            _Rdownloaded = 0L;
-        }
+        return Interlocked.Add(ref _zeroSpeedCount, 1);
     }
-}
+
+    public int ResetLowSpeedCount()
+    {
+        return Interlocked.Exchange(ref _zeroSpeedCount, 0);
+    }
+
+    public long Add(long size)
+    {
+        Interlocked.Add(ref _Rdownloaded, size);
+        return Interlocked.Add(ref _downloaded, size);
+    }
+
+    public void Reset()
+    {
+        Interlocked.Exchange(ref _downloaded, 0);
+    }
+
+    public void ResetVars()
+    {
+        Reset();
+        ResetLowSpeedCount();
+        SingleSegment = false;
+        ResponseLength = null;
+        _Rdownloaded = 0L;
+    }
+}
\ No newline at end of file
diff --git a/src/N_m3u8DL-RE/Entity/StreamFilter.cs b/src/N_m3u8DL-RE/Entity/StreamFilter.cs
index d76d862..54e5bf2 100644
--- a/src/N_m3u8DL-RE/Entity/StreamFilter.cs
+++ b/src/N_m3u8DL-RE/Entity/StreamFilter.cs
@@ -1,56 +1,51 @@
 using N_m3u8DL_RE.Common.Enum;
-using System;
-using System.Collections.Generic;
-using System.Linq;
 using System.Text;
 using System.Text.RegularExpressions;
-using System.Threading.Tasks;
 
-namespace N_m3u8DL_RE.Entity
+namespace N_m3u8DL_RE.Entity;
+
+public class StreamFilter
 {
-    public class StreamFilter
+    public Regex? GroupIdReg { get; set; }
+    public Regex? LanguageReg { get; set; }
+    public Regex? NameReg { get; set; }
+    public Regex? CodecsReg { get; set; }
+    public Regex? ResolutionReg { get; set; }
+    public Regex? FrameRateReg { get; set; }
+    public Regex? ChannelsReg { get; set; }
+    public Regex? VideoRangeReg { get; set; }
+    public Regex? UrlReg { get; set; }
+    public long? SegmentsMinCount { get; set; }
+    public long? SegmentsMaxCount { get; set; }
+    public double? PlaylistMinDur {  get; set; }
+    public double? PlaylistMaxDur {  get; set; }
+    public int? BandwidthMin { get; set; }
+    public int? BandwidthMax { get; set; }
+    public RoleType? Role { get; set; }
+
+    public string For { get; set; } = "best";
+
+    public override string? ToString()
     {
-        public Regex? GroupIdReg { get; set; }
-        public Regex? LanguageReg { get; set; }
-        public Regex? NameReg { get; set; }
-        public Regex? CodecsReg { get; set; }
-        public Regex? ResolutionReg { get; set; }
-        public Regex? FrameRateReg { get; set; }
-        public Regex? ChannelsReg { get; set; }
-        public Regex? VideoRangeReg { get; set; }
-        public Regex? UrlReg { get; set; }
-        public long? SegmentsMinCount { get; set; }
-        public long? SegmentsMaxCount { get; set; }
-        public double? PlaylistMinDur {  get; set; }
-        public double? PlaylistMaxDur {  get; set; }
-        public int? BandwidthMin { get; set; }
-        public int? BandwidthMax { get; set; }
-        public RoleType? Role { get; set; }
+        var sb = new StringBuilder();
 
-        public string For { get; set; } = "best";
+        if (GroupIdReg != null) sb.Append($"GroupIdReg: {GroupIdReg} ");
+        if (LanguageReg != null) sb.Append($"LanguageReg: {LanguageReg} ");
+        if (NameReg != null) sb.Append($"NameReg: {NameReg} ");
+        if (CodecsReg != null) sb.Append($"CodecsReg: {CodecsReg} ");
+        if (ResolutionReg != null) sb.Append($"ResolutionReg: {ResolutionReg} ");
+        if (FrameRateReg != null) sb.Append($"FrameRateReg: {FrameRateReg} ");
+        if (ChannelsReg != null) sb.Append($"ChannelsReg: {ChannelsReg} ");
+        if (VideoRangeReg != null) sb.Append($"VideoRangeReg: {VideoRangeReg} ");
+        if (UrlReg != null) sb.Append($"UrlReg: {UrlReg} ");
+        if (SegmentsMinCount != null) sb.Append($"SegmentsMinCount: {SegmentsMinCount} ");
+        if (SegmentsMaxCount != null) sb.Append($"SegmentsMaxCount: {SegmentsMaxCount} ");
+        if (PlaylistMinDur != null) sb.Append($"PlaylistMinDur: {PlaylistMinDur} ");
+        if (PlaylistMaxDur != null) sb.Append($"PlaylistMaxDur: {PlaylistMaxDur} ");
+        if (BandwidthMin != null) sb.Append($"{nameof(BandwidthMin)}: {BandwidthMin} ");
+        if (BandwidthMax != null) sb.Append($"{nameof(BandwidthMax)}: {BandwidthMax} ");
+        if (Role.HasValue) sb.Append($"Role: {Role} ");
 
-        public override string? ToString()
-        {
-            var sb = new StringBuilder();
-
-            if (GroupIdReg != null) sb.Append($"GroupIdReg: {GroupIdReg} ");
-            if (LanguageReg != null) sb.Append($"LanguageReg: {LanguageReg} ");
-            if (NameReg != null) sb.Append($"NameReg: {NameReg} ");
-            if (CodecsReg != null) sb.Append($"CodecsReg: {CodecsReg} ");
-            if (ResolutionReg != null) sb.Append($"ResolutionReg: {ResolutionReg} ");
-            if (FrameRateReg != null) sb.Append($"FrameRateReg: {FrameRateReg} ");
-            if (ChannelsReg != null) sb.Append($"ChannelsReg: {ChannelsReg} ");
-            if (VideoRangeReg != null) sb.Append($"VideoRangeReg: {VideoRangeReg} ");
-            if (UrlReg != null) sb.Append($"UrlReg: {UrlReg} ");
-            if (SegmentsMinCount != null) sb.Append($"SegmentsMinCount: {SegmentsMinCount} ");
-            if (SegmentsMaxCount != null) sb.Append($"SegmentsMaxCount: {SegmentsMaxCount} ");
-            if (PlaylistMinDur != null) sb.Append($"PlaylistMinDur: {PlaylistMinDur} ");
-            if (PlaylistMaxDur != null) sb.Append($"PlaylistMaxDur: {PlaylistMaxDur} ");
-            if (BandwidthMin != null) sb.Append($"{nameof(BandwidthMin)}: {BandwidthMin} ");
-            if (BandwidthMax != null) sb.Append($"{nameof(BandwidthMax)}: {BandwidthMax} ");
-            if (Role.HasValue) sb.Append($"Role: {Role} ");
-
-            return sb.ToString() + $"For: {For}";
-        }
+        return sb.ToString() + $"For: {For}";
     }
-}
+}
\ No newline at end of file
diff --git a/src/N_m3u8DL-RE/Processor/DemoProcessor.cs b/src/N_m3u8DL-RE/Processor/DemoProcessor.cs
index 71ab689..7e9d356 100644
--- a/src/N_m3u8DL-RE/Processor/DemoProcessor.cs
+++ b/src/N_m3u8DL-RE/Processor/DemoProcessor.cs
@@ -2,26 +2,20 @@
 using N_m3u8DL_RE.Common.Log;
 using N_m3u8DL_RE.Parser.Config;
 using N_m3u8DL_RE.Parser.Processor;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
 
-namespace N_m3u8DL_RE.Processor
+namespace N_m3u8DL_RE.Processor;
+
+internal class DemoProcessor : ContentProcessor
 {
-    internal class DemoProcessor : ContentProcessor
+
+    public override bool CanProcess(ExtractorType extractorType, string rawText, ParserConfig parserConfig)
     {
-
-        public override bool CanProcess(ExtractorType extractorType, string rawText, ParserConfig parserConfig)
-        {
-            return extractorType == ExtractorType.MPEG_DASH && parserConfig.Url.Contains("bitmovin");
-        }
-
-        public override string Process(string rawText, ParserConfig parserConfig)
-        {
-            Logger.InfoMarkUp("[red]Match bitmovin![/]");
-            return rawText;
-        }
+        return extractorType == ExtractorType.MPEG_DASH && parserConfig.Url.Contains("bitmovin");
     }
-}
+
+    public override string Process(string rawText, ParserConfig parserConfig)
+    {
+        Logger.InfoMarkUp("[red]Match bitmovin![/]");
+        return rawText;
+    }
+}
\ No newline at end of file
diff --git a/src/N_m3u8DL-RE/Processor/DemoProcessor2.cs b/src/N_m3u8DL-RE/Processor/DemoProcessor2.cs
index 5825c51..6b480b1 100644
--- a/src/N_m3u8DL-RE/Processor/DemoProcessor2.cs
+++ b/src/N_m3u8DL-RE/Processor/DemoProcessor2.cs
@@ -5,27 +5,21 @@ using N_m3u8DL_RE.Common.Util;
 using N_m3u8DL_RE.Parser.Config;
 using N_m3u8DL_RE.Parser.Processor;
 using N_m3u8DL_RE.Parser.Processor.HLS;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
 
-namespace N_m3u8DL_RE.Processor
+namespace N_m3u8DL_RE.Processor;
+
+internal class DemoProcessor2 : KeyProcessor
 {
-    internal class DemoProcessor2 : KeyProcessor
+    public override bool CanProcess(ExtractorType extractorType, string keyLine, string m3u8Url, string m3u8Content, ParserConfig parserConfig)
     {
-        public override bool CanProcess(ExtractorType extractorType, string keyLine, string m3u8Url, string m3u8Content, ParserConfig parserConfig)
-        {
-            return extractorType == ExtractorType.HLS  && parserConfig.Url.Contains("playertest.longtailvideo.com");
-        }
-
-        public override EncryptInfo Process(string keyLine, string m3u8Url, string m3u8Content, ParserConfig parserConfig)
-        {
-            Logger.InfoMarkUp($"[white on green]My Key Processor => {keyLine}[/]");
-            var info = new DefaultHLSKeyProcessor().Process(keyLine, m3u8Url, m3u8Content, parserConfig);
-            Logger.InfoMarkUp("[red]" + HexUtil.BytesToHex(info.Key!, " ") + "[/]");
-            return info;
-        }
+        return extractorType == ExtractorType.HLS  && parserConfig.Url.Contains("playertest.longtailvideo.com");
     }
-}
+
+    public override EncryptInfo Process(string keyLine, string m3u8Url, string m3u8Content, ParserConfig parserConfig)
+    {
+        Logger.InfoMarkUp($"[white on green]My Key Processor => {keyLine}[/]");
+        var info = new DefaultHLSKeyProcessor().Process(keyLine, m3u8Url, m3u8Content, parserConfig);
+        Logger.InfoMarkUp("[red]" + HexUtil.BytesToHex(info.Key!, " ") + "[/]");
+        return info;
+    }
+}
\ No newline at end of file
diff --git a/src/N_m3u8DL-RE/Processor/NowehoryzontyUrlProcessor.cs b/src/N_m3u8DL-RE/Processor/NowehoryzontyUrlProcessor.cs
index 482f5dc..ab58807 100644
--- a/src/N_m3u8DL-RE/Processor/NowehoryzontyUrlProcessor.cs
+++ b/src/N_m3u8DL-RE/Processor/NowehoryzontyUrlProcessor.cs
@@ -6,219 +6,211 @@ using N_m3u8DL_RE.Parser.Util;
 using NiL.JS.BaseLibrary;
 using NiL.JS.Core;
 using NiL.JS.Extensions;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Security.Cryptography;
-using System.Text;
-using System.Text.RegularExpressions;
-using System.Threading.Tasks;
 
-namespace N_m3u8DL_RE.Processor
+namespace N_m3u8DL_RE.Processor;
+
+// "https://1429754964.rsc.cdn77.org/r/nh22/2022/VNUS_DE_NYKE/19_07_22_2302_skt/h264.mpd?secure=mSvVfvuciJt9wufUyzuBnA==,1658505709774" --urlprocessor-args "nowehoryzonty:timeDifference=-2274,filminfo.secureToken=vx54axqjal4f0yy2"
+internal class NowehoryzontyUrlProcessor : UrlProcessor
 {
-    //"https://1429754964.rsc.cdn77.org/r/nh22/2022/VNUS_DE_NYKE/19_07_22_2302_skt/h264.mpd?secure=mSvVfvuciJt9wufUyzuBnA==,1658505709774" --urlprocessor-args "nowehoryzonty:timeDifference=-2274,filminfo.secureToken=vx54axqjal4f0yy2"
-    internal class NowehoryzontyUrlProcessor : UrlProcessor
+    private static string START = "nowehoryzonty:";
+    private static string? TimeDifferenceStr = null;
+    private static int? TimeDifference = null;
+    private static string? SecureToken = null;
+    private static bool LOG = false;
+    private static Function? Function = null;
+    public override bool CanProcess(ExtractorType extractorType, string oriUrl, ParserConfig parserConfig)
     {
-        private static string START = "nowehoryzonty:";
-        private static string? TimeDifferenceStr = null;
-        private static int? TimeDifference = null;
-        private static string? SecureToken = null;
-        private static bool LOG = false;
-        private static Function? Function = null;
-        public override bool CanProcess(ExtractorType extractorType, string oriUrl, ParserConfig parserConfig)
+        if (extractorType == ExtractorType.MPEG_DASH && parserConfig.UrlProcessorArgs != null && parserConfig.UrlProcessorArgs.StartsWith(START)) 
         {
-            if (extractorType == ExtractorType.MPEG_DASH && parserConfig.UrlProcessorArgs != null && parserConfig.UrlProcessorArgs.StartsWith(START)) 
+            if (!LOG)
             {
-                if (!LOG)
-                {
-                    Logger.WarnMarkUp($"[white on green]www.nowehoryzonty.pl[/] matched! waiting for calc...");
-                    LOG = true;
-                }
-                var context = new Context();
-                context.Eval(JS);
-                Function = context.GetVariable("md5").As<Function>();
-                var argLine = parserConfig.UrlProcessorArgs![START.Length..];
-                TimeDifferenceStr = ParserUtil.GetAttribute(argLine, "timeDifference");
-                SecureToken = ParserUtil.GetAttribute(argLine, "filminfo.secureToken");
-                if (TimeDifferenceStr != null && SecureToken != null)
-                {
-                    TimeDifference = Convert.ToInt32(TimeDifferenceStr);
-                }
-                return true;
+                Logger.WarnMarkUp($"[white on green]www.nowehoryzonty.pl[/] matched! waiting for calc...");
+                LOG = true;
             }
-            return false;
+            var context = new Context();
+            context.Eval(JS);
+            Function = context.GetVariable("md5").As<Function>();
+            var argLine = parserConfig.UrlProcessorArgs![START.Length..];
+            TimeDifferenceStr = ParserUtil.GetAttribute(argLine, "timeDifference");
+            SecureToken = ParserUtil.GetAttribute(argLine, "filminfo.secureToken");
+            if (TimeDifferenceStr != null && SecureToken != null)
+            {
+                TimeDifference = Convert.ToInt32(TimeDifferenceStr);
+            }
+            return true;
         }
-
-        public override string Process(string oriUrl, ParserConfig parserConfig)
-        {
-            var a = new Uri(oriUrl).AbsolutePath;
-            var n = oriUrl + "?secure=" + Calc(a);
-            return n;
-        }
-
-        private static string Calc(string a)
-        {
-            string returnStr = Function!.Call(new Arguments { a, SecureToken, TimeDifference }).ToString();
-            return returnStr;
-        }
-
-        ////https://www.nowehoryzonty.pl/packed/videonho.js?v=1114377281:formatted
-        private static readonly string JS = """
-            var p = function(f, e) {
-                var d = f[0]
-                  , a = f[1]
-                  , b = f[2]
-                  , c = f[3];
-                d = h(d, a, b, c, e[0], 7, -680876936);
-                c = h(c, d, a, b, e[1], 12, -389564586);
-                b = h(b, c, d, a, e[2], 17, 606105819);
-                a = h(a, b, c, d, e[3], 22, -1044525330);
-                d = h(d, a, b, c, e[4], 7, -176418897);
-                c = h(c, d, a, b, e[5], 12, 1200080426);
-                b = h(b, c, d, a, e[6], 17, -1473231341);
-                a = h(a, b, c, d, e[7], 22, -45705983);
-                d = h(d, a, b, c, e[8], 7, 1770035416);
-                c = h(c, d, a, b, e[9], 12, -1958414417);
-                b = h(b, c, d, a, e[10], 17, -42063);
-                a = h(a, b, c, d, e[11], 22, -1990404162);
-                d = h(d, a, b, c, e[12], 7, 1804603682);
-                c = h(c, d, a, b, e[13], 12, -40341101);
-                b = h(b, c, d, a, e[14], 17, -1502002290);
-                a = h(a, b, c, d, e[15], 22, 1236535329);
-                d = k(d, a, b, c, e[1], 5, -165796510);
-                c = k(c, d, a, b, e[6], 9, -1069501632);
-                b = k(b, c, d, a, e[11], 14, 643717713);
-                a = k(a, b, c, d, e[0], 20, -373897302);
-                d = k(d, a, b, c, e[5], 5, -701558691);
-                c = k(c, d, a, b, e[10], 9, 38016083);
-                b = k(b, c, d, a, e[15], 14, -660478335);
-                a = k(a, b, c, d, e[4], 20, -405537848);
-                d = k(d, a, b, c, e[9], 5, 568446438);
-                c = k(c, d, a, b, e[14], 9, -1019803690);
-                b = k(b, c, d, a, e[3], 14, -187363961);
-                a = k(a, b, c, d, e[8], 20, 1163531501);
-                d = k(d, a, b, c, e[13], 5, -1444681467);
-                c = k(c, d, a, b, e[2], 9, -51403784);
-                b = k(b, c, d, a, e[7], 14, 1735328473);
-                a = k(a, b, c, d, e[12], 20, -1926607734);
-                d = g(a ^ b ^ c, d, a, e[5], 4, -378558);
-                c = g(d ^ a ^ b, c, d, e[8], 11, -2022574463);
-                b = g(c ^ d ^ a, b, c, e[11], 16, 1839030562);
-                a = g(b ^ c ^ d, a, b, e[14], 23, -35309556);
-                d = g(a ^ b ^ c, d, a, e[1], 4, -1530992060);
-                c = g(d ^ a ^ b, c, d, e[4], 11, 1272893353);
-                b = g(c ^ d ^ a, b, c, e[7], 16, -155497632);
-                a = g(b ^ c ^ d, a, b, e[10], 23, -1094730640);
-                d = g(a ^ b ^ c, d, a, e[13], 4, 681279174);
-                c = g(d ^ a ^ b, c, d, e[0], 11, -358537222);
-                b = g(c ^ d ^ a, b, c, e[3], 16, -722521979);
-                a = g(b ^ c ^ d, a, b, e[6], 23, 76029189);
-                d = g(a ^ b ^ c, d, a, e[9], 4, -640364487);
-                c = g(d ^ a ^ b, c, d, e[12], 11, -421815835);
-                b = g(c ^ d ^ a, b, c, e[15], 16, 530742520);
-                a = g(b ^ c ^ d, a, b, e[2], 23, -995338651);
-                d = l(d, a, b, c, e[0], 6, -198630844);
-                c = l(c, d, a, b, e[7], 10, 1126891415);
-                b = l(b, c, d, a, e[14], 15, -1416354905);
-                a = l(a, b, c, d, e[5], 21, -57434055);
-                d = l(d, a, b, c, e[12], 6, 1700485571);
-                c = l(c, d, a, b, e[3], 10, -1894986606);
-                b = l(b, c, d, a, e[10], 15, -1051523);
-                a = l(a, b, c, d, e[1], 21, -2054922799);
-                d = l(d, a, b, c, e[8], 6, 1873313359);
-                c = l(c, d, a, b, e[15], 10, -30611744);
-                b = l(b, c, d, a, e[6], 15, -1560198380);
-                a = l(a, b, c, d, e[13], 21, 1309151649);
-                d = l(d, a, b, c, e[4], 6, -145523070);
-                c = l(c, d, a, b, e[11], 10, -1120210379);
-                b = l(b, c, d, a, e[2], 15, 718787259);
-                a = l(a, b, c, d, e[9], 21, -343485551);
-                f[0] = m(d, f[0]);
-                f[1] = m(a, f[1]);
-                f[2] = m(b, f[2]);
-                f[3] = m(c, f[3])
-            }, g = function(f, e, d, a, b, c) {
-                e = m(m(e, f), m(a, c));
-                return m(e << b | e >>> 32 - b, d)
-            }
-              , h = function(f, e, d, a, b, c, n) {
-                return g(e & d | ~e & a, f, e, b, c, n)
-            }
-              , k = function(f, e, d, a, b, c, n) {
-                return g(e & a | d & ~a, f, e, b, c, n)
-            }
-              , l = function(f, e, d, a, b, c, n) {
-                return g(d ^ (e | ~a), f, e, b, c, n)
-            }, r = "0123456789abcdef".split("");
-
-            var m = function(f, e) {
-                return f + e & 4294967295
-            };
-
-            var q = function(f) {
-                var e = f.length, d = [1732584193, -271733879, -1732584194, 271733878], a;
-                for (a = 64; a <= f.length; a += 64) {
-                    var b, c = f.substring(a - 64, a), g = [];
-                    for (b = 0; 64 > b; b += 4)
-                        g[b >> 2] = c.charCodeAt(b) + (c.charCodeAt(b + 1) << 8) + (c.charCodeAt(b + 2) << 16) + (c.charCodeAt(b + 3) << 24);
-                    p(d, g)
-                }
-                f = f.substring(a - 64);
-                b = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
-                for (a = 0; a < f.length; a++)
-                    b[a >> 2] |= f.charCodeAt(a) << (a % 4 << 3);
-                b[a >> 2] |= 128 << (a % 4 << 3);
-                if (55 < a)
-                    for (p(d, b),
-                    a = 0; 16 > a; a++)
-                        b[a] = 0;
-                b[14] = 8 * e;
-                p(d, b);
-                return d
-            };
-
-            var md5 = function(f, e, timeDifference) {
-                var d = Date.now() + 6E4 + timeDifference;
-                e = q(d + f + e);
-                f = [];
-                for (var a = 0; a < e.length; a++) {
-                    var b = e[a];
-                    var c = []
-                      , g = 4;
-                    do
-                        c[--g] = b & 255,
-                        b >>= 8;
-                    while (g);
-                    b = c;
-                    for (c = b.length - 1; 0 <= c; c--)
-                        f.push(b[c])
-                }
-                g = void 0;
-                c = "";
-                for (e = a = b = 0; e < 4 * f.length / 3; g = b >> 2 * (++e & 3) & 63,
-                c += String.fromCharCode(g + 71 - (26 > g ? 6 : 52 > g ? 0 : 62 > g ? 75 : g ^ 63 ? 90 : 87)) + (75 == (e - 1) % 76 ? "\r\n" : ""))
-                    e & 3 ^ 3 && (b = b << 8 ^ f[a++]);
-                for (; e++ & 3; )
-                    c += "\x3d";
-                return c.replace(/\+/g, "-").replace(/\//g, "_") + "," + d
-            };
-
-            "5d41402abc4b2a76b9719d911017c592" != function(f) {
-                for (var e = 0; e < f.length; e++) {
-                    for (var d = e, a = f[e], b = "", c = 0; 4 > c; c++)
-                        b += r[a >> 8 * c + 4 & 15] + r[a >> 8 * c & 15];
-                    f[d] = b
-                }
-                return f.join("")
-            }(q("hello")) && (m = function(f, e) {
-                var d = (f & 65535) + (e & 65535);
-                return (f >> 16) + (e >> 16) + (d >> 16) << 16 | d & 65535
-            }
-            )
-
-            //console.log(md5('/r/nh22/2022/VNUS_DE_NYKE/19_07_22_2302_skt/h264.mpd','vx54axqjal4f0yy2',-2274));
-            //console.log(md5('/r/nh22/2022/VNUS_DE_NYKE/19_07_22_2302_skt/subtitle_pl/34.m4s','vx54axqjal4f0yy2',-2274));
-            
-            """;
+        return false;
     }
-}
+
+    public override string Process(string oriUrl, ParserConfig parserConfig)
+    {
+        var a = new Uri(oriUrl).AbsolutePath;
+        var n = oriUrl + "?secure=" + Calc(a);
+        return n;
+    }
+
+    private static string Calc(string a)
+    {
+        string returnStr = Function!.Call(new Arguments { a, SecureToken, TimeDifference }).ToString();
+        return returnStr;
+    }
+
+    ////https://www.nowehoryzonty.pl/packed/videonho.js?v=1114377281:formatted
+    private static readonly string JS = """
+                                        var p = function(f, e) {
+                                            var d = f[0]
+                                              , a = f[1]
+                                              , b = f[2]
+                                              , c = f[3];
+                                            d = h(d, a, b, c, e[0], 7, -680876936);
+                                            c = h(c, d, a, b, e[1], 12, -389564586);
+                                            b = h(b, c, d, a, e[2], 17, 606105819);
+                                            a = h(a, b, c, d, e[3], 22, -1044525330);
+                                            d = h(d, a, b, c, e[4], 7, -176418897);
+                                            c = h(c, d, a, b, e[5], 12, 1200080426);
+                                            b = h(b, c, d, a, e[6], 17, -1473231341);
+                                            a = h(a, b, c, d, e[7], 22, -45705983);
+                                            d = h(d, a, b, c, e[8], 7, 1770035416);
+                                            c = h(c, d, a, b, e[9], 12, -1958414417);
+                                            b = h(b, c, d, a, e[10], 17, -42063);
+                                            a = h(a, b, c, d, e[11], 22, -1990404162);
+                                            d = h(d, a, b, c, e[12], 7, 1804603682);
+                                            c = h(c, d, a, b, e[13], 12, -40341101);
+                                            b = h(b, c, d, a, e[14], 17, -1502002290);
+                                            a = h(a, b, c, d, e[15], 22, 1236535329);
+                                            d = k(d, a, b, c, e[1], 5, -165796510);
+                                            c = k(c, d, a, b, e[6], 9, -1069501632);
+                                            b = k(b, c, d, a, e[11], 14, 643717713);
+                                            a = k(a, b, c, d, e[0], 20, -373897302);
+                                            d = k(d, a, b, c, e[5], 5, -701558691);
+                                            c = k(c, d, a, b, e[10], 9, 38016083);
+                                            b = k(b, c, d, a, e[15], 14, -660478335);
+                                            a = k(a, b, c, d, e[4], 20, -405537848);
+                                            d = k(d, a, b, c, e[9], 5, 568446438);
+                                            c = k(c, d, a, b, e[14], 9, -1019803690);
+                                            b = k(b, c, d, a, e[3], 14, -187363961);
+                                            a = k(a, b, c, d, e[8], 20, 1163531501);
+                                            d = k(d, a, b, c, e[13], 5, -1444681467);
+                                            c = k(c, d, a, b, e[2], 9, -51403784);
+                                            b = k(b, c, d, a, e[7], 14, 1735328473);
+                                            a = k(a, b, c, d, e[12], 20, -1926607734);
+                                            d = g(a ^ b ^ c, d, a, e[5], 4, -378558);
+                                            c = g(d ^ a ^ b, c, d, e[8], 11, -2022574463);
+                                            b = g(c ^ d ^ a, b, c, e[11], 16, 1839030562);
+                                            a = g(b ^ c ^ d, a, b, e[14], 23, -35309556);
+                                            d = g(a ^ b ^ c, d, a, e[1], 4, -1530992060);
+                                            c = g(d ^ a ^ b, c, d, e[4], 11, 1272893353);
+                                            b = g(c ^ d ^ a, b, c, e[7], 16, -155497632);
+                                            a = g(b ^ c ^ d, a, b, e[10], 23, -1094730640);
+                                            d = g(a ^ b ^ c, d, a, e[13], 4, 681279174);
+                                            c = g(d ^ a ^ b, c, d, e[0], 11, -358537222);
+                                            b = g(c ^ d ^ a, b, c, e[3], 16, -722521979);
+                                            a = g(b ^ c ^ d, a, b, e[6], 23, 76029189);
+                                            d = g(a ^ b ^ c, d, a, e[9], 4, -640364487);
+                                            c = g(d ^ a ^ b, c, d, e[12], 11, -421815835);
+                                            b = g(c ^ d ^ a, b, c, e[15], 16, 530742520);
+                                            a = g(b ^ c ^ d, a, b, e[2], 23, -995338651);
+                                            d = l(d, a, b, c, e[0], 6, -198630844);
+                                            c = l(c, d, a, b, e[7], 10, 1126891415);
+                                            b = l(b, c, d, a, e[14], 15, -1416354905);
+                                            a = l(a, b, c, d, e[5], 21, -57434055);
+                                            d = l(d, a, b, c, e[12], 6, 1700485571);
+                                            c = l(c, d, a, b, e[3], 10, -1894986606);
+                                            b = l(b, c, d, a, e[10], 15, -1051523);
+                                            a = l(a, b, c, d, e[1], 21, -2054922799);
+                                            d = l(d, a, b, c, e[8], 6, 1873313359);
+                                            c = l(c, d, a, b, e[15], 10, -30611744);
+                                            b = l(b, c, d, a, e[6], 15, -1560198380);
+                                            a = l(a, b, c, d, e[13], 21, 1309151649);
+                                            d = l(d, a, b, c, e[4], 6, -145523070);
+                                            c = l(c, d, a, b, e[11], 10, -1120210379);
+                                            b = l(b, c, d, a, e[2], 15, 718787259);
+                                            a = l(a, b, c, d, e[9], 21, -343485551);
+                                            f[0] = m(d, f[0]);
+                                            f[1] = m(a, f[1]);
+                                            f[2] = m(b, f[2]);
+                                            f[3] = m(c, f[3])
+                                        }, g = function(f, e, d, a, b, c) {
+                                            e = m(m(e, f), m(a, c));
+                                            return m(e << b | e >>> 32 - b, d)
+                                        }
+                                          , h = function(f, e, d, a, b, c, n) {
+                                            return g(e & d | ~e & a, f, e, b, c, n)
+                                        }
+                                          , k = function(f, e, d, a, b, c, n) {
+                                            return g(e & a | d & ~a, f, e, b, c, n)
+                                        }
+                                          , l = function(f, e, d, a, b, c, n) {
+                                            return g(d ^ (e | ~a), f, e, b, c, n)
+                                        }, r = "0123456789abcdef".split("");
+
+                                        var m = function(f, e) {
+                                            return f + e & 4294967295
+                                        };
+
+                                        var q = function(f) {
+                                            var e = f.length, d = [1732584193, -271733879, -1732584194, 271733878], a;
+                                            for (a = 64; a <= f.length; a += 64) {
+                                                var b, c = f.substring(a - 64, a), g = [];
+                                                for (b = 0; 64 > b; b += 4)
+                                                    g[b >> 2] = c.charCodeAt(b) + (c.charCodeAt(b + 1) << 8) + (c.charCodeAt(b + 2) << 16) + (c.charCodeAt(b + 3) << 24);
+                                                p(d, g)
+                                            }
+                                            f = f.substring(a - 64);
+                                            b = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
+                                            for (a = 0; a < f.length; a++)
+                                                b[a >> 2] |= f.charCodeAt(a) << (a % 4 << 3);
+                                            b[a >> 2] |= 128 << (a % 4 << 3);
+                                            if (55 < a)
+                                                for (p(d, b),
+                                                a = 0; 16 > a; a++)
+                                                    b[a] = 0;
+                                            b[14] = 8 * e;
+                                            p(d, b);
+                                            return d
+                                        };
+
+                                        var md5 = function(f, e, timeDifference) {
+                                            var d = Date.now() + 6E4 + timeDifference;
+                                            e = q(d + f + e);
+                                            f = [];
+                                            for (var a = 0; a < e.length; a++) {
+                                                var b = e[a];
+                                                var c = []
+                                                  , g = 4;
+                                                do
+                                                    c[--g] = b & 255,
+                                                    b >>= 8;
+                                                while (g);
+                                                b = c;
+                                                for (c = b.length - 1; 0 <= c; c--)
+                                                    f.push(b[c])
+                                            }
+                                            g = void 0;
+                                            c = "";
+                                            for (e = a = b = 0; e < 4 * f.length / 3; g = b >> 2 * (++e & 3) & 63,
+                                            c += String.fromCharCode(g + 71 - (26 > g ? 6 : 52 > g ? 0 : 62 > g ? 75 : g ^ 63 ? 90 : 87)) + (75 == (e - 1) % 76 ? "\r\n" : ""))
+                                                e & 3 ^ 3 && (b = b << 8 ^ f[a++]);
+                                            for (; e++ & 3; )
+                                                c += "\x3d";
+                                            return c.replace(/\+/g, "-").replace(/\//g, "_") + "," + d
+                                        };
+
+                                        "5d41402abc4b2a76b9719d911017c592" != function(f) {
+                                            for (var e = 0; e < f.length; e++) {
+                                                for (var d = e, a = f[e], b = "", c = 0; 4 > c; c++)
+                                                    b += r[a >> 8 * c + 4 & 15] + r[a >> 8 * c & 15];
+                                                f[d] = b
+                                            }
+                                            return f.join("")
+                                        }(q("hello")) && (m = function(f, e) {
+                                            var d = (f & 65535) + (e & 65535);
+                                            return (f >> 16) + (e >> 16) + (d >> 16) << 16 | d & 65535
+                                        }
+                                        )
+
+                                        //console.log(md5('/r/nh22/2022/VNUS_DE_NYKE/19_07_22_2302_skt/h264.mpd','vx54axqjal4f0yy2',-2274));
+                                        //console.log(md5('/r/nh22/2022/VNUS_DE_NYKE/19_07_22_2302_skt/subtitle_pl/34.m4s','vx54axqjal4f0yy2',-2274));
+
+                                        """;
+}
\ No newline at end of file
diff --git a/src/N_m3u8DL-RE/Program.cs b/src/N_m3u8DL-RE/Program.cs
index 2f0c0b5..0d350fd 100644
--- a/src/N_m3u8DL-RE/Program.cs
+++ b/src/N_m3u8DL-RE/Program.cs
@@ -16,437 +16,436 @@ using N_m3u8DL_RE.CommandLine;
 using System.Net;
 using System.Net.Http.Headers;
 
-namespace N_m3u8DL_RE
+namespace N_m3u8DL_RE;
+
+internal class Program
 {
-    internal class Program
+    static async Task Main(string[] args)
     {
-        static async Task Main(string[] args)
+        Console.CancelKeyPress += Console_CancelKeyPress;
+        ServicePointManager.DefaultConnectionLimit = 1024;
+        try { Console.CursorVisible = true; } catch { }
+
+        string loc = "en-US";
+        string currLoc = Thread.CurrentThread.CurrentUICulture.Name;
+        if (currLoc == "zh-CN" || currLoc == "zh-SG") loc = "zh-CN";
+        else if (currLoc.StartsWith("zh-")) loc = "zh-TW";
+
+        // 处理用户-h等请求
+        var index = -1;
+        var list = new List<string>(args);
+        if ((index = list.IndexOf("--ui-language")) != -1 && list.Count > index + 1 && new List<string> { "en-US", "zh-CN", "zh-TW" }.Contains(list[index + 1]))
         {
-            Console.CancelKeyPress += Console_CancelKeyPress;
-            ServicePointManager.DefaultConnectionLimit = 1024;
-            try { Console.CursorVisible = true; } catch { }
-
-            string loc = "en-US";
-            string currLoc = Thread.CurrentThread.CurrentUICulture.Name;
-            if (currLoc == "zh-CN" || currLoc == "zh-SG") loc = "zh-CN";
-            else if (currLoc.StartsWith("zh-")) loc = "zh-TW";
-
-            //处理用户-h等请求
-            var index = -1;
-            var list = new List<string>(args);
-            if ((index = list.IndexOf("--ui-language")) != -1 && list.Count > index + 1 && new List<string> { "en-US", "zh-CN", "zh-TW" }.Contains(list[index + 1]))
-            {
-                loc = list[index + 1];
-            }
-
-            CultureInfo.DefaultThreadCurrentCulture = new CultureInfo(loc);
-            Thread.CurrentThread.CurrentUICulture = CultureInfo.GetCultureInfo(loc);
-            Thread.CurrentThread.CurrentCulture = CultureInfo.GetCultureInfo(loc);
-
-
-            await CommandInvoker.InvokeArgs(args, DoWorkAsync);
+            loc = list[index + 1];
         }
 
-        private static void Console_CancelKeyPress(object? sender, ConsoleCancelEventArgs e)
-        {
-            Logger.WarnMarkUp("Force Exit...");
-            try 
-            { 
-                Console.CursorVisible = true;
-                if (!OperatingSystem.IsWindows())
-                    System.Diagnostics.Process.Start("stty", "echo");
-            } catch { }
-            Environment.Exit(0);
-        }
+        CultureInfo.DefaultThreadCurrentCulture = new CultureInfo(loc);
+        Thread.CurrentThread.CurrentUICulture = CultureInfo.GetCultureInfo(loc);
+        Thread.CurrentThread.CurrentCulture = CultureInfo.GetCultureInfo(loc);
 
-        static int GetOrder(StreamSpec streamSpec)
-        {
-            if (streamSpec.Channels == null) return 0;
+
+        await CommandInvoker.InvokeArgs(args, DoWorkAsync);
+    }
+
+    private static void Console_CancelKeyPress(object? sender, ConsoleCancelEventArgs e)
+    {
+        Logger.WarnMarkUp("Force Exit...");
+        try 
+        { 
+            Console.CursorVisible = true;
+            if (!OperatingSystem.IsWindows())
+                System.Diagnostics.Process.Start("stty", "echo");
+        } catch { }
+        Environment.Exit(0);
+    }
+
+    static int GetOrder(StreamSpec streamSpec)
+    {
+        if (streamSpec.Channels == null) return 0;
             
-            var str = streamSpec.Channels.Split('/')[0];
-            return int.TryParse(str, out var order) ? order : 0;
+        var str = streamSpec.Channels.Split('/')[0];
+        return int.TryParse(str, out var order) ? order : 0;
+    }
+
+    static async Task DoWorkAsync(MyOption option)
+    {
+            
+        if (Console.IsOutputRedirected || Console.IsErrorRedirected)
+        {
+            option.ForceAnsiConsole = true;
+            option.NoAnsiColor = true;
+            Logger.Info(ResString.consoleRedirected);
+        }
+        CustomAnsiConsole.InitConsole(option.ForceAnsiConsole, option.NoAnsiColor);
+        // 检测更新
+        _ = CheckUpdateAsync();
+
+        Logger.IsWriteFile = !option.NoLog;
+        Logger.InitLogFile();
+        Logger.LogLevel = option.LogLevel;
+        Logger.Info(CommandInvoker.VERSION_INFO);
+
+        if (option.UseSystemProxy == false)
+        {
+            HTTPUtil.HttpClientHandler.UseProxy = false;
         }
 
-        static async Task DoWorkAsync(MyOption option)
+        if (option.CustomProxy != null)
         {
-            
-            if (Console.IsOutputRedirected || Console.IsErrorRedirected)
+            HTTPUtil.HttpClientHandler.Proxy = option.CustomProxy;
+            HTTPUtil.HttpClientHandler.UseProxy = true;
+        }
+
+        // 检查互斥的选项
+
+        if (!option.MuxAfterDone && option.MuxImports != null && option.MuxImports.Count > 0)
+        {
+            throw new ArgumentException("MuxAfterDone disabled, MuxImports not allowed!");
+        }
+
+        // LivePipeMux开启时 LiveRealTimeMerge必须开启
+        if (option.LivePipeMux && !option.LiveRealTimeMerge)
+        {
+            Logger.WarnMarkUp("LivePipeMux detected, forced enable LiveRealTimeMerge");
+            option.LiveRealTimeMerge = true;
+        }
+
+        // 预先检查ffmpeg
+        if (option.FFmpegBinaryPath == null)
+            option.FFmpegBinaryPath = GlobalUtil.FindExecutable("ffmpeg");
+
+        if (string.IsNullOrEmpty(option.FFmpegBinaryPath) || !File.Exists(option.FFmpegBinaryPath))
+        {
+            throw new FileNotFoundException(ResString.ffmpegNotFound);
+        }
+
+        Logger.Extra($"ffmpeg => {option.FFmpegBinaryPath}");
+
+        // 预先检查mkvmerge
+        if (option.MuxOptions != null && option.MuxOptions.UseMkvmerge && option.MuxAfterDone)
+        {
+            if (option.MkvmergeBinaryPath == null)
+                option.MkvmergeBinaryPath = GlobalUtil.FindExecutable("mkvmerge");
+            if (string.IsNullOrEmpty(option.MkvmergeBinaryPath) || !File.Exists(option.MkvmergeBinaryPath))
             {
-                option.ForceAnsiConsole = true;
-                option.NoAnsiColor = true;
-                Logger.Info(ResString.consoleRedirected);
+                throw new FileNotFoundException("mkvmerge not found");
             }
-            CustomAnsiConsole.InitConsole(option.ForceAnsiConsole, option.NoAnsiColor);
-            //检测更新
-            CheckUpdateAsync();
+            Logger.Extra($"mkvmerge => {option.MkvmergeBinaryPath}");
+        }
 
-            Logger.IsWriteFile = !option.NoLog;
-            Logger.InitLogFile();
-            Logger.LogLevel = option.LogLevel;
-            Logger.Info(CommandInvoker.VERSION_INFO);
-
-            if (option.UseSystemProxy == false)
+        // 预先检查
+        if ((option.Keys != null && option.Keys.Length > 0) || option.KeyTextFile != null)
+        {
+            if (string.IsNullOrEmpty(option.DecryptionBinaryPath))
             {
-                HTTPUtil.HttpClientHandler.UseProxy = false;
-            }
-
-            if (option.CustomProxy != null)
-            {
-                HTTPUtil.HttpClientHandler.Proxy = option.CustomProxy;
-                HTTPUtil.HttpClientHandler.UseProxy = true;
-            }
-
-            //检查互斥的选项
-
-            if (!option.MuxAfterDone && option.MuxImports != null && option.MuxImports.Count > 0)
-            {
-                throw new ArgumentException("MuxAfterDone disabled, MuxImports not allowed!");
-            }
-
-            //LivePipeMux开启时 LiveRealTimeMerge必须开启
-            if (option.LivePipeMux && !option.LiveRealTimeMerge)
-            {
-                Logger.WarnMarkUp("LivePipeMux detected, forced enable LiveRealTimeMerge");
-                option.LiveRealTimeMerge = true;
-            }
-
-            //预先检查ffmpeg
-            if (option.FFmpegBinaryPath == null)
-                option.FFmpegBinaryPath = GlobalUtil.FindExecutable("ffmpeg");
-
-            if (string.IsNullOrEmpty(option.FFmpegBinaryPath) || !File.Exists(option.FFmpegBinaryPath))
-            {
-                throw new FileNotFoundException(ResString.ffmpegNotFound);
-            }
-
-            Logger.Extra($"ffmpeg => {option.FFmpegBinaryPath}");
-
-            //预先检查mkvmerge
-            if (option.MuxOptions != null && option.MuxOptions.UseMkvmerge && option.MuxAfterDone)
-            {
-                if (option.MkvmergeBinaryPath == null)
-                    option.MkvmergeBinaryPath = GlobalUtil.FindExecutable("mkvmerge");
-                if (string.IsNullOrEmpty(option.MkvmergeBinaryPath) || !File.Exists(option.MkvmergeBinaryPath))
+                if (option.UseShakaPackager)
                 {
-                    throw new FileNotFoundException("mkvmerge not found");
+                    var file = GlobalUtil.FindExecutable("shaka-packager");
+                    var file2 = GlobalUtil.FindExecutable("packager-linux-x64");
+                    var file3 = GlobalUtil.FindExecutable("packager-osx-x64");
+                    var file4 = GlobalUtil.FindExecutable("packager-win-x64");
+                    if (file == null && file2 == null && file3 == null && file4 == null) throw new FileNotFoundException("shaka-packager not found!");
+                    option.DecryptionBinaryPath = file ?? file2 ?? file3 ?? file4;
+                    Logger.Extra($"shaka-packager => {option.DecryptionBinaryPath}");
                 }
-                Logger.Extra($"mkvmerge => {option.MkvmergeBinaryPath}");
-            }
-
-            //预先检查
-            if ((option.Keys != null && option.Keys.Length > 0) || option.KeyTextFile != null)
-            {
-                if (string.IsNullOrEmpty(option.DecryptionBinaryPath))
+                else
                 {
-                    if (option.UseShakaPackager)
-                    {
-                        var file = GlobalUtil.FindExecutable("shaka-packager");
-                        var file2 = GlobalUtil.FindExecutable("packager-linux-x64");
-                        var file3 = GlobalUtil.FindExecutable("packager-osx-x64");
-                        var file4 = GlobalUtil.FindExecutable("packager-win-x64");
-                        if (file == null && file2 == null && file3 == null && file4 == null) throw new FileNotFoundException("shaka-packager not found!");
-                        option.DecryptionBinaryPath = file ?? file2 ?? file3 ?? file4;
-                        Logger.Extra($"shaka-packager => {option.DecryptionBinaryPath}");
-                    }
-                    else
-                    {
-                        var file = GlobalUtil.FindExecutable("mp4decrypt");
-                        if (file == null) throw new FileNotFoundException("mp4decrypt not found!");
-                        option.DecryptionBinaryPath = file;
-                        Logger.Extra($"mp4decrypt => {option.DecryptionBinaryPath}");
-                    }
-                }
-                else if (!File.Exists(option.DecryptionBinaryPath))
-                {
-                    throw new FileNotFoundException(option.DecryptionBinaryPath);
+                    var file = GlobalUtil.FindExecutable("mp4decrypt");
+                    if (file == null) throw new FileNotFoundException("mp4decrypt not found!");
+                    option.DecryptionBinaryPath = file;
+                    Logger.Extra($"mp4decrypt => {option.DecryptionBinaryPath}");
                 }
             }
-
-            //默认的headers
-            var headers = new Dictionary<string, string>()
+            else if (!File.Exists(option.DecryptionBinaryPath))
             {
-                ["user-agent"] = "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36"
-            };
-            //添加或替换用户输入的headers
-            foreach (var item in option.Headers)
-            {
-                headers[item.Key] = item.Value;
-                Logger.Extra($"User-Defined Header => {item.Key}: {item.Value}");
+                throw new FileNotFoundException(option.DecryptionBinaryPath);
             }
+        }
 
-            var parserConfig = new ParserConfig()
+        // 默认的headers
+        var headers = new Dictionary<string, string>()
+        {
+            ["user-agent"] = "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36"
+        };
+        // 添加或替换用户输入的headers
+        foreach (var item in option.Headers)
+        {
+            headers[item.Key] = item.Value;
+            Logger.Extra($"User-Defined Header => {item.Key}: {item.Value}");
+        }
+
+        var parserConfig = new ParserConfig()
+        {
+            AppendUrlParams = option.AppendUrlParams,
+            UrlProcessorArgs = option.UrlProcessorArgs,
+            BaseUrl = option.BaseUrl!,
+            Headers = headers,
+            CustomMethod = option.CustomHLSMethod,
+            CustomeKey = option.CustomHLSKey,
+            CustomeIV = option.CustomHLSIv,
+        };
+
+        // demo1
+        parserConfig.ContentProcessors.Insert(0, new DemoProcessor());
+        // demo2
+        parserConfig.KeyProcessors.Insert(0, new DemoProcessor2());
+        // for www.nowehoryzonty.pl
+        parserConfig.UrlProcessors.Insert(0, new NowehoryzontyUrlProcessor());
+
+        // 等待任务开始时间
+        if (option.TaskStartAt != null && option.TaskStartAt > DateTime.Now)
+        {
+            Logger.InfoMarkUp(ResString.taskStartAt + option.TaskStartAt);
+            while (option.TaskStartAt > DateTime.Now)
             {
-                AppendUrlParams = option.AppendUrlParams,
-                UrlProcessorArgs = option.UrlProcessorArgs,
-                BaseUrl = option.BaseUrl!,
-                Headers = headers,
-                CustomMethod = option.CustomHLSMethod,
-                CustomeKey = option.CustomHLSKey,
-                CustomeIV = option.CustomHLSIv,
-            };
-
-            //demo1
-            parserConfig.ContentProcessors.Insert(0, new DemoProcessor());
-            //demo2
-            parserConfig.KeyProcessors.Insert(0, new DemoProcessor2());
-            //for www.nowehoryzonty.pl
-            parserConfig.UrlProcessors.Insert(0, new NowehoryzontyUrlProcessor());
-
-            //等待任务开始时间
-            if (option.TaskStartAt != null && option.TaskStartAt > DateTime.Now)
-            {
-                Logger.InfoMarkUp(ResString.taskStartAt + option.TaskStartAt);
-                while (option.TaskStartAt > DateTime.Now)
-                {
-                    await Task.Delay(1000);
-                }
+                await Task.Delay(1000);
             }
+        }
 
-            var url = option.Input;
+        var url = option.Input;
 
-            //流提取器配置
-            var extractor = new StreamExtractor(parserConfig);
-            // 从链接加载内容
-            await RetryUtil.WebRequestRetryAsync(async () =>
+        // 流提取器配置
+        var extractor = new StreamExtractor(parserConfig);
+        // 从链接加载内容
+        await RetryUtil.WebRequestRetryAsync(async () =>
+        {
+            await extractor.LoadSourceFromUrlAsync(url);
+            return true;
+        });
+        // 解析流信息
+        var streams = await extractor.ExtractStreamsAsync();
+
+
+        // 全部媒体
+        var lists = streams.OrderBy(p => p.MediaType).ThenByDescending(p => p.Bandwidth).ThenByDescending(GetOrder);
+        // 基本流
+        var basicStreams = lists.Where(x => x.MediaType == null || x.MediaType == MediaType.VIDEO);
+        // 可选音频轨道
+        var audios = lists.Where(x => x.MediaType == MediaType.AUDIO);
+        // 可选字幕轨道
+        var subs = lists.Where(x => x.MediaType == MediaType.SUBTITLES);
+
+        // 尝试从URL或文件读取文件名
+        if (string.IsNullOrEmpty(option.SaveName))
+        {
+            option.SaveName = OtherUtil.GetFileNameFromInput(option.Input);
+        }
+
+        // 生成文件夹
+        var tmpDir = Path.Combine(option.TmpDir ?? Environment.CurrentDirectory, $"{option.SaveName ?? DateTime.Now.ToString("yyyy-MM-dd_HH-mm-ss")}");
+        // 记录文件
+        extractor.RawFiles["meta.json"] = GlobalUtil.ConvertToJson(lists);
+        // 写出文件
+        await WriteRawFilesAsync(option, extractor, tmpDir);
+
+        Logger.Info(ResString.streamsInfo, lists.Count(), basicStreams.Count(), audios.Count(), subs.Count());
+
+        foreach (var item in lists)
+        {
+            Logger.InfoMarkUp(item.ToString());
+        }
+
+        var selectedStreams = new List<StreamSpec>();
+        if (option.DropVideoFilter != null || option.DropAudioFilter != null || option.DropSubtitleFilter != null)
+        {
+            basicStreams = FilterUtil.DoFilterDrop(basicStreams, option.DropVideoFilter);
+            audios = FilterUtil.DoFilterDrop(audios, option.DropAudioFilter);
+            subs = FilterUtil.DoFilterDrop(subs, option.DropSubtitleFilter);
+            lists = basicStreams.Concat(audios).Concat(subs).OrderBy(x => true);
+        }
+
+        if (option.DropVideoFilter != null) Logger.Extra($"DropVideoFilter => {option.DropVideoFilter}");
+        if (option.DropAudioFilter != null) Logger.Extra($"DropAudioFilter => {option.DropAudioFilter}");
+        if (option.DropSubtitleFilter != null) Logger.Extra($"DropSubtitleFilter => {option.DropSubtitleFilter}");
+        if (option.VideoFilter != null) Logger.Extra($"VideoFilter => {option.VideoFilter}");
+        if (option.AudioFilter != null) Logger.Extra($"AudioFilter => {option.AudioFilter}");
+        if (option.SubtitleFilter != null) Logger.Extra($"SubtitleFilter => {option.SubtitleFilter}");
+
+        if (option.AutoSelect)
+        {
+            if (basicStreams.Any())
+                selectedStreams.Add(basicStreams.First());
+            var langs = audios.DistinctBy(a => a.Language).Select(a => a.Language);
+            foreach (var lang in langs)
             {
-                await extractor.LoadSourceFromUrlAsync(url);
-                return true;
-            });
-            //解析流信息
-            var streams = await extractor.ExtractStreamsAsync();
-
-
-            //全部媒体
-            var lists = streams.OrderBy(p => p.MediaType).ThenByDescending(p => p.Bandwidth).ThenByDescending(GetOrder);
-            //基本流
-            var basicStreams = lists.Where(x => x.MediaType == null || x.MediaType == MediaType.VIDEO);
-            //可选音频轨道
-            var audios = lists.Where(x => x.MediaType == MediaType.AUDIO);
-            //可选字幕轨道
-            var subs = lists.Where(x => x.MediaType == MediaType.SUBTITLES);
-
-            //尝试从URL或文件读取文件名
-            if (string.IsNullOrEmpty(option.SaveName))
-            {
-                option.SaveName = OtherUtil.GetFileNameFromInput(option.Input);
+                selectedStreams.Add(audios.Where(a => a.Language == lang).OrderByDescending(a => a.Bandwidth).ThenByDescending(GetOrder).First());
             }
+            selectedStreams.AddRange(subs);
+        }
+        else if (option.SubOnly)
+        {
+            selectedStreams.AddRange(subs);
+        }
+        else if (option.VideoFilter != null || option.AudioFilter != null || option.SubtitleFilter != null)
+        {
+            basicStreams = FilterUtil.DoFilterKeep(basicStreams, option.VideoFilter);
+            audios = FilterUtil.DoFilterKeep(audios, option.AudioFilter);
+            subs = FilterUtil.DoFilterKeep(subs, option.SubtitleFilter);
+            selectedStreams = basicStreams.Concat(audios).Concat(subs).ToList();
+        }
+        else
+        {
+            // 展示交互式选择框
+            selectedStreams = FilterUtil.SelectStreams(lists);
+        }
 
-            //生成文件夹
-            var tmpDir = Path.Combine(option.TmpDir ?? Environment.CurrentDirectory, $"{option.SaveName ?? DateTime.Now.ToString("yyyy-MM-dd_HH-mm-ss")}");
-            //记录文件
-            extractor.RawFiles["meta.json"] = GlobalUtil.ConvertToJson(lists);
-            //写出文件
-            await WriteRawFilesAsync(option, extractor, tmpDir);
+        if (!selectedStreams.Any())
+            throw new Exception(ResString.noStreamsToDownload);
 
-            Logger.Info(ResString.streamsInfo, lists.Count(), basicStreams.Count(), audios.Count(), subs.Count());
+        // HLS: 选中流中若有没加载出playlist的,加载playlist
+        // DASH/MSS: 加载playlist (调用url预处理器)
+        if (selectedStreams.Any(s => s.Playlist == null) || extractor.ExtractorType == ExtractorType.MPEG_DASH || extractor.ExtractorType == ExtractorType.MSS)
+            await extractor.FetchPlayListAsync(selectedStreams);
 
-            foreach (var item in lists)
-            {
-                Logger.InfoMarkUp(item.ToString());
-            }
+        // 直播检测
+        var livingFlag = selectedStreams.Any(s => s.Playlist?.IsLive == true) && !option.LivePerformAsVod;
+        if (livingFlag)
+        {
+            Logger.WarnMarkUp($"[white on darkorange3_1]{ResString.liveFound}[/]");
+        }
 
-            var selectedStreams = new List<StreamSpec>();
-            if (option.DropVideoFilter != null || option.DropAudioFilter != null || option.DropSubtitleFilter != null)
-            {
-                basicStreams = FilterUtil.DoFilterDrop(basicStreams, option.DropVideoFilter);
-                audios = FilterUtil.DoFilterDrop(audios, option.DropAudioFilter);
-                subs = FilterUtil.DoFilterDrop(subs, option.DropSubtitleFilter);
-                lists = basicStreams.Concat(audios).Concat(subs).OrderBy(x => true);
-            }
+        // 无法识别的加密方式,自动开启二进制合并
+        if (selectedStreams.Any(s => s.Playlist!.MediaParts.Any(p => p.MediaSegments.Any(m => m.EncryptInfo.Method == EncryptMethod.UNKNOWN))))
+        {
+            Logger.WarnMarkUp($"[darkorange3_1]{ResString.autoBinaryMerge3}[/]");
+            option.BinaryMerge = true;
+        }
 
-            if (option.DropVideoFilter != null) Logger.Extra($"DropVideoFilter => {option.DropVideoFilter}");
-            if (option.DropAudioFilter != null) Logger.Extra($"DropAudioFilter => {option.DropAudioFilter}");
-            if (option.DropSubtitleFilter != null) Logger.Extra($"DropSubtitleFilter => {option.DropSubtitleFilter}");
-            if (option.VideoFilter != null) Logger.Extra($"VideoFilter => {option.VideoFilter}");
-            if (option.AudioFilter != null) Logger.Extra($"AudioFilter => {option.AudioFilter}");
-            if (option.SubtitleFilter != null) Logger.Extra($"SubtitleFilter => {option.SubtitleFilter}");
+        // 应用用户自定义的分片范围
+        if (!livingFlag)
+            FilterUtil.ApplyCustomRange(selectedStreams, option.CustomRange);
 
-            if (option.AutoSelect)
-            {
-                if (basicStreams.Any())
-                    selectedStreams.Add(basicStreams.First());
-                var langs = audios.DistinctBy(a => a.Language).Select(a => a.Language);
-                foreach (var lang in langs)
-                {
-                    selectedStreams.Add(audios.Where(a => a.Language == lang).OrderByDescending(a => a.Bandwidth).ThenByDescending(GetOrder).First());
-                }
-                selectedStreams.AddRange(subs);
-            }
-            else if (option.SubOnly)
-            {
-                selectedStreams.AddRange(subs);
-            }
-            else if (option.VideoFilter != null || option.AudioFilter != null || option.SubtitleFilter != null)
-            {
-                basicStreams = FilterUtil.DoFilterKeep(basicStreams, option.VideoFilter);
-                audios = FilterUtil.DoFilterKeep(audios, option.AudioFilter);
-                subs = FilterUtil.DoFilterKeep(subs, option.SubtitleFilter);
-                selectedStreams = basicStreams.Concat(audios).Concat(subs).ToList();
-            }
-            else
-            {
-                //展示交互式选择框
-                selectedStreams = FilterUtil.SelectStreams(lists);
-            }
+        // 应用用户自定义的广告分片关键字
+        FilterUtil.CleanAd(selectedStreams, option.AdKeywords);
 
-            if (!selectedStreams.Any())
-                throw new Exception(ResString.noStreamsToDownload);
+        // 记录文件
+        extractor.RawFiles["meta_selected.json"] = GlobalUtil.ConvertToJson(selectedStreams);
 
-            //HLS: 选中流中若有没加载出playlist的,加载playlist
-            //DASH/MSS: 加载playlist (调用url预处理器)
-            if (selectedStreams.Any(s => s.Playlist == null) || extractor.ExtractorType == ExtractorType.MPEG_DASH || extractor.ExtractorType == ExtractorType.MSS)
-                await extractor.FetchPlayListAsync(selectedStreams);
+        Logger.Info(ResString.selectedStream);
+        foreach (var item in selectedStreams)
+        {
+            Logger.InfoMarkUp(item.ToString());
+        }
 
-            //直播检测
-            var livingFlag = selectedStreams.Any(s => s.Playlist?.IsLive == true) && !option.LivePerformAsVod;
-            if (livingFlag)
-            {
-                Logger.WarnMarkUp($"[white on darkorange3_1]{ResString.liveFound}[/]");
-            }
+        // 写出文件
+        await WriteRawFilesAsync(option, extractor, tmpDir);
 
-            //无法识别的加密方式,自动开启二进制合并
-            if (selectedStreams.Any(s => s.Playlist.MediaParts.Any(p => p.MediaSegments.Any(m => m.EncryptInfo.Method == EncryptMethod.UNKNOWN))))
-            {
-                Logger.WarnMarkUp($"[darkorange3_1]{ResString.autoBinaryMerge3}[/]");
-                option.BinaryMerge = true;
-            }
-
-            //应用用户自定义的分片范围
-            if (!livingFlag)
-                FilterUtil.ApplyCustomRange(selectedStreams, option.CustomRange);
-
-            //应用用户自定义的广告分片关键字
-            FilterUtil.CleanAd(selectedStreams, option.AdKeywords);
-
-            //记录文件
-            extractor.RawFiles["meta_selected.json"] = GlobalUtil.ConvertToJson(selectedStreams);
-
-            Logger.Info(ResString.selectedStream);
-            foreach (var item in selectedStreams)
-            {
-                Logger.InfoMarkUp(item.ToString());
-            }
-
-            //写出文件
-            await WriteRawFilesAsync(option, extractor, tmpDir);
-
-            if (option.SkipDownload)
-            {
-                return;
-            }
+        if (option.SkipDownload)
+        {
+            return;
+        }
 
 #if DEBUG
-            Console.WriteLine("Press any key to continue...");
-            Console.ReadKey();
+        Console.WriteLine("Press any key to continue...");
+        Console.ReadKey();
 #endif
 
-            Logger.InfoMarkUp(ResString.saveName + $"[deepskyblue1]{option.SaveName.EscapeMarkup()}[/]");
+        Logger.InfoMarkUp(ResString.saveName + $"[deepskyblue1]{option.SaveName.EscapeMarkup()}[/]");
 
-            //开始MuxAfterDone后自动使用二进制版
-            if (!option.BinaryMerge && option.MuxAfterDone)
-            {
-                option.BinaryMerge = true;
-                Logger.WarnMarkUp($"[darkorange3_1]{ResString.autoBinaryMerge6}[/]");
-            }
-
-            //下载配置
-            var downloadConfig = new DownloaderConfig()
-            {
-                MyOptions = option,
-                DirPrefix = tmpDir,
-                Headers = parserConfig.Headers, //使用命令行解析得到的Headers
-            };
-
-            var result = false;
-
-            if (extractor.ExtractorType == ExtractorType.HTTP_LIVE)
-            {
-                var sldm = new HTTPLiveRecordManager(downloadConfig, selectedStreams, extractor);
-                result = await sldm.StartRecordAsync();
-            }
-            else if (!livingFlag)
-            {
-                //开始下载
-                var sdm = new SimpleDownloadManager(downloadConfig, selectedStreams, extractor);
-                result = await sdm.StartDownloadAsync();
-            }
-            else
-            {
-                var sldm = new SimpleLiveRecordManager2(downloadConfig, selectedStreams, extractor);
-                result = await sldm.StartRecordAsync();
-            }
-
-            if (result)
-            {
-                Logger.InfoMarkUp("[white on green]Done[/]");
-            }
-            else
-            {
-                Logger.ErrorMarkUp("[white on red]Failed[/]");
-                Environment.ExitCode = 1;
-            }
+        // 开始MuxAfterDone后自动使用二进制版
+        if (!option.BinaryMerge && option.MuxAfterDone)
+        {
+            option.BinaryMerge = true;
+            Logger.WarnMarkUp($"[darkorange3_1]{ResString.autoBinaryMerge6}[/]");
         }
 
-        private static async Task WriteRawFilesAsync(MyOption option, StreamExtractor extractor, string tmpDir)
+        // 下载配置
+        var downloadConfig = new DownloaderConfig()
         {
-            //写出json文件
-            if (option.WriteMetaJson)
-            {
-                if (!Directory.Exists(tmpDir)) Directory.CreateDirectory(tmpDir);
-                Logger.Warn(ResString.writeJson);
-                foreach (var item in extractor.RawFiles)
-                {
-                    var file = Path.Combine(tmpDir, item.Key);
-                    if (!File.Exists(file)) await File.WriteAllTextAsync(file, item.Value, Encoding.UTF8);
-                }
-            }
+            MyOptions = option,
+            DirPrefix = tmpDir,
+            Headers = parserConfig.Headers, // 使用命令行解析得到的Headers
+        };
+
+        var result = false;
+
+        if (extractor.ExtractorType == ExtractorType.HTTP_LIVE)
+        {
+            var sldm = new HTTPLiveRecordManager(downloadConfig, selectedStreams, extractor);
+            result = await sldm.StartRecordAsync();
+        }
+        else if (!livingFlag)
+        {
+            // 开始下载
+            var sdm = new SimpleDownloadManager(downloadConfig, selectedStreams, extractor);
+            result = await sdm.StartDownloadAsync();
+        }
+        else
+        {
+            var sldm = new SimpleLiveRecordManager2(downloadConfig, selectedStreams, extractor);
+            result = await sldm.StartRecordAsync();
         }
 
-        static async Task CheckUpdateAsync()
+        if (result)
         {
-            try
-            {
-                var ver = System.Reflection.Assembly.GetExecutingAssembly().GetName().Version!;
-                string nowVer = $"v{ver.Major}.{ver.Minor}.{ver.Build}";
-                string redirctUrl = await Get302Async("https://github.com/nilaoda/N_m3u8DL-RE/releases/latest");
-                string latestVer = redirctUrl.Replace("https://github.com/nilaoda/N_m3u8DL-RE/releases/tag/", "");
-                if (!latestVer.StartsWith(nowVer) && !latestVer.StartsWith("https"))
-                {
-                    Console.Title = $"{ResString.newVersionFound} {latestVer}";
-                    Logger.InfoMarkUp($"[cyan]{ResString.newVersionFound}[/] [red]{latestVer}[/]");
-                }
-            }
-            catch (Exception)
-            {
-                ;
-            }
+            Logger.InfoMarkUp("[white on green]Done[/]");
         }
-
-        //重定向
-        static async Task<string> Get302Async(string url)
+        else
         {
-            //this allows you to set the settings so that we can get the redirect url
-            var handler = new HttpClientHandler()
-            {
-                AllowAutoRedirect = false
-            };
-            string redirectedUrl = "";
-            using (HttpClient client = new(handler))
-            using (HttpResponseMessage response = await client.GetAsync(url))
-            using (HttpContent content = response.Content)
-            {
-                // ... Read the response to see if we have the redirected url
-                if (response.StatusCode == System.Net.HttpStatusCode.Found)
-                {
-                    HttpResponseHeaders headers = response.Headers;
-                    if (headers != null && headers.Location != null)
-                    {
-                        redirectedUrl = headers.Location.AbsoluteUri;
-                    }
-                }
-            }
-
-            return redirectedUrl;
+            Logger.ErrorMarkUp("[white on red]Failed[/]");
+            Environment.ExitCode = 1;
         }
     }
+
+    private static async Task WriteRawFilesAsync(MyOption option, StreamExtractor extractor, string tmpDir)
+    {
+        // 写出json文件
+        if (option.WriteMetaJson)
+        {
+            if (!Directory.Exists(tmpDir)) Directory.CreateDirectory(tmpDir);
+            Logger.Warn(ResString.writeJson);
+            foreach (var item in extractor.RawFiles)
+            {
+                var file = Path.Combine(tmpDir, item.Key);
+                if (!File.Exists(file)) await File.WriteAllTextAsync(file, item.Value, Encoding.UTF8);
+            }
+        }
+    }
+
+    static async Task CheckUpdateAsync()
+    {
+        try
+        {
+            var ver = System.Reflection.Assembly.GetExecutingAssembly().GetName().Version!;
+            string nowVer = $"v{ver.Major}.{ver.Minor}.{ver.Build}";
+            string redirctUrl = await Get302Async("https://github.com/nilaoda/N_m3u8DL-RE/releases/latest");
+            string latestVer = redirctUrl.Replace("https://github.com/nilaoda/N_m3u8DL-RE/releases/tag/", "");
+            if (!latestVer.StartsWith(nowVer) && !latestVer.StartsWith("https"))
+            {
+                Console.Title = $"{ResString.newVersionFound} {latestVer}";
+                Logger.InfoMarkUp($"[cyan]{ResString.newVersionFound}[/] [red]{latestVer}[/]");
+            }
+        }
+        catch (Exception)
+        {
+            ;
+        }
+    }
+
+    // 重定向
+    static async Task<string> Get302Async(string url)
+    {
+        // this allows you to set the settings so that we can get the redirect url
+        var handler = new HttpClientHandler()
+        {
+            AllowAutoRedirect = false
+        };
+        string redirectedUrl = "";
+        using (HttpClient client = new(handler))
+        using (HttpResponseMessage response = await client.GetAsync(url))
+        using (HttpContent content = response.Content)
+        {
+            // ... Read the response to see if we have the redirected url
+            if (response.StatusCode == System.Net.HttpStatusCode.Found)
+            {
+                HttpResponseHeaders headers = response.Headers;
+                if (headers != null && headers.Location != null)
+                {
+                    redirectedUrl = headers.Location.AbsoluteUri;
+                }
+            }
+        }
+
+        return redirectedUrl;
+    }
 }
\ No newline at end of file
diff --git a/src/N_m3u8DL-RE/Util/DownloadUtil.cs b/src/N_m3u8DL-RE/Util/DownloadUtil.cs
index fd5d9d8..63e29e3 100644
--- a/src/N_m3u8DL-RE/Util/DownloadUtil.cs
+++ b/src/N_m3u8DL-RE/Util/DownloadUtil.cs
@@ -2,8 +2,6 @@
 using N_m3u8DL_RE.Common.Resource;
 using N_m3u8DL_RE.Common.Util;
 using N_m3u8DL_RE.Entity;
-using System.IO;
-using System.Net;
 using System.Net.Http.Headers;
 
 namespace N_m3u8DL_RE.Util;
@@ -111,16 +109,16 @@ internal static class DownloadUtil
             size = await responseStream.ReadAsync(buffer, cancellationTokenSource.Token);
             speedContainer.Add(size);
             await stream.WriteAsync(buffer, 0, size);
-            //检测imageHeader
+            // 检测imageHeader
             bool imageHeader = ImageHeaderUtil.IsImageHeader(buffer);
-            //检测GZip(For DDP Audio)
+            // 检测GZip(For DDP Audio)
             bool gZipHeader = buffer.Length > 2 && buffer[0] == 0x1f && buffer[1] == 0x8b;
 
             while ((size = await responseStream.ReadAsync(buffer, cancellationTokenSource.Token)) > 0)
             {
                 speedContainer.Add(size);
                 await stream.WriteAsync(buffer, 0, size);
-                //限速策略
+                // 限速策略
                 while (speedContainer.Downloaded > speedContainer.SpeedLimit)
                 {
                     await Task.Delay(1);
diff --git a/src/N_m3u8DL-RE/Util/FilterUtil.cs b/src/N_m3u8DL-RE/Util/FilterUtil.cs
index f876d00..1a30e5c 100644
--- a/src/N_m3u8DL-RE/Util/FilterUtil.cs
+++ b/src/N_m3u8DL-RE/Util/FilterUtil.cs
@@ -4,12 +4,7 @@ using N_m3u8DL_RE.Common.Log;
 using N_m3u8DL_RE.Common.Resource;
 using N_m3u8DL_RE.Entity;
 using Spectre.Console;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
 using System.Text.RegularExpressions;
-using System.Threading.Tasks;
 
 namespace N_m3u8DL_RE.Util;
 
@@ -17,7 +12,7 @@ public static class FilterUtil
 {
     public static List<StreamSpec> DoFilterKeep(IEnumerable<StreamSpec> lists, StreamFilter? filter)
     {
-        if (filter == null) return new List<StreamSpec>();
+        if (filter == null) return [];
 
         var inputs = lists.Where(_ => true);
         if (filter.GroupIdReg != null)
@@ -56,13 +51,13 @@ public static class FilterUtil
         var bestNumberStr = filter.For.Replace("best", "");
         var worstNumberStr = filter.For.Replace("worst", "");
 
-        if (filter.For == "best" && inputs.Count() > 0)
+        if (filter.For == "best" && inputs.Any())
             inputs = inputs.Take(1).ToList();
-        else if (filter.For == "worst" && inputs.Count() > 0)
+        else if (filter.For == "worst" && inputs.Any())
             inputs = inputs.TakeLast(1).ToList();
-        else if (int.TryParse(bestNumberStr, out int bestNumber) && inputs.Count() > 0)
+        else if (int.TryParse(bestNumberStr, out int bestNumber) && inputs.Any())
             inputs = inputs.Take(bestNumber).ToList();
-        else if (int.TryParse(worstNumberStr, out int worstNumber) && inputs.Count() > 0)
+        else if (int.TryParse(worstNumberStr, out int worstNumber) && inputs.Any())
             inputs = inputs.TakeLast(worstNumber).ToList();
 
         return inputs.ToList();
@@ -82,15 +77,16 @@ public static class FilterUtil
 
     public static List<StreamSpec> SelectStreams(IEnumerable<StreamSpec> lists)
     {
-        if (lists.Count() == 1)
-            return new List<StreamSpec>(lists);
+        var streamSpecs = lists.ToList();
+        if (streamSpecs.Count == 1)
+            return [..streamSpecs];
 
-        //基本流
-        var basicStreams = lists.Where(x => x.MediaType == null);
-        //可选音频轨道
-        var audios = lists.Where(x => x.MediaType == MediaType.AUDIO);
-        //可选字幕轨道
-        var subs = lists.Where(x => x.MediaType == MediaType.SUBTITLES);
+        // 基本流
+        var basicStreams = streamSpecs.Where(x => x.MediaType == null).ToList();
+        // 可选音频轨道
+        var audios = streamSpecs.Where(x => x.MediaType == MediaType.AUDIO).ToList();
+        // 可选字幕轨道
+        var subs = streamSpecs.Where(x => x.MediaType == MediaType.SUBTITLES).ToList();
 
         var prompt = new MultiSelectionPrompt<StreamSpec>()
                 .Title(ResString.promptTitle)
@@ -107,8 +103,8 @@ public static class FilterUtil
                 .InstructionsText(ResString.promptInfo)
             ;
 
-        //默认选中第一个
-        var first = lists.First();
+        // 默认选中第一个
+        var first = streamSpecs.First();
         prompt.Select(first);
 
         if (basicStreams.Any())
@@ -119,7 +115,7 @@ public static class FilterUtil
         if (audios.Any())
         {
             prompt.AddChoiceGroup(new StreamSpec() { Name = "__Audio" }, audios);
-            //默认音轨
+            // 默认音轨
             if (first.AudioId != null)
             {
                 prompt.Select(audios.First(a => a.GroupId == first.AudioId));
@@ -128,17 +124,17 @@ public static class FilterUtil
         if (subs.Any())
         {
             prompt.AddChoiceGroup(new StreamSpec() { Name = "__Subtitle" }, subs);
-            //默认字幕轨
+            // 默认字幕轨
             if (first.SubtitleId != null)
             {
                 prompt.Select(subs.First(s => s.GroupId == first.SubtitleId));
             }
         }
 
-        //如果此时还是没有选中任何流,自动选择一个
+        // 如果此时还是没有选中任何流,自动选择一个
         prompt.Select(basicStreams.Concat(audios).Concat(subs).First());
 
-        //多选
+        // 多选
         var selectedStreams = CustomAnsiConsole.Console.Prompt(prompt);
 
         return selectedStreams;
@@ -147,11 +143,11 @@ public static class FilterUtil
     /// <summary>
     /// 直播使用。对齐各个轨道的起始。
     /// </summary>
-    /// <param name="streams"></param>
+    /// <param name="selectedSteams"></param>
     /// <param name="takeLastCount"></param>
     public static void SyncStreams(List<StreamSpec> selectedSteams, int takeLastCount = 15)
     {
-        //通过Date同步
+        // 通过Date同步
         if (selectedSteams.All(x => x.Playlist!.MediaParts[0].MediaSegments.All(x => x.DateTime != null)))
         {
             var minDate = selectedSteams.Max(s => s.Playlist!.MediaParts[0].MediaSegments.Min(s => s.DateTime))!;
@@ -159,12 +155,12 @@ public static class FilterUtil
             {
                 foreach (var part in item.Playlist!.MediaParts)
                 {
-                    //秒级同步 忽略毫秒
+                    // 秒级同步 忽略毫秒
                     part.MediaSegments = part.MediaSegments.Where(s => s.DateTime!.Value.Ticks / TimeSpan.TicksPerSecond >= minDate.Value.Ticks / TimeSpan.TicksPerSecond).ToList();
                 }
             }
         }
-        else //通过index同步
+        else // 通过index同步
         {
             var minIndex = selectedSteams.Max(s => s.Playlist!.MediaParts[0].MediaSegments.Min(s => s.Index));
             foreach (var item in selectedSteams)
@@ -176,7 +172,7 @@ public static class FilterUtil
             }
         }
 
-        //取最新的N个分片
+        // 取最新的N个分片
         if (selectedSteams.Any(x => x.Playlist!.MediaParts[0].MediaSegments.Count > takeLastCount))
         {
             var skipCount = selectedSteams.Min(x => x.Playlist!.MediaParts[0].MediaSegments.Count) - takeLastCount + 1;
@@ -198,17 +194,15 @@ public static class FilterUtil
     /// <param name="customRange"></param>
     public static void ApplyCustomRange(List<StreamSpec> selectedSteams, CustomRange? customRange)
     {
-        var resultList = selectedSteams.Select(x => 0d).ToList();
-
         if (customRange == null) return;
 
         Logger.InfoMarkUp($"{ResString.customRangeFound}[Cyan underline]{customRange.InputStr}[/]");
         Logger.WarnMarkUp($"[darkorange3_1]{ResString.customRangeWarn}[/]");
 
-        var filteByIndex = customRange.StartSegIndex != null && customRange.EndSegIndex != null;
-        var filteByTime = customRange.StartSec != null && customRange.EndSec != null;
+        var filterByIndex = customRange is { StartSegIndex: not null, EndSegIndex: not null };
+        var filterByTime = customRange is { StartSec: not null, EndSec: not null };
 
-        if (!filteByIndex && !filteByTime)
+        if (!filterByIndex && !filterByTime)
         {
             Logger.ErrorMarkUp(ResString.customRangeInvalid);
             return;
@@ -220,8 +214,8 @@ public static class FilterUtil
             if (stream.Playlist == null) continue;
             foreach (var part in stream.Playlist.MediaParts)
             {
-                var newSegments = new List<MediaSegment>();
-                if (filteByIndex)
+                List<MediaSegment> newSegments;
+                if (filterByIndex)
                     newSegments = part.MediaSegments.Where(seg => seg.Index >= customRange.StartSegIndex && seg.Index <= customRange.EndSegIndex).ToList();
                 else
                     newSegments = part.MediaSegments.Where(seg => stream.Playlist.MediaParts.SelectMany(p => p.MediaSegments).Where(x => x.Index < seg.Index).Sum(x => x.Duration) >= customRange.StartSec
@@ -239,11 +233,11 @@ public static class FilterUtil
     /// 根据用户输入,清除广告分片
     /// </summary>
     /// <param name="selectedSteams"></param>
-    /// <param name="customRange"></param>
+    /// <param name="keywords"></param>
     public static void CleanAd(List<StreamSpec> selectedSteams, string[]? keywords)
     {
         if (keywords == null) return;
-        var regList = keywords.Select(s => new Regex(s));
+        var regList = keywords.Select(s => new Regex(s)).ToList();
         foreach ( var reg in regList)
         {
             Logger.InfoMarkUp($"{ResString.customAdKeywordsFound}[Cyan underline]{reg}[/]");
@@ -257,19 +251,16 @@ public static class FilterUtil
 
             foreach (var part in stream.Playlist.MediaParts)
             {
-                //没有找到广告分片
+                // 没有找到广告分片
                 if (part.MediaSegments.All(x => regList.All(reg => !reg.IsMatch(x.Url))))
                 {
                     continue;
                 }
-                //找到广告分片 清理
-                else
-                {
-                    part.MediaSegments = part.MediaSegments.Where(x => regList.All(reg => !reg.IsMatch(x.Url))).ToList();
-                }
+                // 找到广告分片 清理
+                part.MediaSegments = part.MediaSegments.Where(x => regList.All(reg => !reg.IsMatch(x.Url))).ToList();
             }
 
-            //清理已经为空的 part
+            // 清理已经为空的 part
             stream.Playlist.MediaParts = stream.Playlist.MediaParts.Where(x => x.MediaSegments.Count > 0).ToList();
 
             var countAfter = stream.SegmentsCount;
diff --git a/src/N_m3u8DL-RE/Util/ImageHeaderUtil.cs b/src/N_m3u8DL-RE/Util/ImageHeaderUtil.cs
index 9fe52e9..fd3537a 100644
--- a/src/N_m3u8DL-RE/Util/ImageHeaderUtil.cs
+++ b/src/N_m3u8DL-RE/Util/ImageHeaderUtil.cs
@@ -5,17 +5,17 @@ internal static class ImageHeaderUtil
     public static bool IsImageHeader(byte[] bArr)
     {
         var size = bArr.Length;
-        //PNG HEADER检测
+        // PNG HEADER检测
         if (size > 3 && 137 == bArr[0] && 80 == bArr[1] && 78 == bArr[2] && 71 == bArr[3])
             return true;
-        //GIF HEADER检测
-        else if (size > 3 && 0x47 == bArr[0] && 0x49 == bArr[1] && 0x46 == bArr[2] && 0x38 == bArr[3])
+        // GIF HEADER检测
+        if (size > 3 && 0x47 == bArr[0] && 0x49 == bArr[1] && 0x46 == bArr[2] && 0x38 == bArr[3])
             return true;
-        //BMP HEADER检测
-        else if (size > 10 && 0x42 == bArr[0] && 0x4D == bArr[1] && 0x00 == bArr[5] && 0x00 == bArr[6] && 0x00 == bArr[7] && 0x00 == bArr[8])
+        // BMP HEADER检测
+        if (size > 10 && 0x42 == bArr[0] && 0x4D == bArr[1] && 0x00 == bArr[5] && 0x00 == bArr[6] && 0x00 == bArr[7] && 0x00 == bArr[8])
             return true;
-        //JPEG HEADER检测
-        else if (size > 3 && 0xFF == bArr[0] && 0xD8 == bArr[1] && 0xFF == bArr[2])
+        // JPEG HEADER检测
+        if (size > 3 && 0xFF == bArr[0] && 0xD8 == bArr[1] && 0xFF == bArr[2])
             return true;
         return false;
     }
@@ -24,7 +24,7 @@ internal static class ImageHeaderUtil
     {
         var sourceData = await File.ReadAllBytesAsync(sourcePath);
 
-        //PNG HEADER
+        // PNG HEADER
         if (137 == sourceData[0] && 80 == sourceData[1] && 78 == sourceData[2] && 71 == sourceData[3])
         {
             if (sourceData.Length > 120 && 137 == sourceData[0] && 80 == sourceData[1] && 78 == sourceData[2] && 71 == sourceData[3] && 96 == sourceData[118] && 130 == sourceData[119])
@@ -37,7 +37,7 @@ internal static class ImageHeaderUtil
                 sourceData = sourceData[771..];
             else
             {
-                //手动查询结尾标记 0x47 出现两次
+                // 手动查询结尾标记 0x47 出现两次
                 int skip = 0;
                 for (int i = 4; i < sourceData.Length - 188 * 2 - 4; i++)
                 {
@@ -50,20 +50,20 @@ internal static class ImageHeaderUtil
                 sourceData = sourceData[skip..];
             }
         }
-        //GIF HEADER
+        // GIF HEADER
         else if (0x47 == sourceData[0] && 0x49 == sourceData[1] && 0x46 == sourceData[2] && 0x38 == sourceData[3])
         {
             sourceData = sourceData[42..];
         }
-        //BMP HEADER
+        // BMP HEADER
         else if (0x42 == sourceData[0] && 0x4D == sourceData[1] && 0x00 == sourceData[5] && 0x00 == sourceData[6] && 0x00 == sourceData[7] && 0x00 == sourceData[8])
         {
             sourceData = sourceData[0x3E..];
         }
-        //JPEG HEADER检测
+        // JPEG HEADER检测
         else if (0xFF == sourceData[0] && 0xD8 == sourceData[1] && 0xFF == sourceData[2])
         {
-            //手动查询结尾标记 0x47 出现两次
+            // 手动查询结尾标记 0x47 出现两次
             int skip = 0;
             for (int i = 4; i < sourceData.Length - 188 * 2 - 4; i++)
             {
diff --git a/src/N_m3u8DL-RE/Util/LanguageCodeUtil.cs b/src/N_m3u8DL-RE/Util/LanguageCodeUtil.cs
index 01c19ac..c79876c 100644
--- a/src/N_m3u8DL-RE/Util/LanguageCodeUtil.cs
+++ b/src/N_m3u8DL-RE/Util/LanguageCodeUtil.cs
@@ -1,33 +1,19 @@
 using N_m3u8DL_RE.Entity;
-using System;
-using System.Collections.Generic;
-using System.Globalization;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
 
 namespace N_m3u8DL_RE.Util;
 
-class Language
+internal class Language(string extendCode, string code, string desc, string descA)
 {
-    public string Code;
-    public string ExtendCode;
-    public string Description;
-    public string DescriptionAudio;
-
-    public Language(string extendCode, string code, string desc, string descA)
-    {
-        Code = code;
-        ExtendCode = extendCode;
-        Description = desc;
-        DescriptionAudio = descA;
-    }
+    public readonly string Code = code;
+    public readonly string ExtendCode = extendCode;
+    public readonly string Description = desc;
+    public readonly string DescriptionAudio = descA;
 }
 
 internal static class LanguageCodeUtil
 {
 
-    private readonly static List<Language> ALL_LANGS = @"
+    private static readonly List<Language> ALL_LANGS = @"
 af;afr;Afrikaans;Afrikaans
 af-ZA;afr;Afrikaans (South Africa);Afrikaans (South Africa)
 am;amh;Amharic;Amharic
@@ -389,8 +375,8 @@ MA;msa;Melayu;Melayu
 "
         .Trim().Replace("\r", "").Split('\n').Where(x => !string.IsNullOrWhiteSpace(x)).Select(x =>
         {
-            var arr = x.Trim().Split(';');
-            return new Language(arr[0].Trim(), arr[1].Trim(), arr[2].Trim(), arr[3].Trim());
+            var arr = x.Trim().Split(';', StringSplitOptions.TrimEntries);
+            return new Language(arr[0], arr[1], arr[2], arr[3]);
         }).ToList();
 
     private static Dictionary<string, string> CODE_MAP = @"
@@ -504,8 +490,7 @@ sr;srp
 
     private static string ConvertTwoToThree(string input)
     {
-        if (CODE_MAP.TryGetValue(input, out var code)) return code;
-        return input;
+        return CODE_MAP.GetValueOrDefault(input, input);
     }
 
     /// <summary>
@@ -518,12 +503,12 @@ sr;srp
         if (string.IsNullOrEmpty(outputFile.LangCode)) return;
         var originalLangCode = outputFile.LangCode;
 
-        //先直接查找
+        // 先直接查找
         var lang = ALL_LANGS.FirstOrDefault(a => a.ExtendCode.Equals(outputFile.LangCode, StringComparison.OrdinalIgnoreCase) || a.Code.Equals(outputFile.LangCode, StringComparison.OrdinalIgnoreCase));
-        //处理特殊的扩展语言标记
+        // 处理特殊的扩展语言标记
         if (lang == null)
         {
-            //2位转3位
+            // 2位转3位
             var l = ConvertTwoToThree(outputFile.LangCode.Split('-').First());
             lang = ALL_LANGS.FirstOrDefault(a => a.ExtendCode.Equals(l, StringComparison.OrdinalIgnoreCase) || a.Code.Equals(l, StringComparison.OrdinalIgnoreCase));
         }
@@ -536,10 +521,10 @@ sr;srp
         }
         else if (outputFile.LangCode == null) 
         {
-            outputFile.LangCode = "und"; //无法识别直接置为und
+            outputFile.LangCode = "und"; // 无法识别直接置为und
         }
 
-        //无描述,则把LangCode当作描述
+        // 无描述,则把LangCode当作描述
         if (string.IsNullOrEmpty(outputFile.Description)) outputFile.Description = originalLangCode;
     }
 }
\ No newline at end of file
diff --git a/src/N_m3u8DL-RE/Util/LargeSingleFileSplitUtil.cs b/src/N_m3u8DL-RE/Util/LargeSingleFileSplitUtil.cs
index 3a6264f..c4870c5 100644
--- a/src/N_m3u8DL-RE/Util/LargeSingleFileSplitUtil.cs
+++ b/src/N_m3u8DL-RE/Util/LargeSingleFileSplitUtil.cs
@@ -1,13 +1,6 @@
 using N_m3u8DL_RE.Common.Entity;
 using N_m3u8DL_RE.Common.Log;
 using N_m3u8DL_RE.Common.Util;
-using NiL.JS.Expressions;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Net.Http;
-using System.Text;
-using System.Threading.Tasks;
 
 namespace N_m3u8DL_RE.Util;
 
@@ -15,17 +8,16 @@ internal static class LargeSingleFileSplitUtil
 {
     class Clip
     {
-        public required int index;
-        public required long from;
-        public required long to;
+        public required int Index;
+        public required long From;
+        public required long To;
     }
 
     /// <summary>
     /// URL大文件切片处理
     /// </summary>
-    /// <param name="url"></param>
+    /// <param name="segment"></param>
     /// <param name="headers"></param>
-    /// <param name="splitSegments"></param>
     /// <returns></returns>
     public static async Task<List<MediaSegment>?> SplitUrlAsync(MediaSegment segment, Dictionary<string,string> headers)
     {
@@ -43,10 +35,10 @@ internal static class LargeSingleFileSplitUtil
         {
             splitSegments.Add(new MediaSegment()
             {
-                Index = clip.index,
+                Index = clip.Index,
                 Url = url,
-                StartRange = clip.from,
-                ExpectLength = clip.to == -1 ? null : clip.to - clip.from + 1,
+                StartRange = clip.From,
+                ExpectLength = clip.To == -1 ? null : clip.To - clip.From + 1,
                 EncryptInfo = segment.EncryptInfo,
             });
         }
@@ -85,7 +77,7 @@ internal static class LargeSingleFileSplitUtil
         return totalSizeBytes;
     }
 
-    //此函数主要是切片下载逻辑
+    // 此函数主要是切片下载逻辑
     private static List<Clip> GetAllClips(string url, long fileSize)
     {
         List<Clip> clips = new();
@@ -96,11 +88,11 @@ internal static class LargeSingleFileSplitUtil
         {
             Clip c = new()
             {
-                index = index,
-                from = counter,
-                to = counter + perSize
+                Index = index,
+                From = counter,
+                To = counter + perSize
             };
-            //没到最后
+            // 没到最后
             if (fileSize - perSize > 0)
             {
                 fileSize -= perSize;
@@ -108,10 +100,10 @@ internal static class LargeSingleFileSplitUtil
                 index++;
                 clips.Add(c);
             }
-            //已到最后
+            // 已到最后
             else
             {
-                c.to = -1;
+                c.To = -1;
                 clips.Add(c);
                 break;
             }
diff --git a/src/N_m3u8DL-RE/Util/MP4DecryptUtil.cs b/src/N_m3u8DL-RE/Util/MP4DecryptUtil.cs
index b152295..9b6bdd6 100644
--- a/src/N_m3u8DL-RE/Util/MP4DecryptUtil.cs
+++ b/src/N_m3u8DL-RE/Util/MP4DecryptUtil.cs
@@ -9,7 +9,7 @@ namespace N_m3u8DL_RE.Util;
 
 internal static class MP4DecryptUtil
 {
-    private static string ZeroKid = "00000000000000000000000000000000";
+    private static readonly string ZeroKid = "00000000000000000000000000000000";
     public static async Task<bool> DecryptAsync(bool shakaPackager, string bin, string[]? keys, string source, string dest, string? kid, string init = "", bool isMultiDRM=false)
     {
         if (keys == null || keys.Length == 0) return false;
@@ -25,7 +25,7 @@ internal static class MP4DecryptUtil
 
         if (!string.IsNullOrEmpty(kid))
         {
-            var test = keyPairs.Where(k => k.StartsWith(kid));
+            var test = keyPairs.Where(k => k.StartsWith(kid)).ToList();
             if (test.Any()) keyPair = test.First();
         }
 
@@ -45,16 +45,16 @@ internal static class MP4DecryptUtil
             
         if (keyPair == null) return false;
 
-        //shakaPackager 无法单独解密init文件
+        // shakaPackager 无法单独解密init文件
         if (source.EndsWith("_init.mp4") && shakaPackager) return false;
 
-        var cmd = "";
+        string cmd;
 
         var tmpFile = "";
         if (shakaPackager)
         {
             var enc = source;
-            //shakaPackager 手动构造文件
+            // shakaPackager 手动构造文件
             if (init != "")
             {
                 tmpFile = Path.ChangeExtension(source, ".itmp");
@@ -101,8 +101,8 @@ internal static class MP4DecryptUtil
         {
             FileName = name,
             Arguments = arg,
-            //RedirectStandardOutput = true,
-            //RedirectStandardError = true,
+            // RedirectStandardOutput = true,
+            // RedirectStandardError = true,
             CreateNoWindow = true,
             UseShellExecute = false
         })!.WaitForExitAsync();
@@ -124,8 +124,7 @@ internal static class MP4DecryptUtil
             Logger.InfoMarkUp(ResString.searchKey);
             using var stream = new FileStream(file, FileMode.Open, FileAccess.Read, FileShare.Read);
             using var reader = new StreamReader(stream);
-            var line = "";
-            while ((line = await reader.ReadLineAsync()) != null)
+            while (await reader.ReadLineAsync() is { } line)
             {
                 if (line.Trim().StartsWith(kid))
                 {
@@ -152,17 +151,15 @@ internal static class MP4DecryptUtil
 
     public static ParsedMP4Info GetMP4Info(string output)
     {
-        using (var fs = File.OpenRead(output))
-        {
-            var header = new byte[1 * 1024 * 1024]; //1MB
-            fs.Read(header);
-            return GetMP4Info(header);
-        }
+        using var fs = File.OpenRead(output);
+        var header = new byte[1 * 1024 * 1024]; // 1MB
+        fs.Read(header);
+        return GetMP4Info(header);
     }
 
     public static string? ReadInitShaka(string output, string bin)
     {
-        Regex ShakaKeyIDRegex = new Regex("Key for key_id=([0-9a-f]+) was not found");
+        Regex shakaKeyIdRegex = new("Key for key_id=([0-9a-f]+) was not found");
 
         // TODO: handle the case that shaka packager actually decrypted (key ID == ZeroKid)
         //       - stop process
@@ -182,6 +179,6 @@ internal static class MP4DecryptUtil
         p.Start();
         var errorOutput = p.StandardError.ReadToEnd();
         p.WaitForExit();
-        return ShakaKeyIDRegex.Match(errorOutput).Groups[1].Value;
+        return shakaKeyIdRegex.Match(errorOutput).Groups[1].Value;
     }
 }
\ No newline at end of file
diff --git a/src/N_m3u8DL-RE/Util/MediainfoUtil.cs b/src/N_m3u8DL-RE/Util/MediainfoUtil.cs
index 1ec6187..1e89664 100644
--- a/src/N_m3u8DL-RE/Util/MediainfoUtil.cs
+++ b/src/N_m3u8DL-RE/Util/MediainfoUtil.cs
@@ -1,12 +1,6 @@
 using N_m3u8DL_RE.Entity;
-using System;
-using System.Collections.Generic;
 using System.Diagnostics;
-using System.Linq;
-using System.Text;
 using System.Text.RegularExpressions;
-using System.Threading.Tasks;
-using System.Xml.Linq;
 
 namespace N_m3u8DL_RE.Util;
 
@@ -48,7 +42,7 @@ internal static partial class MediainfoUtil
             RedirectStandardError = true,
             UseShellExecute = false
         })!;
-        var output = p.StandardError.ReadToEnd();
+        var output = await p.StandardError.ReadToEndAsync();
         await p.WaitForExitAsync();
 
         foreach (Match stream in TextRegex().Matches(output))
@@ -87,7 +81,7 @@ internal static partial class MediainfoUtil
 
         if (result.Count == 0)
         {
-            result.Add(new Mediainfo()
+            result.Add(new Mediainfo
             {
                 Type = "Unknown"
             });
diff --git a/src/N_m3u8DL-RE/Util/MergeUtil.cs b/src/N_m3u8DL-RE/Util/MergeUtil.cs
index 1aaadfa..240acbe 100644
--- a/src/N_m3u8DL-RE/Util/MergeUtil.cs
+++ b/src/N_m3u8DL-RE/Util/MergeUtil.cs
@@ -27,18 +27,14 @@ internal static class MergeUtil
         if (!Directory.Exists(Path.GetDirectoryName(outputFilePath)))
             Directory.CreateDirectory(Path.GetDirectoryName(outputFilePath)!);
 
-        string[] inputFilePaths = files;
-        using (var outputStream = File.Create(outputFilePath))
+        var inputFilePaths = files;
+        using var outputStream = File.Create(outputFilePath);
+        foreach (var inputFilePath in inputFilePaths)
         {
-            foreach (var inputFilePath in inputFilePaths)
-            {
-                if (inputFilePath == "")
-                    continue;
-                using (var inputStream = File.OpenRead(inputFilePath))
-                {
-                    inputStream.CopyTo(outputStream);
-                }
-            }
+            if (inputFilePath == "")
+                continue;
+            using var inputStream = File.OpenRead(inputFilePath);
+            inputStream.CopyTo(outputStream);
         }
     }
 
@@ -79,18 +75,18 @@ internal static class MergeUtil
             div = 200;
 
         string outputName = Path.Combine(Path.GetDirectoryName(files[0])!, "T");
-        int index = 0; //序号
+        int index = 0; // 序号
 
-        //按照div的容量分割为小数组
+        // 按照div的容量分割为小数组
         string[][] li = Enumerable.Range(0, files.Count() / div + 1).Select(x => files.Skip(x * div).Take(div).ToArray()).ToArray();
         foreach (var items in li)
         {
-            if (items.Count() == 0)
+            if (!items.Any())
                 continue;
             var output = outputName + index.ToString("0000") + ".ts";
             CombineMultipleFilesIntoSingleFile(items, output);
             newFiles.Add(output);
-            //合并后删除这些文件
+            // 合并后删除这些文件
             foreach (var item in items)
             {
                 File.Delete(item);
@@ -106,7 +102,7 @@ internal static class MergeUtil
         bool writeDate = true, bool useConcatDemuxer = false, string poster = "", string audioName = "", string title = "",
         string copyright = "", string comment = "", string encodingTool = "", string recTime = "")
     {
-        //改为绝对路径
+        // 改为绝对路径
         outputPath = Path.GetFullPath(outputPath);
 
         string dateString = string.IsNullOrEmpty(recTime) ? DateTime.Now.ToString("o") : recTime;
@@ -185,13 +181,13 @@ internal static class MergeUtil
         string dateString = DateTime.Now.ToString("o");
         StringBuilder command = new StringBuilder("-loglevel warning -nostdin -y -dn ");
 
-        //INPUT
+        // INPUT
         foreach (var item in files)
         {
             command.Append($" -i \"{item.FilePath}\" ");
         }
 
-        //MAP
+        // MAP
         for (int i = 0; i < files.Length; i++)
         {
             command.Append($" -map {i} ");
@@ -200,21 +196,21 @@ internal static class MergeUtil
         var srt = files.Any(x => x.FilePath.EndsWith(".srt"));
 
         if (muxFormat == MuxFormat.MP4)
-            command.Append($" -strict unofficial -c:a copy -c:v copy -c:s mov_text "); //mp4不支持vtt/srt字幕,必须转换格式
+            command.Append($" -strict unofficial -c:a copy -c:v copy -c:s mov_text "); // mp4不支持vtt/srt字幕,必须转换格式
         else if (muxFormat == MuxFormat.TS)
             command.Append($" -strict unofficial -c:a copy -c:v copy ");
         else if (muxFormat == MuxFormat.MKV)
             command.Append($" -strict unofficial -c:a copy -c:v copy -c:s {(srt ? "srt" : "webvtt")} ");
         else throw new ArgumentException($"unknown format: {muxFormat}");
 
-        //CLEAN
+        // CLEAN
         command.Append(" -map_metadata -1 ");
 
-        //LANG and NAME
+        // LANG and NAME
         var streamIndex = 0;
         for (int i = 0; i < files.Length; i++)
         {
-            //转换语言代码
+            // 转换语言代码
             LanguageCodeUtil.ConvertLangCodeAndDisplayName(files[i]);
             command.Append($" -metadata:s:{streamIndex} language=\"{files[i].LangCode ?? "und"}\" ");
             if (!string.IsNullOrEmpty(files[i].Description))
@@ -236,11 +232,11 @@ internal static class MergeUtil
         var audioTracks = files.Where(x => x.MediaType == Common.Enum.MediaType.AUDIO);
         var subTracks = files.Where(x => x.MediaType == Common.Enum.MediaType.AUDIO);
         if (videoTracks.Any()) command.Append(" -disposition:v:0 default ");
-        //字幕都不设置默认
+        // 字幕都不设置默认
         if (subTracks.Any()) command.Append(" -disposition:s 0 ");
         if (audioTracks.Any())
         {
-            //音频除了第一个音轨 都不设置默认
+            // 音频除了第一个音轨 都不设置默认
             command.Append(" -disposition:a:0 default ");
             for (int i = 1; i < audioTracks.Count(); i++)
             {
@@ -265,16 +261,16 @@ internal static class MergeUtil
 
         var dFlag = false;
 
-        //LANG and NAME
+        // LANG and NAME
         for (int i = 0; i < files.Length; i++)
         {
-            //转换语言代码
+            // 转换语言代码
             LanguageCodeUtil.ConvertLangCodeAndDisplayName(files[i]);
             command.Append($" --language 0:\"{files[i].LangCode ?? "und"}\" ");
-            //字幕都不设置默认
+            // 字幕都不设置默认
             if (files[i].MediaType == Common.Enum.MediaType.SUBTITLES)
                 command.Append($" --default-track 0:no ");
-            //音频除了第一个音轨 都不设置默认
+            // 音频除了第一个音轨 都不设置默认
             if (files[i].MediaType == Common.Enum.MediaType.AUDIO)
             {
                 if (dFlag)
diff --git a/src/N_m3u8DL-RE/Util/OtherUtil.cs b/src/N_m3u8DL-RE/Util/OtherUtil.cs
index ece61c8..9ea336c 100644
--- a/src/N_m3u8DL-RE/Util/OtherUtil.cs
+++ b/src/N_m3u8DL-RE/Util/OtherUtil.cs
@@ -1,35 +1,29 @@
-using N_m3u8DL_RE.Common.Entity;
-using N_m3u8DL_RE.Common.Log;
-using N_m3u8DL_RE.Enum;
-using System.CommandLine;
+using N_m3u8DL_RE.Enum;
 using System.IO.Compression;
-using System.Text;
 using System.Text.RegularExpressions;
 
 namespace N_m3u8DL_RE.Util;
 
-internal class OtherUtil
+internal static class OtherUtil
 {
     public static Dictionary<string, string> SplitHeaderArrayToDic(string[]? headers)
     {
         Dictionary<string, string> dic = new();
-
-        if (headers != null)
+        if (headers == null) return dic;
+        
+        foreach (string header in headers)
         {
-            foreach (string header in headers)
+            var index = header.IndexOf(':');
+            if (index != -1)
             {
-                var index = header.IndexOf(':');
-                if (index != -1)
-                {
-                    dic[header[..index].Trim().ToLower()] = header[(index + 1)..].Trim();
-                }
+                dic[header[..index].Trim().ToLower()] = header[(index + 1)..].Trim();
             }
         }
 
         return dic;
     }
 
-    private static char[] InvalidChars = "34,60,62,124,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,58,42,63,92,47"
+    private static readonly char[] InvalidChars = "34,60,62,124,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,58,42,63,92,47"
         .Split(',').Select(s => (char)int.Parse(s)).ToArray();
     public static string GetValidFileName(string input, string re = ".", bool filterSlash = false)
     {
@@ -50,6 +44,7 @@ internal class OtherUtil
     /// 从输入自动获取文件名
     /// </summary>
     /// <param name="input"></param>
+    /// <param name="addSuffix"></param>
     /// <returns></returns>
     public static string GetFileNameFromInput(string input, bool addSuffix = true)
     {
@@ -119,7 +114,7 @@ internal class OtherUtil
         return hours * 3600 + minutes * 60 + seconds;
     }
 
-    //若该文件夹为空,删除,同时判断其父文件夹,直到遇到根目录或不为空的目录
+    // 若该文件夹为空,删除,同时判断其父文件夹,直到遇到根目录或不为空的目录
     public static void SafeDeleteDir(string dirPath)
     {
         if (string.IsNullOrEmpty(dirPath) || !Directory.Exists(dirPath))
diff --git a/src/N_m3u8DL-RE/Util/PipeUtil.cs b/src/N_m3u8DL-RE/Util/PipeUtil.cs
index 0d99e5d..e3452a4 100644
--- a/src/N_m3u8DL-RE/Util/PipeUtil.cs
+++ b/src/N_m3u8DL-RE/Util/PipeUtil.cs
@@ -1,14 +1,8 @@
 using N_m3u8DL_RE.Common.Log;
-using N_m3u8DL_RE.Common.Resource;
 using Spectre.Console;
-using System;
-using System.Collections.Generic;
-using System.CommandLine;
 using System.Diagnostics;
 using System.IO.Pipes;
-using System.Linq;
 using System.Text;
-using System.Threading.Tasks;
 
 namespace N_m3u8DL_RE.Util;
 
@@ -67,7 +61,7 @@ internal static class PipeUtil
             if (OperatingSystem.IsWindows())
                 command.Append($" -i \"\\\\.\\pipe\\{item}\" ");
             else
-                //command.Append($" -i \"unix://{Path.Combine(Path.GetTempPath(), $"CoreFxPipe_{item}")}\" ");
+                // command.Append($" -i \"unix://{Path.Combine(Path.GetTempPath(), $"CoreFxPipe_{item}")}\" ");
                 command.Append($" -i \"{Path.Combine(pipeDir, item)}\" ");
         }
 
@@ -103,7 +97,7 @@ internal static class PipeUtil
             CreateNoWindow = true,
             UseShellExecute = false
         };
-        //p.StartInfo.Environment.Add("FFREPORT", "file=ffreport.log:level=42");
+        // p.StartInfo.Environment.Add("FFREPORT", "file=ffreport.log:level=42");
         p.Start();
         p.WaitForExit();
 
diff --git a/src/N_m3u8DL-RE/Util/SubtitleUtil.cs b/src/N_m3u8DL-RE/Util/SubtitleUtil.cs
index 44028e9..52e253a 100644
--- a/src/N_m3u8DL-RE/Util/SubtitleUtil.cs
+++ b/src/N_m3u8DL-RE/Util/SubtitleUtil.cs
@@ -1,11 +1,6 @@
 using N_m3u8DL_RE.Common.Entity;
 using N_m3u8DL_RE.Common.Log;
 using N_m3u8DL_RE.Common.Resource;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
 
 namespace N_m3u8DL_RE.Util;
 
@@ -17,23 +12,21 @@ internal static class SubtitleUtil
     /// <param name="finalVtt"></param>
     /// <param name="tmpDir">临时目录</param>
     /// <returns></returns>
-    public static async Task<bool> TryWriteImagePngsAsync(WebVttSub? finalVtt, string tmpDir)
+    public static async Task TryWriteImagePngsAsync(WebVttSub? finalVtt, string tmpDir)
     {
         if (finalVtt != null && finalVtt.Cues.Any(v => v.Payload.StartsWith("Base64::")))
         {
             Logger.WarnMarkUp(ResString.processImageSub);
-            var _i = 0;
+            var i = 0;
             foreach (var img in finalVtt.Cues.Where(v => v.Payload.StartsWith("Base64::")))
             {
-                var name = $"{_i++}.png";
+                var name = $"{i++}.png";
                 var dest = "";
-                for (; File.Exists(dest = Path.Combine(tmpDir, name)); name = $"{_i++}.png") ;
+                for (; File.Exists(dest = Path.Combine(tmpDir, name)); name = $"{i++}.png") ;
                 var base64 = img.Payload[8..];
                 await File.WriteAllBytesAsync(dest, Convert.FromBase64String(base64));
                 img.Payload = name;
             }
-            return true;
         }
-        else return false;
     }
 }
\ No newline at end of file