From c664ecd9f601fa7d57f69d7f0b45d50cff97ada4 Mon Sep 17 00:00:00 2001
From: croire <1432593898@qq.com>
Date: Sun, 30 Jan 2022 20:38:24 +0800
Subject: [PATCH] =?UTF-8?q?=E8=A7=A3=E5=86=B3=E4=B8=8B=E8=BD=BD=E7=9A=84?=
=?UTF-8?q?=E4=B8=80=E4=BA=9Bbug=EF=BC=9B=E6=94=B6=E8=97=8F=E5=A4=B9?=
=?UTF-8?q?=E4=B8=AD=E7=9A=84=E4=B8=80=E4=BA=9Bbug=EF=BC=9B=E6=95=B4?=
=?UTF-8?q?=E7=90=86=E4=BB=A3=E7=A0=81?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
DownKyi.Core/BiliApi/Video/VideoInfo.cs | 1 +
DownKyi.Core/BiliApi/WebClient.cs | 2 +-
.../Storage/Database/Download/DownloadDb.cs | 7 +-
DownKyi/App.xaml.cs | 63 +--
DownKyi/DownKyi.csproj | 1 +
DownKyi/Models/DownloadStatus.cs | 1 +
.../Services/Download/AddToDownloadService.cs | 377 ++++++++++++++++++
.../Services/Download/AriaDownloadService.cs | 146 +++++--
.../Download/DownloadStorageService.cs | 16 +
DownKyi/Services/SearchService.cs | 12 +-
DownKyi/Services/Utils.cs | 32 +-
DownKyi/Services/VideoInfoService.cs | 2 +-
DownKyi/Utils/DialogUtils.cs | 6 +-
.../DownloadManager/DownloadBaseItem.cs | 21 +-
.../DownloadManager/DownloadingItem.cs | 10 +-
.../ViewDownloadFinishedViewModel.cs | 21 +-
.../ViewDownloadingViewModel.cs | 35 +-
.../ViewModels/PageViewModels/Favorites.cs | 52 ++-
DownKyi/ViewModels/ViewIndexViewModel.cs | 11 -
.../ViewPublicFavoritesViewModel.cs | 95 +++--
.../ViewModels/ViewVideoDetailViewModel.cs | 238 ++---------
.../DownloadManager/ViewDownloadFinished.xaml | 12 +-
.../DownloadManager/ViewDownloading.xaml | 12 +-
DownKyi/Views/ViewPublicFavorites.xaml | 33 +-
24 files changed, 822 insertions(+), 384 deletions(-)
create mode 100644 DownKyi/Services/Download/AddToDownloadService.cs
diff --git a/DownKyi.Core/BiliApi/Video/VideoInfo.cs b/DownKyi.Core/BiliApi/Video/VideoInfo.cs
index 18762f3..ed65ced 100644
--- a/DownKyi.Core/BiliApi/Video/VideoInfo.cs
+++ b/DownKyi.Core/BiliApi/Video/VideoInfo.cs
@@ -16,6 +16,7 @@ namespace DownKyi.Core.BiliApi.Video
///
public static VideoView VideoViewInfo(string bvid = null, long aid = -1)
{
+ // https://api.bilibili.com/x/web-interface/view/detail?bvid=BV1Sg411F7cb&aid=969147110&need_operation_card=1&web_rm_repeat=1&need_elec=1&out_referer=https%3A%2F%2Fspace.bilibili.com%2F42018135%2Ffavlist%3Ffid%3D94341835
string baseUrl = "https://api.bilibili.com/x/web-interface/view";
string referer = "https://www.bilibili.com";
string url;
diff --git a/DownKyi.Core/BiliApi/WebClient.cs b/DownKyi.Core/BiliApi/WebClient.cs
index 2fa1c28..9a514e0 100644
--- a/DownKyi.Core/BiliApi/WebClient.cs
+++ b/DownKyi.Core/BiliApi/WebClient.cs
@@ -49,7 +49,7 @@ namespace DownKyi.Core.BiliApi
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
request.Method = method;
request.Timeout = 30 * 1000;
- request.UserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.77 Safari/537.36";
+ request.UserAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.99 Safari/537.36";
//request.ContentType = "application/json,text/html,application/xhtml+xml,application/xml;charset=UTF-8";
request.Headers["accept-language"] = "zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7";
request.Headers["accept-encoding"] = "gzip, deflate, br";
diff --git a/DownKyi.Core/Storage/Database/Download/DownloadDb.cs b/DownKyi.Core/Storage/Database/Download/DownloadDb.cs
index a467891..7d1b0c5 100644
--- a/DownKyi.Core/Storage/Database/Download/DownloadDb.cs
+++ b/DownKyi.Core/Storage/Database/Download/DownloadDb.cs
@@ -11,9 +11,14 @@ namespace DownKyi.Core.Storage.Database.Download
public class DownloadDb
{
private const string key = "bdb8eb69-3698-4af9-b722-9312d0fba623";
- private readonly DbHelper dbHelper = new DbHelper(StorageManager.GetDownload(), key);
protected string tableName = "download";
+#if DEBUG
+ private readonly DbHelper dbHelper = new DbHelper(StorageManager.GetDownload().Replace(".db","_debug.db"));
+#else
+ private readonly DbHelper dbHelper = new DbHelper(StorageManager.GetDownload(), key);
+#endif
+
///
/// 关闭数据库连接
///
diff --git a/DownKyi/App.xaml.cs b/DownKyi/App.xaml.cs
index ace8540..41e07d7 100644
--- a/DownKyi/App.xaml.cs
+++ b/DownKyi/App.xaml.cs
@@ -18,6 +18,7 @@ using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Linq;
using System.Threading;
+using System.Threading.Tasks;
using System.Windows;
namespace DownKyi
@@ -58,57 +59,63 @@ namespace DownKyi
DownloadedList.AddRange(downloadedItems);
// 下载列表发生变化时执行的任务
- DownloadingList.CollectionChanged += new NotifyCollectionChangedEventHandler((object sender, NotifyCollectionChangedEventArgs e) =>
+ DownloadingList.CollectionChanged += new NotifyCollectionChangedEventHandler(async (object sender, NotifyCollectionChangedEventArgs e) =>
{
- if (e.Action == NotifyCollectionChangedAction.Add)
+ await Task.Run(() =>
{
- foreach (object item in e.NewItems)
+ if (e.Action == NotifyCollectionChangedAction.Add)
{
- if (item is DownloadingItem downloading)
+ foreach (object item in e.NewItems)
{
- //Console.WriteLine("DownloadingList添加");
- downloadStorageService.AddDownloading(downloading);
+ if (item is DownloadingItem downloading)
+ {
+ //Console.WriteLine("DownloadingList添加");
+ downloadStorageService.AddDownloading(downloading);
+ }
}
}
- }
- if (e.Action == NotifyCollectionChangedAction.Remove)
- {
- foreach (object item in e.OldItems)
+ if (e.Action == NotifyCollectionChangedAction.Remove)
{
- if (item is DownloadingItem downloading)
+ foreach (object item in e.OldItems)
{
- //Console.WriteLine("DownloadingList移除");
- downloadStorageService.RemoveDownloading(downloading);
+ if (item is DownloadingItem downloading)
+ {
+ //Console.WriteLine("DownloadingList移除");
+ downloadStorageService.RemoveDownloading(downloading);
+ }
}
}
- }
+ });
});
// 下载完成列表发生变化时执行的任务
- DownloadedList.CollectionChanged += new NotifyCollectionChangedEventHandler((object sender, NotifyCollectionChangedEventArgs e) =>
+ DownloadedList.CollectionChanged += new NotifyCollectionChangedEventHandler(async (object sender, NotifyCollectionChangedEventArgs e) =>
{
- if (e.Action == NotifyCollectionChangedAction.Add)
+ await Task.Run(() =>
{
- foreach (object item in e.NewItems)
+ if (e.Action == NotifyCollectionChangedAction.Add)
{
- if (item is DownloadedItem downloaded)
+ foreach (object item in e.NewItems)
{
- //Console.WriteLine("DownloadedList添加");
- downloadStorageService.AddDownloaded(downloaded);
+ if (item is DownloadedItem downloaded)
+ {
+ //Console.WriteLine("DownloadedList添加");
+ downloadStorageService.AddDownloaded(downloaded);
+ }
}
}
- }
- if (e.Action == NotifyCollectionChangedAction.Remove)
- {
- foreach (object item in e.OldItems)
+ if (e.Action == NotifyCollectionChangedAction.Remove)
{
- if (item is DownloadedItem downloaded)
+ foreach (object item in e.OldItems)
{
- //Console.WriteLine("DownloadedList移除");
- downloadStorageService.RemoveDownloaded(downloaded);
+ if (item is DownloadedItem downloaded)
+ {
+ //Console.WriteLine("DownloadedList移除");
+ downloadStorageService.RemoveDownloaded(downloaded);
+ }
}
}
- }
+ });
});
// 启动下载服务
diff --git a/DownKyi/DownKyi.csproj b/DownKyi/DownKyi.csproj
index d3f6769..405275c 100644
--- a/DownKyi/DownKyi.csproj
+++ b/DownKyi/DownKyi.csproj
@@ -92,6 +92,7 @@
+
diff --git a/DownKyi/Models/DownloadStatus.cs b/DownKyi/Models/DownloadStatus.cs
index 8aa0bd7..a9a0753 100644
--- a/DownKyi/Models/DownloadStatus.cs
+++ b/DownKyi/Models/DownloadStatus.cs
@@ -6,6 +6,7 @@
WAIT_FOR_DOWNLOAD, // 等待下载,下载过,但是启动本次下载周期未开始,如重启程序后未开始
PAUSE_STARTED, // 暂停启动下载
PAUSE, // 暂停
+ //PAUSE_TO_WAIT, // 暂停后等待
DOWNLOADING, // 下载中
DOWNLOAD_SUCCEED, // 下载成功
DOWNLOAD_FAILED, // 下载失败
diff --git a/DownKyi/Services/Download/AddToDownloadService.cs b/DownKyi/Services/Download/AddToDownloadService.cs
new file mode 100644
index 0000000..d9ce014
--- /dev/null
+++ b/DownKyi/Services/Download/AddToDownloadService.cs
@@ -0,0 +1,377 @@
+using DownKyi.Core.BiliApi.BiliUtils;
+using DownKyi.Core.BiliApi.VideoStream;
+using DownKyi.Core.BiliApi.Zone;
+using DownKyi.Core.FileName;
+using DownKyi.Core.Logging;
+using DownKyi.Core.Settings;
+using DownKyi.Core.Utils;
+using DownKyi.Events;
+using DownKyi.Models;
+using DownKyi.Utils;
+using DownKyi.ViewModels.Dialogs;
+using DownKyi.ViewModels.DownloadManager;
+using DownKyi.ViewModels.PageViewModels;
+using Prism.Events;
+using Prism.Services.Dialogs;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Threading;
+
+namespace DownKyi.Services.Download
+{
+ ///
+ /// 添加到下载列表服务
+ ///
+ public class AddToDownloadService
+ {
+ private readonly string Tag = "AddToDownloadService";
+ private IInfoService videoInfoService;
+ private VideoInfoView videoInfoView;
+ private List videoSections;
+
+ // 下载内容
+ private bool downloadAudio = true;
+ private bool downloadVideo = true;
+ private bool downloadDanmaku = true;
+ private bool downloadSubtitle = true;
+ private bool downloadCover = true;
+
+ ///
+ /// 添加下载
+ ///
+ ///
+ public AddToDownloadService(PlayStreamType streamType)
+ {
+ switch (streamType)
+ {
+ case PlayStreamType.VIDEO:
+ videoInfoService = new VideoInfoService(null);
+ break;
+ case PlayStreamType.BANGUMI:
+ videoInfoService = new BangumiInfoService(null);
+ break;
+ case PlayStreamType.CHEESE:
+ videoInfoService = new CheeseInfoService(null);
+ break;
+ default:
+ break;
+ }
+ }
+
+ ///
+ /// 添加下载
+ ///
+ ///
+ ///
+ public AddToDownloadService(string id, PlayStreamType streamType)
+ {
+ switch (streamType)
+ {
+ case PlayStreamType.VIDEO:
+ videoInfoService = new VideoInfoService(id);
+ break;
+ case PlayStreamType.BANGUMI:
+ videoInfoService = new BangumiInfoService(id);
+ break;
+ case PlayStreamType.CHEESE:
+ videoInfoService = new CheeseInfoService(id);
+ break;
+ default:
+ break;
+ }
+ }
+
+ public void SetVideoInfoService(IInfoService videoInfoService)
+ {
+ this.videoInfoService = videoInfoService;
+ }
+
+ public void GetVideo(VideoInfoView videoInfoView, List videoSections)
+ {
+ this.videoInfoView = videoInfoView;
+ this.videoSections = videoSections;
+ }
+
+ public void GetVideo()
+ {
+ videoInfoView = videoInfoService.GetVideoView();
+ if (videoInfoView == null)
+ {
+ LogManager.Debug(Tag, "VideoInfoView is null.");
+ return;
+ }
+
+ videoSections = videoInfoService.GetVideoSections();
+ if (videoSections == null)
+ {
+ LogManager.Debug(Tag, "videoSections is not exist.");
+
+ videoSections = new List
+ {
+ new VideoSection
+ {
+ Id = 0,
+ Title = "default",
+ IsSelected = true,
+ VideoPages = videoInfoService.GetVideoPages()
+ }
+ };
+ }
+ }
+
+ ///
+ /// 解析视频流
+ ///
+ ///
+ public void ParseVideo(IInfoService videoInfoService)
+ {
+ if (videoSections == null) { return; }
+
+ foreach (VideoSection section in videoSections)
+ {
+ foreach (VideoPage page in section.VideoPages)
+ {
+ // 执行解析任务
+ videoInfoService.GetVideoStream(page);
+ }
+ }
+ }
+
+ ///
+ /// 选择文件夹和下载项
+ ///
+ ///
+ public string SetDirectory(IDialogService dialogService)
+ {
+ // 选择的下载文件夹
+ string directory = string.Empty;
+
+ // 是否使用默认下载目录
+ if (SettingsManager.GetInstance().IsUseSaveVideoRootPath() == AllowStatus.YES)
+ {
+ directory = SettingsManager.GetInstance().GetSaveVideoRootPath();
+ }
+ else
+ {
+ // 打开文件夹选择器
+ dialogService.ShowDialog(ViewDownloadSetterViewModel.Tag, null, result =>
+ {
+ if (result.Result == ButtonResult.OK)
+ {
+ // 选择的下载文件夹
+ directory = result.Parameters.GetValue("directory");
+
+ // 下载内容
+ downloadAudio = result.Parameters.GetValue("downloadAudio");
+ downloadVideo = result.Parameters.GetValue("downloadVideo");
+ downloadDanmaku = result.Parameters.GetValue("downloadDanmaku");
+ downloadSubtitle = result.Parameters.GetValue("downloadSubtitle");
+ downloadCover = result.Parameters.GetValue("downloadCover");
+ }
+ });
+ }
+
+ // 下载设置dialog中如果点击取消或者关闭窗口,
+ // 会返回空字符串,
+ // 这时直接退出
+ if (directory == string.Empty) { return null; }
+
+ // 文件夹不存在则创建
+ if (!Directory.Exists(directory))
+ {
+ Directory.CreateDirectory(directory);
+ }
+
+ return directory;
+ }
+
+ public int AddToDownload(IEventAggregator eventAggregator, string directory)
+ {
+ if (directory == null || directory == string.Empty) { return -1; }
+ if (videoSections == null) { return -1; }
+
+ // 视频计数
+ int i = 0;
+
+ // 添加到下载
+ foreach (VideoSection section in videoSections)
+ {
+ foreach (VideoPage page in section.VideoPages)
+ {
+ // 没有解析的也跳过
+ if (page.PlayUrl == null) { continue; }
+
+ // 判断VideoQuality
+ int retry = 0;
+ while (page.VideoQuality == null && retry < 5)
+ {
+ // 执行解析任务
+ videoInfoService.GetVideoStream(page);
+ retry++;
+ }
+ if (page.VideoQuality == null) { continue; }
+
+ // 判断是否同一个视频,需要cid、画质、音质、视频编码都相同
+
+ // 如果存在正在下载列表,则跳过,并提示
+ bool isDownloading = false;
+ foreach (DownloadingItem item in App.DownloadingList)
+ {
+ if (item.DownloadBase == null) { continue; }
+
+ if (item.DownloadBase.Cid == page.Cid && item.Resolution.Id == page.VideoQuality.Quality && item.AudioCodec.Name == page.AudioQualityFormat && item.VideoCodecName == page.VideoQuality.SelectedVideoCodec)
+ {
+ eventAggregator.GetEvent().Publish($"{page.Name}{DictionaryResource.GetString("TipAlreadyToAddDownloading")}");
+ isDownloading = true;
+ break;
+ }
+ }
+ if (isDownloading) { continue; }
+
+ // 如果存在下载完成列表,弹出选择框是否再次下载
+ bool isDownloaded = false;
+ foreach (DownloadedItem item in App.DownloadedList)
+ {
+ if (item.DownloadBase == null) { continue; }
+
+ if (item.DownloadBase.Cid == page.Cid && item.Resolution.Id == page.VideoQuality.Quality && item.AudioCodec.Name == page.AudioQualityFormat && item.VideoCodecName == page.VideoQuality.SelectedVideoCodec)
+ {
+ eventAggregator.GetEvent().Publish($"{page.Name}{DictionaryResource.GetString("TipAlreadyToAddDownloaded")}");
+ isDownloaded = true;
+ break;
+ }
+ }
+ if (isDownloaded) { continue; }
+
+ // 视频分区
+ int zoneId = -1;
+ List zoneList = VideoZone.Instance().GetZones();
+ ZoneAttr zone = zoneList.Find(it => it.Id == videoInfoView.TypeId);
+ if (zone != null)
+ {
+ if (zone.ParentId == 0)
+ {
+ zoneId = zone.Id;
+ }
+ else
+ {
+ ZoneAttr zoneParent = zoneList.Find(it => it.Id == zone.ParentId);
+ if (zoneParent != null)
+ {
+ zoneId = zoneParent.Id;
+ }
+ }
+ }
+
+ // 如果只有一个视频章节,则不在命名中出现
+ string sectionName = string.Empty;
+ if (videoSections.Count > 1)
+ {
+ sectionName = section.Title;
+ }
+
+ // 文件路径
+ List fileNameParts = SettingsManager.GetInstance().GetFileNameParts();
+ FileName fileName = FileName.Builder(fileNameParts)
+ .SetOrder(page.Order)
+ .SetSection(Format.FormatFileName(sectionName))
+ .SetMainTitle(Format.FormatFileName(videoInfoView.Title))
+ .SetPageTitle(Format.FormatFileName(page.Name))
+ .SetVideoZone(videoInfoView.VideoZone.Split('>')[0])
+ .SetAudioQuality(page.AudioQualityFormat)
+ .SetVideoQuality(page.VideoQuality == null ? "" : page.VideoQuality.QualityFormat)
+ .SetVideoCodec(page.VideoQuality == null ? "" : page.VideoQuality.SelectedVideoCodec.Contains("AVC") ? "AVC" : page.VideoQuality.SelectedVideoCodec.Contains("HEVC") ? "HEVC" : "");
+ string filePath = Path.Combine(directory, fileName.RelativePath());
+
+ // 视频类别
+ PlayStreamType playStreamType;
+ switch (videoInfoView.TypeId)
+ {
+ case -10:
+ playStreamType = PlayStreamType.CHEESE;
+ break;
+ case 13:
+ case 23:
+ case 177:
+ case 167:
+ case 11:
+ playStreamType = PlayStreamType.BANGUMI;
+ break;
+ case 1:
+ case 3:
+ case 129:
+ case 4:
+ case 36:
+ case 188:
+ case 234:
+ case 223:
+ case 160:
+ case 211:
+ case 217:
+ case 119:
+ case 155:
+ case 202:
+ case 5:
+ case 181:
+ default:
+ playStreamType = PlayStreamType.VIDEO;
+ break;
+ }
+
+ // 如果不存在,直接添加到下载列表
+ DownloadBase downloadBase = new DownloadBase
+ {
+ Bvid = page.Bvid,
+ Avid = page.Avid,
+ Cid = page.Cid,
+ EpisodeId = page.EpisodeId,
+ CoverUrl = videoInfoView.CoverUrl,
+ PageCoverUrl = page.FirstFrame,
+ ZoneId = zoneId,
+ FilePath = filePath,
+
+ Order = page.Order,
+ MainTitle = videoInfoView.Title,
+ Name = page.Name,
+ Duration = page.Duration,
+ VideoCodecName = page.VideoQuality.SelectedVideoCodec,
+ Resolution = new Quality { Name = page.VideoQuality.QualityFormat, Id = page.VideoQuality.Quality },
+ AudioCodec = Constant.GetAudioQualities().FirstOrDefault(t => { return t.Name == page.AudioQualityFormat; }),
+ };
+ Downloading downloading = new Downloading
+ {
+ PlayStreamType = playStreamType,
+ DownloadStatus = DownloadStatus.NOT_STARTED,
+ };
+
+ // 需要下载的内容
+ downloadBase.NeedDownloadContent["downloadAudio"] = downloadAudio;
+ downloadBase.NeedDownloadContent["downloadVideo"] = downloadVideo;
+ downloadBase.NeedDownloadContent["downloadDanmaku"] = downloadDanmaku;
+ downloadBase.NeedDownloadContent["downloadSubtitle"] = downloadSubtitle;
+ downloadBase.NeedDownloadContent["downloadCover"] = downloadCover;
+
+ DownloadingItem downloadingItem = new DownloadingItem
+ {
+ DownloadBase = downloadBase,
+ Downloading = downloading,
+ PlayUrl = page.PlayUrl,
+ };
+
+ // 添加到下载列表
+ App.PropertyChangeAsync(new Action(() =>
+ {
+ App.DownloadingList.Add(downloadingItem);
+ Thread.Sleep(10);
+ }));
+ i++;
+ }
+ }
+
+ return i;
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/DownKyi/Services/Download/AriaDownloadService.cs b/DownKyi/Services/Download/AriaDownloadService.cs
index 3cf1351..c563825 100644
--- a/DownKyi/Services/Download/AriaDownloadService.cs
+++ b/DownKyi/Services/Download/AriaDownloadService.cs
@@ -32,6 +32,9 @@ namespace DownKyi.Services.Download
{
private CancellationTokenSource tokenSource;
+ private int retry = 5;
+ private string nullMark = "";
+
public AriaDownloadService(ObservableCollection downloadingList, ObservableCollection downloadedList) : base(downloadingList, downloadedList)
{
Tag = "AriaDownloadService";
@@ -136,7 +139,11 @@ namespace DownKyi.Services.Download
else
{
// 记录本次下载的文件
- downloading.Downloading.DownloadFiles.Add(key, fileName);
+ try
+ {
+ downloading.Downloading.DownloadFiles.Add(key, fileName);
+ }
+ catch (ArgumentException) { }
}
// 开始下载
@@ -147,9 +154,8 @@ namespace DownKyi.Services.Download
return Path.Combine(path, fileName);
case DownloadResult.FAILED:
case DownloadResult.ABORT:
- return null;
default:
- return null;
+ return nullMark;
}
}
@@ -322,6 +328,11 @@ namespace DownKyi.Services.Download
// 下载速度
downloading.SpeedDisplay = string.Empty;
+ if (videoUid == nullMark)
+ {
+ return null;
+ }
+
string finalFile = $"{downloading.DownloadBase.FilePath}.mp4";
if (videoUid == null)
{
@@ -361,6 +372,9 @@ namespace DownKyi.Services.Download
if (downloading.PlayUrl != null && downloading.Downloading.DownloadStatus == DownloadStatus.NOT_STARTED)
{
+ // 设置下载状态
+ downloading.Downloading.DownloadStatus = DownloadStatus.DOWNLOADING;
+
return;
}
@@ -453,28 +467,42 @@ namespace DownKyi.Services.Download
{
int maxDownloading = SettingsManager.GetInstance().GetAriaMaxConcurrentDownloads();
int downloadingCount = 0;
- foreach (DownloadingItem downloading in downloadingList)
- {
- if (downloading.Downloading.DownloadStatus == DownloadStatus.DOWNLOADING)
- {
- downloadingCount++;
- }
- }
- foreach (DownloadingItem downloading in downloadingList)
+ try
{
- if (downloadingCount >= maxDownloading)
+ foreach (DownloadingItem downloading in downloadingList)
{
- break;
+ if (downloading.Downloading.DownloadStatus == DownloadStatus.DOWNLOADING)
+ {
+ downloadingCount++;
+ }
}
- // 开始下载
- if (downloading.Downloading.DownloadStatus == DownloadStatus.NOT_STARTED || downloading.Downloading.DownloadStatus == DownloadStatus.WAIT_FOR_DOWNLOAD)
+ foreach (DownloadingItem downloading in downloadingList)
{
- SingleDownload(downloading);
- downloadingCount++;
+ if (downloadingCount >= maxDownloading)
+ {
+ break;
+ }
+
+ // 开始下载
+ if (downloading.Downloading.DownloadStatus == DownloadStatus.NOT_STARTED || downloading.Downloading.DownloadStatus == DownloadStatus.WAIT_FOR_DOWNLOAD)
+ {
+ SingleDownload(downloading);
+ downloadingCount++;
+ }
}
}
+ catch (InvalidOperationException e)
+ {
+ Core.Utils.Debugging.Console.PrintLine("Start DoWork()发生InvalidOperationException异常: {0}", e);
+ LogManager.Error("Start DoWork() InvalidOperationException", e);
+ }
+ catch (Exception e)
+ {
+ Core.Utils.Debugging.Console.PrintLine("Start DoWork()发生异常: {0}", e);
+ LogManager.Error("Start DoWork()", e);
+ }
// 判断是否该结束线程,若为true,跳出while循环
if (cancellationToken.IsCancellationRequested)
@@ -485,7 +513,7 @@ namespace DownKyi.Services.Download
}
// 降低CPU占用
- Thread.Sleep(200);
+ Thread.Sleep(500);
}
}
@@ -515,6 +543,10 @@ namespace DownKyi.Services.Download
// 解析并依次下载音频、视频、弹幕、字幕、封面等内容
Parse(downloading);
+ // 设置下载状态
+ // 必须在解析之后设置为DOWNLOADING
+ downloading.Downloading.DownloadStatus = DownloadStatus.DOWNLOADING;
+
// 暂停
Pause(downloading);
// 是否存在
@@ -524,14 +556,24 @@ namespace DownKyi.Services.Download
return;
}
- // 设置下载状态
- downloading.Downloading.DownloadStatus = DownloadStatus.DOWNLOADING;
-
string audioUid = null;
// 如果需要下载音频
if (downloading.DownloadBase.NeedDownloadContent["downloadAudio"])
{
- audioUid = DownloadAudio(downloading);
+ //audioUid = DownloadAudio(downloading);
+ for (int i = 0; i < retry; i++)
+ {
+ audioUid = DownloadAudio(downloading);
+ if (audioUid != null && audioUid != "")
+ {
+ break;
+ }
+ }
+ }
+ if (audioUid == "")
+ {
+ DownloadFailed(downloading);
+ return;
}
// 暂停
@@ -547,7 +589,20 @@ namespace DownKyi.Services.Download
// 如果需要下载视频
if (downloading.DownloadBase.NeedDownloadContent["downloadVideo"])
{
- videoUid = DownloadVideo(downloading);
+ //videoUid = DownloadVideo(downloading);
+ for (int i = 0; i < retry; i++)
+ {
+ videoUid = DownloadVideo(downloading);
+ if (videoUid != null && videoUid != "")
+ {
+ break;
+ }
+ }
+ }
+ if (videoUid == "")
+ {
+ DownloadFailed(downloading);
+ return;
}
// 暂停
@@ -592,15 +647,16 @@ namespace DownKyi.Services.Download
}
string outputCover = null;
+ string outputPageCover = null;
// 如果需要下载封面
if (downloading.DownloadBase.NeedDownloadContent["downloadCover"])
{
string fileName = $"{downloading.DownloadBase.FilePath}.{GetImageExtension(downloading.DownloadBase.PageCoverUrl)}";
// page的封面
- outputCover = DownloadCover(downloading, downloading.DownloadBase.PageCoverUrl, fileName);
+ outputPageCover = DownloadCover(downloading, downloading.DownloadBase.PageCoverUrl, fileName);
// 封面
- DownloadCover(downloading, downloading.DownloadBase.CoverUrl, $"{path}/Cover.{GetImageExtension(downloading.DownloadBase.CoverUrl)}");
+ outputCover = DownloadCover(downloading, downloading.DownloadBase.CoverUrl, $"{path}/Cover.{GetImageExtension(downloading.DownloadBase.CoverUrl)}");
}
// 暂停
@@ -685,7 +741,7 @@ namespace DownKyi.Services.Download
bool isCover = true;
if (downloading.DownloadBase.NeedDownloadContent["downloadCover"])
{
- if (File.Exists(outputCover))
+ if (File.Exists(outputCover) || File.Exists(outputPageCover))
{
// 成功
isCover = true;
@@ -698,19 +754,11 @@ namespace DownKyi.Services.Download
if (!isMediaSuccess || !isDanmakuSuccess || !isSubtitleSuccess || !isCover)
{
- downloading.DownloadStatusTitle = DictionaryResource.GetString("DownloadFailed");
- downloading.DownloadContent = string.Empty;
- downloading.DownloadingFileSize = string.Empty;
- downloading.SpeedDisplay = string.Empty;
-
- downloading.Downloading.DownloadStatus = DownloadStatus.DOWNLOAD_FAILED;
- downloading.StartOrPause = ButtonIcon.Instance().Retry;
- downloading.StartOrPause.Fill = DictionaryResource.GetColor("ColorPrimary");
+ DownloadFailed(downloading);
return;
}
// 下载完成后处理
-
Downloaded downloaded = new Downloaded
{
MaxSpeedDisplay = Format.FormatSpeed(downloading.Downloading.MaxSpeed),
@@ -737,6 +785,22 @@ namespace DownKyi.Services.Download
}));
}
+ ///
+ /// 下载失败后的处理
+ ///
+ ///
+ private void DownloadFailed(DownloadingItem downloading)
+ {
+ downloading.DownloadStatusTitle = DictionaryResource.GetString("DownloadFailed");
+ downloading.DownloadContent = string.Empty;
+ downloading.DownloadingFileSize = string.Empty;
+ downloading.SpeedDisplay = string.Empty;
+
+ downloading.Downloading.DownloadStatus = DownloadStatus.DOWNLOAD_FAILED;
+ downloading.StartOrPause = ButtonIcon.Instance().Retry;
+ downloading.StartOrPause.Fill = DictionaryResource.GetColor("ColorPrimary");
+ }
+
///
/// 获取图片的扩展名
///
@@ -933,7 +997,17 @@ namespace DownKyi.Services.Download
private void AriaTellStatus(long totalLength, long completedLength, long speed, string gid)
{
// 当前的下载视频
- DownloadingItem video = downloadingList.FirstOrDefault(it => it.Downloading.Gid == 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;
diff --git a/DownKyi/Services/Download/DownloadStorageService.cs b/DownKyi/Services/Download/DownloadStorageService.cs
index e13deec..d0bab9e 100644
--- a/DownKyi/Services/Download/DownloadStorageService.cs
+++ b/DownKyi/Services/Download/DownloadStorageService.cs
@@ -15,6 +15,8 @@ namespace DownKyi.Services.Download
///
public void AddDownloading(DownloadingItem downloadingItem)
{
+ if (downloadingItem == null || downloadingItem.DownloadBase == null) { return; }
+
AddDownloadBase(downloadingItem.DownloadBase);
DownloadingDb downloadingDb = new DownloadingDb();
@@ -32,6 +34,8 @@ namespace DownKyi.Services.Download
///
public void RemoveDownloading(DownloadingItem downloadingItem)
{
+ if (downloadingItem == null || downloadingItem.DownloadBase == null) { return; }
+
RemoveDownloadBase(downloadingItem.DownloadBase.Uuid);
DownloadingDb downloadingDb = new DownloadingDb();
@@ -75,6 +79,8 @@ namespace DownKyi.Services.Download
///
public void UpdateDownloading(DownloadingItem downloadingItem)
{
+ if (downloadingItem == null || downloadingItem.DownloadBase == null) { return; }
+
UpdateDownloadBase(downloadingItem.DownloadBase);
DownloadingDb downloadingDb = new DownloadingDb();
@@ -92,6 +98,8 @@ namespace DownKyi.Services.Download
///
public void AddDownloaded(DownloadedItem downloadedItem)
{
+ if (downloadedItem == null || downloadedItem.DownloadBase == null) { return; }
+
AddDownloadBase(downloadedItem.DownloadBase);
DownloadedDb downloadedDb = new DownloadedDb();
@@ -109,6 +117,8 @@ namespace DownKyi.Services.Download
///
public void RemoveDownloaded(DownloadedItem downloadedItem)
{
+ if (downloadedItem == null || downloadedItem.DownloadBase == null) { return; }
+
RemoveDownloadBase(downloadedItem.DownloadBase.Uuid);
DownloadedDb downloadedDb = new DownloadedDb();
@@ -152,6 +162,8 @@ namespace DownKyi.Services.Download
///
public void UpdateDownloaded(DownloadedItem downloadedItem)
{
+ if (downloadedItem == null || downloadedItem.DownloadBase == null) { return; }
+
UpdateDownloadBase(downloadedItem.DownloadBase);
DownloadedDb downloadedDb = new DownloadedDb();
@@ -169,6 +181,8 @@ namespace DownKyi.Services.Download
///
private void AddDownloadBase(DownloadBase downloadBase)
{
+ if (downloadBase == null) { return; }
+
DownloadBaseDb downloadBaseDb = new DownloadBaseDb();
object obj = downloadBaseDb.QueryById(downloadBase.Uuid);
if (obj == null)
@@ -208,6 +222,8 @@ namespace DownKyi.Services.Download
///
private void UpdateDownloadBase(DownloadBase downloadBase)
{
+ if (downloadBase == null) { return; }
+
DownloadBaseDb downloadBaseDb = new DownloadBaseDb();
downloadBaseDb.Update(downloadBase.Uuid, downloadBase);
downloadBaseDb.Close();
diff --git a/DownKyi/Services/SearchService.cs b/DownKyi/Services/SearchService.cs
index f1426da..8ead7bd 100644
--- a/DownKyi/Services/SearchService.cs
+++ b/DownKyi/Services/SearchService.cs
@@ -8,7 +8,17 @@ namespace DownKyi.Services
public class SearchService
{
///
- /// 解析支持的输入
+ /// 解析支持的输入,
+ /// 支持的格式有:
+ /// av号:av170001, AV170001, https://www.bilibili.com/video/av170001
+ /// BV号:BV17x411w7KC, https://www.bilibili.com/video/BV17x411w7KC
+ /// 番剧(电影、电视剧)ss号:ss32982, SS32982, https://www.bilibili.com/bangumi/play/ss32982
+ /// 番剧(电影、电视剧)ep号:ep317925, EP317925, https://www.bilibili.com/bangumi/play/ep317925
+ /// 番剧(电影、电视剧)md号:md28228367, MD28228367, https://www.bilibili.com/bangumi/media/md28228367
+ /// 课程ss号:https://www.bilibili.com/cheese/play/ss205
+ /// 课程ep号:https://www.bilibili.com/cheese/play/ep3489
+ /// 收藏夹:ml1329019876, ML1329019876, https://www.bilibili.com/medialist/detail/ml1329019876
+ /// 用户空间:uid928123, UID928123, uid:928123, UID:928123, https://space.bilibili.com/928123
///
///
///
diff --git a/DownKyi/Services/Utils.cs b/DownKyi/Services/Utils.cs
index b0f55b3..7489c1c 100644
--- a/DownKyi/Services/Utils.cs
+++ b/DownKyi/Services/Utils.cs
@@ -97,7 +97,13 @@ namespace DownKyi.Services
private static List GetAudioQualityFormatList(PlayUrl playUrl, int defaultAudioQuality)
{
List audioQualityFormatList = new List();
- foreach (var audio in playUrl.Dash.Audio)
+
+ if (playUrl.Dash.Audio == null)
+ {
+ return audioQualityFormatList;
+ }
+
+ foreach (PlayUrlDashVideo audio in playUrl.Dash.Audio)
{
// 音质id大于设置画质时,跳过
if (audio.Id > defaultAudioQuality) { continue; }
@@ -126,7 +132,13 @@ namespace DownKyi.Services
private static List GetVideoQualityList(PlayUrl playUrl, UserInfoSettings userInfo, int defaultQuality, VideoCodecs videoCodecs)
{
List videoQualityList = new List();
- foreach (var video in playUrl.Dash.Video)
+
+ if (playUrl.Dash.Video == null)
+ {
+ return videoQualityList;
+ }
+
+ foreach (PlayUrlDashVideo video in playUrl.Dash.Video)
{
// 画质id大于设置画质时,跳过
if (video.Id > defaultQuality) { continue; }
@@ -139,7 +151,7 @@ namespace DownKyi.Services
}
string qualityFormat = string.Empty;
- var selectedQuality = playUrl.SupportFormats.FirstOrDefault(t => t.Quality == video.Id);
+ PlayUrlSupportFormat selectedQuality = playUrl.SupportFormats.FirstOrDefault(t => t.Quality == video.Id);
if (selectedQuality != null)
{
qualityFormat = selectedQuality.NewDescription;
@@ -148,13 +160,13 @@ namespace DownKyi.Services
// 寻找是否已存在这个画质
// 不存在则添加,存在则修改
string codecName = GetVideoCodecName(video.Codecs);
- var videoQualityExist = videoQualityList.FirstOrDefault(t => t.Quality == video.Id);
+ VideoQuality videoQualityExist = videoQualityList.FirstOrDefault(t => t.Quality == video.Id);
if (videoQualityExist == null)
{
- var videoCodecList = new List();
+ List videoCodecList = new List();
ListHelper.AddUnique(videoCodecList, codecName);
- var videoQuality = new VideoQuality()
+ VideoQuality videoQuality = new VideoQuality()
{
Quality = video.Id,
QualityFormat = qualityFormat,
@@ -171,7 +183,7 @@ namespace DownKyi.Services
}
// 设置选中的视频编码
- var selectedVideoQuality = videoQualityList.FirstOrDefault(t => t.Quality == video.Id);
+ VideoQuality selectedVideoQuality = videoQualityList.FirstOrDefault(t => t.Quality == video.Id);
switch (videoCodecs)
{
case VideoCodecs.AVC:
@@ -186,6 +198,10 @@ namespace DownKyi.Services
videoQualityList[videoQualityList.IndexOf(selectedVideoQuality)].SelectedVideoCodec = "H.265/HEVC";
}
break;
+ case VideoCodecs.NONE:
+ break;
+ default:
+ break;
}
if (videoQualityList[videoQualityList.IndexOf(selectedVideoQuality)].VideoCodecList.Count == 1)
@@ -205,7 +221,7 @@ namespace DownKyi.Services
///
internal static string GetVideoCodecName(string origin)
{
- return origin.Contains("avc") ? "H.264/AVC" : origin.Contains("hev") ? "H.265/HEVC" : "";
+ return origin.Contains("avc") ? "H.264/AVC" : origin.Contains("hev") ? "H.265/HEVC" : origin.Contains("dvh") ? "dolby" : "";
}
}
diff --git a/DownKyi/Services/VideoInfoService.cs b/DownKyi/Services/VideoInfoService.cs
index 5659cca..3bec45f 100644
--- a/DownKyi/Services/VideoInfoService.cs
+++ b/DownKyi/Services/VideoInfoService.cs
@@ -102,7 +102,7 @@ namespace DownKyi.Services
List videoSections = new List();
- foreach (var section in videoView.UgcSeason.Sections)
+ foreach (UgcSection section in videoView.UgcSeason.Sections)
{
List pages = new List();
int order = 0;
diff --git a/DownKyi/Utils/DialogUtils.cs b/DownKyi/Utils/DialogUtils.cs
index 2376ae7..41b08d7 100644
--- a/DownKyi/Utils/DialogUtils.cs
+++ b/DownKyi/Utils/DialogUtils.cs
@@ -34,11 +34,7 @@ namespace DownKyi.Utils
Filter = "mp4 (*.mp4)|*.mp4"
};
var showDialog = dialog.ShowDialog();
- if (showDialog == true)
- {
- return dialog.FileName;
- }
- else { return ""; }
+ return showDialog == true ? dialog.FileName : "";
}
}
diff --git a/DownKyi/ViewModels/DownloadManager/DownloadBaseItem.cs b/DownKyi/ViewModels/DownloadManager/DownloadBaseItem.cs
index 454f94b..4586b8a 100644
--- a/DownKyi/ViewModels/DownloadManager/DownloadBaseItem.cs
+++ b/DownKyi/ViewModels/DownloadManager/DownloadBaseItem.cs
@@ -18,7 +18,10 @@ namespace DownKyi.ViewModels.DownloadManager
{
downloadBase = value;
- ZoneImage = (DrawingImage)Application.Current.Resources[VideoZoneIcon.Instance().GetZoneImageKey(DownloadBase.ZoneId)];
+ if (value != null)
+ {
+ ZoneImage = (DrawingImage)Application.Current.Resources[VideoZoneIcon.Instance().GetZoneImageKey(DownloadBase.ZoneId)];
+ }
}
}
@@ -33,7 +36,7 @@ namespace DownKyi.ViewModels.DownloadManager
// 视频序号
public int Order
{
- get => DownloadBase.Order;
+ get => DownloadBase == null ? 0 : DownloadBase.Order;
set
{
DownloadBase.Order = value;
@@ -44,7 +47,7 @@ namespace DownKyi.ViewModels.DownloadManager
// 视频主标题
public string MainTitle
{
- get => DownloadBase.MainTitle;
+ get => DownloadBase == null ? "" : DownloadBase.MainTitle;
set
{
DownloadBase.MainTitle = value;
@@ -55,7 +58,7 @@ namespace DownKyi.ViewModels.DownloadManager
// 视频标题
public string Name
{
- get => DownloadBase.Name;
+ get => DownloadBase == null ? "" : DownloadBase.Name;
set
{
DownloadBase.Name = value;
@@ -66,7 +69,7 @@ namespace DownKyi.ViewModels.DownloadManager
// 时长
public string Duration
{
- get => DownloadBase.Duration;
+ get => DownloadBase == null ? "" : DownloadBase.Duration;
set
{
DownloadBase.Duration = value;
@@ -77,7 +80,7 @@ namespace DownKyi.ViewModels.DownloadManager
// 视频编码名称,AVC、HEVC
public string VideoCodecName
{
- get => DownloadBase.VideoCodecName;
+ get => DownloadBase == null ? "" : DownloadBase.VideoCodecName;
set
{
DownloadBase.VideoCodecName = value;
@@ -88,7 +91,7 @@ namespace DownKyi.ViewModels.DownloadManager
// 视频画质
public Quality Resolution
{
- get => DownloadBase.Resolution;
+ get => DownloadBase == null ? null : DownloadBase.Resolution;
set
{
DownloadBase.Resolution = value;
@@ -99,7 +102,7 @@ namespace DownKyi.ViewModels.DownloadManager
// 音频编码
public Quality AudioCodec
{
- get => DownloadBase.AudioCodec;
+ get => DownloadBase == null ? null : DownloadBase.AudioCodec;
set
{
DownloadBase.AudioCodec = value;
@@ -110,7 +113,7 @@ namespace DownKyi.ViewModels.DownloadManager
// 文件大小
public string FileSize
{
- get => DownloadBase.FileSize;
+ get => DownloadBase == null ? "" : DownloadBase.FileSize;
set
{
DownloadBase.FileSize = value;
diff --git a/DownKyi/ViewModels/DownloadManager/DownloadingItem.cs b/DownKyi/ViewModels/DownloadManager/DownloadingItem.cs
index 899df6f..dc51226 100644
--- a/DownKyi/ViewModels/DownloadManager/DownloadingItem.cs
+++ b/DownKyi/ViewModels/DownloadManager/DownloadingItem.cs
@@ -23,7 +23,7 @@ namespace DownKyi.ViewModels.DownloadManager
private Downloading downloading;
public Downloading Downloading
{
- get { return downloading; }
+ get => downloading;
set
{
downloading = value;
@@ -164,21 +164,26 @@ namespace DownKyi.ViewModels.DownloadManager
case DownloadStatus.NOT_STARTED:
case DownloadStatus.WAIT_FOR_DOWNLOAD:
Downloading.DownloadStatus = DownloadStatus.PAUSE_STARTED;
+ DownloadStatusTitle = DictionaryResource.GetString("Pausing");
StartOrPause = ButtonIcon.Instance().Start;
StartOrPause.Fill = DictionaryResource.GetColor("ColorPrimary");
break;
case DownloadStatus.PAUSE_STARTED:
Downloading.DownloadStatus = DownloadStatus.WAIT_FOR_DOWNLOAD;
+ DownloadStatusTitle = DictionaryResource.GetString("Waiting");
StartOrPause = ButtonIcon.Instance().Pause;
StartOrPause.Fill = DictionaryResource.GetColor("ColorPrimary");
break;
case DownloadStatus.PAUSE:
- Downloading.DownloadStatus = DownloadStatus.DOWNLOADING;
+ Downloading.DownloadStatus = DownloadStatus.WAIT_FOR_DOWNLOAD;
+ DownloadStatusTitle = DictionaryResource.GetString("Waiting");
StartOrPause = ButtonIcon.Instance().Pause;
StartOrPause.Fill = DictionaryResource.GetColor("ColorPrimary");
break;
+ //case DownloadStatus.PAUSE_TO_WAIT:
case DownloadStatus.DOWNLOADING:
Downloading.DownloadStatus = DownloadStatus.PAUSE;
+ DownloadStatusTitle = DictionaryResource.GetString("Pausing");
StartOrPause = ButtonIcon.Instance().Start;
StartOrPause.Fill = DictionaryResource.GetColor("ColorPrimary");
break;
@@ -188,6 +193,7 @@ namespace DownKyi.ViewModels.DownloadManager
break;
case DownloadStatus.DOWNLOAD_FAILED:
Downloading.DownloadStatus = DownloadStatus.WAIT_FOR_DOWNLOAD;
+ DownloadStatusTitle = DictionaryResource.GetString("Waiting");
StartOrPause = ButtonIcon.Instance().Pause;
StartOrPause.Fill = DictionaryResource.GetColor("ColorPrimary");
break;
diff --git a/DownKyi/ViewModels/DownloadManager/ViewDownloadFinishedViewModel.cs b/DownKyi/ViewModels/DownloadManager/ViewDownloadFinishedViewModel.cs
index 614e524..c4d94a1 100644
--- a/DownKyi/ViewModels/DownloadManager/ViewDownloadFinishedViewModel.cs
+++ b/DownKyi/ViewModels/DownloadManager/ViewDownloadFinishedViewModel.cs
@@ -1,7 +1,11 @@
using DownKyi.Core.Settings;
using Prism.Commands;
using Prism.Events;
+using System;
+using System.Collections.Generic;
using System.Collections.ObjectModel;
+using System.Linq;
+using System.Threading.Tasks;
namespace DownKyi.ViewModels.DownloadManager
{
@@ -89,9 +93,22 @@ namespace DownKyi.ViewModels.DownloadManager
///
/// 清空下载完成列表事件
///
- private void ExecuteClearAllDownloadedCommand()
+ private async void ExecuteClearAllDownloadedCommand()
{
- DownloadedList.Clear();
+ // 使用Clear()不能触发NotifyCollectionChangedAction.Remove事件
+ // 因此遍历删除
+ // DownloadingList中元素被删除后不能继续遍历
+ await Task.Run(() =>
+ {
+ List list = DownloadedList.ToList();
+ foreach (DownloadedItem item in list)
+ {
+ App.PropertyChangeAsync(new Action(() =>
+ {
+ App.DownloadedList.Remove(item);
+ }));
+ }
+ });
}
#endregion
diff --git a/DownKyi/ViewModels/DownloadManager/ViewDownloadingViewModel.cs b/DownKyi/ViewModels/DownloadManager/ViewDownloadingViewModel.cs
index 1985f53..a30c30b 100644
--- a/DownKyi/ViewModels/DownloadManager/ViewDownloadingViewModel.cs
+++ b/DownKyi/ViewModels/DownloadManager/ViewDownloadingViewModel.cs
@@ -3,7 +3,11 @@ using DownKyi.Models;
using DownKyi.Utils;
using Prism.Commands;
using Prism.Events;
+using System;
+using System.Collections.Generic;
using System.Collections.ObjectModel;
+using System.Linq;
+using System.Threading.Tasks;
namespace DownKyi.ViewModels.DownloadManager
{
@@ -45,7 +49,8 @@ namespace DownKyi.ViewModels.DownloadManager
{
case DownloadStatus.NOT_STARTED:
case DownloadStatus.WAIT_FOR_DOWNLOAD:
- downloading.Downloading.DownloadStatus = DownloadStatus.PAUSE_STARTED;
+ downloading.Downloading.DownloadStatus = DownloadStatus.PAUSE;
+ downloading.DownloadStatusTitle = DictionaryResource.GetString("Pausing");
downloading.StartOrPause = ButtonIcon.Instance().Start;
downloading.StartOrPause.Fill = DictionaryResource.GetColor("ColorPrimary");
break;
@@ -53,8 +58,10 @@ namespace DownKyi.ViewModels.DownloadManager
break;
case DownloadStatus.PAUSE:
break;
+ //case DownloadStatus.PAUSE_TO_WAIT:
case DownloadStatus.DOWNLOADING:
downloading.Downloading.DownloadStatus = DownloadStatus.PAUSE;
+ downloading.DownloadStatusTitle = DictionaryResource.GetString("Pausing");
downloading.StartOrPause = ButtonIcon.Instance().Start;
downloading.StartOrPause.Fill = DictionaryResource.GetColor("ColorPrimary");
break;
@@ -85,13 +92,19 @@ namespace DownKyi.ViewModels.DownloadManager
{
case DownloadStatus.NOT_STARTED:
case DownloadStatus.WAIT_FOR_DOWNLOAD:
+ downloading.Downloading.DownloadStatus = DownloadStatus.WAIT_FOR_DOWNLOAD;
+ downloading.DownloadStatusTitle = DictionaryResource.GetString("Waiting");
break;
case DownloadStatus.PAUSE_STARTED:
downloading.Downloading.DownloadStatus = DownloadStatus.WAIT_FOR_DOWNLOAD;
+ downloading.DownloadStatusTitle = DictionaryResource.GetString("Waiting");
break;
case DownloadStatus.PAUSE:
- downloading.Downloading.DownloadStatus = DownloadStatus.DOWNLOADING;
+ downloading.Downloading.DownloadStatus = DownloadStatus.WAIT_FOR_DOWNLOAD;
+ downloading.DownloadStatusTitle = DictionaryResource.GetString("Waiting");
break;
+ //case DownloadStatus.PAUSE_TO_WAIT:
+ // break;
case DownloadStatus.DOWNLOADING:
break;
case DownloadStatus.DOWNLOAD_SUCCEED:
@@ -100,6 +113,7 @@ namespace DownKyi.ViewModels.DownloadManager
break;
case DownloadStatus.DOWNLOAD_FAILED:
downloading.Downloading.DownloadStatus = DownloadStatus.WAIT_FOR_DOWNLOAD;
+ downloading.DownloadStatusTitle = DictionaryResource.GetString("Waiting");
break;
default:
break;
@@ -117,9 +131,22 @@ namespace DownKyi.ViewModels.DownloadManager
///
/// 删除所有下载事件
///
- private void ExecuteDeleteAllDownloadingCommand()
+ private async void ExecuteDeleteAllDownloadingCommand()
{
- DownloadingList.Clear();
+ // 使用Clear()不能触发NotifyCollectionChangedAction.Remove事件
+ // 因此遍历删除
+ // DownloadingList中元素被删除后不能继续遍历
+ await Task.Run(() =>
+ {
+ List list = DownloadingList.ToList();
+ foreach (DownloadingItem item in list)
+ {
+ App.PropertyChangeAsync(new Action(() =>
+ {
+ App.DownloadingList.Remove(item);
+ }));
+ }
+ });
}
#endregion
diff --git a/DownKyi/ViewModels/PageViewModels/Favorites.cs b/DownKyi/ViewModels/PageViewModels/Favorites.cs
index 96f1db8..ae77f79 100644
--- a/DownKyi/ViewModels/PageViewModels/Favorites.cs
+++ b/DownKyi/ViewModels/PageViewModels/Favorites.cs
@@ -1,4 +1,6 @@
-using Prism.Mvvm;
+using DownKyi.Images;
+using DownKyi.Utils;
+using Prism.Mvvm;
using System.Windows.Media.Imaging;
namespace DownKyi.ViewModels.PageViewModels
@@ -57,6 +59,34 @@ namespace DownKyi.ViewModels.PageViewModels
set => SetProperty(ref shareNumber, value);
}
+ private VectorImage play;
+ public VectorImage Play
+ {
+ get => play;
+ set => SetProperty(ref play, value);
+ }
+
+ private VectorImage like;
+ public VectorImage Like
+ {
+ get => like;
+ set => SetProperty(ref like, value);
+ }
+
+ private VectorImage favorite;
+ public VectorImage Favorite
+ {
+ get => favorite;
+ set => SetProperty(ref favorite, value);
+ }
+
+ private VectorImage share;
+ public VectorImage Share
+ {
+ get => share;
+ set => SetProperty(ref share, value);
+ }
+
private string description;
public string Description
{
@@ -84,5 +114,25 @@ namespace DownKyi.ViewModels.PageViewModels
get => upHeader;
set => SetProperty(ref upHeader, value);
}
+
+ public Favorites()
+ {
+ #region 属性初始化
+
+ Play = NormalIcon.Instance().Play;
+ Play.Fill = DictionaryResource.GetColor("ColorTextGrey2");
+
+ Like = NormalIcon.Instance().Like;
+ Like.Fill = DictionaryResource.GetColor("ColorTextGrey2");
+
+ Favorite = NormalIcon.Instance().Favorite;
+ Favorite.Fill = DictionaryResource.GetColor("ColorTextGrey2");
+
+ Share = NormalIcon.Instance().Share;
+ Share.Fill = DictionaryResource.GetColor("ColorTextGrey2");
+
+ #endregion
+ }
+
}
}
diff --git a/DownKyi/ViewModels/ViewIndexViewModel.cs b/DownKyi/ViewModels/ViewIndexViewModel.cs
index c052ef1..ff0a93d 100644
--- a/DownKyi/ViewModels/ViewIndexViewModel.cs
+++ b/DownKyi/ViewModels/ViewIndexViewModel.cs
@@ -198,23 +198,12 @@ namespace DownKyi.ViewModels
#endregion
-
#region 业务逻辑
///
/// 进入B站链接的处理逻辑,
/// 只负责处理输入,并跳转到视频详情页。
/// 不是支持的格式,则进入搜索页面。
- /// 支持的格式有:
- /// av号:av170001, AV170001, https://www.bilibili.com/video/av170001
- /// BV号:BV17x411w7KC, https://www.bilibili.com/video/BV17x411w7KC
- /// 番剧(电影、电视剧)ss号:ss32982, SS32982, https://www.bilibili.com/bangumi/play/ss32982
- /// 番剧(电影、电视剧)ep号:ep317925, EP317925, https://www.bilibili.com/bangumi/play/ep317925
- /// 番剧(电影、电视剧)md号:md28228367, MD28228367, https://www.bilibili.com/bangumi/media/md28228367
- /// 课程ss号:https://www.bilibili.com/cheese/play/ss205
- /// 课程ep号:https://www.bilibili.com/cheese/play/ep3489
- /// 收藏夹:ml1329019876, ML1329019876, https://www.bilibili.com/medialist/detail/ml1329019876
- /// 用户空间:uid928123, UID928123, uid:928123, UID:928123, https://space.bilibili.com/928123
///
private void EnterBili()
{
diff --git a/DownKyi/ViewModels/ViewPublicFavoritesViewModel.cs b/DownKyi/ViewModels/ViewPublicFavoritesViewModel.cs
index 87dc5ce..40d5013 100644
--- a/DownKyi/ViewModels/ViewPublicFavoritesViewModel.cs
+++ b/DownKyi/ViewModels/ViewPublicFavoritesViewModel.cs
@@ -1,12 +1,15 @@
-using DownKyi.Core.Logging;
+using DownKyi.Core.BiliApi.VideoStream;
+using DownKyi.Core.Logging;
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.ObjectModel;
using System.Threading.Tasks;
@@ -18,6 +21,8 @@ namespace DownKyi.ViewModels
{
public const string Tag = "PagePublicFavorites";
+ private readonly IDialogService dialogService;
+
#region 页面属性申明
private string pageName = Tag;
@@ -34,34 +39,6 @@ namespace DownKyi.ViewModels
set => SetProperty(ref arrowBack, value);
}
- private VectorImage play;
- public VectorImage Play
- {
- get => play;
- set => SetProperty(ref play, value);
- }
-
- private VectorImage like;
- public VectorImage Like
- {
- get => like;
- set => SetProperty(ref like, value);
- }
-
- private VectorImage favorite;
- public VectorImage Favorite
- {
- get => favorite;
- set => SetProperty(ref favorite, value);
- }
-
- private VectorImage share;
- public VectorImage Share
- {
- get => share;
- set => SetProperty(ref share, value);
- }
-
private Favorites favorites;
public Favorites Favorites
{
@@ -92,25 +69,15 @@ namespace DownKyi.ViewModels
#endregion
- public ViewPublicFavoritesViewModel(IEventAggregator eventAggregator) : base(eventAggregator)
+ public ViewPublicFavoritesViewModel(IEventAggregator eventAggregator, IDialogService dialogService) : base(eventAggregator)
{
+ this.dialogService = dialogService;
+
#region 属性初始化
ArrowBack = NavigationIcon.Instance().ArrowBack;
ArrowBack.Fill = DictionaryResource.GetColor("ColorTextDark");
- Play = NormalIcon.Instance().Play;
- Play.Fill = DictionaryResource.GetColor("ColorTextGrey2");
-
- Like = NormalIcon.Instance().Like;
- Like.Fill = DictionaryResource.GetColor("ColorTextGrey2");
-
- Favorite = NormalIcon.Instance().Favorite;
- Favorite.Fill = DictionaryResource.GetColor("ColorTextGrey2");
-
- Share = NormalIcon.Instance().Share;
- Share.Fill = DictionaryResource.GetColor("ColorTextGrey2");
-
FavoritesMedias = new ObservableCollection();
#endregion
@@ -185,6 +152,7 @@ namespace DownKyi.ViewModels
///
private void ExecuteAddToDownloadCommand()
{
+ AddToDownload(true);
}
// 添加所有视频到下载列表事件
@@ -196,6 +164,7 @@ namespace DownKyi.ViewModels
///
private void ExecuteAddAllToDownloadCommand()
{
+ AddToDownload(false);
}
// 列表选择事件
@@ -212,6 +181,48 @@ namespace DownKyi.ViewModels
#endregion
+ private async void AddToDownload(bool isOnlySelected)
+ {
+ // 收藏夹里只有视频
+ AddToDownloadService addToDownloadService = new AddToDownloadService(PlayStreamType.VIDEO);
+
+ // 选择文件夹
+ string directory = addToDownloadService.SetDirectory(dialogService);
+
+ // 视频计数
+ int i = 0;
+ await Task.Run(() =>
+ {
+ // 添加到下载
+ foreach (FavoritesMedia media in FavoritesMedias)
+ {
+ // 只下载选中项,跳过未选中项
+ 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 (i == 0)
+ {
+ eventAggregator.GetEvent().Publish(DictionaryResource.GetString("TipAddDownloadingZero"));
+ }
+ else
+ {
+ eventAggregator.GetEvent().Publish($"{DictionaryResource.GetString("TipAddDownloadingFinished1")}{i}{DictionaryResource.GetString("TipAddDownloadingFinished2")}");
+ }
+ }
+
///
/// 初始化页面元素
///
diff --git a/DownKyi/ViewModels/ViewVideoDetailViewModel.cs b/DownKyi/ViewModels/ViewVideoDetailViewModel.cs
index 3937afb..f6f5db7 100644
--- a/DownKyi/ViewModels/ViewVideoDetailViewModel.cs
+++ b/DownKyi/ViewModels/ViewVideoDetailViewModel.cs
@@ -1,18 +1,14 @@
using DownKyi.Core.BiliApi.BiliUtils;
using DownKyi.Core.BiliApi.VideoStream;
-using DownKyi.Core.BiliApi.Zone;
-using DownKyi.Core.FileName;
using DownKyi.Core.Logging;
using DownKyi.Core.Settings;
-using DownKyi.Core.Utils;
using DownKyi.CustomControl;
using DownKyi.Events;
using DownKyi.Images;
-using DownKyi.Models;
using DownKyi.Services;
+using DownKyi.Services.Download;
using DownKyi.Utils;
using DownKyi.ViewModels.Dialogs;
-using DownKyi.ViewModels.DownloadManager;
using DownKyi.ViewModels.PageViewModels;
using Prism.Commands;
using Prism.Events;
@@ -21,7 +17,6 @@ using Prism.Services.Dialogs;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
-using System.IO;
using System.Linq;
using System.Threading.Tasks;
using System.Windows;
@@ -435,12 +430,12 @@ namespace DownKyi.ViewModels
{
foreach (VideoPage page in section.VideoPages)
{
- VideoPage videoPage = section.VideoPages.FirstOrDefault(t => t == page);
+ //VideoPage videoPage = section.VideoPages.FirstOrDefault(t => t == page);
- if (videoPage.IsSelected)
+ if (page.IsSelected)
{
// 执行解析任务
- UnityUpdateView(ParseVideo, null, videoPage);
+ UnityUpdateView(ParseVideo, null, page);
}
}
}
@@ -452,10 +447,10 @@ namespace DownKyi.ViewModels
{
foreach (VideoPage page in section.VideoPages)
{
- VideoPage videoPage = section.VideoPages.FirstOrDefault(t => t == page);
+ //VideoPage videoPage = section.VideoPages.FirstOrDefault(t => t == page);
// 执行解析任务
- UnityUpdateView(ParseVideo, null, videoPage);
+ UnityUpdateView(ParseVideo, null, page);
}
}
}
@@ -465,10 +460,10 @@ namespace DownKyi.ViewModels
{
foreach (VideoPage page in section.VideoPages)
{
- VideoPage videoPage = section.VideoPages.FirstOrDefault(t => t == page);
+ //VideoPage videoPage = section.VideoPages.FirstOrDefault(t => t == page);
// 执行解析任务
- UnityUpdateView(ParseVideo, null, videoPage);
+ UnityUpdateView(ParseVideo, null, page);
}
}
break;
@@ -504,211 +499,37 @@ namespace DownKyi.ViewModels
///
/// 添加到下载列表事件
///
- private void ExecuteAddToDownloadCommand()
+ private async void ExecuteAddToDownloadCommand()
{
- // 选择的下载文件夹
- string directory = string.Empty;
-
- // 下载内容
- bool downloadAudio = true;
- bool downloadVideo = true;
- bool downloadDanmaku = true;
- bool downloadSubtitle = true;
- bool downloadCover = true;
-
- // 是否使用默认下载目录
- if (SettingsManager.GetInstance().IsUseSaveVideoRootPath() == AllowStatus.YES)
+ AddToDownloadService addToDownloadService = null;
+ // 视频
+ if (ParseEntrance.IsAvUrl(InputText) || ParseEntrance.IsBvUrl(InputText))
{
- directory = SettingsManager.GetInstance().GetSaveVideoRootPath();
+ addToDownloadService = new AddToDownloadService(PlayStreamType.VIDEO);
}
- else
+ // 番剧(电影、电视剧)
+ if (ParseEntrance.IsBangumiSeasonUrl(InputText) || ParseEntrance.IsBangumiEpisodeUrl(InputText) || ParseEntrance.IsBangumiMediaUrl(InputText))
{
- // 打开文件夹选择器
- dialogService.ShowDialog(ViewDownloadSetterViewModel.Tag, null, result =>
- {
- if (result.Result == ButtonResult.OK)
- {
- // 选择的下载文件夹
- directory = result.Parameters.GetValue("directory");
-
- // 下载内容
- downloadAudio = result.Parameters.GetValue("downloadAudio");
- downloadVideo = result.Parameters.GetValue("downloadVideo");
- downloadDanmaku = result.Parameters.GetValue("downloadDanmaku");
- downloadSubtitle = result.Parameters.GetValue("downloadSubtitle");
- downloadCover = result.Parameters.GetValue("downloadCover");
- }
- });
+ addToDownloadService = new AddToDownloadService(PlayStreamType.BANGUMI);
}
-
- // 下载设置dialog中如果点击取消或者关闭窗口,
- // 会返回空字符串,
- // 这时直接退出
- if (directory == string.Empty) { return; }
-
- // 文件夹不存在则创建
- if (!Directory.Exists(directory))
+ // 课程
+ if (ParseEntrance.IsCheeseSeasonUrl(InputText) || ParseEntrance.IsCheeseEpisodeUrl(InputText))
{
- Directory.CreateDirectory(directory);
+ addToDownloadService = new AddToDownloadService(PlayStreamType.CHEESE);
}
- // 添加视频计数
- int i = 0;
+ // 选择文件夹
+ string directory = addToDownloadService.SetDirectory(dialogService);
- // 添加到下载
- foreach (VideoSection section in VideoSections)
+ // 视频计数
+ int i = 0;
+ await Task.Run(() =>
{
- foreach (VideoPage page in section.VideoPages)
- {
- // 只下载选中项,跳过未选中项
- if (!page.IsSelected) { continue; }
-
- // 没有解析的也跳过
- if (page.PlayUrl == null) { continue; }
-
- // 判断是否同一个视频,需要cid、画质、音质、视频编码都相同
-
- // 如果存在正在下载列表,则跳过,并提示
- foreach (DownloadingItem item in App.DownloadingList)
- {
- if (item.DownloadBase.Cid == page.Cid && item.Resolution.Id == page.VideoQuality.Quality && item.AudioCodec.Name == page.AudioQualityFormat && item.VideoCodecName == page.VideoQuality.SelectedVideoCodec)
- {
- eventAggregator.GetEvent().Publish($"{page.Name}{DictionaryResource.GetString("TipAlreadyToAddDownloading")}");
- continue;
- }
- }
-
- // 如果存在下载完成列表,弹出选择框是否再次下载
- foreach (DownloadedItem item in App.DownloadedList)
- {
- if (item.DownloadBase.Cid == page.Cid && item.Resolution.Id == page.VideoQuality.Quality && item.AudioCodec.Name == page.AudioQualityFormat && item.VideoCodecName == page.VideoQuality.SelectedVideoCodec)
- {
- eventAggregator.GetEvent().Publish($"{page.Name}{DictionaryResource.GetString("TipAlreadyToAddDownloaded")}");
- continue;
- }
- }
-
- // 视频分区
- int zoneId = -1;
- List zoneList = VideoZone.Instance().GetZones();
- ZoneAttr zone = zoneList.Find(it => it.Id == VideoInfoView.TypeId);
- if (zone != null)
- {
- if (zone.ParentId == 0)
- {
- zoneId = zone.Id;
- }
- else
- {
- ZoneAttr zoneParent = zoneList.Find(it => it.Id == zone.ParentId);
- if (zoneParent != null)
- {
- zoneId = zoneParent.Id;
- }
- }
- }
-
- // 如果只有一个视频章节,则不在命名中出现
- string sectionName = string.Empty;
- if (VideoSections.Count > 1)
- {
- sectionName = section.Title;
- }
-
- // 文件路径
- List fileNameParts = SettingsManager.GetInstance().GetFileNameParts();
- FileName fileName = FileName.Builder(fileNameParts)
- .SetOrder(page.Order)
- .SetSection(Format.FormatFileName(sectionName))
- .SetMainTitle(Format.FormatFileName(VideoInfoView.Title))
- .SetPageTitle(Format.FormatFileName(page.Name))
- .SetVideoZone(VideoInfoView.VideoZone.Split('>')[0])
- .SetAudioQuality(page.AudioQualityFormat)
- .SetVideoQuality(page.VideoQuality.QualityFormat)
- .SetVideoCodec(page.VideoQuality.SelectedVideoCodec.Contains("AVC") ? "AVC" : page.VideoQuality.SelectedVideoCodec.Contains("HEVC") ? "HEVC" : "");
- string filePath = Path.Combine(directory, fileName.RelativePath());
-
- // 视频类别
- PlayStreamType playStreamType;
- switch (VideoInfoView.TypeId)
- {
- case -10:
- playStreamType = PlayStreamType.CHEESE;
- break;
- case 13:
- case 23:
- case 177:
- case 167:
- case 11:
- playStreamType = PlayStreamType.BANGUMI;
- break;
- case 1:
- case 3:
- case 129:
- case 4:
- case 36:
- case 188:
- case 234:
- case 223:
- case 160:
- case 211:
- case 217:
- case 119:
- case 155:
- case 202:
- case 5:
- case 181:
- default:
- playStreamType = PlayStreamType.VIDEO;
- break;
- }
-
- // 如果不存在,直接添加到下载列表
- DownloadBase downloadBase = new DownloadBase
- {
- Bvid = page.Bvid,
- Avid = page.Avid,
- Cid = page.Cid,
- EpisodeId = page.EpisodeId,
- CoverUrl = VideoInfoView.CoverUrl,
- PageCoverUrl = page.FirstFrame,
- ZoneId = zoneId,
- FilePath = filePath,
-
- Order = page.Order,
- MainTitle = VideoInfoView.Title,
- Name = page.Name,
- Duration = page.Duration,
- VideoCodecName = page.VideoQuality.SelectedVideoCodec,
- Resolution = new Quality { Name = page.VideoQuality.QualityFormat, Id = page.VideoQuality.Quality },
- AudioCodec = Constant.GetAudioQualities().FirstOrDefault(t => { return t.Name == page.AudioQualityFormat; }),
- };
- Downloading downloading = new Downloading
- {
- PlayStreamType = playStreamType,
- DownloadStatus = DownloadStatus.NOT_STARTED,
- };
-
- // 需要下载的内容
- downloadBase.NeedDownloadContent["downloadAudio"] = downloadAudio;
- downloadBase.NeedDownloadContent["downloadVideo"] = downloadVideo;
- downloadBase.NeedDownloadContent["downloadDanmaku"] = downloadDanmaku;
- downloadBase.NeedDownloadContent["downloadSubtitle"] = downloadSubtitle;
- downloadBase.NeedDownloadContent["downloadCover"] = downloadCover;
-
- DownloadingItem downloadingItem = new DownloadingItem
- {
- DownloadBase = downloadBase,
- Downloading = downloading,
- PlayUrl = page.PlayUrl,
- //ZoneImage = (DrawingImage)Application.Current.Resources[VideoZoneIcon.Instance().GetZoneImageKey(zoneId)],
- };
-
- // 添加到下载列表
- App.DownloadingList.Add(downloadingItem);
- i++;
- }
- }
+ // 传递video对象
+ addToDownloadService.GetVideo(VideoInfoView, VideoSections.ToList());
+ // 下载
+ i = addToDownloadService.AddToDownload(eventAggregator, directory);
+ });
// 通知用户添加到下载列表的结果
if (i == 0)
@@ -732,7 +553,6 @@ namespace DownKyi.ViewModels
#endregion
-
#region 业务逻辑
///
diff --git a/DownKyi/Views/DownloadManager/ViewDownloadFinished.xaml b/DownKyi/Views/DownloadManager/ViewDownloadFinished.xaml
index 27bbf81..357014f 100644
--- a/DownKyi/Views/DownloadManager/ViewDownloadFinished.xaml
+++ b/DownKyi/Views/DownloadManager/ViewDownloadFinished.xaml
@@ -20,7 +20,7 @@
-
+
+ ItemsSource="{Binding DownloadedList}"
+ VirtualizingPanel.IsVirtualizing="True"
+ VirtualizingPanel.IsVirtualizingWhenGrouping="True">