diff --git a/DownKyi.Core/BiliApi/Bangumi/BangumiType.cs b/DownKyi.Core/BiliApi/Bangumi/BangumiType.cs index fae5abc..16558bf 100644 --- a/DownKyi.Core/BiliApi/Bangumi/BangumiType.cs +++ b/DownKyi.Core/BiliApi/Bangumi/BangumiType.cs @@ -18,5 +18,19 @@ namespace DownKyi.Core.BiliApi.Bangumi { 10, "Unknown" } }; + public static Dictionary TypeId = new Dictionary() + { + { 1, 13 }, + { 2, 23 }, + { 3, 177 }, + { 4, 167 }, + { 5, 11 }, + { 6, -1 }, + { 7, -1 }, + { 8, -1 }, + { 9, -1 }, + { 10, -1 } + }; + } } diff --git a/DownKyi.Core/BiliApi/BiliUtils/Constant.cs b/DownKyi.Core/BiliApi/BiliUtils/Constant.cs index 7b0ce8a..157c706 100644 --- a/DownKyi.Core/BiliApi/BiliUtils/Constant.cs +++ b/DownKyi.Core/BiliApi/BiliUtils/Constant.cs @@ -14,5 +14,15 @@ namespace DownKyi.Core.BiliApi.BiliUtils { 30280, "192K" } }; + /// + /// 音质id及含义 + /// + public static Dictionary AudioQualityId { get; } = new Dictionary() + { + { "64K", 30216 }, + { "132K", 30232 }, + { "192K", 30280 } + }; + } } diff --git a/DownKyi.Core/BiliApi/Video/Models/VideoPage.cs b/DownKyi.Core/BiliApi/Video/Models/VideoPage.cs index bcde7ba..c640552 100644 --- a/DownKyi.Core/BiliApi/Video/Models/VideoPage.cs +++ b/DownKyi.Core/BiliApi/Video/Models/VideoPage.cs @@ -21,5 +21,7 @@ namespace DownKyi.Core.BiliApi.Video.Models public string Weblink { get; set; } [JsonProperty("dimension")] public Dimension Dimension { get; set; } + [JsonProperty("first_frame")] + public string FirstFrame { get; set; } } } diff --git a/DownKyi.Core/BiliApi/VideoStream/VideoStream.cs b/DownKyi.Core/BiliApi/VideoStream/VideoStream.cs index 6ae5ca0..8490aea 100644 --- a/DownKyi.Core/BiliApi/VideoStream/VideoStream.cs +++ b/DownKyi.Core/BiliApi/VideoStream/VideoStream.cs @@ -37,7 +37,8 @@ namespace DownKyi.Core.BiliApi.VideoStream } /// - /// 获取所有字幕 + /// 获取所有字幕
+ /// 若视频没有字幕,返回null ///
/// /// @@ -48,8 +49,12 @@ namespace DownKyi.Core.BiliApi.VideoStream List subRipTexts = new List(); // 获取播放器信息 - var player = PlayerV2(avid, bvid, cid); + PlayerV2 player = PlayerV2(avid, bvid, cid); if (player == null) { return subRipTexts; } + if (player.Subtitle != null && player.Subtitle.Subtitles != null && player.Subtitle.Subtitles.Count == 0) + { + return null; + } foreach (var subtitle in player.Subtitle.Subtitles) { diff --git a/DownKyi.Core/BiliApi/Zone/VideoZoneIcon.cs b/DownKyi.Core/BiliApi/Zone/VideoZoneIcon.cs index 45e29aa..d55ab22 100644 --- a/DownKyi.Core/BiliApi/Zone/VideoZoneIcon.cs +++ b/DownKyi.Core/BiliApi/Zone/VideoZoneIcon.cs @@ -34,6 +34,9 @@ { switch (tid) { + // 课堂 + case -10: + return "Zone.cheeseDrawingImage"; case 1: return "Zone.dougaDrawingImage"; case 13: diff --git a/DownKyi.Core/BiliApi/Zone/ZoneImage.xaml b/DownKyi.Core/BiliApi/Zone/ZoneImage.xaml index 1cdfdfb..28a925d 100644 --- a/DownKyi.Core/BiliApi/Zone/ZoneImage.xaml +++ b/DownKyi.Core/BiliApi/Zone/ZoneImage.xaml @@ -46,6 +46,21 @@ + + + + + + + + + + + + + + + diff --git a/DownKyi.Core/Utils/Format.cs b/DownKyi.Core/Utils/Format.cs index d8f08c8..49ad0e7 100644 --- a/DownKyi.Core/Utils/Format.cs +++ b/DownKyi.Core/Utils/Format.cs @@ -1,4 +1,6 @@ -namespace DownKyi.Core.Utils +using System.Text.RegularExpressions; + +namespace DownKyi.Core.Utils { public static class Format { @@ -164,5 +166,39 @@ return formatFileSize; } + /// + /// 去除非法字符 + /// + /// + /// + public static string FormatFileName(string originName) + { + string destName = originName; + // Windows中不能作为文件名的字符 + destName = destName.Replace("\\", " "); + destName = destName.Replace("/", " "); + destName = destName.Replace(":", " "); + destName = destName.Replace("*", " "); + destName = destName.Replace("?", " "); + destName = destName.Replace("\"", " "); + destName = destName.Replace("<", " "); + destName = destName.Replace(">", " "); + destName = destName.Replace("|", " "); + + // 转义字符 + destName = destName.Replace("\a", ""); + destName = destName.Replace("\b", ""); + destName = destName.Replace("\f", ""); + destName = destName.Replace("\n", ""); + destName = destName.Replace("\r", ""); + destName = destName.Replace("\t", ""); + destName = destName.Replace("\v", ""); + + // 控制字符 + destName = Regex.Replace(destName, @"\p{C}+", string.Empty); + + return destName.Trim(); + } + } } diff --git a/DownKyi/App.xaml.cs b/DownKyi/App.xaml.cs index f39e512..d9b8f33 100644 --- a/DownKyi/App.xaml.cs +++ b/DownKyi/App.xaml.cs @@ -43,76 +43,6 @@ namespace DownKyi DownloadingList = new ObservableCollection(); DownloadedList = new ObservableCollection(); - // test DownloadingList data - DownloadingList.Add(new DownloadingItem - { - MainTitle = "Test1", - Name = "name1=name1=name1=name1=name1=name1=name1=name1=name1=name1=name1", - ZoneImage = (System.Windows.Media.DrawingImage)Current.Resources[Core.BiliApi.Zone.VideoZoneIcon.Instance().GetZoneImageKey(4)], - Resolution = new Resolution - { - Name = "1080P", - Id = 64 - }, - AudioCodecName = "194K", - }); - DownloadingList.Add(new DownloadingItem - { - MainTitle = "Test2", - Name = "name2", - ZoneImage = (System.Windows.Media.DrawingImage)Current.Resources[Core.BiliApi.Zone.VideoZoneIcon.Instance().GetZoneImageKey(3)], - Resolution = new Resolution - { - Name = "1080P", - Id = 64 - }, - Duration = "1h23m50s", - VideoCodecName = "HEVC", - AudioCodecName = "194K", - }); - DownloadingList.Add(new DownloadingItem - { - MainTitle = "Test3", - Name = "name3", - ZoneImage = (System.Windows.Media.DrawingImage)Current.Resources[Core.BiliApi.Zone.VideoZoneIcon.Instance().GetZoneImageKey(3)], - Resolution = new Resolution - { - Name = "720P", - Id = 64 - }, - Duration = "23m50s", - VideoCodecName = "AVC", - AudioCodecName = "194K", - }); - DownloadingList.Add(new DownloadingItem - { - MainTitle = "Test4", - Name = "name4", - ZoneImage = (System.Windows.Media.DrawingImage)Current.Resources[Core.BiliApi.Zone.VideoZoneIcon.Instance().GetZoneImageKey(3)], - Resolution = new Resolution - { - Name = "720P", - Id = 64 - }, - Duration = "23m50s", - VideoCodecName = "AVC", - AudioCodecName = "194K", - }); - DownloadingList.Add(new DownloadingItem - { - MainTitle = "Test5", - Name = "name5", - ZoneImage = (System.Windows.Media.DrawingImage)Current.Resources[Core.BiliApi.Zone.VideoZoneIcon.Instance().GetZoneImageKey(5)], - Resolution = new Resolution - { - Name = "720P", - Id = 64 - }, - Duration = "23m50s", - VideoCodecName = "AVC", - AudioCodecName = "194K", - }); - // TODO 从数据库读取 // 启动下载服务 diff --git a/DownKyi/DownKyi.csproj b/DownKyi/DownKyi.csproj index 7eadf65..e75cd65 100644 --- a/DownKyi/DownKyi.csproj +++ b/DownKyi/DownKyi.csproj @@ -87,6 +87,7 @@ + diff --git a/DownKyi/Languages/Default.xaml b/DownKyi/Languages/Default.xaml index e74787a..e3f29bb 100644 --- a/DownKyi/Languages/Default.xaml +++ b/DownKyi/Languages/Default.xaml @@ -64,6 +64,12 @@ 下载选中项 下载全部 + 已经添加到下载列表~ + 已经下载完成~ + 没有选中项符合下载要求! + 成功添加了 + 项~ + 正在下载 已下载 diff --git a/DownKyi/Models/DownloadBaseItem.cs b/DownKyi/Models/DownloadBaseItem.cs new file mode 100644 index 0000000..6c6c102 --- /dev/null +++ b/DownKyi/Models/DownloadBaseItem.cs @@ -0,0 +1,122 @@ +using Prism.Mvvm; +using System; +using System.Collections.Generic; +using System.Windows.Media; + +namespace DownKyi.Models +{ + public class DownloadBaseItem : BindableBase + { + public DownloadBaseItem() + { + // 唯一id + Uuid = Guid.NewGuid().ToString("N"); + + // 初始化需要下载的内容 + NeedDownloadContent = new Dictionary + { + { "downloadAudio", true }, + { "downloadVideo", true }, + { "downloadDanmaku", true }, + { "downloadSubtitle", true }, + { "downloadCover", true } + }; + } + + // 此条下载项的id + public string Uuid { get; } + + // 需要下载的内容 + public Dictionary NeedDownloadContent { get; private set; } + + // 视频的id + public string Bvid { get; set; } + public long Avid { get; set; } + public long Cid { get; set; } + public long EpisodeId { get; set; } + + // 视频封面的url + public string CoverUrl { get; set; } + + private DrawingImage zoneImage; + public DrawingImage ZoneImage + { + get => zoneImage; + set => SetProperty(ref zoneImage, value); + } + + // 视频序号 + private int order; + public int Order + { + get => order; + set => SetProperty(ref order, value); + } + + // 视频主标题 + private string mainTitle; + public string MainTitle + { + get => mainTitle; + set => SetProperty(ref mainTitle, value); + } + + // 视频标题 + private string name; + public string Name + { + get => name; + set => SetProperty(ref name, value); + } + + // 时长 + private string duration; + public string Duration + { + get => duration; + set => SetProperty(ref duration, value); + } + + // 音频编码 + public int AudioCodecId { get; set; } + private string audioCodecName; + public string AudioCodecName + { + get => audioCodecName; + set => SetProperty(ref audioCodecName, value); + } + + // 视频编码 + // "hev1.2.4.L156.90" + // "avc1.640034" + //public string VideoCodecId { get; set; } + + // 视频编码名称,AVC、HEVC + private string videoCodecName; + public string VideoCodecName + { + get => videoCodecName; + set => SetProperty(ref videoCodecName, value); + } + + // 视频画质 + private Resolution resolution; + public Resolution Resolution + { + get => resolution; + set => SetProperty(ref resolution, value); + } + + // 文件路径,不包含扩展名,所有内容均以此路径下载 + public string FilePath { get; set; } + + // 文件大小 + private string fileSize; + public string FileSize + { + get => fileSize; + set => SetProperty(ref fileSize, value); + } + + } +} diff --git a/DownKyi/Models/DownloadedItem.cs b/DownKyi/Models/DownloadedItem.cs index e13b160..c985651 100644 --- a/DownKyi/Models/DownloadedItem.cs +++ b/DownKyi/Models/DownloadedItem.cs @@ -1,13 +1,9 @@ -using Prism.Mvvm; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace DownKyi.Models +namespace DownKyi.Models { - public class DownloadedItem : BindableBase + public class DownloadedItem : DownloadBaseItem { + public DownloadedItem() : base() + { + } } } diff --git a/DownKyi/Models/DownloadingItem.cs b/DownKyi/Models/DownloadingItem.cs index c2aeb73..94a15d9 100644 --- a/DownKyi/Models/DownloadingItem.cs +++ b/DownKyi/Models/DownloadingItem.cs @@ -1,34 +1,19 @@ -using DownKyi.Core.BiliApi.VideoStream.Models; +using DownKyi.Core.Aria2cNet.Client; +using DownKyi.Core.BiliApi.VideoStream.Models; using DownKyi.Images; using DownKyi.Utils; using Prism.Commands; -using Prism.Mvvm; -using System; using System.Collections.Generic; -using System.Windows.Media; namespace DownKyi.Models { - public class DownloadingItem : BindableBase + public class DownloadingItem : DownloadBaseItem { - public DownloadingItem() + public DownloadingItem() : base() { - // 唯一id - Uuid = Guid.NewGuid().ToString("N"); - // 初始化下载的文件列表 DownloadFiles = new List(); - // 初始化需要下载的内容 - NeedDownloadContent = new Dictionary - { - { "downloadAudio", true }, - { "downloadVideo", true }, - { "downloadDanmaku", true }, - { "downloadSubtitle", true }, - { "downloadCover", true } - }; - // 暂停继续按钮 StartOrPause = ButtonIcon.Instance().Pause; StartOrPause.Fill = DictionaryResource.GetColor("ColorPrimary"); @@ -40,109 +25,15 @@ namespace DownKyi.Models public PlayUrl PlayUrl { get; set; } - // 此条下载项的id - public string Uuid { get; } - // Aria相关 public string Gid { get; set; } - // 文件路径,不包含扩展名,所有内容均以此路径下载 - public string FilePath { get; set; } - // 下载的文件 public List DownloadFiles { get; private set; } - // 文件大小 - private string fileSize; - public string FileSize - { - get => fileSize; - set => SetProperty(ref fileSize, value); - } - // 视频类别 public PlayStreamType PlayStreamType { get; set; } - // 视频的id - public string Bvid { get; set; } - public long Avid { get; set; } - public long Cid { get; set; } - public long EpisodeId { get; set; } - - // 视频封面的url - public string CoverUrl { get; set; } - - private DrawingImage zoneImage; - public DrawingImage ZoneImage - { - get => zoneImage; - set => SetProperty(ref zoneImage, value); - } - - // 视频序号 - private int order; - public int Order - { - get => order; - set => SetProperty(ref order, value); - } - - // 视频主标题 - private string mainTitle; - public string MainTitle - { - get => mainTitle; - set => SetProperty(ref mainTitle, value); - } - - // 视频标题 - private string name; - public string Name - { - get => name; - set => SetProperty(ref name, value); - } - - // 时长 - private string duration; - public string Duration - { - get => duration; - set => SetProperty(ref duration, value); - } - - // 音频编码 - public int AudioCodecId { get; set; } - private string audioCodecName; - public string AudioCodecName - { - get => audioCodecName; - set => SetProperty(ref audioCodecName, value); - } - - // 视频编码 - // "hev1.2.4.L156.90" - // "avc1.640034" - public string VideoCodecId { get; set; } - - // 视频编码名称,AVC、HEVC - private string videoCodecName; - public string VideoCodecName - { - get => videoCodecName; - set => SetProperty(ref videoCodecName, value); - } - - // 视频画质 - private Resolution resolution; - public Resolution Resolution - { - get => resolution; - set => SetProperty(ref resolution, value); - } - - // 需要下载的内容 - public Dictionary NeedDownloadContent { get; private set; } // 正在下载内容(音频、视频、弹幕、字幕、封面) private string downloadContent; diff --git a/DownKyi/Models/VideoInfoView.cs b/DownKyi/Models/VideoInfoView.cs index 5eb92b3..d0d15d7 100644 --- a/DownKyi/Models/VideoInfoView.cs +++ b/DownKyi/Models/VideoInfoView.cs @@ -9,103 +9,104 @@ namespace DownKyi.Models { public string CoverUrl { get; set; } public long UpperMid { get; set; } + public int TypeId { get; set; } private BitmapImage cover; public BitmapImage Cover { - get { return cover; } - set { SetProperty(ref cover, value); } + get => cover; + set => SetProperty(ref cover, value); } private string title; public string Title { - get { return title; } - set { SetProperty(ref title, value); } + get => title; + set => SetProperty(ref title, value); } private string videoZone; public string VideoZone { - get { return videoZone; } - set { SetProperty(ref videoZone, value); } + get => videoZone; + set => SetProperty(ref videoZone, value); } private string createTime; public string CreateTime { - get { return createTime; } - set { SetProperty(ref createTime, value); } + get => createTime; + set => SetProperty(ref createTime, value); } private string playNumber; public string PlayNumber { - get { return playNumber; } - set { SetProperty(ref playNumber, value); } + get => playNumber; + set => SetProperty(ref playNumber, value); } private string danmakuNumber; public string DanmakuNumber { - get { return danmakuNumber; } - set { SetProperty(ref danmakuNumber, value); } + get => danmakuNumber; + set => SetProperty(ref danmakuNumber, value); } private string likeNumber; public string LikeNumber { - get { return likeNumber; } - set { SetProperty(ref likeNumber, value); } + get => likeNumber; + set => SetProperty(ref likeNumber, value); } private string coinNumber; public string CoinNumber { - get { return coinNumber; } - set { SetProperty(ref coinNumber, value); } + get => coinNumber; + set => SetProperty(ref coinNumber, value); } private string favoriteNumber; public string FavoriteNumber { - get { return favoriteNumber; } - set { SetProperty(ref favoriteNumber, value); } + get => favoriteNumber; + set => SetProperty(ref favoriteNumber, value); } private string shareNumber; public string ShareNumber { - get { return shareNumber; } - set { SetProperty(ref shareNumber, value); } + get => shareNumber; + set => SetProperty(ref shareNumber, value); } private string replyNumber; public string ReplyNumber { - get { return replyNumber; } - set { SetProperty(ref replyNumber, value); } + get => replyNumber; + set => SetProperty(ref replyNumber, value); } private string description; public string Description { - get { return description; } - set { SetProperty(ref description, value); } + get => description; + set => SetProperty(ref description, value); } private string upName; public string UpName { - get { return upName; } - set { SetProperty(ref upName, value); } + get => upName; + set => SetProperty(ref upName, value); } private BitmapImage upHeader; public BitmapImage UpHeader { - get { return upHeader; } - set { SetProperty(ref upHeader, value); } + get => upHeader; + set => SetProperty(ref upHeader, value); } } diff --git a/DownKyi/Models/VideoPage.cs b/DownKyi/Models/VideoPage.cs index 11ea48d..7b93e92 100644 --- a/DownKyi/Models/VideoPage.cs +++ b/DownKyi/Models/VideoPage.cs @@ -13,60 +13,62 @@ namespace DownKyi.Models public long Cid { get; set; } public long EpisodeId { get; set; } + public string FirstFrame { get; set; } + private bool isSelected; public bool IsSelected { - get { return isSelected; } - set { SetProperty(ref isSelected, value); } + get => isSelected; + set => SetProperty(ref isSelected, value); } private int order; public int Order { - get { return order; } - set { SetProperty(ref order, value); } + get => order; + set => SetProperty(ref order, value); } private string name; public string Name { - get { return name; } - set { SetProperty(ref name, value); } + get => name; + set => SetProperty(ref name, value); } private string duration; public string Duration { - get { return duration; } - set { SetProperty(ref duration, value); } + get => duration; + set => SetProperty(ref duration, value); } private List audioQualityFormatList; public List AudioQualityFormatList { - get { return audioQualityFormatList; } - set { SetProperty(ref audioQualityFormatList, value); } + get => audioQualityFormatList; + set => SetProperty(ref audioQualityFormatList, value); } private string audioQualityFormat; public string AudioQualityFormat { - get { return audioQualityFormat; } - set { SetProperty(ref audioQualityFormat, value); } + get => audioQualityFormat; + set => SetProperty(ref audioQualityFormat, value); } private List videoQualityList; public List VideoQualityList { - get { return videoQualityList; } - set { SetProperty(ref videoQualityList, value); } + get => videoQualityList; + set => SetProperty(ref videoQualityList, value); } private VideoQuality videoQuality; public VideoQuality VideoQuality { - get { return videoQuality; } - set { SetProperty(ref videoQuality, value); } + get => videoQuality; + set => SetProperty(ref videoQuality, value); } } diff --git a/DownKyi/Models/VideoQuality.cs b/DownKyi/Models/VideoQuality.cs index 6df9996..41b966d 100644 --- a/DownKyi/Models/VideoQuality.cs +++ b/DownKyi/Models/VideoQuality.cs @@ -8,30 +8,29 @@ namespace DownKyi.Models private int quality; public int Quality { - get { return quality; } - set { SetProperty(ref quality, value); } + get => quality; + set => SetProperty(ref quality, value); } private string qualityFormat; public string QualityFormat { - get { return qualityFormat; } - set { SetProperty(ref qualityFormat, value); } + get => qualityFormat; + set => SetProperty(ref qualityFormat, value); } private List videoCodecList; public List VideoCodecList { - get { return videoCodecList; } - set { SetProperty(ref videoCodecList, value); } + get => videoCodecList; + set => SetProperty(ref videoCodecList, value); } private string selectedVideoCodec; public string SelectedVideoCodec { - get { return selectedVideoCodec; } - set { SetProperty(ref selectedVideoCodec, value); } + get => selectedVideoCodec; + set => SetProperty(ref selectedVideoCodec, value); } - } } diff --git a/DownKyi/Services/BangumiInfoService.cs b/DownKyi/Services/BangumiInfoService.cs index e9e0e52..cd89dbf 100644 --- a/DownKyi/Services/BangumiInfoService.cs +++ b/DownKyi/Services/BangumiInfoService.cs @@ -93,6 +93,7 @@ namespace DownKyi.Services Bvid = episode.Bvid, Cid = episode.Cid, EpisodeId = -1, + FirstFrame = episode.Cover, Order = order, Name = name, Duration = "N/A" @@ -149,6 +150,7 @@ namespace DownKyi.Services Bvid = episode.Bvid, Cid = episode.Cid, EpisodeId = -1, + FirstFrame = episode.Cover, Order = order, Name = name, Duration = "N/A" @@ -218,6 +220,9 @@ namespace DownKyi.Services videoInfoView.Cover = cover == null ? null : new BitmapImage(new Uri(cover)); videoInfoView.Title = bangumiSeason.Title; + // 分区id + videoInfoView.TypeId = BangumiType.TypeId[bangumiSeason.Type]; + videoInfoView.VideoZone = DictionaryResource.GetString(BangumiType.Type[bangumiSeason.Type]); videoInfoView.PlayNumber = Format.FormatNumber(bangumiSeason.Stat.Views); diff --git a/DownKyi/Services/CheeseInfoService.cs b/DownKyi/Services/CheeseInfoService.cs index 1a25a12..81f0987 100644 --- a/DownKyi/Services/CheeseInfoService.cs +++ b/DownKyi/Services/CheeseInfoService.cs @@ -61,6 +61,7 @@ namespace DownKyi.Services Bvid = null, Cid = episode.Cid, EpisodeId = episode.Id, + FirstFrame = episode.Cover, Order = order, Name = name, Duration = "N/A" @@ -129,6 +130,10 @@ namespace DownKyi.Services videoInfoView.Cover = cover == null ? null : new BitmapImage(new Uri(cover)); videoInfoView.Title = cheeseView.Title; + // 分区id + // 课堂的type id B站没有定义,这里自定义为-10 + videoInfoView.TypeId = -10; + videoInfoView.VideoZone = DictionaryResource.GetString("Cheese"); videoInfoView.CreateTime = ""; diff --git a/DownKyi/Services/Download/AriaDownloadService.cs b/DownKyi/Services/Download/AriaDownloadService.cs index 3e47673..f2d9e4e 100644 --- a/DownKyi/Services/Download/AriaDownloadService.cs +++ b/DownKyi/Services/Download/AriaDownloadService.cs @@ -67,38 +67,6 @@ namespace DownKyi.Services.Download } return DownloadVideo(downloading, downloadAudio); - //// 如果音频为空,说明没有匹配到可下载的音频 - //if (downloadAudio == null) { return null; } - - //// 下载链接 - //List audioUrls = new List(); - //if (downloadAudio.BaseUrl != null) { audioUrls.Add(downloadAudio.BaseUrl); } - //if (downloadAudio.BackupUrl != null) { audioUrls.AddRange(downloadAudio.BackupUrl); } - - //// 路径 - //string[] temp = downloading.FilePath.Split('/'); - //string path = downloading.FilePath.Replace(temp[temp.Length - 1], ""); - - //// 下载文件名 - //string fileName = Guid.NewGuid().ToString("N"); - //fileName = Path.Combine(path, fileName); - - //// 记录本次下载的文件 - //downloading.DownloadFiles.Add(fileName); - - //// 开始下载 - //DownloadResult downloadStatus = DownloadByAria(downloading, audioUrls, path, fileName); - //switch (downloadStatus) - //{ - // case DownloadResult.SUCCESS: - // return fileName; - // case DownloadResult.FAILED: - // return null; - // case DownloadResult.ABORT: - // return null; - // default: - // return null; - //} } /// @@ -123,7 +91,7 @@ namespace DownKyi.Services.Download PlayUrlDashVideo downloadVideo = null; foreach (PlayUrlDashVideo video in downloading.PlayUrl.Dash.Video) { - if (video.Id == downloading.Resolution.Id && video.Codecs == downloading.VideoCodecId) + if (video.Id == downloading.Resolution.Id && Utils.GetVideoCodecName(video.Codecs) == downloading.VideoCodecName) { downloadVideo = video; break; @@ -155,7 +123,6 @@ namespace DownKyi.Services.Download // 下载文件名 string fileName = Guid.NewGuid().ToString("N"); - fileName = Path.Combine(path, fileName); // 记录本次下载的文件 downloading.DownloadFiles.Add(fileName); @@ -165,7 +132,7 @@ namespace DownKyi.Services.Download switch (downloadStatus) { case DownloadResult.SUCCESS: - return fileName; + return Path.Combine(path, fileName); case DownloadResult.FAILED: return null; case DownloadResult.ABORT: @@ -287,6 +254,11 @@ namespace DownKyi.Services.Download List srtFiles = new List(); var subRipTexts = VideoStream.GetSubtitle(downloading.Avid, downloading.Bvid, downloading.Cid); + if (subRipTexts == null) + { + return null; + } + foreach (var subRip in subRipTexts) { string srtFile = $"{downloading.FilePath}_{subRip.LanDoc}.srt"; @@ -547,11 +519,18 @@ namespace DownKyi.Services.Download // 检测字幕是否下载成功 if (downloading.NeedDownloadContent["downloadSubtitle"]) { - foreach (string subtitle in outputSubtitles) + if (outputSubtitles == null) + { + // 为null时表示不存在字幕 + } + else { - if (File.Exists(subtitle)) + foreach (string subtitle in outputSubtitles) { - // 成功 + if (File.Exists(subtitle)) + { + // 成功 + } } } } @@ -692,7 +671,7 @@ namespace DownKyi.Services.Download } // 添加一个下载 - var ariaAddUri = AriaClient.AddUriAsync(urls, option); + Task ariaAddUri = AriaClient.AddUriAsync(urls, option); if (ariaAddUri == null || ariaAddUri.Result == null || ariaAddUri.Result.Result == null) { return DownloadResult.FAILED; @@ -711,12 +690,10 @@ namespace DownKyi.Services.Download switch (downloading.DownloadStatus) { case DownloadStatus.PAUSE: - var ariaPause = AriaClient.PauseAsync(downloading.Gid); - // TODO + Task ariaPause = AriaClient.PauseAsync(downloading.Gid); break; case DownloadStatus.DOWNLOADING: - var ariaUnpause = AriaClient.UnpauseAsync(downloading.Gid); - // TODO + Task ariaUnpause = AriaClient.UnpauseAsync(downloading.Gid); break; } })); @@ -726,6 +703,7 @@ namespace DownKyi.Services.Download { // 当前的下载视频 DownloadingItem video = downloadingList.FirstOrDefault(it => it.Gid == gid); + if (video == null) { return; } float percent = 0; if (totalLength != 0) diff --git a/DownKyi/Services/Utils.cs b/DownKyi/Services/Utils.cs index 3807f04..c16cf98 100644 --- a/DownKyi/Services/Utils.cs +++ b/DownKyi/Services/Utils.cs @@ -26,9 +26,9 @@ namespace DownKyi.Services page.PlayUrl = playUrl; // 获取设置 - var userInfo = SettingsManager.GetInstance().GetUserInfo(); + UserInfoSettings userInfo = SettingsManager.GetInstance().GetUserInfo(); int defaultQuality = SettingsManager.GetInstance().GetQuality(); - var videoCodecs = SettingsManager.GetInstance().GetVideoCodecs(); + VideoCodecs videoCodecs = SettingsManager.GetInstance().GetVideoCodecs(); int defaultAudioQuality = SettingsManager.GetInstance().GetAudioQuality(); // 未登录时,最高仅720P @@ -201,7 +201,7 @@ namespace DownKyi.Services /// /// /// - private static string GetVideoCodecName(string origin) + internal static string GetVideoCodecName(string origin) { return origin.Contains("avc") ? "H.264/AVC" : origin.Contains("hev") ? "H.265/HEVC" : ""; } diff --git a/DownKyi/Services/VideoInfoService.cs b/DownKyi/Services/VideoInfoService.cs index 49c6b60..2cf5f81 100644 --- a/DownKyi/Services/VideoInfoService.cs +++ b/DownKyi/Services/VideoInfoService.cs @@ -78,6 +78,7 @@ namespace DownKyi.Services Bvid = videoView.Bvid, Cid = page.Cid, EpisodeId = -1, + FirstFrame = page.FirstFrame, Order = order, Name = name, Duration = "N/A" @@ -114,6 +115,7 @@ namespace DownKyi.Services Bvid = episode.Bvid, Cid = episode.Cid, EpisodeId = -1, + FirstFrame = episode.Page.FirstFrame, Order = order, Name = episode.Title, Duration = "N/A" @@ -203,6 +205,9 @@ namespace DownKyi.Services videoInfoView.Cover = cover == null ? null : new BitmapImage(new Uri(cover)); videoInfoView.Title = videoView.Title; + // 分区id + videoInfoView.TypeId = videoView.Tid; + videoInfoView.VideoZone = videoZone; DateTime startTime = TimeZone.CurrentTimeZone.ToLocalTime(new DateTime(1970, 1, 1)); // 当地时区 diff --git a/DownKyi/ViewModels/DownloadManager/ViewDownloadingViewModel.cs b/DownKyi/ViewModels/DownloadManager/ViewDownloadingViewModel.cs index 7972fe8..547438a 100644 --- a/DownKyi/ViewModels/DownloadManager/ViewDownloadingViewModel.cs +++ b/DownKyi/ViewModels/DownloadManager/ViewDownloadingViewModel.cs @@ -1,12 +1,9 @@ -using DownKyi.Models; +using DownKyi.Images; +using DownKyi.Models; +using DownKyi.Utils; using Prism.Commands; using Prism.Events; -using Prism.Mvvm; -using System; -using System.Collections.Generic; using System.Collections.ObjectModel; -using System.Collections.Specialized; -using System.Linq; namespace DownKyi.ViewModels.DownloadManager { @@ -29,15 +26,6 @@ namespace DownKyi.ViewModels.DownloadManager { // 初始化DownloadingList DownloadingList = App.DownloadingList; - - - //// 下载列表发生变化时执行的任务 - //DownloadingList.CollectionChanged += new NotifyCollectionChangedEventHandler((object sender, NotifyCollectionChangedEventArgs e) => - //{ - // // save the downloading list and finished list. - // //SaveHistory(); - //}); - } #region 命令申明 @@ -51,6 +39,34 @@ namespace DownKyi.ViewModels.DownloadManager /// private void ExecutePauseAllDownloadingCommand() { + foreach (DownloadingItem downloading in downloadingList) + { + switch (downloading.DownloadStatus) + { + case DownloadStatus.NOT_STARTED: + case DownloadStatus.WAIT_FOR_DOWNLOAD: + downloading.DownloadStatus = DownloadStatus.PAUSE_STARTED; + break; + case DownloadStatus.PAUSE_STARTED: + break; + case DownloadStatus.PAUSE: + break; + case DownloadStatus.DOWNLOADING: + downloading.DownloadStatus = DownloadStatus.PAUSE; + break; + case DownloadStatus.DOWNLOAD_SUCCEED: + // 下载成功后会从下载列表中删除 + // 不会出现此分支 + break; + case DownloadStatus.DOWNLOAD_FAILED: + break; + default: + break; + } + + downloading.StartOrPause = ButtonIcon.Instance().Start; + downloading.StartOrPause.Fill = DictionaryResource.GetColor("ColorPrimary"); + } } // 继续所有下载事件 @@ -62,6 +78,35 @@ namespace DownKyi.ViewModels.DownloadManager /// private void ExecuteContinueAllDownloadingCommand() { + foreach (DownloadingItem downloading in downloadingList) + { + switch (downloading.DownloadStatus) + { + case DownloadStatus.NOT_STARTED: + case DownloadStatus.WAIT_FOR_DOWNLOAD: + break; + case DownloadStatus.PAUSE_STARTED: + downloading.DownloadStatus = DownloadStatus.WAIT_FOR_DOWNLOAD; + break; + case DownloadStatus.PAUSE: + downloading.DownloadStatus = DownloadStatus.DOWNLOADING; + break; + case DownloadStatus.DOWNLOADING: + break; + case DownloadStatus.DOWNLOAD_SUCCEED: + // 下载成功后会从下载列表中删除 + // 不会出现此分支 + break; + case DownloadStatus.DOWNLOAD_FAILED: + downloading.DownloadStatus = DownloadStatus.WAIT_FOR_DOWNLOAD; + break; + default: + break; + } + + downloading.StartOrPause = ButtonIcon.Instance().Pause; + downloading.StartOrPause.Fill = DictionaryResource.GetColor("ColorPrimary"); + } } // 删除所有下载事件 @@ -73,6 +118,7 @@ namespace DownKyi.ViewModels.DownloadManager /// private void ExecuteDeleteAllDownloadingCommand() { + DownloadingList.Clear(); } #endregion diff --git a/DownKyi/ViewModels/ViewVideoDetailViewModel.cs b/DownKyi/ViewModels/ViewVideoDetailViewModel.cs index 6084403..dfb6892 100644 --- a/DownKyi/ViewModels/ViewVideoDetailViewModel.cs +++ b/DownKyi/ViewModels/ViewVideoDetailViewModel.cs @@ -1,6 +1,9 @@ using DownKyi.Core.BiliApi.BiliUtils; +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; @@ -13,11 +16,13 @@ using Prism.Events; using Prism.Regions; 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; +using System.Windows.Media; namespace DownKyi.ViewModels { @@ -32,76 +37,75 @@ namespace DownKyi.ViewModels private VectorImage arrowBack; public VectorImage ArrowBack { - get { return arrowBack; } - set { SetProperty(ref arrowBack, value); } + get => arrowBack; + set => SetProperty(ref arrowBack, value); } private string inputText; public string InputText { - get { return inputText; } - set { SetProperty(ref inputText, value); } + get => inputText; + set => SetProperty(ref inputText, value); } private GifImage loading; public GifImage Loading { - get { return loading; } - set { SetProperty(ref loading, value); } + get => loading; + set => SetProperty(ref loading, value); } private Visibility loadingVisibility; public Visibility LoadingVisibility { - get { return loadingVisibility; } - set { SetProperty(ref loadingVisibility, value); } + get => loadingVisibility; + set => SetProperty(ref loadingVisibility, value); } private VectorImage downloadManage; public VectorImage DownloadManage { - get { return downloadManage; } - set { SetProperty(ref downloadManage, value); } + get => downloadManage; + set => SetProperty(ref downloadManage, value); } private VideoInfoView videoInfoView; public VideoInfoView VideoInfoView { - get { return videoInfoView; } - set { SetProperty(ref videoInfoView, value); } + get => videoInfoView; + set => SetProperty(ref videoInfoView, value); } private ObservableCollection videoSections; public ObservableCollection VideoSections { - get { return videoSections; } - set { SetProperty(ref videoSections, value); } + get => videoSections; + set => SetProperty(ref videoSections, value); } private bool isSelectAll; public bool IsSelectAll { - get { return isSelectAll; } - set { SetProperty(ref isSelectAll, value); } + get => isSelectAll; + set => SetProperty(ref isSelectAll, value); } private Visibility contentVisibility; public Visibility ContentVisibility { - get { return contentVisibility; } - set { SetProperty(ref contentVisibility, value); } + get => contentVisibility; + set => SetProperty(ref contentVisibility, value); } private Visibility noDataVisibility; public Visibility NoDataVisibility { - get { return noDataVisibility; } - set { SetProperty(ref noDataVisibility, value); } + get => noDataVisibility; + set => SetProperty(ref noDataVisibility, value); } #endregion - public ViewVideoDetailViewModel(IEventAggregator eventAggregator, IDialogService dialogService) : base(eventAggregator) { this.dialogService = dialogService; @@ -265,7 +269,7 @@ namespace DownKyi.ViewModels if (!(parameter is VideoSection section)) { return; } bool isSelectAll = true; - foreach (var page in section.VideoPages) + foreach (VideoPage page in section.VideoPages) { if (!page.IsSelected) { @@ -304,7 +308,7 @@ namespace DownKyi.ViewModels private void ExecuteKeySelectAllCommand(object parameter) { if (!(parameter is VideoSection section)) { return; } - foreach (var page in section.VideoPages) + foreach (VideoPage page in section.VideoPages) { page.IsSelected = true; } @@ -323,14 +327,14 @@ namespace DownKyi.ViewModels if (!(parameter is VideoSection section)) { return; } if (IsSelectAll) { - foreach (var page in section.VideoPages) + foreach (VideoPage page in section.VideoPages) { page.IsSelected = true; } } else { - foreach (var page in section.VideoPages) + foreach (VideoPage page in section.VideoPages) { page.IsSelected = false; } @@ -424,11 +428,11 @@ namespace DownKyi.ViewModels case ParseScope.NONE: break; case ParseScope.SELECTED_ITEM: - foreach (var section in VideoSections) + foreach (VideoSection section in VideoSections) { - foreach (var page in section.VideoPages) + foreach (VideoPage page in section.VideoPages) { - var videoPage = section.VideoPages.FirstOrDefault(t => t == page); + VideoPage videoPage = section.VideoPages.FirstOrDefault(t => t == page); if (videoPage.IsSelected) { @@ -439,13 +443,13 @@ namespace DownKyi.ViewModels } break; case ParseScope.CURRENT_SECTION: - foreach (var section in VideoSections) + foreach (VideoSection section in VideoSections) { if (section.IsSelected) { - foreach (var page in section.VideoPages) + foreach (VideoPage page in section.VideoPages) { - var videoPage = section.VideoPages.FirstOrDefault(t => t == page); + VideoPage videoPage = section.VideoPages.FirstOrDefault(t => t == page); // 执行解析任务 UnityUpdateView(ParseVideo, null, videoPage); @@ -454,11 +458,11 @@ namespace DownKyi.ViewModels } break; case ParseScope.ALL: - foreach (var section in VideoSections) + foreach (VideoSection section in VideoSections) { - foreach (var page in section.VideoPages) + foreach (VideoPage page in section.VideoPages) { - var videoPage = section.VideoPages.FirstOrDefault(t => t == page); + VideoPage videoPage = section.VideoPages.FirstOrDefault(t => t == page); // 执行解析任务 UnityUpdateView(ParseVideo, null, videoPage); @@ -530,12 +534,6 @@ namespace DownKyi.ViewModels downloadDanmaku = result.Parameters.GetValue("downloadDanmaku"); downloadSubtitle = result.Parameters.GetValue("downloadSubtitle"); downloadCover = result.Parameters.GetValue("downloadCover"); - - // 文件夹不存在则创建 - if (!Directory.Exists(directory)) - { - Directory.CreateDirectory(directory); - } } }); } @@ -545,8 +543,144 @@ namespace DownKyi.ViewModels // 这时直接退出 if (directory == string.Empty) { return; } + // 文件夹不存在则创建 + if (!Directory.Exists(directory)) + { + Directory.CreateDirectory(directory); + } + + // 添加视频计数 + int i = 0; + // 添加到下载 - eventAggregator.GetEvent().Publish(directory); + 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.Cid == page.Cid && item.Resolution.Id == page.VideoQuality.Quality && item.AudioCodecName == page.AudioQualityFormat && item.VideoCodecName == page.VideoQuality.SelectedVideoCodec) + { + eventAggregator.GetEvent().Publish($"{page.Name}{DictionaryResource.GetString("TipAlreadyToAddDownloading")}"); + continue; + } + } + + // 如果存在下载完成列表,弹出选择框是否再次下载 + foreach (DownloadedItem item in App.DownloadedList) + { + if (item.Cid == page.Cid && item.Resolution.Id == page.VideoQuality.Quality && item.AudioCodecName == page.AudioQualityFormat && item.VideoCodecName == page.VideoQuality.SelectedVideoCodec) + { + eventAggregator.GetEvent().Publish($"{page.Name}{DictionaryResource.GetString("TipAlreadyToAddDownloaded")}"); + continue; + } + } + + // 文件路径 + List fileNameParts = SettingsManager.GetInstance().GetFileNameParts(); + FileName fileName = FileName.Builder(fileNameParts) + .SetOrder(page.Order) + .SetSection(Format.FormatFileName(section.Title)) + .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; + } + + // 如果不存在,直接添加到下载列表 + DownloadingItem downloading = new DownloadingItem + { + PlayUrl = page.PlayUrl, + + Bvid = page.Bvid, + Avid = page.Avid, + Cid = page.Cid, + EpisodeId = page.EpisodeId, + + CoverUrl = page.FirstFrame, + ZoneImage = (DrawingImage)Application.Current.Resources[VideoZoneIcon.Instance().GetZoneImageKey(VideoInfoView.TypeId)], + + Order = page.Order, + MainTitle = VideoInfoView.Title, + Name = page.Name, + Duration = page.Duration, + AudioCodecId = Constant.AudioQualityId[page.AudioQualityFormat], + AudioCodecName = page.AudioQualityFormat, + VideoCodecName = page.VideoQuality.SelectedVideoCodec, + Resolution = new Resolution { Name = page.VideoQuality.QualityFormat, Id = page.VideoQuality.Quality }, + FilePath = filePath, + + PlayStreamType = playStreamType, + DownloadStatus = DownloadStatus.NOT_STARTED, + }; + // 需要下载的内容 + downloading.NeedDownloadContent["downloadAudio"] = downloadAudio; + downloading.NeedDownloadContent["downloadVideo"] = downloadVideo; + downloading.NeedDownloadContent["downloadDanmaku"] = downloadDanmaku; + downloading.NeedDownloadContent["downloadSubtitle"] = downloadSubtitle; + downloading.NeedDownloadContent["downloadCover"] = downloadCover; + + // 添加到下载列表 + App.DownloadingList.Add(downloading); + i++; + } + } + + // 通知用户添加到下载列表的结果 + if (i == 0) + { + eventAggregator.GetEvent().Publish(DictionaryResource.GetString("TipAddDownloadingZero")); + } + else + { + eventAggregator.GetEvent().Publish($"{DictionaryResource.GetString("TipAddDownloadingFinished1")}{i}{DictionaryResource.GetString("TipAddDownloadingFinished2")}"); + } } /// @@ -627,12 +761,12 @@ namespace DownKyi.ViewModels NoDataVisibility = Visibility.Collapsed; } - var videoSections = videoInfoService.GetVideoSections(); + List videoSections = videoInfoService.GetVideoSections(); if (videoSections == null) { LogManager.Debug(Tag, "videoSections is not exist."); - var pages = videoInfoService.GetVideoPages(); + List pages = videoInfoService.GetVideoPages(); PropertyChangeAsync(new Action(() => { diff --git a/DownKyi/Views/DownloadManager/ViewDownloading.xaml b/DownKyi/Views/DownloadManager/ViewDownloading.xaml index 4313e88..b13f441 100644 --- a/DownKyi/Views/DownloadManager/ViewDownloading.xaml +++ b/DownKyi/Views/DownloadManager/ViewDownloading.xaml @@ -88,7 +88,7 @@ - + diff --git a/DownKyi/Views/ViewVideoDetail.xaml b/DownKyi/Views/ViewVideoDetail.xaml index bb92c01..08d0042 100644 --- a/DownKyi/Views/ViewVideoDetail.xaml +++ b/DownKyi/Views/ViewVideoDetail.xaml @@ -558,6 +558,7 @@ @@ -566,7 +567,7 @@ - - - - - - - - - - -