diff --git a/DownKyi.Core/Aria2cNet/Client/AriaClient.cs b/DownKyi.Core/Aria2cNet/Client/AriaClient.cs
index 77083a4..86bf74a 100644
--- a/DownKyi.Core/Aria2cNet/Client/AriaClient.cs
+++ b/DownKyi.Core/Aria2cNet/Client/AriaClient.cs
@@ -16,7 +16,12 @@ namespace DownKyi.Core.Aria2cNet.Client
public static class AriaClient
{
private static readonly string JSONRPC = "2.0";
- private static readonly string TOKEN = "downkyi";
+ private const string LOCAL_HOST = "http://localhost";
+ private const string TOKEN = "downkyi";
+ private const int LISTEN_PORT = 6800;
+ private static string host = LOCAL_HOST;
+ private static string token = TOKEN;
+ private static int listenPort = LISTEN_PORT;
///
/// This method adds a new download.
@@ -41,7 +46,7 @@ namespace DownKyi.Core.Aria2cNet.Client
{
List
///
///
- public bool SetVideoCodecs(VideoCodecs videoCodecs)
+ public bool SetVideoCodecs(int videoCodecs)
{
appSettings.Video.VideoCodecs = videoCodecs;
return SetSettings();
diff --git a/DownKyi/App.xaml.cs b/DownKyi/App.xaml.cs
index 91687fc..42f74b4 100644
--- a/DownKyi/App.xaml.cs
+++ b/DownKyi/App.xaml.cs
@@ -134,6 +134,9 @@ namespace DownKyi
case Downloader.ARIA:
downloadService = new AriaDownloadService(DownloadingList, DownloadedList);
break;
+ case Downloader.CUSTOM_ARIA:
+ downloadService = new CustomAriaDownloadService(DownloadingList, DownloadedList);
+ break;
}
if (downloadService != null)
{
@@ -182,6 +185,7 @@ namespace DownKyi
containerRegistry.RegisterForNavigation(ViewUserSpaceViewModel.Tag);
containerRegistry.RegisterForNavigation(ViewPublicationViewModel.Tag);
containerRegistry.RegisterForNavigation(ViewModels.ViewChannelViewModel.Tag);
+ containerRegistry.RegisterForNavigation(ViewModels.ViewSeasonsSeriesViewModel.Tag);
containerRegistry.RegisterForNavigation(ViewFriendViewModel.Tag);
containerRegistry.RegisterForNavigation(ViewMySpaceViewModel.Tag);
@@ -213,6 +217,7 @@ namespace DownKyi
// UserSpace
containerRegistry.RegisterForNavigation(ViewArchiveViewModel.Tag);
containerRegistry.RegisterForNavigation(ViewModels.UserSpace.ViewChannelViewModel.Tag);
+ containerRegistry.RegisterForNavigation(ViewModels.UserSpace.ViewSeasonsSeriesViewModel.Tag);
// dialogs
containerRegistry.RegisterDialog(ViewAlertDialogViewModel.Tag);
diff --git a/DownKyi/DownKyi.csproj b/DownKyi/DownKyi.csproj
index 2ebe700..788877d 100644
--- a/DownKyi/DownKyi.csproj
+++ b/DownKyi/DownKyi.csproj
@@ -117,6 +117,7 @@
+
@@ -166,12 +167,15 @@
+
+
+
@@ -243,9 +247,15 @@
ViewArchive.xaml
+
+ ViewSeasonsSeries.xaml
+
ViewChannel.xaml
+
+ ViewSeasonsSeries.xaml
+
ViewChannel.xaml
@@ -446,10 +456,18 @@
Designer
MSBuild:Compile
+
+ MSBuild:Compile
+ Designer
+
Designer
MSBuild:Compile
+
+ MSBuild:Compile
+ Designer
+
Designer
MSBuild:Compile
diff --git a/DownKyi/Images/NormalIcon.cs b/DownKyi/Images/NormalIcon.cs
index 7977bf2..669c798 100644
--- a/DownKyi/Images/NormalIcon.cs
+++ b/DownKyi/Images/NormalIcon.cs
@@ -1,4 +1,6 @@
-namespace DownKyi.Images
+using DownKyi.ViewModels.UserSpace;
+
+namespace DownKyi.Images
{
public class NormalIcon
{
@@ -267,6 +269,46 @@
Fill = "#FF000000"
};
+ Channel1 = new VectorImage
+ {
+ Height = 24,
+ Width = 24,
+ Data = @"M116.57 0 q-30.86 0 -57.71 16 q-26.85 16 -42.85 42.85 q-16 26.85 -16 57.71 l0 468.57 q0 30.86 16 57.72
+ q16 26.86 42.85 42.86 q26.85 16 57.71 16 l643.43 0 q32 0 58.86 -16 q26.86 -16 42.86 -42.86
+ q16 -26.86 16 -57.72 l0 -468.57 q0 -30.86 -16 -57.71 q-16 -26.85 -42.86 -42.85 q-26.86 -16 -58.86 -16
+ l-643.43 0 ZM262.86 848 q-40 0 -72.57 -25.71 q-32.58 -25.72 -39.43 -62.29 l697.14 0 q36.57 0 62.29 -25.71
+ q25.71 -25.72 25.71 -62.29 l0 -521.14 q37.71 11.43 62.86 42.28 q25.14 30.86 25.14 69.72 l0 468.57
+ q0 30.86 -16 57.72 q-16 26.86 -42.86 42.86 q-26.86 16 -58.86 16 l-643.43 0 ZM88 174.86
+ q0 -36.57 25.14 -62.29 q25.14 -25.71 61.72 -25.71 l526.85 0 q36.58 0 62.29 25.71 q25.71 25.72 25.71 62.29
+ l0 350.85 q0 36.58 -25.71 62.29 q-25.71 25.71 -62.29 25.71 l-526.85 0 q-36.57 0 -61.72 -25.71
+ q-25.14 -25.71 -25.14 -62.29 l0 -350.85 ZM609.14 374.86 q11.43 -6.86 11.43 -22.29 q0 -15.43 -11.43 -26.86
+ l-214.85 -146.28 q-13.72 -10.29 -28.57 -1.72 q-14.86 8.58 -14.86 26.86 l0 297.14 q0 18.29 14.86 26.86
+ q14.85 8.57 28.57 -2.86 l214.85 -150.85 Z",
+ Fill = "#FF000000"
+ };
+
+ SeasonsSeries = new VectorImage
+ {
+ Height = 24,
+ Width = 24,
+ Data = @"M974.2 382.22 l-401.47 177.37 q-28.88 13.75 -60.5 13.75 q-31.62 0 -60.5 -13.75 l-400.09 -177.37
+ q-20.63 -11 -33 -30.25 q-12.38 -19.25 -13.06 -43.31 q-0.68 -24.06 10.32 -45.38 q11 -21.32 31.62 -35.07
+ l402.85 -210.36 q28.87 -17.87 63.25 -17.87 q34.38 0 63.25 19.25 l398.72 207.61 q22 12.37 33.69 34.37
+ q11.68 22 10.31 46.06 q-1.38 24.06 -13.06 44 q-11.69 19.94 -32.31 30.94 ZM939.83 292.86 l-400.1 -207.62
+ q-11 -8.25 -24.75 -9.62 q-16.5 1.37 -30.25 9.62 l-400.1 207.62 q-2.75 6.87 -2.75 12.37 q0 5.5 4.13 9.62
+ l397.35 175.99 q20.62 9.63 28.18 9.63 q7.56 0 26.81 -8.25 l404.22 -178.74 q1.38 -11 -4.12 -20.62
+ l1.38 0 ZM20.01 512.84 q8.25 -4.12 19.25 -4.12 q11 0 19.25 5.5 q16.5 9.62 39.87 20.62 l413.85 186.99
+ l394.6 -178.74 l60.49 -30.25 q9.63 -4.12 19.94 -4.12 q10.31 0 19.25 5.5 q8.94 5.5 13.75 15.12
+ q4.81 9.63 3.43 19.25 q-1.37 23.37 -21.99 31.62 l-64.62 31.62 l-424.85 193.87 l-422.1 -192.49
+ q-35.74 -12.38 -68.74 -33 q-9.63 -5.5 -15.13 -14.43 q-5.5 -8.94 -6.18 -19.25 q-0.69 -10.32 4.82 -19.25
+ q5.5 -8.94 15.12 -14.44 l0 0 ZM58.51 732.83 q17.87 9.62 41.25 20.62 l412.47 186.99 l394.6 -178.74
+ l60.49 -30.25 q9.63 -5.5 19.94 -4.81 q10.31 0.69 19.25 6.18 q8.94 5.5 13.75 15.12 q4.81 9.63 3.43 19.94
+ q-1.37 10.32 -6.87 18.57 q-5.5 8.25 -15.12 12.37 l-64.62 31.62 l-424.85 192.49 l-422.1 -191.11
+ q-35.74 -13.75 -68.74 -34.37 q-13.75 -6.88 -17.88 -22 q-4.13 -15.13 3.44 -28.87 q7.56 -13.75 22.68 -17.87
+ q15.13 -4.13 28.88 4.13 Z",
+ Fill = "#FF000000"
+ };
+
PlatformIpad = new VectorImage
{
Height = 16,
@@ -357,6 +399,8 @@
public VectorImage VideoUp { get; private set; }
public VectorImage Channel { get; private set; }
+ public VectorImage Channel1 { get; private set; }
+ public VectorImage SeasonsSeries { get; private set; }
public VectorImage PlatformIpad { get; private set; }
public VectorImage PlatformMobile { get; private set; }
diff --git a/DownKyi/Languages/Default.xaml b/DownKyi/Languages/Default.xaml
index 52a6fed..cc01956 100644
--- a/DownKyi/Languages/Default.xaml
+++ b/DownKyi/Languages/Default.xaml
@@ -63,6 +63,7 @@
全部
投稿视频
频道
+ 合集和列表
请稍等,马上就好~
@@ -192,7 +193,10 @@
选择下载器(重启生效):
内建下载器(测试)
Aria2下载器
+ 自定义Aria2下载器
+ Aria服务器地址:
Aria服务器端口:
+ Aria服务器Token:
Aria日志等级:
Aria同时下载数:
Aria最大线程数:
diff --git a/DownKyi/Models/AppInfo.cs b/DownKyi/Models/AppInfo.cs
index 22c0c6d..f610f8a 100644
--- a/DownKyi/Models/AppInfo.cs
+++ b/DownKyi/Models/AppInfo.cs
@@ -3,12 +3,12 @@
public class AppInfo
{
public string Name { get; } = "哔哩下载姬";
- public int VersionCode { get; } = 510;
+ public int VersionCode { get; } = 511;
#if DEBUG
- public string VersionName { get; } = "1.5.3 Debug";
+ public string VersionName { get; } = "1.5.4 Debug";
#else
- public string VersionName { get; } = "1.5.3";
+ public string VersionName { get; } = "1.5.4";
#endif
}
diff --git a/DownKyi/Properties/AssemblyInfo.cs b/DownKyi/Properties/AssemblyInfo.cs
index cbf3fe1..5a996db 100644
--- a/DownKyi/Properties/AssemblyInfo.cs
+++ b/DownKyi/Properties/AssemblyInfo.cs
@@ -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.3.0")]
-[assembly: AssemblyFileVersion("1.5.3.0")]
+[assembly: AssemblyVersion("1.5.4.0")]
+[assembly: AssemblyFileVersion("1.5.4.0")]
diff --git a/DownKyi/Services/Download/AriaDownloadService.cs b/DownKyi/Services/Download/AriaDownloadService.cs
index 34ef063..03925b2 100644
--- a/DownKyi/Services/Download/AriaDownloadService.cs
+++ b/DownKyi/Services/Download/AriaDownloadService.cs
@@ -232,6 +232,13 @@ namespace DownKyi.Services.Download
///
public void Start()
{
+ // 设置aria token
+ AriaClient.SetToken();
+ // 设置aria host
+ AriaClient.SetHost();
+ // 设置aria listenPort
+ AriaClient.SetListenPort();
+
// 启动Aria服务器
StartAriaServer();
diff --git a/DownKyi/Services/Download/CustomAriaDownloadService.cs b/DownKyi/Services/Download/CustomAriaDownloadService.cs
new file mode 100644
index 0000000..5dbf0c4
--- /dev/null
+++ b/DownKyi/Services/Download/CustomAriaDownloadService.cs
@@ -0,0 +1,442 @@
+using DownKyi.Core.Aria2cNet;
+using DownKyi.Core.Aria2cNet.Client;
+using DownKyi.Core.Aria2cNet.Client.Entity;
+using DownKyi.Core.Aria2cNet.Server;
+using DownKyi.Core.BiliApi.VideoStream.Models;
+using DownKyi.Core.Logging;
+using DownKyi.Core.Settings;
+using DownKyi.Core.Utils;
+using DownKyi.Models;
+using DownKyi.Utils;
+using DownKyi.ViewModels.DownloadManager;
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.IO;
+using System.Linq;
+using System.Threading.Tasks;
+
+namespace DownKyi.Services.Download
+{
+ ///
+ /// 音视频采用Aria下载,其余采用WebClient下载
+ ///
+ public class CustomAriaDownloadService : DownloadService, IDownloadService
+ {
+ public CustomAriaDownloadService(ObservableCollection downloadingList, ObservableCollection downloadedList) : base(downloadingList, downloadedList)
+ {
+ Tag = "AriaDownloadService";
+ }
+
+ #region 音视频
+
+ ///
+ /// 下载音频,返回下载文件路径
+ ///
+ ///
+ ///
+ public override string DownloadAudio(DownloadingItem downloading)
+ {
+ PlayUrlDashVideo downloadAudio = BaseDownloadAudio(downloading);
+
+ return DownloadVideo(downloading, downloadAudio);
+ }
+
+ ///
+ /// 下载视频,返回下载文件路径
+ ///
+ ///
+ ///
+ public override string DownloadVideo(DownloadingItem downloading)
+ {
+ PlayUrlDashVideo downloadVideo = BaseDownloadVideo(downloading);
+
+ return DownloadVideo(downloading, downloadVideo);
+ }
+
+ ///
+ /// 将下载音频和视频的函数中相同代码抽象出来
+ ///
+ ///
+ ///
+ ///
+ private string DownloadVideo(DownloadingItem downloading, PlayUrlDashVideo downloadVideo)
+ {
+ // 如果为空,说明没有匹配到可下载的音频视频
+ if (downloadVideo == null) { return null; }
+
+ // 下载链接
+ List urls = new List();
+ if (downloadVideo.BaseUrl != null) { urls.Add(downloadVideo.BaseUrl); }
+ if (downloadVideo.BackupUrl != null) { urls.AddRange(downloadVideo.BackupUrl); }
+
+ // 路径
+ downloading.DownloadBase.FilePath = downloading.DownloadBase.FilePath.Replace("\\", "/");
+ string[] temp = downloading.DownloadBase.FilePath.Split('/');
+ //string path = downloading.DownloadBase.FilePath.Replace(temp[temp.Length - 1], "");
+ string path = downloading.DownloadBase.FilePath.TrimEnd(temp[temp.Length - 1].ToCharArray());
+
+ // 下载文件名
+ string fileName = Guid.NewGuid().ToString("N");
+ string key = $"{downloadVideo.Id}_{downloadVideo.Codecs}";
+
+ // 老版本数据库没有这一项,会变成null
+ if (downloading.Downloading.DownloadedFiles == null)
+ {
+ downloading.Downloading.DownloadedFiles = new List();
+ }
+
+ if (downloading.Downloading.DownloadFiles.ContainsKey(key))
+ {
+ // 如果存在,表示下载过,
+ // 则继续使用上次下载的文件名
+ fileName = downloading.Downloading.DownloadFiles[key];
+
+ // 还要检查一下文件有没有被人删掉,删掉的话重新下载
+ // 如果下载视频之后音频文件被人删了。此时gid还是视频的,会下错文件
+ if (downloading.Downloading.DownloadedFiles.Contains(key) && File.Exists(Path.Combine(path, fileName)))
+ {
+ return Path.Combine(path, fileName);
+ }
+ }
+ else
+ {
+ // 记录本次下载的文件
+ try
+ {
+ downloading.Downloading.DownloadFiles.Add(key, fileName);
+ }
+ catch (ArgumentException) { }
+ // Gid最好能是每个文件单独存储,现在复用有可能会混
+ // 不过好消息是下载是按固定顺序的,而且下载了两个音频会混流不过
+ downloading.Downloading.Gid = null;
+ }
+
+ // 启用https
+ AllowStatus useSSL = SettingsManager.GetInstance().UseSSL();
+ if (useSSL == AllowStatus.YES)
+ {
+ for (int i = 0; i < urls.Count; i++)
+ {
+ string url = urls[i];
+ if (url.StartsWith("http://"))
+ {
+ urls[i] = url.Replace("http://", "https://");
+ }
+ }
+ }
+ else
+ {
+ for (int i = 0; i < urls.Count; i++)
+ {
+ string url = urls[i];
+ if (url.StartsWith("https://"))
+ {
+ urls[i] = url.Replace("https://", "http://");
+ }
+ }
+ }
+
+ // 开始下载
+ DownloadResult downloadStatus = DownloadByAria(downloading, urls, path, fileName);
+ switch (downloadStatus)
+ {
+ case DownloadResult.SUCCESS:
+ downloading.Downloading.DownloadedFiles.Add(key);
+ downloading.Downloading.Gid = null;
+ return Path.Combine(path, fileName);
+ case DownloadResult.FAILED:
+ case DownloadResult.ABORT:
+ default:
+ return nullMark;
+ }
+ }
+
+ #endregion
+
+ ///
+ /// 下载封面
+ ///
+ ///
+ public override string DownloadCover(DownloadingItem downloading, string coverUrl, string fileName)
+ {
+ return BaseDownloadCover(downloading, coverUrl, fileName);
+ }
+
+ ///
+ /// 下载弹幕
+ ///
+ ///
+ public override string DownloadDanmaku(DownloadingItem downloading)
+ {
+ return BaseDownloadDanmaku(downloading);
+ }
+
+ ///
+ /// 下载字幕
+ ///
+ ///
+ public override List DownloadSubtitle(DownloadingItem downloading)
+ {
+ return BaseDownloadSubtitle(downloading);
+ }
+
+ ///
+ /// 混流音频和视频
+ ///
+ ///
+ ///
+ ///
+ ///
+ public override string MixedFlow(DownloadingItem downloading, string audioUid, string videoUid)
+ {
+ if (videoUid == nullMark)
+ {
+ return null;
+ }
+ return BaseMixedFlow(downloading, audioUid, videoUid);
+ }
+
+ ///
+ /// 解析视频流的下载链接
+ ///
+ ///
+ public override void Parse(DownloadingItem downloading)
+ {
+ BaseParse(downloading);
+ }
+
+ ///
+ /// 停止下载服务(转换await和Task.Wait两种调用形式)
+ ///
+ private async Task EndTask()
+ {
+ // 停止基本任务
+ await BaseEndTask();
+
+ // 关闭Aria服务器
+ await CloseAriaServer();
+ }
+
+ ///
+ /// 停止下载服务
+ ///
+ public void End()
+ {
+ Task.Run(EndTask).Wait();
+ }
+
+ ///
+ /// 启动下载服务
+ ///
+ public void Start()
+ {
+ // 设置aria token
+ AriaClient.SetToken(SettingsManager.GetInstance().GetAriaToken());
+ // 设置aria host
+ AriaClient.SetHost(SettingsManager.GetInstance().GetAriaHost());
+ // 设置aria listenPort
+ AriaClient.SetListenPort(SettingsManager.GetInstance().GetAriaListenPort());
+
+ // 启动基本服务
+ BaseStart();
+ }
+
+ ///
+ /// 强制暂停
+ ///
+ ///
+ ///
+ protected override void Pause(DownloadingItem downloading)
+ {
+ cancellationToken.ThrowIfCancellationRequested();
+
+ downloading.DownloadStatusTitle = DictionaryResource.GetString("Pausing");
+ if (downloading.Downloading.DownloadStatus == DownloadStatus.PAUSE)
+ {
+ throw new OperationCanceledException("Stop thread by pause");
+ }
+ // 是否存在
+ var isExist = IsExist(downloading);
+ if (!isExist.Result)
+ {
+ throw new OperationCanceledException("Task is deleted");
+ }
+ }
+
+ ///
+ /// 是否存在于下载列表中
+ ///
+ ///
+ ///
+ private async Task IsExist(DownloadingItem downloading)
+ {
+ bool isExist = downloadingList.Contains(downloading);
+ if (isExist)
+ {
+ return true;
+ }
+ else
+ {
+ // 先恢复为waiting状态,暂停状态下Remove会导致文件重新下载,原因暂不清楚
+ await AriaClient.UnpauseAsync(downloading.Downloading.Gid);
+ // 移除下载项
+ var ariaRemove = await AriaClient.RemoveAsync(downloading.Downloading.Gid);
+ if (ariaRemove == null || ariaRemove.Result == downloading.Downloading.Gid)
+ {
+ // 从内存中删除下载项
+ await AriaClient.RemoveDownloadResultAsync(downloading.Downloading.Gid);
+ }
+
+ return false;
+ }
+ }
+
+ ///
+ /// 关闭Aria服务器
+ ///
+ private async Task CloseAriaServer()
+ {
+ // 暂停所有下载
+ var ariaPause = await AriaClient.PauseAllAsync();
+#if DEBUG
+ Core.Utils.Debugging.Console.PrintLine(ariaPause.ToString());
+#endif
+
+ // 关闭服务器
+ bool close = AriaServer.CloseServer();
+#if DEBUG
+ Core.Utils.Debugging.Console.PrintLine(close);
+#endif
+ }
+
+ ///
+ /// 采用Aria下载文件
+ ///
+ ///
+ ///
+ private DownloadResult DownloadByAria(DownloadingItem downloading, List urls, string path, string localFileName)
+ {
+ // path已斜杠结尾,去掉斜杠
+ path = path.TrimEnd('/').TrimEnd('\\');
+
+ //检查gid对应任务,如果已创建那么直接使用
+ //但是代理设置会出现不能随时更新的问题
+
+ if (downloading.Downloading.Gid != null)
+ {
+ Task status = AriaClient.TellStatus(downloading.Downloading.Gid);
+ if (status == null || status.Result == null)
+ downloading.Downloading.Gid = null;
+ else if (status.Result.Result == null && status.Result.Error != null)
+ {
+ if (status.Result.Error.Message.Contains("is not found"))
+ {
+ downloading.Downloading.Gid = null;
+ }
+ }
+
+ }
+
+ if (downloading.Downloading.Gid == null)
+ {
+ AriaSendOption option = new AriaSendOption
+ {
+ //HttpProxy = $"http://{Settings.GetAriaHttpProxy()}:{Settings.GetAriaHttpProxyListenPort()}",
+ Dir = path,
+ Out = localFileName,
+ //Header = $"cookie: {LoginHelper.GetLoginInfoCookiesString()}\nreferer: https://www.bilibili.com",
+ //UseHead = "true",
+ UserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.4951.54 Safari/537.36",
+ };
+
+ //// 如果设置了代理,则增加HttpProxy
+ //if (SettingsManager.GetInstance().IsAriaHttpProxy() == AllowStatus.YES)
+ //{
+ // option.HttpProxy = $"http://{SettingsManager.GetInstance().GetAriaHttpProxy()}:{SettingsManager.GetInstance().GetAriaHttpProxyListenPort()}";
+ //}
+
+ // 添加一个下载
+ Task ariaAddUri = AriaClient.AddUriAsync(urls, option);
+ if (ariaAddUri == null || ariaAddUri.Result == null || ariaAddUri.Result.Result == null)
+ {
+ return DownloadResult.FAILED;
+ }
+
+ // 保存gid
+ string gid = ariaAddUri.Result.Result;
+ downloading.Downloading.Gid = gid;
+ }
+ else
+ {
+ Task ariaUnpause = AriaClient.UnpauseAsync(downloading.Downloading.Gid);
+ }
+
+ // 管理下载
+ AriaManager ariaManager = new AriaManager();
+ ariaManager.TellStatus += AriaTellStatus;
+ ariaManager.DownloadFinish += AriaDownloadFinish;
+ return ariaManager.GetDownloadStatus(downloading.Downloading.Gid, new Action(() =>
+ {
+ cancellationToken.ThrowIfCancellationRequested();
+ switch (downloading.Downloading.DownloadStatus)
+ {
+ case DownloadStatus.PAUSE:
+ Task ariaPause = AriaClient.PauseAsync(downloading.Downloading.Gid);
+ // 通知UI,并阻塞当前线程
+ Pause(downloading);
+ break;
+ case DownloadStatus.DOWNLOADING:
+ break;
+ }
+ }));
+ }
+
+ private void AriaTellStatus(long totalLength, long completedLength, long speed, string gid)
+ {
+ // 当前的下载视频
+ DownloadingItem video = null;
+ try
+ {
+ video = downloadingList.FirstOrDefault(it => it.Downloading.Gid == gid);
+ }
+ catch (InvalidOperationException e)
+ {
+ Core.Utils.Debugging.Console.PrintLine("AriaTellStatus()发生异常: {0}", e);
+ LogManager.Error("AriaTellStatus()", e);
+ }
+
+ if (video == null) { return; }
+
+ // 下载进度百分比
+ float percent = 0;
+ if (totalLength != 0)
+ {
+ percent = (float)completedLength / totalLength * 100;
+ }
+
+ // 根据进度判断本次是否需要更新UI
+ if (Math.Abs(percent - video.Progress) < 0.01) { return; }
+
+ // 下载进度
+ video.Progress = percent;
+
+ // 下载大小
+ video.DownloadingFileSize = Format.FormatFileSize(completedLength) + "/" + Format.FormatFileSize(totalLength);
+
+ // 下载速度
+ video.SpeedDisplay = Format.FormatSpeed(speed);
+
+ // 最大下载速度
+ if (video.Downloading.MaxSpeed < speed)
+ {
+ video.Downloading.MaxSpeed = speed;
+ }
+ }
+
+ private void AriaDownloadFinish(bool isSuccess, string downloadPath, string gid, string msg)
+ {
+ //throw new NotImplementedException();
+ }
+ }
+}
diff --git a/DownKyi/Services/Download/DownloadService.cs b/DownKyi/Services/Download/DownloadService.cs
index 2af551b..49a7c60 100644
--- a/DownKyi/Services/Download/DownloadService.cs
+++ b/DownKyi/Services/Download/DownloadService.cs
@@ -1,4 +1,5 @@
-using DownKyi.Core.BiliApi.VideoStream;
+using DownKyi.Core.BiliApi.BiliUtils;
+using DownKyi.Core.BiliApi.VideoStream;
using DownKyi.Core.BiliApi.VideoStream.Models;
using DownKyi.Core.Danmaku2Ass;
using DownKyi.Core.FFmpeg;
@@ -14,6 +15,7 @@ using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;
+using System.Linq;
using System.Threading;
using System.Threading.Tasks;
@@ -82,6 +84,11 @@ namespace DownKyi.Services.Download
{
downloadAudio = downloading.PlayUrl.Dash.Dolby.Audio[0];
}
+ // Hi-Res无损
+ if (downloading.AudioCodec.Id == 30251)
+ {
+ downloadAudio = downloading.PlayUrl.Dash.Flac.Audio;
+ }
}
catch (Exception) { }
@@ -110,7 +117,8 @@ namespace DownKyi.Services.Download
PlayUrlDashVideo downloadVideo = null;
foreach (PlayUrlDashVideo video in downloading.PlayUrl.Dash.Video)
{
- if (video.Id == downloading.Resolution.Id && Utils.GetVideoCodecName(video.Codecs) == downloading.VideoCodecName)
+ Quality codecs = Constant.GetCodecIds().FirstOrDefault(t => t.Id == video.CodecId);
+ if (video.Id == downloading.Resolution.Id && codecs.Name == downloading.VideoCodecName)
{
downloadVideo = video;
break;
diff --git a/DownKyi/Services/Utils.cs b/DownKyi/Services/Utils.cs
index 13f1206..b3e055a 100644
--- a/DownKyi/Services/Utils.cs
+++ b/DownKyi/Services/Utils.cs
@@ -30,7 +30,7 @@ namespace DownKyi.Services
// 获取设置
UserInfoSettings userInfo = SettingsManager.GetInstance().GetUserInfo();
int defaultQuality = SettingsManager.GetInstance().GetQuality();
- VideoCodecs videoCodecs = SettingsManager.GetInstance().GetVideoCodecs();
+ int videoCodecs = SettingsManager.GetInstance().GetVideoCodecs();
int defaultAudioQuality = SettingsManager.GetInstance().GetAudioQuality();
// 未登录时,最高仅720P
@@ -95,7 +95,7 @@ namespace DownKyi.Services
///
///
///
- private static ObservableCollection GetAudioQualityFormatList(PlayUrl playUrl, int defaultAudioQuality)
+ private static ObservableCollection GetAudioQualityFormatList_old(PlayUrl playUrl, int defaultAudioQuality)
{
List audioQualityFormatList = new List();
@@ -122,6 +122,55 @@ namespace DownKyi.Services
return new ObservableCollection(audioQualityFormatList);
}
+
+ ///
+ /// 设置音质
+ ///
+ ///
+ ///
+ ///
+ private static ObservableCollection GetAudioQualityFormatList(PlayUrl playUrl, int defaultAudioQuality)
+ {
+ List audioQualityFormatList = new List();
+ List audioQualities = Constant.GetAudioQualities();
+
+ if (playUrl.Dash.Audio != null && playUrl.Dash.Audio.Count > 0)
+ {
+ foreach (PlayUrlDashVideo audio in playUrl.Dash.Audio)
+ {
+ // 音质id大于设置音质时,跳过
+ if (audio.Id > defaultAudioQuality) { continue; }
+
+ Quality audioQuality = audioQualities.FirstOrDefault(t => { return t.Id == audio.Id; });
+ if (audioQuality != null)
+ {
+ ListHelper.AddUnique(audioQualityFormatList, audioQuality.Name);
+ }
+ }
+ }
+
+ if (audioQualities[3].Id <= defaultAudioQuality - 1000 && playUrl.Dash.Dolby != null)
+ {
+ if (playUrl.Dash.Dolby.Audio != null && playUrl.Dash.Dolby.Audio.Count > 0)
+ {
+ ListHelper.AddUnique(audioQualityFormatList, audioQualities[3].Name);
+ }
+ }
+
+ if (audioQualities[4].Id <= defaultAudioQuality - 1000 && playUrl.Dash.Flac != null)
+ {
+ if (playUrl.Dash.Flac.Audio != null)
+ {
+ ListHelper.AddUnique(audioQualityFormatList, audioQualities[4].Name);
+ }
+ }
+
+ audioQualityFormatList.Sort(new StringLogicalComparer());
+ audioQualityFormatList.Reverse();
+
+ return new ObservableCollection(audioQualityFormatList);
+ }
+
///
/// 设置画质 & 视频编码
///
@@ -130,9 +179,10 @@ namespace DownKyi.Services
///
///
///
- private static List GetVideoQualityList(PlayUrl playUrl, UserInfoSettings userInfo, int defaultQuality, VideoCodecs videoCodecs)
+ private static List GetVideoQualityList(PlayUrl playUrl, UserInfoSettings userInfo, int defaultQuality, int videoCodecs)
{
List videoQualityList = new List();
+ List codeIds = Constant.GetCodecIds();
if (playUrl.Dash.Video == null)
{
@@ -160,7 +210,8 @@ namespace DownKyi.Services
// 寻找是否已存在这个画质
// 不存在则添加,存在则修改
- string codecName = GetVideoCodecName(video.Codecs);
+ //string codecName = GetVideoCodecName(video.Codecs);
+ string codecName = codeIds.FirstOrDefault(t => t.Id == video.CodecId).Name;
VideoQuality videoQualityExist = videoQualityList.FirstOrDefault(t => t.Quality == video.Id);
if (videoQualityExist == null)
{
@@ -191,7 +242,7 @@ namespace DownKyi.Services
// 设置选中的视频编码
VideoQuality selectedVideoQuality = videoQualityList.FirstOrDefault(t => t.Quality == video.Id);
- if(selectedVideoQuality == null) { continue; }
+ if (selectedVideoQuality == null) { continue; }
if (videoQualityList[videoQualityList.IndexOf(selectedVideoQuality)].VideoCodecList.Count == 1)
{
@@ -200,24 +251,29 @@ namespace DownKyi.Services
}
// 设置选中的视频编码
- switch (videoCodecs)
+ //switch (videoCodecs)
+ //{
+ // case VideoCodecs.AVC:
+ // if (videoQualityList[videoQualityList.IndexOf(selectedVideoQuality)].VideoCodecList.Contains("H.264/AVC"))
+ // {
+ // videoQualityList[videoQualityList.IndexOf(selectedVideoQuality)].SelectedVideoCodec = "H.264/AVC";
+ // }
+ // break;
+ // case VideoCodecs.HEVC:
+ // if (videoQualityList[videoQualityList.IndexOf(selectedVideoQuality)].VideoCodecList.Contains("H.265/HEVC"))
+ // {
+ // videoQualityList[videoQualityList.IndexOf(selectedVideoQuality)].SelectedVideoCodec = "H.265/HEVC";
+ // }
+ // break;
+ // case VideoCodecs.NONE:
+ // break;
+ // default:
+ // break;
+ //}
+ string videoCodecsName = codeIds.FirstOrDefault(t => t.Id == videoCodecs).Name;
+ if (videoQualityList[videoQualityList.IndexOf(selectedVideoQuality)].VideoCodecList.Contains(videoCodecsName))
{
- case VideoCodecs.AVC:
- if (videoQualityList[videoQualityList.IndexOf(selectedVideoQuality)].VideoCodecList.Contains("H.264/AVC"))
- {
- videoQualityList[videoQualityList.IndexOf(selectedVideoQuality)].SelectedVideoCodec = "H.264/AVC";
- }
- break;
- case VideoCodecs.HEVC:
- if (videoQualityList[videoQualityList.IndexOf(selectedVideoQuality)].VideoCodecList.Contains("H.265/HEVC"))
- {
- videoQualityList[videoQualityList.IndexOf(selectedVideoQuality)].SelectedVideoCodec = "H.265/HEVC";
- }
- break;
- case VideoCodecs.NONE:
- break;
- default:
- break;
+ videoQualityList[videoQualityList.IndexOf(selectedVideoQuality)].SelectedVideoCodec = videoCodecsName;
}
}
@@ -230,10 +286,10 @@ namespace DownKyi.Services
///
///
///
- internal static string GetVideoCodecName(string origin)
- {
- return origin.Contains("avc") ? "H.264/AVC" : origin.Contains("hev") ? "H.265/HEVC" : origin.Contains("dvh") || origin.Contains("hvc") ? "Dolby Vision" : "";
- }
+ //internal static string GetVideoCodecName(string origin)
+ //{
+ // return origin.Contains("avc") ? "H.264/AVC" : origin.Contains("hev") ? "H.265/HEVC" : origin.Contains("dvh") || origin.Contains("hvc") ? "Dolby Vision" : "";
+ //}
}
}
diff --git a/DownKyi/Utils/DialogUtils.cs b/DownKyi/Utils/DialogUtils.cs
index 41b08d7..9852eb4 100644
--- a/DownKyi/Utils/DialogUtils.cs
+++ b/DownKyi/Utils/DialogUtils.cs
@@ -37,5 +37,21 @@ namespace DownKyi.Utils
return showDialog == true ? dialog.FileName : "";
}
+
+ ///
+ /// 选择多个视频dialog
+ ///
+ ///
+ public static string[] SelectMultiVideoFile()
+ {
+ // 选择文件
+ var dialog = new Microsoft.Win32.OpenFileDialog
+ {
+ Filter = "mp4 (*.mp4)|*.mp4",
+ Multiselect = true
+ };
+ var showDialog = dialog.ShowDialog();
+ return showDialog == true ? dialog.FileNames : new string[0];
+ }
}
}
diff --git a/DownKyi/ViewModels/PageViewModels/VideoPage.cs b/DownKyi/ViewModels/PageViewModels/VideoPage.cs
index f04787b..2eb1b07 100644
--- a/DownKyi/ViewModels/PageViewModels/VideoPage.cs
+++ b/DownKyi/ViewModels/PageViewModels/VideoPage.cs
@@ -91,32 +91,32 @@ namespace DownKyi.ViewModels.PageViewModels
///
private void ExecuteVideoQualitySelectedCommand()
{
- // 杜比视界
- string dolby = string.Empty;
- try
- {
- var qualities = Constant.GetAudioQualities();
- dolby = qualities[3].Name;
- }
- catch (Exception e)
- {
- Core.Utils.Debugging.Console.PrintLine("ExecuteVideoQualitySelectedCommand()发生异常: {0}", e);
- LogManager.Error("ExecuteVideoQualitySelectedCommand", e);
- }
-
- if (VideoQuality != null && VideoQuality.Quality == 126 && PlayUrl != null && PlayUrl.Dash != null && PlayUrl.Dash.Dolby != null)
- {
- ListHelper.AddUnique(AudioQualityFormatList, dolby);
- AudioQualityFormat = dolby;
- }
- else
- {
- if (AudioQualityFormatList.Contains(dolby))
- {
- AudioQualityFormatList.Remove(dolby);
- AudioQualityFormat = AudioQualityFormatList[0];
- }
- }
+ //// 杜比视界
+ //string dolby = string.Empty;
+ //try
+ //{
+ // var qualities = Constant.GetAudioQualities();
+ // dolby = qualities[3].Name;
+ //}
+ //catch (Exception e)
+ //{
+ // Core.Utils.Debugging.Console.PrintLine("ExecuteVideoQualitySelectedCommand()发生异常: {0}", e);
+ // LogManager.Error("ExecuteVideoQualitySelectedCommand", e);
+ //}
+
+ //if (VideoQuality != null && VideoQuality.Quality == 126 && PlayUrl != null && PlayUrl.Dash != null && PlayUrl.Dash.Dolby != null)
+ //{
+ // ListHelper.AddUnique(AudioQualityFormatList, dolby);
+ // AudioQualityFormat = dolby;
+ //}
+ //else
+ //{
+ // if (AudioQualityFormatList.Contains(dolby))
+ // {
+ // AudioQualityFormatList.Remove(dolby);
+ // AudioQualityFormat = AudioQualityFormatList[0];
+ // }
+ //}
}
#endregion
diff --git a/DownKyi/ViewModels/Settings/ViewNetworkViewModel.cs b/DownKyi/ViewModels/Settings/ViewNetworkViewModel.cs
index a46ce58..98c40c3 100644
--- a/DownKyi/ViewModels/Settings/ViewNetworkViewModel.cs
+++ b/DownKyi/ViewModels/Settings/ViewNetworkViewModel.cs
@@ -41,6 +41,13 @@ namespace DownKyi.ViewModels.Settings
set => SetProperty(ref aria2c, value);
}
+ private bool customAria2c;
+ public bool CustomAria2c
+ {
+ get => customAria2c;
+ set => SetProperty(ref customAria2c, value);
+ }
+
private List maxCurrentDownloads;
public List MaxCurrentDownloads
{
@@ -90,6 +97,13 @@ namespace DownKyi.ViewModels.Settings
set => SetProperty(ref httpProxyPort, value);
}
+ private string ariaHost;
+ public string AriaHost
+ {
+ get => ariaHost;
+ set => SetProperty(ref ariaHost, value);
+ }
+
private int ariaListenPort;
public int AriaListenPort
{
@@ -97,6 +111,13 @@ namespace DownKyi.ViewModels.Settings
set => SetProperty(ref ariaListenPort, value);
}
+ private string ariaToken;
+ public string AriaToken
+ {
+ get => ariaToken;
+ set => SetProperty(ref ariaToken, value);
+ }
+
private List ariaLogLevels;
public List AriaLogLevels
{
@@ -259,6 +280,9 @@ namespace DownKyi.ViewModels.Settings
case Downloader.ARIA:
Aria2c = true;
break;
+ case Downloader.CUSTOM_ARIA:
+ CustomAria2c = true;
+ break;
}
// builtin同时下载数
@@ -277,9 +301,15 @@ namespace DownKyi.ViewModels.Settings
// builtin的http代理的端口
HttpProxyPort = SettingsManager.GetInstance().GetHttpProxyListenPort();
+ // Aria服务器host
+ AriaHost = SettingsManager.GetInstance().GetAriaHost();
+
// Aria服务器端口
AriaListenPort = SettingsManager.GetInstance().GetAriaListenPort();
+ // Aria服务器Token
+ AriaToken = SettingsManager.GetInstance().GetAriaToken();
+
// Aria的日志等级
AriaConfigLogLevel ariaLogLevel = SettingsManager.GetInstance().GetAriaLogLevel();
SelectedAriaLogLevel = ariaLogLevel.ToString("G");
@@ -349,6 +379,9 @@ namespace DownKyi.ViewModels.Settings
case "Aria2c":
downloader = Downloader.ARIA;
break;
+ case "CustomAria2c":
+ downloader = Downloader.CUSTOM_ARIA;
+ break;
default:
downloader = SettingsManager.GetInstance().GetDownloader();
break;
@@ -444,6 +477,21 @@ namespace DownKyi.ViewModels.Settings
PublishTip(isSucceed);
}
+ // Aria服务器host事件
+ private DelegateCommand ariaHostCommand;
+ public DelegateCommand AriaHostCommand => ariaHostCommand ?? (ariaHostCommand = new DelegateCommand(ExecuteAriaHostCommand));
+
+ ///
+ /// Aria服务器host事件
+ ///
+ ///
+ private void ExecuteAriaHostCommand(string parameter)
+ {
+ AriaHost = parameter;
+ bool isSucceed = SettingsManager.GetInstance().SetAriaHost(AriaHost);
+ PublishTip(isSucceed);
+ }
+
// Aria服务器端口事件
private DelegateCommand ariaListenPortCommand;
public DelegateCommand AriaListenPortCommand => ariaListenPortCommand ?? (ariaListenPortCommand = new DelegateCommand(ExecuteAriaListenPortCommand));
@@ -461,6 +509,21 @@ namespace DownKyi.ViewModels.Settings
PublishTip(isSucceed);
}
+ // Aria服务器token事件
+ private DelegateCommand ariaTokenCommand;
+ public DelegateCommand AriaTokenCommand => ariaTokenCommand ?? (ariaTokenCommand = new DelegateCommand(ExecuteAriaTokenCommand));
+
+ ///
+ /// Aria服务器token事件
+ ///
+ ///
+ private void ExecuteAriaTokenCommand(string parameter)
+ {
+ AriaToken = parameter;
+ bool isSucceed = SettingsManager.GetInstance().SetAriaToken(AriaToken);
+ PublishTip(isSucceed);
+ }
+
// Aria的日志等级事件
private DelegateCommand ariaLogLevelsCommand;
public DelegateCommand AriaLogLevelsCommand => ariaLogLevelsCommand ?? (ariaLogLevelsCommand = new DelegateCommand(ExecuteAriaLogLevelsCommand));
diff --git a/DownKyi/ViewModels/Settings/ViewVideoViewModel.cs b/DownKyi/ViewModels/Settings/ViewVideoViewModel.cs
index e38dc5f..7bb76fb 100644
--- a/DownKyi/ViewModels/Settings/ViewVideoViewModel.cs
+++ b/DownKyi/ViewModels/Settings/ViewVideoViewModel.cs
@@ -24,15 +24,15 @@ namespace DownKyi.ViewModels.Settings
#region 页面属性申明
- private List videoCodecs;
- public List VideoCodecs
+ private List videoCodecs;
+ public List VideoCodecs
{
get => videoCodecs;
set => SetProperty(ref videoCodecs, value);
}
- private string selectedVideoCodec;
- public string SelectedVideoCodec
+ private Quality selectedVideoCodec;
+ public Quality SelectedVideoCodec
{
get => selectedVideoCodec;
set => SetProperty(ref selectedVideoCodec, value);
@@ -186,18 +186,21 @@ namespace DownKyi.ViewModels.Settings
#region 属性初始化
// 优先下载的视频编码
- VideoCodecs = new List
- {
- "H.264/AVC",
- "H.265/HEVC",
- };
+ VideoCodecs = Constant.GetCodecIds();
+ //VideoCodecs = new List
+ //{
+ // "H.264/AVC",
+ // "H.265/HEVC",
+ //};
// 优先下载画质
VideoQualityList = Constant.GetResolutions();
// 优先下载音质
AudioQualityList = Constant.GetAudioQualities();
- AudioQualityList.RemoveAt(3);
+ //AudioQualityList.RemoveAt(3);
+ AudioQualityList[3].Id = AudioQualityList[3].Id + 1000;
+ AudioQualityList[4].Id = AudioQualityList[4].Id + 1000;
// 文件命名格式
SelectedFileName = new ObservableCollection();
@@ -253,8 +256,9 @@ namespace DownKyi.ViewModels.Settings
isOnNavigatedTo = true;
// 优先下载的视频编码
- VideoCodecs videoCodecs = SettingsManager.GetInstance().GetVideoCodecs();
- SelectedVideoCodec = GetVideoCodecsString(videoCodecs);
+ int videoCodecs = SettingsManager.GetInstance().GetVideoCodecs();
+ //SelectedVideoCodec = GetVideoCodecsString(videoCodecs);
+ SelectedVideoCodec = VideoCodecs.FirstOrDefault(t => { return t.Id == videoCodecs; });
// 优先下载画质
int quality = SettingsManager.GetInstance().GetQuality();
@@ -315,18 +319,20 @@ namespace DownKyi.ViewModels.Settings
#region 命令申明
// 优先下载的视频编码事件
- private DelegateCommand videoCodecsCommand;
- public DelegateCommand VideoCodecsCommand => videoCodecsCommand ?? (videoCodecsCommand = new DelegateCommand(ExecuteVideoCodecsCommand));
+ private DelegateCommand videoCodecsCommand;
+ public DelegateCommand VideoCodecsCommand => videoCodecsCommand ?? (videoCodecsCommand = new DelegateCommand(ExecuteVideoCodecsCommand));
///
/// 优先下载的视频编码事件
///
///
- private void ExecuteVideoCodecsCommand(string parameter)
+ private void ExecuteVideoCodecsCommand(object parameter)
{
- VideoCodecs videoCodecs = GetVideoCodecs(parameter);
+ //VideoCodecs videoCodecs = GetVideoCodecs(parameter);
- bool isSucceed = SettingsManager.GetInstance().SetVideoCodecs(videoCodecs);
+ if (!(parameter is Quality videoCodecs)) { return; }
+
+ bool isSucceed = SettingsManager.GetInstance().SetVideoCodecs(videoCodecs.Id);
PublishTip(isSucceed);
}
@@ -674,49 +680,49 @@ namespace DownKyi.ViewModels.Settings
///
///
///
- private string GetVideoCodecsString(VideoCodecs videoCodecs)
- {
- string codec;
- switch (videoCodecs)
- {
- case Core.Settings.VideoCodecs.NONE:
- codec = "";
- break;
- case Core.Settings.VideoCodecs.AVC:
- codec = "H.264/AVC";
- break;
- case Core.Settings.VideoCodecs.HEVC:
- codec = "H.265/HEVC";
- break;
- default:
- codec = "";
- break;
- }
- return codec;
- }
+ //private string GetVideoCodecsString(VideoCodecs videoCodecs)
+ //{
+ // string codec;
+ // switch (videoCodecs)
+ // {
+ // case Core.Settings.VideoCodecs.NONE:
+ // codec = "";
+ // break;
+ // case Core.Settings.VideoCodecs.AVC:
+ // codec = "H.264/AVC";
+ // break;
+ // case Core.Settings.VideoCodecs.HEVC:
+ // codec = "H.265/HEVC";
+ // break;
+ // default:
+ // codec = "";
+ // break;
+ // }
+ // return codec;
+ //}
///
/// 返回VideoCodecs
///
///
///
- private VideoCodecs GetVideoCodecs(string str)
- {
- VideoCodecs videoCodecs;
- switch (str)
- {
- case "H.264/AVC":
- videoCodecs = Core.Settings.VideoCodecs.AVC;
- break;
- case "H.265/HEVC":
- videoCodecs = Core.Settings.VideoCodecs.HEVC;
- break;
- default:
- videoCodecs = Core.Settings.VideoCodecs.NONE;
- break;
- }
- return videoCodecs;
- }
+ //private VideoCodecs GetVideoCodecs(string str)
+ //{
+ // VideoCodecs videoCodecs;
+ // switch (str)
+ // {
+ // case "H.264/AVC":
+ // videoCodecs = Core.Settings.VideoCodecs.AVC;
+ // break;
+ // case "H.265/HEVC":
+ // videoCodecs = Core.Settings.VideoCodecs.HEVC;
+ // break;
+ // default:
+ // videoCodecs = Core.Settings.VideoCodecs.NONE;
+ // break;
+ // }
+ // return videoCodecs;
+ //}
///
/// 保存下载视频内容到设置
diff --git a/DownKyi/ViewModels/Toolbox/ViewExtractMediaViewModel.cs b/DownKyi/ViewModels/Toolbox/ViewExtractMediaViewModel.cs
index bd8e1e7..cbd4cd6 100644
--- a/DownKyi/ViewModels/Toolbox/ViewExtractMediaViewModel.cs
+++ b/DownKyi/ViewModels/Toolbox/ViewExtractMediaViewModel.cs
@@ -18,11 +18,25 @@ namespace DownKyi.ViewModels.Toolbox
#region 页面属性申明
- private string videoPath;
- public string VideoPath
+ private string videoPathsStr;
+ public string VideoPathsStr
{
- get { return videoPath; }
- set { SetProperty(ref videoPath, value); }
+ get => videoPathsStr;
+ set
+ {
+ SetProperty(ref videoPathsStr, value);
+ }
+ }
+
+ private string[] videoPaths;
+ public string[] VideoPaths
+ {
+ get => videoPaths;
+ set
+ {
+ videoPaths = value;
+ VideoPathsStr = string.Join(Environment.NewLine, value);
+ }
}
private string status;
@@ -38,7 +52,7 @@ namespace DownKyi.ViewModels.Toolbox
{
#region 属性初始化
- VideoPath = string.Empty;
+ VideoPaths = new string[0];
#endregion
}
@@ -60,7 +74,7 @@ namespace DownKyi.ViewModels.Toolbox
return;
}
- VideoPath = DialogUtils.SelectVideoFile();
+ VideoPaths = DialogUtils.SelectMultiVideoFile();
}
// 提取音频事件
@@ -78,24 +92,27 @@ namespace DownKyi.ViewModels.Toolbox
return;
}
- if (VideoPath == "")
+ if (VideoPaths.Length <= 0)
{
eventAggregator.GetEvent().Publish(DictionaryResource.GetString("TipNoSeletedVideo"));
return;
}
- // 音频文件名
- string audioFileName = VideoPath.Remove(VideoPath.Length - 4, 4) + ".aac";
Status = string.Empty;
await Task.Run(() =>
{
- // 执行提取音频程序
isExtracting = true;
- FFmpegHelper.ExtractAudio(VideoPath, audioFileName, new Action((output) =>
+ foreach (var item in VideoPaths)
{
- Status += output + "\n";
- }));
+ // 音频文件名
+ string audioFileName = item.Remove(item.Length - 4, 4) + ".aac";
+ // 执行提取音频程序
+ FFmpegHelper.ExtractAudio(item, audioFileName, new Action((output) =>
+ {
+ Status += output + "\n";
+ }));
+ }
isExtracting = false;
});
}
@@ -115,30 +132,34 @@ namespace DownKyi.ViewModels.Toolbox
return;
}
- if (VideoPath == "")
+ if (VideoPaths.Length <= 0)
{
eventAggregator.GetEvent().Publish(DictionaryResource.GetString("TipNoSeletedVideo"));
return;
}
- // 视频文件名
- string videoFileName = VideoPath.Remove(VideoPath.Length - 4, 4) + "_onlyVideo.mp4";
Status = string.Empty;
await Task.Run(() =>
{
- // 执行提取视频程序
isExtracting = true;
- FFmpegHelper.ExtractVideo(VideoPath, videoFileName, new Action((output) =>
+ foreach (var item in VideoPaths)
{
- Status += output + "\n";
- }));
+ // 视频文件名
+ string videoFileName = item.Remove(item.Length - 4, 4) + "_onlyVideo.mp4";
+ // 执行提取视频程序
+ FFmpegHelper.ExtractVideo(item, videoFileName, new Action((output) =>
+ {
+ Status += output + "\n";
+ }));
+ }
isExtracting = false;
});
}
// Status改变事件
private DelegateCommand statusCommand;
+
public DelegateCommand StatusCommand => statusCommand ?? (statusCommand = new DelegateCommand(ExecuteStatusCommand));
///
diff --git a/DownKyi/ViewModels/UserSpace/SeasonsSeries.cs b/DownKyi/ViewModels/UserSpace/SeasonsSeries.cs
new file mode 100644
index 0000000..0641112
--- /dev/null
+++ b/DownKyi/ViewModels/UserSpace/SeasonsSeries.cs
@@ -0,0 +1,47 @@
+using DownKyi.Images;
+using Prism.Mvvm;
+using System.Windows.Media;
+
+namespace DownKyi.ViewModels.UserSpace
+{
+ public class SeasonsSeries : BindableBase
+ {
+ public long Id { get; set; }
+
+ private ImageSource cover;
+ public ImageSource Cover
+ {
+ get => cover;
+ set => SetProperty(ref cover, value);
+ }
+
+ private VectorImage typeImage;
+ public VectorImage TypeImage
+ {
+ get => typeImage;
+ set => SetProperty(ref typeImage, value);
+ }
+
+ private string name;
+ public string Name
+ {
+ get => name;
+ set => SetProperty(ref name, value);
+ }
+
+ private int count;
+ public int Count
+ {
+ get => count;
+ set => SetProperty(ref count, value);
+ }
+
+ private string ctime;
+ public string Ctime
+ {
+ get => ctime;
+ set => SetProperty(ref ctime, value);
+ }
+
+ }
+}
diff --git a/DownKyi/ViewModels/UserSpace/ViewSeasonsSeriesViewModel.cs b/DownKyi/ViewModels/UserSpace/ViewSeasonsSeriesViewModel.cs
new file mode 100644
index 0000000..b8f783b
--- /dev/null
+++ b/DownKyi/ViewModels/UserSpace/ViewSeasonsSeriesViewModel.cs
@@ -0,0 +1,199 @@
+using DownKyi.Core.BiliApi.Users.Models;
+using DownKyi.Core.Storage;
+using DownKyi.Events;
+using DownKyi.Images;
+using Prism.Commands;
+using Prism.Events;
+using Prism.Regions;
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Threading.Tasks;
+using System.Windows.Media.Imaging;
+
+namespace DownKyi.ViewModels.UserSpace
+{
+ public class ViewSeasonsSeriesViewModel : BaseViewModel
+ {
+ public const string Tag = "PageUserSpaceSeasonsSeries";
+
+ private long mid = -1;
+
+ #region 页面属性申明
+
+ private ObservableCollection seasonsSeries;
+ public ObservableCollection SeasonsSeries
+ {
+ get => seasonsSeries;
+ set => SetProperty(ref seasonsSeries, value);
+ }
+
+ private int selectedItem;
+ public int SelectedItem
+ {
+ get => selectedItem;
+ set => SetProperty(ref selectedItem, value);
+ }
+
+ #endregion
+
+ public ViewSeasonsSeriesViewModel(IEventAggregator eventAggregator) : base(eventAggregator)
+ {
+ #region 属性初始化
+
+ SeasonsSeries = new ObservableCollection();
+
+ #endregion
+ }
+
+ #region 命令申明
+
+ // 视频选择事件
+ private DelegateCommand seasonsSeriesCommand;
+ public DelegateCommand SeasonsSeriesCommand => seasonsSeriesCommand ?? (seasonsSeriesCommand = new DelegateCommand(ExecuteSeasonsSeriesCommand));
+
+ ///
+ /// 视频选择事件
+ ///
+ ///
+ private void ExecuteSeasonsSeriesCommand(object parameter)
+ {
+ if (!(parameter is SeasonsSeries seasonsSeries)) { return; }
+
+ // 应该用枚举的,偷懒直接用数字
+ int type = 0;
+ if (seasonsSeries.TypeImage == NormalIcon.Instance().SeasonsSeries)
+ {
+ type = 1;
+ }
+ else if (seasonsSeries.TypeImage == NormalIcon.Instance().Channel1)
+ {
+ type = 2;
+ }
+ Dictionary data = new Dictionary
+ {
+ { "mid", mid },
+ { "id", seasonsSeries.Id },
+ { "name", seasonsSeries.Name },
+ { "count", seasonsSeries.Count },
+ { "type", type }
+ };
+
+ // 进入视频页面
+ NavigationParam param = new NavigationParam
+ {
+ ViewName = ViewModels.ViewSeasonsSeriesViewModel.Tag,
+ ParentViewName = ViewUserSpaceViewModel.Tag,
+ Parameter = data
+ };
+ eventAggregator.GetEvent().Publish(param);
+
+ SelectedItem = -1;
+ }
+
+ #endregion
+
+ public override void OnNavigatedFrom(NavigationContext navigationContext)
+ {
+ base.OnNavigatedFrom(navigationContext);
+
+ SeasonsSeries.Clear();
+ SelectedItem = -1;
+ }
+
+ ///
+ /// 接收mid参数
+ ///
+ ///
+ public async override void OnNavigatedTo(NavigationContext navigationContext)
+ {
+ base.OnNavigatedTo(navigationContext);
+
+ SeasonsSeries.Clear();
+ SelectedItem = -1;
+
+ // 根据传入参数不同执行不同任务
+ var parameter = navigationContext.Parameters.GetValue("object");
+ if (parameter == null)
+ {
+ return;
+ }
+
+ // 传入mid
+ mid = navigationContext.Parameters.GetValue("mid");
+
+ foreach (var item in parameter.SeasonsList)
+ {
+ if (item.Meta.Total <= 0) { continue; }
+
+ BitmapImage image = null;
+ if (item.Meta.Cover == null || item.Meta.Cover == "")
+ {
+ image = new BitmapImage(new Uri($"pack://application:,,,/Resources/video-placeholder.png"));
+ }
+ else
+ {
+ StorageCover storageCover = new StorageCover();
+ string cover = null;
+ await Task.Run(() =>
+ {
+ cover = storageCover.GetCover(item.Meta.Cover);
+ });
+ image = storageCover.GetCoverThumbnail(cover, 190, 190);
+ }
+
+ // 当地时区
+ DateTime startTime = TimeZone.CurrentTimeZone.ToLocalTime(new DateTime(1970, 1, 1));
+ DateTime dateCTime = startTime.AddSeconds(item.Meta.Ptime);
+ string mtime = dateCTime.ToString("yyyy-MM-dd");
+
+ SeasonsSeries.Add(new SeasonsSeries
+ {
+ Id = item.Meta.SeasonId,
+ Cover = image,
+ TypeImage = NormalIcon.Instance().SeasonsSeries,
+ Name = item.Meta.Name,
+ Count = item.Meta.Total,
+ Ctime = mtime
+ });
+ }
+
+ foreach (var item in parameter.SeriesList)
+ {
+ if (item.Meta.Total <= 0) { continue; }
+
+ BitmapImage image = null;
+ if (item.Meta.Cover == null || item.Meta.Cover == "")
+ {
+ image = new BitmapImage(new Uri($"pack://application:,,,/Resources/video-placeholder.png"));
+ }
+ else
+ {
+ StorageCover storageCover = new StorageCover();
+ string cover = null;
+ await Task.Run(() =>
+ {
+ cover = storageCover.GetCover(item.Meta.Cover);
+ });
+ image = storageCover.GetCoverThumbnail(cover, 190, 190);
+ }
+
+ // 当地时区
+ DateTime startTime = TimeZone.CurrentTimeZone.ToLocalTime(new DateTime(1970, 1, 1));
+ DateTime dateCTime = startTime.AddSeconds(item.Meta.Mtime);
+ string mtime = dateCTime.ToString("yyyy-MM-dd");
+
+ SeasonsSeries.Add(new SeasonsSeries
+ {
+ Id = item.Meta.SeriesId,
+ Cover = image,
+ TypeImage = NormalIcon.Instance().Channel1,
+ Name = item.Meta.Name,
+ Count = item.Meta.Total,
+ Ctime = mtime
+ });
+ }
+
+ }
+ }
+}
diff --git a/DownKyi/ViewModels/ViewIndexViewModel.cs b/DownKyi/ViewModels/ViewIndexViewModel.cs
index ee1fbc8..9eaeb10 100644
--- a/DownKyi/ViewModels/ViewIndexViewModel.cs
+++ b/DownKyi/ViewModels/ViewIndexViewModel.cs
@@ -12,6 +12,7 @@ using Prism.Regions;
using Prism.Services.Dialogs;
using System;
using System.IO;
+using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Media.Imaging;
@@ -214,7 +215,7 @@ namespace DownKyi.ViewModels
}
LogManager.Debug(Tag, $"InputText: {InputText}");
-
+ InputText = Regex.Replace(InputText, @"[【]*[^【]*[^】]*[】 ]", "");
SearchService searchService = new SearchService();
bool isSupport = searchService.BiliInput(InputText, Tag, eventAggregator);
if (!isSupport)
diff --git a/DownKyi/ViewModels/ViewSeasonsSeriesViewModel.cs b/DownKyi/ViewModels/ViewSeasonsSeriesViewModel.cs
new file mode 100644
index 0000000..0c4377f
--- /dev/null
+++ b/DownKyi/ViewModels/ViewSeasonsSeriesViewModel.cs
@@ -0,0 +1,672 @@
+using DownKyi.Core.BiliApi.VideoStream;
+using DownKyi.Core.Storage;
+using DownKyi.Core.Utils;
+using DownKyi.CustomControl;
+using DownKyi.Events;
+using DownKyi.Images;
+using DownKyi.Services;
+using DownKyi.Services.Download;
+using DownKyi.Utils;
+using DownKyi.ViewModels.PageViewModels;
+using Prism.Commands;
+using Prism.Events;
+using Prism.Regions;
+using Prism.Services.Dialogs;
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+using System.Windows;
+using System.Windows.Media.Imaging;
+
+namespace DownKyi.ViewModels
+{
+ public class ViewSeasonsSeriesViewModel : BaseViewModel
+ {
+ public const string Tag = "PageSeasonsSeries";
+
+ private readonly IDialogService dialogService;
+
+ private CancellationTokenSource tokenSource;
+
+ private long mid = -1;
+ private long id = -1;
+ private int type = 0;
+
+ // 每页视频数量,暂时在此写死,以后在设置中增加选项
+ private readonly int VideoNumberInPage = 30;
+
+ #region 页面属性申明
+
+ private string pageName = Tag;
+ public string PageName
+ {
+ get => pageName;
+ set => SetProperty(ref pageName, value);
+ }
+
+ private GifImage loading;
+ public GifImage Loading
+ {
+ get => loading;
+ set => SetProperty(ref loading, value);
+ }
+
+ private Visibility loadingVisibility;
+ public Visibility LoadingVisibility
+ {
+ get => loadingVisibility;
+ set => SetProperty(ref loadingVisibility, value);
+ }
+
+ private Visibility noDataVisibility;
+ public Visibility NoDataVisibility
+ {
+ get => noDataVisibility;
+ set => SetProperty(ref noDataVisibility, value);
+ }
+
+ private VectorImage arrowBack;
+ public VectorImage ArrowBack
+ {
+ get => arrowBack;
+ set => SetProperty(ref arrowBack, value);
+ }
+
+ private VectorImage downloadManage;
+ public VectorImage DownloadManage
+ {
+ get => downloadManage;
+ set => SetProperty(ref downloadManage, value);
+ }
+
+ private string title;
+ public string Title
+ {
+ get => title;
+ set => SetProperty(ref title, value);
+ }
+
+ private bool isEnabled = true;
+ public bool IsEnabled
+ {
+ get => isEnabled;
+ set => SetProperty(ref isEnabled, value);
+ }
+
+ private CustomPagerViewModel pager;
+ public CustomPagerViewModel Pager
+ {
+ get => pager;
+ set => SetProperty(ref pager, value);
+ }
+
+ private ObservableCollection medias;
+ public ObservableCollection Medias
+ {
+ get => medias;
+ set => SetProperty(ref medias, value);
+ }
+
+ private bool isSelectAll;
+ public bool IsSelectAll
+ {
+ get => isSelectAll;
+ set => SetProperty(ref isSelectAll, value);
+ }
+
+ #endregion
+
+ public ViewSeasonsSeriesViewModel(IEventAggregator eventAggregator, IDialogService dialogService) : base(eventAggregator)
+ {
+ this.dialogService = dialogService;
+
+ #region 属性初始化
+
+ // 初始化loading gif
+ Loading = new GifImage(Properties.Resources.loading);
+ Loading.StartAnimate();
+ LoadingVisibility = Visibility.Collapsed;
+ NoDataVisibility = Visibility.Collapsed;
+
+ ArrowBack = NavigationIcon.Instance().ArrowBack;
+ ArrowBack.Fill = DictionaryResource.GetColor("ColorTextDark");
+
+ // 下载管理按钮
+ DownloadManage = ButtonIcon.Instance().DownloadManage;
+ DownloadManage.Height = 24;
+ DownloadManage.Width = 24;
+ DownloadManage.Fill = DictionaryResource.GetColor("ColorPrimary");
+
+ Medias = new ObservableCollection();
+
+ #endregion
+ }
+
+ #region 命令申明
+
+ // 返回事件
+ private DelegateCommand backSpaceCommand;
+ public DelegateCommand BackSpaceCommand => backSpaceCommand ?? (backSpaceCommand = new DelegateCommand(ExecuteBackSpace));
+
+ ///
+ /// 返回事件
+ ///
+ private void ExecuteBackSpace()
+ {
+ ArrowBack.Fill = DictionaryResource.GetColor("ColorText");
+
+ // 结束任务
+ tokenSource?.Cancel();
+
+ NavigationParam parameter = new NavigationParam
+ {
+ ViewName = ParentView,
+ ParentViewName = null,
+ Parameter = null
+ };
+ eventAggregator.GetEvent().Publish(parameter);
+ }
+
+ // 前往下载管理页面
+ private DelegateCommand downloadManagerCommand;
+ public DelegateCommand DownloadManagerCommand => downloadManagerCommand ?? (downloadManagerCommand = new DelegateCommand(ExecuteDownloadManagerCommand));
+
+ ///
+ /// 前往下载管理页面
+ ///
+ private void ExecuteDownloadManagerCommand()
+ {
+ NavigationParam parameter = new NavigationParam
+ {
+ ViewName = ViewDownloadManagerViewModel.Tag,
+ ParentViewName = Tag,
+ Parameter = null
+ };
+ eventAggregator.GetEvent().Publish(parameter);
+ }
+
+ // 全选按钮点击事件
+ private DelegateCommand selectAllCommand;
+ public DelegateCommand SelectAllCommand => selectAllCommand ?? (selectAllCommand = new DelegateCommand(ExecuteSelectAllCommand));
+
+ ///
+ /// 全选按钮点击事件
+ ///
+ ///
+ private void ExecuteSelectAllCommand(object parameter)
+ {
+ if (IsSelectAll)
+ {
+ foreach (var item in Medias)
+ {
+ item.IsSelected = true;
+ }
+ }
+ else
+ {
+ foreach (var item in Medias)
+ {
+ item.IsSelected = false;
+ }
+ }
+ }
+
+ // 列表选择事件
+ private DelegateCommand mediasCommand;
+ public DelegateCommand MediasCommand => mediasCommand ?? (mediasCommand = new DelegateCommand(ExecuteMediasCommand));
+
+ ///
+ /// 列表选择事件
+ ///
+ ///
+ private void ExecuteMediasCommand(object parameter)
+ {
+ if (!(parameter is IList selectedMedia)) { return; }
+
+ if (selectedMedia.Count == Medias.Count)
+ {
+ IsSelectAll = true;
+ }
+ else
+ {
+ IsSelectAll = false;
+ }
+ }
+
+ // 添加选中项到下载列表事件
+ private DelegateCommand addToDownloadCommand;
+ public DelegateCommand AddToDownloadCommand => addToDownloadCommand ?? (addToDownloadCommand = new DelegateCommand(ExecuteAddToDownloadCommand));
+
+ ///
+ /// 添加选中项到下载列表事件
+ ///
+ private void ExecuteAddToDownloadCommand()
+ {
+ AddToDownload(true);
+ }
+
+ // 添加所有视频到下载列表事件
+ private DelegateCommand addAllToDownloadCommand;
+ public DelegateCommand AddAllToDownloadCommand => addAllToDownloadCommand ?? (addAllToDownloadCommand = new DelegateCommand(ExecuteAddAllToDownloadCommand));
+
+ ///
+ /// 添加所有视频到下载列表事件
+ ///
+ private void ExecuteAddAllToDownloadCommand()
+ {
+ AddToDownload(false);
+ }
+
+ #endregion
+
+ ///
+ /// 添加到下载
+ ///
+ ///
+ private async void AddToDownload(bool isOnlySelected)
+ {
+ // 频道里只有视频
+ AddToDownloadService addToDownloadService = new AddToDownloadService(PlayStreamType.VIDEO);
+
+ // 选择文件夹
+ string directory = addToDownloadService.SetDirectory(dialogService);
+
+ // 视频计数
+ int i = 0;
+ await Task.Run(() =>
+ {
+ // 为了避免执行其他操作时,
+ // Medias变化导致的异常
+ var list = Medias.ToList();
+
+ // 添加到下载
+ foreach (var media in list)
+ {
+ // 只下载选中项,跳过未选中项
+ if (isOnlySelected && !media.IsSelected) { continue; }
+
+ /// 有分P的就下载全部
+
+ // 开启服务
+ VideoInfoService videoInfoService = new VideoInfoService(media.Bvid);
+
+ addToDownloadService.SetVideoInfoService(videoInfoService);
+ addToDownloadService.GetVideo();
+ addToDownloadService.ParseVideo(videoInfoService);
+ // 下载
+ i += addToDownloadService.AddToDownload(eventAggregator, directory);
+ }
+ });
+
+ if (directory == null)
+ {
+ return;
+ }
+
+ // 通知用户添加到下载列表的结果
+ if (i <= 0)
+ {
+ eventAggregator.GetEvent().Publish(DictionaryResource.GetString("TipAddDownloadingZero"));
+ }
+ else
+ {
+ eventAggregator.GetEvent().Publish($"{DictionaryResource.GetString("TipAddDownloadingFinished1")}{i}{DictionaryResource.GetString("TipAddDownloadingFinished2")}");
+ }
+ }
+
+ private void OnCountChanged_Pager(int count) { }
+
+ private bool OnCurrentChanged_Pager(int old, int current)
+ {
+ if (!IsEnabled)
+ {
+ //Pager.Current = old;
+ return false;
+ }
+
+ Medias.Clear();
+ IsSelectAll = false;
+ LoadingVisibility = Visibility.Visible;
+ NoDataVisibility = Visibility.Collapsed;
+
+ //UpdateChannel(current);
+
+ if (type == 1) { UpdateSeasons(current); }
+ if (type == 2) { UpdateSeries(current); }
+
+ return true;
+ }
+
+ //private async void UpdateChannel(int current)
+ //{
+ // // 是否正在获取数据
+ // // 在所有的退出分支中都需要设为true
+ // IsEnabled = false;
+
+ // await Task.Run(() =>
+ // {
+ // CancellationToken cancellationToken = tokenSource.Token;
+
+ // var channels = Core.BiliApi.Users.UserSpace.GetChannelVideoList(mid, cid, current, VideoNumberInPage);
+ // if (channels == null || channels.Count == 0)
+ // {
+ // // 没有数据,UI提示
+ // LoadingVisibility = Visibility.Collapsed;
+ // NoDataVisibility = Visibility.Visible;
+ // return;
+ // }
+
+ // foreach (var video in channels)
+ // {
+ // if (video.Cid == 0)
+ // {
+ // continue;
+ // }
+
+ // // 查询、保存封面
+ // string coverUrl = video.Pic;
+ // BitmapImage cover;
+ // if (coverUrl == null || coverUrl == "")
+ // {
+ // cover = null; // new BitmapImage(new Uri($"pack://application:,,,/Resources/video-placeholder.png"));
+ // }
+ // else
+ // {
+ // if (!coverUrl.ToLower().StartsWith("http"))
+ // {
+ // coverUrl = $"https:{video.Pic}";
+ // }
+
+ // StorageCover storageCover = new StorageCover();
+ // cover = storageCover.GetCoverThumbnail(video.Aid, video.Bvid, -1, coverUrl, 200, 125);
+ // }
+
+ // // 播放数
+ // string play = string.Empty;
+ // if (video.Stat != null)
+ // {
+ // if (video.Stat.View > 0)
+ // {
+ // play = Format.FormatNumber(video.Stat.View);
+ // }
+ // else
+ // {
+ // play = "--";
+ // }
+ // }
+ // else
+ // {
+ // play = "--";
+ // }
+
+ // DateTime startTime = TimeZone.CurrentTimeZone.ToLocalTime(new DateTime(1970, 1, 1)); // 当地时区
+ // DateTime dateCTime = startTime.AddSeconds(video.Ctime);
+ // string ctime = dateCTime.ToString("yyyy-MM-dd");
+
+ // App.PropertyChangeAsync(new Action(() =>
+ // {
+ // ChannelMedia media = new ChannelMedia(eventAggregator)
+ // {
+ // Avid = video.Aid,
+ // Bvid = video.Bvid,
+ // Cover = cover ?? new BitmapImage(new Uri($"pack://application:,,,/Resources/video-placeholder.png")),
+ // Duration = Format.FormatDuration3(video.Duration),
+ // Title = video.Title,
+ // PlayNumber = play,
+ // CreateTime = ctime
+ // };
+ // Medias.Add(media);
+
+ // LoadingVisibility = Visibility.Collapsed;
+ // NoDataVisibility = Visibility.Collapsed;
+ // }));
+
+ // // 判断是否该结束线程,若为true,跳出循环
+ // if (cancellationToken.IsCancellationRequested)
+ // {
+ // break;
+ // }
+ // }
+
+ // }, (tokenSource = new CancellationTokenSource()).Token);
+
+ // IsEnabled = true;
+ //}
+
+ private async void UpdateSeasons(int current)
+ {
+ // 是否正在获取数据
+ // 在所有的退出分支中都需要设为true
+ IsEnabled = false;
+
+ await Task.Run(() =>
+ {
+ CancellationToken cancellationToken = tokenSource.Token;
+
+ var seasons = Core.BiliApi.Users.UserSpace.GetSeasonsDetail(mid, id, current, VideoNumberInPage);
+ if (seasons == null || seasons.Meta.Total == 0)
+ {
+ // 没有数据,UI提示
+ LoadingVisibility = Visibility.Collapsed;
+ NoDataVisibility = Visibility.Visible;
+ return;
+ }
+
+ foreach (var video in seasons.Archives)
+ {
+ //if (video.Cid == 0)
+ //{
+ // continue;
+ //}
+
+ // 查询、保存封面
+ string coverUrl = video.Pic;
+ BitmapImage cover;
+ if (coverUrl == null || coverUrl == "")
+ {
+ cover = null; // new BitmapImage(new Uri($"pack://application:,,,/Resources/video-placeholder.png"));
+ }
+ else
+ {
+ if (!coverUrl.ToLower().StartsWith("http"))
+ {
+ coverUrl = $"https:{video.Pic}";
+ }
+
+ StorageCover storageCover = new StorageCover();
+ cover = storageCover.GetCoverThumbnail(video.Aid, video.Bvid, -1, coverUrl, 200, 125);
+ }
+
+ // 播放数
+ string play = string.Empty;
+ if (video.Stat != null)
+ {
+ if (video.Stat.View > 0)
+ {
+ play = Format.FormatNumber(video.Stat.View);
+ }
+ else
+ {
+ play = "--";
+ }
+ }
+ else
+ {
+ play = "--";
+ }
+
+ DateTime startTime = TimeZone.CurrentTimeZone.ToLocalTime(new DateTime(1970, 1, 1)); // 当地时区
+ DateTime dateCTime = startTime.AddSeconds(video.Ctime);
+ string ctime = dateCTime.ToString("yyyy-MM-dd");
+
+ App.PropertyChangeAsync(new Action(() =>
+ {
+ ChannelMedia media = new ChannelMedia(eventAggregator)
+ {
+ Avid = video.Aid,
+ Bvid = video.Bvid,
+ Cover = cover ?? new BitmapImage(new Uri($"pack://application:,,,/Resources/video-placeholder.png")),
+ Duration = Format.FormatDuration3(video.Duration),
+ Title = video.Title,
+ PlayNumber = play,
+ CreateTime = ctime
+ };
+ Medias.Add(media);
+
+ LoadingVisibility = Visibility.Collapsed;
+ NoDataVisibility = Visibility.Collapsed;
+ }));
+
+ // 判断是否该结束线程,若为true,跳出循环
+ if (cancellationToken.IsCancellationRequested)
+ {
+ break;
+ }
+ }
+
+ }, (tokenSource = new CancellationTokenSource()).Token);
+
+ IsEnabled = true;
+ }
+
+ private async void UpdateSeries(int current)
+ {
+ // 是否正在获取数据
+ // 在所有的退出分支中都需要设为true
+ IsEnabled = false;
+
+ await Task.Run(() =>
+ {
+ CancellationToken cancellationToken = tokenSource.Token;
+
+ var meta = Core.BiliApi.Users.UserSpace.GetSeriesMeta(id);
+ var series = Core.BiliApi.Users.UserSpace.GetSeriesDetail(mid, id, current, VideoNumberInPage);
+ if (series == null || meta.Meta.Total == 0)
+ {
+ // 没有数据,UI提示
+ LoadingVisibility = Visibility.Collapsed;
+ NoDataVisibility = Visibility.Visible;
+ return;
+ }
+
+ foreach (var video in series.Archives)
+ {
+ //if (video.Cid == 0)
+ //{
+ // continue;
+ //}
+
+ // 查询、保存封面
+ string coverUrl = video.Pic;
+ BitmapImage cover;
+ if (coverUrl == null || coverUrl == "")
+ {
+ cover = null; // new BitmapImage(new Uri($"pack://application:,,,/Resources/video-placeholder.png"));
+ }
+ else
+ {
+ if (!coverUrl.ToLower().StartsWith("http"))
+ {
+ coverUrl = $"https:{video.Pic}";
+ }
+
+ StorageCover storageCover = new StorageCover();
+ cover = storageCover.GetCoverThumbnail(video.Aid, video.Bvid, -1, coverUrl, 200, 125);
+ }
+
+ // 播放数
+ string play = string.Empty;
+ if (video.Stat != null)
+ {
+ if (video.Stat.View > 0)
+ {
+ play = Format.FormatNumber(video.Stat.View);
+ }
+ else
+ {
+ play = "--";
+ }
+ }
+ else
+ {
+ play = "--";
+ }
+
+ DateTime startTime = TimeZone.CurrentTimeZone.ToLocalTime(new DateTime(1970, 1, 1)); // 当地时区
+ DateTime dateCTime = startTime.AddSeconds(video.Ctime);
+ string ctime = dateCTime.ToString("yyyy-MM-dd");
+
+ App.PropertyChangeAsync(new Action(() =>
+ {
+ ChannelMedia media = new ChannelMedia(eventAggregator)
+ {
+ Avid = video.Aid,
+ Bvid = video.Bvid,
+ Cover = cover ?? new BitmapImage(new Uri($"pack://application:,,,/Resources/video-placeholder.png")),
+ Duration = Format.FormatDuration3(video.Duration),
+ Title = video.Title,
+ PlayNumber = play,
+ CreateTime = ctime
+ };
+ Medias.Add(media);
+
+ LoadingVisibility = Visibility.Collapsed;
+ NoDataVisibility = Visibility.Collapsed;
+ }));
+
+ // 判断是否该结束线程,若为true,跳出循环
+ if (cancellationToken.IsCancellationRequested)
+ {
+ break;
+ }
+ }
+
+ }, (tokenSource = new CancellationTokenSource()).Token);
+
+ IsEnabled = true;
+ }
+
+ ///
+ /// 导航到VideoDetail页面时执行
+ ///
+ ///
+ public override void OnNavigatedTo(NavigationContext navigationContext)
+ {
+ base.OnNavigatedTo(navigationContext);
+
+ ArrowBack.Fill = DictionaryResource.GetColor("ColorTextDark");
+
+ DownloadManage = ButtonIcon.Instance().DownloadManage;
+ DownloadManage.Height = 24;
+ DownloadManage.Width = 24;
+ DownloadManage.Fill = DictionaryResource.GetColor("ColorPrimary");
+
+ // 根据传入参数不同执行不同任务
+ var parameter = navigationContext.Parameters.GetValue>("Parameter");
+ if (parameter == null)
+ {
+ return;
+ }
+
+ Medias.Clear();
+ IsSelectAll = false;
+
+ mid = (long)parameter["mid"];
+ id = (long)parameter["id"];
+ type = (int)parameter["type"];
+ Title = (string)parameter["name"];
+ int count = (int)parameter["count"];
+
+ // 页面选择
+ Pager = new CustomPagerViewModel(1, (int)Math.Ceiling((double)count / VideoNumberInPage));
+ Pager.CurrentChanged += OnCurrentChanged_Pager;
+ Pager.CountChanged += OnCountChanged_Pager;
+ Pager.Current = 1;
+ }
+
+ }
+}
diff --git a/DownKyi/ViewModels/ViewUserSpaceViewModel.cs b/DownKyi/ViewModels/ViewUserSpaceViewModel.cs
index 6bef7cb..830fba7 100644
--- a/DownKyi/ViewModels/ViewUserSpaceViewModel.cs
+++ b/DownKyi/ViewModels/ViewUserSpaceViewModel.cs
@@ -228,12 +228,15 @@ namespace DownKyi.ViewModels
switch (banner.Id)
{
- case 0:
+ case 0: // 投稿
regionManager.RequestNavigate("UserSpaceContentRegion", ViewArchiveViewModel.Tag, param);
break;
- case 1:
+ case 1: // 频道(弃用)
regionManager.RequestNavigate("UserSpaceContentRegion", UserSpace.ViewChannelViewModel.Tag, param);
break;
+ case 2: // 合集和列表
+ regionManager.RequestNavigate("UserSpaceContentRegion", UserSpace.ViewSeasonsSeriesViewModel.Tag, param);
+ break;
}
}
@@ -427,20 +430,38 @@ namespace DownKyi.ViewModels
}
// 频道
- List channelList = null;
+ //List channelList = null;
+ //await Task.Run(() =>
+ //{
+ // channelList = Core.BiliApi.Users.UserSpace.GetChannelList(mid);
+ //});
+ //if (channelList != null && channelList.Count > 0)
+ //{
+ // TabLeftBanners.Add(new TabLeftBanner
+ // {
+ // Object = channelList,
+ // Id = 1,
+ // Icon = NormalIcon.Instance().Channel,
+ // IconColor = "#FF23C9ED",
+ // Title = DictionaryResource.GetString("Channel")
+ // });
+ //}
+
+ // 合集和列表
+ SpaceSeasonsSeries seasonsSeries = null;
await Task.Run(() =>
{
- channelList = Core.BiliApi.Users.UserSpace.GetChannelList(mid);
+ seasonsSeries = Core.BiliApi.Users.UserSpace.GetSeasonsSeries(mid, 1, 20);
});
- if (channelList != null && channelList.Count > 0)
+ if (seasonsSeries != null && seasonsSeries.Page.Total > 0)
{
TabLeftBanners.Add(new TabLeftBanner
{
- Object = channelList,
- Id = 1,
+ Object = seasonsSeries,
+ Id = 2,
Icon = NormalIcon.Instance().Channel,
IconColor = "#FF23C9ED",
- Title = DictionaryResource.GetString("Channel")
+ Title = DictionaryResource.GetString("SeasonsSeries")
});
}
diff --git a/DownKyi/ViewModels/ViewVideoDetailViewModel.cs b/DownKyi/ViewModels/ViewVideoDetailViewModel.cs
index 738f4b5..5d7551b 100644
--- a/DownKyi/ViewModels/ViewVideoDetailViewModel.cs
+++ b/DownKyi/ViewModels/ViewVideoDetailViewModel.cs
@@ -18,6 +18,7 @@ using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
+using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Windows;
@@ -184,7 +185,7 @@ namespace DownKyi.ViewModels
if (InputText == null || InputText == string.Empty) { return; }
LogManager.Debug(Tag, $"InputText: {InputText}");
-
+ InputText = Regex.Replace(InputText, @"[【]*[^【]*[^】]*[】 ]", "");
input = InputText;
// 更新页面
diff --git a/DownKyi/Views/Settings/ViewNetwork.xaml b/DownKyi/Views/Settings/ViewNetwork.xaml
index 23a3e49..884df8e 100644
--- a/DownKyi/Views/Settings/ViewNetwork.xaml
+++ b/DownKyi/Views/Settings/ViewNetwork.xaml
@@ -56,6 +56,15 @@
Foreground="{DynamicResource BrushTextDark}"
IsChecked="{Binding Aria2c}"
Style="{StaticResource RadioStyle}" />
+
@@ -208,6 +217,32 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/DownKyi/Views/Settings/ViewVideo.xaml b/DownKyi/Views/Settings/ViewVideo.xaml
index 4511b48..10eca51 100644
--- a/DownKyi/Views/Settings/ViewVideo.xaml
+++ b/DownKyi/Views/Settings/ViewVideo.xaml
@@ -23,6 +23,7 @@
Name="nameVideoCodecs"
Width="120"
VerticalContentAlignment="Center"
+ DisplayMemberPath="Name"
ItemsSource="{Binding VideoCodecs}"
SelectedValue="{Binding SelectedVideoCodec}">
diff --git a/DownKyi/Views/Toolbox/ViewExtractMedia.xaml b/DownKyi/Views/Toolbox/ViewExtractMedia.xaml
index b47bb26..b3d01b2 100644
--- a/DownKyi/Views/Toolbox/ViewExtractMedia.xaml
+++ b/DownKyi/Views/Toolbox/ViewExtractMedia.xaml
@@ -9,7 +9,7 @@
-
+
@@ -27,12 +27,12 @@
Orientation="Horizontal">
+ Text="{Binding VideoPathsStr, Mode=TwoWay}" />