mirror of
https://github.com/yaobiao131/downkyicore.git
synced 2025-08-10 00:52:31 +00:00
fix: 优化项目代码
1、修复部分字幕下载问题 2、修复自定义aria2设置出错问题
This commit is contained in:
@@ -14,9 +14,9 @@ public static class BangumiInfo
|
||||
/// <returns></returns>
|
||||
public static BangumiMedia BangumiMediaInfo(long mediaId)
|
||||
{
|
||||
string url = $"https://api.bilibili.com/pgc/review/user?media_id={mediaId}";
|
||||
string referer = "https://www.bilibili.com";
|
||||
string response = WebClient.RequestWeb(url, referer);
|
||||
var url = $"https://api.bilibili.com/pgc/review/user?media_id={mediaId}";
|
||||
const string referer = "https://www.bilibili.com";
|
||||
var response = WebClient.RequestWeb(url, referer);
|
||||
|
||||
try
|
||||
{
|
||||
@@ -25,10 +25,8 @@ public static class BangumiInfo
|
||||
{
|
||||
return media.Result.Media;
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
@@ -44,10 +42,10 @@ public static class BangumiInfo
|
||||
/// <param name="seasonId"></param>
|
||||
/// <param name="episodeId"></param>
|
||||
/// <returns></returns>
|
||||
public static BangumiSeason BangumiSeasonInfo(long seasonId = -1, long episodeId = -1)
|
||||
public static BangumiSeason? BangumiSeasonInfo(long seasonId = -1, long episodeId = -1)
|
||||
{
|
||||
string baseUrl = "https://api.bilibili.com/pgc/view/web/season";
|
||||
string referer = "https://www.bilibili.com";
|
||||
const string baseUrl = "https://api.bilibili.com/pgc/view/web/season";
|
||||
const string referer = "https://www.bilibili.com";
|
||||
string url;
|
||||
if (seasonId > -1)
|
||||
{
|
||||
@@ -62,19 +60,12 @@ public static class BangumiInfo
|
||||
return null;
|
||||
}
|
||||
|
||||
string response = WebClient.RequestWeb(url, referer);
|
||||
var response = WebClient.RequestWeb(url, referer);
|
||||
|
||||
try
|
||||
{
|
||||
var bangumiSeason = JsonConvert.DeserializeObject<BangumiSeasonOrigin>(response);
|
||||
if (bangumiSeason != null)
|
||||
{
|
||||
return bangumiSeason.Result;
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return bangumiSeason?.Result;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
public static class BangumiType
|
||||
{
|
||||
public static Dictionary<int, string> Type = new Dictionary<int, string>()
|
||||
public static readonly Dictionary<int, string> Type = new()
|
||||
{
|
||||
{ 1, "Anime" },
|
||||
{ 2, "Movie" },
|
||||
@@ -16,7 +16,7 @@ public static class BangumiType
|
||||
{ 10, "Unknown" }
|
||||
};
|
||||
|
||||
public static Dictionary<int, int> TypeId = new Dictionary<int, int>()
|
||||
public static readonly Dictionary<int, int> TypeId = new()
|
||||
{
|
||||
{ 1, 13 },
|
||||
{ 2, 23 },
|
||||
|
||||
@@ -2,13 +2,13 @@
|
||||
|
||||
public static class BvId
|
||||
{
|
||||
private const string tableStr = "fZodR9XQDSUm21yCkr6zBqiveYah8bt4xsWpHnJE7jL5VG3guMTKNPAwcF"; //码表
|
||||
private static readonly char[] table = tableStr.ToCharArray();
|
||||
private const string TableStr = "fZodR9XQDSUm21yCkr6zBqiveYah8bt4xsWpHnJE7jL5VG3guMTKNPAwcF"; //码表
|
||||
private static readonly char[] Table = TableStr.ToCharArray();
|
||||
|
||||
private static readonly char[] tr = new char[124]; //反查码表
|
||||
private static readonly char[] Tr = new char[124]; //反查码表
|
||||
private const ulong Xor = 177451812; //固定异或值
|
||||
private const ulong add = 8728348608; //固定加法值
|
||||
private static readonly int[] s = { 11, 10, 3, 8, 4, 6 }; //位置编码表
|
||||
private const ulong Add = 8728348608; //固定加法值
|
||||
private static readonly int[] S = { 11, 10, 3, 8, 4, 6 }; //位置编码表
|
||||
|
||||
static BvId()
|
||||
{
|
||||
@@ -18,8 +18,8 @@ public static class BvId
|
||||
//初始化反查码表
|
||||
private static void Tr_init()
|
||||
{
|
||||
for (int i = 0; i < 58; i++)
|
||||
tr[table[i]] = (char)i;
|
||||
for (var i = 0; i < 58; i++)
|
||||
Tr[Table[i]] = (char)i;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -29,13 +29,13 @@ public static class BvId
|
||||
/// <returns></returns>
|
||||
public static ulong Bv2Av(string bvid)
|
||||
{
|
||||
char[] bv = bvid.ToCharArray();
|
||||
var bv = bvid.ToCharArray();
|
||||
|
||||
ulong r = 0;
|
||||
ulong av;
|
||||
for (int i = 0; i < 6; i++)
|
||||
r += tr[bv[s[i]]] * (ulong)Math.Pow(58, i);
|
||||
av = (r - add) ^ Xor;
|
||||
for (var i = 0; i < 6; i++)
|
||||
r += Tr[bv[S[i]]] * (ulong)Math.Pow(58, i);
|
||||
av = (r - Add) ^ Xor;
|
||||
return av;
|
||||
}
|
||||
|
||||
@@ -47,13 +47,13 @@ public static class BvId
|
||||
public static string Av2Bv(ulong av)
|
||||
{
|
||||
//编码结果
|
||||
string res = "BV1 4 1 7 ";
|
||||
char[] result = res.ToCharArray();
|
||||
const string res = "BV1 4 1 7 ";
|
||||
var result = res.ToCharArray();
|
||||
|
||||
av = (av ^ Xor) + add;
|
||||
for (int i = 0; i < 6; i++)
|
||||
result[s[i]] = table[av / (ulong)Math.Pow(58, i) % 58];
|
||||
string bv = new string(result);
|
||||
av = (av ^ Xor) + Add;
|
||||
for (var i = 0; i < 6; i++)
|
||||
result[S[i]] = Table[av / (ulong)Math.Pow(58, i) % 58];
|
||||
var bv = new string(result);
|
||||
return bv;
|
||||
}
|
||||
}
|
||||
@@ -2,8 +2,8 @@
|
||||
|
||||
public static class DanmakuSender
|
||||
{
|
||||
private const uint CRCPOLYNOMIAL = 0xEDB88320;
|
||||
private static readonly uint[] crctable = new uint[256];
|
||||
private const uint Crcpolynomial = 0xEDB88320;
|
||||
private static readonly uint[] Crctable = new uint[256];
|
||||
|
||||
static DanmakuSender()
|
||||
{
|
||||
@@ -12,15 +12,15 @@ public static class DanmakuSender
|
||||
|
||||
private static void CreateTable()
|
||||
{
|
||||
for (int i = 0; i < 256; i++)
|
||||
for (var i = 0; i < 256; i++)
|
||||
{
|
||||
uint crcreg = (uint)i;
|
||||
var crcreg = (uint)i;
|
||||
|
||||
for (int j = 0; j < 8; j++)
|
||||
for (var j = 0; j < 8; j++)
|
||||
{
|
||||
if ((crcreg & 1) != 0)
|
||||
{
|
||||
crcreg = CRCPOLYNOMIAL ^ (crcreg >> 1);
|
||||
crcreg = Crcpolynomial ^ (crcreg >> 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -28,17 +28,16 @@ public static class DanmakuSender
|
||||
}
|
||||
}
|
||||
|
||||
crctable[i] = crcreg;
|
||||
Crctable[i] = crcreg;
|
||||
}
|
||||
}
|
||||
|
||||
private static uint Crc32(string userId)
|
||||
{
|
||||
uint crcstart = 0xFFFFFFFF;
|
||||
for (int i = 0; i < userId.Length; i++)
|
||||
var crcstart = 0xFFFFFFFF;
|
||||
foreach (var index in userId.Select(t => (uint)(crcstart ^ (int)t) & 255))
|
||||
{
|
||||
uint index = (uint)(crcstart ^ (int)userId[i]) & 255;
|
||||
crcstart = (crcstart >> 8) ^ crctable[index];
|
||||
crcstart = (crcstart >> 8) ^ Crctable[index];
|
||||
}
|
||||
|
||||
return crcstart;
|
||||
@@ -47,11 +46,11 @@ public static class DanmakuSender
|
||||
private static uint Crc32LastIndex(string userId)
|
||||
{
|
||||
uint index = 0;
|
||||
uint crcstart = 0xFFFFFFFF;
|
||||
for (int i = 0; i < userId.Length; i++)
|
||||
var crcstart = 0xFFFFFFFF;
|
||||
foreach (var t in userId)
|
||||
{
|
||||
index = (uint)((crcstart ^ (int)userId[i]) & 255);
|
||||
crcstart = (crcstart >> 8) ^ crctable[index];
|
||||
index = (uint)((crcstart ^ (int)t) & 255);
|
||||
crcstart = (crcstart >> 8) ^ Crctable[index];
|
||||
}
|
||||
|
||||
return index;
|
||||
@@ -59,9 +58,9 @@ public static class DanmakuSender
|
||||
|
||||
private static int GetCrcIndex(long t)
|
||||
{
|
||||
for (int i = 0; i < 256; i++)
|
||||
for (var i = 0; i < 256; i++)
|
||||
{
|
||||
if ((crctable[i] >> 24) == t)
|
||||
if ((Crctable[i] >> 24) == t)
|
||||
{
|
||||
return i;
|
||||
}
|
||||
@@ -72,9 +71,9 @@ public static class DanmakuSender
|
||||
|
||||
private static object[] DeepCheck(int i, int[] index)
|
||||
{
|
||||
object[] resultArray = new object[2];
|
||||
var resultArray = new object[2];
|
||||
|
||||
string result = "";
|
||||
var result = "";
|
||||
uint tc; // = 0x00;
|
||||
var hashcode = Crc32(i.ToString());
|
||||
tc = (uint)(hashcode & 0xff ^ index[2]);
|
||||
@@ -86,7 +85,7 @@ public static class DanmakuSender
|
||||
}
|
||||
|
||||
result += (tc - 48).ToString();
|
||||
hashcode = crctable[index[2]] ^ (hashcode >> 8);
|
||||
hashcode = Crctable[index[2]] ^ (hashcode >> 8);
|
||||
tc = (uint)(hashcode & 0xff ^ index[1]);
|
||||
|
||||
if (!(tc <= 57 && tc >= 48))
|
||||
@@ -96,7 +95,7 @@ public static class DanmakuSender
|
||||
}
|
||||
|
||||
result += (tc - 48).ToString();
|
||||
hashcode = crctable[index[1]] ^ (hashcode >> 8);
|
||||
hashcode = Crctable[index[1]] ^ (hashcode >> 8);
|
||||
tc = (uint)(hashcode & 0xff ^ index[0]);
|
||||
|
||||
if (!(tc <= 57 && tc >= 48))
|
||||
@@ -120,38 +119,31 @@ public static class DanmakuSender
|
||||
/// <returns></returns>
|
||||
public static string FindDanmakuSender(string userId)
|
||||
{
|
||||
object[] deepCheckData = new object[2];
|
||||
var deepCheckData = new object[2];
|
||||
|
||||
int[] index = new int[4];
|
||||
uint ht = (uint)Convert.ToInt32($"0x{userId}", 16);
|
||||
var index = new int[4];
|
||||
var ht = (uint)Convert.ToInt32($"0x{userId}", 16);
|
||||
ht ^= 0xffffffff;
|
||||
|
||||
int i;
|
||||
for (i = 3; i > -1; i--)
|
||||
{
|
||||
index[3 - i] = GetCrcIndex(ht >> (i * 8));
|
||||
uint snum = crctable[index[3 - i]];
|
||||
var snum = Crctable[index[3 - i]];
|
||||
ht ^= snum >> ((3 - i) * 8);
|
||||
}
|
||||
|
||||
for (i = 0; i < 100000000; i++)
|
||||
{
|
||||
uint lastindex = Crc32LastIndex(i.ToString());
|
||||
if (lastindex == index[3])
|
||||
var lastindex = Crc32LastIndex(i.ToString());
|
||||
if (lastindex != index[3]) continue;
|
||||
deepCheckData = DeepCheck(i, index);
|
||||
if ((int)deepCheckData[0] != 0)
|
||||
{
|
||||
deepCheckData = DeepCheck(i, index);
|
||||
if ((int)deepCheckData[0] != 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (i == 100000000)
|
||||
{
|
||||
return "-1";
|
||||
}
|
||||
|
||||
return $"{i}{deepCheckData[1]}";
|
||||
return i == 100000000 ? "-1" : $"{i}{deepCheckData[1]}";
|
||||
}
|
||||
}
|
||||
@@ -140,7 +140,7 @@ public static class ParseEntrance
|
||||
/// <returns></returns>
|
||||
public static bool IsBangumiSeasonUrl(string input)
|
||||
{
|
||||
string id = GetBangumiId(input);
|
||||
var id = GetBangumiId(input);
|
||||
return IsBangumiSeasonId(id);
|
||||
}
|
||||
|
||||
@@ -155,14 +155,13 @@ public static class ParseEntrance
|
||||
{
|
||||
return Number.GetInt(input.Remove(0, 2));
|
||||
}
|
||||
else if (IsBangumiSeasonUrl(input))
|
||||
|
||||
if (IsBangumiSeasonUrl(input))
|
||||
{
|
||||
return Number.GetInt(GetBangumiId(input).Remove(0, 2));
|
||||
}
|
||||
else
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -182,7 +181,7 @@ public static class ParseEntrance
|
||||
/// <returns></returns>
|
||||
public static bool IsBangumiEpisodeUrl(string input)
|
||||
{
|
||||
string id = GetBangumiId(input);
|
||||
var id = GetBangumiId(input);
|
||||
return IsBangumiEpisodeId(id);
|
||||
}
|
||||
|
||||
@@ -197,14 +196,13 @@ public static class ParseEntrance
|
||||
{
|
||||
return Number.GetInt(input.Remove(0, 2));
|
||||
}
|
||||
else if (IsBangumiEpisodeUrl(input))
|
||||
|
||||
if (IsBangumiEpisodeUrl(input))
|
||||
{
|
||||
return Number.GetInt(GetBangumiId(input).Remove(0, 2));
|
||||
}
|
||||
else
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -224,7 +222,7 @@ public static class ParseEntrance
|
||||
/// <returns></returns>
|
||||
public static bool IsBangumiMediaUrl(string input)
|
||||
{
|
||||
string id = GetBangumiId(input);
|
||||
var id = GetBangumiId(input);
|
||||
return IsBangumiMediaId(id);
|
||||
}
|
||||
|
||||
@@ -239,14 +237,13 @@ public static class ParseEntrance
|
||||
{
|
||||
return Number.GetInt(input.Remove(0, 2));
|
||||
}
|
||||
else if (IsBangumiMediaUrl(input))
|
||||
|
||||
if (IsBangumiMediaUrl(input))
|
||||
{
|
||||
return Number.GetInt(GetBangumiId(input).Remove(0, 2));
|
||||
}
|
||||
else
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -397,14 +394,13 @@ public static class ParseEntrance
|
||||
{
|
||||
return Regex.IsMatch(input.Remove(0, 4), @"^\d+$");
|
||||
}
|
||||
else if (input.ToLower().StartsWith("uid"))
|
||||
|
||||
if (input.ToLower().StartsWith("uid"))
|
||||
{
|
||||
return Regex.IsMatch(input.Remove(0, 3), @"^\d+$");
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -440,11 +436,13 @@ public static class ParseEntrance
|
||||
{
|
||||
return Number.GetInt(input.Remove(0, 4));
|
||||
}
|
||||
else if (input.ToLower().StartsWith("uid"))
|
||||
|
||||
if (input.ToLower().StartsWith("uid"))
|
||||
{
|
||||
return Number.GetInt(input.Remove(0, 3));
|
||||
}
|
||||
else if (IsUserUrl(input))
|
||||
|
||||
if (IsUserUrl(input))
|
||||
{
|
||||
var url = EnableHttps(input);
|
||||
url = DeleteUrlParam(url);
|
||||
@@ -453,15 +451,11 @@ public static class ParseEntrance
|
||||
{
|
||||
return long.Parse(match.Value);
|
||||
}
|
||||
else
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -520,12 +514,7 @@ public static class ParseEntrance
|
||||
private static string GetBangumiId(string input)
|
||||
{
|
||||
var id = GetId(input, BangumiUrl);
|
||||
if (id != "")
|
||||
{
|
||||
return id;
|
||||
}
|
||||
|
||||
return GetId(input, BangumiMediaUrl);
|
||||
return id != "" ? id : GetId(input, BangumiMediaUrl);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -546,12 +535,7 @@ public static class ParseEntrance
|
||||
/// <returns></returns>
|
||||
private static bool IsIntId(string input, string prefix)
|
||||
{
|
||||
if (input.ToLower().StartsWith(prefix))
|
||||
{
|
||||
return Regex.IsMatch(input.Remove(0, 2), @"^\d+$");
|
||||
}
|
||||
|
||||
return false;
|
||||
return input.ToLower().StartsWith(prefix) && Regex.IsMatch(input.Remove(0, 2), @"^\d+$");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -582,11 +566,6 @@ public static class ParseEntrance
|
||||
url = url.Replace(ShortUrl, VideoUrl);
|
||||
}
|
||||
|
||||
if (!url.StartsWith(baseUrl))
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
return url.Replace(baseUrl, "");
|
||||
return !url.StartsWith(baseUrl) ? "" : url.Replace(baseUrl, "");
|
||||
}
|
||||
}
|
||||
@@ -13,10 +13,10 @@ public static class CheeseInfo
|
||||
/// <param name="seasonId"></param>
|
||||
/// <param name="episodeId"></param>
|
||||
/// <returns></returns>
|
||||
public static CheeseView CheeseViewInfo(long seasonId = -1, long episodeId = -1)
|
||||
public static CheeseView? CheeseViewInfo(long seasonId = -1, long episodeId = -1)
|
||||
{
|
||||
string baseUrl = "https://api.bilibili.com/pugv/view/web/season";
|
||||
string referer = "https://www.bilibili.com";
|
||||
const string baseUrl = "https://api.bilibili.com/pugv/view/web/season";
|
||||
const string referer = "https://www.bilibili.com";
|
||||
string url;
|
||||
if (seasonId > -1)
|
||||
{
|
||||
@@ -31,19 +31,12 @@ public static class CheeseInfo
|
||||
return null;
|
||||
}
|
||||
|
||||
string response = WebClient.RequestWeb(url, referer);
|
||||
var response = WebClient.RequestWeb(url, referer);
|
||||
|
||||
try
|
||||
{
|
||||
CheeseViewOrigin cheese = JsonConvert.DeserializeObject<CheeseViewOrigin>(response);
|
||||
if (cheese != null)
|
||||
{
|
||||
return cheese.Data;
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
var cheese = JsonConvert.DeserializeObject<CheeseViewOrigin>(response);
|
||||
return cheese?.Data;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
@@ -60,23 +53,16 @@ public static class CheeseInfo
|
||||
/// <param name="ps"></param>
|
||||
/// <param name="pn"></param>
|
||||
/// <returns></returns>
|
||||
public static CheeseEpisodeList CheeseEpisodeList(long seasonId, int ps = 50, int pn = 1)
|
||||
public static CheeseEpisodeList? CheeseEpisodeList(long seasonId, int ps = 50, int pn = 1)
|
||||
{
|
||||
string url = $"https://api.bilibili.com/pugv/view/web/ep/list?season_id={seasonId}&pn={pn}&ps={ps}";
|
||||
string referer = "https://www.bilibili.com";
|
||||
string response = WebClient.RequestWeb(url, referer);
|
||||
var url = $"https://api.bilibili.com/pugv/view/web/ep/list?season_id={seasonId}&pn={pn}&ps={ps}";
|
||||
const string referer = "https://www.bilibili.com";
|
||||
var response = WebClient.RequestWeb(url, referer);
|
||||
|
||||
try
|
||||
{
|
||||
CheeseEpisodeListOrigin cheese = JsonConvert.DeserializeObject<CheeseEpisodeListOrigin>(response);
|
||||
if (cheese != null)
|
||||
{
|
||||
return cheese.Data;
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
var cheese = JsonConvert.DeserializeObject<CheeseEpisodeListOrigin>(response);
|
||||
return cheese?.Data;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
|
||||
@@ -11,23 +11,16 @@ public static class FavoritesInfo
|
||||
/// 获取收藏夹元数据
|
||||
/// </summary>
|
||||
/// <param name="mediaId"></param>
|
||||
public static FavoritesMetaInfo GetFavoritesInfo(long mediaId)
|
||||
public static FavoritesMetaInfo? GetFavoritesInfo(long mediaId)
|
||||
{
|
||||
string url = $"https://api.bilibili.com/x/v3/fav/folder/info?media_id={mediaId}";
|
||||
string referer = "https://www.bilibili.com";
|
||||
string response = WebClient.RequestWeb(url, referer);
|
||||
var url = $"https://api.bilibili.com/x/v3/fav/folder/info?media_id={mediaId}";
|
||||
const string referer = "https://www.bilibili.com";
|
||||
var response = WebClient.RequestWeb(url, referer);
|
||||
|
||||
try
|
||||
{
|
||||
var info = JsonConvert.DeserializeObject<FavoritesMetaInfoOrigin>(response);
|
||||
if (info != null)
|
||||
{
|
||||
return info.Data;
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return info?.Data;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
@@ -44,21 +37,17 @@ public static class FavoritesInfo
|
||||
/// <param name="pn">页码</param>
|
||||
/// <param name="ps">每页项数</param>
|
||||
/// <returns></returns>
|
||||
public static List<FavoritesMetaInfo> GetCreatedFavorites(long mid, int pn, int ps)
|
||||
public static List<FavoritesMetaInfo>? GetCreatedFavorites(long mid, int pn, int ps)
|
||||
{
|
||||
string url = $"https://api.bilibili.com/x/v3/fav/folder/created/list?up_mid={mid}&pn={pn}&ps={ps}";
|
||||
string referer = "https://www.bilibili.com";
|
||||
string response = WebClient.RequestWeb(url, referer);
|
||||
var url = $"https://api.bilibili.com/x/v3/fav/folder/created/list?up_mid={mid}&pn={pn}&ps={ps}";
|
||||
const string referer = "https://www.bilibili.com";
|
||||
var response = WebClient.RequestWeb(url, referer);
|
||||
|
||||
try
|
||||
{
|
||||
var favorites = JsonConvert.DeserializeObject<FavoritesListOrigin>(response);
|
||||
if (favorites == null || favorites.Data == null || favorites.Data.List == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return favorites.Data.List;
|
||||
return favorites?.Data.List;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
@@ -75,13 +64,13 @@ public static class FavoritesInfo
|
||||
/// <returns></returns>
|
||||
public static List<FavoritesMetaInfo> GetAllCreatedFavorites(long mid)
|
||||
{
|
||||
List<FavoritesMetaInfo> result = new List<FavoritesMetaInfo>();
|
||||
var result = new List<FavoritesMetaInfo>();
|
||||
|
||||
int i = 0;
|
||||
var i = 0;
|
||||
while (true)
|
||||
{
|
||||
i++;
|
||||
int ps = 50;
|
||||
const int ps = 50;
|
||||
|
||||
var data = GetCreatedFavorites(mid, i, ps);
|
||||
if (data == null || data.Count == 0)
|
||||
@@ -102,11 +91,11 @@ public static class FavoritesInfo
|
||||
/// <param name="pn">页码</param>
|
||||
/// <param name="ps">每页项数</param>
|
||||
/// <returns></returns>
|
||||
public static List<FavoritesMetaInfo> GetCollectedFavorites(long mid, int pn, int ps)
|
||||
public static List<FavoritesMetaInfo>? GetCollectedFavorites(long mid, int pn, int ps)
|
||||
{
|
||||
string url = $"https://api.bilibili.com/x/v3/fav/folder/collected/list?up_mid={mid}&pn={pn}&ps={ps}";
|
||||
string referer = "https://www.bilibili.com";
|
||||
string response = WebClient.RequestWeb(url, referer);
|
||||
var url = $"https://api.bilibili.com/x/v3/fav/folder/collected/list?up_mid={mid}&pn={pn}&ps={ps}";
|
||||
const string referer = "https://www.bilibili.com";
|
||||
var response = WebClient.RequestWeb(url, referer);
|
||||
|
||||
try
|
||||
{
|
||||
@@ -133,13 +122,13 @@ public static class FavoritesInfo
|
||||
/// <returns></returns>
|
||||
public static List<FavoritesMetaInfo> GetAllCollectedFavorites(long mid)
|
||||
{
|
||||
List<FavoritesMetaInfo> result = new List<FavoritesMetaInfo>();
|
||||
var result = new List<FavoritesMetaInfo>();
|
||||
|
||||
int i = 0;
|
||||
var i = 0;
|
||||
while (true)
|
||||
{
|
||||
i++;
|
||||
int ps = 50;
|
||||
const int ps = 50;
|
||||
|
||||
var data = GetCollectedFavorites(mid, i, ps);
|
||||
if (data == null || data.Count == 0)
|
||||
|
||||
@@ -13,12 +13,11 @@ public static class FavoritesResource
|
||||
/// <param name="pn">页码</param>
|
||||
/// <param name="ps">每页项数</param>
|
||||
/// <returns></returns>
|
||||
public static List<FavoritesMedia> GetFavoritesMedia(long mediaId, int pn, int ps)
|
||||
public static List<FavoritesMedia>? GetFavoritesMedia(long mediaId, int pn, int ps)
|
||||
{
|
||||
string url =
|
||||
$"https://api.bilibili.com/x/v3/fav/resource/list?media_id={mediaId}&pn={pn}&ps={ps}&platform=web";
|
||||
string referer = "https://www.bilibili.com";
|
||||
string response = WebClient.RequestWeb(url, referer);
|
||||
var url = $"https://api.bilibili.com/x/v3/fav/resource/list?media_id={mediaId}&pn={pn}&ps={ps}&platform=web";
|
||||
const string referer = "https://www.bilibili.com";
|
||||
var response = WebClient.RequestWeb(url, referer);
|
||||
|
||||
try
|
||||
{
|
||||
@@ -45,13 +44,13 @@ public static class FavoritesResource
|
||||
/// <returns></returns>
|
||||
public static List<FavoritesMedia> GetAllFavoritesMedia(long mediaId)
|
||||
{
|
||||
List<FavoritesMedia> result = new List<FavoritesMedia>();
|
||||
var result = new List<FavoritesMedia>();
|
||||
|
||||
int i = 0;
|
||||
var i = 0;
|
||||
while (true)
|
||||
{
|
||||
i++;
|
||||
int ps = 20;
|
||||
const int ps = 20;
|
||||
|
||||
var data = GetFavoritesMedia(mediaId, i, ps);
|
||||
if (data == null || data.Count == 0)
|
||||
@@ -72,9 +71,9 @@ public static class FavoritesResource
|
||||
/// <returns></returns>
|
||||
public static List<FavoritesMediaId> GetFavoritesMediaId(long mediaId)
|
||||
{
|
||||
string url = $"https://api.bilibili.com/x/v3/fav/resource/ids?media_id={mediaId}";
|
||||
string referer = "https://www.bilibili.com";
|
||||
string response = WebClient.RequestWeb(url, referer);
|
||||
var url = $"https://api.bilibili.com/x/v3/fav/resource/ids?media_id={mediaId}";
|
||||
const string referer = "https://www.bilibili.com";
|
||||
var response = WebClient.RequestWeb(url, referer);
|
||||
|
||||
try
|
||||
{
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
using DownKyi.Core.BiliApi.History.Models;
|
||||
using DownKyi.Core.Logging;
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using Console = DownKyi.Core.Utils.Debugging.Console;
|
||||
|
||||
namespace DownKyi.Core.BiliApi.History
|
||||
{
|
||||
@@ -19,9 +19,9 @@ namespace DownKyi.Core.BiliApi.History
|
||||
/// <param name="ps">每页项数</param>
|
||||
/// <param name="business">历史记录ID类型</param>
|
||||
/// <returns></returns>
|
||||
public static HistoryData GetHistory(long startId, long startTime, int ps = 30, HistoryBusiness business = HistoryBusiness.ARCHIVE)
|
||||
public static HistoryData? GetHistory(long startId, long startTime, int ps = 30, HistoryBusiness business = HistoryBusiness.ARCHIVE)
|
||||
{
|
||||
string businessStr = string.Empty;
|
||||
var businessStr = string.Empty;
|
||||
switch (business)
|
||||
{
|
||||
case HistoryBusiness.ARCHIVE:
|
||||
@@ -41,9 +41,9 @@ namespace DownKyi.Core.BiliApi.History
|
||||
break;
|
||||
}
|
||||
|
||||
string url = $"https://api.bilibili.com/x/web-interface/history/cursor?max={startId}&view_at={startTime}&ps={ps}&business={businessStr}";
|
||||
string referer = "https://www.bilibili.com";
|
||||
string response = WebClient.RequestWeb(url, referer);
|
||||
var url = $"https://api.bilibili.com/x/web-interface/history/cursor?max={startId}&view_at={startTime}&ps={ps}&business={businessStr}";
|
||||
const string referer = "https://www.bilibili.com";
|
||||
var response = WebClient.RequestWeb(url, referer);
|
||||
|
||||
try
|
||||
{
|
||||
@@ -53,7 +53,7 @@ namespace DownKyi.Core.BiliApi.History
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Utils.Debugging.Console.PrintLine("GetHistory()发生异常: {0}", e);
|
||||
Console.PrintLine("GetHistory()发生异常: {0}", e);
|
||||
LogManager.Error("History", e);
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
using DownKyi.Core.BiliApi.History.Models;
|
||||
using DownKyi.Core.Logging;
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Console = DownKyi.Core.Utils.Debugging.Console;
|
||||
|
||||
namespace DownKyi.Core.BiliApi.History
|
||||
{
|
||||
@@ -15,11 +14,11 @@ namespace DownKyi.Core.BiliApi.History
|
||||
/// 获取稍后再看视频列表
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static List<ToViewList> GetToView()
|
||||
public static List<ToViewList>? GetToView()
|
||||
{
|
||||
string url = "https://api.bilibili.com/x/v2/history/toview";
|
||||
string referer = "https://www.bilibili.com";
|
||||
string response = WebClient.RequestWeb(url, referer);
|
||||
const string url = "https://api.bilibili.com/x/v2/history/toview";
|
||||
const string referer = "https://www.bilibili.com";
|
||||
var response = WebClient.RequestWeb(url, referer);
|
||||
|
||||
try
|
||||
{
|
||||
@@ -29,10 +28,10 @@ namespace DownKyi.Core.BiliApi.History
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Utils.Debugging.Console.PrintLine("GetToView()发生异常: {0}", e);
|
||||
Console.PrintLine("GetToView()发生异常: {0}", e);
|
||||
LogManager.Error("ToView", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,6 @@ using DownKyi.Core.Settings;
|
||||
using DownKyi.Core.Settings.Models;
|
||||
using DownKyi.Core.Storage;
|
||||
using DownKyi.Core.Utils;
|
||||
using DownKyi.Core.Utils.Encryptor;
|
||||
using Console = DownKyi.Core.Utils.Debugging.Console;
|
||||
|
||||
namespace DownKyi.Core.BiliApi.Login
|
||||
@@ -12,7 +11,7 @@ namespace DownKyi.Core.BiliApi.Login
|
||||
public static class LoginHelper
|
||||
{
|
||||
// 本地位置
|
||||
private static readonly string LOCAL_LOGIN_INFO = StorageManager.GetLogin();
|
||||
private static readonly string LocalLoginInfo = StorageManager.GetLogin();
|
||||
|
||||
// 16位密码,ps:密码位数没有限制,可任意设置
|
||||
private static readonly string SecretKey = "EsOat*^y1QR!&0J6";
|
||||
@@ -24,10 +23,10 @@ namespace DownKyi.Core.BiliApi.Login
|
||||
/// <returns></returns>
|
||||
public static bool SaveLoginInfoCookies(string url)
|
||||
{
|
||||
string tempFile = LOCAL_LOGIN_INFO + "-" + Guid.NewGuid().ToString("N");
|
||||
CookieContainer cookieContainer = ObjectHelper.ParseCookie(url);
|
||||
var tempFile = LocalLoginInfo + "-" + Guid.NewGuid().ToString("N");
|
||||
var cookieContainer = ObjectHelper.ParseCookie(url);
|
||||
|
||||
bool isSucceed = ObjectHelper.WriteCookiesToDisk(tempFile, cookieContainer);
|
||||
var isSucceed = ObjectHelper.WriteCookiesToDisk(tempFile, cookieContainer);
|
||||
if (isSucceed)
|
||||
{
|
||||
// 加密密钥,增加机器码
|
||||
@@ -35,7 +34,7 @@ namespace DownKyi.Core.BiliApi.Login
|
||||
|
||||
try
|
||||
{
|
||||
File.Copy(tempFile, LOCAL_LOGIN_INFO, true);
|
||||
File.Copy(tempFile, LocalLoginInfo, true);
|
||||
// Encryptor.EncryptFile(tempFile, LOCAL_LOGIN_INFO, password);
|
||||
}
|
||||
catch (Exception e)
|
||||
@@ -61,13 +60,13 @@ namespace DownKyi.Core.BiliApi.Login
|
||||
/// <returns></returns>
|
||||
public static CookieContainer? GetLoginInfoCookies()
|
||||
{
|
||||
var tempFile = LOCAL_LOGIN_INFO + "-" + Guid.NewGuid().ToString("N");
|
||||
var tempFile = LocalLoginInfo + "-" + Guid.NewGuid().ToString("N");
|
||||
|
||||
if (File.Exists(LOCAL_LOGIN_INFO))
|
||||
if (File.Exists(LocalLoginInfo))
|
||||
{
|
||||
try
|
||||
{
|
||||
File.Copy(LOCAL_LOGIN_INFO, tempFile, true);
|
||||
File.Copy(LocalLoginInfo, tempFile, true);
|
||||
// 加密密钥,增加机器码
|
||||
var password = SecretKey;
|
||||
// Encryptor.DecryptFile(LOCAL_LOGIN_INFO, tempFile, password);
|
||||
@@ -89,7 +88,7 @@ namespace DownKyi.Core.BiliApi.Login
|
||||
return null;
|
||||
}
|
||||
|
||||
CookieContainer cookies = ObjectHelper.ReadCookiesFromDisk(tempFile);
|
||||
var cookies = ObjectHelper.ReadCookiesFromDisk(tempFile);
|
||||
|
||||
if (File.Exists(tempFile))
|
||||
{
|
||||
@@ -113,11 +112,7 @@ namespace DownKyi.Core.BiliApi.Login
|
||||
|
||||
var cookies = ObjectHelper.GetAllCookies(cookieContainer);
|
||||
|
||||
string cookie = string.Empty;
|
||||
foreach (var item in cookies)
|
||||
{
|
||||
cookie += item.ToString() + ";";
|
||||
}
|
||||
var cookie = cookies.Aggregate(string.Empty, (current, item) => current + (item + ";"));
|
||||
|
||||
return cookie.TrimEnd(';');
|
||||
}
|
||||
@@ -128,30 +123,26 @@ namespace DownKyi.Core.BiliApi.Login
|
||||
/// <returns></returns>
|
||||
public static bool Logout()
|
||||
{
|
||||
if (File.Exists(LOCAL_LOGIN_INFO))
|
||||
if (!File.Exists(LocalLoginInfo)) return false;
|
||||
try
|
||||
{
|
||||
try
|
||||
{
|
||||
File.Delete(LOCAL_LOGIN_INFO);
|
||||
File.Delete(LocalLoginInfo);
|
||||
|
||||
SettingsManager.GetInstance().SetUserInfo(new UserInfoSettings
|
||||
{
|
||||
Mid = -1,
|
||||
Name = "",
|
||||
IsLogin = false,
|
||||
IsVip = false
|
||||
});
|
||||
return true;
|
||||
}
|
||||
catch (IOException e)
|
||||
SettingsManager.GetInstance().SetUserInfo(new UserInfoSettings
|
||||
{
|
||||
Console.PrintLine("Logout()发生异常: {0}", e);
|
||||
LogManager.Error(e);
|
||||
return false;
|
||||
}
|
||||
Mid = -1,
|
||||
Name = "",
|
||||
IsLogin = false,
|
||||
IsVip = false
|
||||
});
|
||||
return true;
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
Console.PrintLine("Logout()发生异常: {0}", e);
|
||||
LogManager.Error(e);
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,10 +3,11 @@ using DownKyi.Core.BiliApi.Login.Models;
|
||||
using DownKyi.Core.Logging;
|
||||
using DownKyi.Core.Utils;
|
||||
using Newtonsoft.Json;
|
||||
using Console = DownKyi.Core.Utils.Debugging.Console;
|
||||
|
||||
namespace DownKyi.Core.BiliApi.Login;
|
||||
|
||||
public static class LoginQR
|
||||
public static class LoginQr
|
||||
{
|
||||
/// <summary>
|
||||
/// 申请二维码URL及扫码密钥(web端)
|
||||
@@ -14,16 +15,15 @@ public static class LoginQR
|
||||
/// <returns></returns>
|
||||
public static LoginUrlOrigin? GetLoginUrl()
|
||||
{
|
||||
string getLoginUrl = "https://passport.bilibili.com/x/passport-login/web/qrcode/generate";
|
||||
string response = WebClient.RequestWeb(getLoginUrl);
|
||||
Console.Out.WriteLine(response);
|
||||
const string getLoginUrl = "https://passport.bilibili.com/x/passport-login/web/qrcode/generate";
|
||||
var response = WebClient.RequestWeb(getLoginUrl);
|
||||
try
|
||||
{
|
||||
return JsonConvert.DeserializeObject<LoginUrlOrigin>(response);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Utils.Debugging.Console.PrintLine("GetLoginUrl()发生异常: {0}", e);
|
||||
Console.PrintLine("GetLoginUrl()发生异常: {0}", e);
|
||||
LogManager.Error("LoginQR", e);
|
||||
return null;
|
||||
}
|
||||
@@ -33,13 +33,12 @@ public static class LoginQR
|
||||
/// 使用扫码登录(web端)
|
||||
/// </summary>
|
||||
/// <param name="qrcodeKey"></param>
|
||||
/// <param name="goUrl"></param>
|
||||
/// <returns></returns>
|
||||
public static LoginStatus? GetLoginStatus(string qrcodeKey, string goUrl = "https://www.bilibili.com")
|
||||
public static LoginStatus? GetLoginStatus(string qrcodeKey)
|
||||
{
|
||||
string url = "https://passport.bilibili.com/x/passport-login/web/qrcode/poll?qrcode_key=" + qrcodeKey;
|
||||
var url = $"https://passport.bilibili.com/x/passport-login/web/qrcode/poll?qrcode_key={qrcodeKey}";
|
||||
|
||||
string response = WebClient.RequestWeb(url);
|
||||
var response = WebClient.RequestWeb(url);
|
||||
|
||||
try
|
||||
{
|
||||
@@ -47,7 +46,7 @@ public static class LoginQR
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Utils.Debugging.Console.PrintLine("GetLoginInfo()发生异常: {0}", e);
|
||||
Console.PrintLine("GetLoginInfo()发生异常: {0}", e);
|
||||
LogManager.Error("LoginQR", e);
|
||||
return null;
|
||||
}
|
||||
@@ -57,16 +56,16 @@ public static class LoginQR
|
||||
/// 获得登录二维码
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static Bitmap GetLoginQRCode()
|
||||
public static Bitmap? GetLoginQrCode()
|
||||
{
|
||||
try
|
||||
{
|
||||
string loginUrl = GetLoginUrl().Data.Url;
|
||||
return GetLoginQRCode(loginUrl);
|
||||
var loginUrl = GetLoginUrl()?.Data?.Url;
|
||||
return GetLoginQrCode(loginUrl);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Utils.Debugging.Console.PrintLine("GetLoginQRCode()发生异常: {0}", e);
|
||||
Console.PrintLine("GetLoginQrCode()发生异常: {0}", e);
|
||||
LogManager.Error("LoginQR", e);
|
||||
return null;
|
||||
}
|
||||
@@ -77,10 +76,11 @@ public static class LoginQR
|
||||
/// </summary>
|
||||
/// <param name="url"></param>
|
||||
/// <returns></returns>
|
||||
public static Bitmap GetLoginQRCode(string url)
|
||||
public static Bitmap? GetLoginQrCode(string? url)
|
||||
{
|
||||
if (url == null) return null;
|
||||
// 设置的参数影响app能否成功扫码
|
||||
Bitmap qrCode = QRCode.EncodeQRCode(url, 11, 10, null, 0, 0, false);
|
||||
var qrCode = QrCode.EncodeQrCode(url, 11, 10, null, 0, 0, false);
|
||||
|
||||
return qrCode;
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ public static class WbiSign
|
||||
temp.Append(origin[i]);
|
||||
}
|
||||
|
||||
return temp.ToString().Substring(0, 32);
|
||||
return temp.ToString()[..32];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -39,12 +39,7 @@ public static class WbiSign
|
||||
public static string ParametersToQuery(Dictionary<string, string> parameters)
|
||||
{
|
||||
var keys = parameters.Keys.ToList();
|
||||
var queryList = new List<string>();
|
||||
foreach (var item in keys)
|
||||
{
|
||||
var value = parameters[item];
|
||||
queryList.Add($"{item}={value}");
|
||||
}
|
||||
var queryList = (from item in keys let value = parameters[item] select $"{item}={value}").ToList();
|
||||
|
||||
return string.Join("&", queryList);
|
||||
}
|
||||
@@ -54,7 +49,7 @@ public static class WbiSign
|
||||
/// </summary>
|
||||
/// <param name="parameters"></param>
|
||||
/// <returns></returns>
|
||||
public static Dictionary<string, string> EncodeWbi(Dictionary<string, object> parameters)
|
||||
public static Dictionary<string, string> EncodeWbi(Dictionary<string, object?> parameters)
|
||||
{
|
||||
return EncWbi(parameters, GetKey().Item1, GetKey().Item2);
|
||||
}
|
||||
@@ -66,15 +61,16 @@ public static class WbiSign
|
||||
/// <param name="imgKey"></param>
|
||||
/// <param name="subKey"></param>
|
||||
/// <returns></returns>
|
||||
private static Dictionary<string, string> EncWbi(Dictionary<string, object> parameters, string imgKey,
|
||||
string subKey)
|
||||
private static Dictionary<string, string> EncWbi(Dictionary<string, object?> parameters, string imgKey, string subKey)
|
||||
{
|
||||
var paraStr = new Dictionary<string, string>();
|
||||
foreach (var para in parameters)
|
||||
foreach (var (key, value) in parameters)
|
||||
{
|
||||
var key = para.Key;
|
||||
var value = para.Value.ToString();
|
||||
paraStr.Add(key, value);
|
||||
var val = value?.ToString();
|
||||
if (val != null)
|
||||
{
|
||||
paraStr.Add(key, val);
|
||||
}
|
||||
}
|
||||
|
||||
var mixinKey = GetMixinKey(imgKey + subKey);
|
||||
@@ -84,15 +80,11 @@ public static class WbiSign
|
||||
// 按照 key 重排参数
|
||||
paraStr = paraStr.OrderBy(p => p.Key).ToDictionary(p => p.Key, p => p.Value);
|
||||
//过滤 value 中的 "!'()*" 字符
|
||||
paraStr = paraStr.ToDictionary(
|
||||
kvp => kvp.Key,
|
||||
kvp => new string(kvp.Value.Where(chr => !"!'()*".Contains(chr)).ToArray())
|
||||
);
|
||||
paraStr = paraStr.ToDictionary(kvp => kvp.Key, kvp => new string(kvp.Value.Where(chr => !"!'()*".Contains(chr)).ToArray()));
|
||||
// 序列化参数
|
||||
var query = new FormUrlEncodedContent(paraStr).ReadAsStringAsync().Result;
|
||||
//计算 w_rid
|
||||
using var md5 = MD5.Create();
|
||||
//using MD5 md5 = MD5.Create();
|
||||
var hashBytes = md5.ComputeHash(Encoding.UTF8.GetBytes(query + mixinKey));
|
||||
var wbiSign = BitConverter.ToString(hashBytes).Replace("-", "").ToLower();
|
||||
paraStr["w_rid"] = wbiSign;
|
||||
@@ -100,7 +92,7 @@ public static class WbiSign
|
||||
return paraStr;
|
||||
}
|
||||
|
||||
public static Tuple<string, string> GetKey()
|
||||
private static Tuple<string, string> GetKey()
|
||||
{
|
||||
var user = SettingsManager.GetInstance().GetUserInfo();
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ public class UserInfoForNavigation : BaseModel
|
||||
//public int allowance_count { get; set; }
|
||||
//public int answer_status { get; set; }
|
||||
//public int email_verified { get; set; }
|
||||
[JsonProperty("face")] public string Face { get; set; }
|
||||
[JsonProperty("face")] public string? Face { get; set; }
|
||||
|
||||
//public bool has_shop { get; set; }
|
||||
[JsonProperty("isLogin")] public bool IsLogin { get; set; }
|
||||
|
||||
@@ -15,15 +15,15 @@ public class Nickname
|
||||
/// </summary>
|
||||
/// <param name="nickName"></param>
|
||||
/// <returns></returns>
|
||||
public static NicknameStatus CheckNickname(string nickName)
|
||||
public static NicknameStatus? CheckNickname(string nickName)
|
||||
{
|
||||
string url = $"https://api.bilibili.com/x/relation/stat?nickName={nickName}";
|
||||
string referer = "https://www.bilibili.com";
|
||||
string response = WebClient.RequestWeb(url, referer);
|
||||
var url = $"https://api.bilibili.com/x/relation/stat?nickName={nickName}";
|
||||
const string referer = "https://www.bilibili.com";
|
||||
var response = WebClient.RequestWeb(url, referer);
|
||||
|
||||
try
|
||||
{
|
||||
NicknameStatus nickname = JsonConvert.DeserializeObject<NicknameStatus>(response);
|
||||
var nickname = JsonConvert.DeserializeObject<NicknameStatus>(response);
|
||||
return nickname;
|
||||
}
|
||||
catch (Exception e)
|
||||
|
||||
@@ -42,7 +42,7 @@ public static class UserInfo
|
||||
/// <returns></returns>
|
||||
public static UserInfoForSpace? GetUserInfoForSpace(long mid)
|
||||
{
|
||||
var parameters = new Dictionary<string, object>
|
||||
var parameters = new Dictionary<string, object?>
|
||||
{
|
||||
{ "mid", mid }
|
||||
};
|
||||
|
||||
@@ -17,15 +17,15 @@ public static class UserRelation
|
||||
/// <param name="pn">页码</param>
|
||||
/// <param name="ps">每页项数</param>
|
||||
/// <returns></returns>
|
||||
public static RelationFollow GetFollowers(long mid, int pn, int ps)
|
||||
public static RelationFollow? GetFollowers(long mid, int pn, int ps)
|
||||
{
|
||||
string url = $"https://api.bilibili.com/x/relation/followers?vmid={mid}&pn={pn}&ps={ps}";
|
||||
string referer = "https://www.bilibili.com";
|
||||
string response = WebClient.RequestWeb(url, referer);
|
||||
var url = $"https://api.bilibili.com/x/relation/followers?vmid={mid}&pn={pn}&ps={ps}";
|
||||
const string referer = "https://www.bilibili.com";
|
||||
var response = WebClient.RequestWeb(url, referer);
|
||||
|
||||
try
|
||||
{
|
||||
RelationFollowOrigin relationFollower = JsonConvert.DeserializeObject<RelationFollowOrigin>(response);
|
||||
var relationFollower = JsonConvert.DeserializeObject<RelationFollowOrigin>(response);
|
||||
if (relationFollower == null || relationFollower.Data == null)
|
||||
{
|
||||
return null;
|
||||
@@ -48,15 +48,15 @@ public static class UserRelation
|
||||
/// <returns></returns>
|
||||
public static List<RelationFollowInfo> GetAllFollowers(long mid)
|
||||
{
|
||||
List<RelationFollowInfo> result = new List<RelationFollowInfo>();
|
||||
var result = new List<RelationFollowInfo>();
|
||||
|
||||
int i = 0;
|
||||
var i = 0;
|
||||
while (true)
|
||||
{
|
||||
i++;
|
||||
int ps = 50;
|
||||
const int ps = 50;
|
||||
|
||||
RelationFollow data = GetFollowers(mid, i, ps);
|
||||
var data = GetFollowers(mid, i, ps);
|
||||
if (data == null || data.List == null || data.List.Count == 0)
|
||||
{
|
||||
break;
|
||||
@@ -76,23 +76,21 @@ public static class UserRelation
|
||||
/// <param name="ps">每页项数</param>
|
||||
/// <param name="order">排序方式</param>
|
||||
/// <returns></returns>
|
||||
public static RelationFollow GetFollowings(long mid, int pn, int ps,
|
||||
FollowingOrder order = FollowingOrder.DEFAULT)
|
||||
public static RelationFollow? GetFollowings(long mid, int pn, int ps, FollowingOrder order = FollowingOrder.DEFAULT)
|
||||
{
|
||||
string orderType = "";
|
||||
var orderType = "";
|
||||
if (order == FollowingOrder.ATTENTION)
|
||||
{
|
||||
orderType = "attention";
|
||||
}
|
||||
|
||||
string url =
|
||||
$"https://api.bilibili.com/x/relation/followings?vmid={mid}&pn={pn}&ps={ps}&order_type={orderType}";
|
||||
string referer = "https://www.bilibili.com";
|
||||
string response = WebClient.RequestWeb(url, referer);
|
||||
var url = $"https://api.bilibili.com/x/relation/followings?vmid={mid}&pn={pn}&ps={ps}&order_type={orderType}";
|
||||
const string referer = "https://www.bilibili.com";
|
||||
var response = WebClient.RequestWeb(url, referer);
|
||||
|
||||
try
|
||||
{
|
||||
RelationFollowOrigin relationFollower = JsonConvert.DeserializeObject<RelationFollowOrigin>(response);
|
||||
var relationFollower = JsonConvert.DeserializeObject<RelationFollowOrigin>(response);
|
||||
if (relationFollower == null || relationFollower.Data == null)
|
||||
{
|
||||
return null;
|
||||
@@ -116,15 +114,15 @@ public static class UserRelation
|
||||
/// <returns></returns>
|
||||
public static List<RelationFollowInfo> GetAllFollowings(long mid, FollowingOrder order = FollowingOrder.DEFAULT)
|
||||
{
|
||||
List<RelationFollowInfo> result = new List<RelationFollowInfo>();
|
||||
var result = new List<RelationFollowInfo>();
|
||||
|
||||
int i = 0;
|
||||
var i = 0;
|
||||
while (true)
|
||||
{
|
||||
i++;
|
||||
int ps = 50;
|
||||
const int ps = 50;
|
||||
|
||||
RelationFollow data = GetFollowings(mid, i, ps, order);
|
||||
var data = GetFollowings(mid, i, ps, order);
|
||||
if (data == null || data.List == null || data.List.Count == 0)
|
||||
{
|
||||
break;
|
||||
@@ -142,15 +140,15 @@ public static class UserRelation
|
||||
/// <param name="pn">页码</param>
|
||||
/// <param name="ps">每页项数</param>
|
||||
/// <returns></returns>
|
||||
public static List<RelationFollowInfo> GetWhispers(int pn, int ps)
|
||||
public static List<RelationFollowInfo>? GetWhispers(int pn, int ps)
|
||||
{
|
||||
string url = $"https://api.bilibili.com/x/relation/whispers?pn={pn}&ps={ps}";
|
||||
string referer = "https://www.bilibili.com";
|
||||
string response = WebClient.RequestWeb(url, referer);
|
||||
var url = $"https://api.bilibili.com/x/relation/whispers?pn={pn}&ps={ps}";
|
||||
const string referer = "https://www.bilibili.com";
|
||||
var response = WebClient.RequestWeb(url, referer);
|
||||
|
||||
try
|
||||
{
|
||||
RelationWhisper relationWhisper = JsonConvert.DeserializeObject<RelationWhisper>(response);
|
||||
var relationWhisper = JsonConvert.DeserializeObject<RelationWhisper>(response);
|
||||
if (relationWhisper == null || relationWhisper.Data == null || relationWhisper.Data.List == null)
|
||||
{
|
||||
return null;
|
||||
@@ -172,15 +170,15 @@ public static class UserRelation
|
||||
/// <param name="pn">页码</param>
|
||||
/// <param name="ps">每页项数</param>
|
||||
/// <returns></returns>
|
||||
public static List<RelationFollowInfo> GetBlacks(int pn, int ps)
|
||||
public static List<RelationFollowInfo>? GetBlacks(int pn, int ps)
|
||||
{
|
||||
string url = $"https://api.bilibili.com/x/relation/blacks?pn={pn}&ps={ps}";
|
||||
string referer = "https://www.bilibili.com";
|
||||
string response = WebClient.RequestWeb(url, referer);
|
||||
var url = $"https://api.bilibili.com/x/relation/blacks?pn={pn}&ps={ps}";
|
||||
const string referer = "https://www.bilibili.com";
|
||||
var response = WebClient.RequestWeb(url, referer);
|
||||
|
||||
try
|
||||
{
|
||||
RelationBlack relationBlack = JsonConvert.DeserializeObject<RelationBlack>(response);
|
||||
var relationBlack = JsonConvert.DeserializeObject<RelationBlack>(response);
|
||||
if (relationBlack == null || relationBlack.Data == null)
|
||||
{
|
||||
return null;
|
||||
@@ -202,11 +200,11 @@ public static class UserRelation
|
||||
/// 查询关注分组列表
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static List<FollowingGroup> GetFollowingGroup()
|
||||
public static List<FollowingGroup>? GetFollowingGroup()
|
||||
{
|
||||
string url = $"https://api.bilibili.com/x/relation/tags";
|
||||
string referer = "https://www.bilibili.com";
|
||||
string response = WebClient.RequestWeb(url, referer);
|
||||
const string url = $"https://api.bilibili.com/x/relation/tags";
|
||||
const string referer = "https://www.bilibili.com";
|
||||
var response = WebClient.RequestWeb(url, referer);
|
||||
|
||||
try
|
||||
{
|
||||
@@ -234,23 +232,23 @@ public static class UserRelation
|
||||
/// <param name="ps">每页项数</param>
|
||||
/// <param name="order">排序方式</param>
|
||||
/// <returns></returns>
|
||||
public static List<RelationFollowInfo> GetFollowingGroupContent(long tagId, int pn, int ps,
|
||||
public static List<RelationFollowInfo>? GetFollowingGroupContent(long tagId, int pn, int ps,
|
||||
FollowingOrder order = FollowingOrder.DEFAULT)
|
||||
{
|
||||
string orderType = "";
|
||||
var orderType = "";
|
||||
if (order == FollowingOrder.ATTENTION)
|
||||
{
|
||||
orderType = "attention";
|
||||
}
|
||||
|
||||
string url =
|
||||
var url =
|
||||
$"https://api.bilibili.com/x/relation/tag?tagid={tagId}&pn={pn}&ps={ps}&order_type={orderType}";
|
||||
string referer = "https://www.bilibili.com";
|
||||
string response = WebClient.RequestWeb(url, referer);
|
||||
const string referer = "https://www.bilibili.com";
|
||||
var response = WebClient.RequestWeb(url, referer);
|
||||
|
||||
try
|
||||
{
|
||||
FollowingGroupContent content = JsonConvert.DeserializeObject<FollowingGroupContent>(response);
|
||||
var content = JsonConvert.DeserializeObject<FollowingGroupContent>(response);
|
||||
if (content == null || content.Data == null)
|
||||
{
|
||||
return null;
|
||||
@@ -275,13 +273,13 @@ public static class UserRelation
|
||||
public static List<RelationFollowInfo> GetAllFollowingGroupContent(int tagId,
|
||||
FollowingOrder order = FollowingOrder.DEFAULT)
|
||||
{
|
||||
List<RelationFollowInfo> result = new List<RelationFollowInfo>();
|
||||
var result = new List<RelationFollowInfo>();
|
||||
|
||||
int i = 0;
|
||||
var i = 0;
|
||||
while (true)
|
||||
{
|
||||
i++;
|
||||
int ps = 50;
|
||||
const int ps = 50;
|
||||
|
||||
var data = GetFollowingGroupContent(tagId, i, ps, order);
|
||||
if (data == null || data.Count == 0)
|
||||
|
||||
@@ -43,7 +43,7 @@ public static class UserSpace
|
||||
/// </summary>
|
||||
/// <param name="mid">用户id</param>
|
||||
/// <returns></returns>
|
||||
public static List<SpacePublicationListTypeVideoZone?> GetPublicationType(long mid)
|
||||
public static List<SpacePublicationListTypeVideoZone>? GetPublicationType(long mid)
|
||||
{
|
||||
const int pn = 1;
|
||||
const int ps = 1;
|
||||
@@ -54,22 +54,24 @@ public static class UserSpace
|
||||
/// <summary>
|
||||
/// 获取用户投稿视频的所有分区
|
||||
/// </summary>
|
||||
/// <param name="mid">用户id</param>
|
||||
/// <param name="publication"></param>
|
||||
/// <returns></returns>
|
||||
public static List<SpacePublicationListTypeVideoZone?> GetPublicationType(SpacePublicationList? publication)
|
||||
public static List<SpacePublicationListTypeVideoZone>? GetPublicationType(SpacePublicationList? publication)
|
||||
{
|
||||
if (publication?.Tlist == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var result = new List<SpacePublicationListTypeVideoZone?>();
|
||||
var result = new List<SpacePublicationListTypeVideoZone>();
|
||||
var typeList = JObject.Parse(publication.Tlist.ToString("N"));
|
||||
foreach (KeyValuePair<string, JToken> item in typeList)
|
||||
{
|
||||
var value = JsonConvert.DeserializeObject<SpacePublicationListTypeVideoZone>(item.Value.ToString());
|
||||
result.Add(value);
|
||||
if (value != null)
|
||||
result.Add(value);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -93,7 +95,9 @@ public static class UserSpace
|
||||
|
||||
var data = GetPublication(mid, i, ps, tid, order, keyword);
|
||||
if (data?.Vlist == null || data.Vlist.Count == 0)
|
||||
{ break; }
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
result.AddRange(data.Vlist);
|
||||
}
|
||||
@@ -113,7 +117,7 @@ public static class UserSpace
|
||||
/// <returns></returns>
|
||||
public static SpacePublicationList? GetPublication(long mid, int pn, int ps, long tid = 0, PublicationOrder order = PublicationOrder.PUBDATE, string keyword = "")
|
||||
{
|
||||
var parameters = new Dictionary<string, object>
|
||||
var parameters = new Dictionary<string, object?>
|
||||
{
|
||||
{ "mid", mid },
|
||||
{ "pn", pn },
|
||||
@@ -134,8 +138,7 @@ public static class UserSpace
|
||||
{
|
||||
Error = (sender, args) =>
|
||||
{
|
||||
if (Equals(args.ErrorContext.Member, "play") &&
|
||||
args.ErrorContext.OriginalObject.GetType() == typeof(SpacePublicationListVideo))
|
||||
if (Equals(args.ErrorContext.Member, "play") && args.ErrorContext.OriginalObject?.GetType() == typeof(SpacePublicationListVideo))
|
||||
{
|
||||
args.ErrorContext.Handled = true;
|
||||
}
|
||||
@@ -199,10 +202,13 @@ public static class UserSpace
|
||||
|
||||
var data = GetChannelVideoList(mid, cid, i, ps);
|
||||
if (data?.Count == 0)
|
||||
{ break; }
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
result.AddRange(data);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -348,6 +354,7 @@ public static class UserSpace
|
||||
#endregion
|
||||
|
||||
#region 课程
|
||||
|
||||
/// <summary>
|
||||
/// 查询用户发布的课程列表
|
||||
/// </summary>
|
||||
@@ -387,14 +394,17 @@ public static class UserSpace
|
||||
while (true)
|
||||
{
|
||||
i++;
|
||||
var ps = 50;
|
||||
const int ps = 50;
|
||||
|
||||
var data = GetCheese(mid, i, ps);
|
||||
if (data == null || data.Count == 0)
|
||||
{ break; }
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
result.AddRange(data);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -447,13 +457,15 @@ public static class UserSpace
|
||||
|
||||
var data = GetBangumiFollow(mid, type, i, ps);
|
||||
if (data?.List == null || data.List.Count == 0)
|
||||
{ break; }
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
result.AddRange(data.List);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
}
|
||||
@@ -15,16 +15,15 @@ public static class UserStatus
|
||||
/// </summary>
|
||||
/// <param name="mid"></param>
|
||||
/// <returns></returns>
|
||||
public static UserRelationStat GetUserRelationStat(long mid)
|
||||
public static UserRelationStat? GetUserRelationStat(long mid)
|
||||
{
|
||||
string url = $"https://api.bilibili.com/x/relation/stat?vmid={mid}";
|
||||
string referer = "https://www.bilibili.com";
|
||||
string response = WebClient.RequestWeb(url, referer);
|
||||
var url = $"https://api.bilibili.com/x/relation/stat?vmid={mid}";
|
||||
const string referer = "https://www.bilibili.com";
|
||||
var response = WebClient.RequestWeb(url, referer);
|
||||
|
||||
try
|
||||
{
|
||||
UserRelationStatOrigin userRelationStat =
|
||||
JsonConvert.DeserializeObject<UserRelationStatOrigin>(response);
|
||||
var userRelationStat = JsonConvert.DeserializeObject<UserRelationStatOrigin>(response);
|
||||
if (userRelationStat == null || userRelationStat.Data == null)
|
||||
{
|
||||
return null;
|
||||
@@ -47,15 +46,15 @@ public static class UserStatus
|
||||
/// </summary>
|
||||
/// <param name="mid"></param>
|
||||
/// <returns></returns>
|
||||
public static UpStat GetUpStat(long mid)
|
||||
public static UpStat? GetUpStat(long mid)
|
||||
{
|
||||
string url = $"https://api.bilibili.com/x/space/upstat?mid={mid}";
|
||||
string referer = "https://www.bilibili.com";
|
||||
string response = WebClient.RequestWeb(url, referer);
|
||||
var url = $"https://api.bilibili.com/x/space/upstat?mid={mid}";
|
||||
const string referer = "https://www.bilibili.com";
|
||||
var response = WebClient.RequestWeb(url, referer);
|
||||
|
||||
try
|
||||
{
|
||||
UpStatOrigin upStat = JsonConvert.DeserializeObject<UpStatOrigin>(response);
|
||||
var upStat = JsonConvert.DeserializeObject<UpStatOrigin>(response);
|
||||
if (upStat == null || upStat.Data == null)
|
||||
{
|
||||
return null;
|
||||
|
||||
@@ -14,11 +14,11 @@ public static class Dynamic
|
||||
/// <param name="pn">页码</param>
|
||||
/// <param name="ps">每页项数(最大50)</param>
|
||||
/// <returns></returns>
|
||||
public static List<DynamicVideoView> RegionDynamicList(int rid, int pn = 1, int ps = 5)
|
||||
public static List<DynamicVideoView>? RegionDynamicList(int rid, int pn = 1, int ps = 5)
|
||||
{
|
||||
string url = $"https://api.bilibili.com/x/web-interface/dynamic/region?rid={rid}&pn={pn}&ps={ps}";
|
||||
string referer = "https://www.bilibili.com";
|
||||
string response = WebClient.RequestWeb(url, referer);
|
||||
var url = $"https://api.bilibili.com/x/web-interface/dynamic/region?rid={rid}&pn={pn}&ps={ps}";
|
||||
const string referer = "https://www.bilibili.com";
|
||||
var response = WebClient.RequestWeb(url, referer);
|
||||
|
||||
try
|
||||
{
|
||||
@@ -27,10 +27,8 @@ public static class Dynamic
|
||||
{
|
||||
return dynamic.Data.Archives;
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
|
||||
@@ -14,11 +14,11 @@ public static class Ranking
|
||||
/// <param name="day">3日榜或周榜(3/7)</param>
|
||||
/// <param name="original"></param>
|
||||
/// <returns></returns>
|
||||
public static List<RankingVideoView> RegionRankingList(int rid, int day = 3, int original = 0)
|
||||
public static List<RankingVideoView>? RegionRankingList(int rid, int day = 3, int original = 0)
|
||||
{
|
||||
string url = $"https://api.bilibili.com/x/web-interface/ranking/region?rid={rid}&day={day}&ps={original}";
|
||||
string referer = "https://www.bilibili.com";
|
||||
string response = WebClient.RequestWeb(url, referer);
|
||||
var url = $"https://api.bilibili.com/x/web-interface/ranking/region?rid={rid}&day={day}&ps={original}";
|
||||
const string referer = "https://www.bilibili.com";
|
||||
var response = WebClient.RequestWeb(url, referer);
|
||||
|
||||
try
|
||||
{
|
||||
@@ -27,10 +27,8 @@ public static class Ranking
|
||||
{
|
||||
return ranking.Data;
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
|
||||
@@ -18,7 +18,7 @@ public static class VideoInfo
|
||||
{
|
||||
// https://api.bilibili.com/x/web-interface/view/detail?bvid=BV1Sg411F7cb&aid=969147110&need_operation_card=1&web_rm_repeat=1&need_elec=1&out_referer=https%3A%2F%2Fspace.bilibili.com%2F42018135%2Ffavlist%3Ffid%3D94341835
|
||||
|
||||
var parameters = new Dictionary<string, object>();
|
||||
var parameters = new Dictionary<string, object?>();
|
||||
if (bvid != null)
|
||||
{
|
||||
parameters.Add("bvid", bvid);
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
public enum PlayStreamType
|
||||
{
|
||||
VIDEO = 1, // 普通视频
|
||||
BANGUMI, // 番剧、电影、电视剧等
|
||||
CHEESE, // 课程
|
||||
Video = 1, // 普通视频
|
||||
Bangumi, // 番剧、电影、电视剧等
|
||||
Cheese, // 课程
|
||||
}
|
||||
@@ -17,14 +17,24 @@ public static class VideoStream
|
||||
/// <param name="bvid"></param>
|
||||
/// <param name="cid"></param>
|
||||
/// <returns></returns>
|
||||
public static PlayerV2 PlayerV2(long avid, string bvid, long cid)
|
||||
public static PlayerV2? PlayerV2(long avid, string? bvid, long cid)
|
||||
{
|
||||
var parameters = new Dictionary<string, object>
|
||||
var parameters = new Dictionary<string, object?>();
|
||||
|
||||
if (avid > 0)
|
||||
{
|
||||
{ "avid", avid },
|
||||
{ "bvid", bvid },
|
||||
{ "cid", cid },
|
||||
};
|
||||
parameters.Add("bvid", bvid);
|
||||
}
|
||||
|
||||
if (bvid != null)
|
||||
{
|
||||
parameters.Add("avid", avid);
|
||||
}
|
||||
else if (cid > 0)
|
||||
{
|
||||
parameters.Add("cid", cid);
|
||||
}
|
||||
|
||||
var query = WbiSign.ParametersToQuery(WbiSign.EncodeWbi(parameters));
|
||||
var url = $"https://api.bilibili.com/x/player/wbi/v2?{query}";
|
||||
const string referer = "https://www.bilibili.com";
|
||||
@@ -51,7 +61,7 @@ public static class VideoStream
|
||||
/// <param name="bvid"></param>
|
||||
/// <param name="cid"></param>
|
||||
/// <returns></returns>
|
||||
public static List<SubRipText> GetSubtitle(long avid, string bvid, long cid)
|
||||
public static List<SubRipText>? GetSubtitle(long avid, string? bvid, long cid)
|
||||
{
|
||||
var subRipTexts = new List<SubRipText>();
|
||||
|
||||
@@ -107,7 +117,7 @@ public static class VideoStream
|
||||
/// <returns></returns>
|
||||
public static PlayUrl GetVideoPlayUrl(long avid, string bvid, long cid, int quality = 125)
|
||||
{
|
||||
var parameters = new Dictionary<string, object>
|
||||
var parameters = new Dictionary<string, object?>
|
||||
{
|
||||
{ "fourk", 1 },
|
||||
{ "fnver", 0 },
|
||||
@@ -242,11 +252,13 @@ public static class VideoStream
|
||||
{
|
||||
return null;
|
||||
}
|
||||
else if (playUrl.Data != null)
|
||||
|
||||
if (playUrl.Data != null)
|
||||
{
|
||||
return playUrl.Data;
|
||||
}
|
||||
else if (playUrl.Result != null)
|
||||
|
||||
if (playUrl.Result != null)
|
||||
{
|
||||
return playUrl.Result;
|
||||
}
|
||||
@@ -287,18 +299,18 @@ public static class VideoStream
|
||||
{
|
||||
return null;
|
||||
}
|
||||
else if (playUrl.Data != null)
|
||||
|
||||
if (playUrl.Data != null)
|
||||
{
|
||||
return playUrl.Data;
|
||||
}
|
||||
else if (playUrl.Result != null)
|
||||
|
||||
if (playUrl.Result != null)
|
||||
{
|
||||
return playUrl.Result;
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
using System.IO.Compression;
|
||||
using System.Net;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
using DownKyi.Core.BiliApi.Login;
|
||||
using DownKyi.Core.Logging;
|
||||
using DownKyi.Core.Settings;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace DownKyi.Core.BiliApi;
|
||||
|
||||
@@ -12,15 +13,15 @@ internal static class WebClient
|
||||
{
|
||||
internal class SpiOrigin
|
||||
{
|
||||
[JsonProperty("data")] public Spi? Data { get; set; }
|
||||
public int Code { get; set; }
|
||||
public string? Message { get; set; }
|
||||
[JsonPropertyName("data")] public Spi? Data { get; init; }
|
||||
public int Code { get; init; }
|
||||
public string? Message { get; init; }
|
||||
}
|
||||
|
||||
internal class Spi
|
||||
{
|
||||
[JsonProperty("b_3")] public string? Bvuid3 { get; set; }
|
||||
[JsonProperty("b_4")] public string? Bvuid4 { get; set; }
|
||||
[JsonPropertyName("b_3")] public string? Bvuid3 { get; set; }
|
||||
[JsonPropertyName("b_4")] public string? Bvuid4 { get; set; }
|
||||
}
|
||||
|
||||
private static string? _bvuid3 = string.Empty;
|
||||
@@ -44,7 +45,7 @@ internal static class WebClient
|
||||
{
|
||||
const string url = "https://api.bilibili.com/x/frontend/finger/spi";
|
||||
var response = RequestWeb(url);
|
||||
var spi = JsonConvert.DeserializeObject<SpiOrigin>(response);
|
||||
var spi = JsonSerializer.Deserialize<SpiOrigin>(response);
|
||||
_bvuid3 = spi?.Data?.Bvuid3;
|
||||
_bvuid4 = spi?.Data?.Bvuid4;
|
||||
}
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace DownKyi.Core.BiliApi.Zone;
|
||||
namespace DownKyi.Core.BiliApi.Zone;
|
||||
|
||||
public class VideoZone
|
||||
{
|
||||
private static VideoZone that;
|
||||
private readonly List<ZoneAttr> zones = new();
|
||||
private static VideoZone? _that;
|
||||
private readonly List<ZoneAttr> _zones = new();
|
||||
|
||||
/// <summary>
|
||||
/// 使用单例模式获取分区,注意未搜索到的分区需要额外处理
|
||||
@@ -13,17 +11,12 @@ public class VideoZone
|
||||
/// <returns></returns>
|
||||
public static VideoZone Instance()
|
||||
{
|
||||
if (that == null)
|
||||
{
|
||||
that = new VideoZone();
|
||||
}
|
||||
|
||||
return that;
|
||||
return _that ??= new VideoZone();
|
||||
}
|
||||
|
||||
public List<ZoneAttr> GetZones()
|
||||
{
|
||||
return zones;
|
||||
return _zones;
|
||||
}
|
||||
|
||||
private VideoZone()
|
||||
@@ -31,185 +24,180 @@ public class VideoZone
|
||||
// SpacePublicationListType类需要同步更新
|
||||
|
||||
//动画
|
||||
zones.Add(new ZoneAttr(1, "douga", "动画")); // 主分区
|
||||
zones.Add(new ZoneAttr(24, "mad", "MAD·AMV", 1)); // 具有一定制作程度的动画或静画的二次创作视频
|
||||
zones.Add(new ZoneAttr(25, "mmd", "MMD·3D", 1)); // 使用MMD(MikuMikuDance)和其他3D建模类软件制作的视频
|
||||
zones.Add(new ZoneAttr(47, "voice", "短片·手书·配音", 1)); // 追求个人特色和创意表达的自制动画短片、手书(绘)及ACGN相关配音
|
||||
zones.Add(new ZoneAttr(210, "garage_kit", "手办·模玩", 1)); // 手办模玩的测评、改造或其他衍生内容
|
||||
zones.Add(new ZoneAttr(86, "tokusatsu", "特摄", 1)); // 特摄相关衍生视频
|
||||
zones.Add(new ZoneAttr(253, "acgntalks", "动漫杂谈", 1)); // 以谈话形式对ACGN文化圈进行的鉴赏、吐槽、评点、解说、推荐、科普等内容
|
||||
zones.Add(new ZoneAttr(27, "other", "综合", 1)); // 以动画及动画相关内容为素材,包括但不仅限于音频替换、恶搞改编、排行榜等内容
|
||||
_zones.Add(new ZoneAttr(1, "douga", "动画")); // 主分区
|
||||
_zones.Add(new ZoneAttr(24, "mad", "MAD·AMV", 1)); // 具有一定制作程度的动画或静画的二次创作视频
|
||||
_zones.Add(new ZoneAttr(25, "mmd", "MMD·3D", 1)); // 使用MMD(MikuMikuDance)和其他3D建模类软件制作的视频
|
||||
_zones.Add(new ZoneAttr(47, "voice", "短片·手书·配音", 1)); // 追求个人特色和创意表达的自制动画短片、手书(绘)及ACGN相关配音
|
||||
_zones.Add(new ZoneAttr(210, "garage_kit", "手办·模玩", 1)); // 手办模玩的测评、改造或其他衍生内容
|
||||
_zones.Add(new ZoneAttr(86, "tokusatsu", "特摄", 1)); // 特摄相关衍生视频
|
||||
_zones.Add(new ZoneAttr(253, "acgntalks", "动漫杂谈", 1)); // 以谈话形式对ACGN文化圈进行的鉴赏、吐槽、评点、解说、推荐、科普等内容
|
||||
_zones.Add(new ZoneAttr(27, "other", "综合", 1)); // 以动画及动画相关内容为素材,包括但不仅限于音频替换、恶搞改编、排行榜等内容
|
||||
|
||||
//番剧
|
||||
zones.Add(new ZoneAttr(13, "anime", "番剧")); // 主分区
|
||||
zones.Add(new ZoneAttr(33, "serial", "连载动画", 13)); // 当季连载的动画番剧
|
||||
zones.Add(new ZoneAttr(32, "finish", "完结动画", 13)); // 已完结的动画番剧合集
|
||||
zones.Add(new ZoneAttr(51, "information", "资讯", 13)); // 动画番剧相关资讯视频
|
||||
zones.Add(new ZoneAttr(152, "offical", "官方延伸", 13)); // 动画番剧为主题的宣传节目、采访视频,及声优相关视频
|
||||
_zones.Add(new ZoneAttr(13, "anime", "番剧")); // 主分区
|
||||
_zones.Add(new ZoneAttr(33, "serial", "连载动画", 13)); // 当季连载的动画番剧
|
||||
_zones.Add(new ZoneAttr(32, "finish", "完结动画", 13)); // 已完结的动画番剧合集
|
||||
_zones.Add(new ZoneAttr(51, "information", "资讯", 13)); // 动画番剧相关资讯视频
|
||||
_zones.Add(new ZoneAttr(152, "offical", "官方延伸", 13)); // 动画番剧为主题的宣传节目、采访视频,及声优相关视频
|
||||
|
||||
//国创
|
||||
zones.Add(new ZoneAttr(167, "guochuang", "国创")); // 主分区
|
||||
zones.Add(new ZoneAttr(153, "chinese", "国产动画", 167)); // 我国出品的PGC动画
|
||||
zones.Add(new ZoneAttr(168, "original", "国产原创相关", 167)); //
|
||||
zones.Add(new ZoneAttr(169, "puppetry", "布袋戏", 167)); //
|
||||
zones.Add(new ZoneAttr(195, "motioncomic", "动态漫·广播剧", 167)); //
|
||||
zones.Add(new ZoneAttr(170, "information", "资讯", 167)); //
|
||||
_zones.Add(new ZoneAttr(167, "guochuang", "国创")); // 主分区
|
||||
_zones.Add(new ZoneAttr(153, "chinese", "国产动画", 167)); // 我国出品的PGC动画
|
||||
_zones.Add(new ZoneAttr(168, "original", "国产原创相关", 167)); //
|
||||
_zones.Add(new ZoneAttr(169, "puppetry", "布袋戏", 167)); //
|
||||
_zones.Add(new ZoneAttr(195, "motioncomic", "动态漫·广播剧", 167)); //
|
||||
_zones.Add(new ZoneAttr(170, "information", "资讯", 167)); //
|
||||
|
||||
//音乐
|
||||
zones.Add(new ZoneAttr(3, "music", "音乐")); // 主分区
|
||||
zones.Add(new ZoneAttr(28, "original", "原创音乐", 3)); // 原创歌曲及纯音乐,包括改编、重编曲及remix
|
||||
zones.Add(new ZoneAttr(31, "cover", "翻唱", 3)); // 对曲目的人声再演绎视频
|
||||
zones.Add(new ZoneAttr(59, "perform", "演奏", 3)); // 乐器和非传统乐器器材的演奏作品
|
||||
zones.Add(new ZoneAttr(30, "vocaloid", "VOCALOID·UTAU", 3)); // 以VOCALOID等歌声合成引擎为基础,运用各类音源进行的创作
|
||||
zones.Add(new ZoneAttr(29, "live", "音乐现场", 3)); // 音乐表演的实况视频,包括官方/个人拍摄的综艺节目、音乐剧、音乐节、演唱会等
|
||||
zones.Add(new ZoneAttr(193, "mv", "MV", 3)); // 为音乐作品配合拍摄或制作的音乐录影带(Music Video),以及自制拍摄、剪辑、翻拍MV
|
||||
zones.Add(new ZoneAttr(243, "commentary", "乐评盘点", 3)); // 音乐类新闻、盘点、点评、reaction、榜单、采访、幕后故事、唱片开箱等
|
||||
zones.Add(new ZoneAttr(244, "tutorial", "音乐教学", 3)); // 以音乐教学为目的的内容
|
||||
zones.Add(new ZoneAttr(130, "other", "音乐综合", 3)); // 所有无法被收纳到其他音乐二级分区的音乐类视频
|
||||
_zones.Add(new ZoneAttr(3, "music", "音乐")); // 主分区
|
||||
_zones.Add(new ZoneAttr(28, "original", "原创音乐", 3)); // 原创歌曲及纯音乐,包括改编、重编曲及remix
|
||||
_zones.Add(new ZoneAttr(31, "cover", "翻唱", 3)); // 对曲目的人声再演绎视频
|
||||
_zones.Add(new ZoneAttr(59, "perform", "演奏", 3)); // 乐器和非传统乐器器材的演奏作品
|
||||
_zones.Add(new ZoneAttr(30, "vocaloid", "VOCALOID·UTAU", 3)); // 以VOCALOID等歌声合成引擎为基础,运用各类音源进行的创作
|
||||
_zones.Add(new ZoneAttr(29, "live", "音乐现场", 3)); // 音乐表演的实况视频,包括官方/个人拍摄的综艺节目、音乐剧、音乐节、演唱会等
|
||||
_zones.Add(new ZoneAttr(193, "mv", "MV", 3)); // 为音乐作品配合拍摄或制作的音乐录影带(Music Video),以及自制拍摄、剪辑、翻拍MV
|
||||
_zones.Add(new ZoneAttr(243, "commentary", "乐评盘点", 3)); // 音乐类新闻、盘点、点评、reaction、榜单、采访、幕后故事、唱片开箱等
|
||||
_zones.Add(new ZoneAttr(244, "tutorial", "音乐教学", 3)); // 以音乐教学为目的的内容
|
||||
_zones.Add(new ZoneAttr(130, "other", "音乐综合", 3)); // 所有无法被收纳到其他音乐二级分区的音乐类视频
|
||||
|
||||
//舞蹈
|
||||
zones.Add(new ZoneAttr(129, "dance", "舞蹈")); // 主分区
|
||||
zones.Add(new ZoneAttr(20, "otaku", "宅舞", 129)); // 与ACG相关的翻跳、原创舞蹈
|
||||
zones.Add(new ZoneAttr(198, "hiphop", "街舞", 129)); // 收录街舞相关内容,包括赛事现场、舞室作品、个人翻跳、FREESTYLE等
|
||||
zones.Add(new ZoneAttr(199, "star", "明星舞蹈", 129)); // 国内外明星发布的官方舞蹈及其翻跳内容
|
||||
zones.Add(new ZoneAttr(200, "china", "中国舞", 129)); // 传承中国艺术文化的舞蹈内容,包括古典舞、民族民间舞、汉唐舞、古风舞等
|
||||
zones.Add(new ZoneAttr(154, "three_d", "舞蹈综合", 129)); // 收录无法定义到其他舞蹈子分区的舞蹈视频
|
||||
zones.Add(new ZoneAttr(156, "demo", "舞蹈教程", 129)); // 镜面慢速,动作分解,基础教程等具有教学意义的舞蹈视频
|
||||
_zones.Add(new ZoneAttr(129, "dance", "舞蹈")); // 主分区
|
||||
_zones.Add(new ZoneAttr(20, "otaku", "宅舞", 129)); // 与ACG相关的翻跳、原创舞蹈
|
||||
_zones.Add(new ZoneAttr(198, "hiphop", "街舞", 129)); // 收录街舞相关内容,包括赛事现场、舞室作品、个人翻跳、FREESTYLE等
|
||||
_zones.Add(new ZoneAttr(199, "star", "明星舞蹈", 129)); // 国内外明星发布的官方舞蹈及其翻跳内容
|
||||
_zones.Add(new ZoneAttr(200, "china", "中国舞", 129)); // 传承中国艺术文化的舞蹈内容,包括古典舞、民族民间舞、汉唐舞、古风舞等
|
||||
_zones.Add(new ZoneAttr(154, "three_d", "舞蹈综合", 129)); // 收录无法定义到其他舞蹈子分区的舞蹈视频
|
||||
_zones.Add(new ZoneAttr(156, "demo", "舞蹈教程", 129)); // 镜面慢速,动作分解,基础教程等具有教学意义的舞蹈视频
|
||||
|
||||
//游戏
|
||||
zones.Add(new ZoneAttr(4, "game", "游戏")); // 主分区
|
||||
zones.Add(new ZoneAttr(17, "stand_alone", "单机游戏",
|
||||
4)); // 以所有平台(PC、主机、移动端)的单机或联机游戏为主的视频内容,包括游戏预告、CG、实况解说及相关的评测、杂谈与视频剪辑等
|
||||
zones.Add(new ZoneAttr(171, "esports", "电子竞技", 4)); // 具有高对抗性的电子竞技游戏项目,其相关的赛事、实况、攻略、解说、短剧等视频。
|
||||
zones.Add(new ZoneAttr(172, "mobile", "手机游戏", 4)); // 以手机及平板设备为主要平台的游戏,其相关的实况、攻略、解说、短剧、演示等视频。
|
||||
zones.Add(new ZoneAttr(65, "online", "网络游戏", 4)); // 由网络运营商运营的多人在线游戏,以及电子竞技的相关游戏内容。包括赛事、攻略、实况、解说等相关视频
|
||||
zones.Add(new ZoneAttr(173, "board", "桌游棋牌", 4)); // 桌游、棋牌、卡牌对战等及其相关电子版游戏的实况、攻略、解说、演示等视频。
|
||||
zones.Add(new ZoneAttr(121, "gmv", "GMV", 4)); // 由游戏素材制作的MV视频。以游戏内容或CG为主制作的,具有一定创作程度的MV类型的视频
|
||||
zones.Add(new ZoneAttr(136, "music", "音游", 4)); // 各个平台上,通过配合音乐与节奏而进行的音乐类游戏视频
|
||||
zones.Add(new ZoneAttr(19, "mugen", "Mugen", 4)); // 以Mugen引擎为平台制作、或与Mugen相关的游戏视频
|
||||
_zones.Add(new ZoneAttr(4, "game", "游戏")); // 主分区
|
||||
_zones.Add(new ZoneAttr(17, "stand_alone", "单机游戏", 4)); // 以所有平台(PC、主机、移动端)的单机或联机游戏为主的视频内容,包括游戏预告、CG、实况解说及相关的评测、杂谈与视频剪辑等
|
||||
_zones.Add(new ZoneAttr(171, "esports", "电子竞技", 4)); // 具有高对抗性的电子竞技游戏项目,其相关的赛事、实况、攻略、解说、短剧等视频。
|
||||
_zones.Add(new ZoneAttr(172, "mobile", "手机游戏", 4)); // 以手机及平板设备为主要平台的游戏,其相关的实况、攻略、解说、短剧、演示等视频。
|
||||
_zones.Add(new ZoneAttr(65, "online", "网络游戏", 4)); // 由网络运营商运营的多人在线游戏,以及电子竞技的相关游戏内容。包括赛事、攻略、实况、解说等相关视频
|
||||
_zones.Add(new ZoneAttr(173, "board", "桌游棋牌", 4)); // 桌游、棋牌、卡牌对战等及其相关电子版游戏的实况、攻略、解说、演示等视频。
|
||||
_zones.Add(new ZoneAttr(121, "gmv", "GMV", 4)); // 由游戏素材制作的MV视频。以游戏内容或CG为主制作的,具有一定创作程度的MV类型的视频
|
||||
_zones.Add(new ZoneAttr(136, "music", "音游", 4)); // 各个平台上,通过配合音乐与节奏而进行的音乐类游戏视频
|
||||
_zones.Add(new ZoneAttr(19, "mugen", "Mugen", 4)); // 以Mugen引擎为平台制作、或与Mugen相关的游戏视频
|
||||
|
||||
//知识
|
||||
zones.Add(new ZoneAttr(36, "knowledge", "知识")); // 主分区
|
||||
zones.Add(new ZoneAttr(201, "science", "科学科普", 36)); // 回答你的十万个为什么
|
||||
zones.Add(new ZoneAttr(124, "social_science", "社科·法律·心理", 36)); // 基于社会科学、法学、心理学展开或个人观点输出的知识视频
|
||||
zones.Add(new ZoneAttr(228, "humanity_history", "人文历史", 36)); // 看看古今人物,聊聊历史过往,品品文学典籍
|
||||
zones.Add(new ZoneAttr(207, "business", "财经商业", 36)); // 说金融市场,谈宏观经济,一起畅聊商业故事
|
||||
zones.Add(new ZoneAttr(208, "campus", "校园学习", 36)); // 老师很有趣,学生也有才,我们一起搞学习
|
||||
zones.Add(new ZoneAttr(209, "career", "职业职场", 36)); // 职业分享、升级指南,一起成为最有料的职场人
|
||||
zones.Add(new ZoneAttr(229, "design", "设计·创意", 36)); // 天马行空,创意设计,都在这里
|
||||
zones.Add(new ZoneAttr(122, "skill", "野生技能协会", 36)); // 技能党集合,是时候展示真正的技术了
|
||||
_zones.Add(new ZoneAttr(36, "knowledge", "知识")); // 主分区
|
||||
_zones.Add(new ZoneAttr(201, "science", "科学科普", 36)); // 回答你的十万个为什么
|
||||
_zones.Add(new ZoneAttr(124, "social_science", "社科·法律·心理", 36)); // 基于社会科学、法学、心理学展开或个人观点输出的知识视频
|
||||
_zones.Add(new ZoneAttr(228, "humanity_history", "人文历史", 36)); // 看看古今人物,聊聊历史过往,品品文学典籍
|
||||
_zones.Add(new ZoneAttr(207, "business", "财经商业", 36)); // 说金融市场,谈宏观经济,一起畅聊商业故事
|
||||
_zones.Add(new ZoneAttr(208, "campus", "校园学习", 36)); // 老师很有趣,学生也有才,我们一起搞学习
|
||||
_zones.Add(new ZoneAttr(209, "career", "职业职场", 36)); // 职业分享、升级指南,一起成为最有料的职场人
|
||||
_zones.Add(new ZoneAttr(229, "design", "设计·创意", 36)); // 天马行空,创意设计,都在这里
|
||||
_zones.Add(new ZoneAttr(122, "skill", "野生技能协会", 36)); // 技能党集合,是时候展示真正的技术了
|
||||
|
||||
//科技
|
||||
zones.Add(new ZoneAttr(188, "tech", "科技")); // 主分区
|
||||
zones.Add(new ZoneAttr(95, "digital", "数码", 188)); // 科技数码产品大全,一起来做发烧友
|
||||
zones.Add(new ZoneAttr(230, "application", "软件应用", 188)); // 超全软件应用指南
|
||||
zones.Add(new ZoneAttr(231, "computer_tech", "计算机技术", 188)); // 研究分析、教学演示、经验分享......有关计算机技术的都在这里
|
||||
zones.Add(new ZoneAttr(232, "industry", "科工机械", 188)); // 从小芯片到大工程,一起见证科工力量
|
||||
zones.Add(new ZoneAttr(233, "diy", "极客DIY", 188)); // 炫酷技能,极客文化,硬核技巧,准备好你的惊讶
|
||||
_zones.Add(new ZoneAttr(188, "tech", "科技")); // 主分区
|
||||
_zones.Add(new ZoneAttr(95, "digital", "数码", 188)); // 科技数码产品大全,一起来做发烧友
|
||||
_zones.Add(new ZoneAttr(230, "application", "软件应用", 188)); // 超全软件应用指南
|
||||
_zones.Add(new ZoneAttr(231, "computer_tech", "计算机技术", 188)); // 研究分析、教学演示、经验分享......有关计算机技术的都在这里
|
||||
_zones.Add(new ZoneAttr(232, "industry", "科工机械", 188)); // 从小芯片到大工程,一起见证科工力量
|
||||
_zones.Add(new ZoneAttr(233, "diy", "极客DIY", 188)); // 炫酷技能,极客文化,硬核技巧,准备好你的惊讶
|
||||
|
||||
//运动
|
||||
zones.Add(new ZoneAttr(234, "sports", "运动")); // 主分区
|
||||
zones.Add(new ZoneAttr(235, "basketball", "篮球", 234)); // 与篮球相关的视频,包括但不限于篮球赛事、教学、评述、剪辑、剧情等相关内容
|
||||
zones.Add(new ZoneAttr(249, "football", "足球", 234)); // 与足球相关的视频,包括但不限于足球赛事、教学、评述、剪辑、剧情等相关内容
|
||||
zones.Add(new ZoneAttr(164, "aerobics", "健身", 234)); // 与健身相关的视频,包括但不限于瑜伽、CrossFit、健美、力量举、普拉提、街健等相关内容
|
||||
zones.Add(new ZoneAttr(236, "athletic", "竞技体育",
|
||||
234)); // 与竞技体育相关的视频,包括但不限于乒乓、羽毛球、排球、赛车等竞技项目的赛事、评述、剪辑、剧情等相关内容
|
||||
zones.Add(new ZoneAttr(237, "culture", "运动文化",
|
||||
234)); // 与运动文化相关的视频,包络但不限于球鞋、球衣、球星卡等运动衍生品的分享、解读,体育产业的分析、科普等相关内容
|
||||
zones.Add(new ZoneAttr(238, "comprehensive", "运动综合", 234)); // 与运动综合相关的视频,包括但不限于钓鱼、骑行、滑板等日常运动分享、教学、Vlog等相关内容
|
||||
_zones.Add(new ZoneAttr(234, "sports", "运动")); // 主分区
|
||||
_zones.Add(new ZoneAttr(235, "basketball", "篮球", 234)); // 与篮球相关的视频,包括但不限于篮球赛事、教学、评述、剪辑、剧情等相关内容
|
||||
_zones.Add(new ZoneAttr(249, "football", "足球", 234)); // 与足球相关的视频,包括但不限于足球赛事、教学、评述、剪辑、剧情等相关内容
|
||||
_zones.Add(new ZoneAttr(164, "aerobics", "健身", 234)); // 与健身相关的视频,包括但不限于瑜伽、CrossFit、健美、力量举、普拉提、街健等相关内容
|
||||
_zones.Add(new ZoneAttr(236, "athletic", "竞技体育", 234)); // 与竞技体育相关的视频,包括但不限于乒乓、羽毛球、排球、赛车等竞技项目的赛事、评述、剪辑、剧情等相关内容
|
||||
_zones.Add(new ZoneAttr(237, "culture", "运动文化", 234)); // 与运动文化相关的视频,包络但不限于球鞋、球衣、球星卡等运动衍生品的分享、解读,体育产业的分析、科普等相关内容
|
||||
_zones.Add(new ZoneAttr(238, "comprehensive", "运动综合", 234)); // 与运动综合相关的视频,包括但不限于钓鱼、骑行、滑板等日常运动分享、教学、Vlog等相关内容
|
||||
|
||||
//汽车
|
||||
zones.Add(new ZoneAttr(223, "car", "汽车")); // 主分区
|
||||
zones.Add(new ZoneAttr(245, "racing", "赛车", 223)); // F1等汽车运动相关
|
||||
zones.Add(new ZoneAttr(246, "modifiedvehicle", "改装玩车", 223)); // 汽车文化及改装车相关内容,包括改装车、老车修复介绍、汽车聚会分享等内容
|
||||
zones.Add(new ZoneAttr(247, "newenergyvehicle", "新能源车",
|
||||
223)); // 新能源汽车相关内容,包括电动汽车、混合动力汽车等车型种类,包含不限于新车资讯、试驾体验、专业评测、技术解读、知识科普等内容
|
||||
zones.Add(new ZoneAttr(248, "touringcar", "房车", 223)); // 房车及营地相关内容,包括不限于产品介绍、驾驶体验、房车生活和房车旅行等内容
|
||||
zones.Add(new ZoneAttr(240, "motorcycle", "摩托车", 223)); // 骑士们集合啦
|
||||
zones.Add(new ZoneAttr(227, "strategy", "购车攻略", 223)); // 丰富详实的购车建议和新车体验
|
||||
zones.Add(new ZoneAttr(176, "life", "汽车生活", 223)); // 分享汽车及出行相关的生活体验类视频
|
||||
_zones.Add(new ZoneAttr(223, "car", "汽车")); // 主分区
|
||||
_zones.Add(new ZoneAttr(245, "racing", "赛车", 223)); // F1等汽车运动相关
|
||||
_zones.Add(new ZoneAttr(246, "modifiedvehicle", "改装玩车", 223)); // 汽车文化及改装车相关内容,包括改装车、老车修复介绍、汽车聚会分享等内容
|
||||
_zones.Add(new ZoneAttr(247, "newenergyvehicle", "新能源车", 223)); // 新能源汽车相关内容,包括电动汽车、混合动力汽车等车型种类,包含不限于新车资讯、试驾体验、专业评测、技术解读、知识科普等内容
|
||||
_zones.Add(new ZoneAttr(248, "touringcar", "房车", 223)); // 房车及营地相关内容,包括不限于产品介绍、驾驶体验、房车生活和房车旅行等内容
|
||||
_zones.Add(new ZoneAttr(240, "motorcycle", "摩托车", 223)); // 骑士们集合啦
|
||||
_zones.Add(new ZoneAttr(227, "strategy", "购车攻略", 223)); // 丰富详实的购车建议和新车体验
|
||||
_zones.Add(new ZoneAttr(176, "life", "汽车生活", 223)); // 分享汽车及出行相关的生活体验类视频
|
||||
|
||||
//生活
|
||||
zones.Add(new ZoneAttr(160, "life", "生活")); // 主分区
|
||||
zones.Add(new ZoneAttr(138, "funny", "搞笑", 160)); // 各种沙雕有趣的搞笑剪辑,挑战,表演,配音等视频
|
||||
zones.Add(new ZoneAttr(250, "travel", "Energy", 160)); // 为达到观光游览、休闲娱乐为目的的远途旅行、中近途户外生活、本地探店
|
||||
zones.Add(new ZoneAttr(251, "rurallife", "三农", 160)); // 分享美好农村生活
|
||||
zones.Add(new ZoneAttr(239, "home", "家居房产", 160)); // 与买房、装修、居家生活相关的分享
|
||||
zones.Add(new ZoneAttr(161, "handmake", "手工", 160)); // 手工制品的制作过程或成品展示、教程、测评类视频
|
||||
zones.Add(new ZoneAttr(162, "painting", "绘画", 160)); // 绘画过程或绘画教程,以及绘画相关的所有视频
|
||||
zones.Add(new ZoneAttr(21, "daily", "日常", 160)); // 记录日常生活,分享生活故事
|
||||
_zones.Add(new ZoneAttr(160, "life", "生活")); // 主分区
|
||||
_zones.Add(new ZoneAttr(138, "funny", "搞笑", 160)); // 各种沙雕有趣的搞笑剪辑,挑战,表演,配音等视频
|
||||
_zones.Add(new ZoneAttr(250, "travel", "Energy", 160)); // 为达到观光游览、休闲娱乐为目的的远途旅行、中近途户外生活、本地探店
|
||||
_zones.Add(new ZoneAttr(251, "rurallife", "三农", 160)); // 分享美好农村生活
|
||||
_zones.Add(new ZoneAttr(239, "home", "家居房产", 160)); // 与买房、装修、居家生活相关的分享
|
||||
_zones.Add(new ZoneAttr(161, "handmake", "手工", 160)); // 手工制品的制作过程或成品展示、教程、测评类视频
|
||||
_zones.Add(new ZoneAttr(162, "painting", "绘画", 160)); // 绘画过程或绘画教程,以及绘画相关的所有视频
|
||||
_zones.Add(new ZoneAttr(21, "daily", "日常", 160)); // 记录日常生活,分享生活故事
|
||||
|
||||
//美食
|
||||
zones.Add(new ZoneAttr(211, "food", "美食")); // 主分区
|
||||
zones.Add(new ZoneAttr(76, "make", "美食制作", 211)); // 学做人间美味,展示精湛厨艺
|
||||
zones.Add(new ZoneAttr(212, "detective", "美食侦探", 211)); // 寻找美味餐厅,发现街头美食
|
||||
zones.Add(new ZoneAttr(213, "measurement", "美食测评", 211)); // 吃货世界,品尝世间美味
|
||||
zones.Add(new ZoneAttr(214, "rural", "田园美食", 211)); // 品味乡野美食,寻找山与海的味道
|
||||
zones.Add(new ZoneAttr(215, "record", "美食记录", 211)); // 记录一日三餐,给生活添一点幸福感
|
||||
_zones.Add(new ZoneAttr(211, "food", "美食")); // 主分区
|
||||
_zones.Add(new ZoneAttr(76, "make", "美食制作", 211)); // 学做人间美味,展示精湛厨艺
|
||||
_zones.Add(new ZoneAttr(212, "detective", "美食侦探", 211)); // 寻找美味餐厅,发现街头美食
|
||||
_zones.Add(new ZoneAttr(213, "measurement", "美食测评", 211)); // 吃货世界,品尝世间美味
|
||||
_zones.Add(new ZoneAttr(214, "rural", "田园美食", 211)); // 品味乡野美食,寻找山与海的味道
|
||||
_zones.Add(new ZoneAttr(215, "record", "美食记录", 211)); // 记录一日三餐,给生活添一点幸福感
|
||||
|
||||
//动物圈
|
||||
zones.Add(new ZoneAttr(217, "animal", "动物圈")); // 主分区
|
||||
zones.Add(new ZoneAttr(218, "cat", "喵星人", 217)); // 喵喵喵喵喵
|
||||
zones.Add(new ZoneAttr(219, "dog", "汪星人", 217)); // 汪汪汪汪汪
|
||||
zones.Add(new ZoneAttr(220, "panda", "大熊猫", 217)); // 芝麻汤圆营业中
|
||||
zones.Add(new ZoneAttr(221, "wild_animal", "野生动物", 217)); // 内有“猛兽”出没
|
||||
zones.Add(new ZoneAttr(222, "reptiles", "爬宠", 217)); // 鳞甲有灵
|
||||
zones.Add(new ZoneAttr(75, "animal_composite", "动物综合", 217)); // 收录除上述子分区外,其余动物相关视频以及非动物主体或多个动物主体的动物相关延伸内容
|
||||
_zones.Add(new ZoneAttr(217, "animal", "动物圈")); // 主分区
|
||||
_zones.Add(new ZoneAttr(218, "cat", "喵星人", 217)); // 喵喵喵喵喵
|
||||
_zones.Add(new ZoneAttr(219, "dog", "汪星人", 217)); // 汪汪汪汪汪
|
||||
_zones.Add(new ZoneAttr(220, "panda", "大熊猫", 217)); // 芝麻汤圆营业中
|
||||
_zones.Add(new ZoneAttr(221, "wild_animal", "野生动物", 217)); // 内有“猛兽”出没
|
||||
_zones.Add(new ZoneAttr(222, "reptiles", "爬宠", 217)); // 鳞甲有灵
|
||||
_zones.Add(new ZoneAttr(75, "animal_composite", "动物综合", 217)); // 收录除上述子分区外,其余动物相关视频以及非动物主体或多个动物主体的动物相关延伸内容
|
||||
|
||||
//鬼畜
|
||||
zones.Add(new ZoneAttr(119, "kichiku", "鬼畜")); // 主分区
|
||||
zones.Add(new ZoneAttr(22, "guide", "鬼畜调教", 119)); // 使用素材在音频、画面上做一定处理,达到与BGM一定的同步感
|
||||
zones.Add(new ZoneAttr(26, "mad", "音MAD", 119)); // 使用素材音频进行一定的二次创作来达到还原原曲的非商业性质稿件
|
||||
zones.Add(new ZoneAttr(126, "manual_vocaloid", "人力VOCALOID",
|
||||
119)); // 将人物或者角色的无伴奏素材进行人工调音,使其就像VOCALOID一样歌唱的技术
|
||||
zones.Add(new ZoneAttr(216, "theatre", "鬼畜剧场", 119)); // 使用素材进行人工剪辑编排的有剧情的作品
|
||||
zones.Add(new ZoneAttr(127, "course", "教程演示", 119)); // 鬼畜相关的教程演示
|
||||
_zones.Add(new ZoneAttr(119, "kichiku", "鬼畜")); // 主分区
|
||||
_zones.Add(new ZoneAttr(22, "guide", "鬼畜调教", 119)); // 使用素材在音频、画面上做一定处理,达到与BGM一定的同步感
|
||||
_zones.Add(new ZoneAttr(26, "mad", "音MAD", 119)); // 使用素材音频进行一定的二次创作来达到还原原曲的非商业性质稿件
|
||||
_zones.Add(new ZoneAttr(126, "manual_vocaloid", "人力VOCALOID", 119)); // 将人物或者角色的无伴奏素材进行人工调音,使其就像VOCALOID一样歌唱的技术
|
||||
_zones.Add(new ZoneAttr(216, "theatre", "鬼畜剧场", 119)); // 使用素材进行人工剪辑编排的有剧情的作品
|
||||
_zones.Add(new ZoneAttr(127, "course", "教程演示", 119)); // 鬼畜相关的教程演示
|
||||
|
||||
//时尚
|
||||
zones.Add(new ZoneAttr(155, "fashion", "时尚")); // 主分区
|
||||
zones.Add(new ZoneAttr(157, "makeup", "美妆护肤", 155)); // 彩妆护肤、美甲美发、仿妆、医美相关内容分享或产品测评
|
||||
zones.Add(new ZoneAttr(252, "cos", "仿妆cos", 155)); // 对二次元、三次元人物角色进行模仿、还原、展示、演绎的内容
|
||||
zones.Add(new ZoneAttr(158, "clothing", "穿搭", 155)); // 穿搭风格、穿搭技巧的展示分享,涵盖衣服、鞋靴、箱包配件、配饰(帽子、钟表、珠宝首饰)等
|
||||
zones.Add(new ZoneAttr(159, "trend", "时尚潮流", 155)); // 时尚街拍、时装周、时尚大片,时尚品牌、潮流等行业相关记录及知识科普
|
||||
_zones.Add(new ZoneAttr(155, "fashion", "时尚")); // 主分区
|
||||
_zones.Add(new ZoneAttr(157, "makeup", "美妆护肤", 155)); // 彩妆护肤、美甲美发、仿妆、医美相关内容分享或产品测评
|
||||
_zones.Add(new ZoneAttr(252, "cos", "仿妆cos", 155)); // 对二次元、三次元人物角色进行模仿、还原、展示、演绎的内容
|
||||
_zones.Add(new ZoneAttr(158, "clothing", "穿搭", 155)); // 穿搭风格、穿搭技巧的展示分享,涵盖衣服、鞋靴、箱包配件、配饰(帽子、钟表、珠宝首饰)等
|
||||
_zones.Add(new ZoneAttr(159, "trend", "时尚潮流", 155)); // 时尚街拍、时装周、时尚大片,时尚品牌、潮流等行业相关记录及知识科普
|
||||
|
||||
//资讯
|
||||
zones.Add(new ZoneAttr(202, "information", "资讯")); // 主分区
|
||||
zones.Add(new ZoneAttr(203, "hotspot", "热点", 202)); // 全民关注的时政热门资讯
|
||||
zones.Add(new ZoneAttr(204, "global", "环球", 202)); // 全球范围内发生的具有重大影响力的事件动态
|
||||
zones.Add(new ZoneAttr(205, "social", "社会", 202)); // 日常生活的社会事件、社会问题、社会风貌的报道
|
||||
zones.Add(new ZoneAttr(206, "multiple", "综合", 202)); // 除上述领域外其它垂直领域的综合资讯
|
||||
_zones.Add(new ZoneAttr(202, "information", "资讯")); // 主分区
|
||||
_zones.Add(new ZoneAttr(203, "hotspot", "热点", 202)); // 全民关注的时政热门资讯
|
||||
_zones.Add(new ZoneAttr(204, "global", "环球", 202)); // 全球范围内发生的具有重大影响力的事件动态
|
||||
_zones.Add(new ZoneAttr(205, "social", "社会", 202)); // 日常生活的社会事件、社会问题、社会风貌的报道
|
||||
_zones.Add(new ZoneAttr(206, "multiple", "综合", 202)); // 除上述领域外其它垂直领域的综合资讯
|
||||
|
||||
//娱乐
|
||||
zones.Add(new ZoneAttr(5, "ent", "娱乐")); // 主分区
|
||||
zones.Add(new ZoneAttr(71, "variety", "综艺", 5)); // 所有综艺相关,全部一手掌握!
|
||||
zones.Add(new ZoneAttr(241, "talker", "娱乐杂谈", 5)); // 娱乐人物解读、娱乐热点点评、娱乐行业分析
|
||||
zones.Add(new ZoneAttr(242, "fans", "粉丝创作", 5)); // 粉丝向创作视频
|
||||
zones.Add(new ZoneAttr(137, "celebrity", "明星综合", 5)); // 娱乐圈动态、明星资讯相关
|
||||
_zones.Add(new ZoneAttr(5, "ent", "娱乐")); // 主分区
|
||||
_zones.Add(new ZoneAttr(71, "variety", "综艺", 5)); // 所有综艺相关,全部一手掌握!
|
||||
_zones.Add(new ZoneAttr(241, "talker", "娱乐杂谈", 5)); // 娱乐人物解读、娱乐热点点评、娱乐行业分析
|
||||
_zones.Add(new ZoneAttr(242, "fans", "粉丝创作", 5)); // 粉丝向创作视频
|
||||
_zones.Add(new ZoneAttr(137, "celebrity", "明星综合", 5)); // 娱乐圈动态、明星资讯相关
|
||||
|
||||
//影视
|
||||
zones.Add(new ZoneAttr(181, "cinephile", "影视")); // 主分区
|
||||
zones.Add(new ZoneAttr(182, "cinecism", "影视杂谈", 181)); // 影视评论、解说、吐槽、科普等
|
||||
zones.Add(new ZoneAttr(183, "montage", "影视剪辑", 181)); // 对影视素材进行剪辑再创作的视频
|
||||
zones.Add(new ZoneAttr(85, "shortfilm", "小剧场", 181)); // 有场景、有剧情的演绎类内容
|
||||
zones.Add(new ZoneAttr(184, "trailer_info", "预告·资讯", 181)); // 影视类相关资讯,预告,花絮等视频
|
||||
_zones.Add(new ZoneAttr(181, "cinephile", "影视")); // 主分区
|
||||
_zones.Add(new ZoneAttr(182, "cinecism", "影视杂谈", 181)); // 影视评论、解说、吐槽、科普等
|
||||
_zones.Add(new ZoneAttr(183, "montage", "影视剪辑", 181)); // 对影视素材进行剪辑再创作的视频
|
||||
_zones.Add(new ZoneAttr(85, "shortfilm", "小剧场", 181)); // 有场景、有剧情的演绎类内容
|
||||
_zones.Add(new ZoneAttr(184, "trailer_info", "预告·资讯", 181)); // 影视类相关资讯,预告,花絮等视频
|
||||
|
||||
//纪录片
|
||||
zones.Add(new ZoneAttr(177, "documentary", "纪录片")); // 主分区
|
||||
zones.Add(new ZoneAttr(37, "history", "人文·历史", 177)); //
|
||||
zones.Add(new ZoneAttr(178, "science", "科学·探索·自然", 177)); //
|
||||
zones.Add(new ZoneAttr(179, "military", "军事", 177)); //
|
||||
zones.Add(new ZoneAttr(180, "travel", "社会·美食·旅行", 177)); //
|
||||
_zones.Add(new ZoneAttr(177, "documentary", "纪录片")); // 主分区
|
||||
_zones.Add(new ZoneAttr(37, "history", "人文·历史", 177)); //
|
||||
_zones.Add(new ZoneAttr(178, "science", "科学·探索·自然", 177)); //
|
||||
_zones.Add(new ZoneAttr(179, "military", "军事", 177)); //
|
||||
_zones.Add(new ZoneAttr(180, "travel", "社会·美食·旅行", 177)); //
|
||||
|
||||
//电影
|
||||
zones.Add(new ZoneAttr(23, "movie", "电影")); // 主分区
|
||||
zones.Add(new ZoneAttr(147, "chinese", "华语电影", 23)); //
|
||||
zones.Add(new ZoneAttr(145, "west", "欧美电影", 23)); //
|
||||
zones.Add(new ZoneAttr(146, "japan", "日本电影", 23)); //
|
||||
zones.Add(new ZoneAttr(83, "movie", "其他国家", 23)); //
|
||||
_zones.Add(new ZoneAttr(23, "movie", "电影")); // 主分区
|
||||
_zones.Add(new ZoneAttr(147, "chinese", "华语电影", 23)); //
|
||||
_zones.Add(new ZoneAttr(145, "west", "欧美电影", 23)); //
|
||||
_zones.Add(new ZoneAttr(146, "japan", "日本电影", 23)); //
|
||||
_zones.Add(new ZoneAttr(83, "movie", "其他国家", 23)); //
|
||||
|
||||
//电视剧
|
||||
zones.Add(new ZoneAttr(11, "tv", "电视剧")); // 主分区
|
||||
zones.Add(new ZoneAttr(185, "mainland", "国产剧", 11)); //
|
||||
zones.Add(new ZoneAttr(187, "overseas", "海外剧", 11)); //
|
||||
_zones.Add(new ZoneAttr(11, "tv", "电视剧")); // 主分区
|
||||
_zones.Add(new ZoneAttr(185, "mainland", "国产剧", 11)); //
|
||||
_zones.Add(new ZoneAttr(187, "overseas", "海外剧", 11)); //
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,7 @@
|
||||
/// </summary>
|
||||
public class VideoZoneIcon
|
||||
{
|
||||
private static VideoZoneIcon instance;
|
||||
private static VideoZoneIcon? _instance;
|
||||
|
||||
/// <summary>
|
||||
/// 获取VideoZoneIcon实例
|
||||
@@ -13,12 +13,7 @@ public class VideoZoneIcon
|
||||
/// <returns></returns>
|
||||
public static VideoZoneIcon Instance()
|
||||
{
|
||||
if (instance == null)
|
||||
{
|
||||
instance = new VideoZoneIcon();
|
||||
}
|
||||
|
||||
return instance;
|
||||
return _instance ??= new VideoZoneIcon();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -35,55 +30,32 @@ public class VideoZoneIcon
|
||||
/// <returns></returns>
|
||||
public string GetZoneImageKey(int tid)
|
||||
{
|
||||
switch (tid)
|
||||
return tid switch
|
||||
{
|
||||
// 课堂
|
||||
case -10:
|
||||
return "Zone.cheeseDrawingImage";
|
||||
case 1:
|
||||
return "Zone.dougaDrawingImage";
|
||||
case 13:
|
||||
return "Zone.animeDrawingImage";
|
||||
case 167:
|
||||
return "Zone.guochuangDrawingImage";
|
||||
case 3:
|
||||
return "Zone.musicDrawingImage";
|
||||
case 129:
|
||||
return "Zone.danceDrawingImage";
|
||||
case 4:
|
||||
return "Zone.gameDrawingImage";
|
||||
case 36:
|
||||
return "Zone.techDrawingImage";
|
||||
case 188:
|
||||
return "Zone.digitalDrawingImage";
|
||||
case 234:
|
||||
return "Zone.sportsDrawingImage";
|
||||
case 223:
|
||||
return "Zone.carDrawingImage";
|
||||
case 160:
|
||||
return "Zone.lifeDrawingImage";
|
||||
case 211:
|
||||
return "Zone.foodDrawingImage";
|
||||
case 217:
|
||||
return "Zone.animalDrawingImage";
|
||||
case 119:
|
||||
return "Zone.kichikuDrawingImage";
|
||||
case 155:
|
||||
return "Zone.fashionDrawingImage";
|
||||
case 202:
|
||||
return "Zone.informationDrawingImage";
|
||||
case 5:
|
||||
return "Zone.entDrawingImage";
|
||||
case 181:
|
||||
return "Zone.cinephileDrawingImage";
|
||||
case 177:
|
||||
return "Zone.documentaryDrawingImage";
|
||||
case 23:
|
||||
return "Zone.movieDrawingImage";
|
||||
case 11:
|
||||
return "Zone.teleplayDrawingImage";
|
||||
default:
|
||||
return "videoUpDrawingImage";
|
||||
}
|
||||
-10 => "Zone.cheeseDrawingImage",
|
||||
1 => "Zone.dougaDrawingImage",
|
||||
3 => "Zone.musicDrawingImage",
|
||||
4 => "Zone.gameDrawingImage",
|
||||
5 => "Zone.entDrawingImage",
|
||||
11 => "Zone.teleplayDrawingImage",
|
||||
13 => "Zone.animeDrawingImage",
|
||||
23 => "Zone.movieDrawingImage",
|
||||
36 => "Zone.techDrawingImage",
|
||||
119 => "Zone.kichikuDrawingImage",
|
||||
129 => "Zone.danceDrawingImage",
|
||||
155 => "Zone.fashionDrawingImage",
|
||||
160 => "Zone.lifeDrawingImage",
|
||||
167 => "Zone.guochuangDrawingImage",
|
||||
177 => "Zone.documentaryDrawingImage",
|
||||
181 => "Zone.cinephileDrawingImage",
|
||||
188 => "Zone.digitalDrawingImage",
|
||||
202 => "Zone.informationDrawingImage",
|
||||
211 => "Zone.foodDrawingImage",
|
||||
217 => "Zone.animalDrawingImage",
|
||||
223 => "Zone.carDrawingImage",
|
||||
234 => "Zone.sportsDrawingImage",
|
||||
_ => "videoUpDrawingImage"
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -114,7 +114,7 @@ public class Bilibili
|
||||
}
|
||||
|
||||
// 弹幕预处理
|
||||
Producer producer = new Producer(config, danmakus);
|
||||
var producer = new Producer(config, danmakus);
|
||||
producer.StartHandle();
|
||||
|
||||
// 字幕生成
|
||||
|
||||
@@ -17,7 +17,7 @@ public class Collision
|
||||
private List<int> Leaves()
|
||||
{
|
||||
var ret = new List<int>(lineCount);
|
||||
for (int i = 0; i < lineCount; i++) ret.Add(0);
|
||||
for (var i = 0; i < lineCount; i++) ret.Add(0);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -29,10 +29,10 @@ public class Collision
|
||||
/// <returns></returns>
|
||||
public Tuple<int, float> Detect(Display display)
|
||||
{
|
||||
List<float> beyonds = new List<float>();
|
||||
for (int i = 0; i < leaves.Count; i++)
|
||||
var beyonds = new List<float>();
|
||||
for (var i = 0; i < leaves.Count; i++)
|
||||
{
|
||||
float beyond = display.Danmaku.Start - leaves[i];
|
||||
var beyond = display.Danmaku.Start - leaves[i];
|
||||
// 某一行有足够空间,直接返回行号和 0 偏移
|
||||
if (beyond >= 0)
|
||||
{
|
||||
@@ -43,9 +43,9 @@ public class Collision
|
||||
}
|
||||
|
||||
// 所有行都没有空间了,那么找出哪一行能在最短时间内让出空间
|
||||
float soon = beyonds.Max();
|
||||
int lineIndex = beyonds.IndexOf(soon);
|
||||
float offset = -soon;
|
||||
var soon = beyonds.Max();
|
||||
var lineIndex = beyonds.IndexOf(soon);
|
||||
var offset = -soon;
|
||||
return Tuple.Create(lineIndex, offset);
|
||||
}
|
||||
|
||||
|
||||
@@ -22,14 +22,14 @@ public class Creater
|
||||
{
|
||||
var scroll = new Collision(Config.LineCount);
|
||||
var stayed = new Collision(Config.LineCount);
|
||||
Dictionary<string, Collision> collisions = new Dictionary<string, Collision>
|
||||
var collisions = new Dictionary<string, Collision>
|
||||
{
|
||||
{ "scroll", scroll },
|
||||
{ "top", stayed },
|
||||
{ "bottom", stayed }
|
||||
};
|
||||
|
||||
List<Subtitle> subtitles = new List<Subtitle>();
|
||||
var subtitles = new List<Subtitle>();
|
||||
foreach (var danmaku in Danmakus)
|
||||
{
|
||||
// 丢弃不支持的
|
||||
@@ -41,9 +41,7 @@ public class Creater
|
||||
// 创建显示方式对象
|
||||
var display = Display.Factory(Config, danmaku);
|
||||
var collision = collisions[danmaku.Style];
|
||||
var detect = collision.Detect(display);
|
||||
int lineIndex = detect.Item1;
|
||||
float waitingOffset = detect.Item2;
|
||||
var (lineIndex, waitingOffset) = collision.Detect(display);
|
||||
|
||||
// 超过容忍的偏移量,丢弃掉此条弹幕
|
||||
if (waitingOffset > Config.DropOffset)
|
||||
@@ -56,8 +54,8 @@ public class Creater
|
||||
collision.Update(display.Leave, lineIndex, waitingOffset);
|
||||
|
||||
// 再加上自定义偏移
|
||||
float offset = waitingOffset + Config.CustomOffset;
|
||||
Subtitle subtitle = new Subtitle(danmaku, display, offset);
|
||||
var offset = waitingOffset + Config.CustomOffset;
|
||||
var subtitle = new Subtitle(danmaku, display, offset);
|
||||
|
||||
subtitles.Add(subtitle);
|
||||
}
|
||||
@@ -67,18 +65,14 @@ public class Creater
|
||||
|
||||
protected string SetText()
|
||||
{
|
||||
string header = Config.HeaderTemplate
|
||||
var header = Config.HeaderTemplate
|
||||
.Replace("{title}", Config.Title)
|
||||
.Replace("{width}", Config.ScreenWidth.ToString())
|
||||
.Replace("{height}", Config.ScreenHeight.ToString())
|
||||
.Replace("{fontname}", Config.FontName)
|
||||
.Replace("{fontsize}", Config.BaseFontSize.ToString());
|
||||
|
||||
string events = string.Empty;
|
||||
foreach (var subtitle in Subtitles)
|
||||
{
|
||||
events += "\n" + subtitle.Text;
|
||||
}
|
||||
var events = Subtitles.Aggregate(string.Empty, (current, subtitle) => current + "\n" + subtitle.Text);
|
||||
|
||||
return header + events;
|
||||
}
|
||||
|
||||
@@ -52,7 +52,7 @@ public class Display
|
||||
/// <returns></returns>
|
||||
public static Display Factory(Config config, Danmaku danmaku)
|
||||
{
|
||||
Dictionary<string, Display> dict = new Dictionary<string, Display>
|
||||
var dict = new Dictionary<string, Display>
|
||||
{
|
||||
{ "scroll", new ScrollDisplay(config, danmaku) },
|
||||
{ "top", new TopDisplay(config, danmaku) },
|
||||
@@ -92,18 +92,9 @@ public class Display
|
||||
/// <returns></returns>
|
||||
protected int SetMaxLength()
|
||||
{
|
||||
string[] lines = Danmaku.Content.Split('\n');
|
||||
int maxLength = 0;
|
||||
foreach (string line in lines)
|
||||
{
|
||||
int length = Utils.DisplayLength(line);
|
||||
if (maxLength < length)
|
||||
{
|
||||
maxLength = length;
|
||||
}
|
||||
}
|
||||
var lines = Danmaku.Content.Split('\n');
|
||||
|
||||
return maxLength;
|
||||
return lines.Select(Utils.DisplayLength).Prepend(0).Max();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -122,7 +113,7 @@ public class Display
|
||||
/// <returns></returns>
|
||||
protected int SetHeight()
|
||||
{
|
||||
int lineCount = Danmaku.Content.Split('\n').Length;
|
||||
var lineCount = Danmaku.Content.Split('\n').Length;
|
||||
return lineCount * FontSize;
|
||||
}
|
||||
|
||||
@@ -133,7 +124,7 @@ public class Display
|
||||
/// <returns></returns>
|
||||
protected virtual Tuple<int, int> SetHorizontal()
|
||||
{
|
||||
int x = (int)Math.Floor(Config.ScreenWidth / 2.0);
|
||||
var x = (int)Math.Floor(Config.ScreenWidth / 2.0);
|
||||
return Tuple.Create(x, x);
|
||||
}
|
||||
|
||||
@@ -144,7 +135,7 @@ public class Display
|
||||
/// <returns></returns>
|
||||
protected virtual Tuple<int, int> SetVertical()
|
||||
{
|
||||
int y = (int)Math.Floor(Config.ScreenHeight / 2.0);
|
||||
var y = (int)Math.Floor(Config.ScreenHeight / 2.0);
|
||||
return Tuple.Create(y, y);
|
||||
}
|
||||
|
||||
@@ -154,7 +145,7 @@ public class Display
|
||||
/// <returns></returns>
|
||||
protected virtual int SetDuration()
|
||||
{
|
||||
int baseDuration = 3 + Config.TuneDuration;
|
||||
var baseDuration = 3 + Config.TuneDuration;
|
||||
if (baseDuration <= 0)
|
||||
{
|
||||
baseDuration = 0;
|
||||
@@ -217,7 +208,7 @@ public class TopDisplay : Display
|
||||
protected override Tuple<int, int> SetVertical()
|
||||
{
|
||||
// 这里 y 坐标为 0 就是最顶行了
|
||||
int y = LineIndex * Config.BaseFontSize;
|
||||
var y = LineIndex * Config.BaseFontSize;
|
||||
return Tuple.Create(y, y);
|
||||
}
|
||||
}
|
||||
@@ -239,7 +230,7 @@ public class BottomDisplay : Display
|
||||
protected override Tuple<int, int> SetVertical()
|
||||
{
|
||||
// 要让字幕不超出底部,减去高度
|
||||
int y = Config.ScreenHeight - (LineIndex * Config.BaseFontSize) - Height;
|
||||
var y = Config.ScreenHeight - (LineIndex * Config.BaseFontSize) - Height;
|
||||
// 再减去自定义的底部边距
|
||||
y -= Config.BottomMargin;
|
||||
return Tuple.Create(y, y);
|
||||
@@ -284,17 +275,17 @@ public class ScrollDisplay : Display
|
||||
/// <returns></returns>
|
||||
protected override Tuple<int, int> SetHorizontal()
|
||||
{
|
||||
int x1 = Config.ScreenWidth + (int)Math.Floor(Width / 2.0);
|
||||
int x2 = 0 - (int)Math.Floor(Width / 2.0);
|
||||
var x1 = Config.ScreenWidth + (int)Math.Floor(Width / 2.0);
|
||||
var x2 = 0 - (int)Math.Floor(Width / 2.0);
|
||||
return Tuple.Create(x1, x2);
|
||||
}
|
||||
|
||||
protected override Tuple<int, int> SetVertical()
|
||||
{
|
||||
int baseFontSize = Config.BaseFontSize;
|
||||
var baseFontSize = Config.BaseFontSize;
|
||||
|
||||
// 垂直位置,按基准字体大小算每一行的高度
|
||||
int y = (LineIndex + 1) * baseFontSize;
|
||||
var y = (LineIndex + 1) * baseFontSize;
|
||||
|
||||
// 个别弹幕可能字体比基准要大,所以最上的一行还要避免挤出顶部屏幕
|
||||
// 坐标不能小于字体大小
|
||||
@@ -312,7 +303,7 @@ public class ScrollDisplay : Display
|
||||
/// <returns></returns>
|
||||
protected int SetDistance()
|
||||
{
|
||||
Tuple<int, int> x = Horizontal;
|
||||
var x = Horizontal;
|
||||
return x.Item1 - x.Item2;
|
||||
}
|
||||
|
||||
@@ -324,7 +315,7 @@ public class ScrollDisplay : Display
|
||||
{
|
||||
// 基准时间,就是每个字的移动时间
|
||||
// 12 秒加上用户自定义的微调
|
||||
int baseDuration = 12 + Config.TuneDuration;
|
||||
var baseDuration = 12 + Config.TuneDuration;
|
||||
if (baseDuration <= 0)
|
||||
{
|
||||
baseDuration = 1;
|
||||
@@ -350,7 +341,7 @@ public class ScrollDisplay : Display
|
||||
/// <returns></returns>
|
||||
public int AsyncDuration()
|
||||
{
|
||||
int baseDuration = 6 + Config.TuneDuration;
|
||||
var baseDuration = 6 + Config.TuneDuration;
|
||||
if (baseDuration <= 0)
|
||||
{
|
||||
baseDuration = 0;
|
||||
@@ -385,9 +376,9 @@ public class ScrollDisplay : Display
|
||||
/// <returns></returns>
|
||||
protected override int SetDuration()
|
||||
{
|
||||
string methodName = Config.LayoutAlgorithm.Substring(0, 1).ToUpper() + Config.LayoutAlgorithm.Substring(1);
|
||||
var methodName = Config.LayoutAlgorithm.Substring(0, 1).ToUpper() + Config.LayoutAlgorithm.Substring(1);
|
||||
methodName += "Duration";
|
||||
MethodInfo method = typeof(ScrollDisplay).GetMethod(methodName);
|
||||
var method = typeof(ScrollDisplay).GetMethod(methodName);
|
||||
if (method != null)
|
||||
{
|
||||
return (int)method.Invoke(this, null);
|
||||
@@ -405,7 +396,7 @@ public class ScrollDisplay : Display
|
||||
// 对于滚动样式弹幕来说,就是最后一个字符离开最右边缘的时间
|
||||
// 坐标是字幕中点,在屏幕外和内各有半个字幕宽度
|
||||
// 也就是跑过一个字幕宽度的路程
|
||||
float duration = Width / Speed;
|
||||
var duration = Width / Speed;
|
||||
return (int)(Danmaku.Start + duration);
|
||||
}
|
||||
}
|
||||
@@ -18,18 +18,7 @@ public class TopFilter : Filter
|
||||
{
|
||||
public override List<Danmaku> DoFilter(List<Danmaku> danmakus)
|
||||
{
|
||||
List<Danmaku> keep = new List<Danmaku>();
|
||||
foreach (var danmaku in danmakus)
|
||||
{
|
||||
if (danmaku.Style == "top")
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
keep.Add(danmaku);
|
||||
}
|
||||
|
||||
return keep;
|
||||
return danmakus.Where(danmaku => danmaku.Style != "top").ToList();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,18 +29,7 @@ public class BottomFilter : Filter
|
||||
{
|
||||
public override List<Danmaku> DoFilter(List<Danmaku> danmakus)
|
||||
{
|
||||
List<Danmaku> keep = new List<Danmaku>();
|
||||
foreach (var danmaku in danmakus)
|
||||
{
|
||||
if (danmaku.Style == "bottom")
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
keep.Add(danmaku);
|
||||
}
|
||||
|
||||
return keep;
|
||||
return danmakus.Where(danmaku => danmaku.Style != "bottom").ToList();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,18 +40,7 @@ public class ScrollFilter : Filter
|
||||
{
|
||||
public override List<Danmaku> DoFilter(List<Danmaku> danmakus)
|
||||
{
|
||||
List<Danmaku> keep = new List<Danmaku>();
|
||||
foreach (var danmaku in danmakus)
|
||||
{
|
||||
if (danmaku.Style == "scroll")
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
keep.Add(danmaku);
|
||||
}
|
||||
|
||||
return keep;
|
||||
return danmakus.Where(danmaku => danmaku.Style != "scroll").ToList();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -45,7 +45,7 @@ public class Producer
|
||||
|
||||
public void ApplyFilter()
|
||||
{
|
||||
Dictionary<string, int> filterDetail = new Dictionary<string, int>()
|
||||
var filterDetail = new Dictionary<string, int>()
|
||||
{
|
||||
{ "top_filter", 0 },
|
||||
{ "bottom_filter", 0 },
|
||||
@@ -53,10 +53,10 @@ public class Producer
|
||||
//{ "custom_filter",0}
|
||||
};
|
||||
|
||||
List<Danmaku> danmakus = Danmakus;
|
||||
var danmakus = Danmakus;
|
||||
//string[] orders = { "top_filter", "bottom_filter", "scroll_filter", "custom_filter" };
|
||||
string[] orders = { "top_filter", "bottom_filter", "scroll_filter" };
|
||||
foreach (string name in orders)
|
||||
foreach (var name in orders)
|
||||
{
|
||||
Filter filter;
|
||||
try
|
||||
@@ -69,7 +69,7 @@ public class Producer
|
||||
continue;
|
||||
}
|
||||
|
||||
int count = danmakus.Count;
|
||||
var count = danmakus.Count;
|
||||
danmakus = filter.DoFilter(danmakus);
|
||||
filterDetail[name] = count - danmakus.Count;
|
||||
}
|
||||
@@ -80,16 +80,12 @@ public class Producer
|
||||
|
||||
public Dictionary<string, int> Report()
|
||||
{
|
||||
int blockedCount = 0;
|
||||
foreach (int count in FilterDetail.Values)
|
||||
{
|
||||
blockedCount += count;
|
||||
}
|
||||
var blockedCount = FilterDetail.Values.Sum();
|
||||
|
||||
int passedCount = KeepedDanmakus.Count;
|
||||
int totalCount = blockedCount + passedCount;
|
||||
var passedCount = KeepedDanmakus.Count;
|
||||
var totalCount = blockedCount + passedCount;
|
||||
|
||||
Dictionary<string, int> ret = new Dictionary<string, int>
|
||||
var ret = new Dictionary<string, int>
|
||||
{
|
||||
{ "blocked", blockedCount },
|
||||
{ "passed", passedCount },
|
||||
|
||||
@@ -40,7 +40,7 @@ public class Studio
|
||||
/// <returns></returns>
|
||||
protected int SetKeepedCount()
|
||||
{
|
||||
return Creater.Subtitles.Count();
|
||||
return Creater.Subtitles.Count;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -74,7 +74,7 @@ public class Studio
|
||||
|
||||
public Dictionary<string, int> Report()
|
||||
{
|
||||
return new Dictionary<string, int>()
|
||||
return new Dictionary<string, int>
|
||||
{
|
||||
{ "total", Danmakus.Count },
|
||||
{ "droped", DropedCount },
|
||||
|
||||
@@ -103,47 +103,30 @@ public class Subtitle
|
||||
//return "\\3c&HFFFFFF";
|
||||
return "\\3c&H000000";
|
||||
}
|
||||
else
|
||||
{
|
||||
return "\\3c&H000000";
|
||||
}
|
||||
|
||||
return "\\3c&H000000";
|
||||
//return "";
|
||||
}
|
||||
|
||||
protected string SetFontSizeMarkup()
|
||||
{
|
||||
if (Display.IsScaled)
|
||||
{
|
||||
return $"\\fs{Display.FontSize}";
|
||||
}
|
||||
|
||||
return "";
|
||||
return Display.IsScaled ? $"\\fs{Display.FontSize}" : "";
|
||||
}
|
||||
|
||||
protected string SetStyleMarkup()
|
||||
{
|
||||
if (Danmaku.Style == "scroll")
|
||||
{
|
||||
return $"\\move({Position["x1"]}, {Position["y1"]}, {Position["x2"]}, {Position["y2"]})";
|
||||
}
|
||||
|
||||
return $"\\a6\\pos({Position["x1"]}, {Position["y1"]})";
|
||||
return Danmaku.Style == "scroll" ? $"\\move({Position["x1"]}, {Position["y1"]}, {Position["x2"]}, {Position["y2"]})" : $"\\a6\\pos({Position["x1"]}, {Position["y1"]})";
|
||||
}
|
||||
|
||||
protected string SetLayerMarkup()
|
||||
{
|
||||
if (Danmaku.Style != "scroll")
|
||||
{
|
||||
return "-2";
|
||||
}
|
||||
|
||||
return "-1";
|
||||
return Danmaku.Style != "scroll" ? "-2" : "-1";
|
||||
}
|
||||
|
||||
protected string SetContentMarkup()
|
||||
{
|
||||
string markup = StyleMarkup + ColorMarkup + BorderMarkup + FontSizeMarkup;
|
||||
string content = Utils.CorrectTypos(Danmaku.Content);
|
||||
var markup = StyleMarkup + ColorMarkup + BorderMarkup + FontSizeMarkup;
|
||||
var content = Utils.CorrectTypos(Danmaku.Content);
|
||||
return $"{{{markup}}}{content}";
|
||||
}
|
||||
|
||||
|
||||
@@ -50,17 +50,17 @@ internal static class Utils
|
||||
return "0:00:00.00";
|
||||
}
|
||||
|
||||
int i = (int)Math.Floor(seconds / 1.0);
|
||||
int dec = (int)(Math.Round(seconds % 1.0f, 2) * 100);
|
||||
var i = (int)Math.Floor(seconds / 1.0);
|
||||
var dec = (int)(Math.Round(seconds % 1.0f, 2) * 100);
|
||||
if (dec >= 100)
|
||||
{
|
||||
dec = 99;
|
||||
}
|
||||
|
||||
int min = (int)Math.Floor(i / 60.0);
|
||||
int second = (int)(i % 60.0f);
|
||||
var min = (int)Math.Floor(i / 60.0);
|
||||
var second = (int)(i % 60.0f);
|
||||
|
||||
int hour = (int)Math.Floor(min / 60.0);
|
||||
var hour = (int)Math.Floor(min / 60.0);
|
||||
min = (int)Math.Floor(min % 60.0f);
|
||||
|
||||
return $"{hour:D}:{min:D2}:{second:D2}.{dec:D2}";
|
||||
@@ -73,15 +73,9 @@ internal static class Utils
|
||||
/// <returns></returns>
|
||||
public static float Hms2second(string hms)
|
||||
{
|
||||
string[] numbers = hms.Split(':');
|
||||
float seconds = 0;
|
||||
var numbers = hms.Split(':');
|
||||
|
||||
for (int i = 0; i < numbers.Length; i++)
|
||||
{
|
||||
seconds += (float)(float.Parse(numbers[numbers.Length - i - 1]) * Math.Pow(60, i));
|
||||
}
|
||||
|
||||
return seconds;
|
||||
return numbers.Select((t, i) => (float)(float.Parse(numbers[numbers.Length - i - 1]) * Math.Pow(60, i))).Sum();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -92,14 +86,9 @@ internal static class Utils
|
||||
/// <returns></returns>
|
||||
public static float Xhms2second(string xhms)
|
||||
{
|
||||
string[] args = xhms.Replace("+", " +").Replace("-", " -").Split(' ');
|
||||
float result = 0;
|
||||
foreach (string hms in args)
|
||||
{
|
||||
result += Hms2second(hms);
|
||||
}
|
||||
var args = xhms.Replace("+", " +").Replace("-", " -").Split(' ');
|
||||
|
||||
return result;
|
||||
return args.Sum(Hms2second);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -110,7 +99,6 @@ internal static class Utils
|
||||
public static string Int2rgb(int integer)
|
||||
{
|
||||
return integer.ToString("X").PadLeft(6, '0');
|
||||
;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -120,8 +108,8 @@ internal static class Utils
|
||||
/// <returns></returns>
|
||||
public static string Int2bgr(int integer)
|
||||
{
|
||||
string rgb = Int2rgb(integer);
|
||||
string bgr = rgb.Substring(4, 2) + rgb.Substring(2, 2) + rgb.Substring(0, 2);
|
||||
var rgb = Int2rgb(integer);
|
||||
var bgr = rgb.Substring(4, 2) + rgb.Substring(2, 2) + rgb.Substring(0, 2);
|
||||
return bgr;
|
||||
}
|
||||
|
||||
@@ -132,7 +120,7 @@ internal static class Utils
|
||||
/// <returns></returns>
|
||||
public static float[] Int2hls(int integer)
|
||||
{
|
||||
string rgb = Int2rgb(integer);
|
||||
var rgb = Int2rgb(integer);
|
||||
int[] rgb_decimals = { 0, 0, 0 };
|
||||
rgb_decimals[0] = int.Parse(rgb.Substring(0, 2), NumberStyles.HexNumber);
|
||||
rgb_decimals[1] = int.Parse(rgb.Substring(2, 2), NumberStyles.HexNumber);
|
||||
@@ -142,7 +130,7 @@ internal static class Utils
|
||||
rgb_coordinates[0] = (int)Math.Floor(rgb_decimals[0] / 255.0);
|
||||
rgb_coordinates[1] = (int)Math.Floor(rgb_decimals[1] / 255.0);
|
||||
rgb_coordinates[2] = (int)Math.Floor(rgb_decimals[2] / 255.0);
|
||||
float[] hls_corrdinates = Rgb2hls(rgb_coordinates);
|
||||
var hls_corrdinates = Rgb2hls(rgb_coordinates);
|
||||
|
||||
float[] hls = { 0, 0, 0 };
|
||||
hls[0] = hls_corrdinates[0] * 360;
|
||||
@@ -162,8 +150,8 @@ internal static class Utils
|
||||
private static float[] Rgb2hls(int[] rgb)
|
||||
{
|
||||
float[] hls = { 0, 0, 0 };
|
||||
int maxc = rgb.Max();
|
||||
int minc = rgb.Min();
|
||||
var maxc = rgb.Max();
|
||||
var minc = rgb.Min();
|
||||
hls[1] = (minc + maxc) / 2.0f;
|
||||
if (minc == maxc)
|
||||
{
|
||||
@@ -195,7 +183,7 @@ internal static class Utils
|
||||
hls[0] = 4.0f + gc - rc;
|
||||
}
|
||||
|
||||
hls[0] = (hls[0] / 6.0f) % 1.0f;
|
||||
hls[0] = hls[0] / 6.0f % 1.0f;
|
||||
return hls;
|
||||
}
|
||||
|
||||
@@ -211,23 +199,20 @@ internal static class Utils
|
||||
return true;
|
||||
}
|
||||
|
||||
float[] hls = Int2hls(integer);
|
||||
float hue = hls[0];
|
||||
float lightness = hls[1];
|
||||
var hls = Int2hls(integer);
|
||||
var hue = hls[0];
|
||||
var lightness = hls[1];
|
||||
|
||||
// HSL 色轮见
|
||||
// http://zh.wikipedia.org/zh-cn/HSL和HSV色彩空间
|
||||
// 以下的数值都是我的主观判断认为是暗色
|
||||
if ((hue > 30 && hue < 210) && lightness < 33)
|
||||
switch (hue)
|
||||
{
|
||||
return true;
|
||||
// HSL 色轮见
|
||||
// http://zh.wikipedia.org/zh-cn/HSL和HSV色彩空间
|
||||
// 以下的数值都是我的主观判断认为是暗色
|
||||
case > 30 and < 210 when lightness < 33:
|
||||
case < 30 or > 210 when lightness < 66:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((hue < 30 || hue > 210) && lightness < 66)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -7,13 +7,13 @@
|
||||
<RuntimeIdentifier>$([System.Runtime.InteropServices.RuntimeInformation]::RuntimeIdentifier)</RuntimeIdentifier>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Avalonia" Version="11.2.4" />
|
||||
<PackageReference Include="FFMpegCore" Version="5.1.0"/>
|
||||
<PackageReference Include="Avalonia" Version="11.2.5" />
|
||||
<PackageReference Include="FFMpegCore" Version="5.2.0" />
|
||||
<PackageReference Include="Google.Protobuf" Version="3.25.1"/>
|
||||
<PackageReference Include="Microsoft.Data.Sqlite.Core" Version="8.0.7" />
|
||||
<PackageReference Include="Microsoft.Data.Sqlite.Core" Version="9.0.3" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3"/>
|
||||
<PackageReference Include="QRCoder" Version="1.6.0" />
|
||||
<PackageReference Include="SQLitePCLRaw.bundle_e_sqlcipher" Version="2.1.8" />
|
||||
<PackageReference Include="SQLitePCLRaw.bundle_e_sqlcipher" Version="2.1.11" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -1,448 +0,0 @@
|
||||
using System.ComponentModel;
|
||||
using System.Net;
|
||||
|
||||
namespace DownKyi.Core.Downloader;
|
||||
|
||||
/// <summary>
|
||||
/// 文件合并改变事件
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
public delegate void FileMergeProgressChangedEventHandler(object sender, int e);
|
||||
|
||||
/// <summary>
|
||||
/// 多线程下载器
|
||||
/// </summary>
|
||||
public class MultiThreadDownloader
|
||||
{
|
||||
#region 属性
|
||||
|
||||
private string _url;
|
||||
private bool _rangeAllowed;
|
||||
private readonly HttpWebRequest _request;
|
||||
|
||||
private Action<HttpWebRequest> _requestConfigure = req =>
|
||||
req.UserAgent =
|
||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.122 Safari/537.36";
|
||||
|
||||
#endregion 属性
|
||||
|
||||
#region 公共属性
|
||||
|
||||
/// <summary>
|
||||
/// RangeAllowed
|
||||
/// </summary>
|
||||
public bool RangeAllowed
|
||||
{
|
||||
get => _rangeAllowed;
|
||||
set => _rangeAllowed = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 临时文件夹
|
||||
/// </summary>
|
||||
public string TempFileDirectory { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// url地址
|
||||
/// </summary>
|
||||
public string Url
|
||||
{
|
||||
get => _url;
|
||||
set => _url = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 第几部分
|
||||
/// </summary>
|
||||
public int NumberOfParts { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 已接收字节数
|
||||
/// </summary>
|
||||
public long TotalBytesReceived
|
||||
{
|
||||
get
|
||||
{
|
||||
try
|
||||
{
|
||||
lock (this)
|
||||
{
|
||||
return PartialDownloaderList.Where(t => t != null).Sum(t => t.TotalBytesRead);
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 总进度
|
||||
/// </summary>
|
||||
public float TotalProgress { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// 文件大小
|
||||
/// </summary>
|
||||
public long Size { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// 下载速度
|
||||
/// </summary>
|
||||
public float TotalSpeedInBytes
|
||||
{
|
||||
get
|
||||
{
|
||||
lock (this)
|
||||
{
|
||||
return PartialDownloaderList.Sum(t => t.SpeedInBytes);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 下载块
|
||||
/// </summary>
|
||||
public List<PartialDownloader> PartialDownloaderList { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 文件路径
|
||||
/// </summary>
|
||||
public string FilePath { get; set; }
|
||||
|
||||
#endregion 公共属性
|
||||
|
||||
#region 变量
|
||||
|
||||
/// <summary>
|
||||
/// 总下载进度更新事件
|
||||
/// </summary>
|
||||
public event EventHandler TotalProgressChanged;
|
||||
|
||||
/// <summary>
|
||||
/// 文件合并完成事件
|
||||
/// </summary>
|
||||
public event EventHandler FileMergedComplete;
|
||||
|
||||
/// <summary>
|
||||
/// 文件合并事件
|
||||
/// </summary>
|
||||
public event FileMergeProgressChangedEventHandler FileMergeProgressChanged;
|
||||
|
||||
private readonly AsyncOperation _aop;
|
||||
|
||||
#endregion 变量
|
||||
|
||||
#region 下载管理器
|
||||
|
||||
/// <summary>
|
||||
/// 多线程下载管理器
|
||||
/// </summary>
|
||||
/// <param name="sourceUrl"></param>
|
||||
/// <param name="tempDir"></param>
|
||||
/// <param name="savePath"></param>
|
||||
/// <param name="numOfParts"></param>
|
||||
public MultiThreadDownloader(string sourceUrl, string tempDir, string savePath, int numOfParts)
|
||||
{
|
||||
_url = sourceUrl;
|
||||
NumberOfParts = numOfParts;
|
||||
TempFileDirectory = tempDir;
|
||||
PartialDownloaderList = new List<PartialDownloader>();
|
||||
_aop = AsyncOperationManager.CreateOperation(null);
|
||||
FilePath = savePath;
|
||||
_request = WebRequest.Create(sourceUrl) as HttpWebRequest;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 多线程下载管理器
|
||||
/// </summary>
|
||||
/// <param name="sourceUrl"></param>
|
||||
/// <param name="savePath"></param>
|
||||
/// <param name="numOfParts"></param>
|
||||
public MultiThreadDownloader(string sourceUrl, string savePath, int numOfParts) : this(sourceUrl, null,
|
||||
savePath, numOfParts)
|
||||
{
|
||||
TempFileDirectory = Path.Combine(Path.GetTempPath(), "DownKyi");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 多线程下载管理器
|
||||
/// </summary>
|
||||
/// <param name="sourceUrl"></param>
|
||||
/// <param name="numOfParts"></param>
|
||||
public MultiThreadDownloader(string sourceUrl, int numOfParts) : this(sourceUrl, null, numOfParts)
|
||||
{
|
||||
}
|
||||
|
||||
#endregion 下载管理器
|
||||
|
||||
#region 事件
|
||||
|
||||
private void temp_DownloadPartCompleted(object sender, EventArgs e)
|
||||
{
|
||||
WaitOrResumeAll(PartialDownloaderList, true);
|
||||
|
||||
if (TotalBytesReceived == Size)
|
||||
{
|
||||
UpdateProgress();
|
||||
MergeParts();
|
||||
return;
|
||||
}
|
||||
|
||||
PartialDownloaderList.Sort((x, y) => (int)(y.RemainingBytes - x.RemainingBytes));
|
||||
var rem = PartialDownloaderList[0].RemainingBytes;
|
||||
if (rem < 50 * 1024)
|
||||
{
|
||||
WaitOrResumeAll(PartialDownloaderList, false);
|
||||
return;
|
||||
}
|
||||
|
||||
var from = PartialDownloaderList[0].CurrentPosition + rem / 2;
|
||||
var to = PartialDownloaderList[0].To;
|
||||
if (from > to)
|
||||
{
|
||||
WaitOrResumeAll(PartialDownloaderList, false);
|
||||
return;
|
||||
}
|
||||
|
||||
PartialDownloaderList[0].To = from - 1;
|
||||
WaitOrResumeAll(PartialDownloaderList, false);
|
||||
var temp = new PartialDownloader(_url, TempFileDirectory, Guid.NewGuid().ToString(), from, to, true);
|
||||
temp.DownloadPartCompleted += temp_DownloadPartCompleted;
|
||||
temp.DownloadPartProgressChanged += temp_DownloadPartProgressChanged;
|
||||
lock (this)
|
||||
{
|
||||
PartialDownloaderList.Add(temp);
|
||||
}
|
||||
|
||||
temp.Start(_requestConfigure);
|
||||
}
|
||||
|
||||
private void temp_DownloadPartProgressChanged(object sender, EventArgs e)
|
||||
{
|
||||
UpdateProgress();
|
||||
}
|
||||
|
||||
private void UpdateProgress()
|
||||
{
|
||||
int pr = (int)(TotalBytesReceived * 1d / Size * 100);
|
||||
if (TotalProgress != pr)
|
||||
{
|
||||
TotalProgress = pr;
|
||||
if (TotalProgressChanged != null)
|
||||
{
|
||||
_aop.Post(state => TotalProgressChanged(this, EventArgs.Empty), null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion 事件
|
||||
|
||||
#region 方法
|
||||
|
||||
private void CreateFirstPartitions()
|
||||
{
|
||||
Size = GetContentLength(ref _rangeAllowed, ref _url);
|
||||
int maximumPart = (int)(Size / (25 * 1024));
|
||||
maximumPart = maximumPart == 0 ? 1 : maximumPart;
|
||||
if (!_rangeAllowed)
|
||||
{
|
||||
NumberOfParts = 1;
|
||||
}
|
||||
else if (NumberOfParts > maximumPart)
|
||||
{
|
||||
NumberOfParts = maximumPart;
|
||||
}
|
||||
|
||||
for (int i = 0; i < NumberOfParts; i++)
|
||||
{
|
||||
var temp = CreateNew(i, NumberOfParts, Size);
|
||||
temp.DownloadPartProgressChanged += temp_DownloadPartProgressChanged;
|
||||
temp.DownloadPartCompleted += temp_DownloadPartCompleted;
|
||||
lock (this)
|
||||
{
|
||||
PartialDownloaderList.Add(temp);
|
||||
}
|
||||
|
||||
temp.Start(_requestConfigure);
|
||||
}
|
||||
}
|
||||
|
||||
private void MergeParts()
|
||||
{
|
||||
var mergeOrderedList = PartialDownloaderList.OrderBy(x => x.From);
|
||||
var dir = new FileInfo(FilePath).DirectoryName;
|
||||
Directory.CreateDirectory(dir);
|
||||
|
||||
|
||||
using (var fs = File.OpenWrite(FilePath))
|
||||
{
|
||||
long totalBytesWrite = 0;
|
||||
int mergeProgress = 0;
|
||||
foreach (var item in mergeOrderedList)
|
||||
{
|
||||
using (var pdi = File.OpenRead(item.FullPath))
|
||||
{
|
||||
byte[] buffer = new byte[4096];
|
||||
int read;
|
||||
while ((read = pdi.Read(buffer, 0, buffer.Length)) > 0)
|
||||
{
|
||||
fs.Write(buffer, 0, read);
|
||||
totalBytesWrite += read;
|
||||
int temp = (int)(totalBytesWrite * 1d / Size * 100);
|
||||
if (temp != mergeProgress && FileMergeProgressChanged != null)
|
||||
{
|
||||
mergeProgress = temp;
|
||||
_aop.Post(state => FileMergeProgressChanged(this, temp), null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
File.Delete(item.FullPath);
|
||||
}
|
||||
catch
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (FileMergedComplete != null)
|
||||
{
|
||||
_aop.Post(state => FileMergedComplete(state, EventArgs.Empty), this);
|
||||
}
|
||||
}
|
||||
|
||||
private PartialDownloader CreateNew(int order, int parts, long contentLength)
|
||||
{
|
||||
var division = contentLength / parts;
|
||||
var remaining = contentLength % parts;
|
||||
var start = division * order;
|
||||
var end = start + division - 1;
|
||||
end += order == parts - 1 ? remaining : 0;
|
||||
return new PartialDownloader(_url, TempFileDirectory, Guid.NewGuid().ToString("N"), start, end, true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 暂停或继续
|
||||
/// </summary>
|
||||
/// <param name="list"></param>
|
||||
/// <param name="wait"></param>
|
||||
public static void WaitOrResumeAll(List<PartialDownloader> list, bool wait)
|
||||
{
|
||||
for (var index = 0; index < list.Count; index++)
|
||||
{
|
||||
if (wait)
|
||||
{
|
||||
list[index].Wait();
|
||||
}
|
||||
else
|
||||
{
|
||||
list[index].ResumeAfterWait();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 配置请求头
|
||||
/// </summary>
|
||||
/// <param name="config"></param>
|
||||
public void Configure(Action<HttpWebRequest> config)
|
||||
{
|
||||
_requestConfigure = config;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取内容长度
|
||||
/// </summary>
|
||||
/// <param name="rangeAllowed"></param>
|
||||
/// <param name="redirectedUrl"></param>
|
||||
/// <returns></returns>
|
||||
public long GetContentLength(ref bool rangeAllowed, ref string redirectedUrl)
|
||||
{
|
||||
_request.UserAgent =
|
||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.122 Safari/537.36";
|
||||
_request.ServicePoint.ConnectionLimit = 4;
|
||||
_requestConfigure(_request);
|
||||
|
||||
using (var resp = _request.GetResponse() as HttpWebResponse)
|
||||
{
|
||||
redirectedUrl = resp.ResponseUri.OriginalString;
|
||||
var ctl = resp.ContentLength;
|
||||
rangeAllowed = resp.Headers.AllKeys.Select((v, i) => new
|
||||
{
|
||||
HeaderName = v,
|
||||
HeaderValue = resp.Headers[i]
|
||||
}).Any(k => k.HeaderName.ToLower().Contains("range") && k.HeaderValue.ToLower().Contains("byte"));
|
||||
_request.Abort();
|
||||
return ctl;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion 方法
|
||||
|
||||
#region 公共方法
|
||||
|
||||
/// <summary>
|
||||
/// 暂停下载
|
||||
/// </summary>
|
||||
public void Pause()
|
||||
{
|
||||
lock (this)
|
||||
{
|
||||
foreach (var t in PartialDownloaderList.Where(t => !t.Completed))
|
||||
{
|
||||
t.Stop();
|
||||
}
|
||||
}
|
||||
|
||||
Thread.Sleep(200);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 开始下载
|
||||
/// </summary>
|
||||
public void Start()
|
||||
{
|
||||
Task th = new Task(CreateFirstPartitions);
|
||||
th.Start();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 唤醒下载
|
||||
/// </summary>
|
||||
public void Resume()
|
||||
{
|
||||
int count = PartialDownloaderList.Count;
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
if (PartialDownloaderList[i].Stopped)
|
||||
{
|
||||
var from = PartialDownloaderList[i].CurrentPosition + 1;
|
||||
var to = PartialDownloaderList[i].To;
|
||||
if (from > to)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var temp = new PartialDownloader(_url, TempFileDirectory, Guid.NewGuid().ToString(), from, to,
|
||||
_rangeAllowed);
|
||||
temp.DownloadPartProgressChanged += temp_DownloadPartProgressChanged;
|
||||
temp.DownloadPartCompleted += temp_DownloadPartCompleted;
|
||||
lock (this)
|
||||
{
|
||||
PartialDownloaderList.Add(temp);
|
||||
}
|
||||
|
||||
PartialDownloaderList[i].To = PartialDownloaderList[i].CurrentPosition;
|
||||
temp.Start(_requestConfigure);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion 公共方法
|
||||
}
|
||||
@@ -1,263 +0,0 @@
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
using System.Net;
|
||||
|
||||
namespace DownKyi.Core.Downloader;
|
||||
|
||||
/// <summary>
|
||||
/// 部分下载器
|
||||
/// </summary>
|
||||
public class PartialDownloader
|
||||
{
|
||||
/// <summary>
|
||||
/// 这部分完成事件
|
||||
/// </summary>
|
||||
public event EventHandler DownloadPartCompleted;
|
||||
|
||||
/// <summary>
|
||||
/// 部分下载进度改变事件
|
||||
/// </summary>
|
||||
public event EventHandler DownloadPartProgressChanged;
|
||||
|
||||
/// <summary>
|
||||
/// 部分下载停止事件
|
||||
/// </summary>
|
||||
public event EventHandler DownloadPartStopped;
|
||||
|
||||
private readonly AsyncOperation _aop = AsyncOperationManager.CreateOperation(null);
|
||||
private readonly int[] _lastSpeeds;
|
||||
private long _counter;
|
||||
private long _to;
|
||||
private long _totalBytesRead;
|
||||
private bool _wait;
|
||||
|
||||
/// <summary>
|
||||
/// 下载已停止
|
||||
/// </summary>
|
||||
public bool Stopped { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// 下载已完成
|
||||
/// </summary>
|
||||
public bool Completed { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// 下载进度
|
||||
/// </summary>
|
||||
public int Progress { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// 下载目录
|
||||
/// </summary>
|
||||
public string Directory { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 文件名
|
||||
/// </summary>
|
||||
public string FileName { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 已读字节数
|
||||
/// </summary>
|
||||
public long TotalBytesRead => _totalBytesRead;
|
||||
|
||||
/// <summary>
|
||||
/// 内容长度
|
||||
/// </summary>
|
||||
public long ContentLength { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// RangeAllowed
|
||||
/// </summary>
|
||||
public bool RangeAllowed { get; }
|
||||
|
||||
/// <summary>
|
||||
/// url
|
||||
/// </summary>
|
||||
public string Url { get; }
|
||||
|
||||
/// <summary>
|
||||
/// to
|
||||
/// </summary>
|
||||
public long To
|
||||
{
|
||||
get => _to;
|
||||
set
|
||||
{
|
||||
_to = value;
|
||||
ContentLength = _to - From + 1;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// from
|
||||
/// </summary>
|
||||
public long From { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 当前位置
|
||||
/// </summary>
|
||||
public long CurrentPosition => From + _totalBytesRead - 1;
|
||||
|
||||
/// <summary>
|
||||
/// 剩余字节数
|
||||
/// </summary>
|
||||
public long RemainingBytes => ContentLength - _totalBytesRead;
|
||||
|
||||
/// <summary>
|
||||
/// 完整路径
|
||||
/// </summary>
|
||||
public string FullPath => Path.Combine(Directory, FileName);
|
||||
|
||||
/// <summary>
|
||||
/// 下载速度
|
||||
/// </summary>
|
||||
public int SpeedInBytes
|
||||
{
|
||||
get
|
||||
{
|
||||
if (Completed)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int totalSpeeds = _lastSpeeds.Sum();
|
||||
return totalSpeeds / 10;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 部分块下载
|
||||
/// </summary>
|
||||
/// <param name="url"></param>
|
||||
/// <param name="dir"></param>
|
||||
/// <param name="fileGuid"></param>
|
||||
/// <param name="from"></param>
|
||||
/// <param name="to"></param>
|
||||
/// <param name="rangeAllowed"></param>
|
||||
public PartialDownloader(string url, string dir, string fileGuid, long from, long to, bool rangeAllowed)
|
||||
{
|
||||
From = from;
|
||||
_to = to;
|
||||
Url = url;
|
||||
RangeAllowed = rangeAllowed;
|
||||
FileName = fileGuid;
|
||||
Directory = dir;
|
||||
_lastSpeeds = new int[10];
|
||||
}
|
||||
|
||||
private void DownloadProcedure(Action<HttpWebRequest> config)
|
||||
{
|
||||
using (var file = new FileStream(FullPath, FileMode.Create, FileAccess.ReadWrite, FileShare.Delete))
|
||||
{
|
||||
var sw = new Stopwatch();
|
||||
if (WebRequest.Create(Url) is HttpWebRequest req)
|
||||
{
|
||||
req.UserAgent =
|
||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.122 Safari/537.36";
|
||||
req.AllowAutoRedirect = true;
|
||||
req.MaximumAutomaticRedirections = 5;
|
||||
req.ServicePoint.ConnectionLimit += 1;
|
||||
req.ServicePoint.Expect100Continue = true;
|
||||
req.ProtocolVersion = HttpVersion.Version11;
|
||||
req.Proxy = WebRequest.GetSystemWebProxy();
|
||||
config(req);
|
||||
if (RangeAllowed)
|
||||
{
|
||||
req.AddRange(From, _to);
|
||||
}
|
||||
|
||||
if (req.GetResponse() is HttpWebResponse resp)
|
||||
{
|
||||
ContentLength = resp.ContentLength;
|
||||
if (ContentLength <= 0 || (RangeAllowed && ContentLength != _to - From + 1))
|
||||
{
|
||||
throw new Exception("Invalid response content");
|
||||
}
|
||||
|
||||
using (var tempStream = resp.GetResponseStream())
|
||||
{
|
||||
int bytesRead;
|
||||
byte[] buffer = new byte[4096];
|
||||
sw.Start();
|
||||
while ((bytesRead = tempStream.Read(buffer, 0, buffer.Length)) > 0)
|
||||
{
|
||||
if (_totalBytesRead + bytesRead > ContentLength)
|
||||
{
|
||||
bytesRead = (int)(ContentLength - _totalBytesRead);
|
||||
}
|
||||
|
||||
file.Write(buffer, 0, bytesRead);
|
||||
_totalBytesRead += bytesRead;
|
||||
_lastSpeeds[_counter] = (int)(_totalBytesRead / Math.Ceiling(sw.Elapsed.TotalSeconds));
|
||||
_counter = (_counter >= 9) ? 0 : _counter + 1;
|
||||
int tempProgress = (int)(_totalBytesRead * 100 / ContentLength);
|
||||
if (Progress != tempProgress)
|
||||
{
|
||||
Progress = tempProgress;
|
||||
_aop.Post(state => { DownloadPartProgressChanged?.Invoke(this, EventArgs.Empty); },
|
||||
null);
|
||||
}
|
||||
|
||||
if (Stopped || (RangeAllowed && _totalBytesRead == ContentLength))
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
req.Abort();
|
||||
}
|
||||
|
||||
sw.Stop();
|
||||
if (!Stopped && DownloadPartCompleted != null)
|
||||
{
|
||||
_aop.Post(state =>
|
||||
{
|
||||
Completed = true;
|
||||
DownloadPartCompleted(this, EventArgs.Empty);
|
||||
}, null);
|
||||
}
|
||||
|
||||
if (Stopped && DownloadPartStopped != null)
|
||||
{
|
||||
_aop.Post(state => DownloadPartStopped(this, EventArgs.Empty), null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 启动下载
|
||||
/// </summary>
|
||||
public void Start(Action<HttpWebRequest> config)
|
||||
{
|
||||
Stopped = false;
|
||||
var procThread = new Thread(_ => DownloadProcedure(config));
|
||||
procThread.Start();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 下载停止
|
||||
/// </summary>
|
||||
public void Stop()
|
||||
{
|
||||
Stopped = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 暂停等待下载
|
||||
/// </summary>
|
||||
public void Wait()
|
||||
{
|
||||
_wait = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 稍后唤醒
|
||||
/// </summary>
|
||||
public void ResumeAfterWait()
|
||||
{
|
||||
_wait = false;
|
||||
}
|
||||
}
|
||||
@@ -17,8 +17,7 @@ public class FFMpeg
|
||||
|
||||
private FFMpeg()
|
||||
{
|
||||
GlobalFFOptions.Configure(new FFOptions
|
||||
{ BinaryFolder = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "ffmpeg") });
|
||||
GlobalFFOptions.Configure(new FFOptions { BinaryFolder = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "ffmpeg") });
|
||||
FFMpegHelper.VerifyFFMpegExists(GlobalFFOptions.Current);
|
||||
}
|
||||
|
||||
@@ -54,7 +53,7 @@ public class FFMpeg
|
||||
|
||||
if (video == null || !File.Exists(video))
|
||||
{
|
||||
if (SettingsManager.GetInstance().IsTranscodingAacToMp3() == AllowStatus.YES)
|
||||
if (SettingsManager.GetInstance().GetIsTranscodingAacToMp3() == AllowStatus.Yes)
|
||||
{
|
||||
arguments = FFMpegArguments.FromFileInput(audio).OutputToFile(
|
||||
destVideo,
|
||||
|
||||
@@ -4,28 +4,28 @@ namespace DownKyi.Core.FileName;
|
||||
|
||||
public class FileName
|
||||
{
|
||||
private readonly List<FileNamePart> nameParts;
|
||||
private string order = "ORDER";
|
||||
private string section = "SECTION";
|
||||
private string mainTitle = "MAIN_TITLE";
|
||||
private string pageTitle = "PAGE_TITLE";
|
||||
private string videoZone = "VIDEO_ZONE";
|
||||
private string audioQuality = "AUDIO_QUALITY";
|
||||
private string videoQuality = "VIDEO_QUALITY";
|
||||
private string videoCodec = "VIDEO_CODEC";
|
||||
private readonly List<FileNamePart> _nameParts;
|
||||
private string _order = "ORDER";
|
||||
private string _section = "SECTION";
|
||||
private string _mainTitle = "MAIN_TITLE";
|
||||
private string _pageTitle = "PAGE_TITLE";
|
||||
private string _videoZone = "VIDEO_ZONE";
|
||||
private string _audioQuality = "AUDIO_QUALITY";
|
||||
private string _videoQuality = "VIDEO_QUALITY";
|
||||
private string _videoCodec = "VIDEO_CODEC";
|
||||
|
||||
private string videoPublishTime = "VIDEO_PUBLISH_TIME";
|
||||
private string _videoPublishTime = "VIDEO_PUBLISH_TIME";
|
||||
|
||||
private long avid = -1;
|
||||
private string bvid = "BVID";
|
||||
private long cid = -1;
|
||||
private long _avid = -1;
|
||||
private string _bvid = "BVID";
|
||||
private long _cid = -1;
|
||||
|
||||
private long upMid = -1;
|
||||
private string upName = "UP_NAME";
|
||||
private long _upMid = -1;
|
||||
private string _upName = "UP_NAME";
|
||||
|
||||
private FileName(List<FileNamePart> nameParts)
|
||||
{
|
||||
this.nameParts = nameParts;
|
||||
this._nameParts = nameParts;
|
||||
}
|
||||
|
||||
public static FileName Builder(List<FileNamePart> nameParts)
|
||||
@@ -35,149 +35,149 @@ public class FileName
|
||||
|
||||
public FileName SetOrder(int order)
|
||||
{
|
||||
this.order = order.ToString();
|
||||
_order = order.ToString();
|
||||
return this;
|
||||
}
|
||||
|
||||
public FileName SetOrder(int order, int count)
|
||||
{
|
||||
int length = Math.Abs(count).ToString().Length;
|
||||
this.order = order.ToString("D" + length);
|
||||
var length = Math.Abs(count).ToString().Length;
|
||||
_order = order.ToString("D" + length);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public FileName SetSection(string section)
|
||||
{
|
||||
this.section = section;
|
||||
_section = section;
|
||||
return this;
|
||||
}
|
||||
|
||||
public FileName SetMainTitle(string mainTitle)
|
||||
{
|
||||
this.mainTitle = mainTitle;
|
||||
_mainTitle = mainTitle;
|
||||
return this;
|
||||
}
|
||||
|
||||
public FileName SetPageTitle(string pageTitle)
|
||||
{
|
||||
this.pageTitle = pageTitle;
|
||||
_pageTitle = pageTitle;
|
||||
return this;
|
||||
}
|
||||
|
||||
public FileName SetVideoZone(string videoZone)
|
||||
{
|
||||
this.videoZone = videoZone;
|
||||
_videoZone = videoZone;
|
||||
return this;
|
||||
}
|
||||
|
||||
public FileName SetAudioQuality(string audioQuality)
|
||||
{
|
||||
this.audioQuality = audioQuality;
|
||||
_audioQuality = audioQuality;
|
||||
return this;
|
||||
}
|
||||
|
||||
public FileName SetVideoQuality(string videoQuality)
|
||||
{
|
||||
this.videoQuality = videoQuality;
|
||||
_videoQuality = videoQuality;
|
||||
return this;
|
||||
}
|
||||
|
||||
public FileName SetVideoCodec(string videoCodec)
|
||||
{
|
||||
this.videoCodec = videoCodec;
|
||||
_videoCodec = videoCodec;
|
||||
return this;
|
||||
}
|
||||
|
||||
public FileName SetVideoPublishTime(string videoPublishTime)
|
||||
{
|
||||
this.videoPublishTime = videoPublishTime;
|
||||
_videoPublishTime = videoPublishTime;
|
||||
return this;
|
||||
}
|
||||
|
||||
public FileName SetAvid(long avid)
|
||||
{
|
||||
this.avid = avid;
|
||||
_avid = avid;
|
||||
return this;
|
||||
}
|
||||
|
||||
public FileName SetBvid(string bvid)
|
||||
{
|
||||
this.bvid = bvid;
|
||||
_bvid = bvid;
|
||||
return this;
|
||||
}
|
||||
|
||||
public FileName SetCid(long cid)
|
||||
{
|
||||
this.cid = cid;
|
||||
_cid = cid;
|
||||
return this;
|
||||
}
|
||||
|
||||
public FileName SetUpMid(long upMid)
|
||||
{
|
||||
this.upMid = upMid;
|
||||
_upMid = upMid;
|
||||
return this;
|
||||
}
|
||||
|
||||
public FileName SetUpName(string upName)
|
||||
{
|
||||
this.upName = upName;
|
||||
_upName = upName;
|
||||
return this;
|
||||
}
|
||||
|
||||
public string RelativePath()
|
||||
{
|
||||
string path = string.Empty;
|
||||
var path = string.Empty;
|
||||
|
||||
foreach (FileNamePart part in nameParts)
|
||||
foreach (var part in _nameParts)
|
||||
{
|
||||
switch (part)
|
||||
{
|
||||
case FileNamePart.ORDER:
|
||||
path += order;
|
||||
case FileNamePart.Order:
|
||||
path += _order;
|
||||
break;
|
||||
case FileNamePart.SECTION:
|
||||
path += section;
|
||||
case FileNamePart.Section:
|
||||
path += _section;
|
||||
break;
|
||||
case FileNamePart.MAIN_TITLE:
|
||||
path += mainTitle;
|
||||
case FileNamePart.MainTitle:
|
||||
path += _mainTitle;
|
||||
break;
|
||||
case FileNamePart.PAGE_TITLE:
|
||||
path += pageTitle;
|
||||
case FileNamePart.PageTitle:
|
||||
path += _pageTitle;
|
||||
break;
|
||||
case FileNamePart.VIDEO_ZONE:
|
||||
path += videoZone;
|
||||
case FileNamePart.VideoZone:
|
||||
path += _videoZone;
|
||||
break;
|
||||
case FileNamePart.AUDIO_QUALITY:
|
||||
path += audioQuality;
|
||||
case FileNamePart.AudioQuality:
|
||||
path += _audioQuality;
|
||||
break;
|
||||
case FileNamePart.VIDEO_QUALITY:
|
||||
path += videoQuality;
|
||||
case FileNamePart.VideoQuality:
|
||||
path += _videoQuality;
|
||||
break;
|
||||
case FileNamePart.VIDEO_CODEC:
|
||||
path += videoCodec;
|
||||
case FileNamePart.VideoCodec:
|
||||
path += _videoCodec;
|
||||
break;
|
||||
case FileNamePart.VIDEO_PUBLISH_TIME:
|
||||
path += videoPublishTime;
|
||||
case FileNamePart.VideoPublishTime:
|
||||
path += _videoPublishTime;
|
||||
break;
|
||||
case FileNamePart.AVID:
|
||||
path += $"av{avid}";
|
||||
case FileNamePart.Avid:
|
||||
path += $"av{_avid}";
|
||||
break;
|
||||
case FileNamePart.BVID:
|
||||
path += bvid;
|
||||
case FileNamePart.Bvid:
|
||||
path += _bvid;
|
||||
break;
|
||||
case FileNamePart.CID:
|
||||
path += cid;
|
||||
case FileNamePart.Cid:
|
||||
path += _cid;
|
||||
break;
|
||||
case FileNamePart.UP_MID:
|
||||
path += upMid;
|
||||
case FileNamePart.UpMid:
|
||||
path += _upMid;
|
||||
break;
|
||||
case FileNamePart.UP_NAME:
|
||||
path += upName;
|
||||
case FileNamePart.UpName:
|
||||
path += _upName;
|
||||
break;
|
||||
}
|
||||
|
||||
if (((int)part) >= 100)
|
||||
if ((int)part >= 100)
|
||||
{
|
||||
path += HyphenSeparated.Hyphen[(int)part];
|
||||
}
|
||||
|
||||
@@ -3,40 +3,40 @@
|
||||
public enum FileNamePart
|
||||
{
|
||||
// Video
|
||||
ORDER = 1,
|
||||
SECTION,
|
||||
MAIN_TITLE,
|
||||
PAGE_TITLE,
|
||||
VIDEO_ZONE,
|
||||
AUDIO_QUALITY,
|
||||
VIDEO_QUALITY,
|
||||
VIDEO_CODEC,
|
||||
Order = 1,
|
||||
Section,
|
||||
MainTitle,
|
||||
PageTitle,
|
||||
VideoZone,
|
||||
AudioQuality,
|
||||
VideoQuality,
|
||||
VideoCodec,
|
||||
|
||||
VIDEO_PUBLISH_TIME,
|
||||
VideoPublishTime,
|
||||
|
||||
AVID,
|
||||
BVID,
|
||||
CID,
|
||||
Avid,
|
||||
Bvid,
|
||||
Cid,
|
||||
|
||||
UP_MID,
|
||||
UP_NAME,
|
||||
UpMid,
|
||||
UpName,
|
||||
|
||||
// 斜杠
|
||||
SLASH = 100,
|
||||
Slash = 100,
|
||||
|
||||
// HyphenSeparated
|
||||
UNDERSCORE = 101, // 下划线
|
||||
HYPHEN, // 连字符
|
||||
PLUS, // 加号
|
||||
COMMA, // 逗号
|
||||
PERIOD, // 句号
|
||||
AND, // and
|
||||
NUMBER, // #
|
||||
OPEN_PAREN, // 左圆括号
|
||||
CLOSE_PAREN, // 右圆括号
|
||||
OPEN_BRACKET, // 左方括号
|
||||
CLOSE_BRACKET, // 右方括号
|
||||
OPEN_BRACE, // 左花括号
|
||||
CLOSE_brace, // 右花括号
|
||||
BLANK, // 空白符
|
||||
Underscore = 101, // 下划线
|
||||
Hyphen, // 连字符
|
||||
Plus, // 加号
|
||||
Comma, // 逗号
|
||||
Period, // 句号
|
||||
And, // and
|
||||
Number, // #
|
||||
OpenParen, // 左圆括号
|
||||
CloseParen, // 右圆括号
|
||||
OpenBracket, // 左方括号
|
||||
CloseBracket, // 右方括号
|
||||
OpenBrace, // 左花括号
|
||||
CloseBrace, // 右花括号
|
||||
Blank, // 空白符
|
||||
}
|
||||
@@ -6,7 +6,7 @@
|
||||
public static class HyphenSeparated
|
||||
{
|
||||
// 文件名的分隔符
|
||||
public static Dictionary<int, string> Hyphen = new Dictionary<int, string>()
|
||||
public static readonly Dictionary<int, string> Hyphen = new()
|
||||
{
|
||||
{ 100, "/" },
|
||||
{ 101, "_" },
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
using System.Text.RegularExpressions;
|
||||
using DownKyi.Core.Storage;
|
||||
using static System.DateTime;
|
||||
using static System.Threading.Thread;
|
||||
|
||||
namespace DownKyi.Core.Logging;
|
||||
|
||||
@@ -10,29 +11,28 @@ namespace DownKyi.Core.Logging;
|
||||
/// </summary>
|
||||
public class LogManager
|
||||
{
|
||||
private static readonly ConcurrentQueue<Tuple<string, string>> LogQueue =
|
||||
new ConcurrentQueue<Tuple<string, string>>();
|
||||
private static readonly ConcurrentQueue<Tuple<string, string>> LogQueue = new();
|
||||
|
||||
/// <summary>
|
||||
/// 自定义事件
|
||||
/// </summary>
|
||||
public static event Action<LogInfo> Event;
|
||||
public static event Action<LogInfo>? Event;
|
||||
|
||||
static LogManager()
|
||||
{
|
||||
var writeTask = new Task(obj =>
|
||||
{
|
||||
var temp = new List<string[]>();
|
||||
while (true)
|
||||
{
|
||||
Pause.WaitOne(1000, true);
|
||||
List<string[]> temp = new List<string[]>();
|
||||
foreach (var logItem in LogQueue)
|
||||
{
|
||||
string logPath = logItem.Item1;
|
||||
string logMergeContent = string.Concat(logItem.Item2, Environment.NewLine,
|
||||
var logPath = logItem.Item1;
|
||||
var logMergeContent = string.Concat(logItem.Item2, Environment.NewLine,
|
||||
"----------------------------------------------------------------------------------------------------------------------",
|
||||
Environment.NewLine);
|
||||
string[] logArr = temp.FirstOrDefault(d => d[0].Equals(logPath));
|
||||
var logArr = temp.FirstOrDefault(d => d[0].Equals(logPath));
|
||||
if (logArr != null)
|
||||
{
|
||||
logArr[1] = string.Concat(logArr[1], logMergeContent);
|
||||
@@ -59,7 +59,7 @@ public class LogManager
|
||||
writeTask.Start();
|
||||
}
|
||||
|
||||
private static AutoResetEvent Pause => new AutoResetEvent(false);
|
||||
private static AutoResetEvent Pause => new(false);
|
||||
|
||||
/// <summary>
|
||||
/// 日志存放目录,windows默认日志放在当前应用程序运行目录下的Logs文件夹中,macOS、linux存放于applicationData目录下
|
||||
@@ -72,14 +72,13 @@ public class LogManager
|
||||
/// <param name="info"></param>
|
||||
public static void Info(string info)
|
||||
{
|
||||
LogQueue.Enqueue(new Tuple<string, string>(GetLogPath(),
|
||||
$"{Now} [{Thread.CurrentThread.ManagedThreadId}] {nameof(info).ToUpper()} {info}"));
|
||||
var log = new LogInfo()
|
||||
LogQueue.Enqueue(new Tuple<string, string>(GetLogPath(), $"{Now} [{Environment.CurrentManagedThreadId}] {nameof(info).ToUpper()} {info}"));
|
||||
var log = new LogInfo
|
||||
{
|
||||
LogLevel = LogLevel.Info,
|
||||
Message = info,
|
||||
Time = Now,
|
||||
ThreadId = Thread.CurrentThread.ManagedThreadId
|
||||
ThreadId = Environment.CurrentManagedThreadId
|
||||
};
|
||||
Event?.Invoke(log);
|
||||
}
|
||||
@@ -91,14 +90,13 @@ public class LogManager
|
||||
/// <param name="info"></param>
|
||||
public static void Info(string source, string info)
|
||||
{
|
||||
LogQueue.Enqueue(new Tuple<string, string>(GetLogPath(),
|
||||
$"{Now} [{Thread.CurrentThread.ManagedThreadId}] {nameof(info).ToUpper()} {source} {info}"));
|
||||
LogInfo log = new LogInfo()
|
||||
LogQueue.Enqueue(new Tuple<string, string>(GetLogPath(), $"{Now} [{Environment.CurrentManagedThreadId}] {nameof(info).ToUpper()} {source} {info}"));
|
||||
var log = new LogInfo
|
||||
{
|
||||
LogLevel = LogLevel.Info,
|
||||
Message = info,
|
||||
Time = Now,
|
||||
ThreadId = Thread.CurrentThread.ManagedThreadId,
|
||||
ThreadId = Environment.CurrentManagedThreadId,
|
||||
Source = source
|
||||
};
|
||||
Event?.Invoke(log);
|
||||
@@ -112,13 +110,13 @@ public class LogManager
|
||||
public static void Info(Type source, string info)
|
||||
{
|
||||
LogQueue.Enqueue(new Tuple<string, string>(GetLogPath(),
|
||||
$"{Now} [{Thread.CurrentThread.ManagedThreadId}] {nameof(info).ToUpper()} {source.FullName} {info}"));
|
||||
LogInfo log = new LogInfo()
|
||||
$"{Now} [{Environment.CurrentManagedThreadId}] {nameof(info).ToUpper()} {source.FullName} {info}"));
|
||||
var log = new LogInfo
|
||||
{
|
||||
LogLevel = LogLevel.Info,
|
||||
Message = info,
|
||||
Time = Now,
|
||||
ThreadId = Thread.CurrentThread.ManagedThreadId,
|
||||
ThreadId = Environment.CurrentManagedThreadId,
|
||||
Source = source.FullName
|
||||
};
|
||||
Event?.Invoke(log);
|
||||
@@ -130,14 +128,13 @@ public class LogManager
|
||||
/// <param name="debug">异常对象</param>
|
||||
public static void Debug(string debug)
|
||||
{
|
||||
LogQueue.Enqueue(new Tuple<string, string>(GetLogPath(),
|
||||
$"{Now} [{Thread.CurrentThread.ManagedThreadId}] {nameof(debug).ToUpper()} {debug}"));
|
||||
LogInfo log = new LogInfo()
|
||||
LogQueue.Enqueue(new Tuple<string, string>(GetLogPath(), $"{Now} [{Environment.CurrentManagedThreadId}] {nameof(debug).ToUpper()} {debug}"));
|
||||
var log = new LogInfo
|
||||
{
|
||||
LogLevel = LogLevel.Debug,
|
||||
Message = debug,
|
||||
Time = Now,
|
||||
ThreadId = Thread.CurrentThread.ManagedThreadId
|
||||
ThreadId = Environment.CurrentManagedThreadId
|
||||
};
|
||||
Event?.Invoke(log);
|
||||
}
|
||||
@@ -149,14 +146,13 @@ public class LogManager
|
||||
/// <param name="debug">异常对象</param>
|
||||
public static void Debug(string source, string debug)
|
||||
{
|
||||
LogQueue.Enqueue(new Tuple<string, string>(GetLogPath(),
|
||||
$"{Now} [{Thread.CurrentThread.ManagedThreadId}] {nameof(debug).ToUpper()} {source} {debug}"));
|
||||
LogInfo log = new LogInfo()
|
||||
LogQueue.Enqueue(new Tuple<string, string>(GetLogPath(), $"{Now} [{Environment.CurrentManagedThreadId}] {nameof(debug).ToUpper()} {source} {debug}"));
|
||||
var log = new LogInfo
|
||||
{
|
||||
LogLevel = LogLevel.Debug,
|
||||
Message = debug,
|
||||
Time = Now,
|
||||
ThreadId = Thread.CurrentThread.ManagedThreadId,
|
||||
ThreadId = Environment.CurrentManagedThreadId,
|
||||
Source = source
|
||||
};
|
||||
Event?.Invoke(log);
|
||||
@@ -169,14 +165,13 @@ public class LogManager
|
||||
/// <param name="debug">异常对象</param>
|
||||
public static void Debug(Type source, string debug)
|
||||
{
|
||||
LogQueue.Enqueue(new Tuple<string, string>(GetLogPath(),
|
||||
$"{Now} [{Thread.CurrentThread.ManagedThreadId}] {nameof(debug).ToUpper()} {source.FullName} {debug}"));
|
||||
LogInfo log = new LogInfo()
|
||||
LogQueue.Enqueue(new Tuple<string, string>(GetLogPath(), $"{Now} [{Environment.CurrentManagedThreadId}] {nameof(debug).ToUpper()} {source.FullName} {debug}"));
|
||||
var log = new LogInfo
|
||||
{
|
||||
LogLevel = LogLevel.Debug,
|
||||
Message = debug,
|
||||
Time = Now,
|
||||
ThreadId = Thread.CurrentThread.ManagedThreadId,
|
||||
ThreadId = Environment.CurrentManagedThreadId,
|
||||
Source = source.FullName
|
||||
};
|
||||
Event?.Invoke(log);
|
||||
@@ -189,13 +184,13 @@ public class LogManager
|
||||
public static void Error(Exception error)
|
||||
{
|
||||
LogQueue.Enqueue(new Tuple<string, string>(GetLogPath(),
|
||||
$"{Now} [{Thread.CurrentThread.ManagedThreadId}] {nameof(error).ToUpper()} {error.Source} {error.Message}{Environment.NewLine}{error.StackTrace}"));
|
||||
LogInfo log = new LogInfo()
|
||||
$"{Now} [{Environment.CurrentManagedThreadId}] {nameof(error).ToUpper()} {error.Source} {error.Message}{Environment.NewLine}{error.StackTrace}"));
|
||||
var log = new LogInfo
|
||||
{
|
||||
LogLevel = LogLevel.Error,
|
||||
Message = error.Message,
|
||||
Time = Now,
|
||||
ThreadId = Thread.CurrentThread.ManagedThreadId,
|
||||
ThreadId = Environment.CurrentManagedThreadId,
|
||||
Source = error.Source,
|
||||
Exception = error,
|
||||
ExceptionType = error.GetType().Name
|
||||
@@ -211,13 +206,13 @@ public class LogManager
|
||||
public static void Error(Type source, Exception error)
|
||||
{
|
||||
LogQueue.Enqueue(new Tuple<string, string>(GetLogPath(),
|
||||
$"{Now} [{Thread.CurrentThread.ManagedThreadId}] {nameof(error).ToUpper()} {source.FullName} {error.Message}{Environment.NewLine}{error.StackTrace}"));
|
||||
LogInfo log = new LogInfo()
|
||||
$"{Now} [{Environment.CurrentManagedThreadId}] {nameof(error).ToUpper()} {source.FullName} {error.Message}{Environment.NewLine}{error.StackTrace}"));
|
||||
var log = new LogInfo
|
||||
{
|
||||
LogLevel = LogLevel.Error,
|
||||
Message = error.Message,
|
||||
Time = Now,
|
||||
ThreadId = Thread.CurrentThread.ManagedThreadId,
|
||||
ThreadId = Environment.CurrentManagedThreadId,
|
||||
Source = source.FullName,
|
||||
Exception = error,
|
||||
ExceptionType = error.GetType().Name
|
||||
@@ -232,14 +227,13 @@ public class LogManager
|
||||
/// <param name="error">异常信息</param>
|
||||
public static void Error(Type source, string error)
|
||||
{
|
||||
LogQueue.Enqueue(new Tuple<string, string>(GetLogPath(),
|
||||
$"{Now} [{Thread.CurrentThread.ManagedThreadId}] {nameof(error).ToUpper()} {source.FullName} {error}"));
|
||||
LogInfo log = new LogInfo()
|
||||
LogQueue.Enqueue(new Tuple<string, string>(GetLogPath(), $"{Now} [{Environment.CurrentManagedThreadId}] {nameof(error).ToUpper()} {source.FullName} {error}"));
|
||||
var log = new LogInfo
|
||||
{
|
||||
LogLevel = LogLevel.Error,
|
||||
Message = error,
|
||||
Time = Now,
|
||||
ThreadId = Thread.CurrentThread.ManagedThreadId,
|
||||
ThreadId = Environment.CurrentManagedThreadId,
|
||||
Source = source.FullName,
|
||||
//Exception = error,
|
||||
ExceptionType = error.GetType().Name
|
||||
@@ -255,13 +249,13 @@ public class LogManager
|
||||
public static void Error(string source, Exception error)
|
||||
{
|
||||
LogQueue.Enqueue(new Tuple<string, string>(GetLogPath(),
|
||||
$"{Now} [{Thread.CurrentThread.ManagedThreadId}] {nameof(error).ToUpper()} {source} {error.Message}{Environment.NewLine}{error.StackTrace}"));
|
||||
LogInfo log = new LogInfo()
|
||||
$"{Now} [{Environment.CurrentManagedThreadId}] {nameof(error).ToUpper()} {source} {error.Message}{Environment.NewLine}{error.StackTrace}"));
|
||||
var log = new LogInfo
|
||||
{
|
||||
LogLevel = LogLevel.Error,
|
||||
Message = error.Message,
|
||||
Time = Now,
|
||||
ThreadId = Thread.CurrentThread.ManagedThreadId,
|
||||
ThreadId = Environment.CurrentManagedThreadId,
|
||||
Source = source,
|
||||
Exception = error,
|
||||
ExceptionType = error.GetType().Name
|
||||
@@ -276,14 +270,13 @@ public class LogManager
|
||||
/// <param name="error">异常信息</param>
|
||||
public static void Error(string source, string error)
|
||||
{
|
||||
LogQueue.Enqueue(new Tuple<string, string>(GetLogPath(),
|
||||
$"{Now} [{Thread.CurrentThread.ManagedThreadId}] {nameof(error).ToUpper()} {source} {error}"));
|
||||
LogInfo log = new LogInfo()
|
||||
LogQueue.Enqueue(new Tuple<string, string>(GetLogPath(), $"{Now} [{Environment.CurrentManagedThreadId}] {nameof(error).ToUpper()} {source} {error}"));
|
||||
var log = new LogInfo
|
||||
{
|
||||
LogLevel = LogLevel.Error,
|
||||
Message = error,
|
||||
Time = Now,
|
||||
ThreadId = Thread.CurrentThread.ManagedThreadId,
|
||||
ThreadId = Environment.CurrentManagedThreadId,
|
||||
Source = source,
|
||||
//Exception = error,
|
||||
ExceptionType = error.GetType().Name
|
||||
@@ -298,13 +291,13 @@ public class LogManager
|
||||
public static void Fatal(Exception fatal)
|
||||
{
|
||||
LogQueue.Enqueue(new Tuple<string, string>(GetLogPath(),
|
||||
$"{Now} [{Thread.CurrentThread.ManagedThreadId}] {nameof(fatal).ToUpper()} {fatal.Source} {fatal.Message}{Environment.NewLine}{fatal.StackTrace}"));
|
||||
LogInfo log = new LogInfo()
|
||||
$"{Now} [{Environment.CurrentManagedThreadId}] {nameof(fatal).ToUpper()} {fatal.Source} {fatal.Message}{Environment.NewLine}{fatal.StackTrace}"));
|
||||
LogInfo log = new LogInfo
|
||||
{
|
||||
LogLevel = LogLevel.Fatal,
|
||||
Message = fatal.Message,
|
||||
Time = Now,
|
||||
ThreadId = Thread.CurrentThread.ManagedThreadId,
|
||||
ThreadId = Environment.CurrentManagedThreadId,
|
||||
Source = fatal.Source,
|
||||
Exception = fatal,
|
||||
ExceptionType = fatal.GetType().Name
|
||||
@@ -320,13 +313,13 @@ public class LogManager
|
||||
public static void Fatal(Type source, Exception fatal)
|
||||
{
|
||||
LogQueue.Enqueue(new Tuple<string, string>(GetLogPath(),
|
||||
$"{Now} [{Thread.CurrentThread.ManagedThreadId}] {nameof(fatal).ToUpper()} {source.FullName} {fatal.Message}{Environment.NewLine}{fatal.StackTrace}"));
|
||||
LogInfo log = new LogInfo()
|
||||
$"{Now} [{Environment.CurrentManagedThreadId}] {nameof(fatal).ToUpper()} {source.FullName} {fatal.Message}{Environment.NewLine}{fatal.StackTrace}"));
|
||||
var log = new LogInfo
|
||||
{
|
||||
LogLevel = LogLevel.Fatal,
|
||||
Message = fatal.Message,
|
||||
Time = Now,
|
||||
ThreadId = Thread.CurrentThread.ManagedThreadId,
|
||||
ThreadId = Environment.CurrentManagedThreadId,
|
||||
Source = source.FullName,
|
||||
Exception = fatal,
|
||||
ExceptionType = fatal.GetType().Name
|
||||
@@ -341,14 +334,13 @@ public class LogManager
|
||||
/// <param name="fatal">异常对象</param>
|
||||
public static void Fatal(Type source, string fatal)
|
||||
{
|
||||
LogQueue.Enqueue(new Tuple<string, string>(GetLogPath(),
|
||||
$"{Now} [{Thread.CurrentThread.ManagedThreadId}] {nameof(fatal).ToUpper()} {source.FullName} {fatal}"));
|
||||
LogInfo log = new LogInfo()
|
||||
LogQueue.Enqueue(new Tuple<string, string>(GetLogPath(), $"{Now} [{Environment.CurrentManagedThreadId}] {nameof(fatal).ToUpper()} {source.FullName} {fatal}"));
|
||||
var log = new LogInfo
|
||||
{
|
||||
LogLevel = LogLevel.Fatal,
|
||||
Message = fatal,
|
||||
Time = Now,
|
||||
ThreadId = Thread.CurrentThread.ManagedThreadId,
|
||||
ThreadId = Environment.CurrentManagedThreadId,
|
||||
Source = source.FullName,
|
||||
//Exception = fatal,
|
||||
ExceptionType = fatal.GetType().Name
|
||||
@@ -364,13 +356,13 @@ public class LogManager
|
||||
public static void Fatal(string source, Exception fatal)
|
||||
{
|
||||
LogQueue.Enqueue(new Tuple<string, string>(GetLogPath(),
|
||||
$"{Now} [{Thread.CurrentThread.ManagedThreadId}] {nameof(fatal).ToUpper()} {source} {fatal.Message}{Environment.NewLine}{fatal.StackTrace}"));
|
||||
LogInfo log = new LogInfo()
|
||||
$"{Now} [{Environment.CurrentManagedThreadId}] {nameof(fatal).ToUpper()} {source} {fatal.Message}{Environment.NewLine}{fatal.StackTrace}"));
|
||||
var log = new LogInfo
|
||||
{
|
||||
LogLevel = LogLevel.Fatal,
|
||||
Message = fatal.Message,
|
||||
Time = Now,
|
||||
ThreadId = Thread.CurrentThread.ManagedThreadId,
|
||||
ThreadId = Environment.CurrentManagedThreadId,
|
||||
Source = source,
|
||||
Exception = fatal,
|
||||
ExceptionType = fatal.GetType().Name
|
||||
@@ -385,14 +377,13 @@ public class LogManager
|
||||
/// <param name="fatal">异常对象</param>
|
||||
public static void Fatal(string source, string fatal)
|
||||
{
|
||||
LogQueue.Enqueue(new Tuple<string, string>(GetLogPath(),
|
||||
$"{Now} [{Thread.CurrentThread.ManagedThreadId}] {nameof(fatal).ToUpper()} {source} {fatal}"));
|
||||
LogInfo log = new LogInfo()
|
||||
LogQueue.Enqueue(new Tuple<string, string>(GetLogPath(), $"{Now} [{Environment.CurrentManagedThreadId}] {nameof(fatal).ToUpper()} {source} {fatal}"));
|
||||
var log = new LogInfo
|
||||
{
|
||||
LogLevel = LogLevel.Fatal,
|
||||
Message = fatal,
|
||||
Time = Now,
|
||||
ThreadId = Thread.CurrentThread.ManagedThreadId,
|
||||
ThreadId = Environment.CurrentManagedThreadId,
|
||||
Source = source,
|
||||
ExceptionType = fatal.GetType().Name
|
||||
};
|
||||
@@ -402,21 +393,18 @@ public class LogManager
|
||||
private static string GetLogPath()
|
||||
{
|
||||
string newFilePath;
|
||||
var logDir = string.IsNullOrEmpty(LogDirectory)
|
||||
? Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "logs")
|
||||
: LogDirectory;
|
||||
var logDir = string.IsNullOrEmpty(LogDirectory) ? Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "logs") : LogDirectory;
|
||||
Directory.CreateDirectory(logDir);
|
||||
string extension = ".log";
|
||||
string fileNameNotExt = Now.ToString("yyyyMMdd");
|
||||
string fileNamePattern = string.Concat(fileNameNotExt, "(*)", extension);
|
||||
List<string> filePaths = Directory.GetFiles(logDir, fileNamePattern, SearchOption.TopDirectoryOnly).ToList();
|
||||
const string extension = ".log";
|
||||
var fileNameNotExt = Now.ToString("yyyyMMdd");
|
||||
var fileNamePattern = string.Concat(fileNameNotExt, "(*)", extension);
|
||||
var filePaths = Directory.GetFiles(logDir, fileNamePattern, SearchOption.TopDirectoryOnly).ToList();
|
||||
|
||||
if (filePaths.Count > 0)
|
||||
{
|
||||
int fileMaxLen = filePaths.Max(d => d.Length);
|
||||
string lastFilePath =
|
||||
filePaths.Where(d => d.Length == fileMaxLen).OrderByDescending(d => d).FirstOrDefault();
|
||||
if (new FileInfo(lastFilePath).Length > 1 * 1024 * 1024)
|
||||
var fileMaxLen = filePaths.Max(d => d.Length);
|
||||
var lastFilePath = filePaths.Where(d => d.Length == fileMaxLen).OrderByDescending(d => d).FirstOrDefault();
|
||||
if (lastFilePath != null && new FileInfo(lastFilePath).Length > 1 * 1024 * 1024)
|
||||
{
|
||||
var no = new Regex(@"(?is)(?<=\()(.*)(?=\))").Match(Path.GetFileName(lastFilePath)).Value;
|
||||
var parse = int.TryParse(no, out int tempno);
|
||||
@@ -447,10 +435,8 @@ public class LogManager
|
||||
File.CreateText(logPath).Close();
|
||||
}
|
||||
|
||||
using (var sw = File.AppendText(logPath))
|
||||
{
|
||||
sw.Write(logContent);
|
||||
}
|
||||
using var sw = File.AppendText(logPath);
|
||||
sw.Write(logContent);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
|
||||
@@ -2,9 +2,9 @@
|
||||
|
||||
public enum AfterDownloadOperation
|
||||
{
|
||||
NOT_SET = 0,
|
||||
NONE = 1,
|
||||
OPEN_FOLDER,
|
||||
CLOSE_APP,
|
||||
CLOSE_SYSTEM
|
||||
NotSet = 0,
|
||||
None = 1,
|
||||
OpenFolder,
|
||||
CloseApp,
|
||||
CloseSystem
|
||||
}
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
public enum AllowStatus
|
||||
{
|
||||
NONE = 0,
|
||||
NO,
|
||||
YES
|
||||
None = 0,
|
||||
No,
|
||||
Yes
|
||||
}
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
public enum DanmakuLayoutAlgorithm
|
||||
{
|
||||
NONE = 0,
|
||||
ASYNC,
|
||||
SYNC
|
||||
None = 0,
|
||||
Async,
|
||||
Sync
|
||||
}
|
||||
@@ -2,8 +2,8 @@
|
||||
|
||||
public enum Downloader
|
||||
{
|
||||
NOT_SET = 0,
|
||||
BUILT_IN,
|
||||
ARIA,
|
||||
CUSTOM_ARIA,
|
||||
NotSet = 0,
|
||||
BuiltIn,
|
||||
Aria,
|
||||
CustomAria,
|
||||
}
|
||||
@@ -5,6 +5,6 @@
|
||||
/// </summary>
|
||||
public class AboutSettings
|
||||
{
|
||||
public AllowStatus IsReceiveBetaVersion { get; set; } = AllowStatus.NONE;
|
||||
public AllowStatus AutoUpdateWhenLaunch { get; set; } = AllowStatus.NONE;
|
||||
public AllowStatus IsReceiveBetaVersion { get; set; } = AllowStatus.None;
|
||||
public AllowStatus AutoUpdateWhenLaunch { get; set; } = AllowStatus.None;
|
||||
}
|
||||
@@ -6,12 +6,12 @@
|
||||
public class BasicSettings
|
||||
{
|
||||
public ThemeMode ThemeMode { get; set; } = ThemeMode.Default;
|
||||
public AfterDownloadOperation AfterDownload { get; set; } = AfterDownloadOperation.NOT_SET;
|
||||
public AllowStatus IsListenClipboard { get; set; } = AllowStatus.NONE;
|
||||
public AllowStatus IsAutoParseVideo { get; set; } = AllowStatus.NONE;
|
||||
public ParseScope ParseScope { get; set; } = ParseScope.NOT_SET;
|
||||
public AllowStatus IsAutoDownloadAll { get; set; } = AllowStatus.NONE;
|
||||
public AfterDownloadOperation AfterDownload { get; set; } = AfterDownloadOperation.NotSet;
|
||||
public AllowStatus IsListenClipboard { get; set; } = AllowStatus.None;
|
||||
public AllowStatus IsAutoParseVideo { get; set; } = AllowStatus.None;
|
||||
public ParseScope ParseScope { get; set; } = ParseScope.NotSet;
|
||||
public AllowStatus IsAutoDownloadAll { get; set; } = AllowStatus.None;
|
||||
public DownloadFinishedSort DownloadFinishedSort { get; set; } = DownloadFinishedSort.NotSet;
|
||||
public RepeatDownloadStrategy RepeatDownloadStrategy { get; set; } = RepeatDownloadStrategy.Ask;
|
||||
public bool RepeatFileAutoAddNumberSuffix { get; set; } = false;
|
||||
public bool RepeatFileAutoAddNumberSuffix { get; set; }
|
||||
}
|
||||
@@ -5,14 +5,14 @@
|
||||
/// </summary>
|
||||
public class DanmakuSettings
|
||||
{
|
||||
public AllowStatus DanmakuTopFilter { get; set; } = AllowStatus.NONE;
|
||||
public AllowStatus DanmakuBottomFilter { get; set; } = AllowStatus.NONE;
|
||||
public AllowStatus DanmakuScrollFilter { get; set; } = AllowStatus.NONE;
|
||||
public AllowStatus IsCustomDanmakuResolution { get; set; } = AllowStatus.NONE;
|
||||
public AllowStatus DanmakuTopFilter { get; set; } = AllowStatus.None;
|
||||
public AllowStatus DanmakuBottomFilter { get; set; } = AllowStatus.None;
|
||||
public AllowStatus DanmakuScrollFilter { get; set; } = AllowStatus.None;
|
||||
public AllowStatus IsCustomDanmakuResolution { get; set; } = AllowStatus.None;
|
||||
public int DanmakuScreenWidth { get; set; } = -1;
|
||||
public int DanmakuScreenHeight { get; set; } = -1;
|
||||
public string DanmakuFontName { get; set; } = null;
|
||||
public string? DanmakuFontName { get; set; }
|
||||
public int DanmakuFontSize { get; set; } = -1;
|
||||
public int DanmakuLineCount { get; set; } = -1;
|
||||
public DanmakuLayoutAlgorithm DanmakuLayoutAlgorithm { get; set; } = DanmakuLayoutAlgorithm.NONE;
|
||||
public DanmakuLayoutAlgorithm DanmakuLayoutAlgorithm { get; set; } = DanmakuLayoutAlgorithm.None;
|
||||
}
|
||||
@@ -7,27 +7,27 @@ namespace DownKyi.Core.Settings.Models;
|
||||
/// </summary>
|
||||
public class NetworkSettings
|
||||
{
|
||||
public AllowStatus IsLiftingOfRegion { get; set; } = AllowStatus.NONE;
|
||||
public AllowStatus IsLiftingOfRegion { get; set; } = AllowStatus.None;
|
||||
|
||||
public AllowStatus UseSSL { get; set; } = AllowStatus.NONE;
|
||||
public AllowStatus UseSsl { get; set; } = AllowStatus.None;
|
||||
public string UserAgent { get; set; } = string.Empty;
|
||||
|
||||
public Downloader Downloader { get; set; } = Downloader.NOT_SET;
|
||||
public Downloader Downloader { get; set; } = Downloader.NotSet;
|
||||
public int MaxCurrentDownloads { get; set; } = -1;
|
||||
|
||||
#region built-in
|
||||
|
||||
public int Split { get; set; } = -1;
|
||||
public AllowStatus IsHttpProxy { get; set; } = AllowStatus.NONE;
|
||||
public string HttpProxy { get; set; } = null;
|
||||
public AllowStatus IsHttpProxy { get; set; } = AllowStatus.None;
|
||||
public string? HttpProxy { get; set; }
|
||||
public int HttpProxyListenPort { get; set; } = -1;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Aria
|
||||
|
||||
public string AriaToken { get; set; } = null;
|
||||
public string AriaHost { get; set; } = null;
|
||||
public string? AriaToken { get; set; }
|
||||
public string? AriaHost { get; set; }
|
||||
|
||||
public int AriaListenPort { get; set; } = -1;
|
||||
|
||||
@@ -37,8 +37,8 @@ public class NetworkSettings
|
||||
public int AriaMaxDownloadLimit { get; set; } = -1;
|
||||
public AriaConfigFileAllocation AriaFileAllocation { get; set; } = AriaConfigFileAllocation.NOT_SET;
|
||||
|
||||
public AllowStatus IsAriaHttpProxy { get; set; } = AllowStatus.NONE;
|
||||
public string AriaHttpProxy { get; set; } = null;
|
||||
public AllowStatus IsAriaHttpProxy { get; set; } = AllowStatus.None;
|
||||
public string? AriaHttpProxy { get; set; }
|
||||
public int AriaHttpProxyListenPort { get; set; } = -1;
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -11,13 +11,13 @@ public class VideoSettings
|
||||
public int Quality { get; set; } = -1; // 画质
|
||||
public int AudioQuality { get; set; } = -1; // 音质
|
||||
public int VideoParseType { get; set; } = 1; // 视频解析类型
|
||||
public AllowStatus IsTranscodingFlvToMp4 { get; set; } = AllowStatus.NONE; // 是否将flv转为mp4
|
||||
public AllowStatus IsTranscodingAacToMp3 { get; set; } = AllowStatus.NONE; // 是否将aac转为mp3
|
||||
public string SaveVideoRootPath { get; set; } = null; // 视频保存路径
|
||||
public List<string> HistoryVideoRootPaths { get; set; } = null; // 历史视频保存路径
|
||||
public AllowStatus IsUseSaveVideoRootPath { get; set; } = AllowStatus.NONE; // 是否使用默认视频保存路径
|
||||
public VideoContentSettings VideoContent { get; set; } = null; // 下载内容
|
||||
public List<FileNamePart> FileNameParts { get; set; } = null; // 文件命名格式
|
||||
public string FileNamePartTimeFormat { get; set; } = null; // 文件命名中的时间格式
|
||||
public OrderFormat OrderFormat { get; set; } = OrderFormat.NOT_SET; // 文件命名中的序号格式
|
||||
public AllowStatus IsTranscodingFlvToMp4 { get; set; } = AllowStatus.None; // 是否将flv转为mp4
|
||||
public AllowStatus IsTranscodingAacToMp3 { get; set; } = AllowStatus.None; // 是否将aac转为mp3
|
||||
public string? SaveVideoRootPath { get; set; } // 视频保存路径
|
||||
public List<string>? HistoryVideoRootPaths { get; set; } // 历史视频保存路径
|
||||
public AllowStatus IsUseSaveVideoRootPath { get; set; } = AllowStatus.None; // 是否使用默认视频保存路径
|
||||
public VideoContentSettings? VideoContent { get; set; } // 下载内容
|
||||
public List<FileNamePart>? FileNameParts { get; set; } // 文件命名格式
|
||||
public string? FileNamePartTimeFormat { get; set; } // 文件命名中的时间格式
|
||||
public OrderFormat OrderFormat { get; set; } = OrderFormat.NotSet; // 文件命名中的序号格式
|
||||
}
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
public enum OrderFormat
|
||||
{
|
||||
NOT_SET = 0,
|
||||
NATURAL, // 自然数
|
||||
LEADING_ZEROS, // 前导零填充
|
||||
NotSet = 0,
|
||||
Natural, // 自然数
|
||||
LeadingZeros, // 前导零填充
|
||||
}
|
||||
@@ -2,9 +2,9 @@
|
||||
|
||||
public enum ParseScope
|
||||
{
|
||||
NOT_SET = 0,
|
||||
NONE = 1,
|
||||
SELECTED_ITEM,
|
||||
CURRENT_SECTION,
|
||||
ALL
|
||||
NotSet = 0,
|
||||
None = 1,
|
||||
SelectedItem,
|
||||
CurrentSection,
|
||||
All
|
||||
}
|
||||
@@ -3,26 +3,26 @@
|
||||
public partial class SettingsManager
|
||||
{
|
||||
// 是否接收测试版更新
|
||||
private readonly AllowStatus isReceiveBetaVersion = AllowStatus.NO;
|
||||
private const AllowStatus IsReceiveBetaVersion = AllowStatus.No;
|
||||
|
||||
// 是否在启动时自动检查更新
|
||||
private readonly AllowStatus autoUpdateWhenLaunch = AllowStatus.YES;
|
||||
private const AllowStatus AutoUpdateWhenLaunch = AllowStatus.Yes;
|
||||
|
||||
/// <summary>
|
||||
/// 获取是否接收测试版更新
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public AllowStatus IsReceiveBetaVersion()
|
||||
public AllowStatus GetIsReceiveBetaVersion()
|
||||
{
|
||||
appSettings = GetSettings();
|
||||
if (appSettings.About.IsReceiveBetaVersion == AllowStatus.NONE)
|
||||
_appSettings = GetSettings();
|
||||
if (_appSettings.About.IsReceiveBetaVersion == AllowStatus.None)
|
||||
{
|
||||
// 第一次获取,先设置默认值
|
||||
IsReceiveBetaVersion(isReceiveBetaVersion);
|
||||
return isReceiveBetaVersion;
|
||||
SetIsReceiveBetaVersion(IsReceiveBetaVersion);
|
||||
return IsReceiveBetaVersion;
|
||||
}
|
||||
|
||||
return appSettings.About.IsReceiveBetaVersion;
|
||||
return _appSettings.About.IsReceiveBetaVersion;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -30,9 +30,9 @@ public partial class SettingsManager
|
||||
/// </summary>
|
||||
/// <param name="isReceiveBetaVersion"></param>
|
||||
/// <returns></returns>
|
||||
public bool IsReceiveBetaVersion(AllowStatus isReceiveBetaVersion)
|
||||
public bool SetIsReceiveBetaVersion(AllowStatus isReceiveBetaVersion)
|
||||
{
|
||||
appSettings.About.IsReceiveBetaVersion = isReceiveBetaVersion;
|
||||
_appSettings.About.IsReceiveBetaVersion = isReceiveBetaVersion;
|
||||
return SetSettings();
|
||||
}
|
||||
|
||||
@@ -42,15 +42,15 @@ public partial class SettingsManager
|
||||
/// <returns></returns>
|
||||
public AllowStatus GetAutoUpdateWhenLaunch()
|
||||
{
|
||||
appSettings = GetSettings();
|
||||
if (appSettings.About.AutoUpdateWhenLaunch == AllowStatus.NONE)
|
||||
_appSettings = GetSettings();
|
||||
if (_appSettings.About.AutoUpdateWhenLaunch == AllowStatus.None)
|
||||
{
|
||||
// 第一次获取,先设置默认值
|
||||
SetAutoUpdateWhenLaunch(autoUpdateWhenLaunch);
|
||||
return autoUpdateWhenLaunch;
|
||||
SetAutoUpdateWhenLaunch(AutoUpdateWhenLaunch);
|
||||
return AutoUpdateWhenLaunch;
|
||||
}
|
||||
|
||||
return appSettings.About.AutoUpdateWhenLaunch;
|
||||
return _appSettings.About.AutoUpdateWhenLaunch;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -60,7 +60,7 @@ public partial class SettingsManager
|
||||
/// <returns></returns>
|
||||
public bool SetAutoUpdateWhenLaunch(AllowStatus autoUpdateWhenLaunch)
|
||||
{
|
||||
appSettings.About.AutoUpdateWhenLaunch = autoUpdateWhenLaunch;
|
||||
_appSettings.About.AutoUpdateWhenLaunch = autoUpdateWhenLaunch;
|
||||
return SetSettings();
|
||||
}
|
||||
}
|
||||
@@ -3,45 +3,45 @@
|
||||
public partial class SettingsManager
|
||||
{
|
||||
// 默认下载完成后的操作
|
||||
private readonly AfterDownloadOperation _afterDownload = AfterDownloadOperation.NONE;
|
||||
private const AfterDownloadOperation AfterDownload = AfterDownloadOperation.None;
|
||||
|
||||
// 是否监听剪贴板
|
||||
private readonly AllowStatus _isListenClipboard = AllowStatus.YES;
|
||||
private const AllowStatus IsListenClipboard = AllowStatus.Yes;
|
||||
|
||||
// 视频详情页面是否自动解析
|
||||
private readonly AllowStatus _isAutoParseVideo = AllowStatus.NO;
|
||||
private const AllowStatus IsAutoParseVideo = AllowStatus.No;
|
||||
|
||||
// 默认的视频解析项
|
||||
private readonly ParseScope _parseScope = ParseScope.NONE;
|
||||
private const ParseScope ParseScope = Settings.ParseScope.None;
|
||||
|
||||
// 解析后自动下载解析视频
|
||||
private readonly AllowStatus _isAutoDownloadAll = AllowStatus.NO;
|
||||
private const AllowStatus IsAutoDownloadAll = AllowStatus.No;
|
||||
|
||||
// 下载完成列表排序
|
||||
private readonly DownloadFinishedSort _finishedSort = DownloadFinishedSort.DownloadAsc;
|
||||
private const DownloadFinishedSort FinishedSort = DownloadFinishedSort.DownloadAsc;
|
||||
|
||||
// 重复下载策略
|
||||
private readonly RepeatDownloadStrategy _repeatDownloadStrategy = RepeatDownloadStrategy.Ask;
|
||||
private const RepeatDownloadStrategy RepeatDownloadStrategy = Settings.RepeatDownloadStrategy.Ask;
|
||||
|
||||
// 重复文件自动添加数字后缀
|
||||
private readonly bool _repeatFileAutoAddNumberSuffix = false;
|
||||
|
||||
private const bool RepeatFileAutoAddNumberSuffix = false;
|
||||
|
||||
public ThemeMode GetThemeMode()
|
||||
{
|
||||
appSettings = GetSettings();
|
||||
if (appSettings.Basic.ThemeMode == ThemeMode.Default)
|
||||
_appSettings = GetSettings();
|
||||
if (_appSettings.Basic.ThemeMode == ThemeMode.Default)
|
||||
{
|
||||
// 第一次获取,先设置默认值
|
||||
SetThemeMode(ThemeMode.Default);
|
||||
return ThemeMode.Default;
|
||||
}
|
||||
|
||||
return appSettings.Basic.ThemeMode;
|
||||
return _appSettings.Basic.ThemeMode;
|
||||
}
|
||||
|
||||
|
||||
public bool SetThemeMode(ThemeMode themeMode)
|
||||
{
|
||||
appSettings.Basic.ThemeMode = themeMode;
|
||||
_appSettings.Basic.ThemeMode = themeMode;
|
||||
return SetSettings();
|
||||
}
|
||||
|
||||
@@ -51,15 +51,15 @@ public partial class SettingsManager
|
||||
/// <returns></returns>
|
||||
public AfterDownloadOperation GetAfterDownloadOperation()
|
||||
{
|
||||
appSettings = GetSettings();
|
||||
if (appSettings.Basic.AfterDownload == AfterDownloadOperation.NOT_SET)
|
||||
_appSettings = GetSettings();
|
||||
if (_appSettings.Basic.AfterDownload == AfterDownloadOperation.NotSet)
|
||||
{
|
||||
// 第一次获取,先设置默认值
|
||||
SetAfterDownloadOperation(_afterDownload);
|
||||
return _afterDownload;
|
||||
SetAfterDownloadOperation(AfterDownload);
|
||||
return AfterDownload;
|
||||
}
|
||||
|
||||
return appSettings.Basic.AfterDownload;
|
||||
return _appSettings.Basic.AfterDownload;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -69,7 +69,7 @@ public partial class SettingsManager
|
||||
/// <returns></returns>
|
||||
public bool SetAfterDownloadOperation(AfterDownloadOperation afterDownload)
|
||||
{
|
||||
appSettings.Basic.AfterDownload = afterDownload;
|
||||
_appSettings.Basic.AfterDownload = afterDownload;
|
||||
return SetSettings();
|
||||
}
|
||||
|
||||
@@ -77,17 +77,17 @@ public partial class SettingsManager
|
||||
/// 是否监听剪贴板
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public AllowStatus IsListenClipboard()
|
||||
public AllowStatus GetIsListenClipboard()
|
||||
{
|
||||
appSettings = GetSettings();
|
||||
if (appSettings.Basic.IsListenClipboard == AllowStatus.NONE)
|
||||
_appSettings = GetSettings();
|
||||
if (_appSettings.Basic.IsListenClipboard == AllowStatus.None)
|
||||
{
|
||||
// 第一次获取,先设置默认值
|
||||
IsListenClipboard(_isListenClipboard);
|
||||
return _isListenClipboard;
|
||||
SetIsListenClipboard(IsListenClipboard);
|
||||
return IsListenClipboard;
|
||||
}
|
||||
|
||||
return appSettings.Basic.IsListenClipboard;
|
||||
return _appSettings.Basic.IsListenClipboard;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -95,9 +95,9 @@ public partial class SettingsManager
|
||||
/// </summary>
|
||||
/// <param name="isListen"></param>
|
||||
/// <returns></returns>
|
||||
public bool IsListenClipboard(AllowStatus isListen)
|
||||
public bool SetIsListenClipboard(AllowStatus isListen)
|
||||
{
|
||||
appSettings.Basic.IsListenClipboard = isListen;
|
||||
_appSettings.Basic.IsListenClipboard = isListen;
|
||||
return SetSettings();
|
||||
}
|
||||
|
||||
@@ -105,27 +105,27 @@ public partial class SettingsManager
|
||||
/// 视频详情页面是否自动解析
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public AllowStatus IsAutoParseVideo()
|
||||
public AllowStatus GetIsAutoParseVideo()
|
||||
{
|
||||
appSettings = GetSettings();
|
||||
if (appSettings.Basic.IsAutoParseVideo == AllowStatus.NONE)
|
||||
_appSettings = GetSettings();
|
||||
if (_appSettings.Basic.IsAutoParseVideo == AllowStatus.None)
|
||||
{
|
||||
// 第一次获取,先设置默认值
|
||||
IsAutoParseVideo(_isAutoParseVideo);
|
||||
return _isAutoParseVideo;
|
||||
SetIsAutoParseVideo(IsAutoParseVideo);
|
||||
return IsAutoParseVideo;
|
||||
}
|
||||
|
||||
return appSettings.Basic.IsAutoParseVideo;
|
||||
return _appSettings.Basic.IsAutoParseVideo;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 视频详情页面是否自动解析
|
||||
/// </summary>
|
||||
/// <param name="IsAuto"></param>
|
||||
/// <param name="isAuto"></param>
|
||||
/// <returns></returns>
|
||||
public bool IsAutoParseVideo(AllowStatus IsAuto)
|
||||
public bool SetIsAutoParseVideo(AllowStatus isAuto)
|
||||
{
|
||||
appSettings.Basic.IsAutoParseVideo = IsAuto;
|
||||
_appSettings.Basic.IsAutoParseVideo = isAuto;
|
||||
return SetSettings();
|
||||
}
|
||||
|
||||
@@ -135,15 +135,15 @@ public partial class SettingsManager
|
||||
/// <returns></returns>
|
||||
public ParseScope GetParseScope()
|
||||
{
|
||||
appSettings = GetSettings();
|
||||
if (appSettings.Basic.ParseScope == ParseScope.NOT_SET)
|
||||
_appSettings = GetSettings();
|
||||
if (_appSettings.Basic.ParseScope == ParseScope.NotSet)
|
||||
{
|
||||
// 第一次获取,先设置默认值
|
||||
SetParseScope(_parseScope);
|
||||
return _parseScope;
|
||||
SetParseScope(ParseScope);
|
||||
return ParseScope;
|
||||
}
|
||||
|
||||
return appSettings.Basic.ParseScope;
|
||||
return _appSettings.Basic.ParseScope;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -153,7 +153,7 @@ public partial class SettingsManager
|
||||
/// <returns></returns>
|
||||
public bool SetParseScope(ParseScope parseScope)
|
||||
{
|
||||
appSettings.Basic.ParseScope = parseScope;
|
||||
_appSettings.Basic.ParseScope = parseScope;
|
||||
return SetSettings();
|
||||
}
|
||||
|
||||
@@ -161,17 +161,17 @@ public partial class SettingsManager
|
||||
/// 解析后是否自动下载解析视频
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public AllowStatus IsAutoDownloadAll()
|
||||
public AllowStatus GetIsAutoDownloadAll()
|
||||
{
|
||||
appSettings = GetSettings();
|
||||
if (appSettings.Basic.IsAutoDownloadAll == AllowStatus.NONE)
|
||||
_appSettings = GetSettings();
|
||||
if (_appSettings.Basic.IsAutoDownloadAll == AllowStatus.None)
|
||||
{
|
||||
// 第一次获取,先设置默认值
|
||||
IsAutoDownloadAll(_isAutoDownloadAll);
|
||||
return _isAutoDownloadAll;
|
||||
SetIsAutoDownloadAll(IsAutoDownloadAll);
|
||||
return IsAutoDownloadAll;
|
||||
}
|
||||
|
||||
return appSettings.Basic.IsAutoDownloadAll;
|
||||
return _appSettings.Basic.IsAutoDownloadAll;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -179,9 +179,9 @@ public partial class SettingsManager
|
||||
/// </summary>
|
||||
/// <param name="isAutoDownloadAll"></param>
|
||||
/// <returns></returns>
|
||||
public bool IsAutoDownloadAll(AllowStatus isAutoDownloadAll)
|
||||
public bool SetIsAutoDownloadAll(AllowStatus isAutoDownloadAll)
|
||||
{
|
||||
appSettings.Basic.IsAutoDownloadAll = isAutoDownloadAll;
|
||||
_appSettings.Basic.IsAutoDownloadAll = isAutoDownloadAll;
|
||||
return SetSettings();
|
||||
}
|
||||
|
||||
@@ -191,15 +191,15 @@ public partial class SettingsManager
|
||||
/// <returns></returns>
|
||||
public DownloadFinishedSort GetDownloadFinishedSort()
|
||||
{
|
||||
appSettings = GetSettings();
|
||||
if (appSettings.Basic.DownloadFinishedSort == DownloadFinishedSort.NotSet)
|
||||
_appSettings = GetSettings();
|
||||
if (_appSettings.Basic.DownloadFinishedSort == DownloadFinishedSort.NotSet)
|
||||
{
|
||||
// 第一次获取,先设置默认值
|
||||
SetDownloadFinishedSort(_finishedSort);
|
||||
return _finishedSort;
|
||||
SetDownloadFinishedSort(FinishedSort);
|
||||
return FinishedSort;
|
||||
}
|
||||
|
||||
return appSettings.Basic.DownloadFinishedSort;
|
||||
return _appSettings.Basic.DownloadFinishedSort;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -209,7 +209,7 @@ public partial class SettingsManager
|
||||
/// <returns></returns>
|
||||
public bool SetDownloadFinishedSort(DownloadFinishedSort finishedSort)
|
||||
{
|
||||
appSettings.Basic.DownloadFinishedSort = finishedSort;
|
||||
_appSettings.Basic.DownloadFinishedSort = finishedSort;
|
||||
return SetSettings();
|
||||
}
|
||||
|
||||
@@ -219,15 +219,15 @@ public partial class SettingsManager
|
||||
/// <returns></returns>
|
||||
public RepeatDownloadStrategy GetRepeatDownloadStrategy()
|
||||
{
|
||||
appSettings = GetSettings();
|
||||
if (appSettings.Basic.RepeatDownloadStrategy == RepeatDownloadStrategy.Ask)
|
||||
_appSettings = GetSettings();
|
||||
if (_appSettings.Basic.RepeatDownloadStrategy == RepeatDownloadStrategy.Ask)
|
||||
{
|
||||
// 第一次获取,先设置默认值
|
||||
SetRepeatDownloadStrategy(_repeatDownloadStrategy);
|
||||
return _repeatDownloadStrategy;
|
||||
SetRepeatDownloadStrategy(RepeatDownloadStrategy);
|
||||
return RepeatDownloadStrategy;
|
||||
}
|
||||
|
||||
return appSettings.Basic.RepeatDownloadStrategy;
|
||||
return _appSettings.Basic.RepeatDownloadStrategy;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -237,7 +237,7 @@ public partial class SettingsManager
|
||||
/// <returns></returns>
|
||||
public bool SetRepeatDownloadStrategy(RepeatDownloadStrategy repeatDownloadStrategy)
|
||||
{
|
||||
appSettings.Basic.RepeatDownloadStrategy = repeatDownloadStrategy;
|
||||
_appSettings.Basic.RepeatDownloadStrategy = repeatDownloadStrategy;
|
||||
return SetSettings();
|
||||
}
|
||||
|
||||
@@ -247,15 +247,15 @@ public partial class SettingsManager
|
||||
/// <returns></returns>
|
||||
public bool IsRepeatFileAutoAddNumberSuffix()
|
||||
{
|
||||
appSettings = GetSettings();
|
||||
if (appSettings.Basic.RepeatFileAutoAddNumberSuffix == false)
|
||||
_appSettings = GetSettings();
|
||||
if (_appSettings.Basic.RepeatFileAutoAddNumberSuffix == false)
|
||||
{
|
||||
// 第一次获取,先设置默认值
|
||||
IsRepeatFileAutoAddNumberSuffix(_repeatFileAutoAddNumberSuffix);
|
||||
return _repeatFileAutoAddNumberSuffix;
|
||||
IsRepeatFileAutoAddNumberSuffix(RepeatFileAutoAddNumberSuffix);
|
||||
return RepeatFileAutoAddNumberSuffix;
|
||||
}
|
||||
|
||||
return appSettings.Basic.RepeatFileAutoAddNumberSuffix;
|
||||
return _appSettings.Basic.RepeatFileAutoAddNumberSuffix;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -266,7 +266,7 @@ public partial class SettingsManager
|
||||
/// <exception cref="NotImplementedException"></exception>
|
||||
public bool IsRepeatFileAutoAddNumberSuffix(bool repeatFileAutoAddNumberSuffix)
|
||||
{
|
||||
appSettings.Basic.RepeatFileAutoAddNumberSuffix = repeatFileAutoAddNumberSuffix;
|
||||
_appSettings.Basic.RepeatFileAutoAddNumberSuffix = repeatFileAutoAddNumberSuffix;
|
||||
return SetSettings();
|
||||
}
|
||||
}
|
||||
@@ -3,34 +3,34 @@
|
||||
public partial class SettingsManager
|
||||
{
|
||||
// 是否屏蔽顶部弹幕
|
||||
private readonly AllowStatus danmakuTopFilter = AllowStatus.NO;
|
||||
private const AllowStatus DanmakuTopFilter = AllowStatus.No;
|
||||
|
||||
// 是否屏蔽底部弹幕
|
||||
private readonly AllowStatus danmakuBottomFilter = AllowStatus.NO;
|
||||
private const AllowStatus DanmakuBottomFilter = AllowStatus.No;
|
||||
|
||||
// 是否屏蔽滚动弹幕
|
||||
private readonly AllowStatus danmakuScrollFilter = AllowStatus.NO;
|
||||
private const AllowStatus DanmakuScrollFilter = AllowStatus.No;
|
||||
|
||||
// 是否自定义分辨率
|
||||
private readonly AllowStatus isCustomDanmakuResolution = AllowStatus.NO;
|
||||
private const AllowStatus IsCustomDanmakuResolution = AllowStatus.No;
|
||||
|
||||
// 分辨率-宽
|
||||
private readonly int danmakuScreenWidth = 1920;
|
||||
private const int DanmakuScreenWidth = 1920;
|
||||
|
||||
// 分辨率-高
|
||||
private readonly int danmakuScreenHeight = 1080;
|
||||
private const int DanmakuScreenHeight = 1080;
|
||||
|
||||
// 弹幕字体
|
||||
private readonly string danmakuFontName = "黑体";
|
||||
private const string DanmakuFontName = "黑体";
|
||||
|
||||
// 弹幕字体大小
|
||||
private readonly int danmakuFontSize = 50;
|
||||
private const int DanmakuFontSize = 50;
|
||||
|
||||
// 弹幕限制行数
|
||||
private readonly int danmakuLineCount = 0;
|
||||
private const int DanmakuLineCount = 0;
|
||||
|
||||
// 弹幕布局算法
|
||||
private readonly DanmakuLayoutAlgorithm danmakuLayoutAlgorithm = DanmakuLayoutAlgorithm.SYNC;
|
||||
private const DanmakuLayoutAlgorithm DanmakuLayoutAlgorithm = Settings.DanmakuLayoutAlgorithm.Sync;
|
||||
|
||||
|
||||
/// <summary>
|
||||
@@ -39,15 +39,15 @@ public partial class SettingsManager
|
||||
/// <returns></returns>
|
||||
public AllowStatus GetDanmakuTopFilter()
|
||||
{
|
||||
appSettings = GetSettings();
|
||||
if (appSettings.Danmaku.DanmakuTopFilter == AllowStatus.NONE)
|
||||
_appSettings = GetSettings();
|
||||
if (_appSettings.Danmaku.DanmakuTopFilter == AllowStatus.None)
|
||||
{
|
||||
// 第一次获取,先设置默认值
|
||||
SetDanmakuTopFilter(danmakuTopFilter);
|
||||
return danmakuTopFilter;
|
||||
SetDanmakuTopFilter(DanmakuTopFilter);
|
||||
return DanmakuTopFilter;
|
||||
}
|
||||
|
||||
return appSettings.Danmaku.DanmakuTopFilter;
|
||||
return _appSettings.Danmaku.DanmakuTopFilter;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -57,7 +57,7 @@ public partial class SettingsManager
|
||||
/// <returns></returns>
|
||||
public bool SetDanmakuTopFilter(AllowStatus danmakuFilter)
|
||||
{
|
||||
appSettings.Danmaku.DanmakuTopFilter = danmakuFilter;
|
||||
_appSettings.Danmaku.DanmakuTopFilter = danmakuFilter;
|
||||
return SetSettings();
|
||||
}
|
||||
|
||||
@@ -67,15 +67,15 @@ public partial class SettingsManager
|
||||
/// <returns></returns>
|
||||
public AllowStatus GetDanmakuBottomFilter()
|
||||
{
|
||||
appSettings = GetSettings();
|
||||
if (appSettings.Danmaku.DanmakuBottomFilter == AllowStatus.NONE)
|
||||
_appSettings = GetSettings();
|
||||
if (_appSettings.Danmaku.DanmakuBottomFilter == AllowStatus.None)
|
||||
{
|
||||
// 第一次获取,先设置默认值
|
||||
SetDanmakuBottomFilter(danmakuBottomFilter);
|
||||
return danmakuBottomFilter;
|
||||
SetDanmakuBottomFilter(DanmakuBottomFilter);
|
||||
return DanmakuBottomFilter;
|
||||
}
|
||||
|
||||
return appSettings.Danmaku.DanmakuBottomFilter;
|
||||
return _appSettings.Danmaku.DanmakuBottomFilter;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -85,7 +85,7 @@ public partial class SettingsManager
|
||||
/// <returns></returns>
|
||||
public bool SetDanmakuBottomFilter(AllowStatus danmakuFilter)
|
||||
{
|
||||
appSettings.Danmaku.DanmakuBottomFilter = danmakuFilter;
|
||||
_appSettings.Danmaku.DanmakuBottomFilter = danmakuFilter;
|
||||
return SetSettings();
|
||||
}
|
||||
|
||||
@@ -95,15 +95,15 @@ public partial class SettingsManager
|
||||
/// <returns></returns>
|
||||
public AllowStatus GetDanmakuScrollFilter()
|
||||
{
|
||||
appSettings = GetSettings();
|
||||
if (appSettings.Danmaku.DanmakuScrollFilter == AllowStatus.NONE)
|
||||
_appSettings = GetSettings();
|
||||
if (_appSettings.Danmaku.DanmakuScrollFilter == AllowStatus.None)
|
||||
{
|
||||
// 第一次获取,先设置默认值
|
||||
SetDanmakuScrollFilter(danmakuScrollFilter);
|
||||
return danmakuScrollFilter;
|
||||
SetDanmakuScrollFilter(DanmakuScrollFilter);
|
||||
return DanmakuScrollFilter;
|
||||
}
|
||||
|
||||
return appSettings.Danmaku.DanmakuScrollFilter;
|
||||
return _appSettings.Danmaku.DanmakuScrollFilter;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -113,7 +113,7 @@ public partial class SettingsManager
|
||||
/// <returns></returns>
|
||||
public bool SetDanmakuScrollFilter(AllowStatus danmakuFilter)
|
||||
{
|
||||
appSettings.Danmaku.DanmakuScrollFilter = danmakuFilter;
|
||||
_appSettings.Danmaku.DanmakuScrollFilter = danmakuFilter;
|
||||
return SetSettings();
|
||||
}
|
||||
|
||||
@@ -121,17 +121,17 @@ public partial class SettingsManager
|
||||
/// 获取是否自定义分辨率
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public AllowStatus IsCustomDanmakuResolution()
|
||||
public AllowStatus GetIsCustomDanmakuResolution()
|
||||
{
|
||||
appSettings = GetSettings();
|
||||
if (appSettings.Danmaku.IsCustomDanmakuResolution == AllowStatus.NONE)
|
||||
_appSettings = GetSettings();
|
||||
if (_appSettings.Danmaku.IsCustomDanmakuResolution == AllowStatus.None)
|
||||
{
|
||||
// 第一次获取,先设置默认值
|
||||
IsCustomDanmakuResolution(isCustomDanmakuResolution);
|
||||
return isCustomDanmakuResolution;
|
||||
SetIsCustomDanmakuResolution(IsCustomDanmakuResolution);
|
||||
return IsCustomDanmakuResolution;
|
||||
}
|
||||
|
||||
return appSettings.Danmaku.IsCustomDanmakuResolution;
|
||||
return _appSettings.Danmaku.IsCustomDanmakuResolution;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -139,9 +139,9 @@ public partial class SettingsManager
|
||||
/// </summary>
|
||||
/// <param name="isCustomResolution"></param>
|
||||
/// <returns></returns>
|
||||
public bool IsCustomDanmakuResolution(AllowStatus isCustomResolution)
|
||||
public bool SetIsCustomDanmakuResolution(AllowStatus isCustomResolution)
|
||||
{
|
||||
appSettings.Danmaku.IsCustomDanmakuResolution = isCustomResolution;
|
||||
_appSettings.Danmaku.IsCustomDanmakuResolution = isCustomResolution;
|
||||
return SetSettings();
|
||||
}
|
||||
|
||||
@@ -151,15 +151,15 @@ public partial class SettingsManager
|
||||
/// <returns></returns>
|
||||
public int GetDanmakuScreenWidth()
|
||||
{
|
||||
appSettings = GetSettings();
|
||||
if (appSettings.Danmaku.DanmakuScreenWidth == -1)
|
||||
_appSettings = GetSettings();
|
||||
if (_appSettings.Danmaku.DanmakuScreenWidth == -1)
|
||||
{
|
||||
// 第一次获取,先设置默认值
|
||||
SetDanmakuScreenWidth(danmakuScreenWidth);
|
||||
return danmakuScreenWidth;
|
||||
SetDanmakuScreenWidth(DanmakuScreenWidth);
|
||||
return DanmakuScreenWidth;
|
||||
}
|
||||
|
||||
return appSettings.Danmaku.DanmakuScreenWidth;
|
||||
return _appSettings.Danmaku.DanmakuScreenWidth;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -169,7 +169,7 @@ public partial class SettingsManager
|
||||
/// <returns></returns>
|
||||
public bool SetDanmakuScreenWidth(int screenWidth)
|
||||
{
|
||||
appSettings.Danmaku.DanmakuScreenWidth = screenWidth;
|
||||
_appSettings.Danmaku.DanmakuScreenWidth = screenWidth;
|
||||
return SetSettings();
|
||||
}
|
||||
|
||||
@@ -179,15 +179,15 @@ public partial class SettingsManager
|
||||
/// <returns></returns>
|
||||
public int GetDanmakuScreenHeight()
|
||||
{
|
||||
appSettings = GetSettings();
|
||||
if (appSettings.Danmaku.DanmakuScreenHeight == -1)
|
||||
_appSettings = GetSettings();
|
||||
if (_appSettings.Danmaku.DanmakuScreenHeight == -1)
|
||||
{
|
||||
// 第一次获取,先设置默认值
|
||||
SetDanmakuScreenHeight(danmakuScreenHeight);
|
||||
return danmakuScreenHeight;
|
||||
SetDanmakuScreenHeight(DanmakuScreenHeight);
|
||||
return DanmakuScreenHeight;
|
||||
}
|
||||
|
||||
return appSettings.Danmaku.DanmakuScreenHeight;
|
||||
return _appSettings.Danmaku.DanmakuScreenHeight;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -197,7 +197,7 @@ public partial class SettingsManager
|
||||
/// <returns></returns>
|
||||
public bool SetDanmakuScreenHeight(int screenHeight)
|
||||
{
|
||||
appSettings.Danmaku.DanmakuScreenHeight = screenHeight;
|
||||
_appSettings.Danmaku.DanmakuScreenHeight = screenHeight;
|
||||
return SetSettings();
|
||||
}
|
||||
|
||||
@@ -207,15 +207,15 @@ public partial class SettingsManager
|
||||
/// <returns></returns>
|
||||
public string GetDanmakuFontName()
|
||||
{
|
||||
appSettings = GetSettings();
|
||||
if (appSettings.Danmaku.DanmakuFontName == null)
|
||||
_appSettings = GetSettings();
|
||||
if (_appSettings.Danmaku.DanmakuFontName == null)
|
||||
{
|
||||
// 第一次获取,先设置默认值
|
||||
SetDanmakuFontName(danmakuFontName);
|
||||
return danmakuFontName;
|
||||
SetDanmakuFontName(DanmakuFontName);
|
||||
return DanmakuFontName;
|
||||
}
|
||||
|
||||
return appSettings.Danmaku.DanmakuFontName;
|
||||
return _appSettings.Danmaku.DanmakuFontName;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -225,7 +225,7 @@ public partial class SettingsManager
|
||||
/// <returns></returns>
|
||||
public bool SetDanmakuFontName(string danmakuFontName)
|
||||
{
|
||||
appSettings.Danmaku.DanmakuFontName = danmakuFontName;
|
||||
_appSettings.Danmaku.DanmakuFontName = danmakuFontName;
|
||||
return SetSettings();
|
||||
}
|
||||
|
||||
@@ -235,15 +235,15 @@ public partial class SettingsManager
|
||||
/// <returns></returns>
|
||||
public int GetDanmakuFontSize()
|
||||
{
|
||||
appSettings = GetSettings();
|
||||
if (appSettings.Danmaku.DanmakuFontSize == -1)
|
||||
_appSettings = GetSettings();
|
||||
if (_appSettings.Danmaku.DanmakuFontSize == -1)
|
||||
{
|
||||
// 第一次获取,先设置默认值
|
||||
SetDanmakuFontSize(danmakuFontSize);
|
||||
return danmakuFontSize;
|
||||
SetDanmakuFontSize(DanmakuFontSize);
|
||||
return DanmakuFontSize;
|
||||
}
|
||||
|
||||
return appSettings.Danmaku.DanmakuFontSize;
|
||||
return _appSettings.Danmaku.DanmakuFontSize;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -253,7 +253,7 @@ public partial class SettingsManager
|
||||
/// <returns></returns>
|
||||
public bool SetDanmakuFontSize(int danmakuFontSize)
|
||||
{
|
||||
appSettings.Danmaku.DanmakuFontSize = danmakuFontSize;
|
||||
_appSettings.Danmaku.DanmakuFontSize = danmakuFontSize;
|
||||
return SetSettings();
|
||||
}
|
||||
|
||||
@@ -263,15 +263,15 @@ public partial class SettingsManager
|
||||
/// <returns></returns>
|
||||
public int GetDanmakuLineCount()
|
||||
{
|
||||
appSettings = GetSettings();
|
||||
if (appSettings.Danmaku.DanmakuLineCount == -1)
|
||||
_appSettings = GetSettings();
|
||||
if (_appSettings.Danmaku.DanmakuLineCount == -1)
|
||||
{
|
||||
// 第一次获取,先设置默认值
|
||||
SetDanmakuLineCount(danmakuLineCount);
|
||||
return danmakuLineCount;
|
||||
SetDanmakuLineCount(DanmakuLineCount);
|
||||
return DanmakuLineCount;
|
||||
}
|
||||
|
||||
return appSettings.Danmaku.DanmakuLineCount;
|
||||
return _appSettings.Danmaku.DanmakuLineCount;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -281,7 +281,7 @@ public partial class SettingsManager
|
||||
/// <returns></returns>
|
||||
public bool SetDanmakuLineCount(int danmakuLineCount)
|
||||
{
|
||||
appSettings.Danmaku.DanmakuLineCount = danmakuLineCount;
|
||||
_appSettings.Danmaku.DanmakuLineCount = danmakuLineCount;
|
||||
return SetSettings();
|
||||
}
|
||||
|
||||
@@ -291,15 +291,15 @@ public partial class SettingsManager
|
||||
/// <returns></returns>
|
||||
public DanmakuLayoutAlgorithm GetDanmakuLayoutAlgorithm()
|
||||
{
|
||||
appSettings = GetSettings();
|
||||
if (appSettings.Danmaku.DanmakuLayoutAlgorithm == DanmakuLayoutAlgorithm.NONE)
|
||||
_appSettings = GetSettings();
|
||||
if (_appSettings.Danmaku.DanmakuLayoutAlgorithm == DanmakuLayoutAlgorithm.None)
|
||||
{
|
||||
// 第一次获取,先设置默认值
|
||||
SetDanmakuLayoutAlgorithm(danmakuLayoutAlgorithm);
|
||||
return danmakuLayoutAlgorithm;
|
||||
SetDanmakuLayoutAlgorithm(DanmakuLayoutAlgorithm);
|
||||
return DanmakuLayoutAlgorithm;
|
||||
}
|
||||
|
||||
return appSettings.Danmaku.DanmakuLayoutAlgorithm;
|
||||
return _appSettings.Danmaku.DanmakuLayoutAlgorithm;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -309,7 +309,7 @@ public partial class SettingsManager
|
||||
/// <returns></returns>
|
||||
public bool SetDanmakuLayoutAlgorithm(DanmakuLayoutAlgorithm danmakuLayoutAlgorithm)
|
||||
{
|
||||
appSettings.Danmaku.DanmakuLayoutAlgorithm = danmakuLayoutAlgorithm;
|
||||
_appSettings.Danmaku.DanmakuLayoutAlgorithm = danmakuLayoutAlgorithm;
|
||||
return SetSettings();
|
||||
}
|
||||
}
|
||||
@@ -7,73 +7,72 @@ namespace DownKyi.Core.Settings
|
||||
public partial class SettingsManager
|
||||
{
|
||||
// 是否开启解除地区限制
|
||||
private readonly AllowStatus isLiftingOfRegion = AllowStatus.YES;
|
||||
private const AllowStatus IsLiftingOfRegion = AllowStatus.Yes;
|
||||
|
||||
// 启用https
|
||||
private readonly AllowStatus useSSL = AllowStatus.YES;
|
||||
private const AllowStatus UseSsl = AllowStatus.Yes;
|
||||
|
||||
// UserAgent
|
||||
private readonly string userAgent =
|
||||
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36";
|
||||
private const string UserAgent = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36";
|
||||
|
||||
// 下载器
|
||||
private readonly Downloader downloader = Downloader.ARIA;
|
||||
private const Downloader Downloader = Settings.Downloader.Aria;
|
||||
|
||||
// 最大同时下载数(任务数)
|
||||
private readonly int maxCurrentDownloads = 3;
|
||||
private const int MaxCurrentDownloads = 3;
|
||||
|
||||
// 单文件最大线程数
|
||||
private readonly int split = 8;
|
||||
private const int Split = 8;
|
||||
|
||||
// HttpProxy代理
|
||||
private readonly AllowStatus isHttpProxy = AllowStatus.NO;
|
||||
private readonly string httpProxy = string.Empty;
|
||||
private readonly int httpProxyListenPort = 0;
|
||||
private const AllowStatus IsHttpProxy = AllowStatus.No;
|
||||
private readonly string _httpProxy = string.Empty;
|
||||
private const int HttpProxyListenPort = 0;
|
||||
|
||||
// Aria服务器token
|
||||
private readonly string ariaToken = "downkyi";
|
||||
private const string AriaToken = "downkyi";
|
||||
|
||||
// Aria服务器host
|
||||
private readonly string ariaHost = "http://localhost";
|
||||
private const string AriaHost = "http://localhost";
|
||||
|
||||
// Aria服务器端口号
|
||||
private readonly int ariaListenPort = 6800;
|
||||
private const int AriaListenPort = 6800;
|
||||
|
||||
// Aria日志等级
|
||||
private readonly AriaConfigLogLevel ariaLogLevel = AriaConfigLogLevel.INFO;
|
||||
private const AriaConfigLogLevel AriaLogLevel = AriaConfigLogLevel.INFO;
|
||||
|
||||
// Aria单文件最大线程数
|
||||
private readonly int ariaSplit = 5;
|
||||
private const int AriaSplit = 5;
|
||||
|
||||
// Aria下载速度限制
|
||||
private readonly int ariaMaxOverallDownloadLimit = 0;
|
||||
private const int AriaMaxOverallDownloadLimit = 0;
|
||||
|
||||
// Aria下载单文件速度限制
|
||||
private readonly int ariaMaxDownloadLimit = 0;
|
||||
private const int AriaMaxDownloadLimit = 0;
|
||||
|
||||
// Aria文件预分配
|
||||
private readonly AriaConfigFileAllocation ariaFileAllocation = AriaConfigFileAllocation.NONE;
|
||||
private const AriaConfigFileAllocation AriaFileAllocation = AriaConfigFileAllocation.NONE;
|
||||
|
||||
// Aria HttpProxy代理
|
||||
private readonly AllowStatus isAriaHttpProxy = AllowStatus.NO;
|
||||
private readonly string ariaHttpProxy = string.Empty;
|
||||
private readonly int ariaHttpProxyListenPort = 0;
|
||||
private const AllowStatus IsAriaHttpProxy = AllowStatus.No;
|
||||
private readonly string _ariaHttpProxy = string.Empty;
|
||||
private const int AriaHttpProxyListenPort = 0;
|
||||
|
||||
/// <summary>
|
||||
/// 获取是否解除地区限制
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public AllowStatus IsLiftingOfRegion()
|
||||
public AllowStatus GetIsLiftingOfRegion()
|
||||
{
|
||||
appSettings = GetSettings();
|
||||
if (appSettings.Network.IsLiftingOfRegion == AllowStatus.NONE)
|
||||
_appSettings = GetSettings();
|
||||
if (_appSettings.Network.IsLiftingOfRegion == AllowStatus.None)
|
||||
{
|
||||
// 第一次获取,先设置默认值
|
||||
IsLiftingOfRegion(isLiftingOfRegion);
|
||||
return isLiftingOfRegion;
|
||||
SetIsLiftingOfRegion(IsLiftingOfRegion);
|
||||
return IsLiftingOfRegion;
|
||||
}
|
||||
|
||||
return appSettings.Network.IsLiftingOfRegion;
|
||||
return _appSettings.Network.IsLiftingOfRegion;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -81,9 +80,9 @@ namespace DownKyi.Core.Settings
|
||||
/// </summary>
|
||||
/// <param name="isLiftingOfRegion"></param>
|
||||
/// <returns></returns>
|
||||
public bool IsLiftingOfRegion(AllowStatus isLiftingOfRegion)
|
||||
public bool SetIsLiftingOfRegion(AllowStatus isLiftingOfRegion)
|
||||
{
|
||||
appSettings.Network.IsLiftingOfRegion = isLiftingOfRegion;
|
||||
_appSettings.Network.IsLiftingOfRegion = isLiftingOfRegion;
|
||||
return SetSettings();
|
||||
}
|
||||
|
||||
@@ -91,27 +90,27 @@ namespace DownKyi.Core.Settings
|
||||
/// 获取是否启用https
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public AllowStatus UseSSL()
|
||||
public AllowStatus GetUseSsl()
|
||||
{
|
||||
appSettings = GetSettings();
|
||||
if (appSettings.Network.UseSSL == AllowStatus.NONE)
|
||||
_appSettings = GetSettings();
|
||||
if (_appSettings.Network.UseSsl == AllowStatus.None)
|
||||
{
|
||||
// 第一次获取,先设置默认值
|
||||
UseSSL(useSSL);
|
||||
return useSSL;
|
||||
SetUseSsl(UseSsl);
|
||||
return UseSsl;
|
||||
}
|
||||
|
||||
return appSettings.Network.UseSSL;
|
||||
return _appSettings.Network.UseSsl;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置是否启用https
|
||||
/// </summary>
|
||||
/// <param name="useSSL"></param>
|
||||
/// <param name="useSsl"></param>
|
||||
/// <returns></returns>
|
||||
public bool UseSSL(AllowStatus useSSL)
|
||||
public bool SetUseSsl(AllowStatus useSsl)
|
||||
{
|
||||
appSettings.Network.UseSSL = useSSL;
|
||||
_appSettings.Network.UseSsl = useSsl;
|
||||
return SetSettings();
|
||||
}
|
||||
|
||||
@@ -121,15 +120,15 @@ namespace DownKyi.Core.Settings
|
||||
/// <returns></returns>
|
||||
public string GetUserAgent()
|
||||
{
|
||||
appSettings = GetSettings();
|
||||
if (appSettings.Network.UserAgent == string.Empty)
|
||||
_appSettings = GetSettings();
|
||||
if (_appSettings.Network.UserAgent == string.Empty)
|
||||
{
|
||||
// 第一次获取,先设置默认值
|
||||
SetUserAgent(userAgent);
|
||||
return userAgent;
|
||||
SetUserAgent(UserAgent);
|
||||
return UserAgent;
|
||||
}
|
||||
|
||||
return appSettings.Network.UserAgent;
|
||||
return _appSettings.Network.UserAgent;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -139,7 +138,7 @@ namespace DownKyi.Core.Settings
|
||||
/// <returns></returns>
|
||||
public bool SetUserAgent(string userAgent)
|
||||
{
|
||||
appSettings.Network.UserAgent = userAgent;
|
||||
_appSettings.Network.UserAgent = userAgent;
|
||||
return SetSettings();
|
||||
}
|
||||
|
||||
@@ -149,11 +148,11 @@ namespace DownKyi.Core.Settings
|
||||
/// <returns></returns>
|
||||
public Downloader GetDownloader()
|
||||
{
|
||||
appSettings = GetSettings();
|
||||
if (appSettings.Network.Downloader != Downloader.NOT_SET) return appSettings.Network.Downloader;
|
||||
_appSettings = GetSettings();
|
||||
if (_appSettings.Network.Downloader != Downloader.NotSet) return _appSettings.Network.Downloader;
|
||||
// 第一次获取,先设置默认值
|
||||
SetDownloader(downloader);
|
||||
return downloader;
|
||||
SetDownloader(Downloader);
|
||||
return Downloader;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -163,7 +162,7 @@ namespace DownKyi.Core.Settings
|
||||
/// <returns></returns>
|
||||
public bool SetDownloader(Downloader downloader)
|
||||
{
|
||||
appSettings.Network.Downloader = downloader;
|
||||
_appSettings.Network.Downloader = downloader;
|
||||
return SetSettings();
|
||||
}
|
||||
|
||||
@@ -173,11 +172,11 @@ namespace DownKyi.Core.Settings
|
||||
/// <returns></returns>
|
||||
public int GetMaxCurrentDownloads()
|
||||
{
|
||||
appSettings = GetSettings();
|
||||
if (appSettings.Network.MaxCurrentDownloads != -1) return appSettings.Network.MaxCurrentDownloads;
|
||||
_appSettings = GetSettings();
|
||||
if (_appSettings.Network.MaxCurrentDownloads != -1) return _appSettings.Network.MaxCurrentDownloads;
|
||||
// 第一次获取,先设置默认值
|
||||
SetMaxCurrentDownloads(maxCurrentDownloads);
|
||||
return maxCurrentDownloads;
|
||||
SetMaxCurrentDownloads(MaxCurrentDownloads);
|
||||
return MaxCurrentDownloads;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -187,7 +186,7 @@ namespace DownKyi.Core.Settings
|
||||
/// <returns></returns>
|
||||
public bool SetMaxCurrentDownloads(int maxCurrentDownloads)
|
||||
{
|
||||
appSettings.Network.MaxCurrentDownloads = maxCurrentDownloads;
|
||||
_appSettings.Network.MaxCurrentDownloads = maxCurrentDownloads;
|
||||
return SetSettings();
|
||||
}
|
||||
|
||||
@@ -197,11 +196,11 @@ namespace DownKyi.Core.Settings
|
||||
/// <returns></returns>
|
||||
public int GetSplit()
|
||||
{
|
||||
appSettings = GetSettings();
|
||||
if (appSettings.Network.Split != -1) return appSettings.Network.Split;
|
||||
_appSettings = GetSettings();
|
||||
if (_appSettings.Network.Split != -1) return _appSettings.Network.Split;
|
||||
// 第一次获取,先设置默认值
|
||||
SetSplit(split);
|
||||
return split;
|
||||
SetSplit(Split);
|
||||
return Split;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -211,7 +210,7 @@ namespace DownKyi.Core.Settings
|
||||
/// <returns></returns>
|
||||
public bool SetSplit(int split)
|
||||
{
|
||||
appSettings.Network.Split = split;
|
||||
_appSettings.Network.Split = split;
|
||||
return SetSettings();
|
||||
}
|
||||
|
||||
@@ -219,13 +218,13 @@ namespace DownKyi.Core.Settings
|
||||
/// 获取是否开启Http代理
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public AllowStatus IsHttpProxy()
|
||||
public AllowStatus GetIsHttpProxy()
|
||||
{
|
||||
appSettings = GetSettings();
|
||||
if (appSettings.Network.IsHttpProxy != AllowStatus.NONE) return appSettings.Network.IsHttpProxy;
|
||||
_appSettings = GetSettings();
|
||||
if (_appSettings.Network.IsHttpProxy != AllowStatus.None) return _appSettings.Network.IsHttpProxy;
|
||||
// 第一次获取,先设置默认值
|
||||
IsHttpProxy(isHttpProxy);
|
||||
return isHttpProxy;
|
||||
SetIsHttpProxy(IsHttpProxy);
|
||||
return IsHttpProxy;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -233,9 +232,9 @@ namespace DownKyi.Core.Settings
|
||||
/// </summary>
|
||||
/// <param name="isHttpProxy"></param>
|
||||
/// <returns></returns>
|
||||
public bool IsHttpProxy(AllowStatus isHttpProxy)
|
||||
public bool SetIsHttpProxy(AllowStatus isHttpProxy)
|
||||
{
|
||||
appSettings.Network.IsHttpProxy = isHttpProxy;
|
||||
_appSettings.Network.IsHttpProxy = isHttpProxy;
|
||||
return SetSettings();
|
||||
}
|
||||
|
||||
@@ -245,11 +244,11 @@ namespace DownKyi.Core.Settings
|
||||
/// <returns></returns>
|
||||
public string GetHttpProxy()
|
||||
{
|
||||
appSettings = GetSettings();
|
||||
if (appSettings.Network.HttpProxy != null) return appSettings.Network.HttpProxy;
|
||||
_appSettings = GetSettings();
|
||||
if (_appSettings.Network.HttpProxy != null) return _appSettings.Network.HttpProxy;
|
||||
// 第一次获取,先设置默认值
|
||||
SetHttpProxy(httpProxy);
|
||||
return httpProxy;
|
||||
SetHttpProxy(_httpProxy);
|
||||
return _httpProxy;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -259,7 +258,7 @@ namespace DownKyi.Core.Settings
|
||||
/// <returns></returns>
|
||||
public bool SetHttpProxy(string httpProxy)
|
||||
{
|
||||
appSettings.Network.HttpProxy = httpProxy;
|
||||
_appSettings.Network.HttpProxy = httpProxy;
|
||||
return SetSettings();
|
||||
}
|
||||
|
||||
@@ -269,11 +268,11 @@ namespace DownKyi.Core.Settings
|
||||
/// <returns></returns>
|
||||
public int GetHttpProxyListenPort()
|
||||
{
|
||||
appSettings = GetSettings();
|
||||
if (appSettings.Network.HttpProxyListenPort != -1) return appSettings.Network.HttpProxyListenPort;
|
||||
_appSettings = GetSettings();
|
||||
if (_appSettings.Network.HttpProxyListenPort != -1) return _appSettings.Network.HttpProxyListenPort;
|
||||
// 第一次获取,先设置默认值
|
||||
SetHttpProxyListenPort(httpProxyListenPort);
|
||||
return httpProxyListenPort;
|
||||
SetHttpProxyListenPort(HttpProxyListenPort);
|
||||
return HttpProxyListenPort;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -283,7 +282,7 @@ namespace DownKyi.Core.Settings
|
||||
/// <returns></returns>
|
||||
public bool SetHttpProxyListenPort(int httpProxyListenPort)
|
||||
{
|
||||
appSettings.Network.HttpProxyListenPort = httpProxyListenPort;
|
||||
_appSettings.Network.HttpProxyListenPort = httpProxyListenPort;
|
||||
return SetSettings();
|
||||
}
|
||||
|
||||
@@ -293,15 +292,15 @@ namespace DownKyi.Core.Settings
|
||||
/// <returns></returns>
|
||||
public string GetAriaToken()
|
||||
{
|
||||
appSettings = GetSettings();
|
||||
if (appSettings.Network.AriaToken == null)
|
||||
_appSettings = GetSettings();
|
||||
if (_appSettings.Network.AriaToken == null)
|
||||
{
|
||||
// 第一次获取,先设置默认值
|
||||
SetHttpProxy(ariaToken);
|
||||
return ariaToken;
|
||||
SetAriaToken(AriaToken);
|
||||
return AriaToken;
|
||||
}
|
||||
|
||||
return appSettings.Network.AriaToken;
|
||||
return _appSettings.Network.AriaToken;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -311,7 +310,7 @@ namespace DownKyi.Core.Settings
|
||||
/// <returns></returns>
|
||||
public bool SetAriaToken(string token)
|
||||
{
|
||||
appSettings.Network.AriaToken = token;
|
||||
_appSettings.Network.AriaToken = token;
|
||||
return SetSettings();
|
||||
}
|
||||
|
||||
@@ -321,15 +320,15 @@ namespace DownKyi.Core.Settings
|
||||
/// <returns></returns>
|
||||
public string GetAriaHost()
|
||||
{
|
||||
appSettings = GetSettings();
|
||||
if (appSettings.Network.AriaHost == null)
|
||||
_appSettings = GetSettings();
|
||||
if (_appSettings.Network.AriaHost == null)
|
||||
{
|
||||
// 第一次获取,先设置默认值
|
||||
SetHttpProxy(ariaHost);
|
||||
return ariaHost;
|
||||
SetAriaHost(AriaHost);
|
||||
return AriaHost;
|
||||
}
|
||||
|
||||
return appSettings.Network.AriaHost;
|
||||
return _appSettings.Network.AriaHost;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -339,7 +338,7 @@ namespace DownKyi.Core.Settings
|
||||
/// <returns></returns>
|
||||
public bool SetAriaHost(string host)
|
||||
{
|
||||
appSettings.Network.AriaHost = host;
|
||||
_appSettings.Network.AriaHost = host;
|
||||
return SetSettings();
|
||||
}
|
||||
|
||||
@@ -349,15 +348,15 @@ namespace DownKyi.Core.Settings
|
||||
/// <returns></returns>
|
||||
public int GetAriaListenPort()
|
||||
{
|
||||
appSettings = GetSettings();
|
||||
if (appSettings.Network.AriaListenPort == -1)
|
||||
_appSettings = GetSettings();
|
||||
if (_appSettings.Network.AriaListenPort == -1)
|
||||
{
|
||||
// 第一次获取,先设置默认值
|
||||
SetAriaListenPort(ariaListenPort);
|
||||
return ariaListenPort;
|
||||
SetAriaListenPort(AriaListenPort);
|
||||
return AriaListenPort;
|
||||
}
|
||||
|
||||
return appSettings.Network.AriaListenPort;
|
||||
return _appSettings.Network.AriaListenPort;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -367,7 +366,7 @@ namespace DownKyi.Core.Settings
|
||||
/// <returns></returns>
|
||||
public bool SetAriaListenPort(int ariaListenPort)
|
||||
{
|
||||
appSettings.Network.AriaListenPort = ariaListenPort;
|
||||
_appSettings.Network.AriaListenPort = ariaListenPort;
|
||||
return SetSettings();
|
||||
}
|
||||
|
||||
@@ -377,15 +376,15 @@ namespace DownKyi.Core.Settings
|
||||
/// <returns></returns>
|
||||
public AriaConfigLogLevel GetAriaLogLevel()
|
||||
{
|
||||
appSettings = GetSettings();
|
||||
if (appSettings.Network.AriaLogLevel == AriaConfigLogLevel.NOT_SET)
|
||||
_appSettings = GetSettings();
|
||||
if (_appSettings.Network.AriaLogLevel == AriaConfigLogLevel.NOT_SET)
|
||||
{
|
||||
// 第一次获取,先设置默认值
|
||||
SetAriaLogLevel(ariaLogLevel);
|
||||
return ariaLogLevel;
|
||||
SetAriaLogLevel(AriaLogLevel);
|
||||
return AriaLogLevel;
|
||||
}
|
||||
|
||||
return appSettings.Network.AriaLogLevel;
|
||||
return _appSettings.Network.AriaLogLevel;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -395,7 +394,7 @@ namespace DownKyi.Core.Settings
|
||||
/// <returns></returns>
|
||||
public bool SetAriaLogLevel(AriaConfigLogLevel ariaLogLevel)
|
||||
{
|
||||
appSettings.Network.AriaLogLevel = ariaLogLevel;
|
||||
_appSettings.Network.AriaLogLevel = ariaLogLevel;
|
||||
return SetSettings();
|
||||
}
|
||||
|
||||
@@ -405,15 +404,15 @@ namespace DownKyi.Core.Settings
|
||||
/// <returns></returns>
|
||||
public int GetAriaSplit()
|
||||
{
|
||||
appSettings = GetSettings();
|
||||
if (appSettings.Network.AriaSplit == -1)
|
||||
_appSettings = GetSettings();
|
||||
if (_appSettings.Network.AriaSplit == -1)
|
||||
{
|
||||
// 第一次获取,先设置默认值
|
||||
SetAriaSplit(ariaSplit);
|
||||
return ariaSplit;
|
||||
SetAriaSplit(AriaSplit);
|
||||
return AriaSplit;
|
||||
}
|
||||
|
||||
return appSettings.Network.AriaSplit;
|
||||
return _appSettings.Network.AriaSplit;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -423,7 +422,7 @@ namespace DownKyi.Core.Settings
|
||||
/// <returns></returns>
|
||||
public bool SetAriaSplit(int ariaSplit)
|
||||
{
|
||||
appSettings.Network.AriaSplit = ariaSplit;
|
||||
_appSettings.Network.AriaSplit = ariaSplit;
|
||||
return SetSettings();
|
||||
}
|
||||
|
||||
@@ -433,15 +432,15 @@ namespace DownKyi.Core.Settings
|
||||
/// <returns></returns>
|
||||
public int GetAriaMaxOverallDownloadLimit()
|
||||
{
|
||||
appSettings = GetSettings();
|
||||
if (appSettings.Network.AriaMaxOverallDownloadLimit == -1)
|
||||
_appSettings = GetSettings();
|
||||
if (_appSettings.Network.AriaMaxOverallDownloadLimit == -1)
|
||||
{
|
||||
// 第一次获取,先设置默认值
|
||||
SetAriaMaxOverallDownloadLimit(ariaMaxOverallDownloadLimit);
|
||||
return ariaMaxOverallDownloadLimit;
|
||||
SetAriaMaxOverallDownloadLimit(AriaMaxOverallDownloadLimit);
|
||||
return AriaMaxOverallDownloadLimit;
|
||||
}
|
||||
|
||||
return appSettings.Network.AriaMaxOverallDownloadLimit;
|
||||
return _appSettings.Network.AriaMaxOverallDownloadLimit;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -451,7 +450,7 @@ namespace DownKyi.Core.Settings
|
||||
/// <returns></returns>
|
||||
public bool SetAriaMaxOverallDownloadLimit(int ariaMaxOverallDownloadLimit)
|
||||
{
|
||||
appSettings.Network.AriaMaxOverallDownloadLimit = ariaMaxOverallDownloadLimit;
|
||||
_appSettings.Network.AriaMaxOverallDownloadLimit = ariaMaxOverallDownloadLimit;
|
||||
return SetSettings();
|
||||
}
|
||||
|
||||
@@ -461,15 +460,15 @@ namespace DownKyi.Core.Settings
|
||||
/// <returns></returns>
|
||||
public int GetAriaMaxDownloadLimit()
|
||||
{
|
||||
appSettings = GetSettings();
|
||||
if (appSettings.Network.AriaMaxDownloadLimit == -1)
|
||||
_appSettings = GetSettings();
|
||||
if (_appSettings.Network.AriaMaxDownloadLimit == -1)
|
||||
{
|
||||
// 第一次获取,先设置默认值
|
||||
SetAriaMaxDownloadLimit(ariaMaxDownloadLimit);
|
||||
return ariaMaxDownloadLimit;
|
||||
SetAriaMaxDownloadLimit(AriaMaxDownloadLimit);
|
||||
return AriaMaxDownloadLimit;
|
||||
}
|
||||
|
||||
return appSettings.Network.AriaMaxDownloadLimit;
|
||||
return _appSettings.Network.AriaMaxDownloadLimit;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -479,7 +478,7 @@ namespace DownKyi.Core.Settings
|
||||
/// <returns></returns>
|
||||
public bool SetAriaMaxDownloadLimit(int ariaMaxDownloadLimit)
|
||||
{
|
||||
appSettings.Network.AriaMaxDownloadLimit = ariaMaxDownloadLimit;
|
||||
_appSettings.Network.AriaMaxDownloadLimit = ariaMaxDownloadLimit;
|
||||
return SetSettings();
|
||||
}
|
||||
|
||||
@@ -489,15 +488,15 @@ namespace DownKyi.Core.Settings
|
||||
/// <returns></returns>
|
||||
public AriaConfigFileAllocation GetAriaFileAllocation()
|
||||
{
|
||||
appSettings = GetSettings();
|
||||
if (appSettings.Network.AriaFileAllocation == AriaConfigFileAllocation.NOT_SET)
|
||||
_appSettings = GetSettings();
|
||||
if (_appSettings.Network.AriaFileAllocation == AriaConfigFileAllocation.NOT_SET)
|
||||
{
|
||||
// 第一次获取,先设置默认值
|
||||
SetAriaFileAllocation(ariaFileAllocation);
|
||||
return ariaFileAllocation;
|
||||
SetAriaFileAllocation(AriaFileAllocation);
|
||||
return AriaFileAllocation;
|
||||
}
|
||||
|
||||
return appSettings.Network.AriaFileAllocation;
|
||||
return _appSettings.Network.AriaFileAllocation;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -507,7 +506,7 @@ namespace DownKyi.Core.Settings
|
||||
/// <returns></returns>
|
||||
public bool SetAriaFileAllocation(AriaConfigFileAllocation ariaFileAllocation)
|
||||
{
|
||||
appSettings.Network.AriaFileAllocation = ariaFileAllocation;
|
||||
_appSettings.Network.AriaFileAllocation = ariaFileAllocation;
|
||||
return SetSettings();
|
||||
}
|
||||
|
||||
@@ -515,17 +514,17 @@ namespace DownKyi.Core.Settings
|
||||
/// 获取是否开启Aria http代理
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public AllowStatus IsAriaHttpProxy()
|
||||
public AllowStatus GetIsAriaHttpProxy()
|
||||
{
|
||||
appSettings = GetSettings();
|
||||
if (appSettings.Network.IsAriaHttpProxy == AllowStatus.NONE)
|
||||
_appSettings = GetSettings();
|
||||
if (_appSettings.Network.IsAriaHttpProxy == AllowStatus.None)
|
||||
{
|
||||
// 第一次获取,先设置默认值
|
||||
IsAriaHttpProxy(isAriaHttpProxy);
|
||||
return isAriaHttpProxy;
|
||||
SetIsAriaHttpProxy(IsAriaHttpProxy);
|
||||
return IsAriaHttpProxy;
|
||||
}
|
||||
|
||||
return appSettings.Network.IsAriaHttpProxy;
|
||||
return _appSettings.Network.IsAriaHttpProxy;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -533,9 +532,9 @@ namespace DownKyi.Core.Settings
|
||||
/// </summary>
|
||||
/// <param name="isAriaHttpProxy"></param>
|
||||
/// <returns></returns>
|
||||
public bool IsAriaHttpProxy(AllowStatus isAriaHttpProxy)
|
||||
public bool SetIsAriaHttpProxy(AllowStatus isAriaHttpProxy)
|
||||
{
|
||||
appSettings.Network.IsAriaHttpProxy = isAriaHttpProxy;
|
||||
_appSettings.Network.IsAriaHttpProxy = isAriaHttpProxy;
|
||||
return SetSettings();
|
||||
}
|
||||
|
||||
@@ -545,15 +544,15 @@ namespace DownKyi.Core.Settings
|
||||
/// <returns></returns>
|
||||
public string GetAriaHttpProxy()
|
||||
{
|
||||
appSettings = GetSettings();
|
||||
if (appSettings.Network.AriaHttpProxy == null)
|
||||
_appSettings = GetSettings();
|
||||
if (_appSettings.Network.AriaHttpProxy == null)
|
||||
{
|
||||
// 第一次获取,先设置默认值
|
||||
SetAriaHttpProxy(ariaHttpProxy);
|
||||
return ariaHttpProxy;
|
||||
SetAriaHttpProxy(_ariaHttpProxy);
|
||||
return _ariaHttpProxy;
|
||||
}
|
||||
|
||||
return appSettings.Network.AriaHttpProxy;
|
||||
return _appSettings.Network.AriaHttpProxy;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -563,7 +562,7 @@ namespace DownKyi.Core.Settings
|
||||
/// <returns></returns>
|
||||
public bool SetAriaHttpProxy(string ariaHttpProxy)
|
||||
{
|
||||
appSettings.Network.AriaHttpProxy = ariaHttpProxy;
|
||||
_appSettings.Network.AriaHttpProxy = ariaHttpProxy;
|
||||
return SetSettings();
|
||||
}
|
||||
|
||||
@@ -573,15 +572,15 @@ namespace DownKyi.Core.Settings
|
||||
/// <returns></returns>
|
||||
public int GetAriaHttpProxyListenPort()
|
||||
{
|
||||
appSettings = GetSettings();
|
||||
if (appSettings.Network.AriaHttpProxyListenPort == -1)
|
||||
_appSettings = GetSettings();
|
||||
if (_appSettings.Network.AriaHttpProxyListenPort == -1)
|
||||
{
|
||||
// 第一次获取,先设置默认值
|
||||
SetAriaHttpProxyListenPort(ariaHttpProxyListenPort);
|
||||
return ariaHttpProxyListenPort;
|
||||
SetAriaHttpProxyListenPort(AriaHttpProxyListenPort);
|
||||
return AriaHttpProxyListenPort;
|
||||
}
|
||||
|
||||
return appSettings.Network.AriaHttpProxyListenPort;
|
||||
return _appSettings.Network.AriaHttpProxyListenPort;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -591,7 +590,7 @@ namespace DownKyi.Core.Settings
|
||||
/// <returns></returns>
|
||||
public bool SetAriaHttpProxyListenPort(int ariaHttpProxyListenPort)
|
||||
{
|
||||
appSettings.Network.AriaHttpProxyListenPort = ariaHttpProxyListenPort;
|
||||
_appSettings.Network.AriaHttpProxyListenPort = ariaHttpProxyListenPort;
|
||||
return SetSettings();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ namespace DownKyi.Core.Settings;
|
||||
public partial class SettingsManager
|
||||
{
|
||||
// 登录用户的mid
|
||||
private readonly UserInfoSettings userInfo = new UserInfoSettings
|
||||
private readonly UserInfoSettings _userInfo = new()
|
||||
{
|
||||
Mid = -1,
|
||||
Name = "",
|
||||
@@ -19,25 +19,25 @@ public partial class SettingsManager
|
||||
/// <returns></returns>
|
||||
public UserInfoSettings GetUserInfo()
|
||||
{
|
||||
appSettings = GetSettings();
|
||||
if (appSettings.UserInfo == null)
|
||||
_appSettings = GetSettings();
|
||||
if (_appSettings.UserInfo == null)
|
||||
{
|
||||
// 第一次获取,先设置默认值
|
||||
SetUserInfo(userInfo);
|
||||
return userInfo;
|
||||
SetUserInfo(_userInfo);
|
||||
return _userInfo;
|
||||
}
|
||||
|
||||
return appSettings.UserInfo;
|
||||
return _appSettings.UserInfo;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置中保存登录用户的信息,在index刷新用户状态时使用
|
||||
/// </summary>
|
||||
/// <param name="mid"></param>
|
||||
/// <param name="userInfo"></param>
|
||||
/// <returns></returns>
|
||||
public bool SetUserInfo(UserInfoSettings userInfo)
|
||||
{
|
||||
appSettings.UserInfo = userInfo;
|
||||
_appSettings.UserInfo = userInfo;
|
||||
return SetSettings();
|
||||
}
|
||||
}
|
||||
@@ -7,56 +7,56 @@ namespace DownKyi.Core.Settings;
|
||||
public partial class SettingsManager
|
||||
{
|
||||
// 设置优先下载的视频编码
|
||||
private readonly int videoCodecs = 7;
|
||||
private const int VideoCodecs = 7;
|
||||
|
||||
// 设置优先下载画质
|
||||
private readonly int quality = 120;
|
||||
private const int Quality = 120;
|
||||
|
||||
// 设置优先下载音质
|
||||
private readonly int audioQuality = 30280;
|
||||
|
||||
private const int AudioQuality = 30280;
|
||||
|
||||
// 设置首选视频解析方式
|
||||
private const int VideoParseType = 1;
|
||||
|
||||
// 是否下载flv视频后转码为mp4
|
||||
private readonly AllowStatus isTranscodingFlvToMp4 = AllowStatus.YES;
|
||||
|
||||
private const AllowStatus IsTranscodingFlvToMp4 = AllowStatus.Yes;
|
||||
|
||||
// 是否下载aac音频后转码为mp3
|
||||
private readonly AllowStatus isTranscodingAacToMp3 = AllowStatus.YES;
|
||||
private const AllowStatus IsTranscodingAacToMp3 = AllowStatus.Yes;
|
||||
|
||||
// 默认下载目录
|
||||
private readonly string saveVideoRootPath = StorageManager.GetMedia();
|
||||
private readonly string _saveVideoRootPath = StorageManager.GetMedia();
|
||||
|
||||
// 历史下载目录
|
||||
private readonly List<string> historyVideoRootPaths = new();
|
||||
private readonly List<string> _historyVideoRootPaths = new();
|
||||
|
||||
// 是否使用默认下载目录,如果是,则每次点击下载选中项时不再询问下载目录
|
||||
private readonly AllowStatus isUseSaveVideoRootPath = AllowStatus.NO;
|
||||
private const AllowStatus IsUseSaveVideoRootPath = AllowStatus.No;
|
||||
|
||||
// 下载内容
|
||||
private readonly VideoContentSettings videoContent = new();
|
||||
private readonly VideoContentSettings _videoContent = new();
|
||||
|
||||
// 文件命名格式
|
||||
private readonly List<FileNamePart> fileNameParts = new()
|
||||
private readonly List<FileNamePart> _fileNameParts = new()
|
||||
{
|
||||
FileNamePart.MAIN_TITLE,
|
||||
FileNamePart.SLASH,
|
||||
FileNamePart.SECTION,
|
||||
FileNamePart.SLASH,
|
||||
FileNamePart.ORDER,
|
||||
FileNamePart.HYPHEN,
|
||||
FileNamePart.PAGE_TITLE,
|
||||
FileNamePart.HYPHEN,
|
||||
FileNamePart.VIDEO_QUALITY,
|
||||
FileNamePart.HYPHEN,
|
||||
FileNamePart.VIDEO_CODEC,
|
||||
FileNamePart.MainTitle,
|
||||
FileNamePart.Slash,
|
||||
FileNamePart.Section,
|
||||
FileNamePart.Slash,
|
||||
FileNamePart.Order,
|
||||
FileNamePart.Hyphen,
|
||||
FileNamePart.PageTitle,
|
||||
FileNamePart.Hyphen,
|
||||
FileNamePart.VideoQuality,
|
||||
FileNamePart.Hyphen,
|
||||
FileNamePart.VideoCodec,
|
||||
};
|
||||
|
||||
// 文件命名中的时间格式
|
||||
private readonly string fileNamePartTimeFormat = "yyyy-MM-dd";
|
||||
private const string FileNamePartTimeFormat = "yyyy-MM-dd";
|
||||
|
||||
// 文件命名中的序号格式
|
||||
private readonly OrderFormat orderFormat = OrderFormat.NATURAL;
|
||||
private const OrderFormat OrderFormat = Settings.OrderFormat.Natural;
|
||||
|
||||
/// <summary>
|
||||
/// 获取优先下载的视频编码
|
||||
@@ -64,15 +64,15 @@ public partial class SettingsManager
|
||||
/// <returns></returns>
|
||||
public int GetVideoCodecs()
|
||||
{
|
||||
appSettings = GetSettings();
|
||||
if (appSettings.Video.VideoCodecs == -1)
|
||||
_appSettings = GetSettings();
|
||||
if (_appSettings.Video.VideoCodecs == -1)
|
||||
{
|
||||
// 第一次获取,先设置默认值
|
||||
SetVideoCodecs(videoCodecs);
|
||||
return videoCodecs;
|
||||
SetVideoCodecs(VideoCodecs);
|
||||
return VideoCodecs;
|
||||
}
|
||||
|
||||
return appSettings.Video.VideoCodecs;
|
||||
return _appSettings.Video.VideoCodecs;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -82,7 +82,7 @@ public partial class SettingsManager
|
||||
/// <returns></returns>
|
||||
public bool SetVideoCodecs(int videoCodecs)
|
||||
{
|
||||
appSettings.Video.VideoCodecs = videoCodecs;
|
||||
_appSettings.Video.VideoCodecs = videoCodecs;
|
||||
return SetSettings();
|
||||
}
|
||||
|
||||
@@ -92,15 +92,15 @@ public partial class SettingsManager
|
||||
/// <returns></returns>
|
||||
public int GetQuality()
|
||||
{
|
||||
appSettings = GetSettings();
|
||||
if (appSettings.Video.Quality == -1)
|
||||
_appSettings = GetSettings();
|
||||
if (_appSettings.Video.Quality == -1)
|
||||
{
|
||||
// 第一次获取,先设置默认值
|
||||
SetQuality(quality);
|
||||
return quality;
|
||||
SetQuality(Quality);
|
||||
return Quality;
|
||||
}
|
||||
|
||||
return appSettings.Video.Quality;
|
||||
return _appSettings.Video.Quality;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -110,7 +110,7 @@ public partial class SettingsManager
|
||||
/// <returns></returns>
|
||||
public bool SetQuality(int quality)
|
||||
{
|
||||
appSettings.Video.Quality = quality;
|
||||
_appSettings.Video.Quality = quality;
|
||||
return SetSettings();
|
||||
}
|
||||
|
||||
@@ -120,15 +120,15 @@ public partial class SettingsManager
|
||||
/// <returns></returns>
|
||||
public int GetAudioQuality()
|
||||
{
|
||||
appSettings = GetSettings();
|
||||
if (appSettings.Video.AudioQuality == -1)
|
||||
_appSettings = GetSettings();
|
||||
if (_appSettings.Video.AudioQuality == -1)
|
||||
{
|
||||
// 第一次获取,先设置默认值
|
||||
SetAudioQuality(audioQuality);
|
||||
return audioQuality;
|
||||
SetAudioQuality(AudioQuality);
|
||||
return AudioQuality;
|
||||
}
|
||||
|
||||
return appSettings.Video.AudioQuality;
|
||||
return _appSettings.Video.AudioQuality;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -138,7 +138,7 @@ public partial class SettingsManager
|
||||
/// <returns></returns>
|
||||
public bool SetAudioQuality(int quality)
|
||||
{
|
||||
appSettings.Video.AudioQuality = quality;
|
||||
_appSettings.Video.AudioQuality = quality;
|
||||
return SetSettings();
|
||||
}
|
||||
|
||||
@@ -148,15 +148,15 @@ public partial class SettingsManager
|
||||
/// <returns></returns>
|
||||
public int GetVideoParseType()
|
||||
{
|
||||
appSettings = GetSettings();
|
||||
if (appSettings.Video.VideoParseType == null)
|
||||
_appSettings = GetSettings();
|
||||
if (_appSettings.Video.VideoParseType == null)
|
||||
{
|
||||
// 第一次获取,先设置默认值
|
||||
SetVideoParseType(VideoParseType);
|
||||
return VideoParseType;
|
||||
}
|
||||
|
||||
return appSettings.Video.VideoParseType;
|
||||
return _appSettings.Video.VideoParseType;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -166,7 +166,7 @@ public partial class SettingsManager
|
||||
/// <returns></returns>
|
||||
public bool SetVideoParseType(int videoParseType)
|
||||
{
|
||||
appSettings.Video.VideoParseType = videoParseType;
|
||||
_appSettings.Video.VideoParseType = videoParseType;
|
||||
return SetSettings();
|
||||
}
|
||||
|
||||
@@ -174,17 +174,17 @@ public partial class SettingsManager
|
||||
/// 获取是否下载flv视频后转码为mp4
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public AllowStatus IsTranscodingFlvToMp4()
|
||||
public AllowStatus GetIsTranscodingFlvToMp4()
|
||||
{
|
||||
appSettings = GetSettings();
|
||||
if (appSettings.Video.IsTranscodingFlvToMp4 == AllowStatus.NONE)
|
||||
_appSettings = GetSettings();
|
||||
if (_appSettings.Video.IsTranscodingFlvToMp4 == AllowStatus.None)
|
||||
{
|
||||
// 第一次获取,先设置默认值
|
||||
IsTranscodingFlvToMp4(isTranscodingFlvToMp4);
|
||||
return isTranscodingFlvToMp4;
|
||||
SetIsTranscodingFlvToMp4(IsTranscodingFlvToMp4);
|
||||
return IsTranscodingFlvToMp4;
|
||||
}
|
||||
|
||||
return appSettings.Video.IsTranscodingFlvToMp4;
|
||||
return _appSettings.Video.IsTranscodingFlvToMp4;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -192,9 +192,9 @@ public partial class SettingsManager
|
||||
/// </summary>
|
||||
/// <param name="isTranscodingFlvToMp4"></param>
|
||||
/// <returns></returns>
|
||||
public bool IsTranscodingFlvToMp4(AllowStatus isTranscodingFlvToMp4)
|
||||
public bool SetIsTranscodingFlvToMp4(AllowStatus isTranscodingFlvToMp4)
|
||||
{
|
||||
appSettings.Video.IsTranscodingFlvToMp4 = isTranscodingFlvToMp4;
|
||||
_appSettings.Video.IsTranscodingFlvToMp4 = isTranscodingFlvToMp4;
|
||||
return SetSettings();
|
||||
}
|
||||
|
||||
@@ -202,17 +202,17 @@ public partial class SettingsManager
|
||||
/// 获取是否下载aac音频后转码为mp3
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public AllowStatus IsTranscodingAacToMp3()
|
||||
public AllowStatus GetIsTranscodingAacToMp3()
|
||||
{
|
||||
appSettings = GetSettings();
|
||||
if (appSettings.Video.IsTranscodingAacToMp3 == AllowStatus.NONE)
|
||||
_appSettings = GetSettings();
|
||||
if (_appSettings.Video.IsTranscodingAacToMp3 == AllowStatus.None)
|
||||
{
|
||||
// 第一次获取,先设置默认值
|
||||
IsTranscodingAacToMp3(isTranscodingAacToMp3);
|
||||
return isTranscodingAacToMp3;
|
||||
SetIsTranscodingAacToMp3(IsTranscodingAacToMp3);
|
||||
return IsTranscodingAacToMp3;
|
||||
}
|
||||
|
||||
return appSettings.Video.IsTranscodingAacToMp3;
|
||||
return _appSettings.Video.IsTranscodingAacToMp3;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -220,9 +220,9 @@ public partial class SettingsManager
|
||||
/// </summary>
|
||||
/// <param name="isTranscodingAacToMp3"></param>
|
||||
/// <returns></returns>
|
||||
public bool IsTranscodingAacToMp3(AllowStatus isTranscodingAacToMp3)
|
||||
public bool SetIsTranscodingAacToMp3(AllowStatus isTranscodingAacToMp3)
|
||||
{
|
||||
appSettings.Video.IsTranscodingAacToMp3 = isTranscodingAacToMp3;
|
||||
_appSettings.Video.IsTranscodingAacToMp3 = isTranscodingAacToMp3;
|
||||
return SetSettings();
|
||||
}
|
||||
|
||||
@@ -232,15 +232,15 @@ public partial class SettingsManager
|
||||
/// <returns></returns>
|
||||
public string GetSaveVideoRootPath()
|
||||
{
|
||||
appSettings = GetSettings();
|
||||
if (appSettings.Video.SaveVideoRootPath == null)
|
||||
_appSettings = GetSettings();
|
||||
if (_appSettings.Video.SaveVideoRootPath == null)
|
||||
{
|
||||
// 第一次获取,先设置默认值
|
||||
SetSaveVideoRootPath(saveVideoRootPath);
|
||||
return saveVideoRootPath;
|
||||
SetSaveVideoRootPath(_saveVideoRootPath);
|
||||
return _saveVideoRootPath;
|
||||
}
|
||||
|
||||
return appSettings.Video.SaveVideoRootPath;
|
||||
return _appSettings.Video.SaveVideoRootPath;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -250,7 +250,7 @@ public partial class SettingsManager
|
||||
/// <returns></returns>
|
||||
public bool SetSaveVideoRootPath(string path)
|
||||
{
|
||||
appSettings.Video.SaveVideoRootPath = path;
|
||||
_appSettings.Video.SaveVideoRootPath = path;
|
||||
return SetSettings();
|
||||
}
|
||||
|
||||
@@ -260,15 +260,15 @@ public partial class SettingsManager
|
||||
/// <returns></returns>
|
||||
public List<string> GetHistoryVideoRootPaths()
|
||||
{
|
||||
appSettings = GetSettings();
|
||||
if (appSettings.Video.HistoryVideoRootPaths == null)
|
||||
_appSettings = GetSettings();
|
||||
if (_appSettings.Video.HistoryVideoRootPaths == null)
|
||||
{
|
||||
// 第一次获取,先设置默认值
|
||||
SetHistoryVideoRootPaths(historyVideoRootPaths);
|
||||
return historyVideoRootPaths;
|
||||
SetHistoryVideoRootPaths(_historyVideoRootPaths);
|
||||
return _historyVideoRootPaths;
|
||||
}
|
||||
|
||||
return appSettings.Video.HistoryVideoRootPaths;
|
||||
return _appSettings.Video.HistoryVideoRootPaths;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -278,7 +278,7 @@ public partial class SettingsManager
|
||||
/// <returns></returns>
|
||||
public bool SetHistoryVideoRootPaths(List<string> historyPaths)
|
||||
{
|
||||
appSettings.Video.HistoryVideoRootPaths = historyPaths;
|
||||
_appSettings.Video.HistoryVideoRootPaths = historyPaths;
|
||||
return SetSettings();
|
||||
}
|
||||
|
||||
@@ -286,17 +286,17 @@ public partial class SettingsManager
|
||||
/// 获取是否使用默认下载目录
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public AllowStatus IsUseSaveVideoRootPath()
|
||||
public AllowStatus GetIsUseSaveVideoRootPath()
|
||||
{
|
||||
appSettings = GetSettings();
|
||||
if (appSettings.Video.IsUseSaveVideoRootPath == AllowStatus.NONE)
|
||||
_appSettings = GetSettings();
|
||||
if (_appSettings.Video.IsUseSaveVideoRootPath == AllowStatus.None)
|
||||
{
|
||||
// 第一次获取,先设置默认值
|
||||
IsUseSaveVideoRootPath(isUseSaveVideoRootPath);
|
||||
return isUseSaveVideoRootPath;
|
||||
SetIsUseSaveVideoRootPath(IsUseSaveVideoRootPath);
|
||||
return IsUseSaveVideoRootPath;
|
||||
}
|
||||
|
||||
return appSettings.Video.IsUseSaveVideoRootPath;
|
||||
return _appSettings.Video.IsUseSaveVideoRootPath;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -304,9 +304,9 @@ public partial class SettingsManager
|
||||
/// </summary>
|
||||
/// <param name="isUseSaveVideoRootPath"></param>
|
||||
/// <returns></returns>
|
||||
public bool IsUseSaveVideoRootPath(AllowStatus isUseSaveVideoRootPath)
|
||||
public bool SetIsUseSaveVideoRootPath(AllowStatus isUseSaveVideoRootPath)
|
||||
{
|
||||
appSettings.Video.IsUseSaveVideoRootPath = isUseSaveVideoRootPath;
|
||||
_appSettings.Video.IsUseSaveVideoRootPath = isUseSaveVideoRootPath;
|
||||
return SetSettings();
|
||||
}
|
||||
|
||||
@@ -316,15 +316,15 @@ public partial class SettingsManager
|
||||
/// <returns></returns>
|
||||
public VideoContentSettings GetVideoContent()
|
||||
{
|
||||
appSettings = GetSettings();
|
||||
if (appSettings.Video.VideoContent == null)
|
||||
_appSettings = GetSettings();
|
||||
if (_appSettings.Video.VideoContent == null)
|
||||
{
|
||||
// 第一次获取,先设置默认值
|
||||
SetVideoContent(videoContent);
|
||||
return videoContent;
|
||||
SetVideoContent(_videoContent);
|
||||
return _videoContent;
|
||||
}
|
||||
|
||||
return appSettings.Video.VideoContent;
|
||||
return _appSettings.Video.VideoContent;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -334,7 +334,7 @@ public partial class SettingsManager
|
||||
/// <returns></returns>
|
||||
public bool SetVideoContent(VideoContentSettings videoContent)
|
||||
{
|
||||
appSettings.Video.VideoContent = videoContent;
|
||||
_appSettings.Video.VideoContent = videoContent;
|
||||
return SetSettings();
|
||||
}
|
||||
|
||||
@@ -344,25 +344,25 @@ public partial class SettingsManager
|
||||
/// <returns></returns>
|
||||
public List<FileNamePart> GetFileNameParts()
|
||||
{
|
||||
appSettings = GetSettings();
|
||||
if (appSettings.Video.FileNameParts == null || appSettings.Video.FileNameParts.Count == 0)
|
||||
_appSettings = GetSettings();
|
||||
if (_appSettings.Video.FileNameParts == null || _appSettings.Video.FileNameParts.Count == 0)
|
||||
{
|
||||
// 第一次获取,先设置默认值
|
||||
SetFileNameParts(fileNameParts);
|
||||
return fileNameParts;
|
||||
SetFileNameParts(_fileNameParts);
|
||||
return _fileNameParts;
|
||||
}
|
||||
|
||||
return appSettings.Video.FileNameParts;
|
||||
return _appSettings.Video.FileNameParts;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置文件命名格式
|
||||
/// </summary>
|
||||
/// <param name="historyPaths"></param>
|
||||
/// <param name="fileNameParts"></param>
|
||||
/// <returns></returns>
|
||||
public bool SetFileNameParts(List<FileNamePart> fileNameParts)
|
||||
{
|
||||
appSettings.Video.FileNameParts = fileNameParts;
|
||||
_appSettings.Video.FileNameParts = fileNameParts;
|
||||
return SetSettings();
|
||||
}
|
||||
|
||||
@@ -372,16 +372,16 @@ public partial class SettingsManager
|
||||
/// <returns></returns>
|
||||
public string GetFileNamePartTimeFormat()
|
||||
{
|
||||
appSettings = GetSettings();
|
||||
if (appSettings.Video.FileNamePartTimeFormat == null ||
|
||||
appSettings.Video.FileNamePartTimeFormat == string.Empty)
|
||||
_appSettings = GetSettings();
|
||||
if (_appSettings.Video.FileNamePartTimeFormat == null ||
|
||||
_appSettings.Video.FileNamePartTimeFormat == string.Empty)
|
||||
{
|
||||
// 第一次获取,先设置默认值
|
||||
SetFileNamePartTimeFormat(fileNamePartTimeFormat);
|
||||
return fileNamePartTimeFormat;
|
||||
SetFileNamePartTimeFormat(FileNamePartTimeFormat);
|
||||
return FileNamePartTimeFormat;
|
||||
}
|
||||
|
||||
return appSettings.Video.FileNamePartTimeFormat;
|
||||
return _appSettings.Video.FileNamePartTimeFormat;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -391,7 +391,7 @@ public partial class SettingsManager
|
||||
/// <returns></returns>
|
||||
public bool SetFileNamePartTimeFormat(string fileNamePartTimeFormat)
|
||||
{
|
||||
appSettings.Video.FileNamePartTimeFormat = fileNamePartTimeFormat;
|
||||
_appSettings.Video.FileNamePartTimeFormat = fileNamePartTimeFormat;
|
||||
return SetSettings();
|
||||
}
|
||||
|
||||
@@ -401,15 +401,15 @@ public partial class SettingsManager
|
||||
/// <returns></returns>
|
||||
public OrderFormat GetOrderFormat()
|
||||
{
|
||||
appSettings = GetSettings();
|
||||
if (appSettings.Video.OrderFormat == OrderFormat.NOT_SET)
|
||||
_appSettings = GetSettings();
|
||||
if (_appSettings.Video.OrderFormat == OrderFormat.NotSet)
|
||||
{
|
||||
// 第一次获取,先设置默认值
|
||||
SetOrderFormat(orderFormat);
|
||||
return orderFormat;
|
||||
SetOrderFormat(OrderFormat);
|
||||
return OrderFormat;
|
||||
}
|
||||
|
||||
return appSettings.Video.OrderFormat;
|
||||
return _appSettings.Video.OrderFormat;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -419,7 +419,7 @@ public partial class SettingsManager
|
||||
/// <returns></returns>
|
||||
public bool SetOrderFormat(OrderFormat orderFormat)
|
||||
{
|
||||
appSettings.Video.OrderFormat = orderFormat;
|
||||
_appSettings.Video.OrderFormat = orderFormat;
|
||||
return SetSettings();
|
||||
}
|
||||
}
|
||||
@@ -14,17 +14,17 @@ namespace DownKyi.Core.Settings;
|
||||
|
||||
public partial class SettingsManager
|
||||
{
|
||||
private static SettingsManager instance;
|
||||
private static SettingsManager? _instance;
|
||||
|
||||
// 内存中保存一份配置
|
||||
private AppSettings appSettings;
|
||||
private AppSettings _appSettings;
|
||||
|
||||
#if DEBUG
|
||||
// 设置的配置文件
|
||||
private readonly string settingsName = StorageManager.GetSettings() + "_debug.json";
|
||||
private readonly string _settingsName = StorageManager.GetSettings() + "_debug.json";
|
||||
#else
|
||||
// 设置的配置文件
|
||||
private readonly string settingsName = Storage.StorageManager.GetSettings();
|
||||
private readonly string _settingsName = Storage.StorageManager.GetSettings();
|
||||
|
||||
// 密钥
|
||||
private readonly string password = "YO1J$4#p";
|
||||
@@ -36,12 +36,7 @@ public partial class SettingsManager
|
||||
/// <returns></returns>
|
||||
public static SettingsManager GetInstance()
|
||||
{
|
||||
if (instance == null)
|
||||
{
|
||||
instance = new SettingsManager();
|
||||
}
|
||||
|
||||
return instance;
|
||||
return _instance ??= new SettingsManager();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -49,7 +44,7 @@ public partial class SettingsManager
|
||||
/// </summary>
|
||||
private SettingsManager()
|
||||
{
|
||||
appSettings = GetSettings();
|
||||
_appSettings = GetSettings();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -58,9 +53,9 @@ public partial class SettingsManager
|
||||
/// <returns></returns>
|
||||
private AppSettings GetSettings()
|
||||
{
|
||||
if (appSettings != null)
|
||||
if (_appSettings != null)
|
||||
{
|
||||
return appSettings;
|
||||
return _appSettings;
|
||||
}
|
||||
|
||||
try
|
||||
@@ -70,9 +65,8 @@ public partial class SettingsManager
|
||||
//string jsonWordTemplate = streamReader.ReadToEnd();
|
||||
//streamReader.Close();
|
||||
//fileStream.Close();
|
||||
string jsonWordTemplate = string.Empty;
|
||||
using (var stream = new FileStream(settingsName, FileMode.Open, FileAccess.Read,
|
||||
FileShare.ReadWrite | FileShare.Delete))
|
||||
var jsonWordTemplate = string.Empty;
|
||||
using (var stream = new FileStream(_settingsName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite | FileShare.Delete))
|
||||
{
|
||||
using (var reader = new StreamReader(stream, Encoding.UTF8))
|
||||
{
|
||||
@@ -104,7 +98,7 @@ public partial class SettingsManager
|
||||
{
|
||||
try
|
||||
{
|
||||
string json = JsonConvert.SerializeObject(appSettings);
|
||||
var json = JsonConvert.SerializeObject(_appSettings);
|
||||
|
||||
#if DEBUG
|
||||
#else
|
||||
@@ -112,7 +106,7 @@ public partial class SettingsManager
|
||||
json = Encryptor.EncryptString(json, password);
|
||||
#endif
|
||||
|
||||
File.WriteAllText(settingsName, json);
|
||||
File.WriteAllText(_settingsName, json);
|
||||
return true;
|
||||
}
|
||||
catch (Exception e)
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
public enum VideoCodecs
|
||||
{
|
||||
NONE = 0,
|
||||
AVC,
|
||||
HEVC
|
||||
None = 0,
|
||||
Avc,
|
||||
Hevc
|
||||
}
|
||||
@@ -43,7 +43,6 @@ internal static class Constant
|
||||
|
||||
// 历史(搜索、下载) (加密)
|
||||
public static string Download { get; } = $"{Database}/Download.db";
|
||||
public static string History { get; } = $"{Database}/History.db";
|
||||
|
||||
// 配置
|
||||
public static string Config { get; } = $"{Root}/Config";
|
||||
@@ -60,33 +59,9 @@ internal static class Constant
|
||||
// 弹幕
|
||||
public static string Danmaku { get; } = $"{Bilibili}/Danmakus";
|
||||
|
||||
// 字幕
|
||||
public static string Subtitle { get; } = $"{Bilibili}/Subtitle";
|
||||
|
||||
// 评论
|
||||
// TODO
|
||||
|
||||
// 头图
|
||||
public static string Toutu { get; } = $"{Bilibili}/Toutu";
|
||||
|
||||
// 封面
|
||||
public static string Cover { get; } = $"{Bilibili}/Cover";
|
||||
|
||||
// 封面文件索引
|
||||
public static string CoverIndex { get; } = $"{Cover}/Index.db";
|
||||
|
||||
// 视频快照
|
||||
public static string Snapshot { get; } = $"{Bilibili}/Snapshot";
|
||||
|
||||
// 视频快照文件索引
|
||||
public static string SnapshotIndex { get; } = $"{Cover}/Index.db";
|
||||
|
||||
// 用户头像
|
||||
public static string Header { get; } = $"{Bilibili}/Header";
|
||||
|
||||
// 用户头像文件索引
|
||||
public static string HeaderIndex { get; } = $"{Header}/Index.db";
|
||||
|
||||
// 下载
|
||||
public static string Media { get; } = $"{Root}/Media";
|
||||
|
||||
// 缓存
|
||||
public static string Cache { get; } = $"{Root}/Cache";
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
namespace DownKyi.Core.Storage.Database;
|
||||
|
||||
public class Cover
|
||||
{
|
||||
public long Avid { get; set; }
|
||||
public string Bvid { get; set; }
|
||||
public long Cid { get; set; }
|
||||
public string Url { get; set; }
|
||||
public string Md5 { get; set; }
|
||||
}
|
||||
@@ -1,147 +0,0 @@
|
||||
using DownKyi.Core.Logging;
|
||||
using Console = DownKyi.Core.Utils.Debugging.Console;
|
||||
|
||||
namespace DownKyi.Core.Storage.Database;
|
||||
|
||||
public class CoverDb
|
||||
{
|
||||
private const string key = "b5018ecc-09d1-4da2-aa49-4625e41e623e";
|
||||
private readonly string tableName = "cover";
|
||||
|
||||
#if DEBUG
|
||||
private readonly DbHelper dbHelper = new DbHelper(StorageManager.GetCoverIndex().Replace(".db", "_debug.db"));
|
||||
#else
|
||||
private readonly DbHelper dbHelper = new DbHelper(StorageManager.GetCoverIndex(), key);
|
||||
#endif
|
||||
|
||||
public CoverDb()
|
||||
{
|
||||
CreateTable();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 关闭数据库连接
|
||||
/// </summary>
|
||||
public void Close()
|
||||
{
|
||||
dbHelper.Close();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 插入新的数据
|
||||
/// </summary>
|
||||
/// <param name="cover"></param>
|
||||
public void Insert(Cover cover)
|
||||
{
|
||||
try
|
||||
{
|
||||
string sql =
|
||||
$"insert into {tableName} values ({cover.Avid}, '{cover.Bvid}', {cover.Cid}, '{cover.Url}', '{cover.Md5}')";
|
||||
dbHelper.ExecuteNonQuery(sql);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Console.PrintLine("Insert()发生异常: {0}", e);
|
||||
LogManager.Error("CoverDb", e);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 更新数据,以url检索
|
||||
/// </summary>
|
||||
/// <param name="cover"></param>
|
||||
public void Update(Cover cover)
|
||||
{
|
||||
try
|
||||
{
|
||||
string sql =
|
||||
$"update {tableName} set avid={cover.Avid}, bvid='{cover.Bvid}', cid={cover.Cid}, md5='{cover.Md5}' where url glob '{cover.Url}'";
|
||||
dbHelper.ExecuteNonQuery(sql);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Console.PrintLine("Update()发生异常: {0}", e);
|
||||
LogManager.Error("CoverDb", e);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查询所有数据
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public List<Cover> QueryAll()
|
||||
{
|
||||
string sql = $"select * from {tableName}";
|
||||
return Query(sql);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查询url对应的数据
|
||||
/// </summary>
|
||||
/// <param name="url"></param>
|
||||
/// <returns></returns>
|
||||
public Cover QueryByUrl(string url)
|
||||
{
|
||||
string sql = $"select * from {tableName} where url glob '{url}'";
|
||||
List<Cover> query = Query(sql);
|
||||
return query.Count > 0 ? query[0] : null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查询md5对应的数据
|
||||
/// </summary>
|
||||
/// <param name="md5"></param>
|
||||
/// <returns></returns>
|
||||
public Cover QueryByMd5(string md5)
|
||||
{
|
||||
string sql = $"select * from {tableName} where md5 glob '{md5}'";
|
||||
List<Cover> query = Query(sql);
|
||||
return query.Count > 0 ? query[0] : null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查询数据
|
||||
/// </summary>
|
||||
/// <param name="sql"></param>
|
||||
/// <returns></returns>
|
||||
private List<Cover> Query(string sql)
|
||||
{
|
||||
List<Cover> covers = new List<Cover>();
|
||||
|
||||
try
|
||||
{
|
||||
dbHelper.ExecuteQuery(sql, reader =>
|
||||
{
|
||||
while (reader.Read())
|
||||
{
|
||||
Cover cover = new Cover
|
||||
{
|
||||
Avid = (long)reader["avid"],
|
||||
Bvid = (string)reader["bvid"],
|
||||
Cid = (long)reader["cid"],
|
||||
Url = (string)reader["url"],
|
||||
Md5 = (string)reader["md5"]
|
||||
};
|
||||
covers.Add(cover);
|
||||
}
|
||||
});
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Console.PrintLine("Query()发生异常: {0}", e);
|
||||
LogManager.Error($"{tableName}", e);
|
||||
}
|
||||
|
||||
return covers;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 如果表不存在则创建表
|
||||
/// </summary>
|
||||
private void CreateTable()
|
||||
{
|
||||
string sql =
|
||||
$"create table if not exists {tableName} (avid unsigned big int, bvid varchar(20), cid unsigned big int, url varchar(255) unique, md5 varchar(32) unique)";
|
||||
dbHelper.ExecuteNonQuery(sql);
|
||||
}
|
||||
}
|
||||
@@ -7,10 +7,10 @@ namespace DownKyi.Core.Storage.Database;
|
||||
|
||||
public class DbHelper
|
||||
{
|
||||
private readonly string connStr;
|
||||
private readonly SqliteConnection conn;
|
||||
private readonly string _connStr;
|
||||
private readonly SqliteConnection? _conn;
|
||||
|
||||
private static readonly Dictionary<string, SqliteConnection> database = new();
|
||||
private static readonly Dictionary<string, SqliteConnection> Database = new();
|
||||
|
||||
/// <summary>
|
||||
/// 创建一个数据库
|
||||
@@ -18,23 +18,23 @@ public class DbHelper
|
||||
/// <param name="dbPath"></param>
|
||||
public DbHelper(string dbPath)
|
||||
{
|
||||
connStr = new SqliteConnectionStringBuilder
|
||||
_connStr = new SqliteConnectionStringBuilder
|
||||
{
|
||||
Mode = SqliteOpenMode.ReadWriteCreate,
|
||||
DataSource = dbPath
|
||||
}.ToString();
|
||||
if (database.ContainsKey(connStr))
|
||||
if (Database.TryGetValue(_connStr, out var value))
|
||||
{
|
||||
conn = database[connStr];
|
||||
_conn = value;
|
||||
|
||||
if (conn != null)
|
||||
if (_conn != null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
conn = new SqliteConnection(connStr);
|
||||
database.Add(connStr, conn);
|
||||
_conn = new SqliteConnection(_connStr);
|
||||
Database.Add(_connStr, _conn);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -44,25 +44,25 @@ public class DbHelper
|
||||
/// <param name="secretKey"></param>
|
||||
public DbHelper(string dbPath, string secretKey)
|
||||
{
|
||||
connStr = new SqliteConnectionStringBuilder
|
||||
_connStr = new SqliteConnectionStringBuilder
|
||||
{
|
||||
Mode = SqliteOpenMode.ReadWriteCreate,
|
||||
Password = secretKey,
|
||||
DataSource = dbPath
|
||||
}.ToString();
|
||||
if (database.ContainsKey(connStr))
|
||||
if (Database.TryGetValue(_connStr, out var value))
|
||||
{
|
||||
conn = database[connStr];
|
||||
_conn = value;
|
||||
|
||||
if (conn != null)
|
||||
if (_conn != null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
conn = new SqliteConnection(connStr);
|
||||
_conn = new SqliteConnection(_connStr);
|
||||
// conn.SetPassword(secretKey);
|
||||
database.Add(connStr, conn);
|
||||
Database.Add(_connStr, _conn);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -71,7 +71,7 @@ public class DbHelper
|
||||
/// <returns></returns>
|
||||
public bool IsOpen()
|
||||
{
|
||||
return conn.State == ConnectionState.Open;
|
||||
return _conn?.State == ConnectionState.Open;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -79,14 +79,14 @@ public class DbHelper
|
||||
/// </summary>
|
||||
public void Open()
|
||||
{
|
||||
if (conn == null)
|
||||
if (_conn == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!IsOpen())
|
||||
{
|
||||
conn.Open();
|
||||
_conn.Open();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -95,47 +95,44 @@ public class DbHelper
|
||||
/// </summary>
|
||||
public void Close()
|
||||
{
|
||||
if (conn == null)
|
||||
if (_conn == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (IsOpen())
|
||||
{
|
||||
conn.Close();
|
||||
if (!IsOpen()) return;
|
||||
_conn.Close();
|
||||
|
||||
database.Remove(connStr);
|
||||
}
|
||||
Database.Remove(_connStr);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 执行一条SQL语句
|
||||
/// </summary>
|
||||
/// <param name="sql"></param>
|
||||
public void ExecuteNonQuery(string sql, Action<SqliteParameterCollection> action = null)
|
||||
/// <param name="action"></param>
|
||||
public void ExecuteNonQuery(string sql, Action<SqliteParameterCollection>? action = null)
|
||||
{
|
||||
if (conn == null)
|
||||
if (_conn == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
lock (conn)
|
||||
lock (_conn)
|
||||
{
|
||||
Open();
|
||||
using (var tr = conn.BeginTransaction())
|
||||
using var tr = _conn.BeginTransaction();
|
||||
using (var command = _conn.CreateCommand())
|
||||
{
|
||||
using (var command = conn.CreateCommand())
|
||||
{
|
||||
command.CommandText = sql;
|
||||
// 添加参数
|
||||
action?.Invoke(command.Parameters);
|
||||
command.ExecuteNonQuery();
|
||||
}
|
||||
|
||||
tr.Commit();
|
||||
command.CommandText = sql;
|
||||
// 添加参数
|
||||
action?.Invoke(command.Parameters);
|
||||
command.ExecuteNonQuery();
|
||||
}
|
||||
|
||||
tr.Commit();
|
||||
}
|
||||
}
|
||||
catch (SqliteException e)
|
||||
@@ -152,22 +149,20 @@ public class DbHelper
|
||||
/// <param name="action"></param>
|
||||
public void ExecuteQuery(string sql, Action<SqliteDataReader> action)
|
||||
{
|
||||
if (conn == null)
|
||||
if (_conn == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
lock (conn)
|
||||
lock (_conn)
|
||||
{
|
||||
Open();
|
||||
using (var command = conn.CreateCommand())
|
||||
{
|
||||
command.CommandText = sql;
|
||||
var reader = command.ExecuteReader();
|
||||
action(reader);
|
||||
}
|
||||
using var command = _conn.CreateCommand();
|
||||
command.CommandText = sql;
|
||||
var reader = command.ExecuteReader();
|
||||
action(reader);
|
||||
}
|
||||
}
|
||||
catch (SqliteException e)
|
||||
|
||||
@@ -132,14 +132,14 @@ public class DownloadDb
|
||||
/// </summary>
|
||||
/// <param name="uuid"></param>
|
||||
/// <returns></returns>
|
||||
public object QueryById(string uuid)
|
||||
public object? QueryById(string uuid)
|
||||
{
|
||||
string sql = $"select * from {tableName} where id glob '{uuid}'";
|
||||
Dictionary<string, object> query = Query(sql);
|
||||
var sql = $"select * from {tableName} where id glob '{uuid}'";
|
||||
var query = Query(sql);
|
||||
|
||||
if (query.ContainsKey(uuid))
|
||||
{
|
||||
query.TryGetValue(uuid, out object obj);
|
||||
query.TryGetValue(uuid, out var obj);
|
||||
return obj;
|
||||
}
|
||||
else
|
||||
@@ -155,7 +155,7 @@ public class DownloadDb
|
||||
/// <returns></returns>
|
||||
private Dictionary<string, object> Query(string sql)
|
||||
{
|
||||
Dictionary<string, object> objects = new Dictionary<string, object>();
|
||||
var objects = new Dictionary<string, object>();
|
||||
|
||||
dbHelper.ExecuteQuery(sql, reader =>
|
||||
{
|
||||
@@ -164,13 +164,13 @@ public class DownloadDb
|
||||
try
|
||||
{
|
||||
// 读取字节数组
|
||||
byte[] array = (byte[])reader["data"];
|
||||
var array = (byte[])reader["data"];
|
||||
// 定义一个流
|
||||
MemoryStream stream = new MemoryStream(array);
|
||||
var stream = new MemoryStream(array);
|
||||
//定义一个格式化器
|
||||
BinaryFormatter formatter = new BinaryFormatter();
|
||||
var formatter = new BinaryFormatter();
|
||||
// 反序列化
|
||||
object obj = formatter.Deserialize(stream);
|
||||
var obj = formatter.Deserialize(stream);
|
||||
|
||||
objects.Add((string)reader["id"], obj);
|
||||
}
|
||||
@@ -190,7 +190,7 @@ public class DownloadDb
|
||||
/// </summary>
|
||||
protected void CreateTable()
|
||||
{
|
||||
string sql = $"create table if not exists {tableName} (id varchar(255) unique, data blob)";
|
||||
var sql = $"create table if not exists {tableName} (id varchar(255) unique, data blob)";
|
||||
dbHelper.ExecuteNonQuery(sql);
|
||||
}
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
namespace DownKyi.Core.Storage.Database;
|
||||
|
||||
public class Header
|
||||
{
|
||||
public long Mid { get; set; }
|
||||
public string Name { get; set; }
|
||||
public string Url { get; set; }
|
||||
public string Md5 { get; set; }
|
||||
}
|
||||
@@ -1,134 +0,0 @@
|
||||
using DownKyi.Core.Logging;
|
||||
using Console = DownKyi.Core.Utils.Debugging.Console;
|
||||
|
||||
namespace DownKyi.Core.Storage.Database;
|
||||
|
||||
public class HeaderDb
|
||||
{
|
||||
private const string key = "7c1f1f40-7cdf-4d11-ad28-f0137a3c5308";
|
||||
private readonly string tableName = "header";
|
||||
|
||||
#if DEBUG
|
||||
private readonly DbHelper dbHelper = new DbHelper(StorageManager.GetHeaderIndex().Replace(".db", "_debug.db"));
|
||||
#else
|
||||
private readonly DbHelper dbHelper = new DbHelper(StorageManager.GetHeaderIndex(), key);
|
||||
#endif
|
||||
|
||||
public HeaderDb()
|
||||
{
|
||||
CreateTable();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 关闭数据库连接
|
||||
/// </summary>
|
||||
public void Close()
|
||||
{
|
||||
dbHelper.Close();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 插入新的数据
|
||||
/// </summary>
|
||||
/// <param name="header"></param>
|
||||
public void Insert(Header header)
|
||||
{
|
||||
try
|
||||
{
|
||||
string sql =
|
||||
$"insert into {tableName} values ({header.Mid}, '{header.Name}', '{header.Url}', '{header.Md5}')";
|
||||
dbHelper.ExecuteNonQuery(sql);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Console.PrintLine("Insert()发生异常: {0}", e);
|
||||
LogManager.Error("HeaderDb", e);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 更新数据
|
||||
/// </summary>
|
||||
/// <param name="header"></param>
|
||||
public void Update(Header header)
|
||||
{
|
||||
try
|
||||
{
|
||||
string sql =
|
||||
$"update {tableName} set name='{header.Name}', url='{header.Url}', md5='{header.Md5}' where mid={header.Mid}";
|
||||
dbHelper.ExecuteNonQuery(sql);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Console.PrintLine("Update()发生异常: {0}", e);
|
||||
LogManager.Error("HeaderDb", e);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查询所有数据
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public List<Header> QueryAll()
|
||||
{
|
||||
string sql = $"select * from {tableName}";
|
||||
return Query(sql);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查询mid对应的数据
|
||||
/// </summary>
|
||||
/// <param name="mid"></param>
|
||||
/// <returns></returns>
|
||||
public Header QueryByMid(long mid)
|
||||
{
|
||||
string sql = $"select * from {tableName} where mid={mid}";
|
||||
List<Header> query = Query(sql);
|
||||
return query.Count > 0 ? query[0] : null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查询数据
|
||||
/// </summary>
|
||||
/// <param name="sql"></param>
|
||||
/// <returns></returns>
|
||||
private List<Header> Query(string sql)
|
||||
{
|
||||
List<Header> headers = new List<Header>();
|
||||
|
||||
try
|
||||
{
|
||||
dbHelper.ExecuteQuery(sql, reader =>
|
||||
{
|
||||
while (reader.Read())
|
||||
{
|
||||
Header header = new Header
|
||||
{
|
||||
Mid = (long)reader["mid"],
|
||||
Name = (string)reader["name"],
|
||||
Url = (string)reader["url"],
|
||||
Md5 = (string)reader["md5"]
|
||||
};
|
||||
headers.Add(header);
|
||||
}
|
||||
});
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Console.PrintLine("Query()发生异常: {0}", e);
|
||||
LogManager.Error($"{tableName}", e);
|
||||
}
|
||||
|
||||
return headers;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 如果表不存在则创建表
|
||||
/// </summary>
|
||||
private void CreateTable()
|
||||
{
|
||||
string sql =
|
||||
$"create table if not exists {tableName} (mid unsigned big int unique, name varchar(255), url varchar(255), md5 varchar(32))";
|
||||
dbHelper.ExecuteNonQuery(sql);
|
||||
}
|
||||
}
|
||||
@@ -1,281 +0,0 @@
|
||||
using Avalonia;
|
||||
using Avalonia.Media.Imaging;
|
||||
using DownKyi.Core.Logging;
|
||||
using DownKyi.Core.Storage.Database;
|
||||
using DownKyi.Core.Utils.Encryptor;
|
||||
using Console = DownKyi.Core.Utils.Debugging.Console;
|
||||
|
||||
namespace DownKyi.Core.Storage;
|
||||
|
||||
public class StorageCover
|
||||
{
|
||||
// 先判断本地有没有
|
||||
// 如果有
|
||||
// 则返回本地的图片路径
|
||||
// 如果没有
|
||||
// 则下载图片并返回本地的图片路径
|
||||
|
||||
/// <summary>
|
||||
/// 获取封面缩略图
|
||||
/// </summary>
|
||||
/// <param name="avid"></param>
|
||||
/// <param name="bvid"></param>
|
||||
/// <param name="cid"></param>
|
||||
/// <param name="url"></param>
|
||||
/// <param name="width"></param>
|
||||
/// <param name="height"></param>
|
||||
/// <returns></returns>
|
||||
public Bitmap GetCoverThumbnail(long avid, string bvid, long cid, string url, int width, int height)
|
||||
{
|
||||
var header = GetCover(avid, bvid, cid, url);
|
||||
|
||||
return GetCoverThumbnail(header, width, height);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取封面缩略图
|
||||
/// </summary>
|
||||
/// <param name="cover"></param>
|
||||
/// <param name="width"></param>
|
||||
/// <param name="height"></param>
|
||||
/// <returns></returns>
|
||||
public Bitmap GetCoverThumbnail(string cover, int width, int height)
|
||||
{
|
||||
if (cover == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var bitmap = new Bitmap(cover);
|
||||
return bitmap.CreateScaledBitmap(new PixelSize(width, height), BitmapInterpolationMode.Unspecified);
|
||||
|
||||
// return StorageUtils.BitmapToBitmapImage(new Bitmap(thumbnail));
|
||||
}
|
||||
catch (ArgumentException)
|
||||
{
|
||||
try
|
||||
{
|
||||
// SimpleDecoder simpleDecoder = new SimpleDecoder(cover);
|
||||
// Bitmap bitmap = simpleDecoder.WebPtoBitmap();
|
||||
// Image thumbnail = bitmap.GetThumbnailImage(width, height, null, IntPtr.Zero);
|
||||
//
|
||||
// return StorageUtils.BitmapToBitmapImage(new Bitmap(thumbnail));
|
||||
return null;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.PrintLine("GetCoverThumbnail()发生异常: {0}", ex);
|
||||
LogManager.Error("StorageCover.GetCoverThumbnail()", ex);
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Console.PrintLine("GetCoverThumbnail()发生异常: {0}", e);
|
||||
LogManager.Error("StorageCover.GetCoverThumbnail()", e);
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取封面
|
||||
/// </summary>
|
||||
/// <param name="url"></param>
|
||||
/// <returns></returns>
|
||||
public string GetCover(string url)
|
||||
{
|
||||
return GetCover(0, "", 0, url);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取封面
|
||||
/// </summary>
|
||||
/// <param name="avid"></param>
|
||||
/// <param name="bvid"></param>
|
||||
/// <param name="cid"></param>
|
||||
/// <param name="url"></param>
|
||||
/// <returns></returns>
|
||||
public string GetCover(long avid, string bvid, long cid, string url)
|
||||
{
|
||||
var coverDb = new CoverDb();
|
||||
var cover = coverDb.QueryByUrl(url);
|
||||
|
||||
// 如果存在,直接返回
|
||||
// 如果不存在,则先下载
|
||||
if (cover != null)
|
||||
{
|
||||
var coverPath = $"{StorageManager.GetCover()}/{cover.Md5}";
|
||||
if (File.Exists(coverPath))
|
||||
{
|
||||
var newCover = new Cover
|
||||
{
|
||||
Avid = avid,
|
||||
Bvid = bvid,
|
||||
Cid = cid,
|
||||
Url = url,
|
||||
Md5 = cover.Md5
|
||||
};
|
||||
coverDb.Update(newCover);
|
||||
|
||||
//coverDb.Close();
|
||||
return $"{StorageManager.GetCover()}/{cover.Md5}";
|
||||
}
|
||||
else
|
||||
{
|
||||
var md5 = DownloadImage(url);
|
||||
if (md5 != null)
|
||||
{
|
||||
Cover newCover = new Cover
|
||||
{
|
||||
Avid = avid,
|
||||
Bvid = bvid,
|
||||
Cid = cid,
|
||||
Url = url,
|
||||
Md5 = md5
|
||||
};
|
||||
coverDb.Update(newCover);
|
||||
|
||||
//coverDb.Close();
|
||||
return $"{StorageManager.GetCover()}/{md5}";
|
||||
}
|
||||
else
|
||||
{
|
||||
//coverDb.Close();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var md5 = DownloadImage(url);
|
||||
if (md5 != null)
|
||||
{
|
||||
Cover newCover = new Cover
|
||||
{
|
||||
Avid = avid,
|
||||
Bvid = bvid,
|
||||
Cid = cid,
|
||||
Url = url,
|
||||
Md5 = md5
|
||||
};
|
||||
coverDb.Insert(newCover);
|
||||
|
||||
//coverDb.Close();
|
||||
return $"{StorageManager.GetCover()}/{md5}";
|
||||
}
|
||||
else
|
||||
{
|
||||
//coverDb.Close();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<Bitmap?> GetCoverAsync(string url,TimeSpan? timeOut = null)
|
||||
{
|
||||
timeOut = timeOut ?? TimeSpan.FromSeconds(1.5);
|
||||
using var stream = await GetImageBytesAsync(url, timeOut);
|
||||
return stream == null ? null : new Bitmap(stream);
|
||||
}
|
||||
|
||||
|
||||
public async Task<Stream?> GetImageBytesAsync(string imageUrl,TimeSpan? timeOut = null)
|
||||
{
|
||||
try
|
||||
{
|
||||
using (var httpClient = new HttpClient())
|
||||
{
|
||||
if (timeOut is not null)
|
||||
{
|
||||
httpClient.Timeout = timeOut.Value;
|
||||
}
|
||||
var response = await httpClient.GetAsync(imageUrl);
|
||||
response.EnsureSuccessStatusCode();
|
||||
|
||||
var stream = await response.Content.ReadAsStreamAsync();
|
||||
return stream;
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 下载图片
|
||||
/// </summary>
|
||||
/// <param name="url"></param>
|
||||
/// <returns></returns>
|
||||
private string DownloadImage(string url)
|
||||
{
|
||||
var localFile = Path.GetTempPath() + Guid.NewGuid().ToString("N");
|
||||
|
||||
// 下载
|
||||
var isSuccessed = StorageUtils.DownloadImage(url, localFile);
|
||||
if (isSuccessed)
|
||||
{
|
||||
try
|
||||
{
|
||||
string md5 = Hash.GetMD5HashFromFile(localFile);
|
||||
|
||||
if (File.Exists(localFile))
|
||||
{
|
||||
string destFile = $"{StorageManager.GetCover()}/{md5}";
|
||||
|
||||
try
|
||||
{
|
||||
File.Delete(destFile);
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
|
||||
// 移动到指定位置
|
||||
File.Move(localFile, destFile);
|
||||
|
||||
return md5;
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Console.PrintLine("DownloadImage()发生异常: {0}", e);
|
||||
LogManager.Error("StorageCover", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 本地是否存在
|
||||
/// </summary>
|
||||
/// <param name="url"></param>
|
||||
/// <returns></returns>
|
||||
public bool IsLocal(CoverDb coverDb, string url)
|
||||
{
|
||||
var cover = coverDb.QueryByUrl(url);
|
||||
return cover != null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 返回图片md5值
|
||||
/// </summary>
|
||||
/// <param name="coverDb"></param>
|
||||
/// <param name="url"></param>
|
||||
/// <returns></returns>
|
||||
public string LocalCover(CoverDb coverDb, string url)
|
||||
{
|
||||
var cover = coverDb.QueryByUrl(url);
|
||||
return cover.Md5;
|
||||
}
|
||||
}
|
||||
@@ -1,210 +0,0 @@
|
||||
using Avalonia;
|
||||
using Avalonia.Media.Imaging;
|
||||
using DownKyi.Core.Logging;
|
||||
using DownKyi.Core.Storage.Database;
|
||||
using DownKyi.Core.Utils.Encryptor;
|
||||
using Console = DownKyi.Core.Utils.Debugging.Console;
|
||||
|
||||
namespace DownKyi.Core.Storage;
|
||||
|
||||
public class StorageHeader
|
||||
{
|
||||
// 先判断本地有没有
|
||||
// 如果有
|
||||
// 则返回本地的图片路径
|
||||
// 如果没有
|
||||
// 则下载图片并返回本地的图片路径
|
||||
|
||||
/// <summary>
|
||||
/// 获取用户头像缩略图
|
||||
/// </summary>
|
||||
/// <param name="mid"></param>
|
||||
/// <param name="name"></param>
|
||||
/// <param name="url"></param>
|
||||
/// <param name="width"></param>
|
||||
/// <param name="height"></param>
|
||||
/// <returns></returns>
|
||||
public Bitmap GetHeaderThumbnail(long mid, string name, string url, int width, int height)
|
||||
{
|
||||
string header = GetHeader(mid, name, url);
|
||||
if (header == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return GetHeaderThumbnail(header, width, height);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取用户头像缩略图
|
||||
/// </summary>
|
||||
/// <param name="header"></param>
|
||||
/// <param name="width"></param>
|
||||
/// <param name="height"></param>
|
||||
/// <returns></returns>
|
||||
public Bitmap GetHeaderThumbnail(string header, int width, int height)
|
||||
{
|
||||
if (header == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
Bitmap bitmap = new Bitmap(header);
|
||||
var thumbnail = bitmap.CreateScaledBitmap(new PixelSize(width, height), BitmapInterpolationMode.Unspecified);
|
||||
|
||||
return thumbnail;
|
||||
}
|
||||
catch (ArgumentException)
|
||||
{
|
||||
try
|
||||
{
|
||||
/*SimpleDecoder simpleDecoder = new SimpleDecoder(header);
|
||||
Bitmap bitmap = simpleDecoder.WebPtoBitmap();
|
||||
Image thumbnail = bitmap.GetThumbnailImage(width, height, null, IntPtr.Zero);
|
||||
|
||||
return StorageUtils.BitmapToBitmapImage(new Bitmap(thumbnail));*/
|
||||
return null;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.PrintLine("GetHeaderThumbnail()发生异常: {0}", ex);
|
||||
LogManager.Error("StorageHeader.GetHeaderThumbnail()", ex);
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Console.PrintLine("GetHeaderThumbnail()发生异常: {0}", e);
|
||||
LogManager.Error("StorageHeader.GetHeaderThumbnail()", e);
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取用户头像
|
||||
/// </summary>
|
||||
/// <param name="mid"></param>
|
||||
/// <returns></returns>
|
||||
public string GetHeader(long mid, string name, string url)
|
||||
{
|
||||
HeaderDb headerDb = new HeaderDb();
|
||||
Header header = headerDb.QueryByMid(mid);
|
||||
|
||||
if (header != null)
|
||||
{
|
||||
string headerPath = $"{StorageManager.GetHeader()}/{header.Md5}";
|
||||
if (File.Exists(headerPath))
|
||||
{
|
||||
Header newHeader = new Header
|
||||
{
|
||||
Mid = mid,
|
||||
Name = name,
|
||||
Url = url,
|
||||
Md5 = header.Md5
|
||||
};
|
||||
headerDb.Update(newHeader);
|
||||
//headerDb.Close();
|
||||
return $"{StorageManager.GetHeader()}/{header.Md5}";
|
||||
}
|
||||
else
|
||||
{
|
||||
string md5 = DownloadImage(url);
|
||||
if (md5 != null)
|
||||
{
|
||||
Header newHeader = new Header
|
||||
{
|
||||
Mid = mid,
|
||||
Name = name,
|
||||
Url = url,
|
||||
Md5 = md5
|
||||
};
|
||||
headerDb.Insert(newHeader);
|
||||
//headerDb.Close();
|
||||
return $"{StorageManager.GetHeader()}/{md5}";
|
||||
}
|
||||
else
|
||||
{
|
||||
//headerDb.Close();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var md5 = DownloadImage(url);
|
||||
if (md5 != null)
|
||||
{
|
||||
Header newHeader = new Header
|
||||
{
|
||||
Mid = mid,
|
||||
Name = name,
|
||||
Url = url,
|
||||
Md5 = md5
|
||||
};
|
||||
headerDb.Insert(newHeader);
|
||||
//headerDb.Close();
|
||||
return $"{StorageManager.GetHeader()}/{md5}";
|
||||
}
|
||||
else
|
||||
{
|
||||
//headerDb.Close();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 下载图片
|
||||
/// </summary>
|
||||
/// <param name="url"></param>
|
||||
/// <returns></returns>
|
||||
private string DownloadImage(string url)
|
||||
{
|
||||
string localFile = Path.GetTempPath() + Guid.NewGuid().ToString("N");
|
||||
|
||||
// 下载
|
||||
bool isSuccessed = StorageUtils.DownloadImage(url, localFile);
|
||||
if (isSuccessed)
|
||||
{
|
||||
try
|
||||
{
|
||||
string md5 = Hash.GetMD5HashFromFile(localFile);
|
||||
|
||||
if (File.Exists(localFile))
|
||||
{
|
||||
string destFile = $"{StorageManager.GetHeader()}/{md5}";
|
||||
|
||||
try
|
||||
{
|
||||
File.Delete(destFile);
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
|
||||
// 移动到指定位置
|
||||
File.Move(localFile, destFile);
|
||||
|
||||
return md5;
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Console.PrintLine("DownloadImage()发生异常: {0}", e);
|
||||
LogManager.Error("StorageHeader", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -32,16 +32,6 @@ public static class StorageManager
|
||||
return Constant.Download;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取历史记录的文件路径
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static string GetHistory()
|
||||
{
|
||||
CreateDirectory(Constant.Database);
|
||||
return Constant.History;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取设置的文件路径
|
||||
/// </summary>
|
||||
@@ -71,87 +61,15 @@ public static class StorageManager
|
||||
return CreateDirectory(Constant.Danmaku);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取字幕的文件夹路径
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static string GetSubtitle()
|
||||
{
|
||||
return CreateDirectory(Constant.Subtitle);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取头图的文件夹路径
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static string GetToutu()
|
||||
{
|
||||
return CreateDirectory(Constant.Toutu);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取封面的文件夹路径
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static string GetCover()
|
||||
{
|
||||
return CreateDirectory(Constant.Cover);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取封面索引的文件路径
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static string GetCoverIndex()
|
||||
{
|
||||
CreateDirectory(Constant.Cover);
|
||||
return Constant.CoverIndex;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取视频快照的文件夹路径
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static string GetSnapshot()
|
||||
{
|
||||
return CreateDirectory(Constant.Snapshot);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取视频快照索引的文件路径
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static string GetSnapshotIndex()
|
||||
{
|
||||
CreateDirectory(Constant.Snapshot);
|
||||
return Constant.SnapshotIndex;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取用户头像的文件夹路径
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static string GetHeader()
|
||||
{
|
||||
return CreateDirectory(Constant.Header);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取用户头像索引的文件路径
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static string GetHeaderIndex()
|
||||
{
|
||||
CreateDirectory(Constant.Header);
|
||||
return Constant.HeaderIndex;
|
||||
}
|
||||
|
||||
public static string GetMedia()
|
||||
{
|
||||
CreateDirectory(Constant.Media);
|
||||
return Constant.Media;
|
||||
return CreateDirectory(Constant.Media);
|
||||
}
|
||||
|
||||
public static string GetCache()
|
||||
{
|
||||
return CreateDirectory(Constant.Cache);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 若文件夹不存在,则创建文件夹
|
||||
|
||||
@@ -4,7 +4,7 @@ using Console = DownKyi.Core.Utils.Debugging.Console;
|
||||
|
||||
namespace DownKyi.Core.Storage;
|
||||
|
||||
internal static class StorageUtils
|
||||
public static class StorageUtils
|
||||
{
|
||||
/// <summary>
|
||||
/// 下载图片
|
||||
|
||||
@@ -1,13 +1,11 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Security.Cryptography;
|
||||
using System.Security.Cryptography;
|
||||
|
||||
namespace DownKyi.Core.Utils.Encryptor;
|
||||
|
||||
public static partial class Encryptor
|
||||
{
|
||||
private const ulong FC_TAG = 0xFC010203040506CF;
|
||||
private const int BUFFER_SIZE = 128 * 1024;
|
||||
private const ulong FcTag = 0xFC010203040506CF;
|
||||
private const int BufferSize = 128 * 1024;
|
||||
|
||||
/// <summary>
|
||||
/// 加密文件
|
||||
@@ -17,59 +15,54 @@ public static partial class Encryptor
|
||||
/// <param name="password">加密密码</param>
|
||||
public static void EncryptFile(string inFile, string outFile, string password)
|
||||
{
|
||||
using (FileStream fin = File.OpenRead(inFile), fout = File.OpenWrite(outFile))
|
||||
using FileStream fin = File.OpenRead(inFile), fout = File.OpenWrite(outFile);
|
||||
var lSize = fin.Length; // 输入文件长度
|
||||
var size = (int)lSize;
|
||||
var bytes = new byte[BufferSize]; // 缓存
|
||||
var read = -1; // 输入文件读取数量
|
||||
var value = 0;
|
||||
|
||||
// 获取IV和salt
|
||||
var iv = GenerateRandomBytes(16);
|
||||
var salt = GenerateRandomBytes(16);
|
||||
|
||||
// 创建加密对象
|
||||
var sma = CreateRijndael(password, salt);
|
||||
sma.IV = iv;
|
||||
|
||||
// 在输出文件开始部分写入IV和salt
|
||||
fout.Write(iv, 0, iv.Length);
|
||||
fout.Write(salt, 0, salt.Length);
|
||||
|
||||
// 创建散列加密
|
||||
HashAlgorithm hasher = SHA256.Create();
|
||||
using CryptoStream cout = new(fout, sma.CreateEncryptor(), CryptoStreamMode.Write), chash = new(Stream.Null, hasher, CryptoStreamMode.Write);
|
||||
var bw = new BinaryWriter(cout);
|
||||
bw.Write(lSize);
|
||||
|
||||
bw.Write(FcTag);
|
||||
|
||||
// 读写字节块到加密流缓冲区
|
||||
while ((read = fin.Read(bytes, 0, bytes.Length)) != 0)
|
||||
{
|
||||
long lSize = fin.Length; // 输入文件长度
|
||||
int size = (int)lSize;
|
||||
byte[] bytes = new byte[BUFFER_SIZE]; // 缓存
|
||||
int read = -1; // 输入文件读取数量
|
||||
int value = 0;
|
||||
|
||||
// 获取IV和salt
|
||||
byte[] IV = GenerateRandomBytes(16);
|
||||
byte[] salt = GenerateRandomBytes(16);
|
||||
|
||||
// 创建加密对象
|
||||
SymmetricAlgorithm sma = CreateRijndael(password, salt);
|
||||
sma.IV = IV;
|
||||
|
||||
// 在输出文件开始部分写入IV和salt
|
||||
fout.Write(IV, 0, IV.Length);
|
||||
fout.Write(salt, 0, salt.Length);
|
||||
|
||||
// 创建散列加密
|
||||
HashAlgorithm hasher = SHA256.Create();
|
||||
using (CryptoStream cout = new(fout, sma.CreateEncryptor(), CryptoStreamMode.Write),
|
||||
chash = new(Stream.Null, hasher, CryptoStreamMode.Write))
|
||||
{
|
||||
BinaryWriter bw = new BinaryWriter(cout);
|
||||
bw.Write(lSize);
|
||||
|
||||
bw.Write(FC_TAG);
|
||||
|
||||
// 读写字节块到加密流缓冲区
|
||||
while ((read = fin.Read(bytes, 0, bytes.Length)) != 0)
|
||||
{
|
||||
cout.Write(bytes, 0, read);
|
||||
chash.Write(bytes, 0, read);
|
||||
value += read;
|
||||
}
|
||||
|
||||
// 关闭加密流
|
||||
chash.Flush();
|
||||
chash.Close();
|
||||
|
||||
// 读取散列
|
||||
byte[] hash = hasher.Hash;
|
||||
|
||||
// 输入文件写入散列
|
||||
cout.Write(hash, 0, hash.Length);
|
||||
|
||||
// 关闭文件流
|
||||
cout.Flush();
|
||||
cout.Close();
|
||||
}
|
||||
cout.Write(bytes, 0, read);
|
||||
chash.Write(bytes, 0, read);
|
||||
value += read;
|
||||
}
|
||||
|
||||
// 关闭加密流
|
||||
chash.Flush();
|
||||
chash.Close();
|
||||
|
||||
// 读取散列
|
||||
var hash = hasher.Hash;
|
||||
|
||||
// 输入文件写入散列
|
||||
cout.Write(hash, 0, hash.Length);
|
||||
|
||||
// 关闭文件流
|
||||
cout.Flush();
|
||||
cout.Close();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -81,86 +74,81 @@ public static partial class Encryptor
|
||||
public static void DecryptFile(string inFile, string outFile, string password)
|
||||
{
|
||||
// 创建打开文件流
|
||||
using (FileStream fin = File.OpenRead(inFile), fout = File.OpenWrite(outFile))
|
||||
using FileStream fin = File.OpenRead(inFile), fout = File.OpenWrite(outFile);
|
||||
var size = (int)fin.Length;
|
||||
var bytes = new byte[BufferSize];
|
||||
var read = -1;
|
||||
var value = 0;
|
||||
var outValue = 0;
|
||||
|
||||
var iv = new byte[16];
|
||||
fin.Read(iv, 0, 16);
|
||||
var salt = new byte[16];
|
||||
fin.Read(salt, 0, 16);
|
||||
|
||||
var sma = CreateRijndael(password, salt);
|
||||
sma.IV = iv;
|
||||
|
||||
value = 32;
|
||||
long lSize = -1;
|
||||
|
||||
// 创建散列对象, 校验文件
|
||||
HashAlgorithm hasher = SHA256.Create();
|
||||
|
||||
try
|
||||
{
|
||||
int size = (int)fin.Length;
|
||||
byte[] bytes = new byte[BUFFER_SIZE];
|
||||
int read = -1;
|
||||
int value = 0;
|
||||
int outValue = 0;
|
||||
using CryptoStream cin = new(fin, sma.CreateDecryptor(), CryptoStreamMode.Read),
|
||||
chash = new(Stream.Null, hasher, CryptoStreamMode.Write);
|
||||
// 读取文件长度
|
||||
var br = new BinaryReader(cin);
|
||||
lSize = br.ReadInt64();
|
||||
var tag = br.ReadUInt64();
|
||||
|
||||
byte[] IV = new byte[16];
|
||||
fin.Read(IV, 0, 16);
|
||||
byte[] salt = new byte[16];
|
||||
fin.Read(salt, 0, 16);
|
||||
if (FcTag != tag) throw new CryptoHelpException("文件被破坏");
|
||||
|
||||
SymmetricAlgorithm sma = CreateRijndael(password, salt);
|
||||
sma.IV = IV;
|
||||
var numReads = lSize / BufferSize;
|
||||
|
||||
value = 32;
|
||||
long lSize = -1;
|
||||
var slack = lSize % BufferSize;
|
||||
|
||||
// 创建散列对象, 校验文件
|
||||
HashAlgorithm hasher = SHA256.Create();
|
||||
|
||||
try
|
||||
for (var i = 0; i < numReads; ++i)
|
||||
{
|
||||
using (CryptoStream cin = new(fin, sma.CreateDecryptor(), CryptoStreamMode.Read),
|
||||
chash = new(Stream.Null, hasher, CryptoStreamMode.Write))
|
||||
{
|
||||
// 读取文件长度
|
||||
BinaryReader br = new BinaryReader(cin);
|
||||
lSize = br.ReadInt64();
|
||||
ulong tag = br.ReadUInt64();
|
||||
|
||||
if (FC_TAG != tag)
|
||||
throw new CryptoHelpException("文件被破坏");
|
||||
|
||||
long numReads = lSize / BUFFER_SIZE;
|
||||
|
||||
long slack = lSize % BUFFER_SIZE;
|
||||
|
||||
for (int i = 0; i < numReads; ++i)
|
||||
{
|
||||
read = cin.Read(bytes, 0, bytes.Length);
|
||||
fout.Write(bytes, 0, read);
|
||||
chash.Write(bytes, 0, read);
|
||||
value += read;
|
||||
outValue += read;
|
||||
}
|
||||
|
||||
if (slack > 0)
|
||||
{
|
||||
read = cin.Read(bytes, 0, (int)slack);
|
||||
fout.Write(bytes, 0, read);
|
||||
chash.Write(bytes, 0, read);
|
||||
value += read;
|
||||
outValue += read;
|
||||
}
|
||||
|
||||
chash.Flush();
|
||||
chash.Close();
|
||||
|
||||
fout.Flush();
|
||||
fout.Close();
|
||||
|
||||
byte[] curHash = hasher.Hash;
|
||||
|
||||
// 获取比较和旧的散列对象
|
||||
byte[] oldHash = new byte[hasher.HashSize / 8];
|
||||
read = cin.Read(oldHash, 0, oldHash.Length);
|
||||
if ((oldHash.Length != read) || (!CheckByteArrays(oldHash, curHash)))
|
||||
throw new CryptoHelpException("文件被破坏");
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Console.WriteLine("DecryptFile()发生异常: {0}", e);
|
||||
read = cin.Read(bytes, 0, bytes.Length);
|
||||
fout.Write(bytes, 0, read);
|
||||
chash.Write(bytes, 0, read);
|
||||
value += read;
|
||||
outValue += read;
|
||||
}
|
||||
|
||||
if (outValue != lSize)
|
||||
throw new CryptoHelpException("文件大小不匹配");
|
||||
if (slack > 0)
|
||||
{
|
||||
read = cin.Read(bytes, 0, (int)slack);
|
||||
fout.Write(bytes, 0, read);
|
||||
chash.Write(bytes, 0, read);
|
||||
value += read;
|
||||
outValue += read;
|
||||
}
|
||||
|
||||
chash.Flush();
|
||||
chash.Close();
|
||||
|
||||
fout.Flush();
|
||||
fout.Close();
|
||||
|
||||
var curHash = hasher.Hash;
|
||||
|
||||
// 获取比较和旧的散列对象
|
||||
var oldHash = new byte[hasher.HashSize / 8];
|
||||
read = cin.Read(oldHash, 0, oldHash.Length);
|
||||
if (oldHash.Length != read || !CheckByteArrays(oldHash, curHash))
|
||||
throw new CryptoHelpException("文件被破坏");
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Console.WriteLine("DecryptFile()发生异常: {0}", e);
|
||||
}
|
||||
|
||||
if (outValue != lSize)
|
||||
throw new CryptoHelpException("文件大小不匹配");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -173,7 +161,7 @@ public static partial class Encryptor
|
||||
{
|
||||
if (b1.Length == b2.Length)
|
||||
{
|
||||
for (int i = 0; i < b1.Length; ++i)
|
||||
for (var i = 0; i < b1.Length; ++i)
|
||||
{
|
||||
if (b1[i] != b2[i])
|
||||
return false;
|
||||
@@ -193,9 +181,9 @@ public static partial class Encryptor
|
||||
/// <returns>加密对象</returns>
|
||||
private static SymmetricAlgorithm CreateRijndael(string password, byte[] salt)
|
||||
{
|
||||
PasswordDeriveBytes pdb = new PasswordDeriveBytes(password, salt, "SHA256", 1000);
|
||||
var pdb = new PasswordDeriveBytes(password, salt, "SHA256", 1000);
|
||||
|
||||
SymmetricAlgorithm sma = Rijndael.Create();
|
||||
SymmetricAlgorithm sma = Aes.Create();
|
||||
sma.KeySize = 256;
|
||||
sma.Key = pdb.GetBytes(32);
|
||||
sma.Padding = PaddingMode.Zeros;
|
||||
@@ -210,9 +198,9 @@ public static partial class Encryptor
|
||||
private static byte[] GenerateRandomBytes(int count)
|
||||
{
|
||||
// 加密文件随机数生成
|
||||
RandomNumberGenerator rand = new RNGCryptoServiceProvider();
|
||||
var rand = RandomNumberGenerator.Create();
|
||||
|
||||
byte[] bytes = new byte[count];
|
||||
var bytes = new byte[count];
|
||||
rand.GetBytes(bytes);
|
||||
return bytes;
|
||||
}
|
||||
|
||||
@@ -18,12 +18,12 @@ public static partial class Encryptor
|
||||
try
|
||||
{
|
||||
var rgbKey = Encoding.UTF8.GetBytes(encryptKey[..8]); //转换为字节
|
||||
var rgbIV = Encoding.UTF8.GetBytes(encryptKey[..8]);
|
||||
var rgbIv = Encoding.UTF8.GetBytes(encryptKey[..8]);
|
||||
var inputByteArray = Encoding.UTF8.GetBytes(encryptString);
|
||||
var dCSP = new DESCryptoServiceProvider(); //实例化数据加密标准
|
||||
var des = DES.Create(); //实例化数据加密标准
|
||||
var mStream = new MemoryStream(); //实例化内存流
|
||||
//将数据流链接到加密转换的流
|
||||
var cStream = new CryptoStream(mStream, dCSP.CreateEncryptor(rgbKey, rgbIV), CryptoStreamMode.Write);
|
||||
var cStream = new CryptoStream(mStream, des.CreateEncryptor(rgbKey, rgbIv), CryptoStreamMode.Write);
|
||||
cStream.Write(inputByteArray, 0, inputByteArray.Length);
|
||||
cStream.FlushFinalBlock();
|
||||
// 转base64
|
||||
@@ -48,11 +48,11 @@ public static partial class Encryptor
|
||||
try
|
||||
{
|
||||
var rgbKey = Encoding.UTF8.GetBytes(decryptKey);
|
||||
var rgbIV = Encoding.UTF8.GetBytes(decryptKey);
|
||||
var rgbIv = Encoding.UTF8.GetBytes(decryptKey);
|
||||
var inputByteArray = Convert.FromBase64String(decryptString);
|
||||
var DCSP = new DESCryptoServiceProvider();
|
||||
var des = DES.Create();
|
||||
var mStream = new MemoryStream();
|
||||
var cStream = new CryptoStream(mStream, DCSP.CreateDecryptor(rgbKey, rgbIV), CryptoStreamMode.Write);
|
||||
var cStream = new CryptoStream(mStream, des.CreateDecryptor(rgbKey, rgbIv), CryptoStreamMode.Write);
|
||||
cStream.Write(inputByteArray, 0, inputByteArray.Length);
|
||||
cStream.FlushFinalBlock();
|
||||
return Encoding.UTF8.GetString(mStream.ToArray());
|
||||
|
||||
@@ -10,7 +10,7 @@ public static class Hash
|
||||
/// </summary>
|
||||
/// <param name="input"></param>
|
||||
/// <returns></returns>
|
||||
public static string GetMd5Hash(string? input)
|
||||
public static string? GetMd5Hash(string? input)
|
||||
{
|
||||
if (input == null)
|
||||
{
|
||||
@@ -45,7 +45,7 @@ public static class Hash
|
||||
try
|
||||
{
|
||||
var file = new FileStream(fileName, FileMode.Open);
|
||||
MD5 md5 = new MD5CryptoServiceProvider();
|
||||
var md5 = MD5.Create();
|
||||
var retVal = md5.ComputeHash(file);
|
||||
file.Close();
|
||||
|
||||
|
||||
@@ -87,15 +87,13 @@ public static class Format
|
||||
/// <returns></returns>
|
||||
public static string FormatSpeed(float speed)
|
||||
{
|
||||
string formatSpeed = speed switch
|
||||
return speed switch
|
||||
{
|
||||
<= 0 => "0B/s",
|
||||
< 1024 => $"{speed:F2}B/s",
|
||||
< 1024 * 1024 => $"{speed / 1024:F2}KB/s",
|
||||
_ => $"{speed / 1024 / 1024:F2}MB/s"
|
||||
};
|
||||
|
||||
return formatSpeed;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -105,16 +103,14 @@ public static class Format
|
||||
/// <returns></returns>
|
||||
public static string FormatFileSize(long fileSize)
|
||||
{
|
||||
string formatFileSize = fileSize switch
|
||||
return fileSize switch
|
||||
{
|
||||
<= 0 => "0B",
|
||||
< 1024 => fileSize.ToString() + "B",
|
||||
< 1024 => fileSize + "B",
|
||||
< 1024 * 1024 => (fileSize / 1024.0).ToString("#.##") + "KB",
|
||||
< 1024 * 1024 * 1024 => (fileSize / 1024.0 / 1024.0).ToString("#.##") + "MB",
|
||||
_ => (fileSize / 1024.0 / 1024.0 / 1024.0).ToString("#.##") + "GB"
|
||||
};
|
||||
|
||||
return formatFileSize;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -128,9 +124,9 @@ public static class Format
|
||||
destName = Path.GetInvalidFileNameChars().Aggregate(destName, (current, c) => current.Replace(c.ToString(), string.Empty));
|
||||
|
||||
var cleanedName = destName
|
||||
.SkipWhile(c => c == ' ' || c == '.')
|
||||
.SkipWhile(c => c is ' ' or '.')
|
||||
.Reverse()
|
||||
.SkipWhile(c => c == ' ' || c == '.')
|
||||
.SkipWhile(c => c is ' ' or '.')
|
||||
.Reverse()
|
||||
.ToArray();
|
||||
|
||||
|
||||
@@ -39,6 +39,7 @@ public static class ListHelper
|
||||
/// <param name="list"></param>
|
||||
/// <param name="item"></param>
|
||||
/// <param name="index"></param>
|
||||
/// <param name="currentSelection"></param>
|
||||
public static void InsertUnique<T>(Collection<T> list, T item, int index, ref T currentSelection)
|
||||
{
|
||||
if (!list.Contains(item))
|
||||
|
||||
@@ -36,7 +36,7 @@ public static class ObjectHelper
|
||||
}
|
||||
|
||||
// 获取expires
|
||||
var expires = strList2.FirstOrDefault(it => it.Contains("Expires")).Split('=')[1];
|
||||
var expires = strList2.FirstOrDefault(it => it.Contains("Expires"))?.Split('=')[1];
|
||||
var dateTime = DateTime.Now;
|
||||
dateTime = dateTime.AddSeconds(int.Parse(expires));
|
||||
|
||||
@@ -52,14 +52,13 @@ public static class ObjectHelper
|
||||
var value = strList3[1];
|
||||
|
||||
// 不需要
|
||||
if (name == "Expires" || name == "gourl")
|
||||
if (name is "Expires" or "gourl")
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// 添加cookie
|
||||
cookieContainer.Add(
|
||||
new Cookie(name, value.Replace(",", "%2c"), "/", ".bilibili.com") { Expires = dateTime });
|
||||
cookieContainer.Add(new Cookie(name, value.Replace(",", "%2c"), "/", ".bilibili.com") { Expires = dateTime });
|
||||
Console.PrintLine(name + ": " + value + "\t" + cookieContainer.Count);
|
||||
}
|
||||
|
||||
@@ -75,17 +74,16 @@ public static class ObjectHelper
|
||||
{
|
||||
var lstCookies = new List<Cookie>();
|
||||
|
||||
var table = (Hashtable)cc.GetType().InvokeMember("m_domainTable",
|
||||
var table = (Hashtable?)cc.GetType().InvokeMember("m_domainTable",
|
||||
BindingFlags.NonPublic | BindingFlags.GetField |
|
||||
BindingFlags.Instance, null, cc, new object[] { });
|
||||
|
||||
foreach (var pathList in table.Values)
|
||||
foreach (var pathList in table?.Values ?? Array.Empty<Hashtable>())
|
||||
{
|
||||
var lstCookieCol = (SortedList)pathList.GetType().InvokeMember("m_list",
|
||||
BindingFlags.NonPublic | BindingFlags.GetField
|
||||
| BindingFlags.Instance, null, pathList,
|
||||
new object[] { });
|
||||
foreach (CookieCollection colCookies in lstCookieCol.Values)
|
||||
var lstCookieCol = (SortedList?)pathList.GetType().InvokeMember("m_list",
|
||||
BindingFlags.NonPublic | BindingFlags.GetField | BindingFlags.Instance, null, pathList,
|
||||
Array.Empty<object>());
|
||||
foreach (CookieCollection colCookies in lstCookieCol?.Values ?? Array.Empty<CookieCollection>())
|
||||
{
|
||||
foreach (Cookie c in colCookies)
|
||||
{
|
||||
@@ -113,9 +111,9 @@ public static class ObjectHelper
|
||||
/// </summary>
|
||||
/// <param name="file"></param>
|
||||
/// <returns></returns>
|
||||
public static CookieContainer ReadCookiesFromDisk(string file)
|
||||
public static CookieContainer? ReadCookiesFromDisk(string file)
|
||||
{
|
||||
return (CookieContainer)ReadObjectFromDisk(file);
|
||||
return (CookieContainer?)ReadObjectFromDisk(file);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -156,7 +154,7 @@ public static class ObjectHelper
|
||||
/// </summary>
|
||||
/// <param name="file"></param>
|
||||
/// <returns></returns>
|
||||
public static object ReadObjectFromDisk(string file)
|
||||
public static object? ReadObjectFromDisk(string file)
|
||||
{
|
||||
try
|
||||
{
|
||||
|
||||
@@ -3,7 +3,7 @@ using QRCoder;
|
||||
|
||||
namespace DownKyi.Core.Utils;
|
||||
|
||||
public static class QRCode
|
||||
public static class QrCode
|
||||
{
|
||||
/// <summary>
|
||||
/// 生成二维码
|
||||
@@ -16,25 +16,22 @@ public static class QRCode
|
||||
/// <param name="iconBorder">图标边框厚度</param>
|
||||
/// <param name="whiteEdge">二维码白边</param>
|
||||
/// <returns>位图</returns>
|
||||
public static Bitmap EncodeQRCode(string msg, int version, int pixel, string? iconPath, int iconSize, int iconBorder, bool whiteEdge)
|
||||
public static Bitmap EncodeQrCode(string msg, int version, int pixel, string? iconPath, int iconSize, int iconBorder, bool whiteEdge)
|
||||
{
|
||||
var codeGenerator = new QRCodeGenerator();
|
||||
|
||||
var codeData = codeGenerator.CreateQrCode(msg, QRCodeGenerator.ECCLevel.H /* 这里设置容错率的一个级别 */, true,
|
||||
false, QRCodeGenerator.EciMode.Utf8, version);
|
||||
var codeData = codeGenerator.CreateQrCode(msg, QRCodeGenerator.ECCLevel.H /* 这里设置容错率的一个级别 */, true, false, QRCodeGenerator.EciMode.Utf8, version);
|
||||
|
||||
var qrCode = new BitmapByteQRCode(codeData);
|
||||
var qrCodeAsBitmapByteArr = qrCode.GetGraphic(20);
|
||||
|
||||
Bitmap icon;
|
||||
icon = string.IsNullOrEmpty(iconPath) ? null : new Bitmap(iconPath);
|
||||
// Bitmap icon;
|
||||
// icon = string.IsNullOrEmpty(iconPath) ? null : new Bitmap(iconPath);
|
||||
|
||||
Bitmap bmp;
|
||||
using var ms = new MemoryStream(qrCodeAsBitmapByteArr);
|
||||
bmp = new Bitmap(ms);
|
||||
var bmp = new Bitmap(ms);
|
||||
|
||||
// Bitmap bmp = qrCode.GetGraphic(pixel, Color.FromRgb(0,0,0), Color.FromRgb(255,255,255), icon, icon_size, icon_border, white_edge);
|
||||
|
||||
return bmp;
|
||||
}
|
||||
}
|
||||
@@ -148,6 +148,8 @@ public partial class App : PrismApplication
|
||||
{
|
||||
if (e.Action == NotifyCollectionChangedAction.Add)
|
||||
{
|
||||
if (e.NewItems == null) return;
|
||||
|
||||
foreach (var item in e.NewItems)
|
||||
{
|
||||
if (item is DownloadingItem downloading)
|
||||
@@ -160,6 +162,8 @@ public partial class App : PrismApplication
|
||||
|
||||
if (e.Action == NotifyCollectionChangedAction.Remove)
|
||||
{
|
||||
if (e.OldItems == null) return;
|
||||
|
||||
foreach (var item in e.OldItems)
|
||||
{
|
||||
if (item is DownloadingItem downloading)
|
||||
@@ -179,6 +183,7 @@ public partial class App : PrismApplication
|
||||
{
|
||||
if (e.Action == NotifyCollectionChangedAction.Add)
|
||||
{
|
||||
if (e.NewItems == null) return;
|
||||
foreach (var item in e.NewItems)
|
||||
{
|
||||
if (item is DownloadedItem downloaded)
|
||||
@@ -191,6 +196,7 @@ public partial class App : PrismApplication
|
||||
|
||||
if (e.Action == NotifyCollectionChangedAction.Remove)
|
||||
{
|
||||
if (e.OldItems == null) return;
|
||||
foreach (var item in e.OldItems)
|
||||
{
|
||||
if (item is DownloadedItem downloaded)
|
||||
@@ -207,16 +213,16 @@ public partial class App : PrismApplication
|
||||
var download = SettingsManager.GetInstance().GetDownloader();
|
||||
switch (download)
|
||||
{
|
||||
case Core.Settings.Downloader.NOT_SET:
|
||||
case Core.Settings.Downloader.NotSet:
|
||||
break;
|
||||
case Core.Settings.Downloader.BUILT_IN:
|
||||
_downloadService = new BuiltinDownloadService(DownloadingList, DownloadedList, (IDialogService)Container.GetContainer().GetService(typeof(IDialogService)));
|
||||
case Core.Settings.Downloader.BuiltIn:
|
||||
_downloadService = new BuiltinDownloadService(DownloadingList, DownloadedList, (IDialogService?)Container.GetContainer().GetService(typeof(IDialogService)));
|
||||
break;
|
||||
case Core.Settings.Downloader.ARIA:
|
||||
_downloadService = new AriaDownloadService(DownloadingList, DownloadedList, (IDialogService)Container.GetContainer().GetService(typeof(IDialogService)));
|
||||
case Core.Settings.Downloader.Aria:
|
||||
_downloadService = new AriaDownloadService(DownloadingList, DownloadedList, (IDialogService?)Container.GetContainer().GetService(typeof(IDialogService)));
|
||||
break;
|
||||
case Core.Settings.Downloader.CUSTOM_ARIA:
|
||||
_downloadService = new CustomAriaDownloadService(DownloadingList, DownloadedList, (IDialogService)Container.GetContainer().GetService(typeof(IDialogService)));
|
||||
case Core.Settings.Downloader.CustomAria:
|
||||
_downloadService = new CustomAriaDownloadService(DownloadingList, DownloadedList, (IDialogService?)Container.GetContainer().GetService(typeof(IDialogService)));
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -264,7 +270,7 @@ public partial class App : PrismApplication
|
||||
// 按序号排序
|
||||
list?.Sort((x, y) =>
|
||||
{
|
||||
var compare = x.MainTitle.CompareTo(y.MainTitle);
|
||||
var compare = string.Compare(x.MainTitle, y.MainTitle, StringComparison.Ordinal);
|
||||
return compare == 0 ? x.Order.CompareTo(y.Order) : compare;
|
||||
});
|
||||
break;
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
namespace DownKyi
|
||||
namespace DownKyi;
|
||||
|
||||
public class AppConstant
|
||||
{
|
||||
public class AppConstant
|
||||
{
|
||||
public const string ClipboardId = "32ff00b1-1a09-4b25-9ca7-dcb6914b141c";
|
||||
}
|
||||
}
|
||||
public const string ClipboardId = "32ff00b1-1a09-4b25-9ca7-dcb6914b141c";
|
||||
}
|
||||
15
DownKyi/CustomControl/AsyncImageLoader/IAsyncImageLoader.cs
Normal file
15
DownKyi/CustomControl/AsyncImageLoader/IAsyncImageLoader.cs
Normal file
@@ -0,0 +1,15 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Avalonia.Media.Imaging;
|
||||
|
||||
namespace DownKyi.CustomControl.AsyncImageLoader;
|
||||
|
||||
public interface IAsyncImageLoader : IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// Loads image
|
||||
/// </summary>
|
||||
/// <param name="url">Target url</param>
|
||||
/// <returns>Bitmap</returns>
|
||||
public Task<Bitmap?> ProvideImageAsync(string url);
|
||||
}
|
||||
73
DownKyi/CustomControl/AsyncImageLoader/ImageBrushLoader.cs
Normal file
73
DownKyi/CustomControl/AsyncImageLoader/ImageBrushLoader.cs
Normal file
@@ -0,0 +1,73 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using Avalonia;
|
||||
using Avalonia.Logging;
|
||||
using Avalonia.Media;
|
||||
using Avalonia.Media.Imaging;
|
||||
using DownKyi.Core.Storage;
|
||||
using DownKyi.CustomControl.AsyncImageLoader.Loaders;
|
||||
|
||||
namespace DownKyi.CustomControl.AsyncImageLoader;
|
||||
|
||||
public static class ImageBrushLoader
|
||||
{
|
||||
private static readonly ParametrizedLogger? Logger;
|
||||
public static IAsyncImageLoader AsyncImageLoader { get; set; } = new DiskCachedWebImageLoader(Path.Combine(StorageManager.GetCache(), "Images"));
|
||||
|
||||
static ImageBrushLoader()
|
||||
{
|
||||
SourceProperty.Changed.AddClassHandler<ImageBrush>(OnSourceChanged);
|
||||
Logger = Avalonia.Logging.Logger.TryGet(LogEventLevel.Error, ImageLoader.AsyncImageLoaderLogArea);
|
||||
}
|
||||
|
||||
private static async void OnSourceChanged(ImageBrush imageBrush, AvaloniaPropertyChangedEventArgs args)
|
||||
{
|
||||
var (oldValue, newValue) = args.GetOldAndNewValue<string?>();
|
||||
if (oldValue == newValue)
|
||||
return;
|
||||
|
||||
SetIsLoading(imageBrush, true);
|
||||
|
||||
Bitmap? bitmap = null;
|
||||
try
|
||||
{
|
||||
if (newValue is not null)
|
||||
{
|
||||
bitmap = await AsyncImageLoader.ProvideImageAsync(newValue);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger?.Log("ImageBrushLoader", "ImageBrushLoader image resolution failed: {0}", e);
|
||||
}
|
||||
|
||||
if (GetSource(imageBrush) != newValue) return;
|
||||
imageBrush.Source = bitmap;
|
||||
|
||||
SetIsLoading(imageBrush, false);
|
||||
}
|
||||
|
||||
public static readonly AttachedProperty<string?> SourceProperty = AvaloniaProperty.RegisterAttached<ImageBrush, string?>("Source", typeof(ImageLoader));
|
||||
|
||||
public static string? GetSource(ImageBrush element)
|
||||
{
|
||||
return element.GetValue(SourceProperty);
|
||||
}
|
||||
|
||||
public static void SetSource(ImageBrush element, string? value)
|
||||
{
|
||||
element.SetValue(SourceProperty, value);
|
||||
}
|
||||
|
||||
public static readonly AttachedProperty<bool> IsLoadingProperty = AvaloniaProperty.RegisterAttached<ImageBrush, bool>("IsLoading", typeof(ImageLoader));
|
||||
|
||||
public static bool GetIsLoading(ImageBrush element)
|
||||
{
|
||||
return element.GetValue(IsLoadingProperty);
|
||||
}
|
||||
|
||||
private static void SetIsLoading(ImageBrush element, bool value)
|
||||
{
|
||||
element.SetValue(IsLoadingProperty, value);
|
||||
}
|
||||
}
|
||||
107
DownKyi/CustomControl/AsyncImageLoader/ImageLoader.cs
Normal file
107
DownKyi/CustomControl/AsyncImageLoader/ImageLoader.cs
Normal file
@@ -0,0 +1,107 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Logging;
|
||||
using Avalonia.Media.Imaging;
|
||||
using DownKyi.Core.Storage;
|
||||
using DownKyi.CustomControl.AsyncImageLoader.Loaders;
|
||||
|
||||
namespace DownKyi.CustomControl.AsyncImageLoader;
|
||||
|
||||
public static class ImageLoader
|
||||
{
|
||||
private static readonly ParametrizedLogger? Logger;
|
||||
|
||||
public const string AsyncImageLoaderLogArea = "AsyncImageLoader";
|
||||
|
||||
public static readonly AttachedProperty<string?> SourceProperty =
|
||||
AvaloniaProperty.RegisterAttached<Image, string?>("Source", typeof(ImageLoader));
|
||||
|
||||
public static readonly AttachedProperty<bool> IsLoadingProperty =
|
||||
AvaloniaProperty.RegisterAttached<Image, bool>("IsLoading", typeof(ImageLoader));
|
||||
|
||||
static ImageLoader()
|
||||
{
|
||||
SourceProperty.Changed.AddClassHandler<Image>(OnSourceChanged);
|
||||
Logger = Avalonia.Logging.Logger.TryGet(LogEventLevel.Error, AsyncImageLoaderLogArea);
|
||||
}
|
||||
|
||||
public static IAsyncImageLoader AsyncImageLoader { get; set; } = new DiskCachedWebImageLoader(Path.Combine(StorageManager.GetCache(), "Images"));
|
||||
|
||||
private static readonly ConcurrentDictionary<Image, CancellationTokenSource> PendingOperations = new();
|
||||
|
||||
private static async void OnSourceChanged(Image sender, AvaloniaPropertyChangedEventArgs args)
|
||||
{
|
||||
var url = args.GetNewValue<string?>();
|
||||
|
||||
var cts = PendingOperations.AddOrUpdate(sender, new CancellationTokenSource(), (x, y) =>
|
||||
{
|
||||
y.Cancel();
|
||||
return new CancellationTokenSource();
|
||||
}
|
||||
);
|
||||
|
||||
if (url == null)
|
||||
{
|
||||
((ICollection<KeyValuePair<Image, CancellationTokenSource>>)PendingOperations).Remove(new KeyValuePair<Image, CancellationTokenSource>(sender, cts));
|
||||
sender.Source = null;
|
||||
return;
|
||||
}
|
||||
|
||||
SetIsLoading(sender, true);
|
||||
|
||||
var bitmap = await Task.Run(async () =>
|
||||
{
|
||||
try
|
||||
{
|
||||
// A small delay allows to cancel early if the image goes out of screen too fast (eg. scrolling)
|
||||
// The Bitmap constructor is expensive and cannot be cancelled
|
||||
await Task.Delay(10, cts.Token);
|
||||
|
||||
return await AsyncImageLoader.ProvideImageAsync(url);
|
||||
}
|
||||
catch (TaskCanceledException)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger?.Log(LogEventLevel.Error, "ImageLoader image resolution failed: {0}", e);
|
||||
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
||||
if (bitmap != null && !cts.Token.IsCancellationRequested)
|
||||
sender.Source = bitmap!;
|
||||
|
||||
// "It is not guaranteed to be thread safe by ICollection, but ConcurrentDictionary's implementation is. Additionally, we recently exposed this API for .NET 5 as a public ConcurrentDictionary.TryRemove"
|
||||
((ICollection<KeyValuePair<Image, CancellationTokenSource>>)PendingOperations).Remove(new KeyValuePair<Image, CancellationTokenSource>(sender, cts));
|
||||
SetIsLoading(sender, false);
|
||||
}
|
||||
|
||||
public static string? GetSource(Image element)
|
||||
{
|
||||
return element.GetValue(SourceProperty);
|
||||
}
|
||||
|
||||
public static void SetSource(Image element, string? value)
|
||||
{
|
||||
element.SetValue(SourceProperty, value);
|
||||
}
|
||||
|
||||
public static bool GetIsLoading(Image element)
|
||||
{
|
||||
return element.GetValue(IsLoadingProperty);
|
||||
}
|
||||
|
||||
private static void SetIsLoading(Image element, bool value)
|
||||
{
|
||||
element.SetValue(IsLoadingProperty, value);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
<Styles xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:controls="clr-namespace:DownKyi.CustomControl.AsyncImageLoader.Loaders">
|
||||
<Design.PreviewWith>
|
||||
<controls:AdvancedImage />
|
||||
</Design.PreviewWith>
|
||||
|
||||
<Style Selector="controls|AdvancedImage">
|
||||
<Setter Property="Template">
|
||||
<ControlTemplate>
|
||||
<Grid>
|
||||
<!-- CurrentImage will be rendered with codebehind, just as it is done in the Image -->
|
||||
<ProgressBar VerticalAlignment="Center" MinWidth="0" MaxWidth="100"
|
||||
IsIndeterminate="True"
|
||||
IsVisible="{TemplateBinding IsLoading}" />
|
||||
</Grid>
|
||||
</ControlTemplate>
|
||||
</Setter>
|
||||
</Style>
|
||||
</Styles>
|
||||
@@ -0,0 +1,339 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Logging;
|
||||
using Avalonia.Markup.Xaml;
|
||||
using Avalonia.Media;
|
||||
using Avalonia.Media.Imaging;
|
||||
using Avalonia.Platform;
|
||||
|
||||
namespace DownKyi.CustomControl.AsyncImageLoader.Loaders;
|
||||
|
||||
public class AdvancedImage : ContentControl
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines the <see cref="Loader" /> property.
|
||||
/// </summary>
|
||||
public static readonly StyledProperty<IAsyncImageLoader?> LoaderProperty = AvaloniaProperty.Register<AdvancedImage, IAsyncImageLoader?>(nameof(Loader));
|
||||
|
||||
/// <summary>
|
||||
/// Defines the <see cref="Source" /> property.
|
||||
/// </summary>
|
||||
public static readonly StyledProperty<string?> SourceProperty = AvaloniaProperty.Register<AdvancedImage, string?>(nameof(Source));
|
||||
|
||||
/// <summary>
|
||||
/// Defines the <see cref="ShouldLoaderChangeTriggerUpdate" /> property.
|
||||
/// </summary>
|
||||
public static readonly DirectProperty<AdvancedImage, bool> ShouldLoaderChangeTriggerUpdateProperty =
|
||||
AvaloniaProperty.RegisterDirect<AdvancedImage, bool>(
|
||||
nameof(ShouldLoaderChangeTriggerUpdate),
|
||||
image => image._shouldLoaderChangeTriggerUpdate,
|
||||
(image, b) => image._shouldLoaderChangeTriggerUpdate = b
|
||||
);
|
||||
|
||||
/// <summary>
|
||||
/// Defines the <see cref="IsLoading" /> property.
|
||||
/// </summary>
|
||||
public static readonly DirectProperty<AdvancedImage, bool> IsLoadingProperty =
|
||||
AvaloniaProperty.RegisterDirect<AdvancedImage, bool>(
|
||||
nameof(IsLoading),
|
||||
image => image._isLoading);
|
||||
|
||||
/// <summary>
|
||||
/// Defines the <see cref="CurrentImage" /> property.
|
||||
/// </summary>
|
||||
public static readonly DirectProperty<AdvancedImage, IImage?> CurrentImageProperty =
|
||||
AvaloniaProperty.RegisterDirect<AdvancedImage, IImage?>(
|
||||
nameof(CurrentImage),
|
||||
image => image._currentImage);
|
||||
|
||||
/// <summary>
|
||||
/// Defines the <see cref="Stretch" /> property.
|
||||
/// </summary>
|
||||
public static readonly StyledProperty<Stretch> StretchProperty =
|
||||
Image.StretchProperty.AddOwner<AdvancedImage>();
|
||||
|
||||
/// <summary>
|
||||
/// Defines the <see cref="StretchDirection" /> property.
|
||||
/// </summary>
|
||||
public static readonly StyledProperty<StretchDirection> StretchDirectionProperty =
|
||||
Image.StretchDirectionProperty.AddOwner<AdvancedImage>();
|
||||
|
||||
private readonly Uri? _baseUri;
|
||||
|
||||
private RoundedRect _cornerRadiusClip;
|
||||
|
||||
private IImage? _currentImage;
|
||||
private bool _isCornerRadiusUsed;
|
||||
|
||||
private bool _isLoading;
|
||||
|
||||
private bool _shouldLoaderChangeTriggerUpdate;
|
||||
|
||||
private CancellationTokenSource? _updateCancellationToken;
|
||||
private readonly ParametrizedLogger? _logger;
|
||||
|
||||
static AdvancedImage()
|
||||
{
|
||||
AffectsRender<AdvancedImage>(CurrentImageProperty, StretchProperty, StretchDirectionProperty,
|
||||
CornerRadiusProperty);
|
||||
AffectsMeasure<AdvancedImage>(CurrentImageProperty, StretchProperty, StretchDirectionProperty);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="AdvancedImage" /> class.
|
||||
/// </summary>
|
||||
/// <param name="baseUri">The base URL for the XAML context.</param>
|
||||
public AdvancedImage(Uri? baseUri)
|
||||
{
|
||||
_baseUri = baseUri;
|
||||
_logger = Logger.TryGet(LogEventLevel.Error, ImageLoader.AsyncImageLoaderLogArea);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="AdvancedImage" /> class.
|
||||
/// </summary>
|
||||
/// <param name="serviceProvider">The XAML service provider.</param>
|
||||
public AdvancedImage(IServiceProvider serviceProvider)
|
||||
: this((serviceProvider.GetService(typeof(IUriContext)) as IUriContext)?.BaseUri)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the URI for image that will be displayed.
|
||||
/// </summary>
|
||||
public IAsyncImageLoader? Loader
|
||||
{
|
||||
get => GetValue(LoaderProperty);
|
||||
set => SetValue(LoaderProperty, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the URI for image that will be displayed.
|
||||
/// </summary>
|
||||
public string? Source
|
||||
{
|
||||
get => GetValue(SourceProperty);
|
||||
set => SetValue(SourceProperty, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the value controlling whether the image should be reloaded after changing the loader.
|
||||
/// </summary>
|
||||
public bool ShouldLoaderChangeTriggerUpdate
|
||||
{
|
||||
get => _shouldLoaderChangeTriggerUpdate;
|
||||
set => SetAndRaise(ShouldLoaderChangeTriggerUpdateProperty, ref _shouldLoaderChangeTriggerUpdate, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating is image currently is loading state.
|
||||
/// </summary>
|
||||
public bool IsLoading
|
||||
{
|
||||
get => _isLoading;
|
||||
private set => SetAndRaise(IsLoadingProperty, ref _isLoading, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a currently loaded IImage.
|
||||
/// </summary>
|
||||
public IImage? CurrentImage
|
||||
{
|
||||
get => _currentImage;
|
||||
set => SetAndRaise(CurrentImageProperty, ref _currentImage, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value controlling how the image will be stretched.
|
||||
/// </summary>
|
||||
public Stretch Stretch
|
||||
{
|
||||
get => GetValue(StretchProperty);
|
||||
set => SetValue(StretchProperty, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value controlling in what direction the image will be stretched.
|
||||
/// </summary>
|
||||
public StretchDirection StretchDirection
|
||||
{
|
||||
get => GetValue(StretchDirectionProperty);
|
||||
set => SetValue(StretchDirectionProperty, value);
|
||||
}
|
||||
|
||||
protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
|
||||
{
|
||||
if (change.Property == SourceProperty)
|
||||
UpdateImage(change.GetNewValue<string>(), Loader);
|
||||
else if (change.Property == LoaderProperty && ShouldLoaderChangeTriggerUpdate)
|
||||
UpdateImage(change.GetNewValue<string>(), Loader);
|
||||
else if (change.Property == CurrentImageProperty)
|
||||
ClearSourceIfUserProvideImage();
|
||||
else if (change.Property == CornerRadiusProperty)
|
||||
UpdateCornerRadius(change.GetNewValue<CornerRadius>());
|
||||
else if (change.Property == BoundsProperty && CornerRadius != default) UpdateCornerRadius(CornerRadius);
|
||||
base.OnPropertyChanged(change);
|
||||
}
|
||||
|
||||
private void ClearSourceIfUserProvideImage()
|
||||
{
|
||||
if (CurrentImage is not null and not ImageWrapper)
|
||||
{
|
||||
// User provided image himself
|
||||
Source = null;
|
||||
}
|
||||
}
|
||||
|
||||
private async void UpdateImage(string? source, IAsyncImageLoader? loader)
|
||||
{
|
||||
var cancellationTokenSource = new CancellationTokenSource();
|
||||
|
||||
var oldCancellationToken = Interlocked.Exchange(ref _updateCancellationToken, cancellationTokenSource);
|
||||
|
||||
try
|
||||
{
|
||||
oldCancellationToken?.Cancel();
|
||||
}
|
||||
catch (ObjectDisposedException)
|
||||
{
|
||||
}
|
||||
|
||||
if (source is null && CurrentImage is not ImageWrapper)
|
||||
{
|
||||
// User provided image himself
|
||||
return;
|
||||
}
|
||||
|
||||
IsLoading = true;
|
||||
CurrentImage = null;
|
||||
|
||||
|
||||
var bitmap = await Task.Run(async () =>
|
||||
{
|
||||
try
|
||||
{
|
||||
if (source == null)
|
||||
return null;
|
||||
|
||||
// A small delay allows to cancel early if the image goes out of screen too fast (eg. scrolling)
|
||||
// The Bitmap constructor is expensive and cannot be cancelled
|
||||
await Task.Delay(10, cancellationTokenSource.Token);
|
||||
|
||||
// Hack to support relative URI
|
||||
// TODO: Refactor IAsyncImageLoader to support BaseUri
|
||||
try
|
||||
{
|
||||
var uri = new Uri(source, UriKind.RelativeOrAbsolute);
|
||||
if (AssetLoader.Exists(uri, _baseUri))
|
||||
return new Bitmap(AssetLoader.Open(uri, _baseUri));
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
|
||||
loader ??= ImageLoader.AsyncImageLoader;
|
||||
return await loader.ProvideImageAsync(source);
|
||||
}
|
||||
catch (TaskCanceledException)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_logger?.Log(this, "AdvancedImage image resolution failed: {0}", e);
|
||||
|
||||
return null;
|
||||
}
|
||||
finally
|
||||
{
|
||||
cancellationTokenSource.Dispose();
|
||||
}
|
||||
}, CancellationToken.None);
|
||||
|
||||
if (cancellationTokenSource.IsCancellationRequested)
|
||||
return;
|
||||
CurrentImage = bitmap is null ? null : new ImageWrapper(bitmap);
|
||||
IsLoading = false;
|
||||
}
|
||||
|
||||
private void UpdateCornerRadius(CornerRadius radius)
|
||||
{
|
||||
_isCornerRadiusUsed = radius != default;
|
||||
_cornerRadiusClip = new RoundedRect(new Rect(0, 0, Bounds.Width, Bounds.Height), radius);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Renders the control.
|
||||
/// </summary>
|
||||
/// <param name="context">The drawing context.</param>
|
||||
public override void Render(DrawingContext context)
|
||||
{
|
||||
var source = CurrentImage;
|
||||
|
||||
if (source != null && Bounds is { Width: > 0, Height: > 0 })
|
||||
{
|
||||
var viewPort = new Rect(Bounds.Size);
|
||||
var sourceSize = source.Size;
|
||||
|
||||
var scale = Stretch.CalculateScaling(Bounds.Size, sourceSize, StretchDirection);
|
||||
var scaledSize = sourceSize * scale;
|
||||
var destRect = viewPort
|
||||
.CenterRect(new Rect(scaledSize))
|
||||
.Intersect(viewPort);
|
||||
var sourceRect = new Rect(sourceSize)
|
||||
.CenterRect(new Rect(destRect.Size / scale));
|
||||
|
||||
DrawingContext.PushedState? pushedState =
|
||||
_isCornerRadiusUsed ? context.PushClip(_cornerRadiusClip) : null;
|
||||
context.DrawImage(source, sourceRect, destRect);
|
||||
pushedState?.Dispose();
|
||||
}
|
||||
else
|
||||
{
|
||||
base.Render(context);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Measures the control.
|
||||
/// </summary>
|
||||
/// <param name="availableSize">The available size.</param>
|
||||
/// <returns>The desired size of the control.</returns>
|
||||
protected override Size MeasureOverride(Size availableSize)
|
||||
{
|
||||
return CurrentImage != null
|
||||
? Stretch.CalculateSize(availableSize, CurrentImage.Size, StretchDirection)
|
||||
: base.MeasureOverride(availableSize);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override Size ArrangeOverride(Size finalSize)
|
||||
{
|
||||
return CurrentImage != null
|
||||
? Stretch.CalculateSize(finalSize, CurrentImage.Size)
|
||||
: base.ArrangeOverride(finalSize);
|
||||
}
|
||||
|
||||
public sealed class ImageWrapper : IImage
|
||||
{
|
||||
public IImage ImageImplementation { get; }
|
||||
|
||||
internal ImageWrapper(IImage imageImplementation)
|
||||
{
|
||||
ImageImplementation = imageImplementation;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Draw(DrawingContext context, Rect sourceRect, Rect destRect)
|
||||
{
|
||||
ImageImplementation.Draw(context, sourceRect, destRect);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Size Size => ImageImplementation.Size;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,177 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using Avalonia.Logging;
|
||||
using Avalonia.Media.Imaging;
|
||||
using Avalonia.Platform;
|
||||
|
||||
namespace DownKyi.CustomControl.AsyncImageLoader.Loaders;
|
||||
|
||||
public class BaseWebImageLoader : IAsyncImageLoader
|
||||
{
|
||||
private readonly ParametrizedLogger? _logger;
|
||||
private readonly bool _shouldDisposeHttpClient;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance with new <see cref="HttpClient" /> instance
|
||||
/// </summary>
|
||||
public BaseWebImageLoader() : this(new HttpClient(), true)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance with the provided <see cref="HttpClient" />, and specifies whether that
|
||||
/// <see cref="HttpClient" /> should be disposed when this instance is disposed.
|
||||
/// </summary>
|
||||
/// <param name="httpClient">The HttpMessageHandler responsible for processing the HTTP response messages.</param>
|
||||
/// <param name="disposeHttpClient">
|
||||
/// true if the inner handler should be disposed of by Dispose; false if you intend to
|
||||
/// reuse the HttpClient.
|
||||
/// </param>
|
||||
public BaseWebImageLoader(HttpClient httpClient, bool disposeHttpClient)
|
||||
{
|
||||
HttpClient = httpClient;
|
||||
_shouldDisposeHttpClient = disposeHttpClient;
|
||||
_logger = Logger.TryGet(LogEventLevel.Error, ImageLoader.AsyncImageLoaderLogArea);
|
||||
}
|
||||
|
||||
protected HttpClient HttpClient { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public virtual async Task<Bitmap?> ProvideImageAsync(string url)
|
||||
{
|
||||
return await LoadAsync(url).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to load bitmap
|
||||
/// </summary>
|
||||
/// <param name="url">Target url</param>
|
||||
/// <returns>Bitmap</returns>
|
||||
protected virtual async Task<Bitmap?> LoadAsync(string url)
|
||||
{
|
||||
var internalOrCachedBitmap =
|
||||
await LoadFromLocalAsync(url).ConfigureAwait(false)
|
||||
?? await LoadFromInternalAsync(url).ConfigureAwait(false)
|
||||
?? await LoadFromGlobalCache(url).ConfigureAwait(false);
|
||||
if (internalOrCachedBitmap != null) return internalOrCachedBitmap;
|
||||
|
||||
try
|
||||
{
|
||||
var externalBytes = await LoadDataFromExternalAsync(url).ConfigureAwait(false);
|
||||
if (externalBytes == null) return null;
|
||||
|
||||
using var memoryStream = new MemoryStream(externalBytes);
|
||||
var bitmap = new Bitmap(memoryStream);
|
||||
await SaveToGlobalCache(url, externalBytes).ConfigureAwait(false);
|
||||
return bitmap;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_logger?.Log(this, "Failed to resolve image: {RequestUri}\nException: {Exception}", url, e);
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// the url maybe is local file url,so if file exists ,we got a Bitmap
|
||||
/// </summary>
|
||||
/// <param name="url"></param>
|
||||
/// <returns></returns>
|
||||
private Task<Bitmap?> LoadFromLocalAsync(string url)
|
||||
{
|
||||
return Task.FromResult(File.Exists(url) ? new Bitmap(url) : null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Receives image bytes from an internal source (for example, from the disk).
|
||||
/// This data will be NOT cached globally (because it is assumed that it is already in internal source us and does not
|
||||
/// require global caching)
|
||||
/// </summary>
|
||||
/// <param name="url">Target url</param>
|
||||
/// <returns>Bitmap</returns>
|
||||
protected virtual Task<Bitmap?> LoadFromInternalAsync(string url)
|
||||
{
|
||||
try
|
||||
{
|
||||
var uri = url.StartsWith("/")
|
||||
? new Uri(url, UriKind.Relative)
|
||||
: new Uri(url, UriKind.RelativeOrAbsolute);
|
||||
|
||||
if (uri.Scheme == Uri.UriSchemeHttp || uri.Scheme == Uri.UriSchemeHttps)
|
||||
return Task.FromResult<Bitmap?>(null);
|
||||
|
||||
if (uri is { IsAbsoluteUri: true, IsFile: true })
|
||||
return Task.FromResult(new Bitmap(uri.LocalPath))!;
|
||||
|
||||
return Task.FromResult(new Bitmap(AssetLoader.Open(uri)))!;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_logger?.Log(this,
|
||||
"Failed to resolve image from request with uri: {RequestUri}\nException: {Exception}", url, e);
|
||||
return Task.FromResult<Bitmap?>(null);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Receives image bytes from an external source (for example, from the Internet).
|
||||
/// This data will be cached globally (if required by the current implementation)
|
||||
/// </summary>
|
||||
/// <param name="url">Target url</param>
|
||||
/// <returns>Image bytes</returns>
|
||||
protected virtual async Task<byte[]?> LoadDataFromExternalAsync(string url)
|
||||
{
|
||||
try
|
||||
{
|
||||
return await HttpClient.GetByteArrayAsync(url).ConfigureAwait(false);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_logger?.Log(this,
|
||||
"Failed to resolve image from request with uri: {RequestUri}\nException: {Exception}", url, e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to load image from global cache (if it is stored before)
|
||||
/// </summary>
|
||||
/// <param name="url">Target url</param>
|
||||
/// <returns>Bitmap</returns>
|
||||
protected virtual Task<Bitmap?> LoadFromGlobalCache(string url)
|
||||
{
|
||||
// Current implementation does not provide global caching
|
||||
return Task.FromResult<Bitmap?>(null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to load image from global cache (if it is stored before)
|
||||
/// </summary>
|
||||
/// <param name="url">Target url</param>
|
||||
/// <param name="imageBytes">Bytes to save</param>
|
||||
/// <returns>Bitmap</returns>
|
||||
protected virtual Task SaveToGlobalCache(string url, byte[] imageBytes)
|
||||
{
|
||||
// Current implementation does not provide global caching
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
~BaseWebImageLoader()
|
||||
{
|
||||
Dispose(false);
|
||||
}
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing && _shouldDisposeHttpClient) HttpClient.Dispose();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Net.Http;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Avalonia.Media.Imaging;
|
||||
|
||||
namespace DownKyi.CustomControl.AsyncImageLoader.Loaders;
|
||||
|
||||
public class DiskCachedWebImageLoader : BaseWebImageLoader
|
||||
{
|
||||
private readonly string _cacheFolder;
|
||||
|
||||
public DiskCachedWebImageLoader(string cacheFolder = "Cache/Images/")
|
||||
{
|
||||
_cacheFolder = cacheFolder;
|
||||
}
|
||||
|
||||
public DiskCachedWebImageLoader(HttpClient httpClient, bool disposeHttpClient, string cacheFolder = "Cache/Images/") : base(httpClient, disposeHttpClient)
|
||||
{
|
||||
_cacheFolder = cacheFolder;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override Task<Bitmap?> LoadFromGlobalCache(string url)
|
||||
{
|
||||
var path = Path.Combine(_cacheFolder, CreateMd5(url));
|
||||
|
||||
return File.Exists(path) ? Task.FromResult<Bitmap?>(new Bitmap(path)) : Task.FromResult<Bitmap?>(null);
|
||||
}
|
||||
|
||||
#if NETSTANDARD2_1
|
||||
protected override async Task SaveToGlobalCache(string url, byte[] imageBytes) {
|
||||
var path = Path.Combine(_cacheFolder, CreateMd5(url));
|
||||
|
||||
Directory.CreateDirectory(_cacheFolder);
|
||||
await File.WriteAllBytesAsync(path, imageBytes).ConfigureAwait(false);
|
||||
}
|
||||
#else
|
||||
protected override Task SaveToGlobalCache(string url, byte[] imageBytes)
|
||||
{
|
||||
var path = Path.Combine(_cacheFolder, CreateMd5(url));
|
||||
Directory.CreateDirectory(_cacheFolder);
|
||||
File.WriteAllBytes(path, imageBytes);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
#endif
|
||||
|
||||
protected static string CreateMd5(string input)
|
||||
{
|
||||
// Use input string to calculate MD5 hash
|
||||
using var md5 = MD5.Create();
|
||||
var inputBytes = Encoding.ASCII.GetBytes(input);
|
||||
var hashBytes = md5.ComputeHash(inputBytes);
|
||||
|
||||
// Convert the byte array to hexadecimal string
|
||||
return BitConverter.ToString(hashBytes).Replace("-", "");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
using System.Collections.Concurrent;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using Avalonia.Media.Imaging;
|
||||
|
||||
namespace DownKyi.CustomControl.AsyncImageLoader.Loaders;
|
||||
|
||||
public class RamCachedWebImageLoader : BaseWebImageLoader
|
||||
{
|
||||
private readonly ConcurrentDictionary<string, Task<Bitmap?>> _memoryCache = new();
|
||||
|
||||
/// <inheritdoc />
|
||||
public RamCachedWebImageLoader()
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public RamCachedWebImageLoader(HttpClient httpClient, bool disposeHttpClient) : base(httpClient, disposeHttpClient)
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override async Task<Bitmap?> ProvideImageAsync(string url)
|
||||
{
|
||||
var bitmap = await _memoryCache.GetOrAdd(url, LoadAsync).ConfigureAwait(false);
|
||||
// If load failed - remove from cache and return
|
||||
// Next load attempt will try to load image again
|
||||
if (bitmap == null) _memoryCache.TryRemove(url, out _);
|
||||
return bitmap;
|
||||
}
|
||||
}
|
||||
@@ -74,8 +74,8 @@ public class Loading : TemplatedControl
|
||||
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
|
||||
{
|
||||
base.OnApplyTemplate(e);
|
||||
double maxSideLength = Math.Min(this.Width, this.Height);
|
||||
double ellipseDiameter = 0.1 * maxSideLength;
|
||||
var maxSideLength = Math.Min(Width, Height);
|
||||
var ellipseDiameter = 0.1 * maxSideLength;
|
||||
if (maxSideLength <= 40)
|
||||
{
|
||||
ellipseDiameter += 1;
|
||||
|
||||
@@ -16,15 +16,15 @@
|
||||
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Avalonia" Version="11.2.4" />
|
||||
<PackageReference Include="Avalonia.Controls.DataGrid" Version="11.2.4" />
|
||||
<PackageReference Include="Avalonia.Desktop" Version="11.2.4" />
|
||||
<PackageReference Include="Avalonia.Fonts.Inter" Version="11.2.4" />
|
||||
<PackageReference Include="Avalonia" Version="11.2.5" />
|
||||
<PackageReference Include="Avalonia.Controls.DataGrid" Version="11.2.5" />
|
||||
<PackageReference Include="Avalonia.Desktop" Version="11.2.5" />
|
||||
<PackageReference Include="Avalonia.Fonts.Inter" Version="11.2.5" />
|
||||
<!--Condition below is needed to remove Avalonia.Diagnostics package from build output in Release configuration.-->
|
||||
<PackageReference Condition="'$(Configuration)' == 'Debug'" Include="Avalonia.Diagnostics" Version="11.2.4" />
|
||||
<PackageReference Include="Avalonia.Themes.Simple" Version="11.2.4" />
|
||||
<PackageReference Include="Avalonia.Xaml.Behaviors" Version="11.2.0.9" />
|
||||
<PackageReference Include="Downloader" Version="3.1.2" />
|
||||
<PackageReference Condition="'$(Configuration)' == 'Debug'" Include="Avalonia.Diagnostics" Version="11.2.5" />
|
||||
<PackageReference Include="Avalonia.Themes.Simple" Version="11.2.5" />
|
||||
<PackageReference Include="Avalonia.Xaml.Behaviors" Version="11.2.0.14" />
|
||||
<PackageReference Include="Downloader" Version="3.3.4" />
|
||||
<PackageReference Include="Prism.Avalonia" Version="8.1.97.11073" />
|
||||
<PackageReference Include="Prism.DryIoc.Avalonia" Version="8.1.97.11073" />
|
||||
</ItemGroup>
|
||||
|
||||
@@ -19,7 +19,7 @@ public class Downloaded // : DownloadBase
|
||||
{
|
||||
FinishedTimestamp = finishedTimestamp;
|
||||
|
||||
var startTime = TimeZone.CurrentTimeZone.ToLocalTime(new DateTime(1970, 1, 1)); // 当地时区
|
||||
var startTime = TimeZoneInfo.ConvertTimeFromUtc(new DateTime(1970, 1, 1), TimeZoneInfo.Local); // 当地时区
|
||||
var dateTime = startTime.AddSeconds(finishedTimestamp);
|
||||
FinishedTime = dateTime.ToString("yyyy-MM-dd HH:mm:ss");
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ public class Downloading // : DownloadBase
|
||||
}
|
||||
|
||||
// Aria相关
|
||||
public string Gid { get; set; }
|
||||
public string? Gid { get; set; }
|
||||
|
||||
// 下载的文件
|
||||
public Dictionary<string, string> DownloadFiles { get; set; }
|
||||
|
||||
@@ -13,7 +13,7 @@ public class AlertService
|
||||
|
||||
public AlertService(IDialogService? dialogService)
|
||||
{
|
||||
this._dialogService = dialogService;
|
||||
_dialogService = dialogService;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -135,7 +135,7 @@ public class BangumiInfoService : IInfoService
|
||||
// 文件命名中的时间格式
|
||||
var timeFormat = SettingsManager.GetInstance().GetFileNamePartTimeFormat();
|
||||
// 视频发布时间
|
||||
var startTime = TimeZone.CurrentTimeZone.ToLocalTime(new DateTime(1970, 1, 1)); // 当地时区
|
||||
var startTime = TimeZoneInfo.ConvertTimeFromUtc(new DateTime(1970, 1, 1), TimeZoneInfo.Local); // 当地时区
|
||||
var dateTime = startTime.AddSeconds(episode.PubTime);
|
||||
page.PublishTime = dateTime.ToString(timeFormat);
|
||||
|
||||
@@ -228,7 +228,7 @@ public class BangumiInfoService : IInfoService
|
||||
// 文件命名中的时间格式
|
||||
var timeFormat = SettingsManager.GetInstance().GetFileNamePartTimeFormat();
|
||||
// 视频发布时间
|
||||
var startTime = TimeZone.CurrentTimeZone.ToLocalTime(new DateTime(1970, 1, 1)); // 当地时区
|
||||
var startTime = TimeZoneInfo.ConvertTimeFromUtc(new DateTime(1970, 1, 1), TimeZoneInfo.Local); // 当地时区
|
||||
var dateTime = startTime.AddSeconds(episode.PubTime);
|
||||
page.PublishTime = dateTime.ToString(timeFormat);
|
||||
|
||||
@@ -271,25 +271,17 @@ public class BangumiInfoService : IInfoService
|
||||
// 查询、保存封面
|
||||
// 将SeasonId保存到avid字段中
|
||||
// 每集封面的cid保存到cid字段,EpisodeId保存到bvid字段中
|
||||
var storageCover = new StorageCover();
|
||||
var coverUrl = _bangumiSeason.Cover;
|
||||
var cover = storageCover.GetCover(_bangumiSeason.SeasonId, "bangumi", -1, coverUrl);
|
||||
|
||||
// 获取用户头像
|
||||
string upName;
|
||||
string? header;
|
||||
if (_bangumiSeason.UpInfo != null)
|
||||
{
|
||||
upName = _bangumiSeason.UpInfo.Name;
|
||||
|
||||
var storageHeader = new StorageHeader();
|
||||
header = storageHeader.GetHeader(_bangumiSeason.UpInfo.Mid, _bangumiSeason.UpInfo.Name,
|
||||
_bangumiSeason.UpInfo.Avatar);
|
||||
}
|
||||
else
|
||||
{
|
||||
upName = "";
|
||||
header = null;
|
||||
}
|
||||
|
||||
// 为videoInfoView赋值
|
||||
@@ -298,7 +290,6 @@ public class BangumiInfoService : IInfoService
|
||||
{
|
||||
videoInfoView.CoverUrl = coverUrl;
|
||||
|
||||
videoInfoView.Cover = cover == null ? null : ImageHelper.LoadFromResource(new Uri(cover));
|
||||
videoInfoView.Title = _bangumiSeason.Title;
|
||||
|
||||
// 分区id
|
||||
@@ -316,17 +307,8 @@ public class BangumiInfoService : IInfoService
|
||||
videoInfoView.Description = _bangumiSeason.Evaluate;
|
||||
|
||||
videoInfoView.UpName = upName;
|
||||
if (header != null)
|
||||
{
|
||||
var storageHeader = new StorageHeader();
|
||||
videoInfoView.UpHeader = storageHeader.GetHeaderThumbnail(header, 48, 48);
|
||||
|
||||
videoInfoView.UpperMid = _bangumiSeason.UpInfo.Mid;
|
||||
}
|
||||
else
|
||||
{
|
||||
videoInfoView.UpHeader = null;
|
||||
}
|
||||
videoInfoView.UpHeader = _bangumiSeason.UpInfo.Avatar;
|
||||
videoInfoView.UpperMid = _bangumiSeason.UpInfo.Mid;
|
||||
});
|
||||
|
||||
return videoInfoView;
|
||||
|
||||
@@ -103,7 +103,7 @@ public class CheeseInfoService : IInfoService
|
||||
// 文件命名中的时间格式
|
||||
var timeFormat = SettingsManager.GetInstance().GetFileNamePartTimeFormat();
|
||||
// 视频发布时间
|
||||
var startTime = TimeZone.CurrentTimeZone.ToLocalTime(new DateTime(1970, 1, 1)); // 当地时区
|
||||
var startTime = TimeZoneInfo.ConvertTimeFromUtc(new DateTime(1970, 1, 1), TimeZoneInfo.Local); // 当地时区
|
||||
var dateTime = startTime.AddSeconds(episode.ReleaseDate);
|
||||
page.PublishTime = dateTime.ToString(timeFormat);
|
||||
|
||||
@@ -146,23 +146,17 @@ public class CheeseInfoService : IInfoService
|
||||
// 查询、保存封面
|
||||
// 将SeasonId保存到avid字段中
|
||||
// 每集封面的cid保存到cid字段,EpisodeId保存到bvid字段中
|
||||
var storageCover = new StorageCover();
|
||||
var coverUrl = _cheeseView.Cover;
|
||||
var cover = storageCover.GetCover(_cheeseView.SeasonId, "cheese", -1, coverUrl);
|
||||
|
||||
// 获取用户头像
|
||||
string upName;
|
||||
string header;
|
||||
if (_cheeseView.UpInfo != null)
|
||||
{
|
||||
upName = _cheeseView.UpInfo.Name;
|
||||
var storageHeader = new StorageHeader();
|
||||
header = storageHeader.GetHeader(_cheeseView.UpInfo.Mid, _cheeseView.UpInfo.Name, _cheeseView.UpInfo.Avatar);
|
||||
}
|
||||
else
|
||||
{
|
||||
upName = "";
|
||||
header = null;
|
||||
}
|
||||
|
||||
// 为videoInfoView赋值
|
||||
@@ -171,7 +165,6 @@ public class CheeseInfoService : IInfoService
|
||||
{
|
||||
videoInfoView.CoverUrl = coverUrl;
|
||||
|
||||
videoInfoView.Cover = cover == null ? null : ImageHelper.LoadFromResource(new Uri(cover));
|
||||
videoInfoView.Title = _cheeseView.Title;
|
||||
|
||||
// 分区id
|
||||
@@ -191,17 +184,8 @@ public class CheeseInfoService : IInfoService
|
||||
videoInfoView.Description = _cheeseView.Subtitle;
|
||||
|
||||
videoInfoView.UpName = upName;
|
||||
if (header != null)
|
||||
{
|
||||
var storageHeader = new StorageHeader();
|
||||
videoInfoView.UpHeader = storageHeader.GetHeaderThumbnail(header, 48, 48);
|
||||
|
||||
videoInfoView.UpperMid = _cheeseView.UpInfo.Mid;
|
||||
}
|
||||
else
|
||||
{
|
||||
videoInfoView.UpHeader = null;
|
||||
}
|
||||
videoInfoView.UpHeader = _cheeseView.UpInfo.Avatar;
|
||||
videoInfoView.UpperMid = _cheeseView.UpInfo.Mid;
|
||||
});
|
||||
|
||||
return videoInfoView;
|
||||
|
||||
@@ -30,7 +30,7 @@ public class AddToDownloadService
|
||||
{
|
||||
private readonly string Tag = "AddToDownloadService";
|
||||
private IInfoService _videoInfoService;
|
||||
private VideoInfoView _videoInfoView;
|
||||
private VideoInfoView? _videoInfoView;
|
||||
private List<VideoSection>? _videoSections;
|
||||
|
||||
// 下载内容
|
||||
@@ -48,13 +48,13 @@ public class AddToDownloadService
|
||||
{
|
||||
switch (streamType)
|
||||
{
|
||||
case PlayStreamType.VIDEO:
|
||||
case PlayStreamType.Video:
|
||||
_videoInfoService = new VideoInfoService(null);
|
||||
break;
|
||||
case PlayStreamType.BANGUMI:
|
||||
case PlayStreamType.Bangumi:
|
||||
_videoInfoService = new BangumiInfoService(null);
|
||||
break;
|
||||
case PlayStreamType.CHEESE:
|
||||
case PlayStreamType.Cheese:
|
||||
_videoInfoService = new CheeseInfoService(null);
|
||||
break;
|
||||
default:
|
||||
@@ -71,13 +71,13 @@ public class AddToDownloadService
|
||||
{
|
||||
switch (streamType)
|
||||
{
|
||||
case PlayStreamType.VIDEO:
|
||||
case PlayStreamType.Video:
|
||||
_videoInfoService = new VideoInfoService(id);
|
||||
break;
|
||||
case PlayStreamType.BANGUMI:
|
||||
case PlayStreamType.Bangumi:
|
||||
_videoInfoService = new BangumiInfoService(id);
|
||||
break;
|
||||
case PlayStreamType.CHEESE:
|
||||
case PlayStreamType.Cheese:
|
||||
_videoInfoService = new CheeseInfoService(id);
|
||||
break;
|
||||
default:
|
||||
@@ -157,13 +157,14 @@ public class AddToDownloadService
|
||||
/// 选择文件夹和下载项
|
||||
/// </summary>
|
||||
/// <param name="dialogService"></param>
|
||||
public async Task<string?> SetDirectory(IDialogService dialogService)
|
||||
public async Task<string?> SetDirectory(IDialogService? dialogService)
|
||||
{
|
||||
if (dialogService == null) return null;
|
||||
// 选择的下载文件夹
|
||||
var directory = string.Empty;
|
||||
|
||||
// 是否使用默认下载目录
|
||||
if (SettingsManager.GetInstance().IsUseSaveVideoRootPath() == AllowStatus.YES)
|
||||
if (SettingsManager.GetInstance().GetIsUseSaveVideoRootPath() == AllowStatus.Yes)
|
||||
{
|
||||
// 下载内容
|
||||
var videoContent = SettingsManager.GetInstance().GetVideoContent();
|
||||
@@ -232,8 +233,7 @@ public class AddToDownloadService
|
||||
/// <param name="directory">下载路径</param>
|
||||
/// <param name="isAll">是否下载所有,包括未选中项</param>
|
||||
/// <returns>添加的数量</returns>
|
||||
public async Task<int> AddToDownload(IEventAggregator eventAggregator, IDialogService dialogService,
|
||||
string? directory, bool isAll = false)
|
||||
public async Task<int> AddToDownload(IEventAggregator eventAggregator, IDialogService? dialogService, string? directory, bool isAll = false)
|
||||
{
|
||||
if (string.IsNullOrEmpty(directory))
|
||||
{
|
||||
@@ -319,8 +319,7 @@ public class AddToDownloadService
|
||||
continue;
|
||||
}
|
||||
|
||||
if (item.DownloadBase.Cid == page.Cid && item.Resolution.Id == page.VideoQuality.Quality &&
|
||||
item.AudioCodec.Name == page.AudioQualityFormat &&
|
||||
if (item.DownloadBase.Cid == page.Cid && item.Resolution.Id == page.VideoQuality.Quality && item.AudioCodec.Name == page.AudioQualityFormat &&
|
||||
item.VideoCodecName == page.VideoQuality.SelectedVideoCodec)
|
||||
{
|
||||
// eventAggregator.GetEvent<MessageEvent>().Publish($"{page.Name}{DictionaryResource.GetString("TipAlreadyToAddDownloaded")}");
|
||||
@@ -338,8 +337,7 @@ public class AddToDownloadService
|
||||
{ "message", $"{item.Name}已下载,是否重新下载" },
|
||||
};
|
||||
|
||||
await dialogService.ShowDialogAsync(ViewAlreadyDownloadedDialogViewModel.Tag, param,
|
||||
buttonResult => { result = buttonResult.Result; });
|
||||
await dialogService.ShowDialogAsync(ViewAlreadyDownloadedDialogViewModel.Tag, param, buttonResult => { result = buttonResult.Result; });
|
||||
});
|
||||
|
||||
if (result == ButtonResult.OK)
|
||||
@@ -377,7 +375,7 @@ public class AddToDownloadService
|
||||
// 视频分区
|
||||
var zoneId = -1;
|
||||
var zoneList = VideoZone.Instance().GetZones();
|
||||
var zone = zoneList.Find(it => it.Id == _videoInfoView.TypeId);
|
||||
var zone = zoneList.Find(it => it.Id == _videoInfoView?.TypeId);
|
||||
if (zone != null)
|
||||
{
|
||||
if (zone.ParentId == 0)
|
||||
@@ -426,10 +424,10 @@ public class AddToDownloadService
|
||||
var orderFormat = SettingsManager.GetInstance().GetOrderFormat();
|
||||
switch (orderFormat)
|
||||
{
|
||||
case OrderFormat.NATURAL:
|
||||
case OrderFormat.Natural:
|
||||
fileName.SetOrder(page.Order);
|
||||
break;
|
||||
case OrderFormat.LEADING_ZEROS:
|
||||
case OrderFormat.LeadingZeros:
|
||||
fileName.SetOrder(page.Order, section.VideoPages.Count);
|
||||
break;
|
||||
}
|
||||
@@ -466,14 +464,14 @@ public class AddToDownloadService
|
||||
switch (_videoInfoView.TypeId)
|
||||
{
|
||||
case -10:
|
||||
playStreamType = PlayStreamType.CHEESE;
|
||||
playStreamType = PlayStreamType.Cheese;
|
||||
break;
|
||||
case 13:
|
||||
case 23:
|
||||
case 177:
|
||||
case 167:
|
||||
case 11:
|
||||
playStreamType = PlayStreamType.BANGUMI;
|
||||
playStreamType = PlayStreamType.Bangumi;
|
||||
break;
|
||||
case 1:
|
||||
case 3:
|
||||
@@ -492,7 +490,7 @@ public class AddToDownloadService
|
||||
case 5:
|
||||
case 181:
|
||||
default:
|
||||
playStreamType = PlayStreamType.VIDEO;
|
||||
playStreamType = PlayStreamType.Video;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -516,8 +514,7 @@ public class AddToDownloadService
|
||||
Name = page.Name,
|
||||
Duration = page.Duration,
|
||||
VideoCodecName = page.VideoQuality.SelectedVideoCodec,
|
||||
Resolution = new Quality
|
||||
{ Name = page.VideoQuality.QualityFormat, Id = page.VideoQuality.Quality },
|
||||
Resolution = new Quality { Name = page.VideoQuality.QualityFormat, Id = page.VideoQuality.Quality },
|
||||
AudioCodec = Constant.GetAudioQualities().FirstOrDefault(t => { return t.Name == page.AudioQualityFormat; }),
|
||||
Page = page.Page
|
||||
};
|
||||
|
||||
@@ -26,7 +26,7 @@ public class AriaDownloadService : DownloadService, IDownloadService
|
||||
public AriaDownloadService(
|
||||
ObservableCollection<DownloadingItem> downloadingList,
|
||||
ObservableCollection<DownloadedItem> downloadedList,
|
||||
IDialogService dialogService) :
|
||||
IDialogService? dialogService) :
|
||||
base(downloadingList, downloadedList, dialogService)
|
||||
{
|
||||
Tag = "AriaDownloadService";
|
||||
@@ -64,7 +64,7 @@ public class AriaDownloadService : DownloadService, IDownloadService
|
||||
/// <param name="downloading"></param>
|
||||
/// <param name="downloadVideo"></param>
|
||||
/// <returns></returns>
|
||||
private string DownloadVideo(DownloadingItem downloading, PlayUrlDashVideo downloadVideo)
|
||||
private string DownloadVideo(DownloadingItem downloading, PlayUrlDashVideo? downloadVideo)
|
||||
{
|
||||
// 如果为空,说明没有匹配到可下载的音频视频
|
||||
if (downloadVideo == null)
|
||||
@@ -130,8 +130,8 @@ public class AriaDownloadService : DownloadService, IDownloadService
|
||||
}
|
||||
|
||||
// 启用https
|
||||
var useSSL = SettingsManager.GetInstance().UseSSL();
|
||||
if (useSSL == AllowStatus.YES)
|
||||
var useSSL = SettingsManager.GetInstance().GetUseSsl();
|
||||
if (useSSL == AllowStatus.Yes)
|
||||
{
|
||||
for (var i = 0; i < urls.Count; i++)
|
||||
{
|
||||
@@ -359,7 +359,7 @@ public class AriaDownloadService : DownloadService, IDownloadService
|
||||
// 显示错误信息
|
||||
if (errorMessage != null && errorMessage.Contains("ERROR"))
|
||||
{
|
||||
var alertService = new AlertService(dialogService);
|
||||
var alertService = new AlertService(DialogService);
|
||||
var result = await alertService.ShowMessage(SystemIcon.Instance().Error,
|
||||
$"Aria2 {DictionaryResource.GetString("Error")}",
|
||||
errorMessage,
|
||||
@@ -440,7 +440,7 @@ public class AriaDownloadService : DownloadService, IDownloadService
|
||||
};
|
||||
|
||||
// 如果设置了代理,则增加HttpProxy
|
||||
if (SettingsManager.GetInstance().IsAriaHttpProxy() == AllowStatus.YES)
|
||||
if (SettingsManager.GetInstance().GetIsAriaHttpProxy() == AllowStatus.Yes)
|
||||
{
|
||||
option.HttpProxy =
|
||||
$"http://{SettingsManager.GetInstance().GetAriaHttpProxy()}:{SettingsManager.GetInstance().GetAriaHttpProxyListenPort()}";
|
||||
@@ -485,7 +485,7 @@ public class AriaDownloadService : DownloadService, IDownloadService
|
||||
private void AriaTellStatus(long totalLength, long completedLength, long speed, string gid)
|
||||
{
|
||||
// 当前的下载视频
|
||||
DownloadingItem video = null;
|
||||
DownloadingItem? video = null;
|
||||
try
|
||||
{
|
||||
video = downloadingList.FirstOrDefault(it => it.Downloading.Gid == gid);
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user