修复一些bug;支持杜比全景声、杜比视界,最高8K画质

pull/252/head
leiurayer 3 years ago
parent 98f978679d
commit 537d0df17b

@ -1,7 +1,9 @@
using DownKyi.Core.Aria2cNet.Client; using DownKyi.Core.Aria2cNet.Client;
using DownKyi.Core.Aria2cNet.Client.Entity;
using DownKyi.Core.Logging; using DownKyi.Core.Logging;
using System; using System;
using System.Threading; using System.Threading;
using System.Threading.Tasks;
namespace DownKyi.Core.Aria2cNet namespace DownKyi.Core.Aria2cNet
{ {
@ -45,7 +47,7 @@ namespace DownKyi.Core.Aria2cNet
string filePath = ""; string filePath = "";
while (true) while (true)
{ {
var status = AriaClient.TellStatus(gid); Task<AriaTellStatus> status = AriaClient.TellStatus(gid);
if (status == null || status.Result == null) { continue; } if (status == null || status.Result == null) { continue; }
if (status.Result.Result == null && status.Result.Error != null) if (status.Result.Result == null && status.Result.Error != null)
@ -80,9 +82,12 @@ namespace DownKyi.Core.Aria2cNet
break; break;
} }
if (status.Result.Result.ErrorCode != null && status.Result.Result.ErrorCode != "0") if (status.Result.Result.ErrorCode != null && status.Result.Result.ErrorCode != "0")
{
if (status.Result != null)
{ {
Utils.Debugging.Console.PrintLine("ErrorMessage: " + status.Result.Result.ErrorMessage); Utils.Debugging.Console.PrintLine("ErrorMessage: " + status.Result.Result.ErrorMessage);
LogManager.Error("AriaManager", status.Result.Result.ErrorMessage); LogManager.Error("AriaManager", status.Result.Result.ErrorMessage);
}
//// 如果返回状态码不是200则继续 //// 如果返回状态码不是200则继续
//if (status.Result.Result.ErrorMessage.Contains("The response status is not successful")) //if (status.Result.Result.ErrorMessage.Contains("The response status is not successful"))
@ -92,9 +97,12 @@ namespace DownKyi.Core.Aria2cNet
//} //}
// aira中删除记录 // aira中删除记录
var ariaRemove1 = AriaClient.RemoveDownloadResultAsync(gid); Task<AriaRemove> ariaRemove1 = AriaClient.RemoveDownloadResultAsync(gid);
Utils.Debugging.Console.PrintLine(ariaRemove1); Utils.Debugging.Console.PrintLine(ariaRemove1);
if (ariaRemove1.Result != null)
{
LogManager.Debug("AriaManager", ariaRemove1.Result.Result); LogManager.Debug("AriaManager", ariaRemove1.Result.Result);
}
// 返回回调信息,退出函数 // 返回回调信息,退出函数
OnDownloadFinish(false, null, gid, status.Result.Result.ErrorMessage); OnDownloadFinish(false, null, gid, status.Result.Result.ErrorMessage);
@ -116,7 +124,7 @@ namespace DownKyi.Core.Aria2cNet
while (true) while (true)
{ {
// 查询全局status // 查询全局status
var globalStatus = await AriaClient.GetGlobalStatAsync(); AriaGetGlobalStat globalStatus = await AriaClient.GetGlobalStatAsync();
if (globalStatus == null || globalStatus.Result == null) { continue; } if (globalStatus == null || globalStatus.Result == null) { continue; }
long globalSpeed = long.Parse(globalStatus.Result.DownloadSpeed); long globalSpeed = long.Parse(globalStatus.Result.DownloadSpeed);

@ -1093,8 +1093,9 @@ namespace DownKyi.Core.Aria2cNet.Client
} }
catch (WebException e) catch (WebException e)
{ {
Utils.Debugging.Console.PrintLine("Request()发生Web异常: {0}", e); //Utils.Debugging.Console.PrintLine("Request()发生Web异常: {0}", e);
LogManager.Error("AriaClient", e); //LogManager.Error("AriaClient", e);
//return Request(url, parameters, retry - 1); //return Request(url, parameters, retry - 1);
string html = string.Empty; string html = string.Empty;
@ -1108,8 +1109,8 @@ namespace DownKyi.Core.Aria2cNet.Client
} }
} }
Console.WriteLine($"本次请求使用的参数:{parameters}"); //Console.WriteLine($"本次请求使用的参数:{parameters}");
Console.WriteLine($"返回的web数据{html}"); //Console.WriteLine($"返回的web数据{html}");
return html; return html;
} }
catch (IOException e) catch (IOException e)

@ -6,6 +6,8 @@ namespace DownKyi.Core.BiliApi.BiliUtils
{ {
private static readonly List<Quality> resolutions = new List<Quality> private static readonly List<Quality> resolutions = new List<Quality>
{ {
new Quality { Name = "超高清 8K", Id = 127 },
new Quality { Name = "杜比视界", Id = 126 },
new Quality { Name = "HDR 真彩", Id = 125 }, new Quality { Name = "HDR 真彩", Id = 125 },
new Quality { Name = "4K 超清", Id = 120 }, new Quality { Name = "4K 超清", Id = 120 },
new Quality { Name = "1080P 60帧", Id = 116 }, new Quality { Name = "1080P 60帧", Id = 116 },
@ -22,6 +24,7 @@ namespace DownKyi.Core.BiliApi.BiliUtils
new Quality { Name = "64K", Id = 30216 }, new Quality { Name = "64K", Id = 30216 },
new Quality { Name = "132K", Id = 30232 }, new Quality { Name = "132K", Id = 30232 },
new Quality { Name = "192K", Id = 30280 }, new Quality { Name = "192K", Id = 30280 },
new Quality { Name = "Dolby Atmos", Id = 30250 },
}; };
/// <summary> /// <summary>

@ -16,6 +16,7 @@ namespace DownKyi.Core.BiliApi.Video
/// <returns></returns> /// <returns></returns>
public static VideoView VideoViewInfo(string bvid = null, long aid = -1) 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 baseUrl = "https://api.bilibili.com/x/web-interface/view";
string referer = "https://www.bilibili.com"; string referer = "https://www.bilibili.com";
string url; string url;

@ -16,5 +16,7 @@ namespace DownKyi.Core.BiliApi.VideoStream.Models
public List<PlayUrlDashVideo> Video { get; set; } public List<PlayUrlDashVideo> Video { get; set; }
[JsonProperty("audio")] [JsonProperty("audio")]
public List<PlayUrlDashVideo> Audio { get; set; } public List<PlayUrlDashVideo> Audio { get; set; }
[JsonProperty("dolby")]
public PlayUrlDashDolby Dolby { get; set; }
} }
} }

@ -0,0 +1,13 @@
using DownKyi.Core.BiliApi.Models;
using Newtonsoft.Json;
using System.Collections.Generic;
namespace DownKyi.Core.BiliApi.VideoStream.Models
{
public class PlayUrlDashDolby : BaseModel
{
// type
[JsonProperty("audio")]
public List<PlayUrlDashVideo> Audio { get; set; }
}
}

@ -8,9 +8,9 @@ namespace DownKyi.Core.BiliApi.VideoStream.Models
{ {
[JsonProperty("id")] [JsonProperty("id")]
public int Id { get; set; } public int Id { get; set; }
[JsonProperty("baseUrl")] [JsonProperty("base_url")]
public string BaseUrl { get; set; } public string BaseUrl { get; set; }
[JsonProperty("backupUrl")] [JsonProperty("backup_url")]
public List<string> BackupUrl { get; set; } public List<string> BackupUrl { get; set; }
// bandwidth // bandwidth
[JsonProperty("mimeType")] [JsonProperty("mimeType")]

@ -93,7 +93,7 @@ namespace DownKyi.Core.BiliApi.VideoStream
/// <returns></returns> /// <returns></returns>
public static PlayUrl GetVideoPlayUrl(long avid, string bvid, long cid, int quality = 125) public static PlayUrl GetVideoPlayUrl(long avid, string bvid, long cid, int quality = 125)
{ {
string baseUrl = $"https://api.bilibili.com/x/player/playurl?cid={cid}&qn={quality}&fourk=1&fnver=0&fnval=80"; string baseUrl = $"https://api.bilibili.com/x/player/playurl?cid={cid}&qn={quality}&fourk=1&fnver=0&fnval=4048";
string url; string url;
if (bvid != null) { url = $"{baseUrl}&bvid={bvid}"; } if (bvid != null) { url = $"{baseUrl}&bvid={bvid}"; }
else if (avid > -1) { url = $"{baseUrl}&aid={avid}"; } else if (avid > -1) { url = $"{baseUrl}&aid={avid}"; }
@ -112,7 +112,7 @@ namespace DownKyi.Core.BiliApi.VideoStream
/// <returns></returns> /// <returns></returns>
public static PlayUrl GetBangumiPlayUrl(long avid, string bvid, long cid, int quality = 125) public static PlayUrl GetBangumiPlayUrl(long avid, string bvid, long cid, int quality = 125)
{ {
string baseUrl = $"https://api.bilibili.com/pgc/player/web/playurl?cid={cid}&qn={quality}&fourk=1&fnver=0&fnval=80"; string baseUrl = $"https://api.bilibili.com/pgc/player/web/playurl?cid={cid}&qn={quality}&fourk=1&fnver=0&fnval=4048";
string url; string url;
if (bvid != null) { url = $"{baseUrl}&bvid={bvid}"; } if (bvid != null) { url = $"{baseUrl}&bvid={bvid}"; }
else if (avid > -1) { url = $"{baseUrl}&aid={avid}"; } else if (avid > -1) { url = $"{baseUrl}&aid={avid}"; }
@ -131,7 +131,7 @@ namespace DownKyi.Core.BiliApi.VideoStream
/// <returns></returns> /// <returns></returns>
public static PlayUrl GetCheesePlayUrl(long avid, string bvid, long cid, long episodeId, int quality = 125) public static PlayUrl GetCheesePlayUrl(long avid, string bvid, long cid, long episodeId, int quality = 125)
{ {
string baseUrl = $"https://api.bilibili.com/pugv/player/web/playurl?cid={cid}&qn={quality}&fourk=1&fnver=0&fnval=80"; string baseUrl = $"https://api.bilibili.com/pugv/player/web/playurl?cid={cid}&qn={quality}&fourk=1&fnver=0&fnval=4048";
string url; string url;
if (bvid != null) { url = $"{baseUrl}&bvid={bvid}"; } if (bvid != null) { url = $"{baseUrl}&bvid={bvid}"; }
else if (avid > -1) { url = $"{baseUrl}&aid={avid}"; } else if (avid > -1) { url = $"{baseUrl}&aid={avid}"; }

@ -49,7 +49,13 @@ namespace DownKyi.Core.BiliApi
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url); HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
request.Method = method; request.Method = method;
request.Timeout = 30 * 1000; 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";
// MacOS Safari
string safari = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_5) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.1.1 Safari/605.1.15";
// Windows 10 Chrome
string chrome = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.99 Safari/537.36";
request.UserAgent = chrome;
//request.ContentType = "application/json,text/html,application/xhtml+xml,application/xml;charset=UTF-8"; //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-language"] = "zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7";
request.Headers["accept-encoding"] = "gzip, deflate, br"; request.Headers["accept-encoding"] = "gzip, deflate, br";

@ -211,6 +211,7 @@
<Compile Include="BiliApi\VideoStream\Models\PlayerV2.cs" /> <Compile Include="BiliApi\VideoStream\Models\PlayerV2.cs" />
<Compile Include="BiliApi\VideoStream\Models\PlayUrl.cs" /> <Compile Include="BiliApi\VideoStream\Models\PlayUrl.cs" />
<Compile Include="BiliApi\VideoStream\Models\PlayUrlDash.cs" /> <Compile Include="BiliApi\VideoStream\Models\PlayUrlDash.cs" />
<Compile Include="BiliApi\VideoStream\Models\PlayUrlDashDolby.cs" />
<Compile Include="BiliApi\VideoStream\Models\PlayUrlDashVideo.cs" /> <Compile Include="BiliApi\VideoStream\Models\PlayUrlDashVideo.cs" />
<Compile Include="BiliApi\VideoStream\Models\PlayUrlDurl.cs" /> <Compile Include="BiliApi\VideoStream\Models\PlayUrlDurl.cs" />
<Compile Include="BiliApi\VideoStream\Models\PlayUrlSupportFormat.cs" /> <Compile Include="BiliApi\VideoStream\Models\PlayUrlSupportFormat.cs" />

@ -11,9 +11,14 @@ namespace DownKyi.Core.Storage.Database.Download
public class DownloadDb public class DownloadDb
{ {
private const string key = "bdb8eb69-3698-4af9-b722-9312d0fba623"; private const string key = "bdb8eb69-3698-4af9-b722-9312d0fba623";
private readonly DbHelper dbHelper = new DbHelper(StorageManager.GetDownload(), key);
protected string tableName = "download"; 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
/// <summary> /// <summary>
/// 关闭数据库连接 /// 关闭数据库连接
/// </summary> /// </summary>

@ -30,7 +30,7 @@ namespace DownKyi.Core.Storage
{ {
string header = GetCover(avid, bvid, cid, url); string header = GetCover(avid, bvid, cid, url);
return GetGetCoverThumbnail(header, width, height); return GetCoverThumbnail(header, width, height);
} }
/// <summary> /// <summary>
@ -40,10 +40,10 @@ namespace DownKyi.Core.Storage
/// <param name="width"></param> /// <param name="width"></param>
/// <param name="height"></param> /// <param name="height"></param>
/// <returns></returns> /// <returns></returns>
public BitmapImage GetGetCoverThumbnail(string cover, int width, int height) public BitmapImage GetCoverThumbnail(string cover, int width, int height)
{ {
var bitmap = new Bitmap(cover); Bitmap bitmap = new Bitmap(cover);
var thumbnail = bitmap.GetThumbnailImage(width, height, null, IntPtr.Zero); Image thumbnail = bitmap.GetThumbnailImage(width, height, null, IntPtr.Zero);
return StorageUtils.BitmapToBitmapImage(new Bitmap(thumbnail)); return StorageUtils.BitmapToBitmapImage(new Bitmap(thumbnail));
} }
@ -69,7 +69,7 @@ namespace DownKyi.Core.Storage
public string GetCover(long avid, string bvid, long cid, string url) public string GetCover(long avid, string bvid, long cid, string url)
{ {
CoverDb coverDb = new CoverDb(); CoverDb coverDb = new CoverDb();
var cover = coverDb.QueryByUrl(url); Cover cover = coverDb.QueryByUrl(url);
// 如果存在,直接返回 // 如果存在,直接返回
// 如果不存在,则先下载 // 如果不存在,则先下载
@ -195,7 +195,7 @@ namespace DownKyi.Core.Storage
/// <returns></returns> /// <returns></returns>
public bool IsLocal(CoverDb coverDb, string url) public bool IsLocal(CoverDb coverDb, string url)
{ {
var cover = coverDb.QueryByUrl(url); Cover cover = coverDb.QueryByUrl(url);
return cover != null; return cover != null;
} }
@ -207,7 +207,7 @@ namespace DownKyi.Core.Storage
/// <returns></returns> /// <returns></returns>
public string LocalCover(CoverDb coverDb, string url) public string LocalCover(CoverDb coverDb, string url)
{ {
var cover = coverDb.QueryByUrl(url); Cover cover = coverDb.QueryByUrl(url);
return cover.Md5; return cover.Md5;
} }

@ -174,6 +174,7 @@ namespace DownKyi.Core.Utils
public static string FormatFileName(string originName) public static string FormatFileName(string originName)
{ {
string destName = originName; string destName = originName;
// Windows中不能作为文件名的字符 // Windows中不能作为文件名的字符
destName = destName.Replace("\\", " "); destName = destName.Replace("\\", " ");
destName = destName.Replace("/", " "); destName = destName.Replace("/", " ");
@ -197,7 +198,8 @@ namespace DownKyi.Core.Utils
// 控制字符 // 控制字符
destName = Regex.Replace(destName, @"\p{C}+", string.Empty); destName = Regex.Replace(destName, @"\p{C}+", string.Empty);
return destName.Trim(); // 移除前导和尾部的空白字符、dot符
return destName.Trim().TrimStart('.').TrimEnd('.');
} }
} }

@ -1,15 +1,31 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.ObjectModel;
namespace DownKyi.Core.Utils namespace DownKyi.Core.Utils
{ {
public static class ListHelper public static class ListHelper
{ {
/// <summary>
/// 判断ObservableCollection中是否存在不存在则添加
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="list"></param>
/// <param name="item"></param>
public static void AddUnique<T>(ObservableCollection<T> list, T item)
{
if (!list.Contains(item))
{
list.Add(item);
}
}
/// <summary> /// <summary>
/// 判断List中是否存在不存在则添加 /// 判断List中是否存在不存在则添加
/// </summary> /// </summary>
/// <typeparam name="T"></typeparam> /// <typeparam name="T"></typeparam>
/// <param name="list"></param> /// <param name="list"></param>
/// <param name="item"></param>
public static void AddUnique<T>(List<T> list, T item) public static void AddUnique<T>(List<T> list, T item)
{ {
if (!list.Exists(t => t.Equals(item))) if (!list.Exists(t => t.Equals(item)))

@ -18,6 +18,7 @@ using System.Collections.ObjectModel;
using System.Collections.Specialized; using System.Collections.Specialized;
using System.Linq; using System.Linq;
using System.Threading; using System.Threading;
using System.Threading.Tasks;
using System.Windows; using System.Windows;
namespace DownKyi namespace DownKyi
@ -58,7 +59,9 @@ namespace DownKyi
DownloadedList.AddRange(downloadedItems); DownloadedList.AddRange(downloadedItems);
// 下载列表发生变化时执行的任务 // 下载列表发生变化时执行的任务
DownloadingList.CollectionChanged += new NotifyCollectionChangedEventHandler((object sender, NotifyCollectionChangedEventArgs e) => DownloadingList.CollectionChanged += new NotifyCollectionChangedEventHandler(async (object sender, NotifyCollectionChangedEventArgs e) =>
{
await Task.Run(() =>
{ {
if (e.Action == NotifyCollectionChangedAction.Add) if (e.Action == NotifyCollectionChangedAction.Add)
{ {
@ -83,9 +86,12 @@ namespace DownKyi
} }
} }
}); });
});
// 下载完成列表发生变化时执行的任务 // 下载完成列表发生变化时执行的任务
DownloadedList.CollectionChanged += new NotifyCollectionChangedEventHandler((object sender, NotifyCollectionChangedEventArgs e) => DownloadedList.CollectionChanged += new NotifyCollectionChangedEventHandler(async (object sender, NotifyCollectionChangedEventArgs e) =>
{
await Task.Run(() =>
{ {
if (e.Action == NotifyCollectionChangedAction.Add) if (e.Action == NotifyCollectionChangedAction.Add)
{ {
@ -110,6 +116,7 @@ namespace DownKyi
} }
} }
}); });
});
// 启动下载服务 // 启动下载服务
downloadService = new AriaDownloadService(DownloadingList, DownloadedList); downloadService = new AriaDownloadService(DownloadingList, DownloadedList);

@ -92,6 +92,7 @@
<Compile Include="Models\Downloaded.cs" /> <Compile Include="Models\Downloaded.cs" />
<Compile Include="Models\Downloading.cs" /> <Compile Include="Models\Downloading.cs" />
<Compile Include="Models\DownloadStatus.cs" /> <Compile Include="Models\DownloadStatus.cs" />
<Compile Include="Services\Download\AddToDownloadService.cs" />
<Compile Include="Services\Download\DownloadStorageService.cs" /> <Compile Include="Services\Download\DownloadStorageService.cs" />
<Compile Include="Services\SearchService.cs" /> <Compile Include="Services\SearchService.cs" />
<Compile Include="ViewModels\PageViewModels\Favorites.cs" /> <Compile Include="ViewModels\PageViewModels\Favorites.cs" />

@ -6,6 +6,7 @@
WAIT_FOR_DOWNLOAD, // 等待下载,下载过,但是启动本次下载周期未开始,如重启程序后未开始 WAIT_FOR_DOWNLOAD, // 等待下载,下载过,但是启动本次下载周期未开始,如重启程序后未开始
PAUSE_STARTED, // 暂停启动下载 PAUSE_STARTED, // 暂停启动下载
PAUSE, // 暂停 PAUSE, // 暂停
//PAUSE_TO_WAIT, // 暂停后等待
DOWNLOADING, // 下载中 DOWNLOADING, // 下载中
DOWNLOAD_SUCCEED, // 下载成功 DOWNLOAD_SUCCEED, // 下载成功
DOWNLOAD_FAILED, // 下载失败 DOWNLOAD_FAILED, // 下载失败

@ -0,0 +1,389 @@
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
{
/// <summary>
/// 添加到下载列表服务
/// </summary>
public class AddToDownloadService
{
private readonly string Tag = "AddToDownloadService";
private IInfoService videoInfoService;
private VideoInfoView videoInfoView;
private List<VideoSection> videoSections;
// 下载内容
private bool downloadAudio = true;
private bool downloadVideo = true;
private bool downloadDanmaku = true;
private bool downloadSubtitle = true;
private bool downloadCover = true;
/// <summary>
/// 添加下载
/// </summary>
/// <param name="streamType"></param>
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;
}
}
/// <summary>
/// 添加下载
/// </summary>
/// <param name="id"></param>
/// <param name="streamType"></param>
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<VideoSection> 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<VideoSection>
{
new VideoSection
{
Id = 0,
Title = "default",
IsSelected = true,
VideoPages = videoInfoService.GetVideoPages()
}
};
}
// 将所有视频设置为选中
foreach (VideoSection section in videoSections)
{
foreach (var item in section.VideoPages)
{
item.IsSelected = true;
}
}
}
/// <summary>
/// 解析视频流
/// </summary>
/// <param name="videoInfoService"></param>
public void ParseVideo(IInfoService videoInfoService)
{
if (videoSections == null) { return; }
foreach (VideoSection section in videoSections)
{
foreach (VideoPage page in section.VideoPages)
{
// 执行解析任务
videoInfoService.GetVideoStream(page);
}
}
}
/// <summary>
/// 选择文件夹和下载项
/// </summary>
/// <param name="dialogService"></param>
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<string>("directory");
// 下载内容
downloadAudio = result.Parameters.GetValue<bool>("downloadAudio");
downloadVideo = result.Parameters.GetValue<bool>("downloadVideo");
downloadDanmaku = result.Parameters.GetValue<bool>("downloadDanmaku");
downloadSubtitle = result.Parameters.GetValue<bool>("downloadSubtitle");
downloadCover = result.Parameters.GetValue<bool>("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.IsSelected) { continue; }
// 没有解析的也跳过
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<MessageEvent>().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<MessageEvent>().Publish($"{page.Name}{DictionaryResource.GetString("TipAlreadyToAddDownloaded")}");
isDownloaded = true;
break;
}
}
if (isDownloaded) { continue; }
// 视频分区
int zoneId = -1;
List<ZoneAttr> 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<FileNamePart> 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" : page.VideoQuality.SelectedVideoCodec.Contains("Dolby") ? "Dolby Vision" : "");
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;
}
}
}

@ -32,6 +32,9 @@ namespace DownKyi.Services.Download
{ {
private CancellationTokenSource tokenSource; private CancellationTokenSource tokenSource;
private int retry = 5;
private string nullMark = "<null>";
public AriaDownloadService(ObservableCollection<DownloadingItem> downloadingList, ObservableCollection<DownloadedItem> downloadedList) : base(downloadingList, downloadedList) public AriaDownloadService(ObservableCollection<DownloadingItem> downloadingList, ObservableCollection<DownloadedItem> downloadedList) : base(downloadingList, downloadedList)
{ {
Tag = "AriaDownloadService"; Tag = "AriaDownloadService";
@ -68,6 +71,17 @@ namespace DownKyi.Services.Download
} }
} }
// 避免Dolby==null及其它未知情况直接使用异常捕获
try
{
// Dolby Atmos
if (downloading.AudioCodec.Id == 30250)
{
downloadAudio = downloading.PlayUrl.Dash.Dolby.Audio[0];
}
}
catch (Exception) { }
return DownloadVideo(downloading, downloadAudio); return DownloadVideo(downloading, downloadAudio);
} }
@ -136,8 +150,12 @@ namespace DownKyi.Services.Download
else else
{ {
// 记录本次下载的文件 // 记录本次下载的文件
try
{
downloading.Downloading.DownloadFiles.Add(key, fileName); downloading.Downloading.DownloadFiles.Add(key, fileName);
} }
catch (ArgumentException) { }
}
// 开始下载 // 开始下载
DownloadResult downloadStatus = DownloadByAria(downloading, urls, path, fileName); DownloadResult downloadStatus = DownloadByAria(downloading, urls, path, fileName);
@ -147,9 +165,8 @@ namespace DownKyi.Services.Download
return Path.Combine(path, fileName); return Path.Combine(path, fileName);
case DownloadResult.FAILED: case DownloadResult.FAILED:
case DownloadResult.ABORT: case DownloadResult.ABORT:
return null;
default: default:
return null; return nullMark;
} }
} }
@ -322,6 +339,11 @@ namespace DownKyi.Services.Download
// 下载速度 // 下载速度
downloading.SpeedDisplay = string.Empty; downloading.SpeedDisplay = string.Empty;
if (videoUid == nullMark)
{
return null;
}
string finalFile = $"{downloading.DownloadBase.FilePath}.mp4"; string finalFile = $"{downloading.DownloadBase.FilePath}.mp4";
if (videoUid == null) if (videoUid == null)
{ {
@ -361,6 +383,9 @@ namespace DownKyi.Services.Download
if (downloading.PlayUrl != null && downloading.Downloading.DownloadStatus == DownloadStatus.NOT_STARTED) if (downloading.PlayUrl != null && downloading.Downloading.DownloadStatus == DownloadStatus.NOT_STARTED)
{ {
// 设置下载状态
downloading.Downloading.DownloadStatus = DownloadStatus.DOWNLOADING;
return; return;
} }
@ -453,6 +478,9 @@ namespace DownKyi.Services.Download
{ {
int maxDownloading = SettingsManager.GetInstance().GetAriaMaxConcurrentDownloads(); int maxDownloading = SettingsManager.GetInstance().GetAriaMaxConcurrentDownloads();
int downloadingCount = 0; int downloadingCount = 0;
try
{
foreach (DownloadingItem downloading in downloadingList) foreach (DownloadingItem downloading in downloadingList)
{ {
if (downloading.Downloading.DownloadStatus == DownloadStatus.DOWNLOADING) if (downloading.Downloading.DownloadStatus == DownloadStatus.DOWNLOADING)
@ -475,6 +503,17 @@ namespace DownKyi.Services.Download
downloadingCount++; 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循环 // 判断是否该结束线程若为true跳出while循环
if (cancellationToken.IsCancellationRequested) if (cancellationToken.IsCancellationRequested)
@ -485,7 +524,7 @@ namespace DownKyi.Services.Download
} }
// 降低CPU占用 // 降低CPU占用
Thread.Sleep(200); Thread.Sleep(500);
} }
} }
@ -515,6 +554,10 @@ namespace DownKyi.Services.Download
// 解析并依次下载音频、视频、弹幕、字幕、封面等内容 // 解析并依次下载音频、视频、弹幕、字幕、封面等内容
Parse(downloading); Parse(downloading);
// 设置下载状态
// 必须在解析之后设置为DOWNLOADING
downloading.Downloading.DownloadStatus = DownloadStatus.DOWNLOADING;
// 暂停 // 暂停
Pause(downloading); Pause(downloading);
// 是否存在 // 是否存在
@ -524,14 +567,24 @@ namespace DownKyi.Services.Download
return; return;
} }
// 设置下载状态
downloading.Downloading.DownloadStatus = DownloadStatus.DOWNLOADING;
string audioUid = null; string audioUid = null;
// 如果需要下载音频 // 如果需要下载音频
if (downloading.DownloadBase.NeedDownloadContent["downloadAudio"]) if (downloading.DownloadBase.NeedDownloadContent["downloadAudio"])
{
//audioUid = DownloadAudio(downloading);
for (int i = 0; i < retry; i++)
{ {
audioUid = DownloadAudio(downloading); audioUid = DownloadAudio(downloading);
if (audioUid != null && audioUid != "<null>")
{
break;
}
}
}
if (audioUid == "<null>")
{
DownloadFailed(downloading);
return;
} }
// 暂停 // 暂停
@ -546,8 +599,21 @@ namespace DownKyi.Services.Download
string videoUid = null; string videoUid = null;
// 如果需要下载视频 // 如果需要下载视频
if (downloading.DownloadBase.NeedDownloadContent["downloadVideo"]) if (downloading.DownloadBase.NeedDownloadContent["downloadVideo"])
{
//videoUid = DownloadVideo(downloading);
for (int i = 0; i < retry; i++)
{ {
videoUid = DownloadVideo(downloading); videoUid = DownloadVideo(downloading);
if (videoUid != null && videoUid != "<null>")
{
break;
}
}
}
if (videoUid == "<null>")
{
DownloadFailed(downloading);
return;
} }
// 暂停 // 暂停
@ -592,15 +658,16 @@ namespace DownKyi.Services.Download
} }
string outputCover = null; string outputCover = null;
string outputPageCover = null;
// 如果需要下载封面 // 如果需要下载封面
if (downloading.DownloadBase.NeedDownloadContent["downloadCover"]) if (downloading.DownloadBase.NeedDownloadContent["downloadCover"])
{ {
string fileName = $"{downloading.DownloadBase.FilePath}.{GetImageExtension(downloading.DownloadBase.PageCoverUrl)}"; string fileName = $"{downloading.DownloadBase.FilePath}.{GetImageExtension(downloading.DownloadBase.PageCoverUrl)}";
// page的封面 // 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 +752,7 @@ namespace DownKyi.Services.Download
bool isCover = true; bool isCover = true;
if (downloading.DownloadBase.NeedDownloadContent["downloadCover"]) if (downloading.DownloadBase.NeedDownloadContent["downloadCover"])
{ {
if (File.Exists(outputCover)) if (File.Exists(outputCover) || File.Exists(outputPageCover))
{ {
// 成功 // 成功
isCover = true; isCover = true;
@ -698,19 +765,11 @@ namespace DownKyi.Services.Download
if (!isMediaSuccess || !isDanmakuSuccess || !isSubtitleSuccess || !isCover) if (!isMediaSuccess || !isDanmakuSuccess || !isSubtitleSuccess || !isCover)
{ {
downloading.DownloadStatusTitle = DictionaryResource.GetString("DownloadFailed"); DownloadFailed(downloading);
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");
return; return;
} }
// 下载完成后处理 // 下载完成后处理
Downloaded downloaded = new Downloaded Downloaded downloaded = new Downloaded
{ {
MaxSpeedDisplay = Format.FormatSpeed(downloading.Downloading.MaxSpeed), MaxSpeedDisplay = Format.FormatSpeed(downloading.Downloading.MaxSpeed),
@ -737,6 +796,22 @@ namespace DownKyi.Services.Download
})); }));
} }
/// <summary>
/// 下载失败后的处理
/// </summary>
/// <param name="downloading"></param>
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");
}
/// <summary> /// <summary>
/// 获取图片的扩展名 /// 获取图片的扩展名
/// </summary> /// </summary>
@ -828,7 +903,7 @@ namespace DownKyi.Services.Download
$"Cookie: {LoginHelper.GetLoginInfoCookiesString()}", $"Cookie: {LoginHelper.GetLoginInfoCookiesString()}",
$"Origin: https://www.bilibili.com", $"Origin: https://www.bilibili.com",
$"Referer: https://www.bilibili.com", $"Referer: https://www.bilibili.com",
$"User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit / 537.36(KHTML, like Gecko) Chrome / 91.0.4472.77 Safari / 537.36" $"User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.99 Safari/537.36"
}; };
AriaConfig config = new AriaConfig() AriaConfig config = new AriaConfig()
@ -933,7 +1008,17 @@ namespace DownKyi.Services.Download
private void AriaTellStatus(long totalLength, long completedLength, long speed, string gid) 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; } if (video == null) { return; }
float percent = 0; float percent = 0;

@ -15,6 +15,8 @@ namespace DownKyi.Services.Download
/// <param name="downloadingItem"></param> /// <param name="downloadingItem"></param>
public void AddDownloading(DownloadingItem downloadingItem) public void AddDownloading(DownloadingItem downloadingItem)
{ {
if (downloadingItem == null || downloadingItem.DownloadBase == null) { return; }
AddDownloadBase(downloadingItem.DownloadBase); AddDownloadBase(downloadingItem.DownloadBase);
DownloadingDb downloadingDb = new DownloadingDb(); DownloadingDb downloadingDb = new DownloadingDb();
@ -32,6 +34,8 @@ namespace DownKyi.Services.Download
/// <param name="downloadingItem"></param> /// <param name="downloadingItem"></param>
public void RemoveDownloading(DownloadingItem downloadingItem) public void RemoveDownloading(DownloadingItem downloadingItem)
{ {
if (downloadingItem == null || downloadingItem.DownloadBase == null) { return; }
RemoveDownloadBase(downloadingItem.DownloadBase.Uuid); RemoveDownloadBase(downloadingItem.DownloadBase.Uuid);
DownloadingDb downloadingDb = new DownloadingDb(); DownloadingDb downloadingDb = new DownloadingDb();
@ -75,6 +79,8 @@ namespace DownKyi.Services.Download
/// <param name="downloadingItem"></param> /// <param name="downloadingItem"></param>
public void UpdateDownloading(DownloadingItem downloadingItem) public void UpdateDownloading(DownloadingItem downloadingItem)
{ {
if (downloadingItem == null || downloadingItem.DownloadBase == null) { return; }
UpdateDownloadBase(downloadingItem.DownloadBase); UpdateDownloadBase(downloadingItem.DownloadBase);
DownloadingDb downloadingDb = new DownloadingDb(); DownloadingDb downloadingDb = new DownloadingDb();
@ -92,6 +98,8 @@ namespace DownKyi.Services.Download
/// <param name="downloadedItem"></param> /// <param name="downloadedItem"></param>
public void AddDownloaded(DownloadedItem downloadedItem) public void AddDownloaded(DownloadedItem downloadedItem)
{ {
if (downloadedItem == null || downloadedItem.DownloadBase == null) { return; }
AddDownloadBase(downloadedItem.DownloadBase); AddDownloadBase(downloadedItem.DownloadBase);
DownloadedDb downloadedDb = new DownloadedDb(); DownloadedDb downloadedDb = new DownloadedDb();
@ -109,6 +117,8 @@ namespace DownKyi.Services.Download
/// <param name="downloadedItem"></param> /// <param name="downloadedItem"></param>
public void RemoveDownloaded(DownloadedItem downloadedItem) public void RemoveDownloaded(DownloadedItem downloadedItem)
{ {
if (downloadedItem == null || downloadedItem.DownloadBase == null) { return; }
RemoveDownloadBase(downloadedItem.DownloadBase.Uuid); RemoveDownloadBase(downloadedItem.DownloadBase.Uuid);
DownloadedDb downloadedDb = new DownloadedDb(); DownloadedDb downloadedDb = new DownloadedDb();
@ -152,6 +162,8 @@ namespace DownKyi.Services.Download
/// <param name="downloadedItem"></param> /// <param name="downloadedItem"></param>
public void UpdateDownloaded(DownloadedItem downloadedItem) public void UpdateDownloaded(DownloadedItem downloadedItem)
{ {
if (downloadedItem == null || downloadedItem.DownloadBase == null) { return; }
UpdateDownloadBase(downloadedItem.DownloadBase); UpdateDownloadBase(downloadedItem.DownloadBase);
DownloadedDb downloadedDb = new DownloadedDb(); DownloadedDb downloadedDb = new DownloadedDb();
@ -169,6 +181,8 @@ namespace DownKyi.Services.Download
/// <param name="downloadBase"></param> /// <param name="downloadBase"></param>
private void AddDownloadBase(DownloadBase downloadBase) private void AddDownloadBase(DownloadBase downloadBase)
{ {
if (downloadBase == null) { return; }
DownloadBaseDb downloadBaseDb = new DownloadBaseDb(); DownloadBaseDb downloadBaseDb = new DownloadBaseDb();
object obj = downloadBaseDb.QueryById(downloadBase.Uuid); object obj = downloadBaseDb.QueryById(downloadBase.Uuid);
if (obj == null) if (obj == null)
@ -208,6 +222,8 @@ namespace DownKyi.Services.Download
/// <param name="downloadBase"></param> /// <param name="downloadBase"></param>
private void UpdateDownloadBase(DownloadBase downloadBase) private void UpdateDownloadBase(DownloadBase downloadBase)
{ {
if (downloadBase == null) { return; }
DownloadBaseDb downloadBaseDb = new DownloadBaseDb(); DownloadBaseDb downloadBaseDb = new DownloadBaseDb();
downloadBaseDb.Update(downloadBase.Uuid, downloadBase); downloadBaseDb.Update(downloadBase.Uuid, downloadBase);
downloadBaseDb.Close(); downloadBaseDb.Close();

@ -26,7 +26,7 @@ namespace DownKyi.Services
// 查询、保存封面 // 查询、保存封面
StorageCover storageCover = new StorageCover(); StorageCover storageCover = new StorageCover();
string coverUrl = favoritesMetaInfo.Cover; string coverUrl = favoritesMetaInfo.Cover;
string cover = storageCover.GetCover(favoritesMetaInfo.Id, "Favorites", favoritesMetaInfo.Mid, coverUrl); BitmapImage cover = storageCover.GetCoverThumbnail(favoritesMetaInfo.Id, "Favorites", favoritesMetaInfo.Mid, coverUrl, 300, 188);
// 获取用户头像 // 获取用户头像
string upName; string upName;
@ -49,7 +49,7 @@ namespace DownKyi.Services
{ {
favorites.CoverUrl = coverUrl; favorites.CoverUrl = coverUrl;
favorites.Cover = cover == null ? null : new BitmapImage(new Uri(cover)); favorites.Cover = cover;
favorites.Title = favoritesMetaInfo.Title; favorites.Title = favoritesMetaInfo.Title;
DateTime startTime = TimeZone.CurrentTimeZone.ToLocalTime(new DateTime(1970, 1, 1)); // 当地时区 DateTime startTime = TimeZone.CurrentTimeZone.ToLocalTime(new DateTime(1970, 1, 1)); // 当地时区
@ -93,12 +93,14 @@ namespace DownKyi.Services
int order = 0; int order = 0;
foreach (var media in medias) foreach (var media in medias)
{ {
if (media.Title == "已失效视频") { continue; }
order++; order++;
// 查询、保存封面 // 查询、保存封面
StorageCover storageCover = new StorageCover(); StorageCover storageCover = new StorageCover();
string coverUrl = media.Cover; string coverUrl = media.Cover;
string cover = storageCover.GetCover(media.Id, media.Bvid, -1, coverUrl); BitmapImage cover = storageCover.GetCoverThumbnail(media.Id, media.Bvid, -1, coverUrl, 200, 125);
App.PropertyChangeAsync(new Action(() => App.PropertyChangeAsync(new Action(() =>
{ {
@ -107,7 +109,7 @@ namespace DownKyi.Services
Avid = media.Id, Avid = media.Id,
Bvid = media.Bvid, Bvid = media.Bvid,
Order = order, Order = order,
Cover = cover == null ? null : new BitmapImage(new Uri(cover)), Cover = cover,
Title = media.Title, Title = media.Title,
PlayNumber = media.CntInfo != null ? Format.FormatNumber(media.CntInfo.Play) : "0", PlayNumber = media.CntInfo != null ? Format.FormatNumber(media.CntInfo.Play) : "0",
DanmakuNumber = media.CntInfo != null ? Format.FormatNumber(media.CntInfo.Danmaku) : "0", DanmakuNumber = media.CntInfo != null ? Format.FormatNumber(media.CntInfo.Danmaku) : "0",
@ -120,7 +122,7 @@ namespace DownKyi.Services
if (!result.ToList().Exists(t => t.Avid == newMedia.Avid)) if (!result.ToList().Exists(t => t.Avid == newMedia.Avid))
{ {
result.Add(newMedia); result.Add(newMedia);
Thread.Sleep(50); Thread.Sleep(10);
} }
})); }));
} }

@ -8,7 +8,17 @@ namespace DownKyi.Services
public class SearchService public class SearchService
{ {
/// <summary> /// <summary>
/// 解析支持的输入 /// 解析支持的输入,
/// 支持的格式有:<para/>
/// av号av170001, AV170001, https://www.bilibili.com/video/av170001 <para/>
/// BV号BV17x411w7KC, https://www.bilibili.com/video/BV17x411w7KC <para/>
/// 番剧电影、电视剧ss号ss32982, SS32982, https://www.bilibili.com/bangumi/play/ss32982 <para/>
/// 番剧电影、电视剧ep号ep317925, EP317925, https://www.bilibili.com/bangumi/play/ep317925 <para/>
/// 番剧电影、电视剧md号md28228367, MD28228367, https://www.bilibili.com/bangumi/media/md28228367 <para/>
/// 课程ss号https://www.bilibili.com/cheese/play/ss205 <para/>
/// 课程ep号https://www.bilibili.com/cheese/play/ep3489 <para/>
/// 收藏夹ml1329019876, ML1329019876, https://www.bilibili.com/medialist/detail/ml1329019876 <para/>
/// 用户空间uid928123, UID928123, uid:928123, UID:928123, https://space.bilibili.com/928123
/// </summary> /// </summary>
/// <param name="input"></param> /// <param name="input"></param>
/// <param name="parentViewName"></param> /// <param name="parentViewName"></param>

@ -5,6 +5,7 @@ using DownKyi.Core.Settings.Models;
using DownKyi.Core.Utils; using DownKyi.Core.Utils;
using DownKyi.ViewModels.PageViewModels; using DownKyi.ViewModels.PageViewModels;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq; using System.Linq;
namespace DownKyi.Services namespace DownKyi.Services
@ -94,10 +95,16 @@ namespace DownKyi.Services
/// <param name="playUrl"></param> /// <param name="playUrl"></param>
/// <param name="defaultAudioQuality"></param> /// <param name="defaultAudioQuality"></param>
/// <returns></returns> /// <returns></returns>
private static List<string> GetAudioQualityFormatList(PlayUrl playUrl, int defaultAudioQuality) private static ObservableCollection<string> GetAudioQualityFormatList(PlayUrl playUrl, int defaultAudioQuality)
{ {
List<string> audioQualityFormatList = new List<string>(); List<string> audioQualityFormatList = new List<string>();
foreach (var audio in playUrl.Dash.Audio)
if (playUrl.Dash.Audio == null)
{
return new ObservableCollection<string>();
}
foreach (PlayUrlDashVideo audio in playUrl.Dash.Audio)
{ {
// 音质id大于设置画质时跳过 // 音质id大于设置画质时跳过
if (audio.Id > defaultAudioQuality) { continue; } if (audio.Id > defaultAudioQuality) { continue; }
@ -112,7 +119,7 @@ namespace DownKyi.Services
audioQualityFormatList.Sort(new StringLogicalComparer<string>()); audioQualityFormatList.Sort(new StringLogicalComparer<string>());
audioQualityFormatList.Reverse(); audioQualityFormatList.Reverse();
return audioQualityFormatList; return new ObservableCollection<string>(audioQualityFormatList);
} }
/// <summary> /// <summary>
@ -126,7 +133,13 @@ namespace DownKyi.Services
private static List<VideoQuality> GetVideoQualityList(PlayUrl playUrl, UserInfoSettings userInfo, int defaultQuality, VideoCodecs videoCodecs) private static List<VideoQuality> GetVideoQualityList(PlayUrl playUrl, UserInfoSettings userInfo, int defaultQuality, VideoCodecs videoCodecs)
{ {
List<VideoQuality> videoQualityList = new List<VideoQuality>(); List<VideoQuality> videoQualityList = new List<VideoQuality>();
foreach (var video in playUrl.Dash.Video)
if (playUrl.Dash.Video == null)
{
return videoQualityList;
}
foreach (PlayUrlDashVideo video in playUrl.Dash.Video)
{ {
// 画质id大于设置画质时跳过 // 画质id大于设置画质时跳过
if (video.Id > defaultQuality) { continue; } if (video.Id > defaultQuality) { continue; }
@ -139,7 +152,7 @@ namespace DownKyi.Services
} }
string qualityFormat = string.Empty; 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) if (selectedQuality != null)
{ {
qualityFormat = selectedQuality.NewDescription; qualityFormat = selectedQuality.NewDescription;
@ -148,13 +161,13 @@ namespace DownKyi.Services
// 寻找是否已存在这个画质 // 寻找是否已存在这个画质
// 不存在则添加,存在则修改 // 不存在则添加,存在则修改
string codecName = GetVideoCodecName(video.Codecs); 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) if (videoQualityExist == null)
{ {
var videoCodecList = new List<string>(); List<string> videoCodecList = new List<string>();
ListHelper.AddUnique(videoCodecList, codecName); ListHelper.AddUnique(videoCodecList, codecName);
var videoQuality = new VideoQuality() VideoQuality videoQuality = new VideoQuality()
{ {
Quality = video.Id, Quality = video.Id,
QualityFormat = qualityFormat, QualityFormat = qualityFormat,
@ -171,7 +184,7 @@ namespace DownKyi.Services
} }
// 设置选中的视频编码 // 设置选中的视频编码
var selectedVideoQuality = videoQualityList.FirstOrDefault(t => t.Quality == video.Id); VideoQuality selectedVideoQuality = videoQualityList.FirstOrDefault(t => t.Quality == video.Id);
switch (videoCodecs) switch (videoCodecs)
{ {
case VideoCodecs.AVC: case VideoCodecs.AVC:
@ -186,6 +199,10 @@ namespace DownKyi.Services
videoQualityList[videoQualityList.IndexOf(selectedVideoQuality)].SelectedVideoCodec = "H.265/HEVC"; videoQualityList[videoQualityList.IndexOf(selectedVideoQuality)].SelectedVideoCodec = "H.265/HEVC";
} }
break; break;
case VideoCodecs.NONE:
break;
default:
break;
} }
if (videoQualityList[videoQualityList.IndexOf(selectedVideoQuality)].VideoCodecList.Count == 1) if (videoQualityList[videoQualityList.IndexOf(selectedVideoQuality)].VideoCodecList.Count == 1)
@ -205,7 +222,7 @@ namespace DownKyi.Services
/// <returns></returns> /// <returns></returns>
internal static string GetVideoCodecName(string origin) 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") || origin.Contains("hvc") ? "Dolby Vision" : "";
} }
} }

@ -102,7 +102,7 @@ namespace DownKyi.Services
List<VideoSection> videoSections = new List<VideoSection>(); List<VideoSection> videoSections = new List<VideoSection>();
foreach (var section in videoView.UgcSeason.Sections) foreach (UgcSection section in videoView.UgcSeason.Sections)
{ {
List<ViewModels.PageViewModels.VideoPage> pages = new List<ViewModels.PageViewModels.VideoPage>(); List<ViewModels.PageViewModels.VideoPage> pages = new List<ViewModels.PageViewModels.VideoPage>();
int order = 0; int order = 0;

@ -34,11 +34,7 @@ namespace DownKyi.Utils
Filter = "mp4 (*.mp4)|*.mp4" Filter = "mp4 (*.mp4)|*.mp4"
}; };
var showDialog = dialog.ShowDialog(); var showDialog = dialog.ShowDialog();
if (showDialog == true) return showDialog == true ? dialog.FileName : "";
{
return dialog.FileName;
}
else { return ""; }
} }
} }

@ -18,9 +18,12 @@ namespace DownKyi.ViewModels.DownloadManager
{ {
downloadBase = value; downloadBase = value;
if (value != null)
{
ZoneImage = (DrawingImage)Application.Current.Resources[VideoZoneIcon.Instance().GetZoneImageKey(DownloadBase.ZoneId)]; ZoneImage = (DrawingImage)Application.Current.Resources[VideoZoneIcon.Instance().GetZoneImageKey(DownloadBase.ZoneId)];
} }
} }
}
// 视频分区image // 视频分区image
private DrawingImage zoneImage; private DrawingImage zoneImage;
@ -33,7 +36,7 @@ namespace DownKyi.ViewModels.DownloadManager
// 视频序号 // 视频序号
public int Order public int Order
{ {
get => DownloadBase.Order; get => DownloadBase == null ? 0 : DownloadBase.Order;
set set
{ {
DownloadBase.Order = value; DownloadBase.Order = value;
@ -44,7 +47,7 @@ namespace DownKyi.ViewModels.DownloadManager
// 视频主标题 // 视频主标题
public string MainTitle public string MainTitle
{ {
get => DownloadBase.MainTitle; get => DownloadBase == null ? "" : DownloadBase.MainTitle;
set set
{ {
DownloadBase.MainTitle = value; DownloadBase.MainTitle = value;
@ -55,7 +58,7 @@ namespace DownKyi.ViewModels.DownloadManager
// 视频标题 // 视频标题
public string Name public string Name
{ {
get => DownloadBase.Name; get => DownloadBase == null ? "" : DownloadBase.Name;
set set
{ {
DownloadBase.Name = value; DownloadBase.Name = value;
@ -66,7 +69,7 @@ namespace DownKyi.ViewModels.DownloadManager
// 时长 // 时长
public string Duration public string Duration
{ {
get => DownloadBase.Duration; get => DownloadBase == null ? "" : DownloadBase.Duration;
set set
{ {
DownloadBase.Duration = value; DownloadBase.Duration = value;
@ -77,7 +80,7 @@ namespace DownKyi.ViewModels.DownloadManager
// 视频编码名称AVC、HEVC // 视频编码名称AVC、HEVC
public string VideoCodecName public string VideoCodecName
{ {
get => DownloadBase.VideoCodecName; get => DownloadBase == null ? "" : DownloadBase.VideoCodecName;
set set
{ {
DownloadBase.VideoCodecName = value; DownloadBase.VideoCodecName = value;
@ -88,7 +91,7 @@ namespace DownKyi.ViewModels.DownloadManager
// 视频画质 // 视频画质
public Quality Resolution public Quality Resolution
{ {
get => DownloadBase.Resolution; get => DownloadBase == null ? null : DownloadBase.Resolution;
set set
{ {
DownloadBase.Resolution = value; DownloadBase.Resolution = value;
@ -99,7 +102,7 @@ namespace DownKyi.ViewModels.DownloadManager
// 音频编码 // 音频编码
public Quality AudioCodec public Quality AudioCodec
{ {
get => DownloadBase.AudioCodec; get => DownloadBase == null ? null : DownloadBase.AudioCodec;
set set
{ {
DownloadBase.AudioCodec = value; DownloadBase.AudioCodec = value;
@ -110,7 +113,7 @@ namespace DownKyi.ViewModels.DownloadManager
// 文件大小 // 文件大小
public string FileSize public string FileSize
{ {
get => DownloadBase.FileSize; get => DownloadBase == null ? "" : DownloadBase.FileSize;
set set
{ {
DownloadBase.FileSize = value; DownloadBase.FileSize = value;

@ -23,7 +23,7 @@ namespace DownKyi.ViewModels.DownloadManager
private Downloading downloading; private Downloading downloading;
public Downloading Downloading public Downloading Downloading
{ {
get { return downloading; } get => downloading;
set set
{ {
downloading = value; downloading = value;
@ -164,21 +164,26 @@ namespace DownKyi.ViewModels.DownloadManager
case DownloadStatus.NOT_STARTED: case DownloadStatus.NOT_STARTED:
case DownloadStatus.WAIT_FOR_DOWNLOAD: case DownloadStatus.WAIT_FOR_DOWNLOAD:
Downloading.DownloadStatus = DownloadStatus.PAUSE_STARTED; Downloading.DownloadStatus = DownloadStatus.PAUSE_STARTED;
DownloadStatusTitle = DictionaryResource.GetString("Pausing");
StartOrPause = ButtonIcon.Instance().Start; StartOrPause = ButtonIcon.Instance().Start;
StartOrPause.Fill = DictionaryResource.GetColor("ColorPrimary"); StartOrPause.Fill = DictionaryResource.GetColor("ColorPrimary");
break; break;
case DownloadStatus.PAUSE_STARTED: case DownloadStatus.PAUSE_STARTED:
Downloading.DownloadStatus = DownloadStatus.WAIT_FOR_DOWNLOAD; Downloading.DownloadStatus = DownloadStatus.WAIT_FOR_DOWNLOAD;
DownloadStatusTitle = DictionaryResource.GetString("Waiting");
StartOrPause = ButtonIcon.Instance().Pause; StartOrPause = ButtonIcon.Instance().Pause;
StartOrPause.Fill = DictionaryResource.GetColor("ColorPrimary"); StartOrPause.Fill = DictionaryResource.GetColor("ColorPrimary");
break; break;
case DownloadStatus.PAUSE: case DownloadStatus.PAUSE:
Downloading.DownloadStatus = DownloadStatus.DOWNLOADING; Downloading.DownloadStatus = DownloadStatus.WAIT_FOR_DOWNLOAD;
DownloadStatusTitle = DictionaryResource.GetString("Waiting");
StartOrPause = ButtonIcon.Instance().Pause; StartOrPause = ButtonIcon.Instance().Pause;
StartOrPause.Fill = DictionaryResource.GetColor("ColorPrimary"); StartOrPause.Fill = DictionaryResource.GetColor("ColorPrimary");
break; break;
//case DownloadStatus.PAUSE_TO_WAIT:
case DownloadStatus.DOWNLOADING: case DownloadStatus.DOWNLOADING:
Downloading.DownloadStatus = DownloadStatus.PAUSE; Downloading.DownloadStatus = DownloadStatus.PAUSE;
DownloadStatusTitle = DictionaryResource.GetString("Pausing");
StartOrPause = ButtonIcon.Instance().Start; StartOrPause = ButtonIcon.Instance().Start;
StartOrPause.Fill = DictionaryResource.GetColor("ColorPrimary"); StartOrPause.Fill = DictionaryResource.GetColor("ColorPrimary");
break; break;
@ -188,6 +193,7 @@ namespace DownKyi.ViewModels.DownloadManager
break; break;
case DownloadStatus.DOWNLOAD_FAILED: case DownloadStatus.DOWNLOAD_FAILED:
Downloading.DownloadStatus = DownloadStatus.WAIT_FOR_DOWNLOAD; Downloading.DownloadStatus = DownloadStatus.WAIT_FOR_DOWNLOAD;
DownloadStatusTitle = DictionaryResource.GetString("Waiting");
StartOrPause = ButtonIcon.Instance().Pause; StartOrPause = ButtonIcon.Instance().Pause;
StartOrPause.Fill = DictionaryResource.GetColor("ColorPrimary"); StartOrPause.Fill = DictionaryResource.GetColor("ColorPrimary");
break; break;

@ -1,7 +1,11 @@
using DownKyi.Core.Settings; using DownKyi.Core.Settings;
using Prism.Commands; using Prism.Commands;
using Prism.Events; using Prism.Events;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using System.Linq;
using System.Threading.Tasks;
namespace DownKyi.ViewModels.DownloadManager namespace DownKyi.ViewModels.DownloadManager
{ {
@ -89,9 +93,22 @@ namespace DownKyi.ViewModels.DownloadManager
/// <summary> /// <summary>
/// 清空下载完成列表事件 /// 清空下载完成列表事件
/// </summary> /// </summary>
private void ExecuteClearAllDownloadedCommand() private async void ExecuteClearAllDownloadedCommand()
{ {
DownloadedList.Clear(); // 使用Clear()不能触发NotifyCollectionChangedAction.Remove事件
// 因此遍历删除
// DownloadingList中元素被删除后不能继续遍历
await Task.Run(() =>
{
List<DownloadedItem> list = DownloadedList.ToList();
foreach (DownloadedItem item in list)
{
App.PropertyChangeAsync(new Action(() =>
{
App.DownloadedList.Remove(item);
}));
}
});
} }
#endregion #endregion

@ -3,7 +3,11 @@ using DownKyi.Models;
using DownKyi.Utils; using DownKyi.Utils;
using Prism.Commands; using Prism.Commands;
using Prism.Events; using Prism.Events;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using System.Linq;
using System.Threading.Tasks;
namespace DownKyi.ViewModels.DownloadManager namespace DownKyi.ViewModels.DownloadManager
{ {
@ -45,7 +49,8 @@ namespace DownKyi.ViewModels.DownloadManager
{ {
case DownloadStatus.NOT_STARTED: case DownloadStatus.NOT_STARTED:
case DownloadStatus.WAIT_FOR_DOWNLOAD: 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 = ButtonIcon.Instance().Start;
downloading.StartOrPause.Fill = DictionaryResource.GetColor("ColorPrimary"); downloading.StartOrPause.Fill = DictionaryResource.GetColor("ColorPrimary");
break; break;
@ -53,8 +58,10 @@ namespace DownKyi.ViewModels.DownloadManager
break; break;
case DownloadStatus.PAUSE: case DownloadStatus.PAUSE:
break; break;
//case DownloadStatus.PAUSE_TO_WAIT:
case DownloadStatus.DOWNLOADING: case DownloadStatus.DOWNLOADING:
downloading.Downloading.DownloadStatus = DownloadStatus.PAUSE; downloading.Downloading.DownloadStatus = DownloadStatus.PAUSE;
downloading.DownloadStatusTitle = DictionaryResource.GetString("Pausing");
downloading.StartOrPause = ButtonIcon.Instance().Start; downloading.StartOrPause = ButtonIcon.Instance().Start;
downloading.StartOrPause.Fill = DictionaryResource.GetColor("ColorPrimary"); downloading.StartOrPause.Fill = DictionaryResource.GetColor("ColorPrimary");
break; break;
@ -85,13 +92,19 @@ namespace DownKyi.ViewModels.DownloadManager
{ {
case DownloadStatus.NOT_STARTED: case DownloadStatus.NOT_STARTED:
case DownloadStatus.WAIT_FOR_DOWNLOAD: case DownloadStatus.WAIT_FOR_DOWNLOAD:
downloading.Downloading.DownloadStatus = DownloadStatus.WAIT_FOR_DOWNLOAD;
downloading.DownloadStatusTitle = DictionaryResource.GetString("Waiting");
break; break;
case DownloadStatus.PAUSE_STARTED: case DownloadStatus.PAUSE_STARTED:
downloading.Downloading.DownloadStatus = DownloadStatus.WAIT_FOR_DOWNLOAD; downloading.Downloading.DownloadStatus = DownloadStatus.WAIT_FOR_DOWNLOAD;
downloading.DownloadStatusTitle = DictionaryResource.GetString("Waiting");
break; break;
case DownloadStatus.PAUSE: case DownloadStatus.PAUSE:
downloading.Downloading.DownloadStatus = DownloadStatus.DOWNLOADING; downloading.Downloading.DownloadStatus = DownloadStatus.WAIT_FOR_DOWNLOAD;
downloading.DownloadStatusTitle = DictionaryResource.GetString("Waiting");
break; break;
//case DownloadStatus.PAUSE_TO_WAIT:
// break;
case DownloadStatus.DOWNLOADING: case DownloadStatus.DOWNLOADING:
break; break;
case DownloadStatus.DOWNLOAD_SUCCEED: case DownloadStatus.DOWNLOAD_SUCCEED:
@ -100,6 +113,7 @@ namespace DownKyi.ViewModels.DownloadManager
break; break;
case DownloadStatus.DOWNLOAD_FAILED: case DownloadStatus.DOWNLOAD_FAILED:
downloading.Downloading.DownloadStatus = DownloadStatus.WAIT_FOR_DOWNLOAD; downloading.Downloading.DownloadStatus = DownloadStatus.WAIT_FOR_DOWNLOAD;
downloading.DownloadStatusTitle = DictionaryResource.GetString("Waiting");
break; break;
default: default:
break; break;
@ -117,9 +131,22 @@ namespace DownKyi.ViewModels.DownloadManager
/// <summary> /// <summary>
/// 删除所有下载事件 /// 删除所有下载事件
/// </summary> /// </summary>
private void ExecuteDeleteAllDownloadingCommand() private async void ExecuteDeleteAllDownloadingCommand()
{
// 使用Clear()不能触发NotifyCollectionChangedAction.Remove事件
// 因此遍历删除
// DownloadingList中元素被删除后不能继续遍历
await Task.Run(() =>
{ {
DownloadingList.Clear(); List<DownloadingItem> list = DownloadingList.ToList();
foreach (DownloadingItem item in list)
{
App.PropertyChangeAsync(new Action(() =>
{
App.DownloadingList.Remove(item);
}));
}
});
} }
#endregion #endregion

@ -1,4 +1,6 @@
using Prism.Mvvm; using DownKyi.Images;
using DownKyi.Utils;
using Prism.Mvvm;
using System.Windows.Media.Imaging; using System.Windows.Media.Imaging;
namespace DownKyi.ViewModels.PageViewModels namespace DownKyi.ViewModels.PageViewModels
@ -57,6 +59,34 @@ namespace DownKyi.ViewModels.PageViewModels
set => SetProperty(ref shareNumber, value); 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; private string description;
public string Description public string Description
{ {
@ -84,5 +114,25 @@ namespace DownKyi.ViewModels.PageViewModels
get => upHeader; get => upHeader;
set => SetProperty(ref upHeader, value); 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
}
} }
} }

@ -1,6 +1,10 @@
using DownKyi.Core.BiliApi.VideoStream.Models; using DownKyi.Core.BiliApi.BiliUtils;
using DownKyi.Core.BiliApi.VideoStream.Models;
using DownKyi.Core.Utils;
using Prism.Commands;
using Prism.Mvvm; using Prism.Mvvm;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.ObjectModel;
namespace DownKyi.ViewModels.PageViewModels namespace DownKyi.ViewModels.PageViewModels
{ {
@ -43,8 +47,8 @@ namespace DownKyi.ViewModels.PageViewModels
set => SetProperty(ref duration, value); set => SetProperty(ref duration, value);
} }
private List<string> audioQualityFormatList; private ObservableCollection<string> audioQualityFormatList;
public List<string> AudioQualityFormatList public ObservableCollection<string> AudioQualityFormatList
{ {
get => audioQualityFormatList; get => audioQualityFormatList;
set => SetProperty(ref audioQualityFormatList, value); set => SetProperty(ref audioQualityFormatList, value);
@ -71,5 +75,37 @@ namespace DownKyi.ViewModels.PageViewModels
set => SetProperty(ref videoQuality, value); set => SetProperty(ref videoQuality, value);
} }
#region
// 视频画质选择事件
private DelegateCommand videoQualitySelectedCommand;
public DelegateCommand VideoQualitySelectedCommand => videoQualitySelectedCommand ?? (videoQualitySelectedCommand = new DelegateCommand(ExecuteVideoQualitySelectedCommand));
/// <summary>
/// 视频画质选择事件
/// </summary>
private void ExecuteVideoQualitySelectedCommand()
{
// 杜比视界
string dolby = Constant.GetAudioQualities()[3].Name;
if (VideoQuality.Quality == 126)
{
ListHelper.AddUnique(AudioQualityFormatList, dolby);
AudioQualityFormat = dolby;
}
else
{
if (AudioQualityFormatList.Contains(dolby))
{
AudioQualityFormatList.Remove(dolby);
AudioQualityFormat = AudioQualityFormatList[0];
}
}
}
#endregion
} }
} }

@ -125,6 +125,7 @@ namespace DownKyi.ViewModels.Settings
// 优先下载音质 // 优先下载音质
AudioQualityList = Constant.GetAudioQualities(); AudioQualityList = Constant.GetAudioQualities();
AudioQualityList.RemoveAt(3);
// 文件命名格式 // 文件命名格式
SelectedFileName = new ObservableCollection<DisplayFileNamePart>(); SelectedFileName = new ObservableCollection<DisplayFileNamePart>();

@ -198,23 +198,12 @@ namespace DownKyi.ViewModels
#endregion #endregion
#region 业务逻辑 #region 业务逻辑
/// <summary> /// <summary>
/// 进入B站链接的处理逻辑 /// 进入B站链接的处理逻辑
/// 只负责处理输入,并跳转到视频详情页。<para/> /// 只负责处理输入,并跳转到视频详情页。<para/>
/// 不是支持的格式,则进入搜索页面。 /// 不是支持的格式,则进入搜索页面。
/// 支持的格式有:<para/>
/// av号av170001, AV170001, https://www.bilibili.com/video/av170001 <para/>
/// BV号BV17x411w7KC, https://www.bilibili.com/video/BV17x411w7KC <para/>
/// 番剧电影、电视剧ss号ss32982, SS32982, https://www.bilibili.com/bangumi/play/ss32982 <para/>
/// 番剧电影、电视剧ep号ep317925, EP317925, https://www.bilibili.com/bangumi/play/ep317925 <para/>
/// 番剧电影、电视剧md号md28228367, MD28228367, https://www.bilibili.com/bangumi/media/md28228367 <para/>
/// 课程ss号https://www.bilibili.com/cheese/play/ss205 <para/>
/// 课程ep号https://www.bilibili.com/cheese/play/ep3489 <para/>
/// 收藏夹ml1329019876, ML1329019876, https://www.bilibili.com/medialist/detail/ml1329019876 <para/>
/// 用户空间uid928123, UID928123, uid:928123, UID:928123, https://space.bilibili.com/928123
/// </summary> /// </summary>
private void EnterBili() private void EnterBili()
{ {

@ -1,12 +1,15 @@
using DownKyi.Core.Logging; using DownKyi.Core.BiliApi.VideoStream;
using DownKyi.Core.Logging;
using DownKyi.Events; using DownKyi.Events;
using DownKyi.Images; using DownKyi.Images;
using DownKyi.Services; using DownKyi.Services;
using DownKyi.Services.Download;
using DownKyi.Utils; using DownKyi.Utils;
using DownKyi.ViewModels.PageViewModels; using DownKyi.ViewModels.PageViewModels;
using Prism.Commands; using Prism.Commands;
using Prism.Events; using Prism.Events;
using Prism.Regions; using Prism.Regions;
using Prism.Services.Dialogs;
using System; using System;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using System.Threading.Tasks; using System.Threading.Tasks;
@ -18,6 +21,8 @@ namespace DownKyi.ViewModels
{ {
public const string Tag = "PagePublicFavorites"; public const string Tag = "PagePublicFavorites";
private readonly IDialogService dialogService;
#region 页面属性申明 #region 页面属性申明
private string pageName = Tag; private string pageName = Tag;
@ -34,34 +39,6 @@ namespace DownKyi.ViewModels
set => SetProperty(ref arrowBack, value); 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; private Favorites favorites;
public Favorites Favorites public Favorites Favorites
{ {
@ -92,25 +69,15 @@ namespace DownKyi.ViewModels
#endregion #endregion
public ViewPublicFavoritesViewModel(IEventAggregator eventAggregator) : base(eventAggregator) public ViewPublicFavoritesViewModel(IEventAggregator eventAggregator, IDialogService dialogService) : base(eventAggregator)
{ {
this.dialogService = dialogService;
#region 属性初始化 #region 属性初始化
ArrowBack = NavigationIcon.Instance().ArrowBack; ArrowBack = NavigationIcon.Instance().ArrowBack;
ArrowBack.Fill = DictionaryResource.GetColor("ColorTextDark"); 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<FavoritesMedia>(); FavoritesMedias = new ObservableCollection<FavoritesMedia>();
#endregion #endregion
@ -185,6 +152,7 @@ namespace DownKyi.ViewModels
/// </summary> /// </summary>
private void ExecuteAddToDownloadCommand() private void ExecuteAddToDownloadCommand()
{ {
AddToDownload(true);
} }
// 添加所有视频到下载列表事件 // 添加所有视频到下载列表事件
@ -196,6 +164,7 @@ namespace DownKyi.ViewModels
/// </summary> /// </summary>
private void ExecuteAddAllToDownloadCommand() private void ExecuteAddAllToDownloadCommand()
{ {
AddToDownload(false);
} }
// 列表选择事件 // 列表选择事件
@ -212,6 +181,48 @@ namespace DownKyi.ViewModels
#endregion #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<MessageEvent>().Publish(DictionaryResource.GetString("TipAddDownloadingZero"));
}
else
{
eventAggregator.GetEvent<MessageEvent>().Publish($"{DictionaryResource.GetString("TipAddDownloadingFinished1")}{i}{DictionaryResource.GetString("TipAddDownloadingFinished2")}");
}
}
/// <summary> /// <summary>
/// 初始化页面元素 /// 初始化页面元素
/// </summary> /// </summary>

@ -1,18 +1,14 @@
using DownKyi.Core.BiliApi.BiliUtils; using DownKyi.Core.BiliApi.BiliUtils;
using DownKyi.Core.BiliApi.VideoStream; using DownKyi.Core.BiliApi.VideoStream;
using DownKyi.Core.BiliApi.Zone;
using DownKyi.Core.FileName;
using DownKyi.Core.Logging; using DownKyi.Core.Logging;
using DownKyi.Core.Settings; using DownKyi.Core.Settings;
using DownKyi.Core.Utils;
using DownKyi.CustomControl; using DownKyi.CustomControl;
using DownKyi.Events; using DownKyi.Events;
using DownKyi.Images; using DownKyi.Images;
using DownKyi.Models;
using DownKyi.Services; using DownKyi.Services;
using DownKyi.Services.Download;
using DownKyi.Utils; using DownKyi.Utils;
using DownKyi.ViewModels.Dialogs; using DownKyi.ViewModels.Dialogs;
using DownKyi.ViewModels.DownloadManager;
using DownKyi.ViewModels.PageViewModels; using DownKyi.ViewModels.PageViewModels;
using Prism.Commands; using Prism.Commands;
using Prism.Events; using Prism.Events;
@ -21,7 +17,6 @@ using Prism.Services.Dialogs;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using System.IO;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Windows; using System.Windows;
@ -435,12 +430,12 @@ namespace DownKyi.ViewModels
{ {
foreach (VideoPage page in section.VideoPages) 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) 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) 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; break;
@ -504,211 +499,37 @@ namespace DownKyi.ViewModels
/// <summary> /// <summary>
/// 添加到下载列表事件 /// 添加到下载列表事件
/// </summary> /// </summary>
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)
{
directory = SettingsManager.GetInstance().GetSaveVideoRootPath();
}
else
{
// 打开文件夹选择器
dialogService.ShowDialog(ViewDownloadSetterViewModel.Tag, null, result =>
{
if (result.Result == ButtonResult.OK)
{
// 选择的下载文件夹
directory = result.Parameters.GetValue<string>("directory");
// 下载内容
downloadAudio = result.Parameters.GetValue<bool>("downloadAudio");
downloadVideo = result.Parameters.GetValue<bool>("downloadVideo");
downloadDanmaku = result.Parameters.GetValue<bool>("downloadDanmaku");
downloadSubtitle = result.Parameters.GetValue<bool>("downloadSubtitle");
downloadCover = result.Parameters.GetValue<bool>("downloadCover");
}
});
}
// 下载设置dialog中如果点击取消或者关闭窗口
// 会返回空字符串,
// 这时直接退出
if (directory == string.Empty) { return; }
// 文件夹不存在则创建
if (!Directory.Exists(directory))
{
Directory.CreateDirectory(directory);
}
// 添加视频计数
int i = 0;
// 添加到下载
foreach (VideoSection section in VideoSections)
{
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<MessageEvent>().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) AddToDownloadService addToDownloadService = null;
{ // 视频
eventAggregator.GetEvent<MessageEvent>().Publish($"{page.Name}{DictionaryResource.GetString("TipAlreadyToAddDownloaded")}"); if (ParseEntrance.IsAvUrl(InputText) || ParseEntrance.IsBvUrl(InputText))
continue;
}
}
// 视频分区
int zoneId = -1;
List<ZoneAttr> 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; addToDownloadService = new AddToDownloadService(PlayStreamType.VIDEO);
}
} }
} // 番剧(电影、电视剧)
if (ParseEntrance.IsBangumiSeasonUrl(InputText) || ParseEntrance.IsBangumiEpisodeUrl(InputText) || ParseEntrance.IsBangumiMediaUrl(InputText))
// 如果只有一个视频章节,则不在命名中出现
string sectionName = string.Empty;
if (VideoSections.Count > 1)
{ {
sectionName = section.Title; addToDownloadService = new AddToDownloadService(PlayStreamType.BANGUMI);
} }
// 课程
// 文件路径 if (ParseEntrance.IsCheeseSeasonUrl(InputText) || ParseEntrance.IsCheeseEpisodeUrl(InputText))
List<FileNamePart> 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: addToDownloadService = new AddToDownloadService(PlayStreamType.CHEESE);
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 string directory = addToDownloadService.SetDirectory(dialogService);
{
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 // 视频计数
int i = 0;
await Task.Run(() =>
{ {
DownloadBase = downloadBase, // 传递video对象
Downloading = downloading, addToDownloadService.GetVideo(VideoInfoView, VideoSections.ToList());
PlayUrl = page.PlayUrl, // 下载
//ZoneImage = (DrawingImage)Application.Current.Resources[VideoZoneIcon.Instance().GetZoneImageKey(zoneId)], i = addToDownloadService.AddToDownload(eventAggregator, directory);
}; });
// 添加到下载列表
App.DownloadingList.Add(downloadingItem);
i++;
}
}
// 通知用户添加到下载列表的结果 // 通知用户添加到下载列表的结果
if (i == 0) if (i == 0)
@ -732,7 +553,6 @@ namespace DownKyi.ViewModels
#endregion #endregion
#region 业务逻辑 #region 业务逻辑
/// <summary> /// <summary>

@ -20,7 +20,7 @@
<ColumnDefinition Width="*" /> <ColumnDefinition Width="*" />
<ColumnDefinition Width="auto" /> <ColumnDefinition Width="auto" />
<ColumnDefinition Width="120" /> <ColumnDefinition Width="120" />
<ColumnDefinition Width="20" /> <ColumnDefinition Width="30" />
</Grid.ColumnDefinitions> </Grid.ColumnDefinitions>
<Image <Image
@ -187,7 +187,9 @@
Grid.Row="0" Grid.Row="0"
BorderThickness="0" BorderThickness="0"
ItemContainerStyle="{StaticResource DownloadedStyle}" ItemContainerStyle="{StaticResource DownloadedStyle}"
ItemsSource="{Binding DownloadedList}"> ItemsSource="{Binding DownloadedList}"
VirtualizingPanel.IsVirtualizing="True"
VirtualizingPanel.IsVirtualizingWhenGrouping="True">
<ListBox.Style> <ListBox.Style>
<Style TargetType="ListBox"> <Style TargetType="ListBox">
<Setter Property="Template"> <Setter Property="Template">
@ -198,7 +200,11 @@
Padding="0" Padding="0"
BorderBrush="{TemplateBinding BorderBrush}" BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"> BorderThickness="{TemplateBinding BorderThickness}">
<ScrollViewer CanContentScroll="False" Focusable="False"> <ScrollViewer
CanContentScroll="False"
Focusable="False"
HorizontalScrollBarVisibility="Hidden"
VerticalScrollBarVisibility="Auto">
<ItemsPresenter /> <ItemsPresenter />
</ScrollViewer> </ScrollViewer>
</Border> </Border>

@ -20,7 +20,7 @@
<ColumnDefinition Width="*" /> <ColumnDefinition Width="*" />
<ColumnDefinition Width="320" /> <ColumnDefinition Width="320" />
<ColumnDefinition Width="80" /> <ColumnDefinition Width="80" />
<ColumnDefinition Width="20" /> <ColumnDefinition Width="30" />
</Grid.ColumnDefinitions> </Grid.ColumnDefinitions>
<Image <Image
@ -241,7 +241,9 @@
Grid.Row="0" Grid.Row="0"
BorderThickness="0" BorderThickness="0"
ItemContainerStyle="{StaticResource DownloadingStyle}" ItemContainerStyle="{StaticResource DownloadingStyle}"
ItemsSource="{Binding DownloadingList}"> ItemsSource="{Binding DownloadingList}"
VirtualizingPanel.IsVirtualizing="True"
VirtualizingPanel.IsVirtualizingWhenGrouping="True">
<ListBox.Style> <ListBox.Style>
<Style TargetType="ListBox"> <Style TargetType="ListBox">
<Setter Property="Template"> <Setter Property="Template">
@ -252,7 +254,11 @@
Padding="0" Padding="0"
BorderBrush="{TemplateBinding BorderBrush}" BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"> BorderThickness="{TemplateBinding BorderThickness}">
<ScrollViewer CanContentScroll="False" Focusable="False"> <ScrollViewer
CanContentScroll="False"
Focusable="False"
HorizontalScrollBarVisibility="Hidden"
VerticalScrollBarVisibility="Auto">
<ItemsPresenter /> <ItemsPresenter />
</ScrollViewer> </ScrollViewer>
</Border> </Border>

@ -148,10 +148,10 @@
Orientation="Vertical"> Orientation="Vertical">
<ContentControl Width="16" Height="16"> <ContentControl Width="16" Height="16">
<Path <Path
Width="{Binding Play.Width}" Width="{Binding Favorites.Play.Width}"
Height="{Binding Play.Height}" Height="{Binding Favorites.Play.Height}"
Data="{Binding Play.Data}" Data="{Binding Favorites.Play.Data}"
Fill="{Binding Play.Fill}" Fill="{Binding Favorites.Play.Fill}"
Stretch="UniformToFill" /> Stretch="UniformToFill" />
</ContentControl> </ContentControl>
<TextBlock <TextBlock
@ -168,10 +168,10 @@
Orientation="Vertical"> Orientation="Vertical">
<ContentControl Width="16" Height="16"> <ContentControl Width="16" Height="16">
<Path <Path
Width="{Binding Like.Width}" Width="{Binding Favorites.Like.Width}"
Height="{Binding Like.Height}" Height="{Binding Favorites.Like.Height}"
Data="{Binding Like.Data}" Data="{Binding Favorites.Like.Data}"
Fill="{Binding Like.Fill}" Fill="{Binding Favorites.Like.Fill}"
Stretch="UniformToFill" /> Stretch="UniformToFill" />
</ContentControl> </ContentControl>
<TextBlock <TextBlock
@ -188,10 +188,10 @@
Orientation="Vertical"> Orientation="Vertical">
<ContentControl Width="16" Height="16"> <ContentControl Width="16" Height="16">
<Path <Path
Width="{Binding Favorite.Width}" Width="{Binding Favorites.Favorite.Width}"
Height="{Binding Favorite.Height}" Height="{Binding Favorites.Favorite.Height}"
Data="{Binding Favorite.Data}" Data="{Binding Favorites.Favorite.Data}"
Fill="{Binding Favorite.Fill}" Fill="{Binding Favorites.Favorite.Fill}"
Stretch="UniformToFill" /> Stretch="UniformToFill" />
</ContentControl> </ContentControl>
<TextBlock <TextBlock
@ -208,10 +208,10 @@
Orientation="Vertical"> Orientation="Vertical">
<ContentControl Width="16" Height="16"> <ContentControl Width="16" Height="16">
<Path <Path
Width="{Binding Share.Width}" Width="{Binding Favorites.Share.Width}"
Height="{Binding Share.Height}" Height="{Binding Favorites.Share.Height}"
Data="{Binding Share.Data}" Data="{Binding Favorites.Share.Data}"
Fill="{Binding Share.Fill}" Fill="{Binding Favorites.Share.Fill}"
Stretch="UniformToFill" /> Stretch="UniformToFill" />
</ContentControl> </ContentControl>
<TextBlock <TextBlock
@ -449,7 +449,6 @@
</TextBlock> </TextBlock>
</Grid> </Grid>
</Grid> </Grid>
<ControlTemplate.Triggers> <ControlTemplate.Triggers>

@ -619,14 +619,14 @@
DisplayMemberBinding="{Binding Order}" DisplayMemberBinding="{Binding Order}"
Header="{DynamicResource Order}" /> Header="{DynamicResource Order}" />
<GridViewColumn <GridViewColumn
Width="550" Width="540"
DisplayMemberBinding="{Binding Name}" DisplayMemberBinding="{Binding Name}"
Header="{DynamicResource Name}" /> Header="{DynamicResource Name}" />
<GridViewColumn <GridViewColumn
Width="100" Width="100"
DisplayMemberBinding="{Binding Duration}" DisplayMemberBinding="{Binding Duration}"
Header="{DynamicResource Duration}" /> Header="{DynamicResource Duration}" />
<GridViewColumn Width="100" Header="{DynamicResource AudioQuality}"> <GridViewColumn Width="120" Header="{DynamicResource AudioQuality}">
<GridViewColumn.CellTemplate> <GridViewColumn.CellTemplate>
<DataTemplate> <DataTemplate>
<Grid> <Grid>
@ -644,7 +644,13 @@
IsSynchronizedWithCurrentItem="True" IsSynchronizedWithCurrentItem="True"
ItemsSource="{Binding VideoQualityList, Mode=TwoWay, IsAsync=True}" ItemsSource="{Binding VideoQualityList, Mode=TwoWay, IsAsync=True}"
SelectedItem="{Binding VideoQuality, Mode=TwoWay}" SelectedItem="{Binding VideoQuality, Mode=TwoWay}"
SelectedValuePath="Quality" /> SelectedValuePath="Quality">
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectionChanged">
<i:InvokeCommandAction Command="{Binding VideoQualitySelectedCommand}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</ComboBox>
</Grid> </Grid>
</DataTemplate> </DataTemplate>
</GridViewColumn.CellTemplate> </GridViewColumn.CellTemplate>

Loading…
Cancel
Save