From 81d98085e7696aa8fbe4bd2492bf85b64944e22f Mon Sep 17 00:00:00 2001
From: My-Responsitories <email.youxiang.alpha@gmail.com>
Date: Fri, 16 Dec 2022 13:02:04 +0800
Subject: [PATCH 1/2] make biliplus support ss/md & replace upos host

---
 BBDown.Core/Parser.cs        |   2 +-
 BBDown/BBDownUtil.cs         | 136 +++++++++++++++++------------------
 BBDown/CommandLineInvoker.cs |   3 +
 BBDown/MyOption.cs           |   1 +
 BBDown/Program.cs            |  90 +++++++++++++----------
 5 files changed, 126 insertions(+), 106 deletions(-)

diff --git a/BBDown.Core/Parser.cs b/BBDown.Core/Parser.cs
index 5495f00bc..bb57bb3d3 100644
--- a/BBDown.Core/Parser.cs
+++ b/BBDown.Core/Parser.cs
@@ -384,7 +384,7 @@ private static string GetTimeStamp(bool bflag)
         private static string GetSign(string parms, bool isBiliPlus)
         {
             string toEncode = parms + (isBiliPlus ? "acd495b248ec528c2eed1e862d393126" : "59b43e04ad6965f34319062b478f83dd");
-            return string.Join("", MD5.HashData(Encoding.UTF8.GetBytes(toEncode)).Select(i => i.ToString("x2")).ToArray());
+            return string.Concat(MD5.HashData(Encoding.UTF8.GetBytes(toEncode)).Select(i => i.ToString("x2")).ToArray());
         }
 
         [GeneratedRegex("window.__playinfo__=([\\s\\S]*?)<\\/script>")]
diff --git a/BBDown/BBDownUtil.cs b/BBDown/BBDownUtil.cs
index 743bc00bf..74b7e4b35 100644
--- a/BBDown/BBDownUtil.cs
+++ b/BBDown/BBDownUtil.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
 using System.Collections.Concurrent;
 using System.Collections.Generic;
 using System.Collections.Specialized;
@@ -53,14 +53,10 @@ public static async Task<string> GetAvIdAsync(string input)
                 {
                     avid = AvRegex().Match(input).Groups[1].Value;
                 }
-                else if (input.Contains("video/BV"))
+                else if (input.ToLower().Contains("video/bv"))
                 {
                     avid = await GetAidByBVAsync(BVRegex().Match(input).Groups[1].Value);
                 }
-                else if (input.Contains("video/bv"))
-                {
-                    avid = await GetAidByBVAsync(BvRegex().Match(input).Groups[1].Value);
-                }
                 else if (input.Contains("/cheese/"))
                 {
                     string epId = "";
@@ -79,6 +75,11 @@ public static async Task<string> GetAvIdAsync(string input)
                     string epId = EpRegex().Match(input).Groups[1].Value;
                     avid = $"ep:{epId}";
                 }
+                else if (input.Contains("/ss"))
+                {
+                    string epId = await GetEpIdByBangumiSSIdAsync(SsRegex().Match(input).Groups[1].Value);
+                    avid = $"ep:{epId}";
+                }
                 else if (input.Contains("/medialist/") && input.Contains("business_id=") && input.Contains("business=space_collection")) //列表类型是合集
                 {
                     string bizId = GetQueryString("business_id", input);
@@ -135,11 +136,7 @@ public static async Task<string> GetAvIdAsync(string input)
                     avid = $"ep:{epId}";
                 }
             }
-            else if (input.StartsWith("BV"))
-            {
-                avid = await GetAidByBVAsync(input[2..]);
-            }
-            else if (input.StartsWith("bv"))
+            else if (input.ToLower().StartsWith("bv"))
             {
                 avid = await GetAidByBVAsync(input[2..]);
             }
@@ -154,31 +151,14 @@ public static async Task<string> GetAvIdAsync(string input)
             }
             else if (input.StartsWith("ss"))
             {
-                string web = await GetWebSourceAsync("https://www.bilibili.com/bangumi/play/" + input);
-                Regex regex = StateRegex();
-                string json = regex.Match(web).Groups[1].Value;
-                try
-                {
-                    using var jDoc = JsonDocument.Parse(json);
-                    string epId = jDoc.RootElement.GetProperty("epList").EnumerateArray().First().GetProperty("id").ToString();
-                    avid = $"ep:{epId}";
-                }
-                catch (JsonException)
-                {
-                    throw new Exception("输入有误");
-                }
+                string epId = await GetEpIdByBangumiSSIdAsync(input[2..]);
+                avid = $"ep:{epId}";
             }
             else if (input.StartsWith("md"))
             {
                 string mdId = MdRegex().Match(input).Groups[1].Value;
-                try
-                {
-                    avid = await GetAvIdAsync(await GetSSIdByMDAsync(mdId));
-                }
-                catch (JsonException)
-                {
-                    throw new Exception("输入有误");
-                }
+                string epId = await GetEpIdByMDAsync(mdId);
+                avid = $"ep:{epId}";
             }
             else
             {
@@ -209,7 +189,7 @@ public static string FormatTime(int time, bool absolute = false)
         }
 
         /// <summary>
-        /// 通过avid检测是否为版权内容,如果是的话返回ep:xx格式
+        /// 通过avid检测是否为版权内容, 如果是的话返回ep:xx格式
         /// </summary>
         /// <param name="avid"></param>
         /// <returns></returns>
@@ -238,11 +218,31 @@ public static async Task<string> FixAvidAsync(string avid)
 
         public static async Task<string> GetAidByBVAsync(string bv)
         {
-            string api = $"https://api.bilibili.com/x/web-interface/archive/stat?bvid={bv}";
-            string json = await GetWebSourceAsync(api);
-            using var jDoc = JsonDocument.Parse(json);
-            string aid = jDoc.RootElement.GetProperty("data").GetProperty("aid").ToString();
-            return aid;
+            if (bv.Length == 10)
+            {
+                // 能在本地就在本地
+                string TABLE = "fZodR9XQDSUm21yCkr6zBqiveYah8bt4xsWpHnJE7jL5VG3guMTKNPAwcF";
+                var BVChange = new Dictionary<char, byte>();
+                byte[] S = { 9, 8, 1, 6, 2, 4 };
+                long XOR = 177451812;
+                long ADD = 8728348608;
+                for (byte i = 0; i < 58; i++) BVChange.Add(TABLE[i], i);
+
+                long T = 0;
+                for (byte i = 0; i < 6; i++)
+                {
+                    T += (long)Math.Pow(58, i) * BVChange[bv[S[i]]];
+                }
+                return ((T - ADD) ^ XOR).ToString();
+            }
+            else
+            {
+                string api = $"https://api.bilibili.com/x/web-interface/archive/stat?bvid={bv}";
+                string json = await GetWebSourceAsync(api);
+                using var jDoc = JsonDocument.Parse(json);
+                string aid = jDoc.RootElement.GetProperty("data").GetProperty("aid").ToString();
+                return aid;
+            }
         }
 
         public static async Task<string> GetEpidBySSIdAsync(string ssid)
@@ -254,13 +254,22 @@ public static async Task<string> GetEpidBySSIdAsync(string ssid)
             return epId;
         }
 
-        public static async Task<string> GetSSIdByMDAsync(string mdId)
+        public static async Task<string> GetEpIdByBangumiSSIdAsync(string ssId)
+		{
+            string api = $"https://{Core.Config.EPHOST}/pgc/view/web/season?season_id={ssId}";
+            string json = await GetWebSourceAsync(api);
+            using var jDoc = JsonDocument.Parse(json);
+            string epId = jDoc.RootElement.GetProperty("result").GetProperty("episodes").EnumerateArray().First().GetProperty("id").ToString();
+            return epId;
+        }
+
+        public static async Task<string> GetEpIdByMDAsync(string mdId)
 		{
-            var api = $"https://api.bilibili.com/pgc/review/user?media_id={mdId}";
-            var json = await GetWebSourceAsync(api);
+            string api = $"https://api.bilibili.com/pgc/review/user?media_id={mdId}";
+            string json = await GetWebSourceAsync(api);
             using var jDoc = JsonDocument.Parse(json);
-            var ssId = "ss" + jDoc.RootElement.GetProperty("result").GetProperty("media").GetProperty("season_id").ToString();
-            return ssId;
+            string epId = jDoc.RootElement.GetProperty("result").GetProperty("media").GetProperty("new_ep").GetProperty("id").ToString();
+            return epId;
         }
 
         private static async Task RangeDownloadToTmpAsync(int id, string url, string tmpName, long fromPosition, long? toPosition, Action<int, long, long> onProgress, bool failOnRangeNotSupported = false)
@@ -402,7 +411,7 @@ await RangeDownloadToTmpAsync(clip.index, url, tmp, clip.from, clip.to == -1 ? n
                 }
                 catch (NotSupportedException)
                 {
-                    if (++retry == 3) throw new Exception($"服务器可能并不支持多线程下载,请使用 --multi-thread false 关闭多线程");
+                    if (++retry == 3) throw new Exception($"服务器可能并不支持多线程下载, 请使用 --multi-thread false 关闭多线程");
                     goto reDown;
                 }
                 catch (Exception)
@@ -448,7 +457,7 @@ private static List<Clip> GetAllClips(string url, long fileSize)
         }
 
         /// <summary>
-        /// 输入一堆已存在的文件,合并到新文件
+        /// 输入一堆已存在的文件, 合并到新文件
         /// </summary>
         /// <param name="files"></param>
         /// <param name="outputFilePath"></param>
@@ -564,7 +573,7 @@ public static string GetValidFileName(string input, string re = ".", bool filter
 
 
         /// <summary>
-        /// 获取url字符串参数,返回参数值字符串
+        /// 获取url字符串参数, 返回参数值字符串
         /// </summary>
         /// <param name="name">参数名称</param>
         /// <param name="url">url字符串</param>
@@ -605,22 +614,13 @@ public static string GetSession(string buvid3)
         public static string GetSign(string parms)
         {
             string toEncode = parms + "59b43e04ad6965f34319062b478f83dd";
-            byte[] bs = Encoding.UTF8.GetBytes(toEncode);
-            byte[] hs = MD5.HashData(bs);
-            StringBuilder sb = new();
-            foreach (byte b in hs)
-            {
-                sb.Append(b.ToString("x2"));
-            }
-            return sb.ToString();
+            return string.Concat(MD5.HashData(Encoding.UTF8.GetBytes(toEncode)).Select(i => i.ToString("x2")).ToArray());
         }
 
         public static string GetTimeStamp(bool bflag)
         {
-            TimeSpan ts = DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, 0);
-            string ret = bflag ? Convert.ToInt64(ts.TotalSeconds).ToString() : Convert.ToInt64(ts.TotalMilliseconds).ToString();
-
-            return ret;
+            DateTimeOffset ts = DateTimeOffset.Now;
+            return bflag ? ts.ToUnixTimeSeconds().ToString() : ts.ToUnixTimeMilliseconds().ToString();
         }
 
         //https://stackoverflow.com/questions/1344221/how-can-i-generate-random-alphanumeric-strings
@@ -750,7 +750,7 @@ public static async Task<List<ViewPoint>> FetchPointsAsync(string cid, string ai
         }
 
         /// <summary>
-        /// 生成metadata文件,用于ffmpeg混流章节信息
+        /// 生成metadata文件, 用于ffmpeg混流章节信息
         /// </summary>
         /// <param name="points"></param>
         /// <returns></returns>
@@ -772,7 +772,7 @@ public static string GetFFmpegMetaString(List<ViewPoint> points)
         }
 
         /// <summary>
-        /// 生成metadata文件,用于mp4box混流章节信息
+        /// 生成metadata文件, 用于mp4box混流章节信息
         /// </summary>
         /// <param name="points"></param>
         /// <returns></returns>
@@ -810,17 +810,15 @@ public static async Task<bool> CheckLogin(string cookie)
             }
         }
 
-        [GeneratedRegex("av(\\d{1,})")]
+        [GeneratedRegex("av(\\d+)")]
         private static partial Regex AvRegex();
-        [GeneratedRegex("BV(\\w+)")]
+        [GeneratedRegex("[Bb][Vv](\\w+)")]
         private static partial Regex BVRegex();
-        [GeneratedRegex("bv(\\w+)")]
-        private static partial Regex BvRegex();
-        [GeneratedRegex("/ep(\\d{1,})")]
+        [GeneratedRegex("/ep(\\d+)")]
         private static partial Regex EpRegex();
-        [GeneratedRegex("/ss(\\d{1,})")]
+        [GeneratedRegex("/ss(\\d+)")]
         private static partial Regex SsRegex();
-        [GeneratedRegex("space\\.bilibili\\.com/(\\d{1,})")]
+        [GeneratedRegex("space\\.bilibili\\.com/(\\d+)")]
         private static partial Regex UidRegex();
         [GeneratedRegex("global\\.bilibili\\.com/play/\\d+/(\\d+)")]
         private static partial Regex GlobalEpRegex();
@@ -828,9 +826,9 @@ public static async Task<bool> CheckLogin(string cookie)
         private static partial Regex BangumiMdRegex();
         [GeneratedRegex("window.__INITIAL_STATE__=([\\s\\S].*?);\\(function\\(\\)")]
         private static partial Regex StateRegex();
-        [GeneratedRegex("ep(\\d{1,})")]
+        [GeneratedRegex("ep(\\d+)")]
         private static partial Regex EpRegex2();
-        [GeneratedRegex("md(\\d{1,})")]
+        [GeneratedRegex("md(\\d+)")]
         private static partial Regex MdRegex();
         [GeneratedRegex("^\\d+$")]
         private static partial Regex NumRegex();
diff --git a/BBDown/CommandLineInvoker.cs b/BBDown/CommandLineInvoker.cs
index a800d3e62..47f625104 100644
--- a/BBDown/CommandLineInvoker.cs
+++ b/BBDown/CommandLineInvoker.cs
@@ -44,6 +44,7 @@ internal class CommandLineInvoker
         private readonly static Option<string> FFmpegPath = new(new string[] { "--ffmpeg-path" }, "设置ffmpeg的路径");
         private readonly static Option<string> Mp4boxPath = new(new string[] { "--mp4box-path" }, "设置mp4box的路径");
         private readonly static Option<string> Aria2cPath = new(new string[] { "--aria2c-path" }, "设置aria2c的路径");
+        private readonly static Option<string> UposHost = new(new string[] { "--upos-host" }, "自定义upos服务器");
         private readonly static Option<string> DelayPerPage = new(new string[] { "--delay-per-page" }, "设置下载合集分P之间的下载间隔时间(单位: 秒, 默认无间隔)");
         private readonly static Option<string> FilePattern = new(new string[] { "--file-pattern", "-F" }, $"使用内置变量自定义单P存储文件名:\r\n\r\n" + $"<videoTitle>: 视频主标题\r\n" + $"<pageNumber>: 视频分P序号\r\n" + $"<pageNumberWithZero>: 视频分P序号(前缀补零)\r\n" + $"<pageTitle>: 视频分P标题\r\n" + $"<aid>: 视频aid\r\n" + $"<cid>: 视频cid\r\n" + $"<dfn>: 视频清晰度\r\n" + $"<res>: 视频分辨率\r\n" + $"<fps>: 视频帧率\r\n" + $"<videoCodecs>: 视频编码\r\n" + $"<videoBandwidth>: 视频码率\r\n" + $"<audioCodecs>: 音频编码\r\n" + $"<audioBandwidth>: 音频码率\r\n" + $"<ownerName>: 上传者名称\r\n" + $"<ownerMid>: 上传者mid\r\n\r\n" + $"默认为: {Program.SinglePageDefaultSavePath}\r\n");
         private readonly static Option<string> MultiFilePattern = new(new string[] { "--multi-file-pattern", "-M" }, $"使用内置变量自定义多P存储文件名:\r\n\r\n" + $"默认为: {Program.MultiPageDefaultSavePath}\r\n");
@@ -102,6 +103,7 @@ protected override MyOption GetBoundValue(BindingContext bindingContext)
                 if (bindingContext.ParseResult.HasOption(FFmpegPath)) option.FFmpegPath = bindingContext.ParseResult.GetValueForOption(FFmpegPath)!;
                 if (bindingContext.ParseResult.HasOption(Mp4boxPath)) option.Mp4boxPath = bindingContext.ParseResult.GetValueForOption(Mp4boxPath)!;
                 if (bindingContext.ParseResult.HasOption(Aria2cPath)) option.Aria2cPath = bindingContext.ParseResult.GetValueForOption(Aria2cPath)!;
+                if (bindingContext.ParseResult.HasOption(UposHost)) option.UposHost = bindingContext.ParseResult.GetValueForOption(UposHost)!;
                 if (bindingContext.ParseResult.HasOption(DelayPerPage)) option.DelayPerPage = bindingContext.ParseResult.GetValueForOption(DelayPerPage)!;
                 if (bindingContext.ParseResult.HasOption(Host)) option.Host = bindingContext.ParseResult.GetValueForOption(Host)!;
                 if (bindingContext.ParseResult.HasOption(EpHost)) option.EpHost = bindingContext.ParseResult.GetValueForOption(EpHost)!;
@@ -156,6 +158,7 @@ public static RootCommand GetRootCommand(Func<MyOption, Task> action)
                 FFmpegPath,
                 Mp4boxPath,
                 Aria2cPath,
+                UposHost,
                 DelayPerPage,
                 Host,
                 EpHost,
diff --git a/BBDown/MyOption.cs b/BBDown/MyOption.cs
index 773e84662..e8629bb2b 100644
--- a/BBDown/MyOption.cs
+++ b/BBDown/MyOption.cs
@@ -43,6 +43,7 @@ internal class MyOption
         public string FFmpegPath { get; set; } = "";
         public string Mp4boxPath { get; set; } = "";
         public string Aria2cPath { get; set; } = "";
+        public string UposHost { get; set; } = "";
         public string DelayPerPage { get; set; } = "0";
         public string Host { get; set; } = "api.bilibili.com";
         public string EpHost { get; set; } = "api.bilibili.com";
diff --git a/BBDown/Program.cs b/BBDown/Program.cs
index 73c6d58f7..dcf2c7e17 100644
--- a/BBDown/Program.cs
+++ b/BBDown/Program.cs
@@ -110,7 +110,7 @@ private static async Task DoWorkAsync(MyOption myOption)
                 //兼容旧版本命令行参数并给出警告
                 if (myOption.AddDfnSubfix)
                 {
-                    LogWarn("--add-dfn-subfix 已被弃用,建议使用 --file-pattern/-F 或 --multi-file-pattern/-M 来自定义输出文件名格式");
+                    LogWarn("--add-dfn-subfix 已被弃用, 建议使用 --file-pattern/-F 或 --multi-file-pattern/-M 来自定义输出文件名格式");
                     if (string.IsNullOrEmpty(myOption.FilePattern) && string.IsNullOrEmpty(myOption.MultiFilePattern))
                     {
                         SinglePageDefaultSavePath += "[<dfn>]";
@@ -120,27 +120,27 @@ private static async Task DoWorkAsync(MyOption myOption)
                 }
                 if (myOption.Aria2cProxy != "")
                 {
-                    LogWarn("--aria2c-proxy 已被弃用,请使用 --aria2c-args 来设置aria2c代理,本次执行已添加该代理");
+                    LogWarn("--aria2c-proxy 已被弃用, 请使用 --aria2c-args 来设置aria2c代理, 本次执行已添加该代理");
                     myOption.Aria2cArgs += $" --all-proxy=\"{myOption.Aria2cProxy}\"";
                 }
                 if (myOption.OnlyHevc)
                 {
-                    LogWarn("--only-hevc/-hevc 已被弃用,请使用 --encoding-priority 来设置编码优先级,本次执行已将hevc设置为最高优先级");
+                    LogWarn("--only-hevc/-hevc 已被弃用, 请使用 --encoding-priority 来设置编码优先级, 本次执行已将hevc设置为最高优先级");
                     myOption.EncodingPriority = "hevc";
                 }
                 if (myOption.OnlyAvc)
                 {
-                    LogWarn("--only-avc/-avc 已被弃用,请使用 --encoding-priority 来设置编码优先级,本次执行已将avc设置为最高优先级");
+                    LogWarn("--only-avc/-avc 已被弃用, 请使用 --encoding-priority 来设置编码优先级, 本次执行已将avc设置为最高优先级");
                     myOption.EncodingPriority = "avc";
                 }
                 if (myOption.OnlyAv1)
                 {
-                    LogWarn("--only-av1/-av1 已被弃用,请使用 --encoding-priority 来设置编码优先级,本次执行已将av1设置为最高优先级");
+                    LogWarn("--only-av1/-av1 已被弃用, 请使用 --encoding-priority 来设置编码优先级, 本次执行已将av1设置为最高优先级");
                     myOption.EncodingPriority = "av1";
                 }
                 if (myOption.NoPaddingPageNum)
                 {
-                    LogWarn("--no-padding-page-num 已被弃用,建议使用 --file-pattern/-F 或 --multi-file-pattern/-M 来自定义输出文件名格式");
+                    LogWarn("--no-padding-page-num 已被弃用, 建议使用 --file-pattern/-F 或 --multi-file-pattern/-M 来自定义输出文件名格式");
                     if (string.IsNullOrEmpty(myOption.FilePattern) && string.IsNullOrEmpty(myOption.MultiFilePattern))
                     {
                         MultiPageDefaultSavePath = MultiPageDefaultSavePath.Replace("<pageNumberWithZero>", "<pageNumber>");
@@ -200,6 +200,7 @@ private static async Task DoWorkAsync(MyOption myOption)
                 string savePathFormat = myOption.FilePattern;
                 string lang = myOption.Language;
                 string selectPage = myOption.SelectPage.ToUpper();
+                string uposHost = myOption.UposHost;
                 string aidOri = ""; //原始aid
                 int delay = Convert.ToInt32(myOption.DelayPerPage);
                 Config.HOST = myOption.Host;
@@ -325,7 +326,7 @@ private static async Task DoWorkAsync(MyOption myOption)
                 Log("获取aid...");
                 aidOri = await GetAvIdAsync(input);
                 Log("获取aid结束: " + aidOri);
-                //-p的优先级大于URL中的自带p参数,所以先清空selectedPages
+                //-p的优先级大于URL中的自带p参数, 所以先清空selectedPages
                 if (!string.IsNullOrEmpty(selectPage) && selectPage != "ALL")
                 {
                     selectedPages = new List<string>();
@@ -420,11 +421,11 @@ private static async Task DoWorkAsync(MyOption myOption)
                     catch { LogError("解析分P参数时失败了~"); selectedPages = null; };
                 }
 
-                //如果用户没有选择分P,根据epid来确定某一集
+                //如果用户没有选择分P, 根据epid来确定某一集
                 if (selectedPages == null && selectPage != "ALL" && !string.IsNullOrEmpty(vInfo.Index))
                 {
                     selectedPages = new List<string> { vInfo.Index };
-                    Log("程序已自动选择你输入的集数,如果要下载其他集数请自行指定分P(如可使用-p ALL代表全部)");
+                    Log("程序已自动选择你输入的集数, 如果要下载其他集数请自行指定分P(如可使用-p ALL代表全部)");
                 }
 
                 Log($"共计 {pagesInfo.Count} 个分P, 已选择:" + (selectedPages == null ? "ALL" : string.Join(",", selectedPages)));
@@ -436,7 +437,7 @@ private static async Task DoWorkAsync(MyOption myOption)
 
                 // 根据p数选择存储路径
                 savePathFormat = string.IsNullOrEmpty(myOption.FilePattern) ? SinglePageDefaultSavePath : myOption.FilePattern;
-                // 1. 多P; 2. 只有1P,但是是番剧,尚未完结时 按照多P处理
+                // 1. 多P; 2. 只有1P, 但是是番剧, 尚未完结时 按照多P处理
                 if (pagesCount > 1 || (bangumi && !vInfo.IsBangumiEnd))
                 {
                     savePathFormat = string.IsNullOrEmpty(myOption.MultiFilePattern) ? MultiPageDefaultSavePath : myOption.MultiFilePattern;
@@ -538,7 +539,7 @@ private static async Task DoWorkAsync(MyOption myOption)
 
                         var savePath = "";
 
-                        //此处代码简直灾难,后续优化吧
+                        //此处代码简直灾难, 后续优化吧
                         if ((videoTracks.Count != 0 || audioTracks.Count != 0) && clips.Count == 0)   //dash
                         {
                             if (webJsonStr.Contains("\"video\":[") && videoTracks.Count == 0)
@@ -613,31 +614,46 @@ private static async Task DoWorkAsync(MyOption myOption)
                             if (audioTracks.Count > 0)
                                 LogColor($"[音频] [{audioTracks[aIndex].codecs}] [{audioTracks[aIndex].bandwith} kbps] [~{FormatFileSize(audioTracks[aIndex].dur * audioTracks[aIndex].bandwith * 1024 / 8)}]", false);
 
-                            //处理PCDN
-                            var pcdnReg = PcdnRegex();
-                            if (videoTracks.Count > 0 && pcdnReg.IsMatch(videoTracks[vIndex].baseUrl))
+                            if (uposHost == "")
                             {
-                                LogWarn($"检测到视频流为PCDN,尝试强制替换为{BACKUP_HOST}……");
-                                videoTracks[vIndex].baseUrl = pcdnReg.Replace(videoTracks[vIndex].baseUrl, $"://{BACKUP_HOST}/");
-                            }
-
-                            if (audioTracks.Count > 0 && pcdnReg.IsMatch(audioTracks[aIndex].baseUrl))
-                            {
-                                LogWarn($"检测到音频流为PCDN,尝试强制替换为{BACKUP_HOST}……");
-                                audioTracks[aIndex].baseUrl = pcdnReg.Replace(audioTracks[aIndex].baseUrl, $"://{BACKUP_HOST}/");
-                            }
+                                //处理PCDN
+                                var pcdnReg = PcdnRegex();
+                                if (videoTracks.Count > 0 && pcdnReg.IsMatch(videoTracks[vIndex].baseUrl))
+                                {
+                                    LogWarn($"检测到视频流为PCDN, 尝试强制替换为{BACKUP_HOST}……");
+                                    videoTracks[vIndex].baseUrl = pcdnReg.Replace(videoTracks[vIndex].baseUrl, $"://{BACKUP_HOST}/");
+                                }
+                                if (audioTracks.Count > 0 && pcdnReg.IsMatch(audioTracks[aIndex].baseUrl))
+                                {
+                                    LogWarn($"检测到音频流为PCDN, 尝试强制替换为{BACKUP_HOST}……");
+                                    audioTracks[aIndex].baseUrl = pcdnReg.Replace(audioTracks[aIndex].baseUrl, $"://{BACKUP_HOST}/");
+                                }
 
-                            var akamReg = AkamRegex();
-                            if (videoTracks.Count > 0 && Config.AREA != "" && videoTracks[vIndex].baseUrl.Contains("akamaized.net"))
-                            {
-                                LogWarn($"检测到视频流为外国源,尝试强制替换为{BACKUP_HOST}……");
-                                videoTracks[vIndex].baseUrl = akamReg.Replace(videoTracks[vIndex].baseUrl, $"://{BACKUP_HOST}/");
+                                var akamReg = AkamRegex();
+                                if (videoTracks.Count > 0 && Config.AREA != "" && videoTracks[vIndex].baseUrl.Contains("akamaized.net"))
+                                {
+                                    LogWarn($"检测到视频流为外国源, 尝试强制替换为{BACKUP_HOST}……");
+                                    videoTracks[vIndex].baseUrl = akamReg.Replace(videoTracks[vIndex].baseUrl, $"://{BACKUP_HOST}/");
+                                }
+                                if (audioTracks.Count > 0 && Config.AREA != "" && audioTracks[aIndex].baseUrl.Contains("akamaized.net"))
+                                {
+                                    LogWarn($"检测到音频流为外国源, 尝试强制替换为{BACKUP_HOST}……");
+                                    audioTracks[aIndex].baseUrl = akamReg.Replace(audioTracks[aIndex].baseUrl, $"://{BACKUP_HOST}/");
+                                }
                             }
-
-                            if (audioTracks.Count > 0 && Config.AREA != "" && audioTracks[aIndex].baseUrl.Contains("akamaized.net"))
+                            else
                             {
-                                LogWarn($"检测到音频流为外国源,尝试强制替换为{BACKUP_HOST}……");
-                                audioTracks[aIndex].baseUrl = akamReg.Replace(audioTracks[aIndex].baseUrl, $"://{BACKUP_HOST}/");
+                                var uposReg = UposRegex();
+                                if (videoTracks.Count > 0)
+                                {
+                                    Log($"尝试将视频流强制替换为{uposHost}……");
+                                    videoTracks[vIndex].baseUrl = uposReg.Replace(videoTracks[vIndex].baseUrl, $"://{uposHost}/");
+                                }
+                                if (audioTracks.Count > 0)
+                                {
+                                    Log($"尝试将音频流强制替换为{uposHost}……");
+                                    audioTracks[aIndex].baseUrl = uposReg.Replace(audioTracks[aIndex].baseUrl, $"://{uposHost}/");
+                                }
                             }
 
                             LogDebug("Format Before: " + savePathFormat);
@@ -678,7 +694,7 @@ private static async Task DoWorkAsync(MyOption myOption)
                                     continue;
                                 }
 
-                                //杜比视界,若ffmpeg版本小于5.0,使用mp4box封装
+                                //杜比视界, 若ffmpeg版本小于5.0, 使用mp4box封装
                                 if (videoTracks[vIndex].dfn == Config.qualitys["126"] && !useMp4box && !CheckFFmpegDOVI())
                                 {
                                     LogWarn($"检测到杜比视界清晰度且您的ffmpeg版本小于5.0,将使用mp4box混流...");
@@ -873,11 +889,11 @@ private static async Task DoWorkAsync(MyOption myOption)
                         {
                             if (webJsonStr.Contains("平台不可观看"))
                             {
-                                throw new Exception("当前(WEB)平台不可观看,请尝试使用TV API解析。");
+                                throw new Exception("当前(WEB)平台不可观看, 请尝试使用TV API解析。");
                             }
                             else if (webJsonStr.Contains("地区不可观看") || webJsonStr.Contains("地区不支持"))
                             {
-                                throw new Exception("当前地区不可观看,尝试设置系统代理后解析。");
+                                throw new Exception("当前地区不可观看, 尝试设置系统代理后解析。");
                             }
                             else if (webJsonStr.Contains("购买后才能观看"))
                             {
@@ -916,7 +932,7 @@ private static async Task DoWorkAsync(MyOption myOption)
 
         private static List<Video> SortTracks(List<Video> videoTracks, Dictionary<string, int> dfnPriority, Dictionary<string, byte> encodingPriority, bool bandwithAscending)
         {
-            //用户同时输入了自定义分辨率优先级和自定义编码优先级,则根据输入顺序依次进行排序
+            //用户同时输入了自定义分辨率优先级和自定义编码优先级, 则根据输入顺序依次进行排序
             return dfnPriority.Count > 0 && encodingPriority.Count > 0 && Environment.CommandLine.IndexOf("--encoding-priority") < Environment.CommandLine.IndexOf("--dfn-priority")
                 ? videoTracks
                     .OrderBy(v => encodingPriority.TryGetValue(v.codecs, out byte i) ? i : 100)
@@ -1077,6 +1093,8 @@ private static async Task LoginTV()
         private static partial Regex PcdnRegex();
         [GeneratedRegex("://.*akamaized\\.net/")]
         private static partial Regex AkamRegex();
+        [GeneratedRegex("://[^/]+/")]
+        private static partial Regex UposRegex();
         [GeneratedRegex("<(\\w+?)>")]
         private static partial Regex InfoRegex();
     }

From 3cb326ec1ca7b35337847ca52c5f97dbb65e4af6 Mon Sep 17 00:00:00 2001
From: My-Responsitories <email.youxiang.alpha@gmail.com>
Date: Fri, 16 Dec 2022 13:32:11 +0800
Subject: [PATCH 2/2] edit '-intl' help information

---
 BBDown/CommandLineInvoker.cs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/BBDown/CommandLineInvoker.cs b/BBDown/CommandLineInvoker.cs
index 47f625104..1e0df8d7e 100644
--- a/BBDown/CommandLineInvoker.cs
+++ b/BBDown/CommandLineInvoker.cs
@@ -14,7 +14,7 @@ internal class CommandLineInvoker
         private readonly static Argument<string> Url = new("url", description: "视频地址 或 av|bv|BV|ep|ss");
         private readonly static Option<bool> UseTvApi = new(new string[] { "--use-tv-api", "-tv" }, "使用TV端解析模式");
         private readonly static Option<bool> UseAppApi = new(new string[] { "--use-app-api", "-app" }, "使用APP端解析模式");
-        private readonly static Option<bool> UseIntlApi = new(new string[] { "--use-intl-api", "-intl" }, "使用国际版解析模式");
+        private readonly static Option<bool> UseIntlApi = new(new string[] { "--use-intl-api", "-intl" }, "使用国际版(东南亚视频)解析模式");
         private readonly static Option<bool> UseMP4box = new(new string[] { "--use-mp4box" }, "使用MP4Box来混流");
         private readonly static Option<string> EncodingPriority = new(new string[] { "--encoding-priority" }, "视频编码的选择优先级, 用逗号分割 例: \"hevc,av1,avc\"");
         private readonly static Option<string> DfnPriority = new(new string[] { "--dfn-priority" }, "画质优先级,用逗号分隔 例: \"8K 超高清, 1080P 高码率, HDR 真彩, 杜比视界\"");