From 0a26704f2ac4112423cf8601d724dc0ae7d9581f Mon Sep 17 00:00:00 2001
From: croire <1432593898@qq.com>
Date: Wed, 15 Dec 2021 21:42:16 +0800
Subject: [PATCH] =?UTF-8?q?=E5=BC=80=E5=8F=91=E4=B8=8B=E8=BD=BD=E6=9C=8D?=
=?UTF-8?q?=E5=8A=A1=E4=B8=AD=EF=BC=8C=E4=BF=9D=E5=AD=98=E5=B7=A5=E4=BD=9C?=
=?UTF-8?q?=EF=BC=8C20211215?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../BiliApi/VideoStream/VideoStream.cs | 1 +
DownKyi.Core/BiliApi/Zone/VideoZone.cs | 60 ++-
DownKyi.Core/BiliApi/Zone/VideoZoneIcon.cs | 85 +++
DownKyi.Core/BiliApi/Zone/ZoneImage.xaml | 293 +++++++++++
DownKyi.Core/Danmaku2Ass/Studio.cs | 10 +-
DownKyi.Core/DownKyi.Core.csproj | 8 +-
DownKyi.Core/FFmpeg/FFmpegHelper.cs | 8 +-
DownKyi/App.xaml | 1 +
DownKyi/App.xaml.cs | 73 ++-
DownKyi/Images/ButtonIcon.cs | 39 ++
DownKyi/Images/LogoIcon.cs | 2 +-
DownKyi/Languages/Default.xaml | 17 +
DownKyi/Models/DownloadStatus.cs | 1 +
DownKyi/Models/DownloadingItem.cs | 220 +++++++-
.../Services/Download/AriaDownloadService.cs | 485 +++++++++++++++---
DownKyi/Services/Download/IDownloadService.cs | 9 +-
DownKyi/Themes/ColorBrush.xaml | 2 +
DownKyi/Themes/Colors/ColorDefault.xaml | 2 +
DownKyi/Utils/DictionaryResource.cs | 2 +-
.../ViewDownloadingViewModel.cs | 53 +-
.../DownloadManager/ViewDownloading.xaml | 302 ++++++++++-
21 files changed, 1549 insertions(+), 124 deletions(-)
create mode 100644 DownKyi.Core/BiliApi/Zone/VideoZoneIcon.cs
create mode 100644 DownKyi.Core/BiliApi/Zone/ZoneImage.xaml
diff --git a/DownKyi.Core/BiliApi/VideoStream/VideoStream.cs b/DownKyi.Core/BiliApi/VideoStream/VideoStream.cs
index 2508f24..6ae5ca0 100644
--- a/DownKyi.Core/BiliApi/VideoStream/VideoStream.cs
+++ b/DownKyi.Core/BiliApi/VideoStream/VideoStream.cs
@@ -49,6 +49,7 @@ namespace DownKyi.Core.BiliApi.VideoStream
// 获取播放器信息
var player = PlayerV2(avid, bvid, cid);
+ if (player == null) { return subRipTexts; }
foreach (var subtitle in player.Subtitle.Subtitles)
{
diff --git a/DownKyi.Core/BiliApi/Zone/VideoZone.cs b/DownKyi.Core/BiliApi/Zone/VideoZone.cs
index 741a918..9474086 100644
--- a/DownKyi.Core/BiliApi/Zone/VideoZone.cs
+++ b/DownKyi.Core/BiliApi/Zone/VideoZone.cs
@@ -2,7 +2,6 @@
namespace DownKyi.Core.BiliApi.Zone
{
-
public class VideoZone
{
private static VideoZone that;
@@ -59,7 +58,7 @@ namespace DownKyi.Core.BiliApi.Zone
zones.Add(new ZoneAttr(30, "vocaloid", "VOCALOID·UTAU", 3)); //以雅马哈Vocaloid和UTAU引擎为基础,包含其他调教引擎,运用各类音源进行的歌曲创作内容
zones.Add(new ZoneAttr(194, "electronic", "电音", 3)); //以电子合成器、音乐软体等产生的电子声响制作的音乐
zones.Add(new ZoneAttr(59, "perform", "演奏", 3)); //传统或非传统乐器及器材的演奏作品
- zones.Add(new ZoneAttr(193, "mv", "MV", 3)); //音乐录影带,为搭配音乐而拍摄的短片
+ zones.Add(new ZoneAttr(193, "mv", "MV", 3)); //音乐录影带,为搭配音乐而拍摄或制作的视频
zones.Add(new ZoneAttr(29, "live", "音乐现场", 3)); //音乐实况表演视频
zones.Add(new ZoneAttr(130, "other", "音乐综合", 3)); //收录无法定义到其他音乐子分区的音乐视频
@@ -84,37 +83,48 @@ namespace DownKyi.Core.BiliApi.Zone
zones.Add(new ZoneAttr(19, "mugen", "Mugen", 4)); //以Mugen引擎为平台制作、或与Mugen相关的游戏视频
//知识
- zones.Add(new ZoneAttr(36, "technology", "知识")); // 主分区
+ zones.Add(new ZoneAttr(36, "knowledge", "知识")); // 主分区
zones.Add(new ZoneAttr(201, "science", "科学科普", 36)); //回答你的十万个为什么
- zones.Add(new ZoneAttr(124, "fun", "社科人文", 36)); //聊聊互联网社会法律,看看历史趣闻艺术,品品文化心理人物
- zones.Add(new ZoneAttr(207, "finance", "财经", 36)); //宏观经济分析,证券市场动态,商业帝国故事,知识与财富齐飞~
- zones.Add(new ZoneAttr(208, "campus", "校园学习", 36)); //老师很有趣,同学多人才,我们都爱搞学习
- zones.Add(new ZoneAttr(209, "career", "职业职场", 36)); //职场加油站,成为最有料的职场人
- zones.Add(new ZoneAttr(122, "wild", "野生技术协会", 36)); //炫酷技能大集合,是时候展现真正的技术了
-
- //数码
- zones.Add(new ZoneAttr(188, "digital", "数码")); // 主分区
- zones.Add(new ZoneAttr(95, "mobile", "手机平板", 188)); //手机平板、app 和产品教程等相关视频
- zones.Add(new ZoneAttr(189, "pc", "电脑装机", 188)); //电脑、笔记本、装机配件、外设和软件教程等相关视频
- zones.Add(new ZoneAttr(190, "photography", "摄影摄像", 188)); //摄影摄像器材、拍摄剪辑技巧、拍摄作品分享等相关视频
- zones.Add(new ZoneAttr(191, "intelligence_av", "影音智能", 188)); //影音设备、智能硬件、生活家电等相关视频
+ zones.Add(new ZoneAttr(124, "social_science", "社科·法律·心理", 36)); //基于社会科学、法学、心理学展开或个人观点输出的知识视频
+ zones.Add(new ZoneAttr(228, "humanity_history", "人文历史", 36)); //看看古今人物,聊聊历史过往,品品文学典籍
+ zones.Add(new ZoneAttr(207, "business", "财经商业", 36)); //说金融市场,谈宏观经济,一起畅聊商业故事
+ zones.Add(new ZoneAttr(208, "campus", "校园学习", 36)); //老师很有趣,学生也有才,我们一起搞学习
+ zones.Add(new ZoneAttr(209, "career", "职业职场", 36)); //职业分享、升级指南,一起成为最有料的职场人
+ zones.Add(new ZoneAttr(229, "design", "设计·创意", 36)); //天马行空,创意设计,都在这里
+ zones.Add(new ZoneAttr(122, "skill", "野生技能协会", 36)); //技能党集合,是时候展示真正的技术了
+
+ //科技
+ zones.Add(new ZoneAttr(188, "tech", "科技")); // 主分区
+ zones.Add(new ZoneAttr(95, "digital", "数码", 188)); //科技数码产品大全,一起来做发烧友
+ zones.Add(new ZoneAttr(230, "application", "软件应用", 188)); //超全软件应用指南
+ zones.Add(new ZoneAttr(231, "computer_tech", "计算机技术", 188)); //研究分析、教学演示、经验分享......有关计算机技术的都在这里
+ zones.Add(new ZoneAttr(232, "industry", "工业·工程·机械", 188)); //前方高能,机甲重工即将出没
+ zones.Add(new ZoneAttr(233, "diy", "极客DIY", 188)); //炫酷技能,极客文化,硬核技巧,准备好你的惊讶
+
+ //运动
+ zones.Add(new ZoneAttr(234, "sports", "运动")); // 主分区
+ zones.Add(new ZoneAttr(235, "basketballfootball", "篮球·足球", 234)); //与篮球、足球相关的视频,包括但不限于篮足球赛事、教学、评述、剪辑、剧情等相关内容
+ zones.Add(new ZoneAttr(164, "aerobics", "健身", 234)); //与健身相关的视频,包括但不限于瑜伽、CrossFit、健美、力量举、普拉提、街健等相关内容
+ zones.Add(new ZoneAttr(236, "athletic", "竞技体育", 234)); //与竞技体育相关的视频,包括但不限于乒乓、羽毛球、排球、赛车等竞技项目的赛事、评述、剪辑、剧情等相关内容
+ zones.Add(new ZoneAttr(237, "culture", "运动文化", 234)); //与运动文化相关的视频,包络但不限于球鞋、球衣、球星卡等运动衍生品的分享、解读,体育产业的分析、科普等相关内容
+ zones.Add(new ZoneAttr(238, "comprehensive", "运动综合", 234)); //与运动综合相关的视频,包括但不限于钓鱼、骑行、滑板等日常运动分享、教学、Vlog等相关内容
//汽车
zones.Add(new ZoneAttr(223, "car", "汽车")); // 主分区
zones.Add(new ZoneAttr(176, "life", "汽车生活", 223)); //分享汽车及出行相关的生活体验类视频
zones.Add(new ZoneAttr(224, "culture", "汽车文化", 223)); //车迷的精神圣地,包括汽车赛事、品牌历史、汽车改装、经典车型和汽车模型等
zones.Add(new ZoneAttr(225, "geek", "汽车极客", 223)); //汽车硬核达人聚集地,包括DIY造车、专业评测和技术知识分享
+ zones.Add(new ZoneAttr(240, "motorcycle", "摩托车", 223)); //骑士们集合啦
zones.Add(new ZoneAttr(226, "smart", "智能出行", 223)); //探索新能源汽车和未来智能出行的前沿阵地
zones.Add(new ZoneAttr(227, "strategy", "购车攻略", 223)); //丰富详实的购车建议和新车体验
//生活
zones.Add(new ZoneAttr(160, "life", "生活")); // 主分区
zones.Add(new ZoneAttr(138, "funny", "搞笑", 160)); //各种沙雕有趣的搞笑剪辑,挑战,表演,配音等视频
- zones.Add(new ZoneAttr(21, "daily", "日常", 160)); //记录日常生活,分享生活故事
+ zones.Add(new ZoneAttr(239, "home", "家居房产", 160)); //与买房、装修、居家生活相关的分享
zones.Add(new ZoneAttr(161, "handmake", "手工", 160)); //手工制品的制作过程或成品展示、教程、测评类视频
zones.Add(new ZoneAttr(162, "painting", "绘画", 160)); //绘画过程或绘画教程,以及绘画相关的所有视频
- zones.Add(new ZoneAttr(163, "sports", "运动", 160)); //运动相关的记录、教程、装备评测和精彩瞬间剪辑视频
- zones.Add(new ZoneAttr(174, "other", "其他", 160)); //对分区归属不明的视频进行归纳整合的特定分区
+ zones.Add(new ZoneAttr(21, "daily", "日常", 160)); //记录日常生活,分享生活故事
//美食
zones.Add(new ZoneAttr(211, "food", "美食")); // 主分区
@@ -143,11 +153,9 @@ namespace DownKyi.Core.BiliApi.Zone
//时尚
zones.Add(new ZoneAttr(155, "fashion", "时尚")); // 主分区
- zones.Add(new ZoneAttr(157, "makeup", "美妆", 155)); //涵盖妆容、发型、美甲等教程,彩妆、护肤相关产品测评、分享等
- zones.Add(new ZoneAttr(158, "clothing", "服饰", 155)); //服饰风格、搭配技巧相关的展示和教程视频
- zones.Add(new ZoneAttr(164, "aerobics", "健身", 155)); //器械、有氧、拉伸运动等,以达到强身健体、减肥瘦身、形体塑造目的
- zones.Add(new ZoneAttr(159, "catwalk", "T台", 155)); //发布会走秀现场及模特相关时尚片、采访、后台花絮
- zones.Add(new ZoneAttr(192, "trends", "风尚标", 155)); //时尚明星专访、街拍、时尚购物相关知识科普
+ zones.Add(new ZoneAttr(157, "makeup", "美妆护肤", 155)); //彩妆护肤、美甲美发、仿妆、医美相关内容分享或产品测评
+ zones.Add(new ZoneAttr(158, "clothing", "穿搭", 155)); //穿搭风格、穿搭技巧的展示分享,涵盖衣服、鞋靴、箱包配件、配饰(帽子、钟表、珠宝首饰)等
+ zones.Add(new ZoneAttr(159, "trend", "时尚潮流", 155)); //时尚街拍、时装周、时尚大片,时尚品牌、潮流等行业相关记录及知识科普
//资讯
zones.Add(new ZoneAttr(202, "information", "资讯")); // 主分区
@@ -158,8 +166,10 @@ namespace DownKyi.Core.BiliApi.Zone
//娱乐
zones.Add(new ZoneAttr(5, "ent", "娱乐")); // 主分区
- zones.Add(new ZoneAttr(71, "variety", "综艺", 5)); //国内外有趣的综艺和综艺相关精彩剪辑
- zones.Add(new ZoneAttr(137, "star", "明星", 5)); //娱乐圈动态、明星资讯相关
+ zones.Add(new ZoneAttr(71, "variety", "综艺", 5)); //所有综艺相关,全部一手掌握!
+ zones.Add(new ZoneAttr(241, "talker", "娱乐杂谈", 5)); //娱乐人物解读、娱乐热点点评、娱乐行业分析
+ zones.Add(new ZoneAttr(242, "fans", "粉丝创作", 5)); //粉丝向创作视频
+ zones.Add(new ZoneAttr(137, "celebrity", "明星综合", 5)); //娱乐圈动态、明星资讯相关
//影视
zones.Add(new ZoneAttr(181, "cinephile", "影视")); // 主分区
diff --git a/DownKyi.Core/BiliApi/Zone/VideoZoneIcon.cs b/DownKyi.Core/BiliApi/Zone/VideoZoneIcon.cs
new file mode 100644
index 0000000..45e29aa
--- /dev/null
+++ b/DownKyi.Core/BiliApi/Zone/VideoZoneIcon.cs
@@ -0,0 +1,85 @@
+namespace DownKyi.Core.BiliApi.Zone
+{
+ ///
+ /// 视频分区图标
+ ///
+ public class VideoZoneIcon
+ {
+ private static VideoZoneIcon instance;
+
+ ///
+ /// 获取VideoZoneIcon实例
+ ///
+ ///
+ public static VideoZoneIcon Instance()
+ {
+ if (instance == null)
+ {
+ instance = new VideoZoneIcon();
+ }
+ return instance;
+ }
+
+ ///
+ /// 隐藏VideoZoneIcon()方法,必须使用单例模式
+ ///
+ private VideoZoneIcon() { }
+
+ ///
+ /// 根据tid,获取视频分区图标
+ ///
+ ///
+ ///
+ public string GetZoneImageKey(int tid)
+ {
+ switch (tid)
+ {
+ case 1:
+ return "Zone.dougaDrawingImage";
+ case 13:
+ return "Zone.animeDrawingImage";
+ case 167:
+ return "Zone.guochuangDrawingImage";
+ case 3:
+ return "Zone.musicDrawingImage";
+ case 129:
+ return "Zone.danceDrawingImage";
+ case 4:
+ return "Zone.gameDrawingImage";
+ case 36:
+ return "Zone.techDrawingImage";
+ case 188:
+ return "Zone.digitalDrawingImage";
+ case 234:
+ return "Zone.sportsDrawingImage";
+ case 223:
+ return "Zone.carDrawingImage";
+ case 160:
+ return "Zone.lifeDrawingImage";
+ case 211:
+ return "Zone.foodDrawingImage";
+ case 217:
+ return "Zone.animalDrawingImage";
+ case 119:
+ return "Zone.kichikuDrawingImage";
+ case 155:
+ return "Zone.fashionDrawingImage";
+ case 202:
+ return "Zone.informationDrawingImage";
+ case 5:
+ return "Zone.entDrawingImage";
+ case 181:
+ return "Zone.cinephileDrawingImage";
+ case 177:
+ return "Zone.documentaryDrawingImage";
+ case 23:
+ return "Zone.movieDrawingImage";
+ case 11:
+ return "Zone.teleplayDrawingImage";
+ default:
+ return "videoUpDrawingImage";
+ }
+ }
+
+ }
+}
diff --git a/DownKyi.Core/BiliApi/Zone/ZoneImage.xaml b/DownKyi.Core/BiliApi/Zone/ZoneImage.xaml
new file mode 100644
index 0000000..1cdfdfb
--- /dev/null
+++ b/DownKyi.Core/BiliApi/Zone/ZoneImage.xaml
@@ -0,0 +1,293 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/DownKyi.Core/Danmaku2Ass/Studio.cs b/DownKyi.Core/Danmaku2Ass/Studio.cs
index 97acc48..630e323 100644
--- a/DownKyi.Core/Danmaku2Ass/Studio.cs
+++ b/DownKyi.Core/Danmaku2Ass/Studio.cs
@@ -1,4 +1,5 @@
-using System.Collections.Generic;
+using System;
+using System.Collections.Generic;
using System.IO;
using System.Linq;
@@ -67,7 +68,12 @@ namespace DownKyi.Core.Danmaku2Ass
public void CreateFile(string fileName, string text)
{
- File.WriteAllText(fileName, text);
+ try
+ {
+ File.WriteAllText(fileName, text);
+ }
+ catch (Exception)
+ { }
}
public Dictionary Report()
diff --git a/DownKyi.Core/DownKyi.Core.csproj b/DownKyi.Core/DownKyi.Core.csproj
index 834a58f..131523f 100644
--- a/DownKyi.Core/DownKyi.Core.csproj
+++ b/DownKyi.Core/DownKyi.Core.csproj
@@ -194,6 +194,7 @@
+
@@ -264,7 +265,12 @@
-
+
+
+ MSBuild:Compile
+ Designer
+
+
diff --git a/DownKyi.Core/FFmpeg/FFmpegHelper.cs b/DownKyi.Core/FFmpeg/FFmpegHelper.cs
index b77bf99..e94b204 100644
--- a/DownKyi.Core/FFmpeg/FFmpegHelper.cs
+++ b/DownKyi.Core/FFmpeg/FFmpegHelper.cs
@@ -7,13 +7,13 @@ namespace DownKyi.Core.FFmpeg
{
public static class FFmpegHelper
{
- private const string Tag = "PageToolboxDelogo";
+ private const string Tag = "FFmpegHelper";
///
/// 合并音频和视频
///
- ///
- ///
+ /// 音频
+ /// 视频
///
public static bool MergeVideo(string video1, string video2, string destVideo)
{
@@ -24,7 +24,7 @@ namespace DownKyi.Core.FFmpeg
}
if (video2 == null || !File.Exists(video2))
{
- param = $"-i \"{video1}\" -acodec copy -vcodec copy -f mp4 \"{destVideo}\"";
+ param = $"-i \"{video1}\" -acodec copy -f aac \"{destVideo}\"";
}
if (!File.Exists(video1) && !File.Exists(video2)) { return false; }
diff --git a/DownKyi/App.xaml b/DownKyi/App.xaml
index 34ef81c..7835032 100644
--- a/DownKyi/App.xaml
+++ b/DownKyi/App.xaml
@@ -9,6 +9,7 @@
+
diff --git a/DownKyi/App.xaml.cs b/DownKyi/App.xaml.cs
index 7ac3eaf..f39e512 100644
--- a/DownKyi/App.xaml.cs
+++ b/DownKyi/App.xaml.cs
@@ -40,10 +40,81 @@ namespace DownKyi
//DictionaryResource.LoadLanguage("en_US");
// 初始化数据
- // TODO 从数据库读取
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 从数据库读取
+
// 启动下载服务
downloadService = new AriaDownloadService(DownloadingList, DownloadedList);
downloadService.Start();
diff --git a/DownKyi/Images/ButtonIcon.cs b/DownKyi/Images/ButtonIcon.cs
index 73caefb..d747936 100644
--- a/DownKyi/Images/ButtonIcon.cs
+++ b/DownKyi/Images/ButtonIcon.cs
@@ -68,6 +68,41 @@
V8.5z M5.5,10v15h21V10H5.5z",
Fill = "#FF000000"
};
+
+ Delete = new VectorImage
+ {
+ Height = 18,
+ Width = 18,
+ Data = @"M634.29 513.52 l364.34 -363.32 q25.37 -27.4 25.37 -60.89 q0 -33.49 -26.38 -59.88 q-26.38 -26.39 -59.88 -26.39
+ q-33.49 0 -60.9 25.38 l-363.32 364.33 l-363.32 -372.45 q-28.42 -20.3 -65.46 -20.3 q-37.04 0 -64.44 20.3
+ q-20.3 27.4 -20.3 64.44 q0 37.04 20.3 65.46 l372.45 363.32 l-364.33 363.32 q-25.38 27.41 -25.38 60.9 q0 33.49 26.39 59.88
+ q26.39 26.38 59.88 26.38 q33.49 0 60.89 -25.37 l363.32 -364.34 l363.32 364.34 q27.41 25.37 60.9 25.37 q33.49 0 59.88 -26.38
+ q26.38 -26.38 26.38 -59.88 q0 -33.49 -25.37 -60.9 l-364.34 -363.32 Z",
+ Fill = "#FF000000"
+ };
+
+ Start = new VectorImage
+ {
+ Height = 20,
+ Width = 17,
+ Data = @"M895.12 402.34 l-633.28 -383.81 q-30.16 -17.82 -64.43 -18.51 q-34.27 -0.69 -65.11 16.45 q-30.84 17.13 -47.97 47.29
+ q-17.13 30.16 -17.13 64.42 l0 767.62 q0 34.27 17.13 63.74 q17.14 29.47 47.97 47.29 q30.84 17.82 65.11 17.13
+ q34.27 -0.69 64.43 -18.5 l633.28 -383.81 q28.79 -17.82 45.24 -46.6 q16.45 -28.79 16.45 -63.06 q0 -34.27 -16.45 -63.05
+ q-16.45 -28.79 -45.24 -46.61 Z",
+ Fill = "#FF000000"
+ };
+
+ Pause = new VectorImage
+ {
+ Height = 20,
+ Width = 15,
+ Data = @"M255.66 0 q-53.75 0 -90.97 37.21 q-37.21 37.21 -37.21 90.96 l0 769.04 q1.38 55.12 37.21 90.95 q35.84 35.84 90.28 35.84
+ q54.44 0 90.96 -35.84 q36.52 -35.83 37.9 -90.95 l0 -769.04 q-1.38 -53.75 -38.59 -90.96 q-37.21 -37.21 -89.58 -37.21
+ ZM768.34 0 q-52.37 0 -89.58 37.21 q-37.21 37.21 -38.59 90.96 l0 769.04 q1.38 55.12 37.9 90.95 q36.52 35.84 90.96 35.84
+ q54.44 0 90.28 -35.84 q35.83 -35.83 37.21 -90.95 l0 -769.04 q0 -53.75 -37.21 -90.96 q-37.22 -37.21 -90.97 -37.21 Z",
+ Fill = "#FF000000"
+ };
+
}
public VectorImage GeneralSearch { get; private set; }
@@ -75,5 +110,9 @@
public VectorImage DownloadManage { get; private set; }
public VectorImage Toolbox { get; private set; }
+ public VectorImage Delete { get; private set; }
+ public VectorImage Start { get; private set; }
+ public VectorImage Pause { get; private set; }
+
}
}
diff --git a/DownKyi/Images/LogoIcon.cs b/DownKyi/Images/LogoIcon.cs
index 853394f..6e074bc 100644
--- a/DownKyi/Images/LogoIcon.cs
+++ b/DownKyi/Images/LogoIcon.cs
@@ -1,6 +1,6 @@
namespace DownKyi.Images
{
- class LogoIcon
+ public class LogoIcon
{
private static LogoIcon instance;
public static LogoIcon Instance()
diff --git a/DownKyi/Languages/Default.xaml b/DownKyi/Languages/Default.xaml
index 559fef5..ab0dcf4 100644
--- a/DownKyi/Languages/Default.xaml
+++ b/DownKyi/Languages/Default.xaml
@@ -68,6 +68,23 @@
正在下载
已下载
+ 音频
+ 视频
+ 弹幕
+ 字幕
+ 封面
+ 正在解析……
+ 下载中……
+ 混流中……
+ 暂停中……
+ 等待中……
+
+ 正在下载
+ 个视频!
+ 全部暂停
+ 全部开始
+ 全部删除
+
按回车键应用设置
diff --git a/DownKyi/Models/DownloadStatus.cs b/DownKyi/Models/DownloadStatus.cs
index a462076..8aa0bd7 100644
--- a/DownKyi/Models/DownloadStatus.cs
+++ b/DownKyi/Models/DownloadStatus.cs
@@ -4,6 +4,7 @@
{
NOT_STARTED, // 未开始,从未开始下载
WAIT_FOR_DOWNLOAD, // 等待下载,下载过,但是启动本次下载周期未开始,如重启程序后未开始
+ PAUSE_STARTED, // 暂停启动下载
PAUSE, // 暂停
DOWNLOADING, // 下载中
DOWNLOAD_SUCCEED, // 下载成功
diff --git a/DownKyi/Models/DownloadingItem.cs b/DownKyi/Models/DownloadingItem.cs
index eacb533..c2aeb73 100644
--- a/DownKyi/Models/DownloadingItem.cs
+++ b/DownKyi/Models/DownloadingItem.cs
@@ -1,20 +1,64 @@
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 DownloadingItem()
+ {
+ // 唯一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");
+
+ // 删除按钮
+ Delete = ButtonIcon.Instance().Delete;
+ Delete.Fill = DictionaryResource.GetColor("ColorPrimary");
+ }
+
public PlayUrl PlayUrl { get; set; }
// 此条下载项的id
public string Uuid { get; }
+ // Aria相关
+ public string Gid { get; set; }
+
// 文件路径,不包含扩展名,所有内容均以此路径下载
public string FilePath { get; set; }
- // Aria相关
- public string Gid { 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; }
@@ -28,28 +72,43 @@ namespace DownKyi.Models
// 视频封面的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 { return order; }
- set { SetProperty(ref order, value); }
+ get => order;
+ set => SetProperty(ref order, value);
}
// 视频主标题
private string mainTitle;
public string MainTitle
{
- get { return mainTitle; }
- set { SetProperty(ref mainTitle, value); }
+ get => mainTitle;
+ set => SetProperty(ref mainTitle, 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 => duration;
+ set => SetProperty(ref duration, value);
}
// 音频编码
@@ -57,35 +116,160 @@ namespace DownKyi.Models
private string audioCodecName;
public string AudioCodecName
{
- get { return audioCodecName; }
- set { SetProperty(ref audioCodecName, value); }
+ get => audioCodecName;
+ set => SetProperty(ref audioCodecName, value);
}
// 视频编码
- public int VideoCodecId { get; set; }
+ // "hev1.2.4.L156.90"
+ // "avc1.640034"
+ public string VideoCodecId { get; set; }
+
+ // 视频编码名称,AVC、HEVC
private string videoCodecName;
public string VideoCodecName
{
- get { return videoCodecName; }
- set { SetProperty(ref videoCodecName, value); }
+ 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;
public string DownloadContent
{
- get { return downloadContent; }
- set { SetProperty(ref downloadContent, value); }
+ get => downloadContent;
+ set => SetProperty(ref downloadContent, value);
}
// 下载状态
public DownloadStatus DownloadStatus { get; set; }
+
+ // 下载状态显示
private string downloadStatusTitle;
public string DownloadStatusTitle
{
- get { return downloadStatusTitle; }
- set { SetProperty(ref downloadStatusTitle, value); }
+ get => downloadStatusTitle;
+ set => SetProperty(ref downloadStatusTitle, value);
+ }
+
+ // 下载进度
+ private float progress;
+ public float Progress
+ {
+ get => progress;
+ set => SetProperty(ref progress, value);
+ }
+
+ // 已下载大小/文件大小
+ private string downloadingFileSize;
+ public string DownloadingFileSize
+ {
+ get => downloadingFileSize;
+ set => SetProperty(ref downloadingFileSize, value);
+ }
+
+ // 下载的最高速度
+ public long MaxSpeed { get; set; }
+
+ // 下载速度
+ private string speedDisplay;
+ public string SpeedDisplay
+ {
+ get => speedDisplay;
+ set => SetProperty(ref speedDisplay, value);
+ }
+
+
+ #region 控制按钮
+
+ private VectorImage startOrPause;
+ public VectorImage StartOrPause
+ {
+ get => startOrPause;
+ set => SetProperty(ref startOrPause, value);
}
+ private VectorImage delete;
+ public VectorImage Delete
+ {
+ get => delete;
+ set => SetProperty(ref delete, value);
+ }
+
+ #endregion
+
+ #region 命令申明
+
+ // 下载列表暂停继续事件
+ private DelegateCommand startOrPauseCommand;
+ public DelegateCommand StartOrPauseCommand => startOrPauseCommand ?? (startOrPauseCommand = new DelegateCommand(ExecuteStartOrPauseCommand));
+
+ ///
+ /// 下载列表暂停继续事件
+ ///
+ private void ExecuteStartOrPauseCommand()
+ {
+ switch (DownloadStatus)
+ {
+ case DownloadStatus.NOT_STARTED:
+ case DownloadStatus.WAIT_FOR_DOWNLOAD:
+ DownloadStatus = DownloadStatus.PAUSE_STARTED;
+ StartOrPause = ButtonIcon.Instance().Start;
+ StartOrPause.Fill = DictionaryResource.GetColor("ColorPrimary");
+ break;
+ case DownloadStatus.PAUSE_STARTED:
+ DownloadStatus = DownloadStatus.WAIT_FOR_DOWNLOAD;
+ StartOrPause = ButtonIcon.Instance().Pause;
+ StartOrPause.Fill = DictionaryResource.GetColor("ColorPrimary");
+ break;
+ case DownloadStatus.PAUSE:
+ DownloadStatus = DownloadStatus.DOWNLOADING;
+ StartOrPause = ButtonIcon.Instance().Pause;
+ StartOrPause.Fill = DictionaryResource.GetColor("ColorPrimary");
+ break;
+ case DownloadStatus.DOWNLOADING:
+ DownloadStatus = DownloadStatus.PAUSE;
+ StartOrPause = ButtonIcon.Instance().Start;
+ StartOrPause.Fill = DictionaryResource.GetColor("ColorPrimary");
+ break;
+ case DownloadStatus.DOWNLOAD_SUCCEED:
+ // 下载成功后会从下载列表中删除
+ // 不会出现此分支
+ break;
+ case DownloadStatus.DOWNLOAD_FAILED:
+ DownloadStatus = DownloadStatus.WAIT_FOR_DOWNLOAD;
+ StartOrPause = ButtonIcon.Instance().Pause;
+ StartOrPause.Fill = DictionaryResource.GetColor("ColorPrimary");
+ break;
+ default:
+ break;
+ }
+ }
+
+ // 下载列表删除事件
+ private DelegateCommand deleteCommand;
+ public DelegateCommand DeleteCommand => deleteCommand ?? (deleteCommand = new DelegateCommand(ExecuteDeleteCommand));
+
+ ///
+ /// 下载列表删除事件
+ ///
+ private void ExecuteDeleteCommand()
+ {
+ App.DownloadingList.Remove(this);
+ }
+
+ #endregion
+
}
}
diff --git a/DownKyi/Services/Download/AriaDownloadService.cs b/DownKyi/Services/Download/AriaDownloadService.cs
index dab3dff..3e47673 100644
--- a/DownKyi/Services/Download/AriaDownloadService.cs
+++ b/DownKyi/Services/Download/AriaDownloadService.cs
@@ -4,15 +4,20 @@ using DownKyi.Core.Aria2cNet.Client.Entity;
using DownKyi.Core.Aria2cNet.Server;
using DownKyi.Core.BiliApi.Login;
using DownKyi.Core.BiliApi.VideoStream;
+using DownKyi.Core.BiliApi.VideoStream.Models;
using DownKyi.Core.Danmaku2Ass;
+using DownKyi.Core.FFmpeg;
using DownKyi.Core.Logging;
using DownKyi.Core.Settings;
using DownKyi.Core.Storage;
+using DownKyi.Core.Utils;
using DownKyi.Models;
+using DownKyi.Utils;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;
+using System.Linq;
using System.Threading;
using System.Threading.Tasks;
@@ -30,31 +35,164 @@ namespace DownKyi.Services.Download
Tag = "AriaDownloadService";
}
+ #region 音视频
+
+ ///
+ /// 下载音频,返回下载文件路径
+ ///
+ ///
+ ///
public string DownloadAudio(DownloadingItem downloading)
{
+ // 更新状态显示
+ downloading.DownloadStatusTitle = DictionaryResource.GetString("WhileDownloading");
+ downloading.DownloadContent = DictionaryResource.GetString("DownloadingAudio");
+
// 如果没有Dash,返回null
- if(downloading.PlayUrl.Dash == null) { return null; }
+ if (downloading.PlayUrl == null || downloading.PlayUrl.Dash == null) { return null; }
// 如果audio列表没有内容,则返回null
if (downloading.PlayUrl.Dash.Audio == null) { return null; }
else if (downloading.PlayUrl.Dash.Audio.Count == 0) { return null; }
+ // 根据音频id匹配
+ PlayUrlDashVideo downloadAudio = null;
+ foreach (PlayUrlDashVideo audio in downloading.PlayUrl.Dash.Audio)
+ {
+ if (audio.Id == downloading.AudioCodecId)
+ {
+ downloadAudio = audio;
+ break;
+ }
+ }
+
+ 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;
+ //}
+ }
+
+ ///
+ /// 下载视频,返回下载文件路径
+ ///
+ ///
+ ///
+ public string DownloadVideo(DownloadingItem downloading)
+ {
+ // 更新状态显示
+ downloading.DownloadStatusTitle = DictionaryResource.GetString("WhileDownloading");
+ downloading.DownloadContent = DictionaryResource.GetString("DownloadingVideo");
+
+ // 如果没有Dash,返回null
+ if (downloading.PlayUrl == null || downloading.PlayUrl.Dash == null) { return null; }
- throw new NotImplementedException();
+ // 如果Video列表没有内容,则返回null
+ if (downloading.PlayUrl.Dash.Video == null) { return null; }
+ else if (downloading.PlayUrl.Dash.Video.Count == 0) { return null; }
+
+ // 根据视频编码匹配
+ PlayUrlDashVideo downloadVideo = null;
+ foreach (PlayUrlDashVideo video in downloading.PlayUrl.Dash.Video)
+ {
+ if (video.Id == downloading.Resolution.Id && video.Codecs == downloading.VideoCodecId)
+ {
+ downloadVideo = video;
+ break;
+ }
+ }
+
+ return DownloadVideo(downloading, downloadVideo);
}
+ ///
+ /// 将下载音频和视频的函数中相同代码抽象出来
+ ///
+ ///
+ ///
+ ///
+ private string DownloadVideo(DownloadingItem downloading, PlayUrlDashVideo downloadVideo)
+ {
+ // 如果为空,说明没有匹配到可下载的音频视频
+ if (downloadVideo == null) { return null; }
+
+ // 下载链接
+ List urls = new List();
+ if (downloadVideo.BaseUrl != null) { urls.Add(downloadVideo.BaseUrl); }
+ if (downloadVideo.BackupUrl != null) { urls.AddRange(downloadVideo.BackupUrl); }
+
+ // 路径
+ 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, urls, path, fileName);
+ switch (downloadStatus)
+ {
+ case DownloadResult.SUCCESS:
+ return fileName;
+ case DownloadResult.FAILED:
+ return null;
+ case DownloadResult.ABORT:
+ return null;
+ default:
+ return null;
+ }
+ }
+
+ #endregion
+
///
/// 下载封面
///
///
- public void DownloadCover(DownloadingItem downloading)
+ public string DownloadCover(DownloadingItem downloading)
{
+ // 更新状态显示
+ downloading.DownloadStatusTitle = DictionaryResource.GetString("WhileDownloading");
+ downloading.DownloadContent = DictionaryResource.GetString("DownloadingCover");
+
// 查询、保存封面
StorageCover storageCover = new StorageCover();
string cover = storageCover.GetCover(downloading.Avid, downloading.Bvid, downloading.Cid, downloading.CoverUrl);
if (cover == null)
{
- return;
+ return null;
}
// 图片的扩展名
@@ -67,24 +205,39 @@ namespace DownKyi.Services.Download
// 复制图片到指定位置
try
{
- File.Copy(coverPath, $"{downloading.FilePath}.{fileExtension}");
+ string fileName = $"{downloading.FilePath}.{fileExtension}";
+ File.Copy(coverPath, fileName);
+
+ // 记录本次下载的文件
+ downloading.DownloadFiles.Add(fileName);
+
+ return fileName;
}
catch (Exception e)
{
Core.Utils.Debugging.Console.PrintLine(e);
LogManager.Error(Tag, e);
}
+
+ return null;
}
///
/// 下载弹幕
///
///
- public void DownloadDanmaku(DownloadingItem downloading)
+ public string DownloadDanmaku(DownloadingItem downloading)
{
+ // 更新状态显示
+ downloading.DownloadStatusTitle = DictionaryResource.GetString("WhileDownloading");
+ downloading.DownloadContent = DictionaryResource.GetString("DownloadingDanmaku");
+
string title = $"{downloading.Name}";
string assFile = $"{downloading.FilePath}.ass";
+ // 记录本次下载的文件
+ downloading.DownloadFiles.Add(assFile);
+
int screenWidth = SettingsManager.GetInstance().GetDanmakuScreenWidth();
int screenHeight = SettingsManager.GetInstance().GetDanmakuScreenHeight();
//if (SettingsManager.GetInstance().IsCustomDanmakuResolution() != AllowStatus.YES)
@@ -117,14 +270,22 @@ namespace DownKyi.Services.Download
.SetBottomFilter(SettingsManager.GetInstance().GetDanmakuBottomFilter() == AllowStatus.YES)
.SetScrollFilter(SettingsManager.GetInstance().GetDanmakuScrollFilter() == AllowStatus.YES)
.Create(downloading.Avid, downloading.Cid, subtitleConfig, assFile);
+
+ return assFile;
}
///
/// 下载字幕
///
///
- public void DownloadSubtitle(DownloadingItem downloading)
+ public List DownloadSubtitle(DownloadingItem downloading)
{
+ // 更新状态显示
+ downloading.DownloadStatusTitle = DictionaryResource.GetString("WhileDownloading");
+ downloading.DownloadContent = DictionaryResource.GetString("DownloadingSubtitle");
+
+ List srtFiles = new List();
+
var subRipTexts = VideoStream.GetSubtitle(downloading.Avid, downloading.Bvid, downloading.Cid);
foreach (var subRip in subRipTexts)
{
@@ -132,6 +293,11 @@ namespace DownKyi.Services.Download
try
{
File.WriteAllText(srtFile, subRip.SrtString);
+
+ // 记录本次下载的文件
+ downloading.DownloadFiles.Add(srtFile);
+
+ srtFiles.Add(srtFile);
}
catch (Exception e)
{
@@ -139,30 +305,44 @@ namespace DownKyi.Services.Download
LogManager.Error("DownloadSubtitle()", e);
}
}
- }
- public string DownloadVideo(DownloadingItem downloading)
- {
- throw new NotImplementedException();
+ return srtFiles;
}
///
- /// 停止下载服务
+ /// 混流音频和视频
///
- public void End()
+ ///
+ ///
+ ///
+ ///
+ public string MixedFlow(DownloadingItem downloading, string audioUid, string videoUid)
{
- // TODO something
+ // 更新状态显示
+ downloading.DownloadStatusTitle = DictionaryResource.GetString("MixedFlow");
+ downloading.DownloadContent = DictionaryResource.GetString("DownloadingVideo");
- // 关闭Aria服务器
- CloseAriaServer();
+ string finalFile = $"{downloading.FilePath}.mp4";
+ if (videoUid == null)
+ {
+ finalFile = $"{downloading.FilePath}.aac";
+ }
- // 结束任务
- tokenSource.Cancel();
- }
+ // 合并音视频
+ FFmpegHelper.MergeVideo(audioUid, videoUid, finalFile);
- public void MixedFlow(DownloadingItem downloading, string audioUid, string videoUid)
- {
- throw new NotImplementedException();
+ // 获取文件大小
+ if (File.Exists(finalFile))
+ {
+ FileInfo info = new FileInfo(finalFile);
+ downloading.FileSize = Format.FormatFileSize(info.Length);
+ }
+ else
+ {
+ downloading.FileSize = Format.FormatFileSize(0);
+ }
+
+ return finalFile;
}
///
@@ -171,11 +351,16 @@ namespace DownKyi.Services.Download
///
public void Parse(DownloadingItem downloading)
{
+ // 更新状态显示
+ downloading.DownloadStatusTitle = DictionaryResource.GetString("Parsing");
+ downloading.DownloadContent = string.Empty;
+
if (downloading.PlayUrl != null && downloading.DownloadStatus == DownloadStatus.NOT_STARTED)
{
return;
}
+ // 解析
switch (downloading.PlayStreamType)
{
case PlayStreamType.VIDEO:
@@ -192,6 +377,21 @@ namespace DownKyi.Services.Download
}
}
+ ///
+ /// 停止下载服务
+ ///
+ public void End()
+ {
+ // TODO
+ // 保存数据
+
+ // 关闭Aria服务器
+ CloseAriaServer();
+
+ // 结束任务
+ tokenSource.Cancel();
+ }
+
///
/// 启动下载服务
///
@@ -206,58 +406,34 @@ namespace DownKyi.Services.Download
///
/// 执行任务
///
- private async void DoWork()
+ private void DoWork()
{
CancellationToken cancellationToken = tokenSource.Token;
while (true)
{
int maxDownloading = SettingsManager.GetInstance().GetAriaMaxConcurrentDownloads();
- int countDownloading = 0;
- bool isOutOfMaxDownloading = false;
+ int downloadingCount = 0;
foreach (DownloadingItem downloading in downloadingList)
{
- // 对正在下载的元素计数
if (downloading.DownloadStatus == DownloadStatus.DOWNLOADING)
{
- countDownloading++;
+ downloadingCount++;
}
+ }
- // 正在下载数量等于最大下载数量,退出本次循环
- if (countDownloading == maxDownloading)
+ foreach (DownloadingItem downloading in downloadingList)
+ {
+ if (downloadingCount >= maxDownloading)
{
break;
}
- // 正在下载数量大于最大下载数量
- if (countDownloading > maxDownloading)
+ // 开始下载
+ if (downloading.DownloadStatus == DownloadStatus.NOT_STARTED || downloading.DownloadStatus == DownloadStatus.WAIT_FOR_DOWNLOAD)
{
- isOutOfMaxDownloading = true;
+ SingleDownload(downloading);
+ downloadingCount++;
}
-
- // 将超过下载数量的元素暂停
- if (isOutOfMaxDownloading)
- {
- if (downloading.DownloadStatus == DownloadStatus.DOWNLOADING)
- {
- downloading.DownloadStatus = DownloadStatus.PAUSE;
- }
- }
-
- await Task.Run(new Action(() =>
- {
- downloading.DownloadStatus = DownloadStatus.DOWNLOADING;
-
- // 依次下载音频、视频、弹幕、字幕、封面等内容
- Parse(downloading);
- string audioUid = DownloadAudio(downloading);
- string videoUid = DownloadVideo(downloading);
- DownloadDanmaku(downloading);
- DownloadSubtitle(downloading);
- DownloadCover(downloading);
- MixedFlow(downloading, audioUid, videoUid);
- }),
- (tokenSource = new CancellationTokenSource()).Token);
-
}
// 判断是否该结束线程,若为true,跳出while循环
@@ -269,8 +445,165 @@ namespace DownKyi.Services.Download
}
// 降低CPU占用
- Thread.Sleep(500);
+ Thread.Sleep(100);
+ }
+ }
+
+ ///
+ /// 下载一个视频
+ ///
+ ///
+ ///
+ private async void SingleDownload(DownloadingItem downloading)
+ {
+ await Task.Run(new Action(() =>
+ {
+ downloading.DownloadStatus = DownloadStatus.DOWNLOADING;
+
+ // 初始化
+ downloading.DownloadStatusTitle = string.Empty;
+ downloading.DownloadContent = string.Empty;
+ downloading.DownloadFiles.Clear();
+
+ // 解析并依次下载音频、视频、弹幕、字幕、封面等内容
+ Parse(downloading);
+
+ // 暂停
+ Pause(downloading);
+
+ string audioUid = null;
+ // 如果需要下载音频
+ if (downloading.NeedDownloadContent["downloadAudio"])
+ {
+ audioUid = DownloadAudio(downloading);
+ }
+
+ // 暂停
+ Pause(downloading);
+
+ string videoUid = null;
+ // 如果需要下载视频
+ if (downloading.NeedDownloadContent["downloadVideo"])
+ {
+ videoUid = DownloadVideo(downloading);
+ }
+
+ // 暂停
+ Pause(downloading);
+
+ string outputDanmaku = null;
+ // 如果需要下载弹幕
+ if (downloading.NeedDownloadContent["downloadDanmaku"])
+ {
+ outputDanmaku = DownloadDanmaku(downloading);
+ }
+
+ // 暂停
+ Pause(downloading);
+
+ List outputSubtitles = null;
+ // 如果需要下载字幕
+ if (downloading.NeedDownloadContent["downloadSubtitle"])
+ {
+ outputSubtitles = DownloadSubtitle(downloading);
+ }
+
+ // 暂停
+ Pause(downloading);
+
+ string outputCover = null;
+ // 如果需要下载封面
+ if (downloading.NeedDownloadContent["downloadCover"])
+ {
+ outputCover = DownloadCover(downloading);
+ }
+
+ // 暂停
+ Pause(downloading);
+
+ // 混流
+ string outputMedia = MixedFlow(downloading, audioUid, videoUid);
+
+ // 暂停
+ Pause(downloading);
+
+ // 检测音频、视频是否下载成功
+ if (downloading.NeedDownloadContent["downloadAudio"] || downloading.NeedDownloadContent["downloadVideo"])
+ {
+ // 只有下载音频不下载视频时才输出aac
+ // 只要下载视频就输出mp4
+ if (File.Exists(outputMedia))
+ {
+ // 成功
+ }
+ }
+
+ // 检测弹幕是否下载成功
+ if (downloading.NeedDownloadContent["downloadDanmaku"] && File.Exists(outputDanmaku))
+ {
+ // 成功
+ }
+
+ // 检测字幕是否下载成功
+ if (downloading.NeedDownloadContent["downloadSubtitle"])
+ {
+ foreach (string subtitle in outputSubtitles)
+ {
+ if (File.Exists(subtitle))
+ {
+ // 成功
+ }
+ }
+ }
+
+ // 检测封面是否下载成功
+ if (downloading.NeedDownloadContent["downloadCover"] && File.Exists(outputCover))
+ {
+ // 成功
+ }
+
+ // TODO
+ // 将下载结果写入数据库
+ // 包括下载请求的DownloadingItem对象,
+ // 下载结果是否成功等
+ // 对是否成功的判断:只要outputMedia存在则成功,否则失败
+
+ }));
+ }
+
+ ///
+ /// 强制暂停
+ ///
+ ///
+ private void Pause(DownloadingItem downloading)
+ {
+ string oldStatus = downloading.DownloadStatusTitle;
+ downloading.DownloadStatusTitle = DictionaryResource.GetString("Pausing");
+ while (downloading.DownloadStatus == DownloadStatus.PAUSE)
+ {
+ // 降低CPU占用
+ Thread.Sleep(100);
}
+ downloading.DownloadStatusTitle = DictionaryResource.GetString("Waiting");
+
+ int maxDownloading = SettingsManager.GetInstance().GetAriaMaxConcurrentDownloads();
+ int downloadingCount;
+ do
+ {
+ downloadingCount = 0;
+ foreach (DownloadingItem item in downloadingList)
+ {
+ if (item.DownloadStatus == DownloadStatus.DOWNLOADING)
+ {
+ downloadingCount++;
+ }
+ }
+
+ // 降低CPU占用
+ Thread.Sleep(100);
+ } while (downloadingCount > maxDownloading);
+
+ downloading.DownloadStatusTitle = oldStatus;
}
///
@@ -340,11 +673,8 @@ namespace DownKyi.Services.Download
///
///
///
- private DownloadResult DownloadByAria(DownloadingItem downloading, List urls, string localFileName)
+ private DownloadResult DownloadByAria(DownloadingItem downloading, List urls, string path, string localFileName)
{
- string[] temp = downloading.FilePath.Split('/');
- string path = downloading.FilePath.Replace(temp[temp.Length - 1], "");
-
AriaSendOption option = new AriaSendOption
{
//HttpProxy = $"http://{Settings.GetAriaHttpProxy()}:{Settings.GetAriaHttpProxyListenPort()}",
@@ -394,12 +724,39 @@ namespace DownKyi.Services.Download
private void AriaTellStatus(long totalLength, long completedLength, long speed, string gid)
{
- throw new NotImplementedException();
+ // 当前的下载视频
+ DownloadingItem video = downloadingList.FirstOrDefault(it => it.Gid == gid);
+
+ float percent = 0;
+ if (totalLength != 0)
+ {
+ percent = (float)completedLength / totalLength * 100;
+ }
+
+ // 根据进度判断本次是否需要更新UI
+ // TODO 小于多少需要测试
+ if (percent - video.Progress < 0.01) { return; }
+
+ // 下载进度
+ video.Progress = percent;
+
+ // 下载大小
+ video.DownloadingFileSize = Format.FormatFileSize(completedLength) + "/" + Format.FormatFileSize(totalLength);
+
+ // 下载速度
+ video.SpeedDisplay = Format.FormatSpeed(speed);
+
+ // 最大下载速度
+ if (video.MaxSpeed < speed)
+ {
+ video.MaxSpeed = speed;
+ }
+
}
private void AriaDownloadFinish(bool isSuccess, string downloadPath, string gid, string msg)
{
- throw new NotImplementedException();
+ //throw new NotImplementedException();
}
}
}
diff --git a/DownKyi/Services/Download/IDownloadService.cs b/DownKyi/Services/Download/IDownloadService.cs
index a3632b1..8facfb4 100644
--- a/DownKyi/Services/Download/IDownloadService.cs
+++ b/DownKyi/Services/Download/IDownloadService.cs
@@ -1,4 +1,5 @@
using DownKyi.Models;
+using System.Collections.Generic;
namespace DownKyi.Services.Download
{
@@ -7,10 +8,10 @@ namespace DownKyi.Services.Download
void Parse(DownloadingItem downloading);
string DownloadAudio(DownloadingItem downloading);
string DownloadVideo(DownloadingItem downloading);
- void DownloadDanmaku(DownloadingItem downloading);
- void DownloadSubtitle(DownloadingItem downloading);
- void DownloadCover(DownloadingItem downloading);
- void MixedFlow(DownloadingItem downloading, string audioUid, string videoUid);
+ string DownloadDanmaku(DownloadingItem downloading);
+ List DownloadSubtitle(DownloadingItem downloading);
+ string DownloadCover(DownloadingItem downloading);
+ string MixedFlow(DownloadingItem downloading, string audioUid, string videoUid);
void Start();
void End();
diff --git a/DownKyi/Themes/ColorBrush.xaml b/DownKyi/Themes/ColorBrush.xaml
index bc81eda..181e106 100644
--- a/DownKyi/Themes/ColorBrush.xaml
+++ b/DownKyi/Themes/ColorBrush.xaml
@@ -23,6 +23,7 @@
+
@@ -34,6 +35,7 @@
+
diff --git a/DownKyi/Themes/Colors/ColorDefault.xaml b/DownKyi/Themes/Colors/ColorDefault.xaml
index fd949c1..7ce177c 100644
--- a/DownKyi/Themes/Colors/ColorDefault.xaml
+++ b/DownKyi/Themes/Colors/ColorDefault.xaml
@@ -23,6 +23,7 @@
#FFFFFFFF
#FF00A1D6
#FFBDBDBD
+ #FFE4E4E4
#FFBDBDBD
#C8BDBDBD
#7FBDBDBD
@@ -34,6 +35,7 @@
#FFF4F4F4
#FF999999
+ #7F999999
#7FD0D0D0
#7FD0D0D0
diff --git a/DownKyi/Utils/DictionaryResource.cs b/DownKyi/Utils/DictionaryResource.cs
index 93290d8..71147da 100644
--- a/DownKyi/Utils/DictionaryResource.cs
+++ b/DownKyi/Utils/DictionaryResource.cs
@@ -26,7 +26,7 @@ namespace DownKyi.Utils
///
public static string GetString(string resourceKey)
{
- return (string)Application.Current.Resources[resourceKey];
+ return Application.Current == null ? "" : (string)Application.Current.Resources[resourceKey];
}
///
diff --git a/DownKyi/ViewModels/DownloadManager/ViewDownloadingViewModel.cs b/DownKyi/ViewModels/DownloadManager/ViewDownloadingViewModel.cs
index d46ce89..7972fe8 100644
--- a/DownKyi/ViewModels/DownloadManager/ViewDownloadingViewModel.cs
+++ b/DownKyi/ViewModels/DownloadManager/ViewDownloadingViewModel.cs
@@ -5,6 +5,7 @@ using Prism.Mvvm;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
+using System.Collections.Specialized;
using System.Linq;
namespace DownKyi.ViewModels.DownloadManager
@@ -18,15 +19,63 @@ namespace DownKyi.ViewModels.DownloadManager
private ObservableCollection downloadingList;
public ObservableCollection DownloadingList
{
- get { return downloadingList; }
- set { SetProperty(ref downloadingList, value); }
+ get => downloadingList;
+ set => SetProperty(ref downloadingList, value);
}
#endregion
public ViewDownloadingViewModel(IEventAggregator eventAggregator) : base(eventAggregator)
{
+ // 初始化DownloadingList
DownloadingList = App.DownloadingList;
+
+
+ //// 下载列表发生变化时执行的任务
+ //DownloadingList.CollectionChanged += new NotifyCollectionChangedEventHandler((object sender, NotifyCollectionChangedEventArgs e) =>
+ //{
+ // // save the downloading list and finished list.
+ // //SaveHistory();
+ //});
+
+ }
+
+ #region 命令申明
+
+ // 暂停所有下载事件
+ private DelegateCommand pauseAllDownloadingCommand;
+ public DelegateCommand PauseAllDownloadingCommand => pauseAllDownloadingCommand ?? (pauseAllDownloadingCommand = new DelegateCommand(ExecutePauseAllDownloadingCommand));
+
+ ///
+ /// 暂停所有下载事件
+ ///
+ private void ExecutePauseAllDownloadingCommand()
+ {
}
+
+ // 继续所有下载事件
+ private DelegateCommand continueAllDownloadingCommand;
+ public DelegateCommand ContinueAllDownloadingCommand => continueAllDownloadingCommand ?? (continueAllDownloadingCommand = new DelegateCommand(ExecuteContinueAllDownloadingCommand));
+
+ ///
+ /// 继续所有下载事件
+ ///
+ private void ExecuteContinueAllDownloadingCommand()
+ {
+ }
+
+ // 删除所有下载事件
+ private DelegateCommand deleteAllDownloadingCommand;
+ public DelegateCommand DeleteAllDownloadingCommand => deleteAllDownloadingCommand ?? (deleteAllDownloadingCommand = new DelegateCommand(ExecuteDeleteAllDownloadingCommand));
+
+ ///
+ /// 删除所有下载事件
+ ///
+ private void ExecuteDeleteAllDownloadingCommand()
+ {
+ }
+
+ #endregion
+
}
}
diff --git a/DownKyi/Views/DownloadManager/ViewDownloading.xaml b/DownKyi/Views/DownloadManager/ViewDownloading.xaml
index eeb1677..4313e88 100644
--- a/DownKyi/Views/DownloadManager/ViewDownloading.xaml
+++ b/DownKyi/Views/DownloadManager/ViewDownloading.xaml
@@ -2,9 +2,309 @@
x:Class="DownKyi.Views.DownloadManager.ViewDownloading"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+ xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
xmlns:prism="http://prismlibrary.com/"
prism:ViewModelLocator.AutoWireViewModel="True">
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
- 正在下载
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+