diff --git a/BBDown/BBDown.csproj b/BBDown/BBDown.csproj
index dd76470f1..2ad648e9b 100644
--- a/BBDown/BBDown.csproj
+++ b/BBDown/BBDown.csproj
@@ -3,7 +3,7 @@
Exe
netcoreapp3.1
- 1.2.4
+ 1.3.0
BBDown是一个免费且便捷高效的哔哩哔哩下载/解析软件.
diff --git a/BBDown/BBDownBangumiInfoFetcher.cs b/BBDown/BBDownBangumiInfoFetcher.cs
new file mode 100644
index 000000000..e80721807
--- /dev/null
+++ b/BBDown/BBDownBangumiInfoFetcher.cs
@@ -0,0 +1,59 @@
+using Newtonsoft.Json.Linq;
+using System;
+using System.Collections.Generic;
+using System.Text;
+using static BBDown.BBDownEntity;
+using static BBDown.BBDownUtil;
+
+namespace BBDown
+{
+ class BBDownBangumiInfoFetcher : IFetcher
+ {
+ public BBDownVInfo Fetch(string id)
+ {
+ id = id.Substring(3);
+ string index = "";
+ string api = $"https://api.bilibili.com/pgc/view/web/season?ep_id={id}";
+ string json = GetWebSource(api);
+ JObject infoJson = JObject.Parse(json);
+ string cover = infoJson["result"]["cover"].ToString();
+ string title = infoJson["result"]["title"].ToString();
+ string desc = infoJson["result"]["evaluate"].ToString();
+ string pubTime = infoJson["result"]["publish"]["pub_time"].ToString();
+ JArray pages = JArray.Parse(infoJson["result"]["episodes"].ToString());
+ List pagesInfo = new List();
+ int i = 1;
+ foreach (JObject page in pages)
+ {
+ string res = "";
+ try
+ {
+ res = page["dimension"]["width"].ToString() + "x" + page["dimension"]["height"].ToString();
+ }
+ catch (Exception) { }
+ string _title = page["long_title"].ToString();
+ if(string.IsNullOrEmpty(_title)) _title = page["title"].ToString();
+ Page p = new Page(i++,
+ page["aid"].ToString(),
+ page["cid"].ToString(),
+ page["id"].ToString(),
+ GetValidFileName(_title),
+ 0, res);
+ if (p.epid == id) index = p.index.ToString();
+ pagesInfo.Add(p);
+ }
+
+ var info = new BBDownVInfo();
+ info.Title = GetValidFileName(title).Trim();
+ info.Desc = GetValidFileName(desc).Trim();
+ info.Pic = cover;
+ info.PubTime = pubTime;
+ info.PagesInfo = pagesInfo;
+ info.IsBangumi = true;
+ info.IsCheese = true;
+ info.Index = index;
+
+ return info;
+ }
+ }
+}
diff --git a/BBDown/BBDownCheeseInfoFetcher.cs b/BBDown/BBDownCheeseInfoFetcher.cs
index f5bfbe50f..460791218 100644
--- a/BBDown/BBDownCheeseInfoFetcher.cs
+++ b/BBDown/BBDownCheeseInfoFetcher.cs
@@ -11,6 +11,7 @@ class BBDownCheeseInfoFetcher : IFetcher
{
public BBDownVInfo Fetch(string id)
{
+ id = id.Substring(7);
string index = "";
string api = $"https://api.bilibili.com/pugv/view/web/season?ep_id={id}";
string json = GetWebSource(api);
@@ -35,11 +36,10 @@ public BBDownVInfo Fetch(string id)
pubTime = pubTime != "" ? (new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc).AddSeconds(Convert.ToDouble(pubTime)).ToLocalTime().ToString()) : "";
var info = new BBDownVInfo();
- info.Title = GetValidFileName(title);
- info.Desc = GetValidFileName(desc);
+ info.Title = GetValidFileName(title).Trim();
+ info.Desc = GetValidFileName(desc).Trim();
info.Pic = cover;
info.PubTime = pubTime;
- info.Subtitles = new List(); //课程不考虑外挂字幕
info.PagesInfo = pagesInfo;
info.IsBangumi = true;
info.IsCheese = true;
diff --git a/BBDown/BBDownMuxer.cs b/BBDown/BBDownMuxer.cs
index cb510852d..b5f9389a5 100644
--- a/BBDown/BBDownMuxer.cs
+++ b/BBDown/BBDownMuxer.cs
@@ -37,7 +37,7 @@ public static int ffmpeg(string parms)
public static int MuxAV(string videoPath, string audioPath, string outPath, string desc = "", string title = "", string episodeId = "", string pic = "", List subs = null, bool audioOnly = false, bool videoOnly = false)
{
- if (!Directory.Exists(Path.GetDirectoryName(outPath)))
+ if (outPath.Contains("/") && ! Directory.Exists(Path.GetDirectoryName(outPath)))
Directory.CreateDirectory(Path.GetDirectoryName(outPath));
//----分析并生成-i参数
StringBuilder inputArg = new StringBuilder();
diff --git a/BBDown/BBDownNormalInfoFetcher.cs b/BBDown/BBDownNormalInfoFetcher.cs
index de8b3a321..d9a567e79 100644
--- a/BBDown/BBDownNormalInfoFetcher.cs
+++ b/BBDown/BBDownNormalInfoFetcher.cs
@@ -22,17 +22,6 @@ public BBDownVInfo Fetch(string id)
pubTime = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc).AddSeconds(Convert.ToDouble(pubTime)).ToLocalTime().ToString();
bool bangumi = false;
- JArray subs = JArray.Parse(infoJson["data"]["subtitle"]["list"].ToString());
- List subtitleInfo = new List();
- foreach (JObject sub in subs)
- {
- Subtitle subtitle = new Subtitle();
- subtitle.url = sub["subtitle_url"].ToString();
- subtitle.lan = sub["lan"].ToString();
- subtitle.path = $"{id}/{id}.{subtitle.lan}.srt";
- subtitleInfo.Add(subtitle);
- }
-
JArray pages = JArray.Parse(infoJson["data"]["pages"].ToString());
List pagesInfo = new List();
foreach (JObject page in pages)
@@ -63,11 +52,10 @@ public BBDownVInfo Fetch(string id)
catch { }
var info = new BBDownVInfo();
- info.Title = GetValidFileName(title);
- info.Desc = GetValidFileName(desc);
+ info.Title = GetValidFileName(title).Trim();
+ info.Desc = GetValidFileName(desc).Trim();
info.Pic = pic;
info.PubTime = pubTime;
- info.Subtitles = subtitleInfo;
info.PagesInfo = pagesInfo;
info.IsBangumi = bangumi;
diff --git a/BBDown/BBDownParser.cs b/BBDown/BBDownParser.cs
index 1fbeba32f..238c8f645 100644
--- a/BBDown/BBDownParser.cs
+++ b/BBDown/BBDownParser.cs
@@ -9,8 +9,10 @@ namespace BBDown
{
class BBDownParser
{
- public static string GetPlayJson(string aid, string cid, string epId, bool tvApi, bool bangumi, bool cheese, string qn = "0")
+ public static string GetPlayJson(string aidOri, string aid, string cid, string epId, bool tvApi, string qn = "0")
{
+ bool cheese = aidOri.StartsWith("cheese:");
+ bool bangumi = cheese || aidOri.StartsWith("ep:");
LogDebug("aid={0},cid={1},epId={2},tvApi={3},bangumi={4},cheese={5},qn={6}", aid, cid, epId, tvApi, bangumi, cheese, qn);
string prefix = tvApi ? (bangumi ? "api.snm0516.aisee.tv/pgc/player/api/playurltv" : "api.snm0516.aisee.tv/x/tv/ugc/playurl")
: (bangumi ? "api.bilibili.com/pgc/player/web/playurl" : "api.bilibili.com/x/player/playurl");
diff --git a/BBDown/BBDownSubUtil.cs b/BBDown/BBDownSubUtil.cs
index d66ae4ce6..f1a0b9e47 100644
--- a/BBDown/BBDownSubUtil.cs
+++ b/BBDown/BBDownSubUtil.cs
@@ -3,12 +3,78 @@
using System.Collections.Generic;
using System.IO;
using System.Text;
+using static BBDown.BBDownEntity;
using static BBDown.BBDownUtil;
+using static BBDown.BBDownLogger;
+using System.Text.RegularExpressions;
namespace BBDown
{
class BBDownSubUtil
{
+ public static List GetSubtitles(string aid, string cid)
+ {
+ List subtitles = new List();
+ try
+ {
+ string api = $"https://api.bilibili.com/x/web-interface/view?aid={aid}&cid={cid}";
+ string json = GetWebSource(api);
+ JObject infoJson = JObject.Parse(json);
+ JArray subs = JArray.Parse(infoJson["data"]["subtitle"]["list"].ToString());
+ foreach (JObject sub in subs)
+ {
+ Subtitle subtitle = new Subtitle();
+ subtitle.url = sub["subtitle_url"].ToString();
+ subtitle.lan = sub["lan"].ToString();
+ subtitle.path = $"{aid}/{aid}.{cid}.{subtitle.lan}.srt";
+ subtitles.Add(subtitle);
+ }
+ return subtitles;
+ }
+ catch (Exception)
+ {
+ try
+ {
+ //grpc调用接口 protobuf
+ string api = "https://app.biliapi.net/bilibili.community.service.dm.v1.DM/DmView";
+ int _aid = Convert.ToInt32(aid);
+ int _cid = Convert.ToInt32(cid);
+ int _type = 1;
+ byte[] data = new byte[18];
+ data[0] = 0x0; data[1] = 0x0; data[2] = 0x0; data[3] = 0x0; data[4] = 0xD; //先固定死了
+ int i = 5;
+ data[i++] = Convert.ToByte(1 << 3 | 0); // index=1
+ while ((_aid & -128) != 0)
+ {
+ data[i++] = Convert.ToByte((_aid & 127) | 128);
+ _aid = _aid >> 7;
+ }
+ data[i++] = Convert.ToByte(_aid);
+ data[i++] = Convert.ToByte(2 << 3 | 0); // index=2
+ while ((_cid & -128) != 0)
+ {
+ data[i++] = Convert.ToByte((_cid & 127) | 128);
+ _cid = _cid >> 7;
+ }
+ data[i++] = Convert.ToByte(_cid);
+ data[i++] = Convert.ToByte(3 << 3 | 0); // index=3
+ data[i++] = Convert.ToByte(_type);
+ string t = GetPostResponse(api, data);
+ Regex reg = new Regex("(zh-Han[st]).*?(http.*?\\.json)");
+ foreach(Match m in reg.Matches(t))
+ {
+ Subtitle subtitle = new Subtitle();
+ subtitle.url = m.Groups[2].Value;
+ subtitle.lan = m.Groups[1].Value;
+ subtitle.path = $"{aid}/{aid}.{cid}.{subtitle.lan}.srt";
+ subtitles.Add(subtitle);
+ }
+ return subtitles;
+ }
+ catch (Exception) { return subtitles; } //返回空列表
+ }
+ }
+
public static void SaveSubtitle(string url, string path)
{
File.WriteAllText(path, ConvertSubFromJson(GetWebSource(url)), new UTF8Encoding());
diff --git a/BBDown/BBDownUtil.cs b/BBDown/BBDownUtil.cs
index c7ea89223..7ad2c96d0 100644
--- a/BBDown/BBDownUtil.cs
+++ b/BBDown/BBDownUtil.cs
@@ -4,7 +4,6 @@
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
-using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net;
@@ -14,7 +13,6 @@
using System.Security.Cryptography;
using System.Text;
using System.Text.RegularExpressions;
-using System.Threading;
using System.Threading.Tasks;
using System.Web;
using static BBDown.BBDownEntity;
@@ -24,6 +22,25 @@ namespace BBDown
{
class BBDownUtil
{
+ public static async Task CheckUpdateAsync()
+ {
+ try
+ {
+ var ver = System.Reflection.Assembly.GetExecutingAssembly().GetName().Version;
+ string nowVer = $"{ver.Major}.{ver.Minor}.{ver.Build}";
+ string redirctUrl = await Get302("https://github.com/nilaoda/BBDown/releases/latest");
+ string latestVer = redirctUrl.Replace("https://github.com/nilaoda/BBDown/releases/tag/", "");
+ if (nowVer != latestVer && !latestVer.StartsWith("https"))
+ {
+ Console.Title = $"发现新版本:{latestVer}";
+ }
+ }
+ catch (Exception)
+ {
+ ;
+ }
+ }
+
public static async Task GetAvIdAsync(string input)
{
if (input.StartsWith("http"))
@@ -44,21 +61,29 @@ public static async Task GetAvIdAsync(string input)
}
else if (input.Contains("/cheese/"))
{
- string epId = Regex.Match(input, "ep(\\d{1,})").Groups[1].Value;
+ string epId = "";
+ if (input.Contains("/ep"))
+ {
+ epId = Regex.Match(input, "/ep(\\d{1,})").Groups[1].Value;
+ }
+ else if(input.Contains("/ss"))
+ {
+ epId = GetEpidBySSId(Regex.Match(input, "/ss(\\d{1,})").Groups[1].Value);
+ }
return $"cheese:{epId}";
}
+ else if (input.Contains("/ep"))
+ {
+ string epId = Regex.Match(input, "/ep(\\d{1,})").Groups[1].Value;
+ return $"ep:{epId}";
+ }
else
{
string web = GetWebSource(input);
Regex regex = new Regex("window.__INITIAL_STATE__=([\\s\\S].*?);\\(function\\(\\)");
string json = regex.Match(web).Groups[1].Value;
- string aid = JObject.Parse(json)["epInfo"]["aid"].ToString();
- string cid = JObject.Parse(json)["epInfo"]["cid"].ToString();
- if (aid == "-1" || cid == "-1") //重新获取
- {
- aid = JObject.Parse(json)["epList"][0]["aid"].ToString();
- }
- return aid;
+ string epId = JObject.Parse(json)["epList"][0]["id"].ToString();
+ return $"ep:{epId}";
}
}
else if (input.StartsWith("BV"))
@@ -73,18 +98,18 @@ public static async Task GetAvIdAsync(string input)
{
return input.ToLower().Replace("av", "");
}
- else if (input.StartsWith("ep") || input.StartsWith("ss"))
+ else if (input.StartsWith("ep"))
+ {
+ string epId = Regex.Match(input, "ep(\\d{1,})").Groups[1].Value;
+ return $"ep:{epId}";
+ }
+ else if (input.StartsWith("ss"))
{
string web = GetWebSource("https://www.bilibili.com/bangumi/play/" + input);
Regex regex = new Regex("window.__INITIAL_STATE__=([\\s\\S].*?);\\(function\\(\\)");
string json = regex.Match(web).Groups[1].Value;
- string aid = JObject.Parse(json)["epInfo"]["aid"].ToString();
- string cid = JObject.Parse(json)["epInfo"]["cid"].ToString();
- if (aid == "-1" || cid == "-1") //重新获取
- {
- aid = JObject.Parse(json)["epList"][0]["aid"].ToString();
- }
- return aid;
+ string epId = JObject.Parse(json)["epList"][0]["id"].ToString();
+ return $"ep:{epId}";
}
else
{
@@ -178,6 +203,56 @@ public static string GetWebSource(String url)
return htmlCode;
}
+ public static string GetPostResponse(string Url, byte[] postData)
+ {
+ string htmlCode = string.Empty;
+ HttpWebRequest request = (HttpWebRequest)WebRequest.Create(Url);
+ request.Method = "POST";
+ request.ContentType = "application/grpc";
+ request.ContentLength = postData.Length;
+ request.UserAgent = "Dalvik/2.1.0 (Linux; U; Android 6.0.1; oneplus a5010 Build/V417IR) 6.10.0 os/android model/oneplus a5010 mobi_app/android build/6100500 channel/bili innerVer/6100500 osVer/6.0.1 network/2";
+ request.Headers.Add("Cookie", Program.COOKIE);
+ Stream myRequestStream = request.GetRequestStream();
+ myRequestStream.Write(postData);
+ myRequestStream.Close();
+ HttpWebResponse webResponse = (HttpWebResponse)request.GetResponse();
+ if (webResponse.ContentEncoding != null
+ && webResponse.ContentEncoding.ToLower() == "gzip") //如果使用了GZip则先解压
+ {
+ using (Stream streamReceive = webResponse.GetResponseStream())
+ {
+ using (var zipStream =
+ new GZipInputStream(streamReceive))
+ {
+ using (StreamReader sr = new StreamReader(zipStream, Encoding.UTF8))
+ {
+ htmlCode = sr.ReadToEnd();
+ }
+ }
+ }
+ }
+ else
+ {
+ using (Stream streamReceive = webResponse.GetResponseStream())
+ {
+ using (StreamReader sr = new StreamReader(streamReceive, Encoding.UTF8))
+ {
+ htmlCode = sr.ReadToEnd();
+ }
+ }
+ }
+
+ if (webResponse != null)
+ {
+ webResponse.Close();
+ }
+ if (request != null)
+ {
+ request.Abort();
+ }
+ return htmlCode;
+ }
+
public static string GetAidByBV(string bv)
{
string api = $"https://api.bilibili.com/x/web-interface/archive/stat?bvid={bv}";
@@ -186,6 +261,14 @@ public static string GetAidByBV(string bv)
return aid;
}
+ public static string GetEpidBySSId(string ssid)
+ {
+ string api = $"https://api.bilibili.com/pugv/view/web/season?season_id={ssid}";
+ string json = GetWebSource(api);
+ string epId = JObject.Parse(json)["data"]["episodes"][0]["id"].ToString();
+ return epId;
+ }
+
public static async Task DownloadFile(string url, string path)
{
string tmpName = Path.Combine(Path.GetDirectoryName(path), Path.GetFileNameWithoutExtension(path) + ".tmp");
diff --git a/BBDown/BBDownVInfo.cs b/BBDown/BBDownVInfo.cs
index 384219ddc..7a1c41c4a 100644
--- a/BBDown/BBDownVInfo.cs
+++ b/BBDown/BBDownVInfo.cs
@@ -35,11 +35,6 @@ class BBDownVInfo
private bool isBangumi;
private bool isCheese;
- ///
- /// 视频CC字幕
- ///
- private List subtitles;
-
///
/// 视频分P信息
///
@@ -52,7 +47,6 @@ class BBDownVInfo
public bool IsBangumi { get => isBangumi; set => isBangumi = value; }
public bool IsCheese { get => isCheese; set => isCheese = value; }
public string Index { get => index; set => index = value; }
- internal List Subtitles { get => subtitles; set => subtitles = value; }
internal List PagesInfo { get => pagesInfo; set => pagesInfo = value; }
}
}
diff --git a/BBDown/Program.cs b/BBDown/Program.cs
index 2d0bc4528..fc732b6c0 100644
--- a/BBDown/Program.cs
+++ b/BBDown/Program.cs
@@ -8,7 +8,6 @@
using System.Drawing;
using System.IO;
using System.Net;
-using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using static BBDown.BBDownEntity;
@@ -252,6 +251,11 @@ private static async Task DoWorkAsync(MyOption myOption)
Console.Write("请注意:任何BUG请前往以下网址反馈:\r\n" +
"https://github.com/nilaoda/BBDown/issues\r\n");
Console.WriteLine();
+ //检测更新
+ new Thread(async () =>
+ {
+ await CheckUpdateAsync();
+ }).Start();
try
{
bool interactMode = myOption.Interactive;
@@ -265,7 +269,7 @@ private static async Task DoWorkAsync(MyOption myOption)
DEBUG_LOG = myOption.Debug;
string input = myOption.Url;
string selectPage = myOption.SelectPage;
- string aid = "";
+ string aidOri = ""; //原始aid
COOKIE = myOption.Cookie;
TOKEN = myOption.AccessToken != null ? myOption.AccessToken.Replace("access_token=", "") : "";
@@ -297,10 +301,10 @@ private static async Task DoWorkAsync(MyOption myOption)
TOKEN = File.ReadAllText(Path.Combine(AppContext.BaseDirectory, "BBDownTV.data"));
}
Log("获取aid...");
- aid = await GetAvIdAsync(input);
- Log("获取aid结束: " + aid);
+ aidOri = await GetAvIdAsync(input);
+ Log("获取aid结束: " + aidOri);
//-p的优先级大于URL中的自带p参数,所以先清空selectedPages
- if (!string.IsNullOrEmpty(selectPage))
+ if (!string.IsNullOrEmpty(selectPage) && selectPage != "ALL")
{
selectedPages = new List();
try
@@ -328,21 +332,18 @@ private static async Task DoWorkAsync(MyOption myOption)
catch { LogError("解析分P参数时失败了~"); selectedPages = null; };
}
- if (string.IsNullOrEmpty(aid)) throw new Exception("输入有误");
+ if (string.IsNullOrEmpty(aidOri)) throw new Exception("输入有误");
+ Log("获取视频信息...");
IFetcher fetcher = new BBDownNormalInfoFetcher();
- string cheeseId = "";
- if (aid.StartsWith("cheese"))
+ if (aidOri.StartsWith("cheese"))
{
- cheeseId = aid.Substring(7);
fetcher = new BBDownCheeseInfoFetcher();
}
- var vInfo = fetcher.Fetch(cheeseId != "" ? cheeseId : aid);
- //如果用户没有选择分P,根据epid来确定某一集
- if (selectedPages == null && !string.IsNullOrEmpty(vInfo.Index))
+ else if (aidOri.StartsWith("ep"))
{
- selectedPages = new List { vInfo.Index };
+ fetcher = new BBDownBangumiInfoFetcher();
}
- Log("获取视频信息...");
+ var vInfo = fetcher.Fetch(aidOri);
string title = vInfo.Title;
string desc = vInfo.Desc;
string pic = vInfo.Pic;
@@ -350,35 +351,23 @@ private static async Task DoWorkAsync(MyOption myOption)
LogColor("视频标题: " + title);
Log("发布时间: " + pubTime);
List pagesInfo = vInfo.PagesInfo;
- List subtitleInfo = vInfo.Subtitles;
+ List subtitleInfo = new List();
bool more = false;
bool bangumi = vInfo.IsBangumi;
bool cheese = vInfo.IsCheese;
if (!infoMode)
{
- if (!Directory.Exists(aid))
- {
- Directory.CreateDirectory(aid);
- }
- Log("下载封面...");
- LogDebug("下载:{0}", pic);
- new WebClient().DownloadFile(pic, $"{aid}/{aid}.jpg");
- foreach (Subtitle s in subtitleInfo)
- {
- Log($"下载字幕 {s.lan}...");
- LogDebug("下载:{0}", s.url);
- BBDownSubUtil.SaveSubtitle(s.url, s.path);
- }
+
}
+ //打印分P信息
foreach (Page p in pagesInfo)
{
- if (more) continue;
- if (p.index > 5)
+ if (more && p.index != pagesInfo.Count) continue;
+ if (!more && p.index > 5)
{
- Log("P...");
- Log("分P太多, 已经省略部分...");
+ Log("......");
more = true;
}
else
@@ -386,6 +375,14 @@ private static async Task DoWorkAsync(MyOption myOption)
Log($"P{p.index}: [{p.cid}] [{p.title}] [{FormatTime(p.dur)}]");
}
}
+
+ //如果用户没有选择分P,根据epid来确定某一集
+ if (selectedPages == null && selectPage != "ALL" && !string.IsNullOrEmpty(vInfo.Index))
+ {
+ selectedPages = new List { vInfo.Index };
+ Log("程序已自动选择你输入的集数,如果要下载其他集数请自行指定分P(可使用参数ALL代表全部)");
+ }
+
Log($"共计 {pagesInfo.Count} 个分P, 已选择:" + (selectedPages == null ? "ALL" : string.Join(",", selectedPages)));
//过滤不需要的分P
@@ -395,13 +392,34 @@ private static async Task DoWorkAsync(MyOption myOption)
foreach (Page p in pagesInfo)
{
Log($"开始解析P{p.index}...");
+ if (!infoMode)
+ {
+ if (!Directory.Exists(p.aid))
+ {
+ Directory.CreateDirectory(p.aid);
+ }
+ if (!File.Exists($"{p.aid}/{p.aid}.jpg"))
+ {
+ Log("下载封面...");
+ LogDebug("下载:{0}", pic);
+ new WebClient().DownloadFile(pic, $"{p.aid}/{p.aid}.jpg");
+ }
+ LogDebug("获取字幕...");
+ subtitleInfo = BBDownSubUtil.GetSubtitles(p.aid, p.cid);
+ foreach (Subtitle s in subtitleInfo)
+ {
+ Log($"下载字幕 {s.lan} => {BBDownSubUtil.SubDescDic[s.lan]}...");
+ LogDebug("下载:{0}", s.url);
+ BBDownSubUtil.SaveSubtitle(s.url, s.path);
+ }
+ }
List