mirror of
https://github.com/leiurayer/downkyi.git
synced 2025-03-23 07:40:12 +08:00
保存工作进度
This commit is contained in:
parent
1ca966f617
commit
e5310718ab
@ -2,7 +2,7 @@
|
||||
|
||||
public static class BiliLocator
|
||||
{
|
||||
private static ILogin _login;
|
||||
private static ILogin? _login;
|
||||
public static ILogin Login
|
||||
{
|
||||
get
|
||||
@ -12,7 +12,7 @@ public static class BiliLocator
|
||||
}
|
||||
}
|
||||
|
||||
private static IUser _user;
|
||||
private static IUser? _user;
|
||||
public static IUser User
|
||||
{
|
||||
get
|
||||
@ -21,4 +21,18 @@ public static class BiliLocator
|
||||
return _user;
|
||||
}
|
||||
}
|
||||
|
||||
private static IVideo? _video;
|
||||
public static IVideo Video(string input)
|
||||
{
|
||||
_video ??= new Web.Video(input);
|
||||
|
||||
if (_video.Input() != input)
|
||||
{
|
||||
_video = new Web.Video(input);
|
||||
}
|
||||
|
||||
return _video;
|
||||
}
|
||||
|
||||
}
|
@ -23,9 +23,9 @@ public static class Cookies
|
||||
/// </summary>
|
||||
/// <param name="file"></param>
|
||||
/// <returns></returns>
|
||||
public static CookieContainer ReadCookiesFromDisk(string file)
|
||||
public static CookieContainer? ReadCookiesFromDisk(string file)
|
||||
{
|
||||
return (CookieContainer)ObjectHelper.ReadObjectFromDisk(file);
|
||||
return (CookieContainer?)ObjectHelper.ReadObjectFromDisk(file);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -37,16 +37,16 @@ public static class Cookies
|
||||
{
|
||||
var lstCookies = new List<Cookie>();
|
||||
|
||||
Hashtable table = (Hashtable)cc.GetType().InvokeMember("m_domainTable",
|
||||
var table = (Hashtable?)cc.GetType().InvokeMember("m_domainTable",
|
||||
System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.GetField |
|
||||
System.Reflection.BindingFlags.Instance, null, cc, Array.Empty<object>());
|
||||
|
||||
foreach (object pathList in table.Values)
|
||||
foreach (object pathList in table!.Values)
|
||||
{
|
||||
SortedList lstCookieCol = (SortedList)pathList.GetType().InvokeMember("m_list",
|
||||
var lstCookieCol = (SortedList?)pathList.GetType().InvokeMember("m_list",
|
||||
System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.GetField
|
||||
| System.Reflection.BindingFlags.Instance, null, pathList, Array.Empty<object>());
|
||||
foreach (CookieCollection colCookies in lstCookieCol.Values)
|
||||
foreach (CookieCollection colCookies in lstCookieCol!.Values)
|
||||
{
|
||||
foreach (Cookie c in colCookies.Cast<Cookie>())
|
||||
{
|
||||
@ -92,7 +92,7 @@ public static class Cookies
|
||||
if (strList2.Length == 0) { return cookieContainer; }
|
||||
|
||||
// 获取expires
|
||||
string expires = strList2.FirstOrDefault(it => it.Contains("Expires")).Split('=')[1];
|
||||
string expires = strList2.FirstOrDefault(it => it.Contains("Expires"))!.Split('=')[1];
|
||||
DateTime dateTime = DateTime.Now;
|
||||
dateTime = dateTime.AddSeconds(int.Parse(expires));
|
||||
|
||||
|
@ -8,18 +8,18 @@ public interface ILogin
|
||||
/// 申请二维码
|
||||
/// </summary>
|
||||
/// <returns>(url, key)</returns>
|
||||
Tuple<string, string> GetQRCodeUrl();
|
||||
Tuple<string, string>? GetQRCodeUrl();
|
||||
|
||||
/// <summary>
|
||||
/// 扫码登录
|
||||
/// </summary>
|
||||
/// <param name="qrcodeKey"></param>
|
||||
/// <returns></returns>
|
||||
QRCodeStatus PollQRCode(string qrcodeKey);
|
||||
QRCodeStatus? PollQRCode(string qrcodeKey);
|
||||
|
||||
/// <summary>
|
||||
/// 导航栏用户信息
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
NavigationInfo GetNavigationInfo();
|
||||
NavigationInfo? GetNavigationInfo();
|
||||
}
|
10
src/Downkyi.Core/Bili/IVideo.cs
Normal file
10
src/Downkyi.Core/Bili/IVideo.cs
Normal file
@ -0,0 +1,10 @@
|
||||
using Downkyi.Core.Bili.Models;
|
||||
|
||||
namespace Downkyi.Core.Bili;
|
||||
|
||||
public interface IVideo
|
||||
{
|
||||
string Input();
|
||||
|
||||
VideoInfo? GetVideoInfo(string? bvid = null, long aid = -1);
|
||||
}
|
@ -3,8 +3,8 @@
|
||||
public class NavigationInfo
|
||||
{
|
||||
public long Mid { get; set; }
|
||||
public string Name { get; set; }
|
||||
public string Header { get; set; }
|
||||
public string Name { get; set; } = string.Empty;
|
||||
public string Header { get; set; } = string.Empty;
|
||||
public int VipStatus { get; set; } // 会员开通状态 // 0:无;1:有
|
||||
public bool IsLogin { get; set; }
|
||||
}
|
@ -2,8 +2,8 @@
|
||||
|
||||
public class QRCodeStatus
|
||||
{
|
||||
public string Url { get; set; }
|
||||
public string Token { get; set; }
|
||||
public string Url { get; set; } = string.Empty;
|
||||
public string Token { get; set; } = string.Empty;
|
||||
public long Timestamp { get; set; }
|
||||
|
||||
/// <summary>
|
||||
@ -13,5 +13,5 @@ public class QRCodeStatus
|
||||
/// 86101:未扫码
|
||||
/// </summary>
|
||||
public long Code { get; set; }
|
||||
public string Message { get; set; }
|
||||
public string Message { get; set; } = string.Empty;
|
||||
}
|
@ -2,6 +2,6 @@
|
||||
|
||||
public class Quality
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public string Name { get; set; } = string.Empty;
|
||||
public int Id { get; set; }
|
||||
}
|
11
src/Downkyi.Core/Bili/Models/VideoInfo.cs
Normal file
11
src/Downkyi.Core/Bili/Models/VideoInfo.cs
Normal file
@ -0,0 +1,11 @@
|
||||
namespace Downkyi.Core.Bili.Models;
|
||||
|
||||
public class VideoInfo
|
||||
{
|
||||
public long Aid { get; set; }
|
||||
public string Bvid { get; set; } = string.Empty;
|
||||
public long Cid { get; set; }
|
||||
public string Title { get; set; } = string.Empty;
|
||||
public string Description { get; set; } = string.Empty;
|
||||
public string PublishTime { get; set; } = string.Empty;
|
||||
}
|
@ -459,7 +459,7 @@ public static class ParseEntrance
|
||||
/// <returns></returns>
|
||||
private static string EnableHttps(string url)
|
||||
{
|
||||
if (!IsUrl(url)) { return null; }
|
||||
if (!IsUrl(url)) { return string.Empty; }
|
||||
|
||||
return url.Replace("http://", "https://");
|
||||
}
|
||||
|
@ -1,17 +1,17 @@
|
||||
using BiliSharp;
|
||||
using BiliSharp.Api.Login;
|
||||
using Downkyi.BiliSharp;
|
||||
using Downkyi.BiliSharp.Api.Login;
|
||||
using Downkyi.Core.Bili.Models;
|
||||
using Downkyi.Core.Settings;
|
||||
|
||||
namespace Downkyi.Core.Bili.Web;
|
||||
|
||||
public class Login : ILogin
|
||||
internal class Login : ILogin
|
||||
{
|
||||
/// <summary>
|
||||
/// 申请二维码(web端)
|
||||
/// </summary>
|
||||
/// <returns>(url, key)</returns>
|
||||
public Tuple<string, string> GetQRCodeUrl()
|
||||
public Tuple<string, string>? GetQRCodeUrl()
|
||||
{
|
||||
string userAgent = SettingsManager.GetInstance().GetUserAgent();
|
||||
BiliManager.Instance().SetUserAgent(userAgent);
|
||||
@ -26,7 +26,7 @@ public class Login : ILogin
|
||||
/// 扫码登录(web端)
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public QRCodeStatus PollQRCode(string qrcodeKey)
|
||||
public QRCodeStatus? PollQRCode(string qrcodeKey)
|
||||
{
|
||||
string userAgent = SettingsManager.GetInstance().GetUserAgent();
|
||||
BiliManager.Instance().SetUserAgent(userAgent);
|
||||
@ -48,7 +48,7 @@ public class Login : ILogin
|
||||
/// 导航栏用户信息
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public NavigationInfo GetNavigationInfo()
|
||||
public NavigationInfo? GetNavigationInfo()
|
||||
{
|
||||
string userAgent = SettingsManager.GetInstance().GetUserAgent();
|
||||
BiliManager.Instance().SetUserAgent(userAgent);
|
||||
|
@ -58,7 +58,7 @@ public static class LoginHelper
|
||||
/// 获得登录的cookies
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static CookieContainer GetLoginInfoCookies()
|
||||
public static CookieContainer? GetLoginInfoCookies()
|
||||
{
|
||||
string tempFile = LOCAL_LOGIN_INFO + "-" + Guid.NewGuid().ToString("N");
|
||||
|
||||
@ -80,7 +80,7 @@ public static class LoginHelper
|
||||
}
|
||||
else { return null; }
|
||||
|
||||
CookieContainer cookies = Cookies.ReadCookiesFromDisk(tempFile);
|
||||
var cookies = Cookies.ReadCookiesFromDisk(tempFile);
|
||||
|
||||
if (File.Exists(tempFile))
|
||||
{
|
||||
|
@ -1,5 +1,5 @@
|
||||
namespace Downkyi.Core.Bili.Web;
|
||||
|
||||
public class User : IUser
|
||||
internal class User : IUser
|
||||
{
|
||||
}
|
71
src/Downkyi.Core/Bili/Web/Video.cs
Normal file
71
src/Downkyi.Core/Bili/Web/Video.cs
Normal file
@ -0,0 +1,71 @@
|
||||
using Downkyi.BiliSharp.Api.Models.Video;
|
||||
using Downkyi.Core.Bili.Models;
|
||||
using Downkyi.Core.Bili.Utils;
|
||||
|
||||
namespace Downkyi.Core.Bili.Web;
|
||||
|
||||
internal class Video : IVideo
|
||||
{
|
||||
private readonly VideoView? videoView;
|
||||
|
||||
private readonly string _input = string.Empty;
|
||||
|
||||
internal Video(string input)
|
||||
{
|
||||
if (input == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_input = input;
|
||||
|
||||
if (ParseEntrance.IsAvId(input) || ParseEntrance.IsAvUrl(input))
|
||||
{
|
||||
long avid = ParseEntrance.GetAvId(input);
|
||||
videoView = BiliSharp.Api.Video.VideoInfo.GetVideoViewInfo(null, avid);
|
||||
}
|
||||
|
||||
if (ParseEntrance.IsBvId(input) || ParseEntrance.IsBvUrl(input))
|
||||
{
|
||||
string bvid = ParseEntrance.GetBvId(input);
|
||||
videoView = BiliSharp.Api.Video.VideoInfo.GetVideoViewInfo(bvid);
|
||||
}
|
||||
}
|
||||
|
||||
public string Input()
|
||||
{
|
||||
return _input;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取视频详情页信息
|
||||
/// </summary>
|
||||
/// <param name="bvid"></param>
|
||||
/// <param name="aid"></param>
|
||||
/// <returns></returns>
|
||||
public VideoInfo? GetVideoInfo(string? bvid = null, long aid = -1)
|
||||
{
|
||||
if (videoView == null) { return null; }
|
||||
if (videoView.Data == null) { return null; }
|
||||
if (videoView.Data.View == null) { return null; }
|
||||
|
||||
/* 视频发布时间 */
|
||||
// 当地时区
|
||||
DateTime startTime = TimeZoneInfo.ConvertTime(new DateTime(1970, 1, 1), TimeZoneInfo.Local);
|
||||
DateTime dateTime = startTime.AddSeconds(videoView.Data.View.Pubdate);
|
||||
string publishTime = dateTime.ToString("yyyy-MM-dd HH:mm:ss");
|
||||
|
||||
VideoInfo videoInfo = new()
|
||||
{
|
||||
Aid = videoView.Data.View.Aid,
|
||||
Bvid = videoView.Data.View.Bvid,
|
||||
Cid = videoView.Data.View.Cid,
|
||||
Title = videoView.Data.View.Title,
|
||||
Description = videoView.Data.View.Desc,
|
||||
PublishTime = publishTime,
|
||||
};
|
||||
|
||||
return videoInfo;
|
||||
}
|
||||
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
@ -10,14 +10,10 @@
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Aria2cNet" Version="1.0.1" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||
<PackageReference Include="NLog" Version="5.2.5" />
|
||||
<PackageReference Include="Downkyi.BiliSharp" Version="0.0.3" />
|
||||
<PackageReference Include="NLog" Version="5.3.2" />
|
||||
<PackageReference Include="sqlite-net-pcl" Version="1.8.116" />
|
||||
<PackageReference Include="SQLitePCLRaw.bundle_green" Version="2.1.6" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\BiliSharp\BiliSharp.csproj" />
|
||||
<PackageReference Include="SQLitePCLRaw.bundle_green" Version="2.1.8" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
@ -3,6 +3,13 @@ using System.Net;
|
||||
|
||||
namespace Downkyi.Core.Downloader;
|
||||
|
||||
#pragma warning disable CS8601 // 引用类型赋值可能为 null。
|
||||
#pragma warning disable CS8602 // 解引用可能出现空引用。
|
||||
#pragma warning disable CS8604 // 引用类型参数可能为 null。
|
||||
#pragma warning disable CS8618 // 在退出构造函数时,不可为 null 的字段必须包含非 null 值。请考虑声明为可以为 null。
|
||||
#pragma warning disable CS8622 // 参数类型中引用类型的为 Null 性与目标委托不匹配(可能是由于为 Null 性特性)。
|
||||
#pragma warning disable CS8625 // 无法将 null 字面量转换为非 null 的引用类型。
|
||||
|
||||
/// <summary>
|
||||
/// 文件合并改变事件
|
||||
/// </summary>
|
||||
@ -170,7 +177,7 @@ public class MultiThreadDownloader
|
||||
/// </summary>
|
||||
/// <param name="sourceUrl"></param>
|
||||
/// <param name="numOfParts"></param>
|
||||
public MultiThreadDownloader(string sourceUrl, int numOfParts) : this(sourceUrl, null, numOfParts)
|
||||
public MultiThreadDownloader(string sourceUrl, int numOfParts) : this(sourceUrl, null, numOfParts)
|
||||
{
|
||||
}
|
||||
|
||||
@ -361,6 +368,7 @@ public class MultiThreadDownloader
|
||||
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);
|
||||
|
||||
@ -438,4 +446,11 @@ public class MultiThreadDownloader
|
||||
}
|
||||
|
||||
#endregion 公共方法
|
||||
}
|
||||
}
|
||||
|
||||
#pragma warning restore CS8625 // 无法将 null 字面量转换为非 null 的引用类型。
|
||||
#pragma warning restore CS8622 // 参数类型中引用类型的为 Null 性与目标委托不匹配(可能是由于为 Null 性特性)。
|
||||
#pragma warning restore CS8618 // 在退出构造函数时,不可为 null 的字段必须包含非 null 值。请考虑声明为可以为 null。
|
||||
#pragma warning restore CS8604 // 引用类型参数可能为 null。
|
||||
#pragma warning restore CS8602 // 解引用可能出现空引用。
|
||||
#pragma warning restore CS8601 // 引用类型赋值可能为 null。
|
@ -4,6 +4,8 @@ using System.Net;
|
||||
|
||||
namespace Downkyi.Core.Downloader;
|
||||
|
||||
#pragma warning disable CS8618 // 在退出构造函数时,不可为 null 的字段必须包含非 null 值。请考虑声明为可以为 null。
|
||||
|
||||
/// <summary>
|
||||
/// 部分下载器
|
||||
/// </summary>
|
||||
@ -259,4 +261,6 @@ public class PartialDownloader
|
||||
{
|
||||
_wait = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#pragma warning restore CS8618 // 在退出构造函数时,不可为 null 的字段必须包含非 null 值。请考虑声明为可以为 null。
|
@ -210,7 +210,7 @@ public static class FFmpegHelper
|
||||
/// <param name="arg">参数</param>
|
||||
/// <param name="workingDirectory">工作路径</param>
|
||||
/// <param name="output">输出重定向</param>
|
||||
private static void ExcuteProcess(string exe, string arg, string workingDirectory, DataReceivedEventHandler output)
|
||||
private static void ExcuteProcess(string exe, string arg, string? workingDirectory, DataReceivedEventHandler output)
|
||||
{
|
||||
using var p = new Process();
|
||||
p.StartInfo.FileName = exe;
|
||||
|
@ -13,7 +13,7 @@ public class DanmakuSettings
|
||||
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; } = null;
|
||||
public int DanmakuFontSize { get; set; } = -1;
|
||||
public int DanmakuLineCount { get; set; } = -1;
|
||||
public DanmakuLayoutAlgorithm DanmakuLayoutAlgorithm { get; set; } = DanmakuLayoutAlgorithm.NONE;
|
||||
|
@ -19,13 +19,13 @@ public class NetworkSettings
|
||||
#region built-in
|
||||
public int Split { get; set; } = -1;
|
||||
public AllowStatus IsHttpProxy { get; set; } = AllowStatus.NONE;
|
||||
public string HttpProxy { get; set; } = null;
|
||||
public string? HttpProxy { get; set; } = null;
|
||||
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; } = null;
|
||||
public string? AriaHost { get; set; } = null;
|
||||
public int AriaListenPort { get; set; } = -1;
|
||||
public AriaConfigLogLevel AriaLogLevel { get; set; } = AriaConfigLogLevel.NOT_SET;
|
||||
public int AriaSplit { get; set; } = -1;
|
||||
@ -34,7 +34,7 @@ public class NetworkSettings
|
||||
public AriaConfigFileAllocation AriaFileAllocation { get; set; } = AriaConfigFileAllocation.NOT_SET;
|
||||
|
||||
public AllowStatus IsAriaHttpProxy { get; set; } = AllowStatus.NONE;
|
||||
public string AriaHttpProxy { get; set; } = null;
|
||||
public string? AriaHttpProxy { get; set; } = null;
|
||||
public int AriaHttpProxyListenPort { get; set; } = -1;
|
||||
#endregion
|
||||
}
|
@ -3,7 +3,7 @@
|
||||
public class UserInfoSettings
|
||||
{
|
||||
public long Mid { get; set; }
|
||||
public string Name { get; set; }
|
||||
public string Name { get; set; } = string.Empty;
|
||||
public bool IsLogin { get; set; } // 是否登录
|
||||
public bool IsVip { get; set; } // 是否为大会员,未登录时为false
|
||||
}
|
@ -12,11 +12,11 @@ public class VideoSettings
|
||||
public int Quality { get; set; } = -1; // 画质
|
||||
public int AudioQuality { get; set; } = -1; // 音质
|
||||
public AllowStatus IsTranscodingFlvToMp4 { get; set; } = AllowStatus.NONE; // 是否将flv转为mp4
|
||||
public string SaveVideoRootPath { get; set; } = null; // 视频保存路径
|
||||
public List<string> HistoryVideoRootPaths { get; set; } = null; // 历史视频保存路径
|
||||
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 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; // 文件命名中的序号格式
|
||||
}
|
@ -289,7 +289,7 @@ public partial class SettingsManager
|
||||
/// </summary>
|
||||
/// <param name="historyPaths"></param>
|
||||
/// <returns></returns>
|
||||
public bool SetFileNameParts(List<FileNamePart> fileNameParts)
|
||||
public bool SetFileNameParts(List<FileNamePart>? fileNameParts)
|
||||
{
|
||||
appSettings.Video.FileNameParts = fileNameParts;
|
||||
return SetSettings();
|
||||
|
@ -10,7 +10,7 @@ namespace Downkyi.Core.Settings;
|
||||
|
||||
public partial class SettingsManager
|
||||
{
|
||||
private static SettingsManager instance;
|
||||
private static SettingsManager? instance;
|
||||
|
||||
// 内存中保存一份配置
|
||||
private AppSettings appSettings;
|
||||
@ -41,7 +41,7 @@ public partial class SettingsManager
|
||||
/// </summary>
|
||||
private SettingsManager()
|
||||
{
|
||||
appSettings = GetSettings();
|
||||
appSettings = GetSettings() ?? new AppSettings();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -67,7 +67,7 @@ public partial class SettingsManager
|
||||
jsonWordTemplate = Encryptor.DecryptString(jsonWordTemplate, password);
|
||||
#endif
|
||||
|
||||
return JsonConvert.DeserializeObject<AppSettings>(jsonWordTemplate);
|
||||
return JsonConvert.DeserializeObject<AppSettings>(jsonWordTemplate) ?? new AppSettings();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -4,7 +4,7 @@
|
||||
public class LinkStack<T>
|
||||
{
|
||||
//栈顶指示器
|
||||
public Node<T> Top { get; set; }
|
||||
public Node<T>? Top { get; set; }
|
||||
|
||||
//栈中结点的个数
|
||||
public int NCount { get; set; }
|
||||
@ -49,36 +49,36 @@ public class LinkStack<T>
|
||||
}
|
||||
|
||||
//出栈
|
||||
public T Pop()
|
||||
public T? Pop()
|
||||
{
|
||||
if (IsEmpty())
|
||||
{
|
||||
return default;
|
||||
}
|
||||
Node<T> p = Top;
|
||||
Top = Top.Next;
|
||||
Node<T>? p = Top;
|
||||
Top = Top!.Next;
|
||||
--NCount;
|
||||
return p.Data;
|
||||
return p!.Data;
|
||||
}
|
||||
|
||||
//
|
||||
public T Peek()
|
||||
public T? Peek()
|
||||
{
|
||||
if (IsEmpty())
|
||||
{
|
||||
return default;
|
||||
}
|
||||
|
||||
return Top.Data;
|
||||
return Top!.Data;
|
||||
}
|
||||
}
|
||||
|
||||
//结点定义
|
||||
public class Node<T>
|
||||
{
|
||||
public T Data;
|
||||
public T? Data;
|
||||
|
||||
public Node<T> Next;
|
||||
public Node<T>? Next;
|
||||
|
||||
public Node(T item)
|
||||
{
|
||||
|
@ -27,7 +27,7 @@ public static class ListHelper
|
||||
/// <param name="item"></param>
|
||||
public static void AddUnique<T>(List<T> list, T item)
|
||||
{
|
||||
if (!list.Exists(t => t.Equals(item)))
|
||||
if (!list.Exists(t => t!.Equals(item)))
|
||||
{
|
||||
list.Add(item);
|
||||
}
|
||||
@ -42,7 +42,7 @@ public static class ListHelper
|
||||
/// <param name="index"></param>
|
||||
public static void InsertUnique<T>(List<T> list, T item, int index)
|
||||
{
|
||||
if (!list.Exists(t => t.Equals(item)))
|
||||
if (!list.Exists(t => t!.Equals(item)))
|
||||
{
|
||||
list.Insert(index, item);
|
||||
}
|
||||
|
@ -15,8 +15,8 @@ public static class ObjectHelper
|
||||
try
|
||||
{
|
||||
using Stream stream = File.Create(file);
|
||||
var formatter = new BinaryFormatter();
|
||||
#pragma warning disable SYSLIB0011 // 类型或成员已过时
|
||||
var formatter = new BinaryFormatter();
|
||||
formatter.Serialize(stream, obj);
|
||||
#pragma warning restore SYSLIB0011 // 类型或成员已过时
|
||||
|
||||
@ -39,13 +39,13 @@ public static class ObjectHelper
|
||||
/// </summary>
|
||||
/// <param name="file"></param>
|
||||
/// <returns></returns>
|
||||
public static object ReadObjectFromDisk(string file)
|
||||
public static object? ReadObjectFromDisk(string file)
|
||||
{
|
||||
try
|
||||
{
|
||||
using Stream stream = File.Open(file, FileMode.Open);
|
||||
var formatter = new BinaryFormatter();
|
||||
#pragma warning disable SYSLIB0011 // 类型或成员已过时
|
||||
var formatter = new BinaryFormatter();
|
||||
return formatter.Deserialize(stream);
|
||||
#pragma warning restore SYSLIB0011 // 类型或成员已过时
|
||||
}
|
||||
|
@ -25,7 +25,7 @@ public class Stack<T>
|
||||
_data[++_top] = value;
|
||||
}
|
||||
|
||||
public T Pop()
|
||||
public T? Pop()
|
||||
{
|
||||
if (IsEmpty)
|
||||
{
|
||||
@ -35,7 +35,7 @@ public class Stack<T>
|
||||
return _data[_top--];
|
||||
}
|
||||
|
||||
public T Peek()
|
||||
public T? Peek()
|
||||
{
|
||||
if (IsEmpty)
|
||||
{
|
||||
|
@ -1,6 +1,6 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
54
src/Downkyi.UI/Models/VideoInfoView.cs
Normal file
54
src/Downkyi.UI/Models/VideoInfoView.cs
Normal file
@ -0,0 +1,54 @@
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
|
||||
namespace Downkyi.UI.Models
|
||||
{
|
||||
public partial class VideoInfoView : ObservableObject
|
||||
{
|
||||
public string CoverUrl { get; set; } = string.Empty;
|
||||
public long UpperMid { get; set; }
|
||||
public int TypeId { get; set; }
|
||||
|
||||
[ObservableProperty]
|
||||
private string _cover = string.Empty;
|
||||
|
||||
[ObservableProperty]
|
||||
private string _title = string.Empty;
|
||||
|
||||
[ObservableProperty]
|
||||
private string _videoZone = string.Empty;
|
||||
|
||||
[ObservableProperty]
|
||||
private string _createTime = string.Empty;
|
||||
|
||||
[ObservableProperty]
|
||||
private string _playNumber = string.Empty;
|
||||
|
||||
[ObservableProperty]
|
||||
private string _danmakuNumber = string.Empty;
|
||||
|
||||
[ObservableProperty]
|
||||
private string _likeNumber = string.Empty;
|
||||
|
||||
[ObservableProperty]
|
||||
private string _coinNumber = string.Empty;
|
||||
|
||||
[ObservableProperty]
|
||||
private string _favoriteNumber = string.Empty;
|
||||
|
||||
[ObservableProperty]
|
||||
private string _shareNumber = string.Empty;
|
||||
|
||||
[ObservableProperty]
|
||||
private string _replyNumber = string.Empty;
|
||||
|
||||
[ObservableProperty]
|
||||
private string _description = string.Empty;
|
||||
|
||||
[ObservableProperty]
|
||||
private string _upName = string.Empty;
|
||||
|
||||
[ObservableProperty]
|
||||
private string _upHeader = string.Empty;
|
||||
|
||||
}
|
||||
}
|
8
src/Downkyi.UI/Services/IVideoInfoService.cs
Normal file
8
src/Downkyi.UI/Services/IVideoInfoService.cs
Normal file
@ -0,0 +1,8 @@
|
||||
using Downkyi.UI.Models;
|
||||
|
||||
namespace Downkyi.UI.Services;
|
||||
|
||||
public interface IVideoInfoService
|
||||
{
|
||||
VideoInfoView? GetVideoView(string input);
|
||||
}
|
@ -19,7 +19,7 @@ public class MainSearchService : IMainSearchService
|
||||
{
|
||||
// 移除剪贴板id
|
||||
//string validId = input.Replace(AppConstant.ClipboardId, "");
|
||||
|
||||
|
||||
string validId = input;
|
||||
|
||||
// 参数
|
||||
|
21
src/Downkyi.UI/Services/VideoInfoService.cs
Normal file
21
src/Downkyi.UI/Services/VideoInfoService.cs
Normal file
@ -0,0 +1,21 @@
|
||||
using Downkyi.Core.Bili;
|
||||
using Downkyi.UI.Models;
|
||||
|
||||
namespace Downkyi.UI.Services;
|
||||
|
||||
public class VideoInfoService : IVideoInfoService
|
||||
{
|
||||
|
||||
public VideoInfoView? GetVideoView(string input)
|
||||
{
|
||||
if (input == null) { return null; }
|
||||
|
||||
var video = BiliLocator.Video(input);
|
||||
var videoInfo = video.GetVideoInfo();
|
||||
|
||||
|
||||
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
}
|
@ -6,7 +6,6 @@ using Downkyi.Core.Settings;
|
||||
using Downkyi.Core.Settings.Models;
|
||||
using Downkyi.Core.Storage;
|
||||
using Downkyi.UI.Mvvm;
|
||||
using Downkyi.UI.Services;
|
||||
using Downkyi.UI.ViewModels.DownloadManager;
|
||||
using Downkyi.UI.ViewModels.Login;
|
||||
using Downkyi.UI.ViewModels.Settings;
|
||||
|
@ -28,12 +28,12 @@ public class BaseSettingsViewModel : ViewModelBase
|
||||
// 发送通知
|
||||
if (isSucceed)
|
||||
{
|
||||
NotificationEvent.Publish(TipSettingUpdated);
|
||||
//NotificationEvent.Publish(TipSettingUpdated);
|
||||
Log.Logger.Info($"{key}: {TipSettingUpdated}");
|
||||
}
|
||||
else
|
||||
{
|
||||
NotificationEvent.Publish(TipSettingFailed);
|
||||
//NotificationEvent.Publish(TipSettingFailed);
|
||||
Log.Logger.Info($"{key}: {TipSettingFailed}");
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,8 @@
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
using Downkyi.UI.Models;
|
||||
using Downkyi.UI.Mvvm;
|
||||
using Downkyi.UI.ViewModels.DownloadManager;
|
||||
|
||||
namespace Downkyi.UI.ViewModels.Video;
|
||||
|
||||
@ -7,8 +10,42 @@ public partial class VideoDetailViewModel : ViewModelBase
|
||||
{
|
||||
public const string Key = "VideoDetail";
|
||||
|
||||
// 保存输入字符串,避免被用户修改
|
||||
private string? input = null;
|
||||
|
||||
#region 页面属性申明
|
||||
|
||||
[ObservableProperty]
|
||||
private string _inputText = string.Empty;
|
||||
|
||||
[ObservableProperty]
|
||||
private bool _loadingVisibility;
|
||||
|
||||
[ObservableProperty]
|
||||
private bool _contentVisibility;
|
||||
|
||||
[ObservableProperty]
|
||||
private VideoInfoView _videoInfoView = new();
|
||||
|
||||
#endregion
|
||||
|
||||
public VideoDetailViewModel(BaseServices baseServices) : base(baseServices)
|
||||
{
|
||||
#region 属性初始化
|
||||
|
||||
ContentVisibility = true;
|
||||
|
||||
VideoInfoView.Title = "video name";
|
||||
|
||||
VideoInfoView.CoinNumber = "10";
|
||||
VideoInfoView.DanmakuNumber = "1";
|
||||
VideoInfoView.FavoriteNumber = "1";
|
||||
VideoInfoView.LikeNumber = "10";
|
||||
VideoInfoView.PlayNumber = "0";
|
||||
VideoInfoView.ReplyNumber = "1";
|
||||
VideoInfoView.ShareNumber = "1";
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
#region 命令申明
|
||||
@ -24,6 +61,30 @@ public partial class VideoDetailViewModel : ViewModelBase
|
||||
await NavigationService.BackwardAsync(parameter);
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private void Input() { }
|
||||
|
||||
[RelayCommand(FlowExceptionsToTaskScheduler = true)]
|
||||
private async Task DownloadManager()
|
||||
{
|
||||
await NavigationService.ForwardAsync(DownloadManagerViewModel.Key);
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private void CopyCover() { }
|
||||
|
||||
[RelayCommand]
|
||||
private void CopyCoverUrl() { }
|
||||
|
||||
[RelayCommand]
|
||||
private void Upper() { }
|
||||
|
||||
#endregion
|
||||
|
||||
public override void OnNavigatedTo(Dictionary<string, object>? parameter)
|
||||
{
|
||||
base.OnNavigatedTo(parameter);
|
||||
|
||||
}
|
||||
|
||||
}
|
BIN
src/Downkyi/Assets/bili/loading/loading.gif
Normal file
BIN
src/Downkyi/Assets/bili/loading/loading.gif
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.2 KiB |
BIN
src/Downkyi/Assets/gradient.png
Normal file
BIN
src/Downkyi/Assets/gradient.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.5 KiB |
@ -1,7 +1,7 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<OutputType>WinExe</OutputType>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<BuiltInComInteropSupport>true</BuiltInComInteropSupport>
|
||||
<AssemblyName>Downkyi</AssemblyName>
|
||||
@ -12,7 +12,7 @@
|
||||
<Company>DownKyi</Company>
|
||||
<Product>DownKyi</Product>
|
||||
<Description>DownKyi</Description>
|
||||
<Copyright>Copyright © Downkyi 2020-2023</Copyright>
|
||||
<Copyright>Copyright © Downkyi 2020-2024</Copyright>
|
||||
<Version>2.0.0</Version>
|
||||
<FileVersion>2.0.0</FileVersion>
|
||||
<AssemblyVersion>2.0.0</AssemblyVersion>
|
||||
@ -32,21 +32,21 @@
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="AsyncImageLoader.Avalonia" Version="3.2.1" />
|
||||
<PackageReference Include="Avalonia" Version="11.0.5" />
|
||||
<PackageReference Include="Avalonia.Controls.DataGrid" Version="11.0.5" />
|
||||
<PackageReference Include="Avalonia.Controls.ItemsRepeater" Version="11.0.5" />
|
||||
<PackageReference Include="Avalonia.Desktop" Version="11.0.5" />
|
||||
<PackageReference Include="Avalonia.Themes.Fluent" Version="11.0.5" />
|
||||
<PackageReference Include="Avalonia" Version="11.1.1" />
|
||||
<PackageReference Include="Avalonia.Controls.DataGrid" Version="11.1.1" />
|
||||
<PackageReference Include="Avalonia.Controls.ItemsRepeater" Version="11.1.1" />
|
||||
<PackageReference Include="Avalonia.Desktop" Version="11.1.1" />
|
||||
<PackageReference Include="Avalonia.Gif" Version="1.0.0" />
|
||||
<PackageReference Include="Avalonia.Themes.Fluent" Version="11.1.1" />
|
||||
<!--Condition below is needed to remove Avalonia.Diagnostics package from build output in Release configuration.-->
|
||||
<PackageReference Condition="'$(Configuration)' == 'Debug'" Include="Avalonia.Diagnostics" Version="11.0.5" />
|
||||
<PackageReference Include="Avalonia.Xaml.Behaviors" Version="11.0.2" />
|
||||
<PackageReference Include="MessageBox.Avalonia" Version="3.1.5.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="7.0.0" />
|
||||
<PackageReference Include="QRCoder" Version="1.4.3" />
|
||||
<PackageReference Condition="'$(Configuration)' == 'Debug'" Include="Avalonia.Diagnostics" Version="11.1.1" />
|
||||
<PackageReference Include="Avalonia.Xaml.Behaviors" Version="11.1.0" />
|
||||
<PackageReference Include="MessageBox.Avalonia" Version="3.1.6" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="8.0.0" />
|
||||
<PackageReference Include="QRCoder" Version="1.6.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Avalonia.Gif\Avalonia.Gif.csproj" />
|
||||
<ProjectReference Include="..\Downkyi.UI\Downkyi.UI.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
|
@ -19,8 +19,8 @@
|
||||
|
||||
<!-- Index -->
|
||||
<sys:String x:Key="Login">登录</sys:String>
|
||||
<sys:String x:Key="IndexHintText">请输入B站网址链接或av、BV号等……</sys:String>
|
||||
<sys:String x:Key="IndexHintTextSimple">请输入B站视频播放地址……</sys:String>
|
||||
<sys:String x:Key="IndexHintText">请输入B站网址链接或av、BV号等......</sys:String>
|
||||
<sys:String x:Key="IndexHintTextSimple">请输入B站视频播放地址......</sys:String>
|
||||
<sys:String x:Key="Settings">设置</sys:String>
|
||||
<sys:String x:Key="DownloadManager">下载管理</sys:String>
|
||||
<sys:String x:Key="Toolbox">工具箱</sys:String>
|
||||
@ -120,6 +120,7 @@
|
||||
<sys:String x:Key="WhisperFollowing">悄悄关注</sys:String>
|
||||
|
||||
<!-- VideoDetail -->
|
||||
<sys:String x:Key="HintText">请输入B站视频播放地址......</sys:String>
|
||||
<sys:String x:Key="CopyCover">复制封面图片</sys:String>
|
||||
<sys:String x:Key="CopyCoverUrl">复制封面URL</sys:String>
|
||||
<sys:String x:Key="Play">播放</sys:String>
|
||||
|
@ -1,5 +1,4 @@
|
||||
using CommunityToolkit.Mvvm.DependencyInjection;
|
||||
using Downkyi.Services;
|
||||
using Downkyi.UI.Services;
|
||||
using System;
|
||||
using System.Text.RegularExpressions;
|
||||
|
@ -46,6 +46,12 @@ public static class ServiceLocator
|
||||
public static UserSpaceViewModel UserSpaceViewModel =>
|
||||
Ioc.Default.GetRequiredService<UserSpaceViewModel>();
|
||||
|
||||
// 视频页面
|
||||
public static PublicFavoritesViewModel PublicFavoritesViewModel =>
|
||||
Ioc.Default.GetRequiredService<PublicFavoritesViewModel>();
|
||||
public static VideoDetailViewModel VideoDetailViewModel =>
|
||||
Ioc.Default.GetRequiredService<VideoDetailViewModel>();
|
||||
|
||||
// 设置
|
||||
public static SettingsViewModel SettingsViewModel =>
|
||||
Ioc.Default.GetRequiredService<SettingsViewModel>();
|
||||
@ -126,8 +132,8 @@ public static class ServiceLocator
|
||||
.AddSingleton<MySpaceViewModel>()
|
||||
.AddSingleton<UserSpaceViewModel>()
|
||||
//
|
||||
.AddSingleton<VideoDetailViewModel>()
|
||||
.AddSingleton<PublicFavoritesViewModel>()
|
||||
.AddSingleton<VideoDetailViewModel>()
|
||||
.BuildServiceProvider());
|
||||
}
|
||||
}
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
<!-- TextBox.main -->
|
||||
<Style Selector="TextBox.main">
|
||||
<Setter Property="VerticalContentAlignment" Value="Center" />
|
||||
<Setter Property="Background" Value="Transparent" />
|
||||
<Setter Property="BorderThickness" Value="2" />
|
||||
<Setter Property="CornerRadius" Value="20" />
|
||||
@ -19,6 +20,26 @@
|
||||
<Setter Property="BorderBrush" Value="{DynamicResource BrushPrimary}" />
|
||||
</Style>
|
||||
|
||||
<!-- TextBox.main2 -->
|
||||
<Style Selector="TextBox.main2">
|
||||
<Setter Property="VerticalContentAlignment" Value="Center" />
|
||||
<Setter Property="Background" Value="Transparent" />
|
||||
<Setter Property="BorderThickness" Value="2" />
|
||||
<Setter Property="CornerRadius" Value="16" />
|
||||
<Setter Property="Height" Value="32" />
|
||||
<Setter Property="FontSize" Value="14" />
|
||||
<Setter Property="Padding" Value="16,0,0,0" />
|
||||
<Setter Property="SelectionBrush" Value="{DynamicResource BrushPrimaryTranslucent}" />
|
||||
<Setter Property="SelectionForegroundBrush" Value="White" />
|
||||
<Setter Property="BorderBrush" Value="{DynamicResource BrushPrimary}" />
|
||||
</Style>
|
||||
<Style Selector="TextBox.main2:pointerover /template/ Border">
|
||||
<Setter Property="BorderBrush" Value="{DynamicResource BrushPrimary}" />
|
||||
</Style>
|
||||
<Style Selector="TextBox.main2:focus /template/ Border">
|
||||
<Setter Property="BorderBrush" Value="{DynamicResource BrushPrimary}" />
|
||||
</Style>
|
||||
|
||||
<!-- TextBox.normal -->
|
||||
<Style Selector="TextBox.normal">
|
||||
<Setter Property="Background" Value="Transparent" />
|
||||
|
@ -13,6 +13,7 @@ using Downkyi.UI.ViewModels.Login;
|
||||
using Downkyi.UI.ViewModels.Settings;
|
||||
using Downkyi.UI.ViewModels.Toolbox;
|
||||
using Downkyi.UI.ViewModels.User;
|
||||
using Downkyi.UI.ViewModels.Video;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
@ -168,6 +169,12 @@ public partial class MainWindowViewModel : ViewModelBase
|
||||
case ToolboxViewModel.Key:
|
||||
viewModel = Ioc.Default.GetRequiredService<ToolboxViewModel>();
|
||||
break;
|
||||
case VideoDetailViewModel.Key:
|
||||
viewModel = Ioc.Default.GetRequiredService<VideoDetailViewModel>();
|
||||
break;
|
||||
case PublicFavoritesViewModel.Key:
|
||||
viewModel = Ioc.Default.GetRequiredService<PublicFavoritesViewModel>();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
@ -139,7 +139,6 @@
|
||||
<Grid Grid.Row="2" ColumnDefinitions="*,4*,*">
|
||||
<TextBox
|
||||
Grid.Column="1"
|
||||
VerticalContentAlignment="Center"
|
||||
Classes="main"
|
||||
Text="{Binding Path=InputText, Mode=TwoWay}"
|
||||
Watermark="{DynamicResource IndexHintText}">
|
||||
|
@ -4,11 +4,12 @@
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
Title="SplashScreen"
|
||||
Title="{DynamicResource AppName}"
|
||||
Width="500"
|
||||
Height="300"
|
||||
d:DesignHeight="300"
|
||||
d:DesignWidth="500"
|
||||
Background="Transparent"
|
||||
Icon="/Assets/logo.ico"
|
||||
SystemDecorations="None"
|
||||
WindowStartupLocation="CenterScreen"
|
||||
|
385
src/Downkyi/Views/Video/VideoDetailView.axaml
Normal file
385
src/Downkyi/Views/Video/VideoDetailView.axaml
Normal file
@ -0,0 +1,385 @@
|
||||
<UserControl
|
||||
x:Class="Downkyi.Views.Video.VideoDetailView"
|
||||
xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:asyncImageLoader="clr-namespace:AsyncImageLoader;assembly=AsyncImageLoader.Avalonia"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:gif="clr-namespace:Avalonia.Gif;assembly=Avalonia.Gif"
|
||||
xmlns:i="clr-namespace:Avalonia.Xaml.Interactivity;assembly=Avalonia.Xaml.Interactivity"
|
||||
xmlns:ia="clr-namespace:Avalonia.Xaml.Interactions.Core;assembly=Avalonia.Xaml.Interactions"
|
||||
xmlns:local="using:Downkyi"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:vm="using:Downkyi.UI.ViewModels.Video"
|
||||
d:DesignHeight="450"
|
||||
d:DesignWidth="800"
|
||||
x:CompileBindings="True"
|
||||
x:DataType="vm:VideoDetailViewModel"
|
||||
Design.DataContext="{x:Static local:ServiceLocator.VideoDetailViewModel}"
|
||||
mc:Ignorable="d">
|
||||
<UserControl.Resources>
|
||||
<DrawingImage x:Key="DownloadManagerDrawingImage">
|
||||
<DrawingImage.Drawing>
|
||||
<DrawingGroup ClipGeometry="M0,0 V20.01 H24.15 V0 H0 Z">
|
||||
<DrawingGroup.Transform>
|
||||
<TranslateTransform X="0.012936355546116829" Y="0.0082860654219985" />
|
||||
</DrawingGroup.Transform>
|
||||
<DrawingGroup Opacity="1">
|
||||
<DrawingGroup Opacity="1">
|
||||
<GeometryDrawing Brush="{DynamicResource BrushPrimary}" Geometry="F1 M24.15,20.01z M0,0z M11.9,19.91L4.6,19.91A4.38,4.38,0,0,1,0,15.21L0,4.71A4.38,4.38,0,0,1,4.6,0L8.5,0A3,3,0,0,1,11.2,1.3A4.45,4.45,0,0,0,15.8,3.4A17.83,17.83,0,0,1,19.8,3.4A4.25,4.25,0,0,1,24,7.71A57.48,57.48,0,0,1,24,16A4.2,4.2,0,0,1,19.7,20C16.9,19.9,14.4,19.9,11.9,19.9z M11.8,18.51L19.3,18.51A2.76,2.76,0,0,0,22.3,15.81A60.28,60.28,0,0,0,22.3,7.31A2.7,2.7,0,0,0,19.8,4.71A25.41,25.41,0,0,0,15.9,4.61C11.8,4.51,12.9,5.11,9.9,2.01A2.29,2.29,0,0,0,8.2,1.21L4.4,1.21A3.07,3.07,0,0,0,1.1,4.41L1.1,15.11A2.94,2.94,0,0,0,4.3,18.31C6.9,18.51,9.4,18.51,11.8,18.51z" />
|
||||
<GeometryDrawing Brush="{DynamicResource BrushPrimary}" Geometry="F1 M24.15,20.01z M0,0z M17.8,0.11L21.1,0.11C21.6,0.11 22,0.11 22,0.81 22,1.51 21.6,1.61 21.1,1.61L14.4,1.61C13.9,1.61 13.5,1.51 13.5,0.91 13.5,0.31 13.9,0.21 14.4,0.21 15.5,0.11 16.6,0.11 17.8,0.11z M11.2,13.51L11.2,8.11C11.2,7.61 11.2,7.01 11.9,7.01 12.6,7.01 12.5,7.61 12.5,8.01L12.5,13.21 14.2,11.51C14.5,11.11 14.9,10.81 15.4,11.31 15.9,11.81 15.5,12.21 15.2,12.51 14.3,13.51 13.4,14.41 12.5,15.31 12.1,15.81 11.7,15.81 11.2,15.41 10.2,14.41 9.2,13.31 8.2,12.31A0.68,0.68,0,0,1,8.1,11.31C8.4,10.91,8.8,11.01,9.1,11.41A10.43,10.43,0,0,1,11.2,13.51z" />
|
||||
</DrawingGroup>
|
||||
</DrawingGroup>
|
||||
</DrawingGroup>
|
||||
</DrawingImage.Drawing>
|
||||
</DrawingImage>
|
||||
</UserControl.Resources>
|
||||
|
||||
<Grid RowDefinitions="50,10,*">
|
||||
<Grid
|
||||
Grid.Row="0"
|
||||
Height="50"
|
||||
ColumnDefinitions="40,*,50">
|
||||
<Button
|
||||
Grid.Column="0"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center"
|
||||
Classes="transparent"
|
||||
Command="{Binding BackwardCommand}">
|
||||
<Image
|
||||
Width="24"
|
||||
Height="24"
|
||||
Source="{StaticResource ArrowBackDrawingImage}" />
|
||||
</Button>
|
||||
|
||||
<TextBox
|
||||
Grid.Column="1"
|
||||
Classes="main2"
|
||||
Text="{Binding Path=InputText, Mode=TwoWay}"
|
||||
Watermark="{DynamicResource HintText}">
|
||||
<TextBox.KeyBindings>
|
||||
<KeyBinding Command="{Binding InputCommand}" Gesture="Enter" />
|
||||
</TextBox.KeyBindings>
|
||||
<TextBox.InnerRightContent>
|
||||
<gif:GifImage
|
||||
Width="20"
|
||||
Height="20"
|
||||
Margin="8"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
IsVisible="{Binding LoadingVisibility}"
|
||||
SourceUri="avares://Downkyi/Assets/bili/loading/loading.gif"
|
||||
Stretch="Uniform" />
|
||||
</TextBox.InnerRightContent>
|
||||
</TextBox>
|
||||
|
||||
<Button
|
||||
Grid.Column="2"
|
||||
Width="24"
|
||||
Height="24"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
Classes="transparent"
|
||||
Command="{Binding DownloadManagerCommand}"
|
||||
Cursor="Hand">
|
||||
<Image
|
||||
Width="24"
|
||||
Height="24"
|
||||
Source="{StaticResource DownloadManagerDrawingImage}" />
|
||||
</Button>
|
||||
</Grid>
|
||||
|
||||
<Image
|
||||
Grid.Row="1"
|
||||
Opacity="0.3"
|
||||
Source="/Assets/gradient.png"
|
||||
Stretch="Fill" />
|
||||
|
||||
<Grid Grid.Row="2" IsVisible="{Binding ContentVisibility}">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="100*" MaxHeight="180" />
|
||||
<RowDefinition Height="200*" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<DockPanel Grid.Row="0" Margin="10,0">
|
||||
<Image
|
||||
MinWidth="150"
|
||||
MaxWidth="300"
|
||||
VerticalAlignment="Top"
|
||||
asyncImageLoader:ImageLoader.Source="{Binding VideoInfoView.Cover}"
|
||||
DockPanel.Dock="Left">
|
||||
<Image.ContextMenu>
|
||||
<ContextMenu>
|
||||
<MenuItem Command="{Binding CopyCoverCommand}" Header="{DynamicResource CopyCover}" />
|
||||
<MenuItem Command="{Binding CopyCoverUrlCommand}" Header="{DynamicResource CopyCoverUrl}" />
|
||||
<!-- TODO 复制封面到文件 -->
|
||||
</ContextMenu>
|
||||
</Image.ContextMenu>
|
||||
</Image>
|
||||
|
||||
<Grid
|
||||
Margin="10,0,0,0"
|
||||
VerticalAlignment="Stretch"
|
||||
DockPanel.Dock="Left"
|
||||
RowDefinitions="70,*">
|
||||
|
||||
<Grid Grid.Row="0" ColumnDefinitions="*,120">
|
||||
<!-- 标题、分区、发布时间(番剧不显示)、播放量、弹幕数量、up主 -->
|
||||
<Grid Grid.Column="0" RowDefinitions="3*,2*,2*">
|
||||
<TextBlock
|
||||
Grid.Row="0"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center"
|
||||
FontSize="16"
|
||||
FontWeight="Bold"
|
||||
Text="{Binding VideoInfoView.Title}"
|
||||
TextTrimming="CharacterEllipsis"
|
||||
ToolTip.Tip="{Binding VideoInfoView.Title}" />
|
||||
|
||||
<StackPanel
|
||||
Grid.Row="1"
|
||||
VerticalAlignment="Center"
|
||||
Orientation="Horizontal">
|
||||
<TextBlock
|
||||
Margin="0,0,10,0"
|
||||
Foreground="{DynamicResource BrushTextGrey}"
|
||||
Text="{Binding VideoInfoView.VideoZone}" />
|
||||
<TextBlock Foreground="{DynamicResource BrushTextGrey}" Text="{Binding VideoInfoView.CreateTime}" />
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel
|
||||
Grid.Row="2"
|
||||
VerticalAlignment="Center"
|
||||
Orientation="Horizontal">
|
||||
<StackPanel
|
||||
Name="PlayNumber"
|
||||
Margin="0,0,10,0"
|
||||
Orientation="Horizontal">
|
||||
<TextBlock
|
||||
FontSize="12"
|
||||
Foreground="{DynamicResource BrushTextGrey}"
|
||||
Text="{Binding VideoInfoView.PlayNumber}" />
|
||||
<TextBlock
|
||||
FontSize="12"
|
||||
Foreground="{DynamicResource BrushTextGrey}"
|
||||
Text="{DynamicResource Play}" />
|
||||
<i:Interaction.Behaviors>
|
||||
<ia:DataTriggerBehavior
|
||||
Binding="{Binding VideoInfoView.PlayNumber}"
|
||||
ComparisonCondition="LessThanOrEqual"
|
||||
Value="0">
|
||||
<ia:ChangePropertyAction
|
||||
PropertyName="IsVisible"
|
||||
TargetObject="{Binding #PlayNumber}"
|
||||
Value="False" />
|
||||
</ia:DataTriggerBehavior>
|
||||
</i:Interaction.Behaviors>
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel
|
||||
Name="DanmakuNumber"
|
||||
Margin="0,0,10,0"
|
||||
Orientation="Horizontal">
|
||||
<TextBlock
|
||||
FontSize="12"
|
||||
Foreground="{DynamicResource BrushTextGrey}"
|
||||
Text="{Binding VideoInfoView.DanmakuNumber}" />
|
||||
<TextBlock
|
||||
FontSize="12"
|
||||
Foreground="{DynamicResource BrushTextGrey}"
|
||||
Text="{DynamicResource Danmaku}" />
|
||||
<i:Interaction.Behaviors>
|
||||
<ia:DataTriggerBehavior
|
||||
Binding="{Binding VideoInfoView.DanmakuNumber}"
|
||||
ComparisonCondition="LessThanOrEqual"
|
||||
Value="0">
|
||||
<ia:ChangePropertyAction
|
||||
PropertyName="IsVisible"
|
||||
TargetObject="{Binding #DanmakuNumber}"
|
||||
Value="False" />
|
||||
</ia:DataTriggerBehavior>
|
||||
</i:Interaction.Behaviors>
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel
|
||||
Name="LikeNumber"
|
||||
Margin="0,0,10,0"
|
||||
Orientation="Horizontal">
|
||||
<TextBlock
|
||||
FontSize="12"
|
||||
Foreground="{DynamicResource BrushTextGrey}"
|
||||
Text="{Binding VideoInfoView.LikeNumber}" />
|
||||
<TextBlock
|
||||
FontSize="12"
|
||||
Foreground="{DynamicResource BrushTextGrey}"
|
||||
Text="{DynamicResource Like}" />
|
||||
<i:Interaction.Behaviors>
|
||||
<ia:DataTriggerBehavior
|
||||
Binding="{Binding VideoInfoView.LikeNumber}"
|
||||
ComparisonCondition="LessThanOrEqual"
|
||||
Value="0">
|
||||
<ia:ChangePropertyAction
|
||||
PropertyName="IsVisible"
|
||||
TargetObject="{Binding #LikeNumber}"
|
||||
Value="False" />
|
||||
</ia:DataTriggerBehavior>
|
||||
</i:Interaction.Behaviors>
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel
|
||||
Name="CoinNumber"
|
||||
Margin="0,0,10,0"
|
||||
Orientation="Horizontal">
|
||||
<TextBlock
|
||||
FontSize="12"
|
||||
Foreground="{DynamicResource BrushTextGrey}"
|
||||
Text="{Binding VideoInfoView.CoinNumber}" />
|
||||
<TextBlock
|
||||
FontSize="12"
|
||||
Foreground="{DynamicResource BrushTextGrey}"
|
||||
Text="{DynamicResource Coin}" />
|
||||
<i:Interaction.Behaviors>
|
||||
<ia:DataTriggerBehavior
|
||||
Binding="{Binding VideoInfoView.CoinNumber}"
|
||||
ComparisonCondition="LessThanOrEqual"
|
||||
Value="0">
|
||||
<ia:ChangePropertyAction
|
||||
PropertyName="IsVisible"
|
||||
TargetObject="{Binding #CoinNumber}"
|
||||
Value="False" />
|
||||
</ia:DataTriggerBehavior>
|
||||
</i:Interaction.Behaviors>
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel
|
||||
Name="FavoriteNumber"
|
||||
Margin="0,0,10,0"
|
||||
Orientation="Horizontal">
|
||||
<TextBlock
|
||||
FontSize="12"
|
||||
Foreground="{DynamicResource BrushTextGrey}"
|
||||
Text="{Binding VideoInfoView.FavoriteNumber}" />
|
||||
<TextBlock
|
||||
FontSize="12"
|
||||
Foreground="{DynamicResource BrushTextGrey}"
|
||||
Text="{DynamicResource Favorite}" />
|
||||
<i:Interaction.Behaviors>
|
||||
<ia:DataTriggerBehavior
|
||||
Binding="{Binding VideoInfoView.FavoriteNumber}"
|
||||
ComparisonCondition="LessThanOrEqual"
|
||||
Value="0">
|
||||
<ia:ChangePropertyAction
|
||||
PropertyName="IsVisible"
|
||||
TargetObject="{Binding #FavoriteNumber}"
|
||||
Value="False" />
|
||||
</ia:DataTriggerBehavior>
|
||||
</i:Interaction.Behaviors>
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel
|
||||
Name="ShareNumber"
|
||||
Margin="0,0,10,0"
|
||||
Orientation="Horizontal">
|
||||
<TextBlock
|
||||
FontSize="12"
|
||||
Foreground="{DynamicResource BrushTextGrey}"
|
||||
Text="{Binding VideoInfoView.ShareNumber}" />
|
||||
<TextBlock
|
||||
FontSize="12"
|
||||
Foreground="{DynamicResource BrushTextGrey}"
|
||||
Text="{DynamicResource Share}" />
|
||||
<i:Interaction.Behaviors>
|
||||
<ia:DataTriggerBehavior
|
||||
Binding="{Binding VideoInfoView.ShareNumber}"
|
||||
ComparisonCondition="LessThanOrEqual"
|
||||
Value="0">
|
||||
<ia:ChangePropertyAction
|
||||
PropertyName="IsVisible"
|
||||
TargetObject="{Binding #ShareNumber}"
|
||||
Value="False" />
|
||||
</ia:DataTriggerBehavior>
|
||||
</i:Interaction.Behaviors>
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel
|
||||
Name="ReplyNumber"
|
||||
Margin="0,0,10,0"
|
||||
Orientation="Horizontal">
|
||||
<TextBlock
|
||||
FontSize="12"
|
||||
Foreground="{DynamicResource BrushTextGrey}"
|
||||
Text="{Binding VideoInfoView.ReplyNumber}" />
|
||||
<TextBlock
|
||||
FontSize="12"
|
||||
Foreground="{DynamicResource BrushTextGrey}"
|
||||
Text="{DynamicResource Reply}" />
|
||||
<i:Interaction.Behaviors>
|
||||
<ia:DataTriggerBehavior
|
||||
Binding="{Binding VideoInfoView.ReplyNumber}"
|
||||
ComparisonCondition="LessThanOrEqual"
|
||||
Value="0">
|
||||
<ia:ChangePropertyAction
|
||||
PropertyName="IsVisible"
|
||||
TargetObject="{Binding #ReplyNumber}"
|
||||
Value="False" />
|
||||
</ia:DataTriggerBehavior>
|
||||
</i:Interaction.Behaviors>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
|
||||
<StackPanel
|
||||
Name="nameUp"
|
||||
Grid.Column="1"
|
||||
Cursor="Hand"
|
||||
ToolTip.Tip="{Binding VideoInfoView.UpName}">
|
||||
<i:Interaction.Behaviors>
|
||||
<ia:EventTriggerBehavior EventName="Tapped">
|
||||
<ia:InvokeCommandAction Command="{Binding UpperCommand}" />
|
||||
</ia:EventTriggerBehavior>
|
||||
</i:Interaction.Behaviors>
|
||||
|
||||
<Image
|
||||
Width="48"
|
||||
Height="48"
|
||||
asyncImageLoader:ImageLoader.Source="{Binding VideoInfoView.UpHeader}">
|
||||
<Image.Clip>
|
||||
<EllipseGeometry
|
||||
Center="24,24"
|
||||
RadiusX="24"
|
||||
RadiusY="24" />
|
||||
</Image.Clip>
|
||||
</Image>
|
||||
<TextBlock
|
||||
Margin="0,2,0,0"
|
||||
HorizontalAlignment="Center"
|
||||
FontSize="12"
|
||||
Foreground="{DynamicResource BrushTextDark}"
|
||||
Text="{Binding VideoInfoView.UpName}"
|
||||
TextTrimming="CharacterEllipsis" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
|
||||
<ScrollViewer
|
||||
Grid.Row="1"
|
||||
Margin="0,5,0,0"
|
||||
VerticalScrollBarVisibility="Auto">
|
||||
<TextBox
|
||||
Background="{x:Null}"
|
||||
BorderBrush="{x:Null}"
|
||||
BorderThickness="0"
|
||||
IsReadOnly="True"
|
||||
Text="{Binding VideoInfoView.Description}"
|
||||
TextWrapping="WrapWithOverflow" />
|
||||
</ScrollViewer>
|
||||
</Grid>
|
||||
</DockPanel>
|
||||
|
||||
<Grid
|
||||
Grid.Row="1"
|
||||
Margin="10"
|
||||
RowDefinitions="*,40" />
|
||||
|
||||
</Grid>
|
||||
|
||||
</Grid>
|
||||
</UserControl>
|
12
src/Downkyi/Views/Video/VideoDetailView.axaml.cs
Normal file
12
src/Downkyi/Views/Video/VideoDetailView.axaml.cs
Normal file
@ -0,0 +1,12 @@
|
||||
using Avalonia.Controls;
|
||||
|
||||
namespace Downkyi.Views.Video
|
||||
{
|
||||
public partial class VideoDetailView : UserControl
|
||||
{
|
||||
public VideoDetailView()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user