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

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

@ -6,6 +6,8 @@ namespace DownKyi.Core.BiliApi.BiliUtils
{
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 = "4K 超清", Id = 120 },
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 = "132K", Id = 30232 },
new Quality { Name = "192K", Id = 30280 },
new Quality { Name = "Dolby Atmos", Id = 30250 },
};
/// <summary>

@ -16,6 +16,7 @@ namespace DownKyi.Core.BiliApi.Video
/// <returns></returns>
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;

@ -16,5 +16,7 @@ namespace DownKyi.Core.BiliApi.VideoStream.Models
public List<PlayUrlDashVideo> Video { get; set; }
[JsonProperty("audio")]
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")]
public int Id { get; set; }
[JsonProperty("baseUrl")]
[JsonProperty("base_url")]
public string BaseUrl { get; set; }
[JsonProperty("backupUrl")]
[JsonProperty("backup_url")]
public List<string> BackupUrl { get; set; }
// bandwidth
[JsonProperty("mimeType")]

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

@ -49,7 +49,13 @@ 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";
// 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.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";

@ -211,6 +211,7 @@
<Compile Include="BiliApi\VideoStream\Models\PlayerV2.cs" />
<Compile Include="BiliApi\VideoStream\Models\PlayUrl.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\PlayUrlDurl.cs" />
<Compile Include="BiliApi\VideoStream\Models\PlayUrlSupportFormat.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
/// <summary>
/// 关闭数据库连接
/// </summary>

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

@ -174,6 +174,7 @@ namespace DownKyi.Core.Utils
public static string FormatFileName(string originName)
{
string destName = originName;
// Windows中不能作为文件名的字符
destName = destName.Replace("\\", " ");
destName = destName.Replace("/", " ");
@ -197,7 +198,8 @@ namespace DownKyi.Core.Utils
// 控制字符
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.ObjectModel;
namespace DownKyi.Core.Utils
{
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>
/// 判断List中是否存在不存在则添加
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="list"></param>
/// <param name="item"></param>
public static void AddUnique<T>(List<T> list, T item)
{
if (!list.Exists(t => t.Equals(item)))

@ -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);
}
}
}
}
});
});
// 启动下载服务

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

@ -6,6 +6,7 @@
WAIT_FOR_DOWNLOAD, // 等待下载,下载过,但是启动本次下载周期未开始,如重启程序后未开始
PAUSE_STARTED, // 暂停启动下载
PAUSE, // 暂停
//PAUSE_TO_WAIT, // 暂停后等待
DOWNLOADING, // 下载中
DOWNLOAD_SUCCEED, // 下载成功
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 int retry = 5;
private string nullMark = "<null>";
public AriaDownloadService(ObservableCollection<DownloadingItem> downloadingList, ObservableCollection<DownloadedItem> downloadedList) : base(downloadingList, downloadedList)
{
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);
}
@ -136,7 +150,11 @@ namespace DownKyi.Services.Download
else
{
// 记录本次下载的文件
downloading.Downloading.DownloadFiles.Add(key, fileName);
try
{
downloading.Downloading.DownloadFiles.Add(key, fileName);
}
catch (ArgumentException) { }
}
// 开始下载
@ -147,9 +165,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 +339,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 +383,9 @@ namespace DownKyi.Services.Download
if (downloading.PlayUrl != null && downloading.Downloading.DownloadStatus == DownloadStatus.NOT_STARTED)
{
// 设置下载状态
downloading.Downloading.DownloadStatus = DownloadStatus.DOWNLOADING;
return;
}
@ -453,28 +478,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 +524,7 @@ namespace DownKyi.Services.Download
}
// 降低CPU占用
Thread.Sleep(200);
Thread.Sleep(500);
}
}
@ -515,6 +554,10 @@ namespace DownKyi.Services.Download
// 解析并依次下载音频、视频、弹幕、字幕、封面等内容
Parse(downloading);
// 设置下载状态
// 必须在解析之后设置为DOWNLOADING
downloading.Downloading.DownloadStatus = DownloadStatus.DOWNLOADING;
// 暂停
Pause(downloading);
// 是否存在
@ -524,14 +567,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 != "<null>")
{
break;
}
}
}
if (audioUid == "<null>")
{
DownloadFailed(downloading);
return;
}
// 暂停
@ -547,7 +600,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 != "<null>")
{
break;
}
}
}
if (videoUid == "<null>")
{
DownloadFailed(downloading);
return;
}
// 暂停
@ -592,15 +658,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 +752,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 +765,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 +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>
@ -828,7 +903,7 @@ namespace DownKyi.Services.Download
$"Cookie: {LoginHelper.GetLoginInfoCookiesString()}",
$"Origin: 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()
@ -933,7 +1008,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;

@ -15,6 +15,8 @@ namespace DownKyi.Services.Download
/// <param name="downloadingItem"></param>
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
/// <param name="downloadingItem"></param>
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
/// <param name="downloadingItem"></param>
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
/// <param name="downloadedItem"></param>
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
/// <param name="downloadedItem"></param>
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
/// <param name="downloadedItem"></param>
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
/// <param name="downloadBase"></param>
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
/// <param name="downloadBase"></param>
private void UpdateDownloadBase(DownloadBase downloadBase)
{
if (downloadBase == null) { return; }
DownloadBaseDb downloadBaseDb = new DownloadBaseDb();
downloadBaseDb.Update(downloadBase.Uuid, downloadBase);
downloadBaseDb.Close();

@ -26,7 +26,7 @@ namespace DownKyi.Services
// 查询、保存封面
StorageCover storageCover = new StorageCover();
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;
@ -49,7 +49,7 @@ namespace DownKyi.Services
{
favorites.CoverUrl = coverUrl;
favorites.Cover = cover == null ? null : new BitmapImage(new Uri(cover));
favorites.Cover = cover;
favorites.Title = favoritesMetaInfo.Title;
DateTime startTime = TimeZone.CurrentTimeZone.ToLocalTime(new DateTime(1970, 1, 1)); // 当地时区
@ -93,12 +93,14 @@ namespace DownKyi.Services
int order = 0;
foreach (var media in medias)
{
if (media.Title == "已失效视频") { continue; }
order++;
// 查询、保存封面
StorageCover storageCover = new StorageCover();
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(() =>
{
@ -107,7 +109,7 @@ namespace DownKyi.Services
Avid = media.Id,
Bvid = media.Bvid,
Order = order,
Cover = cover == null ? null : new BitmapImage(new Uri(cover)),
Cover = cover,
Title = media.Title,
PlayNumber = media.CntInfo != null ? Format.FormatNumber(media.CntInfo.Play) : "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))
{
result.Add(newMedia);
Thread.Sleep(50);
Thread.Sleep(10);
}
}));
}

@ -8,7 +8,17 @@ namespace DownKyi.Services
public class SearchService
{
/// <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>
/// <param name="input"></param>
/// <param name="parentViewName"></param>

@ -5,6 +5,7 @@ using DownKyi.Core.Settings.Models;
using DownKyi.Core.Utils;
using DownKyi.ViewModels.PageViewModels;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
namespace DownKyi.Services
@ -94,10 +95,16 @@ namespace DownKyi.Services
/// <param name="playUrl"></param>
/// <param name="defaultAudioQuality"></param>
/// <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>();
foreach (var audio in playUrl.Dash.Audio)
if (playUrl.Dash.Audio == null)
{
return new ObservableCollection<string>();
}
foreach (PlayUrlDashVideo audio in playUrl.Dash.Audio)
{
// 音质id大于设置画质时跳过
if (audio.Id > defaultAudioQuality) { continue; }
@ -112,7 +119,7 @@ namespace DownKyi.Services
audioQualityFormatList.Sort(new StringLogicalComparer<string>());
audioQualityFormatList.Reverse();
return audioQualityFormatList;
return new ObservableCollection<string>(audioQualityFormatList);
}
/// <summary>
@ -126,7 +133,13 @@ namespace DownKyi.Services
private static List<VideoQuality> GetVideoQualityList(PlayUrl playUrl, UserInfoSettings userInfo, int defaultQuality, VideoCodecs videoCodecs)
{
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大于设置画质时跳过
if (video.Id > defaultQuality) { continue; }
@ -139,7 +152,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 +161,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<string>();
List<string> videoCodecList = new List<string>();
ListHelper.AddUnique(videoCodecList, codecName);
var videoQuality = new VideoQuality()
VideoQuality videoQuality = new VideoQuality()
{
Quality = video.Id,
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)
{
case VideoCodecs.AVC:
@ -186,6 +199,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 +222,7 @@ namespace DownKyi.Services
/// <returns></returns>
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>();
foreach (var section in videoView.UgcSeason.Sections)
foreach (UgcSection section in videoView.UgcSeason.Sections)
{
List<ViewModels.PageViewModels.VideoPage> pages = new List<ViewModels.PageViewModels.VideoPage>();
int order = 0;

@ -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 : "";
}
}

@ -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;

@ -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;

@ -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
/// <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

@ -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
/// <summary>
/// 删除所有下载事件
/// </summary>
private void ExecuteDeleteAllDownloadingCommand()
private async void ExecuteDeleteAllDownloadingCommand()
{
DownloadingList.Clear();
// 使用Clear()不能触发NotifyCollectionChangedAction.Remove事件
// 因此遍历删除
// DownloadingList中元素被删除后不能继续遍历
await Task.Run(() =>
{
List<DownloadingItem> list = DownloadingList.ToList();
foreach (DownloadingItem item in list)
{
App.PropertyChangeAsync(new Action(() =>
{
App.DownloadingList.Remove(item);
}));
}
});
}
#endregion

@ -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
}
}
}

@ -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 System.Collections.Generic;
using System.Collections.ObjectModel;
namespace DownKyi.ViewModels.PageViewModels
{
@ -43,8 +47,8 @@ namespace DownKyi.ViewModels.PageViewModels
set => SetProperty(ref duration, value);
}
private List<string> audioQualityFormatList;
public List<string> AudioQualityFormatList
private ObservableCollection<string> audioQualityFormatList;
public ObservableCollection<string> AudioQualityFormatList
{
get => audioQualityFormatList;
set => SetProperty(ref audioQualityFormatList, value);
@ -71,5 +75,37 @@ namespace DownKyi.ViewModels.PageViewModels
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.RemoveAt(3);
// 文件命名格式
SelectedFileName = new ObservableCollection<DisplayFileNamePart>();

@ -198,23 +198,12 @@ namespace DownKyi.ViewModels
#endregion
#region 业务逻辑
/// <summary>
/// 进入B站链接的处理逻辑
/// 只负责处理输入,并跳转到视频详情页。<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>
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.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<FavoritesMedia>();
#endregion
@ -185,6 +152,7 @@ namespace DownKyi.ViewModels
/// </summary>
private void ExecuteAddToDownloadCommand()
{
AddToDownload(true);
}
// 添加所有视频到下载列表事件
@ -196,6 +164,7 @@ namespace DownKyi.ViewModels
/// </summary>
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<MessageEvent>().Publish(DictionaryResource.GetString("TipAddDownloadingZero"));
}
else
{
eventAggregator.GetEvent<MessageEvent>().Publish($"{DictionaryResource.GetString("TipAddDownloadingFinished1")}{i}{DictionaryResource.GetString("TipAddDownloadingFinished2")}");
}
}
/// <summary>
/// 初始化页面元素
/// </summary>

@ -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
/// <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)
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<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");
}
});
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<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)
{
eventAggregator.GetEvent<MessageEvent>().Publish($"{page.Name}{DictionaryResource.GetString("TipAlreadyToAddDownloaded")}");
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.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 业务逻辑
/// <summary>

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

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

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

@ -619,14 +619,14 @@
DisplayMemberBinding="{Binding Order}"
Header="{DynamicResource Order}" />
<GridViewColumn
Width="550"
Width="540"
DisplayMemberBinding="{Binding Name}"
Header="{DynamicResource Name}" />
<GridViewColumn
Width="100"
DisplayMemberBinding="{Binding Duration}"
Header="{DynamicResource Duration}" />
<GridViewColumn Width="100" Header="{DynamicResource AudioQuality}">
<GridViewColumn Width="120" Header="{DynamicResource AudioQuality}">
<GridViewColumn.CellTemplate>
<DataTemplate>
<Grid>
@ -644,7 +644,13 @@
IsSynchronizedWithCurrentItem="True"
ItemsSource="{Binding VideoQualityList, Mode=TwoWay, IsAsync=True}"
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>
</DataTemplate>
</GridViewColumn.CellTemplate>

Loading…
Cancel
Save