Merge branch 'leiurayer:main' into main

pull/745/head
Jeb Feng 1 year ago committed by GitHub
commit 7f46acbea5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -1,5 +1,10 @@
# 更新日志
## `2023/05/24` v1.5.9
* [新增] 下载完成系统提示。
* [修复] UP主个人空间访问。
## `2023/04/30` v1.5.8
* [新增] 视频详情页搜索功能。

@ -0,0 +1,120 @@
using DownKyi.Core.Settings;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Text.RegularExpressions;
namespace DownKyi.Core.BiliApi.Sign
{
public static class WbiSign
{
/// <summary>
/// 打乱重排实时口令
/// </summary>
/// <param name="origin"></param>
/// <returns></returns>
private static string GetMixinKey(string origin)
{
int[] mixinKeyEncTab = new int[]
{
46, 47, 18, 2, 53, 8, 23, 32, 15, 50, 10, 31, 58, 3, 45, 35, 27, 43, 5, 49,
33, 9, 42, 19, 29, 28, 14, 39,12, 38, 41, 13, 37, 48, 7, 16, 24, 55, 40,
61, 26, 17, 0, 1, 60, 51, 30, 4, 22, 25, 54, 21, 56, 59, 6, 63, 57, 62, 11,
36, 20, 34, 44, 52
};
var temp = new StringBuilder();
foreach (var i in mixinKeyEncTab)
{
temp.Append(origin[i]);
}
return temp.ToString().Substring(0, 32);
}
/// <summary>
/// 将字典参数转为字符串
/// </summary>
/// <param name="parameters"></param>
/// <returns></returns>
public static string ParametersToQuery(Dictionary<string, object> parameters)
{
var keys = parameters.Keys.ToList();
var queryList = new List<string>();
foreach (var item in keys)
{
var value = parameters[item];
queryList.Add($"{item}={value}");
}
return string.Join("&", queryList);
}
/// <summary>
/// Wbi签名返回所有参数字典
/// </summary>
/// <param name="parameters"></param>
/// <returns></returns>
public static Dictionary<string, object> EncodeWbi(Dictionary<string, object> parameters)
{
return EncodeWbi(parameters, GetKey().Item1, GetKey().Item2);
}
/// <summary>
/// Wbi签名返回所有参数字典
/// </summary>
/// <param name="parameters"></param>
/// <param name="imgKey"></param>
/// <param name="subKey"></param>
/// <returns></returns>
public static Dictionary<string, object> EncodeWbi(Dictionary<string, object> parameters, string imgKey, string subKey)
{
var mixinKey = GetMixinKey(imgKey + subKey);
var chrFilter = new Regex("[!'()*]");
var newParameters = new Dictionary<string, object>
{
{ "wts", (long)(DateTime.UtcNow - new DateTime(1970, 1, 1)).TotalSeconds }
};
foreach (var para in parameters)
{
var key = para.Key;
var value = para.Value.ToString();
var encodedValue = chrFilter.Replace(value, "");
newParameters.Add(Uri.EscapeDataString(key), Uri.EscapeDataString(encodedValue));
}
var keys = newParameters.Keys.ToList();
keys.Sort();
var queryList = new List<string>();
foreach (var item in keys)
{
var value = newParameters[item];
queryList.Add($"{item}={value}");
}
var queryString = string.Join("&", queryList);
var md5Hasher = MD5.Create();
var hashStr = queryString + mixinKey;
var hashedQueryString = md5Hasher.ComputeHash(Encoding.UTF8.GetBytes(hashStr));
var wbiSign = BitConverter.ToString(hashedQueryString).Replace("-", "").ToLower();
newParameters.Add("w_rid", wbiSign);
return newParameters;
}
public static Tuple<string, string> GetKey()
{
var user = SettingsManager.GetInstance().GetUserInfo();
return new Tuple<string, string>(user.ImgKey, user.SubKey);
}
}
}

@ -53,6 +53,9 @@ namespace DownKyi.Core.BiliApi.Users.Models
//public int vip_theme_type { get; set; }
[JsonProperty("wallet")]
public UserInfoWallet Wallet { get; set; }
[JsonProperty("wbi_img")]
public Wbi Wbi { get; set; }
}
//public class NavDataLevelInfo
@ -105,4 +108,14 @@ namespace DownKyi.Core.BiliApi.Users.Models
[JsonProperty("mid")]
public long Mid { get; set; }
}
[JsonObject]
public class Wbi
{
[JsonProperty("img_url")]
public string ImgUrl { get; set; }
[JsonProperty("sub_url")]
public string SubUrl { get; set; }
}
}

@ -1,7 +1,9 @@
using DownKyi.Core.BiliApi.Users.Models;
using DownKyi.Core.BiliApi.Sign;
using DownKyi.Core.BiliApi.Users.Models;
using DownKyi.Core.Logging;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
namespace DownKyi.Core.BiliApi.Users
{
@ -25,8 +27,7 @@ namespace DownKyi.Core.BiliApi.Users
UserInfoForNavigationOrigin userInfo = JsonConvert.DeserializeObject<UserInfoForNavigationOrigin>(response);
if (userInfo == null || userInfo.Data == null) { return null; }
if (userInfo.Data.IsLogin) { return userInfo.Data; }
else { return null; }
return userInfo.Data;
}
catch (Exception e)
{
@ -43,7 +44,12 @@ namespace DownKyi.Core.BiliApi.Users
/// <returns></returns>
public static UserInfoForSpace GetUserInfoForSpace(long mid)
{
string url = $"https://api.bilibili.com/x/space/wbi/acc/info?mid={mid}";
var parameters = new Dictionary<string, object>
{
{ "mid", mid }
};
string query = WbiSign.ParametersToQuery(WbiSign.EncodeWbi(parameters));
string url = $"https://api.bilibili.com/x/space/wbi/acc/info?{query}";
string referer = "https://www.bilibili.com";
string response = WebClient.RequestWeb(url, referer);

@ -1,4 +1,5 @@
using DownKyi.Core.BiliApi.Users.Models;
using DownKyi.Core.BiliApi.Sign;
using DownKyi.Core.BiliApi.Users.Models;
using DownKyi.Core.Logging;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
@ -114,7 +115,17 @@ namespace DownKyi.Core.BiliApi.Users
/// <returns></returns>
public static SpacePublicationList GetPublication(long mid, int pn, int ps, long tid = 0, PublicationOrder order = PublicationOrder.PUBDATE, string keyword = "")
{
string url = $"https://api.bilibili.com/x/space/wbi/arc/search?mid={mid}&pn={pn}&ps={ps}&order={order.ToString("G").ToLower()}&tid={tid}&keyword={keyword}";
var parameters = new Dictionary<string, object>
{
{ "mid", mid },
{ "pn", pn },
{ "ps", ps },
{ "order", order.ToString("G").ToLower() },
{ "tid", tid },
{ "keyword", keyword },
};
string query = WbiSign.ParametersToQuery(WbiSign.EncodeWbi(parameters));
string url = $"https://api.bilibili.com/x/space/wbi/arc/search?{query}";
string referer = "https://www.bilibili.com";
string response = WebClient.RequestWeb(url, referer);

@ -1,4 +1,5 @@
using DownKyi.Core.BiliApi.Video.Models;
using DownKyi.Core.BiliApi.Sign;
using DownKyi.Core.BiliApi.Video.Models;
using DownKyi.Core.Logging;
using Newtonsoft.Json;
using System;
@ -17,13 +18,23 @@ namespace DownKyi.Core.BiliApi.Video
public static VideoView VideoViewInfo(string bvid = null, long aid = -1)
{
// 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
string baseUrl = "https://api.bilibili.com/x/web-interface/view";
string referer = "https://www.bilibili.com";
string url;
if (bvid != null) { url = $"{baseUrl}?bvid={bvid}"; }
else if (aid > -1) { url = $"{baseUrl}?aid={aid}"; }
else { return null; }
var parameters = new Dictionary<string, object>();
if (bvid != null)
{
parameters.Add("bvid", bvid);
}
else if (aid > -1)
{
parameters.Add("aid", aid);
}
else
{
return null;
}
string query = WbiSign.ParametersToQuery(WbiSign.EncodeWbi(parameters));
string url = $"https://api.bilibili.com/x/web-interface/wbi/view?{query}";
string referer = "https://www.bilibili.com";
string response = WebClient.RequestWeb(url, referer);
try

@ -1,4 +1,5 @@
using DownKyi.Core.BiliApi.Models.Json;
using DownKyi.Core.BiliApi.Sign;
using DownKyi.Core.BiliApi.VideoStream.Models;
using DownKyi.Core.Logging;
using Newtonsoft.Json;
@ -19,7 +20,14 @@ namespace DownKyi.Core.BiliApi.VideoStream
/// <returns></returns>
public static PlayerV2 PlayerV2(long avid, string bvid, long cid)
{
string url = $"https://api.bilibili.com/x/player/v2?cid={cid}&aid={avid}&bvid={bvid}";
var parameters = new Dictionary<string, object>
{
{ "avid", avid },
{ "bvid", bvid },
{ "cid", cid },
};
string query = WbiSign.ParametersToQuery(WbiSign.EncodeWbi(parameters));
string url = $"https://api.bilibili.com/x/player/wbi/v2?{query}";
string referer = "https://www.bilibili.com";
string response = WebClient.RequestWeb(url, referer);
@ -93,11 +101,30 @@ namespace DownKyi.Core.BiliApi.VideoStream
/// <returns></returns>
public static PlayUrl GetVideoPlayUrl(long avid, string bvid, long cid, int quality = 125)
{
string baseUrl = $"https://api.bilibili.com/x/player/playurl?cid={cid}&qn={quality}&fourk=1&fnver=0&fnval=4048";
string url;
if (bvid != null) { url = $"{baseUrl}&bvid={bvid}"; }
else if (avid > -1) { url = $"{baseUrl}&aid={avid}"; }
else { return null; }
var parameters = new Dictionary<string, object>
{
{ "fourk", 1 },
{ "fnver", 0 },
{ "fnval", 4048 },
{ "cid", cid },
{ "qn", quality },
};
if (bvid != null)
{
parameters.Add("bvid", bvid);
}
else if (avid > -1)
{
parameters.Add("aid", avid);
}
else
{
return null;
}
string query = WbiSign.ParametersToQuery(WbiSign.EncodeWbi(parameters));
string url = $"https://api.bilibili.com/x/player/wbi/playurl?{query}";
return GetPlayUrl(url);
}

@ -186,6 +186,7 @@
<Compile Include="BiliApi\Login\LoginQR.cs" />
<Compile Include="BiliApi\Login\Models\LoginStatus.cs" />
<Compile Include="BiliApi\Login\Models\LoginUrl.cs" />
<Compile Include="BiliApi\Sign\WbiSign.cs" />
<Compile Include="BiliApi\Users\Models\BangumiFollow.cs" />
<Compile Include="BiliApi\Users\Models\BangumiFollowAreas.cs" />
<Compile Include="BiliApi\Users\Models\BangumiFollowNewEp.cs" />

@ -32,5 +32,5 @@ using System.Runtime.InteropServices;
//可以指定所有这些值,也可以使用“生成号”和“修订号”的默认值
//通过使用 "*",如下所示:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("2.1.8.0")]
[assembly: AssemblyFileVersion("2.1.8.0")]
[assembly: AssemblyVersion("2.1.9.0")]
[assembly: AssemblyFileVersion("2.1.9.0")]

@ -6,5 +6,8 @@
public string Name { get; set; }
public bool IsLogin { get; set; } // 是否登录
public bool IsVip { get; set; } // 是否为大会员未登录时为false
public string ImgKey { get; set; }
public string SubKey { get; set; }
}
}

@ -55,13 +55,23 @@ namespace DownKyi.Core.Settings
/// <returns></returns>
private AppSettings GetSettings()
{
if (appSettings != null) { return appSettings; }
try
{
FileStream fileStream = new FileStream(settingsName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
StreamReader streamReader = new StreamReader(fileStream, System.Text.Encoding.UTF8);
string jsonWordTemplate = streamReader.ReadToEnd();
streamReader.Close();
fileStream.Close();
//FileStream fileStream = new FileStream(settingsName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
//StreamReader streamReader = new StreamReader(fileStream, System.Text.Encoding.UTF8);
//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))
{
using (var reader = new StreamReader(stream, System.Text.Encoding.UTF8))
{
jsonWordTemplate = reader.ReadToEnd();
}
}
#if DEBUG
#else

@ -198,6 +198,12 @@ namespace DownKyi.Core.Utils
// 控制字符
destName = Regex.Replace(destName, @"\p{C}+", string.Empty);
// 如果只有空白字符、dot符
if (destName == " " || destName == ".")
{
return "[empty title]";
}
// 移除前导和尾部的空白字符、dot符
int i, j;
for (i = 0; i < destName.Length; i++)

@ -200,7 +200,7 @@
<system:String x:Key="UseSSL">启用https若下载器提示SSL错误则关闭此项</system:String>
<system:String x:Key="UserAgent">UserAgent</system:String>
<system:String x:Key="SelectDownloader">选择下载器(重启生效):</system:String>
<system:String x:Key="BuiltinDownloader">内建下载器(测试)</system:String>
<system:String x:Key="BuiltinDownloader">内建下载器</system:String>
<system:String x:Key="Aria2cDownloader">Aria2下载器</system:String>
<system:String x:Key="CustomAria2cDownloader">自定义Aria2下载器</system:String>
<system:String x:Key="AriaServerHost">Aria服务器地址</system:String>

@ -11,7 +11,7 @@ namespace DownKyi.Models
const int a = 1;
const int b = 5;
const int c = 8;
const int c = 9;
public AppInfo()
{

@ -51,5 +51,5 @@ using System.Windows;
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.5.8.0")]
[assembly: AssemblyFileVersion("1.5.8.0")]
[assembly: AssemblyVersion("1.5.9.0")]
[assembly: AssemblyFileVersion("1.5.9.0")]

@ -31,12 +31,13 @@ namespace DownKyi.Services
/// 显示一个警告弹窗
/// </summary>
/// <param name="message"></param>
/// <param name="buttonNumber"></param>
/// <returns></returns>
public ButtonResult ShowWarning(string message)
public ButtonResult ShowWarning(string message, int buttonNumber = 1)
{
VectorImage image = SystemIcon.Instance().Warning;
string title = DictionaryResource.GetString("Warning");
return ShowMessage(image, title, message, 1);
return ShowMessage(image, title, message, buttonNumber);
}
/// <summary>

@ -20,14 +20,14 @@ using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Media.Imaging;
namespace DownKyi.Services.Download
{
public abstract class DownloadService
{
protected string Tag = "DownloadService";
protected TaskbarIcon _notifyIcon;
protected TaskbarIcon _notifyIcon;
protected IDialogService dialogService;
protected ObservableCollection<DownloadingItem> downloadingList;
protected ObservableCollection<DownloadedItem> downloadedList;
@ -805,6 +805,8 @@ namespace DownKyi.Services.Download
tokenSource = new CancellationTokenSource();
cancellationToken = tokenSource.Token;
_notifyIcon = new TaskbarIcon();
_notifyIcon.IconSource = new BitmapImage(new Uri("pack://application:,,,/Resources/favicon.ico"));
workTask = Task.Run(DoWork);
}

@ -155,7 +155,7 @@ namespace DownKyi.ViewModels.DownloadManager
private void ExecuteRemoveVideoCommand()
{
AlertService alertService = new AlertService(DialogService);
ButtonResult result = alertService.ShowWarning(DictionaryResource.GetString("ConfirmDelete"));
ButtonResult result = alertService.ShowWarning(DictionaryResource.GetString("ConfirmDelete"), 2);
if (result != ButtonResult.OK)
{
return;

@ -218,7 +218,7 @@ namespace DownKyi.ViewModels.DownloadManager
private void ExecuteDeleteCommand()
{
AlertService alertService = new AlertService(DialogService);
ButtonResult result = alertService.ShowWarning(DictionaryResource.GetString("ConfirmDelete"));
ButtonResult result = alertService.ShowWarning(DictionaryResource.GetString("ConfirmDelete"), 2);
if (result != ButtonResult.OK)
{
return;

@ -1,4 +1,5 @@
using DownKyi.Core.BiliApi.Users;
using DownKyi.Core.BiliApi.Users.Models;
using DownKyi.Core.Logging;
using DownKyi.Core.Settings;
using DownKyi.Core.Settings.Models;
@ -12,6 +13,7 @@ using Prism.Regions;
using Prism.Services.Dialogs;
using System;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Windows;
@ -227,15 +229,59 @@ namespace DownKyi.ViewModels
InputText = string.Empty;
}
private async Task<UserInfoForNavigation> GetUserInfo()
{
UserInfoForNavigation userInfo = null;
await Task.Run(new Action(() =>
{
// 获取用户信息
userInfo = UserInfo.GetUserInfoForNavigation();
if (userInfo != null)
{
SettingsManager.GetInstance().SetUserInfo(new UserInfoSettings
{
Mid = userInfo.Mid,
Name = userInfo.Name,
IsLogin = userInfo.IsLogin,
IsVip = userInfo.VipStatus == 1,
ImgKey = userInfo.Wbi.ImgUrl.Split('/').ToList().Last().Split('.')[0],
SubKey = userInfo.Wbi.SubUrl.Split('/').ToList().Last().Split('.')[0],
});
}
else
{
SettingsManager.GetInstance().SetUserInfo(new UserInfoSettings
{
Mid = -1,
Name = "",
IsLogin = false,
IsVip = false,
});
}
}));
return userInfo;
}
/// <summary>
/// 更新用户登录信息
/// </summary>
private async void UpdateUserInfo()
private async void UpdateUserInfo(bool isBackgroud = false)
{
try
{
if (isBackgroud)
{
// 获取用户信息
await GetUserInfo();
return;
}
LoginPanelVisibility = Visibility.Hidden;
// 获取用户信息
var userInfo = await GetUserInfo();
// 检查本地是否存在login文件没有则说明未登录
if (!File.Exists(StorageManager.GetLogin()))
{
@ -245,54 +291,26 @@ namespace DownKyi.ViewModels
return;
}
await Task.Run(new Action(() =>
LoginPanelVisibility = Visibility.Visible;
if (userInfo != null)
{
// 获取用户信息
var userInfo = UserInfo.GetUserInfoForNavigation();
if (userInfo != null)
if (userInfo.Face != null)
{
SettingsManager.GetInstance().SetUserInfo(new UserInfoSettings
{
Mid = userInfo.Mid,
Name = userInfo.Name,
IsLogin = userInfo.IsLogin,
IsVip = userInfo.VipStatus == 1
});
Header = new StorageHeader().GetHeaderThumbnail(userInfo.Mid, userInfo.Name, userInfo.Face, 36, 36);
}
else
{
SettingsManager.GetInstance().SetUserInfo(new UserInfoSettings
{
Mid = -1,
Name = "",
IsLogin = false,
IsVip = false
});
Header = new BitmapImage(new Uri("pack://application:,,,/Resources/default_header.jpg"));
}
UserName = userInfo.Name;
}
else
{
Header = new BitmapImage(new Uri("pack://application:,,,/Resources/default_header.jpg"));
UserName = null;
}
PropertyChangeAsync(new Action(() =>
{
LoginPanelVisibility = Visibility.Visible;
if (userInfo != null)
{
if (userInfo.Face != null)
{
Header = new StorageHeader().GetHeaderThumbnail(userInfo.Mid, userInfo.Name, userInfo.Face, 36, 36);
}
else
{
Header = new BitmapImage(new Uri("pack://application:,,,/Resources/default_header.jpg"));
}
UserName = userInfo.Name;
}
else
{
Header = new BitmapImage(new Uri("pack://application:,,,/Resources/default_header.jpg"));
UserName = null;
}
}));
}));
}
catch (Exception e)
{
@ -316,23 +334,31 @@ namespace DownKyi.ViewModels
string parameter = navigationContext.Parameters.GetValue<string>("Parameter");
if (parameter == null)
{
// 其他情况只更新设置的用户信息不更新UI
UpdateUserInfo(true);
return;
}
// 启动
if (parameter == "start")
{
UpdateUserInfo();
}
// 从登录页面返回
if (parameter == "login")
else if (parameter == "login")
{
UpdateUserInfo();
}
// 注销
if (parameter == "logout")
else if (parameter == "logout")
{
UpdateUserInfo();
}
else
{
// 其他情况只更新设置的用户信息不更新UI
UpdateUserInfo(true);
}
}

Loading…
Cancel
Save