diff --git a/images/app/index.png b/images/app/index.png
index 6a71637..a950b9a 100644
Binary files a/images/app/index.png and b/images/app/index.png differ
diff --git a/src/DownKyi.Core/Aria2cNet/AriaManager.cs b/src/DownKyi.Core/Aria2cNet/AriaManager.cs
index 59fd6e3..c40e2cc 100644
--- a/src/DownKyi.Core/Aria2cNet/AriaManager.cs
+++ b/src/DownKyi.Core/Aria2cNet/AriaManager.cs
@@ -1,5 +1,6 @@
using DownKyi.Core.Aria2cNet.Client;
using DownKyi.Core.Logging;
+using System;
using System.Threading;
namespace DownKyi.Core.Aria2cNet
@@ -32,10 +33,14 @@ namespace DownKyi.Core.Aria2cNet
///
/// 获取gid下载项的状态
+ ///
+ /// TODO
+ /// 对于下载的不同状态的返回值的测试
///
///
+ ///
///
- public DownloadStatus GetDownloadStatus(string gid)
+ public DownloadResult GetDownloadStatus(string gid, Action action = null)
{
string filePath = "";
while (true)
@@ -48,7 +53,7 @@ namespace DownKyi.Core.Aria2cNet
if (status.Result.Error.Message.Contains("is not found"))
{
OnDownloadFinish(false, null, gid, status.Result.Error.Message);
- return DownloadStatus.ABORT;
+ return DownloadResult.ABORT;
}
}
@@ -60,9 +65,16 @@ namespace DownKyi.Core.Aria2cNet
long totalLength = long.Parse(status.Result.Result.TotalLength);
long completedLength = long.Parse(status.Result.Result.CompletedLength);
long speed = long.Parse(status.Result.Result.DownloadSpeed);
+
// 回调
OnTellStatus(totalLength, completedLength, speed, gid);
+ // 在外部执行
+ if (action != null)
+ {
+ action.Invoke();
+ }
+
if (status.Result.Result.Status == "complete")
{
break;
@@ -86,14 +98,14 @@ namespace DownKyi.Core.Aria2cNet
// 返回回调信息,退出函数
OnDownloadFinish(false, null, gid, status.Result.Result.ErrorMessage);
- return DownloadStatus.FAILED;
+ return DownloadResult.FAILED;
}
// 降低CPU占用
Thread.Sleep(100);
}
OnDownloadFinish(true, filePath, gid, null);
- return DownloadStatus.SUCCESS;
+ return DownloadResult.SUCCESS;
}
///
diff --git a/src/DownKyi.Core/Aria2cNet/DownloadResult.cs b/src/DownKyi.Core/Aria2cNet/DownloadResult.cs
new file mode 100644
index 0000000..17f6ab2
--- /dev/null
+++ b/src/DownKyi.Core/Aria2cNet/DownloadResult.cs
@@ -0,0 +1,12 @@
+namespace DownKyi.Core.Aria2cNet
+{
+ ///
+ /// 下载状态
+ ///
+ public enum DownloadResult
+ {
+ SUCCESS = 1,
+ FAILED,
+ ABORT
+ }
+}
diff --git a/src/DownKyi.Core/BiliApi/Bangumi/BangumiType.cs b/src/DownKyi.Core/BiliApi/Bangumi/BangumiType.cs
index fae5abc..16558bf 100644
--- a/src/DownKyi.Core/BiliApi/Bangumi/BangumiType.cs
+++ b/src/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/src/DownKyi.Core/BiliApi/BiliUtils/Constant.cs b/src/DownKyi.Core/BiliApi/BiliUtils/Constant.cs
index 7b0ce8a..157c706 100644
--- a/src/DownKyi.Core/BiliApi/BiliUtils/Constant.cs
+++ b/src/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/src/DownKyi.Core/BiliApi/Models/Json/SubRipText.cs b/src/DownKyi.Core/BiliApi/Models/Json/SubRipText.cs
new file mode 100644
index 0000000..bfa9928
--- /dev/null
+++ b/src/DownKyi.Core/BiliApi/Models/Json/SubRipText.cs
@@ -0,0 +1,14 @@
+using Newtonsoft.Json;
+
+namespace DownKyi.Core.BiliApi.Models.Json
+{
+ public class SubRipText : BaseModel
+ {
+ [JsonProperty("lan")]
+ public string Lan { get; set; }
+ [JsonProperty("lan_doc")]
+ public string LanDoc { get; set; }
+ [JsonProperty("srtString")]
+ public string SrtString { get; set; }
+ }
+}
diff --git a/src/DownKyi.Core/BiliApi/Models/Json/Subtitle.cs b/src/DownKyi.Core/BiliApi/Models/Json/Subtitle.cs
new file mode 100644
index 0000000..80f9c94
--- /dev/null
+++ b/src/DownKyi.Core/BiliApi/Models/Json/Subtitle.cs
@@ -0,0 +1,16 @@
+using Newtonsoft.Json;
+
+namespace DownKyi.Core.BiliApi.Models.Json
+{
+ public class Subtitle : BaseModel
+ {
+ [JsonProperty("from")]
+ public float From { get; set; }
+ [JsonProperty("to")]
+ public float To { get; set; }
+ [JsonProperty("location")]
+ public int Location { get; set; }
+ [JsonProperty("content")]
+ public string Content { get; set; }
+ }
+}
diff --git a/src/DownKyi.Core/BiliApi/Models/Json/SubtitleJson.cs b/src/DownKyi.Core/BiliApi/Models/Json/SubtitleJson.cs
new file mode 100644
index 0000000..6437610
--- /dev/null
+++ b/src/DownKyi.Core/BiliApi/Models/Json/SubtitleJson.cs
@@ -0,0 +1,69 @@
+using Newtonsoft.Json;
+using System;
+using System.Collections.Generic;
+
+namespace DownKyi.Core.BiliApi.Models.Json
+{
+ public class SubtitleJson : BaseModel
+ {
+ [JsonProperty("font_size")]
+ public float FontSize { get; set; }
+ [JsonProperty("font_color")]
+ public string FontColor { get; set; }
+ [JsonProperty("background_alpha")]
+ public float BackgroundAlpha { get; set; }
+ [JsonProperty("background_color")]
+ public string BackgroundColor { get; set; }
+ [JsonProperty("Stroke")]
+ public string Stroke { get; set; }
+ [JsonProperty("body")]
+ public List Body { get; set; }
+
+ ///
+ /// srt格式字幕
+ ///
+ ///
+ public string ToSubRip()
+ {
+ string subRip = string.Empty;
+ for (int i = 0; i < Body.Count; i++)
+ {
+ subRip += $"{i + 1}\n";
+ subRip += $"{Second2hms(Body[i].From)} --> {Second2hms(Body[i].To)}\n";
+ subRip += $"{Body[i].Content}\n";
+ subRip += "\n";
+ }
+
+ return subRip;
+ }
+
+ ///
+ /// 秒数转 时:分:秒 格式
+ ///
+ ///
+ ///
+ private static string Second2hms(float seconds)
+ {
+ if (seconds < 0)
+ {
+ return "00:00:00,000";
+ }
+
+ int i = (int)Math.Floor(seconds / 1.0);
+ int dec = (int)(Math.Round(seconds % 1.0f, 2) * 100);
+ if (dec >= 100)
+ {
+ dec = 99;
+ }
+
+ int min = (int)Math.Floor(i / 60.0);
+ int second = (int)(i % 60.0f);
+
+ int hour = (int)Math.Floor(min / 60.0);
+ min = (int)Math.Floor(min % 60.0f);
+
+ return $"{hour:D2}:{min:D2}:{second:D2},{dec:D3}";
+ }
+
+ }
+}
diff --git a/src/DownKyi.Core/BiliApi/Video/Models/VideoPage.cs b/src/DownKyi.Core/BiliApi/Video/Models/VideoPage.cs
index bcde7ba..c640552 100644
--- a/src/DownKyi.Core/BiliApi/Video/Models/VideoPage.cs
+++ b/src/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/src/DownKyi.Core/BiliApi/VideoStream/Models/PlayerV2.cs b/src/DownKyi.Core/BiliApi/VideoStream/Models/PlayerV2.cs
new file mode 100644
index 0000000..9ed962d
--- /dev/null
+++ b/src/DownKyi.Core/BiliApi/VideoStream/Models/PlayerV2.cs
@@ -0,0 +1,34 @@
+using DownKyi.Core.BiliApi.Models;
+using Newtonsoft.Json;
+
+namespace DownKyi.Core.BiliApi.VideoStream.Models
+{
+ // https://api.bilibili.com/x/player/v2?cid={cid}&aid={avid}&bvid={bvid}
+ public class PlayerV2Origin : BaseModel
+ {
+ //[JsonProperty("code")]
+ //public int Code { get; set; }
+ //[JsonProperty("message")]
+ //public string Message { get; set; }
+ //[JsonProperty("ttl")]
+ //public int Ttl { get; set; }
+ [JsonProperty("data")]
+ public PlayerV2 Data { get; set; }
+ }
+
+ public class PlayerV2 : BaseModel
+ {
+ [JsonProperty("aid")]
+ public long Aid { get; set; }
+ [JsonProperty("bvid")]
+ public string Bvid { get; set; }
+ // allow_bp
+ // no_share
+ [JsonProperty("cid")]
+ public long Cid { get; set; }
+ // ...
+ [JsonProperty("subtitle")]
+ public SubtitleInfo Subtitle { get; set; }
+ }
+
+}
diff --git a/src/DownKyi.Core/BiliApi/VideoStream/Models/Subtitle.cs b/src/DownKyi.Core/BiliApi/VideoStream/Models/Subtitle.cs
new file mode 100644
index 0000000..a0861b1
--- /dev/null
+++ b/src/DownKyi.Core/BiliApi/VideoStream/Models/Subtitle.cs
@@ -0,0 +1,25 @@
+using DownKyi.Core.BiliApi.Models;
+using Newtonsoft.Json;
+
+namespace DownKyi.Core.BiliApi.VideoStream.Models
+{
+ public class Subtitle : BaseModel
+ {
+ [JsonProperty("id")]
+ public long Id { get; set; }
+ [JsonProperty("lan")]
+ public string Lan { get; set; }
+ [JsonProperty("lan_doc")]
+ public string LanDoc { get; set; }
+ [JsonProperty("is_lock")]
+ public bool IsLock { get; set; }
+ [JsonProperty("author_mid")]
+ public long AuthorMid { get; set; }
+ [JsonProperty("subtitle_url")]
+ public string SubtitleUrl { get; set; }
+ [JsonProperty("type")]
+ public int Type { get; set; }
+ [JsonProperty("id_str")]
+ public string IdStr { get; set; }
+ }
+}
diff --git a/src/DownKyi.Core/BiliApi/VideoStream/Models/SubtitleInfo.cs b/src/DownKyi.Core/BiliApi/VideoStream/Models/SubtitleInfo.cs
new file mode 100644
index 0000000..27b3078
--- /dev/null
+++ b/src/DownKyi.Core/BiliApi/VideoStream/Models/SubtitleInfo.cs
@@ -0,0 +1,18 @@
+using DownKyi.Core.BiliApi.Models;
+using Newtonsoft.Json;
+using System.Collections.Generic;
+
+namespace DownKyi.Core.BiliApi.VideoStream.Models
+{
+ public class SubtitleInfo : BaseModel
+ {
+ [JsonProperty("allow_submit")]
+ public bool AllowSubmit { get; set; }
+ [JsonProperty("lan")]
+ public string Lan { get; set; }
+ [JsonProperty("lan_doc")]
+ public string LanDoc { get; set; }
+ [JsonProperty("subtitles")]
+ public List Subtitles { get; set; }
+ }
+}
diff --git a/src/DownKyi.Core/BiliApi/VideoStream/VideoStream.cs b/src/DownKyi.Core/BiliApi/VideoStream/VideoStream.cs
index b41e84d..8490aea 100644
--- a/src/DownKyi.Core/BiliApi/VideoStream/VideoStream.cs
+++ b/src/DownKyi.Core/BiliApi/VideoStream/VideoStream.cs
@@ -1,13 +1,88 @@
-using DownKyi.Core.BiliApi.VideoStream.Models;
+using DownKyi.Core.BiliApi.Models.Json;
+using DownKyi.Core.BiliApi.VideoStream.Models;
using DownKyi.Core.Logging;
using Newtonsoft.Json;
using System;
+using System.Collections.Generic;
namespace DownKyi.Core.BiliApi.VideoStream
{
public static class VideoStream
{
+ ///
+ /// 获取播放器信息(web端)
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static PlayerV2 PlayerV2(long avid, string bvid, long cid)
+ {
+ string url = $"https://api.bilibili.com/x/player/v2?cid={cid}&aid={avid}&bvid={bvid}";
+ string referer = "https://www.bilibili.com";
+ string response = WebClient.RequestWeb(url, referer);
+
+ try
+ {
+ var playUrl = JsonConvert.DeserializeObject(response);
+ return playUrl?.Data;
+ }
+ catch (Exception e)
+ {
+ Utils.Debugging.Console.PrintLine("PlayerV2()发生异常: {0}", e);
+ LogManager.Error("PlayerV2()", e);
+ return null;
+ }
+ }
+
+ ///
+ /// 获取所有字幕
+ /// 若视频没有字幕,返回null
+ ///
+ ///
+ ///
+ ///
+ ///
+ public static List GetSubtitle(long avid, string bvid, long cid)
+ {
+ List subRipTexts = new List();
+
+ // 获取播放器信息
+ 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)
+ {
+ string referer = "https://www.bilibili.com";
+ string response = WebClient.RequestWeb($"https:{subtitle.SubtitleUrl}", referer);
+
+ try
+ {
+ var subtitleJson = JsonConvert.DeserializeObject(response);
+ if (subtitleJson == null) { continue; }
+
+ subRipTexts.Add(new SubRipText
+ {
+ Lan = subtitle.Lan,
+ LanDoc = subtitle.LanDoc,
+ SrtString = subtitleJson.ToSubRip()
+ });
+ }
+ catch (Exception e)
+ {
+ Utils.Debugging.Console.PrintLine("GetSubtitle()发生异常: {0}", e);
+ LogManager.Error("GetSubtitle()", e);
+ }
+ }
+
+ return subRipTexts;
+ }
+
///
/// 获取普通视频的视频流
///
@@ -92,7 +167,7 @@ namespace DownKyi.Core.BiliApi.VideoStream
catch (Exception e)
{
Utils.Debugging.Console.PrintLine("GetPlayUrl()发生异常: {0}", e);
- LogManager.Error("GetPlayUrl", e);
+ LogManager.Error("GetPlayUrl()", e);
return null;
}
}
diff --git a/src/DownKyi.Core/BiliApi/Zone/VideoZone.cs b/src/DownKyi.Core/BiliApi/Zone/VideoZone.cs
index 741a918..038a120 100644
--- a/src/DownKyi.Core/BiliApi/Zone/VideoZone.cs
+++ b/src/DownKyi.Core/BiliApi/Zone/VideoZone.cs
@@ -2,7 +2,6 @@
namespace DownKyi.Core.BiliApi.Zone
{
-
public class VideoZone
{
private static VideoZone that;
@@ -21,7 +20,7 @@ namespace DownKyi.Core.BiliApi.Zone
return that;
}
- public List GetZone()
+ public List GetZones()
{
return zones;
}
@@ -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/src/DownKyi.Core/BiliApi/Zone/VideoZoneIcon.cs b/src/DownKyi.Core/BiliApi/Zone/VideoZoneIcon.cs
new file mode 100644
index 0000000..d55ab22
--- /dev/null
+++ b/src/DownKyi.Core/BiliApi/Zone/VideoZoneIcon.cs
@@ -0,0 +1,88 @@
+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 -10:
+ return "Zone.cheeseDrawingImage";
+ 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/src/DownKyi.Core/BiliApi/Zone/ZoneImage.xaml b/src/DownKyi.Core/BiliApi/Zone/ZoneImage.xaml
new file mode 100644
index 0000000..28a925d
--- /dev/null
+++ b/src/DownKyi.Core/BiliApi/Zone/ZoneImage.xaml
@@ -0,0 +1,308 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/DownKyi.Core/Danmaku2Ass/Studio.cs b/src/DownKyi.Core/Danmaku2Ass/Studio.cs
index 97acc48..630e323 100644
--- a/src/DownKyi.Core/Danmaku2Ass/Studio.cs
+++ b/src/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/src/DownKyi.Core/Danmaku2Ass/Utils.cs b/src/DownKyi.Core/Danmaku2Ass/Utils.cs
index 2d874cc..d54e48d 100644
--- a/src/DownKyi.Core/Danmaku2Ass/Utils.cs
+++ b/src/DownKyi.Core/Danmaku2Ass/Utils.cs
@@ -62,6 +62,7 @@ namespace DownKyi.Core.Danmaku2Ass
int second = (int)(i % 60.0f);
int hour = (int)Math.Floor(min / 60.0);
+ min = (int)Math.Floor(min % 60.0f);
return $"{hour:D}:{min:D2}:{second:D2}.{dec:D2}";
}
diff --git a/src/DownKyi.Core/DownKyi.Core.csproj b/src/DownKyi.Core/DownKyi.Core.csproj
index 9ca3132..131523f 100644
--- a/src/DownKyi.Core/DownKyi.Core.csproj
+++ b/src/DownKyi.Core/DownKyi.Core.csproj
@@ -111,7 +111,7 @@
-
+
@@ -160,13 +160,19 @@
+
+
+
+
+
+
@@ -188,6 +194,7 @@
+
@@ -203,6 +210,9 @@
+
+
+
@@ -255,7 +265,12 @@
-
+
+
+ MSBuild:Compile
+ Designer
+
+
diff --git a/src/DownKyi.Core/FFmpeg/FFmpegHelper.cs b/src/DownKyi.Core/FFmpeg/FFmpegHelper.cs
index b77bf99..e94b204 100644
--- a/src/DownKyi.Core/FFmpeg/FFmpegHelper.cs
+++ b/src/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/src/DownKyi.Core/FileName/FileName.cs b/src/DownKyi.Core/FileName/FileName.cs
new file mode 100644
index 0000000..23d9c1d
--- /dev/null
+++ b/src/DownKyi.Core/FileName/FileName.cs
@@ -0,0 +1,130 @@
+using System.Collections.Generic;
+using System.Text.RegularExpressions;
+
+namespace DownKyi.Core.FileName
+{
+ public class FileName
+ {
+ private readonly List nameParts;
+ private int order = -1;
+ private string section = "SECTION";
+ private string mainTitle = "MAIN_TITLE";
+ private string pageTitle = "PAGE_TITLE";
+ private string videoZone = "VIDEO_ZONE";
+ private string audioQuality = "AUDIO_QUALITY";
+ private string videoQuality = "VIDEO_QUALITY";
+ private string videoCodec = "VIDEO_CODEC";
+
+ private FileName(List nameParts)
+ {
+ this.nameParts = nameParts;
+ }
+
+ public static FileName Builder(List nameParts)
+ {
+ return new FileName(nameParts);
+ }
+
+ public FileName SetOrder(int order)
+ {
+ this.order = order;
+ return this;
+ }
+
+ public FileName SetSection(string section)
+ {
+ this.section = section;
+ return this;
+ }
+
+ public FileName SetMainTitle(string mainTitle)
+ {
+ this.mainTitle = mainTitle;
+ return this;
+ }
+
+ public FileName SetPageTitle(string pageTitle)
+ {
+ this.pageTitle = pageTitle;
+ return this;
+ }
+
+ public FileName SetVideoZone(string videoZone)
+ {
+ this.videoZone = videoZone;
+ return this;
+ }
+
+ public FileName SetAudioQuality(string audioQuality)
+ {
+ this.audioQuality = audioQuality;
+ return this;
+ }
+
+ public FileName SetVideoQuality(string videoQuality)
+ {
+ this.videoQuality = videoQuality;
+ return this;
+ }
+
+ public FileName SetVideoCodec(string videoCodec)
+ {
+ this.videoCodec = videoCodec;
+ return this;
+ }
+
+ public string RelativePath()
+ {
+ string path = string.Empty;
+
+ foreach (FileNamePart part in nameParts)
+ {
+ switch (part)
+ {
+ case FileNamePart.ORDER:
+ if (order != -1)
+ {
+ path += order;
+ }
+ else
+ {
+ path += "ORDER";
+ }
+ break;
+ case FileNamePart.SECTION:
+ path += section;
+ break;
+ case FileNamePart.MAIN_TITLE:
+ path += mainTitle;
+ break;
+ case FileNamePart.PAGE_TITLE:
+ path += pageTitle;
+ break;
+ case FileNamePart.VIDEO_ZONE:
+ path += videoZone;
+ break;
+ case FileNamePart.AUDIO_QUALITY:
+ path += audioQuality;
+ break;
+ case FileNamePart.VIDEO_QUALITY:
+ path += videoQuality;
+ break;
+ case FileNamePart.VIDEO_CODEC:
+ path += videoCodec;
+ break;
+ }
+
+ if (((int)part) >= 100)
+ {
+ path += HyphenSeparated.Hyphen[(int)part];
+ }
+ }
+
+ // 避免连续多个斜杠
+ path = Regex.Replace(path, @"//+", "/");
+ // 避免以斜杠开头和结尾的情况
+ return path.TrimEnd('/').TrimStart('/');
+ }
+
+ }
+}
diff --git a/src/DownKyi.Core/FileName/FileNamePart.cs b/src/DownKyi.Core/FileName/FileNamePart.cs
new file mode 100644
index 0000000..551829c
--- /dev/null
+++ b/src/DownKyi.Core/FileName/FileNamePart.cs
@@ -0,0 +1,35 @@
+namespace DownKyi.Core.FileName
+{
+ public enum FileNamePart
+ {
+ // Video
+ ORDER = 1,
+ SECTION,
+ MAIN_TITLE,
+ PAGE_TITLE,
+ VIDEO_ZONE,
+ AUDIO_QUALITY,
+ VIDEO_QUALITY,
+ VIDEO_CODEC,
+
+ // 斜杠
+ SLASH = 100,
+
+ // HyphenSeparated
+ UNDERSCORE = 101, // 下划线
+ HYPHEN, // 连字符
+ PLUS, // 加号
+ COMMA, // 逗号
+ PERIOD, // 句号
+ AND, // and
+ NUMBER, // #
+ OPEN_PAREN, // 左圆括号
+ CLOSE_PAREN, // 右圆括号
+ OPEN_BRACKET, // 左方括号
+ CLOSE_BRACKET, // 右方括号
+ OPEN_BRACE, // 左花括号
+ CLOSE_brace, // 右花括号
+ BLANK, // 空白符
+
+ }
+}
diff --git a/src/DownKyi.Core/FileName/HyphenSeparated.cs b/src/DownKyi.Core/FileName/HyphenSeparated.cs
new file mode 100644
index 0000000..0bb3083
--- /dev/null
+++ b/src/DownKyi.Core/FileName/HyphenSeparated.cs
@@ -0,0 +1,31 @@
+using System.Collections.Generic;
+
+namespace DownKyi.Core.FileName
+{
+ ///
+ /// 文件名字段
+ ///
+ public static class HyphenSeparated
+ {
+ // 文件名的分隔符
+ public static Dictionary Hyphen = new Dictionary()
+ {
+ { 100, "/" },
+ { 101, "_" },
+ { 102, "-" },
+ { 103, "+" },
+ { 104, "," },
+ { 105, "." },
+ { 106, "&" },
+ { 107, "#" },
+ { 108, "(" },
+ { 109, ")" },
+ { 110, "[" },
+ { 111, "]" },
+ { 112, "{" },
+ { 113, "}" },
+ { 114, " " },
+ };
+
+ }
+}
diff --git a/src/DownKyi.Core/Settings/Models/VideoSettings.cs b/src/DownKyi.Core/Settings/Models/VideoSettings.cs
index 0863423..d5fe834 100644
--- a/src/DownKyi.Core/Settings/Models/VideoSettings.cs
+++ b/src/DownKyi.Core/Settings/Models/VideoSettings.cs
@@ -1,4 +1,5 @@
-using System.Collections.Generic;
+using DownKyi.Core.FileName;
+using System.Collections.Generic;
namespace DownKyi.Core.Settings.Models
{
@@ -7,16 +8,13 @@ namespace DownKyi.Core.Settings.Models
///
public class VideoSettings
{
- public VideoCodecs VideoCodecs { get; set; }
- public int Quality { get; set; }
- public int AudioQuality { get; set; }
- public AllowStatus IsAddOrder { get; set; }
- public AllowStatus IsTranscodingFlvToMp4 { get; set; }
- public string SaveVideoRootPath { get; set; }
- public List HistoryVideoRootPaths { get; set; }
- public AllowStatus IsUseSaveVideoRootPath { get; set; }
- public AllowStatus IsCreateFolderForMedia { get; set; }
- public AllowStatus IsDownloadDanmaku { get; set; }
- public AllowStatus IsDownloadCover { get; set; }
+ public VideoCodecs VideoCodecs { get; set; } // AVC or HEVC
+ public int Quality { get; set; } // 画质
+ public int AudioQuality { get; set; } // 音质
+ public AllowStatus IsTranscodingFlvToMp4 { get; set; } // 是否将flv转为mp4
+ public string SaveVideoRootPath { get; set; } // 视频保存路径
+ public List HistoryVideoRootPaths { get; set; } // 历史视频保存路径
+ public AllowStatus IsUseSaveVideoRootPath { get; set; } // 是否使用默认视频保存路径
+ public List FileNameParts { get; set; } // 文件命名格式
}
}
diff --git a/src/DownKyi.Core/Settings/SettingsManager.Video.cs b/src/DownKyi.Core/Settings/SettingsManager.Video.cs
index 1b6700c..aa50f59 100644
--- a/src/DownKyi.Core/Settings/SettingsManager.Video.cs
+++ b/src/DownKyi.Core/Settings/SettingsManager.Video.cs
@@ -1,4 +1,5 @@
-using System;
+using DownKyi.Core.FileName;
+using System;
using System.Collections.Generic;
using System.IO;
@@ -15,9 +16,6 @@ namespace DownKyi.Core.Settings
// 设置优先下载音质
private readonly int audioQuality = 30280;
- // 是否在下载的视频前增加序号
- private readonly AllowStatus isAddOrder = AllowStatus.NO;
-
// 是否下载flv视频后转码为mp4
private readonly AllowStatus isTranscodingFlvToMp4 = AllowStatus.YES;
@@ -30,14 +28,21 @@ namespace DownKyi.Core.Settings
// 是否使用默认下载目录,如果是,则每次点击下载选中项时不再询问下载目录
private readonly AllowStatus isUseSaveVideoRootPath = AllowStatus.NO;
- // 是否为不同视频分别创建文件夹
- private readonly AllowStatus isCreateFolderForMedia = AllowStatus.YES;
-
- // 是否在下载视频的同时下载弹幕
- private readonly AllowStatus isDownloadDanmaku = AllowStatus.YES;
-
- // 是否在下载视频的同时下载封面
- private readonly AllowStatus isDownloadCover = AllowStatus.YES;
+ // 文件命名格式
+ private readonly List fileNameParts = new List
+ {
+ FileNamePart.MAIN_TITLE,
+ FileNamePart.SLASH,
+ FileNamePart.SECTION,
+ FileNamePart.SLASH,
+ FileNamePart.ORDER,
+ FileNamePart.HYPHEN,
+ FileNamePart.PAGE_TITLE,
+ FileNamePart.HYPHEN,
+ FileNamePart.VIDEO_QUALITY,
+ FileNamePart.HYPHEN,
+ FileNamePart.VIDEO_CODEC,
+ };
///
/// 获取优先下载的视频编码
@@ -120,33 +125,6 @@ namespace DownKyi.Core.Settings
return SetSettings();
}
- ///
- /// 获取是否给视频增加序号
- ///
- ///
- public AllowStatus IsAddOrder()
- {
- appSettings = GetSettings();
- if (appSettings.Video.IsAddOrder == 0)
- {
- // 第一次获取,先设置默认值
- IsAddOrder(isAddOrder);
- return isAddOrder;
- }
- return appSettings.Video.IsAddOrder;
- }
-
- ///
- /// 设置是否给视频增加序号
- ///
- ///
- ///
- public bool IsAddOrder(AllowStatus isAddOrder)
- {
- appSettings.Video.IsAddOrder = isAddOrder;
- return SetSettings();
- }
-
///
/// 获取是否下载flv视频后转码为mp4
///
@@ -256,83 +234,29 @@ namespace DownKyi.Core.Settings
}
///
- /// 获取是否为不同视频分别创建文件夹
- ///
- ///
- public AllowStatus IsCreateFolderForMedia()
- {
- appSettings = GetSettings();
- if (appSettings.Video.IsCreateFolderForMedia == 0)
- {
- // 第一次获取,先设置默认值
- IsCreateFolderForMedia(isCreateFolderForMedia);
- return isCreateFolderForMedia;
- }
- return appSettings.Video.IsCreateFolderForMedia;
- }
-
- ///
- /// 设置是否为不同视频分别创建文件夹
- ///
- ///
- ///
- public bool IsCreateFolderForMedia(AllowStatus isCreateFolderForMedia)
- {
- appSettings.Video.IsCreateFolderForMedia = isCreateFolderForMedia;
- return SetSettings();
- }
-
- ///
- /// 获取是否在下载视频的同时下载弹幕
- ///
- ///
- public AllowStatus IsDownloadDanmaku()
- {
- appSettings = GetSettings();
- if (appSettings.Video.IsDownloadDanmaku == 0)
- {
- // 第一次获取,先设置默认值
- IsDownloadDanmaku(isDownloadDanmaku);
- return isDownloadDanmaku;
- }
- return appSettings.Video.IsDownloadDanmaku;
- }
-
- ///
- /// 设置是否在下载视频的同时下载弹幕
- ///
- ///
- ///
- public bool IsDownloadDanmaku(AllowStatus isDownloadDanmaku)
- {
- appSettings.Video.IsDownloadDanmaku = isDownloadDanmaku;
- return SetSettings();
- }
-
- ///
- /// 获取是否在下载视频的同时下载封面
+ /// 获取文件命名格式
///
///
- public AllowStatus IsDownloadCover()
+ public List GetFileNameParts()
{
appSettings = GetSettings();
- if (appSettings.Video.IsDownloadCover == 0)
+ if (appSettings.Video.FileNameParts == null || appSettings.Video.FileNameParts.Count == 0)
{
// 第一次获取,先设置默认值
- IsDownloadCover(isDownloadCover);
- return isDownloadCover;
+ SetFileNameParts(fileNameParts);
+ return fileNameParts;
}
- return appSettings.Video.IsDownloadCover;
+ return appSettings.Video.FileNameParts;
}
///
- /// 设置是否在下载视频的同时下载封面
+ /// 设置文件命名格式
///
- ///
+ ///
///
- public bool IsDownloadCover(AllowStatus isDownloadCover)
+ public bool SetFileNameParts(List fileNameParts)
{
- appSettings.Video.IsDownloadCover = isDownloadCover;
+ appSettings.Video.FileNameParts = fileNameParts;
return SetSettings();
}
diff --git a/src/DownKyi.Core/Utils/Format.cs b/src/DownKyi.Core/Utils/Format.cs
index d8f08c8..c3a611c 100644
--- a/src/DownKyi.Core/Utils/Format.cs
+++ b/src/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
{
@@ -120,15 +122,15 @@
}
else if (speed < 1024)
{
- formatSpeed = speed.ToString("#.##") + "B/s";
+ formatSpeed = string.Format("{0:F2}", speed) + "B/s";
}
else if (speed < 1024 * 1024)
{
- formatSpeed = (speed / 1024).ToString("#.##") + "KB/s";
+ formatSpeed = string.Format("{0:F2}", speed / 1024) + "KB/s";
}
else
{
- formatSpeed = (speed / 1024 / 1024).ToString("#.##") + "MB/s";
+ formatSpeed = string.Format("{0:F2}", speed / 1024 / 1024) + "MB/s";
}
return formatSpeed;
}
@@ -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/src/DownKyi/App.xaml b/src/DownKyi/App.xaml
index 34ef81c..7835032 100644
--- a/src/DownKyi/App.xaml
+++ b/src/DownKyi/App.xaml
@@ -9,6 +9,7 @@
+
diff --git a/src/DownKyi/App.xaml.cs b/src/DownKyi/App.xaml.cs
index bfad80b..d9b8f33 100644
--- a/src/DownKyi/App.xaml.cs
+++ b/src/DownKyi/App.xaml.cs
@@ -1,4 +1,6 @@
-using DownKyi.Utils;
+using DownKyi.Models;
+using DownKyi.Services.Download;
+using DownKyi.Utils;
using DownKyi.ViewModels;
using DownKyi.ViewModels.Dialogs;
using DownKyi.ViewModels.DownloadManager;
@@ -11,6 +13,7 @@ using DownKyi.Views.Settings;
using DownKyi.Views.Toolbox;
using Prism.Ioc;
using System;
+using System.Collections.ObjectModel;
using System.Windows;
namespace DownKyi
@@ -20,6 +23,12 @@ namespace DownKyi
///
public partial class App
{
+ public static ObservableCollection DownloadingList { get; set; }
+ public static ObservableCollection DownloadedList { get; set; }
+
+ // 下载服务
+ private IDownloadService downloadService;
+
protected override Window CreateShell()
{
// 设置主题
@@ -30,9 +39,27 @@ namespace DownKyi
DictionaryResource.LoadLanguage("Default");
//DictionaryResource.LoadLanguage("en_US");
+ // 初始化数据
+ DownloadingList = new ObservableCollection();
+ DownloadedList = new ObservableCollection();
+
+ // TODO 从数据库读取
+
+ // 启动下载服务
+ downloadService = new AriaDownloadService(DownloadingList, DownloadedList);
+ downloadService.Start();
+
return Container.Resolve();
}
+ protected override void OnExit(ExitEventArgs e)
+ {
+ // 关闭下载服务
+ downloadService.End();
+
+ base.OnExit(e);
+ }
+
protected override void RegisterTypes(IContainerRegistry containerRegistry)
{
// pages
diff --git a/src/DownKyi/DownKyi.csproj b/src/DownKyi/DownKyi.csproj
index d2b905c..e75cd65 100644
--- a/src/DownKyi/DownKyi.csproj
+++ b/src/DownKyi/DownKyi.csproj
@@ -87,9 +87,15 @@
+
+
+
+
+
+
@@ -98,7 +104,10 @@
+
+
+
diff --git a/src/DownKyi/Images/ButtonIcon.cs b/src/DownKyi/Images/ButtonIcon.cs
index 73caefb..d747936 100644
--- a/src/DownKyi/Images/ButtonIcon.cs
+++ b/src/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/src/DownKyi/Images/LogoIcon.cs b/src/DownKyi/Images/LogoIcon.cs
index 853394f..6e074bc 100644
--- a/src/DownKyi/Images/LogoIcon.cs
+++ b/src/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/src/DownKyi/Languages/Default.xaml b/src/DownKyi/Languages/Default.xaml
index 559fef5..e3f29bb 100644
--- a/src/DownKyi/Languages/Default.xaml
+++ b/src/DownKyi/Languages/Default.xaml
@@ -64,10 +64,33 @@
下载选中项
下载全部
+ 已经添加到下载列表~
+ 已经下载完成~
+ 没有选中项符合下载要求!
+ 成功添加了
+ 项~
+
正在下载
已下载
+ 音频
+ 视频
+ 弹幕
+ 字幕
+ 封面
+ 正在解析……
+ 下载中……
+ 混流中……
+ 暂停中……
+ 等待中……
+
+ 正在下载
+ 个视频!
+ 全部暂停
+ 全部开始
+ 全部删除
+
按回车键应用设置
@@ -96,16 +119,23 @@
视频
优先下载的视频编码:
优先下载的视频画质:
- 启用视频编号
- 勾选后,将为下载完成的视频的文件名添加上序号
下载FLV视频后转码为mp4
使用默认下载目录
默认下载目录:
默认将文件下载到该文件夹中
更改目录
- 为不同视频分别创建文件夹
- 在下载视频的同时下载弹幕
- 在下载视频的同时下载封面
+ 文件命名格式
+ 文件名:
+ 可选字段:
+ 序号
+ 视频章节
+ 视频标题
+ 分P标题
+ 视频分区
+ 音质
+ 画质
+ 视频编码
+ 空格
弹幕
按类型屏蔽
diff --git a/src/DownKyi/Models/DisplayFileNamePart.cs b/src/DownKyi/Models/DisplayFileNamePart.cs
new file mode 100644
index 0000000..4d8ab20
--- /dev/null
+++ b/src/DownKyi/Models/DisplayFileNamePart.cs
@@ -0,0 +1,17 @@
+using DownKyi.Core.FileName;
+using Prism.Mvvm;
+
+namespace DownKyi.Models
+{
+ public class DisplayFileNamePart : BindableBase
+ {
+ public FileNamePart Id { get; set; }
+
+ private string title;
+ public string Title
+ {
+ get => title;
+ set => SetProperty(ref title, value);
+ }
+ }
+}
diff --git a/src/DownKyi/Models/DownloadBaseItem.cs b/src/DownKyi/Models/DownloadBaseItem.cs
new file mode 100644
index 0000000..6c6c102
--- /dev/null
+++ b/src/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/src/DownKyi/Models/DownloadStatus.cs b/src/DownKyi/Models/DownloadStatus.cs
new file mode 100644
index 0000000..8aa0bd7
--- /dev/null
+++ b/src/DownKyi/Models/DownloadStatus.cs
@@ -0,0 +1,13 @@
+namespace DownKyi.Models
+{
+ public enum DownloadStatus
+ {
+ NOT_STARTED, // 未开始,从未开始下载
+ WAIT_FOR_DOWNLOAD, // 等待下载,下载过,但是启动本次下载周期未开始,如重启程序后未开始
+ PAUSE_STARTED, // 暂停启动下载
+ PAUSE, // 暂停
+ DOWNLOADING, // 下载中
+ DOWNLOAD_SUCCEED, // 下载成功
+ DOWNLOAD_FAILED, // 下载失败
+ }
+}
diff --git a/src/DownKyi/Models/DownloadedItem.cs b/src/DownKyi/Models/DownloadedItem.cs
new file mode 100644
index 0000000..c985651
--- /dev/null
+++ b/src/DownKyi/Models/DownloadedItem.cs
@@ -0,0 +1,9 @@
+namespace DownKyi.Models
+{
+ public class DownloadedItem : DownloadBaseItem
+ {
+ public DownloadedItem() : base()
+ {
+ }
+ }
+}
diff --git a/src/DownKyi/Models/DownloadingItem.cs b/src/DownKyi/Models/DownloadingItem.cs
new file mode 100644
index 0000000..acde187
--- /dev/null
+++ b/src/DownKyi/Models/DownloadingItem.cs
@@ -0,0 +1,165 @@
+using DownKyi.Core.BiliApi.VideoStream.Models;
+using DownKyi.Images;
+using DownKyi.Utils;
+using Prism.Commands;
+using System.Collections.Generic;
+
+namespace DownKyi.Models
+{
+ public class DownloadingItem : DownloadBaseItem
+ {
+ public DownloadingItem() : base()
+ {
+ // 初始化下载的文件列表
+ DownloadFiles = new List();
+
+ // 暂停继续按钮
+ StartOrPause = ButtonIcon.Instance().Pause;
+ StartOrPause.Fill = DictionaryResource.GetColor("ColorPrimary");
+
+ // 删除按钮
+ Delete = ButtonIcon.Instance().Delete;
+ Delete.Fill = DictionaryResource.GetColor("ColorPrimary");
+ }
+
+ public PlayUrl PlayUrl { get; set; }
+
+ // Aria相关
+ public string Gid { get; set; }
+
+ // 下载的文件
+ public List DownloadFiles { get; private set; }
+
+ // 视频类别
+ public PlayStreamType PlayStreamType { get; set; }
+
+
+ // 正在下载内容(音频、视频、弹幕、字幕、封面)
+ private string downloadContent;
+ public string DownloadContent
+ {
+ get => downloadContent;
+ set => SetProperty(ref downloadContent, value);
+ }
+
+ // 下载状态
+ public DownloadStatus DownloadStatus { get; set; }
+
+ // 下载状态显示
+ private string downloadStatusTitle;
+ public string DownloadStatusTitle
+ {
+ 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/src/DownKyi/Models/PlayStreamType.cs b/src/DownKyi/Models/PlayStreamType.cs
new file mode 100644
index 0000000..959d7b0
--- /dev/null
+++ b/src/DownKyi/Models/PlayStreamType.cs
@@ -0,0 +1,9 @@
+namespace DownKyi.Models
+{
+ public enum PlayStreamType
+ {
+ VIDEO = 1, // 普通视频
+ BANGUMI, // 番剧、电影、电视剧等
+ CHEESE, // 课程
+ }
+}
diff --git a/src/DownKyi/Models/VideoInfoView.cs b/src/DownKyi/Models/VideoInfoView.cs
index 5eb92b3..d0d15d7 100644
--- a/src/DownKyi/Models/VideoInfoView.cs
+++ b/src/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/src/DownKyi/Models/VideoPage.cs b/src/DownKyi/Models/VideoPage.cs
index 11ea48d..7b93e92 100644
--- a/src/DownKyi/Models/VideoPage.cs
+++ b/src/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/src/DownKyi/Models/VideoQuality.cs b/src/DownKyi/Models/VideoQuality.cs
index 6df9996..41b966d 100644
--- a/src/DownKyi/Models/VideoQuality.cs
+++ b/src/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/src/DownKyi/Services/BangumiInfoService.cs b/src/DownKyi/Services/BangumiInfoService.cs
index 17f0d87..cd89dbf 100644
--- a/src/DownKyi/Services/BangumiInfoService.cs
+++ b/src/DownKyi/Services/BangumiInfoService.cs
@@ -8,6 +8,7 @@ using DownKyi.Models;
using DownKyi.Utils;
using System;
using System.Collections.Generic;
+using System.Text.RegularExpressions;
using System.Windows.Media.Imaging;
namespace DownKyi.Services
@@ -63,21 +64,28 @@ namespace DownKyi.Services
string name;
// 判断title是否为数字,如果是,则将share_copy作为name,否则将title作为name
- if (int.TryParse(episode.Title, out int result))
- {
- name = episode.ShareCopy;
- }
- else
- {
- if (episode.LongTitle != null && episode.LongTitle != "")
- {
- name = $"{episode.Title} {episode.LongTitle}";
- }
- else
- {
- name = episode.Title;
- }
- }
+ //if (int.TryParse(episode.Title, out int result))
+ //{
+ // name = Regex.Replace(episode.ShareCopy, @"《.*?》", "");
+ // //name = episode.ShareCopy;
+ //}
+ //else
+ //{
+ // if (episode.LongTitle != null && episode.LongTitle != "")
+ // {
+ // name = $"{episode.Title} {episode.LongTitle}";
+ // }
+ // else
+ // {
+ // name = episode.Title;
+ // }
+ //}
+
+ // 将share_copy作为name,删除《》中的标题
+ name = Regex.Replace(episode.ShareCopy, @"^《.*?》", "");
+
+ // 删除前后空白符
+ name = name.Trim();
VideoPage page = new VideoPage
{
@@ -85,6 +93,7 @@ namespace DownKyi.Services
Bvid = episode.Bvid,
Cid = episode.Cid,
EpisodeId = -1,
+ FirstFrame = episode.Cover,
Order = order,
Name = name,
Duration = "N/A"
@@ -141,6 +150,7 @@ namespace DownKyi.Services
Bvid = episode.Bvid,
Cid = episode.Cid,
EpisodeId = -1,
+ FirstFrame = episode.Cover,
Order = order,
Name = name,
Duration = "N/A"
@@ -210,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/src/DownKyi/Services/CheeseInfoService.cs b/src/DownKyi/Services/CheeseInfoService.cs
index 1a25a12..81f0987 100644
--- a/src/DownKyi/Services/CheeseInfoService.cs
+++ b/src/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/src/DownKyi/Services/Download/AriaDownloadService.cs b/src/DownKyi/Services/Download/AriaDownloadService.cs
new file mode 100644
index 0000000..abcf28a
--- /dev/null
+++ b/src/DownKyi/Services/Download/AriaDownloadService.cs
@@ -0,0 +1,763 @@
+using DownKyi.Core.Aria2cNet;
+using DownKyi.Core.Aria2cNet.Client;
+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;
+
+namespace DownKyi.Services.Download
+{
+ ///
+ /// 音视频采用Aria下载,其余采用WebClient下载
+ ///
+ public class AriaDownloadService : DownloadService, IDownloadService
+ {
+ private CancellationTokenSource tokenSource;
+
+ public AriaDownloadService(ObservableCollection downloadingList, ObservableCollection downloadedList) : base(downloadingList, downloadedList)
+ {
+ Tag = "AriaDownloadService";
+ }
+
+ #region 音视频
+
+ ///
+ /// 下载音频,返回下载文件路径
+ ///
+ ///
+ ///
+ public string DownloadAudio(DownloadingItem downloading)
+ {
+ // 更新状态显示
+ downloading.DownloadStatusTitle = DictionaryResource.GetString("WhileDownloading");
+ downloading.DownloadContent = DictionaryResource.GetString("DownloadingAudio");
+
+ // 如果没有Dash,返回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);
+ }
+
+ ///
+ /// 下载视频,返回下载文件路径
+ ///
+ ///
+ ///
+ 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; }
+
+ // 如果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 && Utils.GetVideoCodecName(video.Codecs) == downloading.VideoCodecName)
+ {
+ 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");
+
+ // 记录本次下载的文件
+ downloading.DownloadFiles.Add(fileName);
+
+ // 开始下载
+ DownloadResult downloadStatus = DownloadByAria(downloading, urls, path, fileName);
+ switch (downloadStatus)
+ {
+ case DownloadResult.SUCCESS:
+ return Path.Combine(path, fileName);
+ case DownloadResult.FAILED:
+ return null;
+ case DownloadResult.ABORT:
+ return null;
+ default:
+ return null;
+ }
+ }
+
+ #endregion
+
+ ///
+ /// 下载封面
+ ///
+ ///
+ public string DownloadCover(DownloadingItem downloading)
+ {
+ // 更新状态显示
+ downloading.DownloadStatusTitle = DictionaryResource.GetString("WhileDownloading");
+ downloading.DownloadContent = DictionaryResource.GetString("DownloadingCover");
+ // 下载大小
+ downloading.DownloadingFileSize = string.Empty;
+ // 下载速度
+ downloading.SpeedDisplay = string.Empty;
+
+ // 查询、保存封面
+ StorageCover storageCover = new StorageCover();
+ string cover = storageCover.GetCover(downloading.Avid, downloading.Bvid, downloading.Cid, downloading.CoverUrl);
+ if (cover == null)
+ {
+ return null;
+ }
+
+ // 图片的扩展名
+ string[] temp = downloading.CoverUrl.Split('.');
+ string fileExtension = temp[temp.Length - 1];
+
+ // 图片的地址
+ string coverPath = $"{StorageManager.GetCover()}/{cover}";
+
+ // 复制图片到指定位置
+ try
+ {
+ 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 string DownloadDanmaku(DownloadingItem downloading)
+ {
+ // 更新状态显示
+ downloading.DownloadStatusTitle = DictionaryResource.GetString("WhileDownloading");
+ downloading.DownloadContent = DictionaryResource.GetString("DownloadingDanmaku");
+ // 下载大小
+ downloading.DownloadingFileSize = string.Empty;
+ // 下载速度
+ downloading.SpeedDisplay = string.Empty;
+
+ 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)
+ //{
+ // if (downloadingEntity.Width > 0 && downloadingEntity.Height > 0)
+ // {
+ // screenWidth = downloadingEntity.Width;
+ // screenHeight = downloadingEntity.Height;
+ // }
+ //}
+
+ // 字幕配置
+ Config subtitleConfig = new Config
+ {
+ Title = title,
+ ScreenWidth = screenWidth,
+ ScreenHeight = screenHeight,
+ FontName = SettingsManager.GetInstance().GetDanmakuFontName(),
+ BaseFontSize = SettingsManager.GetInstance().GetDanmakuFontSize(),
+ LineCount = SettingsManager.GetInstance().GetDanmakuLineCount(),
+ LayoutAlgorithm = SettingsManager.GetInstance().GetDanmakuLayoutAlgorithm().ToString("G").ToLower(), // async/sync
+ TuneDuration = 0,
+ DropOffset = 0,
+ BottomMargin = 0,
+ CustomOffset = 0
+ };
+
+ Core.Danmaku2Ass.Bilibili.GetInstance()
+ .SetTopFilter(SettingsManager.GetInstance().GetDanmakuTopFilter() == AllowStatus.YES)
+ .SetBottomFilter(SettingsManager.GetInstance().GetDanmakuBottomFilter() == AllowStatus.YES)
+ .SetScrollFilter(SettingsManager.GetInstance().GetDanmakuScrollFilter() == AllowStatus.YES)
+ .Create(downloading.Avid, downloading.Cid, subtitleConfig, assFile);
+
+ return assFile;
+ }
+
+ ///
+ /// 下载字幕
+ ///
+ ///
+ public List DownloadSubtitle(DownloadingItem downloading)
+ {
+ // 更新状态显示
+ downloading.DownloadStatusTitle = DictionaryResource.GetString("WhileDownloading");
+ downloading.DownloadContent = DictionaryResource.GetString("DownloadingSubtitle");
+ // 下载大小
+ downloading.DownloadingFileSize = string.Empty;
+ // 下载速度
+ downloading.SpeedDisplay = string.Empty;
+
+ 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";
+ try
+ {
+ File.WriteAllText(srtFile, subRip.SrtString);
+
+ // 记录本次下载的文件
+ downloading.DownloadFiles.Add(srtFile);
+
+ srtFiles.Add(srtFile);
+ }
+ catch (Exception e)
+ {
+ Core.Utils.Debugging.Console.PrintLine("DownloadSubtitle()发生异常: {0}", e);
+ LogManager.Error("DownloadSubtitle()", e);
+ }
+ }
+
+ return srtFiles;
+ }
+
+ ///
+ /// 混流音频和视频
+ ///
+ ///
+ ///
+ ///
+ ///
+ public string MixedFlow(DownloadingItem downloading, string audioUid, string videoUid)
+ {
+ // 更新状态显示
+ downloading.DownloadStatusTitle = DictionaryResource.GetString("MixedFlow");
+ downloading.DownloadContent = DictionaryResource.GetString("DownloadingVideo");
+ // 下载大小
+ downloading.DownloadingFileSize = string.Empty;
+ // 下载速度
+ downloading.SpeedDisplay = string.Empty;
+
+ string finalFile = $"{downloading.FilePath}.mp4";
+ if (videoUid == null)
+ {
+ finalFile = $"{downloading.FilePath}.aac";
+ }
+
+ // 合并音视频
+ FFmpegHelper.MergeVideo(audioUid, videoUid, finalFile);
+
+ // 获取文件大小
+ if (File.Exists(finalFile))
+ {
+ FileInfo info = new FileInfo(finalFile);
+ downloading.FileSize = Format.FormatFileSize(info.Length);
+ }
+ else
+ {
+ downloading.FileSize = Format.FormatFileSize(0);
+ }
+
+ return finalFile;
+ }
+
+ ///
+ /// 解析视频流的下载链接
+ ///
+ ///
+ public void Parse(DownloadingItem downloading)
+ {
+ // 更新状态显示
+ downloading.DownloadStatusTitle = DictionaryResource.GetString("Parsing");
+ downloading.DownloadContent = string.Empty;
+ // 下载大小
+ downloading.DownloadingFileSize = string.Empty;
+ // 下载速度
+ downloading.SpeedDisplay = string.Empty;
+
+ if (downloading.PlayUrl != null && downloading.DownloadStatus == DownloadStatus.NOT_STARTED)
+ {
+ return;
+ }
+
+ // 解析
+ switch (downloading.PlayStreamType)
+ {
+ case PlayStreamType.VIDEO:
+ downloading.PlayUrl = VideoStream.GetVideoPlayUrl(downloading.Avid, downloading.Bvid, downloading.Cid);
+ break;
+ case PlayStreamType.BANGUMI:
+ downloading.PlayUrl = VideoStream.GetBangumiPlayUrl(downloading.Avid, downloading.Bvid, downloading.Cid);
+ break;
+ case PlayStreamType.CHEESE:
+ downloading.PlayUrl = VideoStream.GetCheesePlayUrl(downloading.Avid, downloading.Bvid, downloading.Cid, downloading.EpisodeId);
+ break;
+ default:
+ break;
+ }
+ }
+
+ ///
+ /// 停止下载服务
+ ///
+ public void End()
+ {
+ // TODO
+ // 保存数据
+
+ // 关闭Aria服务器
+ CloseAriaServer();
+
+ // 结束任务
+ tokenSource.Cancel();
+ }
+
+ ///
+ /// 启动下载服务
+ ///
+ public async void Start()
+ {
+ // 启动Aria服务器
+ StartAriaServer();
+
+ await Task.Run(DoWork, (tokenSource = new CancellationTokenSource()).Token);
+ }
+
+ ///
+ /// 执行任务
+ ///
+ private void DoWork()
+ {
+ CancellationToken cancellationToken = tokenSource.Token;
+ while (true)
+ {
+ int maxDownloading = SettingsManager.GetInstance().GetAriaMaxConcurrentDownloads();
+ int downloadingCount = 0;
+ foreach (DownloadingItem downloading in downloadingList)
+ {
+ if (downloading.DownloadStatus == DownloadStatus.DOWNLOADING)
+ {
+ downloadingCount++;
+ }
+ }
+
+ foreach (DownloadingItem downloading in downloadingList)
+ {
+ if (downloadingCount >= maxDownloading)
+ {
+ break;
+ }
+
+ // 开始下载
+ if (downloading.DownloadStatus == DownloadStatus.NOT_STARTED || downloading.DownloadStatus == DownloadStatus.WAIT_FOR_DOWNLOAD)
+ {
+ SingleDownload(downloading);
+ downloadingCount++;
+ }
+ }
+
+ // 判断是否该结束线程,若为true,跳出while循环
+ if (cancellationToken.IsCancellationRequested)
+ {
+ Core.Utils.Debugging.Console.PrintLine("AriaDownloadService: 下载服务结束,跳出while循环");
+ LogManager.Debug(Tag, "下载服务结束");
+ break;
+ }
+
+ // 降低CPU占用
+ 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"])
+ {
+ if (outputSubtitles == null)
+ {
+ // 为null时表示不存在字幕
+ }
+ else
+ {
+ 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;
+ }
+
+ ///
+ /// 启动Aria服务器
+ ///
+ private async void StartAriaServer()
+ {
+ List header = new List
+ {
+ $"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"
+ };
+
+ AriaConfig config = new AriaConfig()
+ {
+ ListenPort = SettingsManager.GetInstance().GetAriaListenPort(),
+ Token = "downkyi",
+ LogLevel = SettingsManager.GetInstance().GetAriaLogLevel(),
+ MaxConcurrentDownloads = SettingsManager.GetInstance().GetAriaMaxConcurrentDownloads(),
+ MaxConnectionPerServer = 16, // 最大取16
+ Split = SettingsManager.GetInstance().GetAriaSplit(),
+ //MaxTries = 5,
+ MinSplitSize = 10, // 10MB
+ MaxOverallDownloadLimit = SettingsManager.GetInstance().GetAriaMaxOverallDownloadLimit() * 1024L, // 输入的单位是KB/s,所以需要乘以1024
+ MaxDownloadLimit = SettingsManager.GetInstance().GetAriaMaxDownloadLimit() * 1024L, // 输入的单位是KB/s,所以需要乘以1024
+ MaxOverallUploadLimit = 0,
+ MaxUploadLimit = 0,
+ ContinueDownload = true,
+ FileAllocation = SettingsManager.GetInstance().GetAriaFileAllocation(),
+ Headers = header
+ };
+ var task = await AriaServer.StartServerAsync(config);
+ if (task) { Console.WriteLine("Start ServerAsync Completed"); }
+ Console.WriteLine("Start ServerAsync end");
+
+ // 恢复所有下载
+ //var ariaPause = await AriaClient.UnpauseAllAsync();
+ //if (ariaPause != null)
+ //{
+ // Core.Utils.Debugging.Console.PrintLine(ariaPause.ToString());
+ //}
+ }
+
+ ///
+ /// 关闭Aria服务器
+ ///
+ private void CloseAriaServer()
+ {
+ new Thread(() =>
+ {
+ // 暂停所有下载
+ var ariaPause = AriaClient.PauseAllAsync();
+ Core.Utils.Debugging.Console.PrintLine(ariaPause.ToString());
+
+ // 关闭服务器
+ bool close = AriaServer.CloseServer();
+ Core.Utils.Debugging.Console.PrintLine(close);
+ })
+ { IsBackground = false }
+ .Start();
+ }
+
+ ///
+ /// 采用Aria下载文件
+ ///
+ ///
+ ///
+ private DownloadResult DownloadByAria(DownloadingItem downloading, List urls, string path, string localFileName)
+ {
+ // path已斜杠结尾,去掉斜杠
+ path = path.TrimEnd('/').TrimEnd('\\');
+
+ AriaSendOption option = new AriaSendOption
+ {
+ //HttpProxy = $"http://{Settings.GetAriaHttpProxy()}:{Settings.GetAriaHttpProxyListenPort()}",
+ Dir = path,
+ Out = localFileName
+ //Header = $"cookie: {Login.GetLoginInfoCookiesString()}\nreferer: https://www.bilibili.com",
+ //UseHead = "true",
+ //UserAgent = Utils.GetUserAgent()
+ };
+
+ // 如果设置了代理,则增加HttpProxy
+ if (SettingsManager.GetInstance().IsAriaHttpProxy() == AllowStatus.YES)
+ {
+ option.HttpProxy = $"http://{SettingsManager.GetInstance().GetAriaHttpProxy()}:{SettingsManager.GetInstance().GetAriaHttpProxyListenPort()}";
+ }
+
+ // 添加一个下载
+ Task ariaAddUri = AriaClient.AddUriAsync(urls, option);
+ if (ariaAddUri == null || ariaAddUri.Result == null || ariaAddUri.Result.Result == null)
+ {
+ return DownloadResult.FAILED;
+ }
+
+ // 保存gid
+ string gid = ariaAddUri.Result.Result;
+ downloading.Gid = gid;
+
+ // 管理下载
+ AriaManager ariaManager = new AriaManager();
+ ariaManager.TellStatus += AriaTellStatus;
+ ariaManager.DownloadFinish += AriaDownloadFinish;
+ return ariaManager.GetDownloadStatus(gid, new Action(() =>
+ {
+ switch (downloading.DownloadStatus)
+ {
+ case DownloadStatus.PAUSE:
+ Task ariaPause = AriaClient.PauseAsync(downloading.Gid);
+ // 通知UI,并阻塞当前线程
+ Pause(downloading);
+ break;
+ case DownloadStatus.DOWNLOADING:
+ Task ariaUnpause = AriaClient.UnpauseAsync(downloading.Gid);
+ break;
+ }
+ }));
+ }
+
+ private void AriaTellStatus(long totalLength, long completedLength, long speed, string gid)
+ {
+ // 当前的下载视频
+ DownloadingItem video = downloadingList.FirstOrDefault(it => it.Gid == gid);
+ if (video == null) { return; }
+
+ float percent = 0;
+ if (totalLength != 0)
+ {
+ percent = (float)completedLength / totalLength * 100;
+ }
+
+ // 根据进度判断本次是否需要更新UI
+ if (Math.Abs(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();
+ }
+ }
+}
diff --git a/src/DownKyi/Services/Download/DownloadService.cs b/src/DownKyi/Services/Download/DownloadService.cs
new file mode 100644
index 0000000..07288ee
--- /dev/null
+++ b/src/DownKyi/Services/Download/DownloadService.cs
@@ -0,0 +1,25 @@
+using DownKyi.Models;
+using System.Collections.ObjectModel;
+
+namespace DownKyi.Services.Download
+{
+ public class DownloadService
+ {
+ protected string Tag = "DownloadService";
+
+ protected ObservableCollection downloadingList;
+ protected ObservableCollection downloadedList;
+
+ ///
+ /// 初始化
+ ///
+ ///
+ ///
+ public DownloadService(ObservableCollection downloadingList, ObservableCollection downloadedList)
+ {
+ this.downloadingList = downloadingList;
+ this.downloadedList = downloadedList;
+ }
+
+ }
+}
diff --git a/src/DownKyi/Services/Download/IDownloadService.cs b/src/DownKyi/Services/Download/IDownloadService.cs
new file mode 100644
index 0000000..8facfb4
--- /dev/null
+++ b/src/DownKyi/Services/Download/IDownloadService.cs
@@ -0,0 +1,19 @@
+using DownKyi.Models;
+using System.Collections.Generic;
+
+namespace DownKyi.Services.Download
+{
+ public interface IDownloadService
+ {
+ void Parse(DownloadingItem downloading);
+ string DownloadAudio(DownloadingItem downloading);
+ string DownloadVideo(DownloadingItem downloading);
+ 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/src/DownKyi/Services/Utils.cs b/src/DownKyi/Services/Utils.cs
index 3807f04..c16cf98 100644
--- a/src/DownKyi/Services/Utils.cs
+++ b/src/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/src/DownKyi/Services/VideoInfoService.cs b/src/DownKyi/Services/VideoInfoService.cs
index 49c6b60..a807aaa 100644
--- a/src/DownKyi/Services/VideoInfoService.cs
+++ b/src/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"
@@ -160,7 +162,7 @@ namespace DownKyi.Services
// 分区
string videoZone = string.Empty;
- var zoneList = Core.BiliApi.Zone.VideoZone.Instance().GetZone();
+ var zoneList = Core.BiliApi.Zone.VideoZone.Instance().GetZones();
var zone = zoneList.Find(it => it.Id == videoView.Tid);
if (zone != null)
{
@@ -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/src/DownKyi/Themes/ColorBrush.xaml b/src/DownKyi/Themes/ColorBrush.xaml
index bc81eda..181e106 100644
--- a/src/DownKyi/Themes/ColorBrush.xaml
+++ b/src/DownKyi/Themes/ColorBrush.xaml
@@ -23,6 +23,7 @@
+
@@ -34,6 +35,7 @@
+
diff --git a/src/DownKyi/Themes/Colors/ColorDefault.xaml b/src/DownKyi/Themes/Colors/ColorDefault.xaml
index fd949c1..7ce177c 100644
--- a/src/DownKyi/Themes/Colors/ColorDefault.xaml
+++ b/src/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/src/DownKyi/Themes/Styles/StyleListBox.xaml b/src/DownKyi/Themes/Styles/StyleListBox.xaml
index 0cd9181..5edeebd 100644
--- a/src/DownKyi/Themes/Styles/StyleListBox.xaml
+++ b/src/DownKyi/Themes/Styles/StyleListBox.xaml
@@ -1,5 +1,6 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/DownKyi/Utils/DictionaryResource.cs b/src/DownKyi/Utils/DictionaryResource.cs
index 93290d8..71147da 100644
--- a/src/DownKyi/Utils/DictionaryResource.cs
+++ b/src/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/src/DownKyi/ViewModels/DownloadManager/ViewDownloadFinishedViewModel.cs b/src/DownKyi/ViewModels/DownloadManager/ViewDownloadFinishedViewModel.cs
index 16f97fe..a305a98 100644
--- a/src/DownKyi/ViewModels/DownloadManager/ViewDownloadFinishedViewModel.cs
+++ b/src/DownKyi/ViewModels/DownloadManager/ViewDownloadFinishedViewModel.cs
@@ -1,8 +1,10 @@
-using Prism.Commands;
+using DownKyi.Models;
+using Prism.Commands;
using Prism.Events;
using Prism.Mvvm;
using System;
using System.Collections.Generic;
+using System.Collections.ObjectModel;
using System.Linq;
namespace DownKyi.ViewModels.DownloadManager
@@ -11,9 +13,20 @@ namespace DownKyi.ViewModels.DownloadManager
{
public const string Tag = "PageDownloadManagerDownloadFinished";
- public ViewDownloadFinishedViewModel(IEventAggregator eventAggregator) : base(eventAggregator)
+ #region 页面属性申明
+
+ private ObservableCollection downloadedList;
+ public ObservableCollection DownloadedList
{
+ get { return downloadedList; }
+ set { SetProperty(ref downloadedList, value); }
+ }
+ #endregion
+
+ public ViewDownloadFinishedViewModel(IEventAggregator eventAggregator) : base(eventAggregator)
+ {
+ DownloadedList = App.DownloadedList;
}
}
}
diff --git a/src/DownKyi/ViewModels/DownloadManager/ViewDownloadingViewModel.cs b/src/DownKyi/ViewModels/DownloadManager/ViewDownloadingViewModel.cs
index ba87677..547438a 100644
--- a/src/DownKyi/ViewModels/DownloadManager/ViewDownloadingViewModel.cs
+++ b/src/DownKyi/ViewModels/DownloadManager/ViewDownloadingViewModel.cs
@@ -1,9 +1,9 @@
-using Prism.Commands;
+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.Linq;
+using System.Collections.ObjectModel;
namespace DownKyi.ViewModels.DownloadManager
{
@@ -11,9 +11,117 @@ namespace DownKyi.ViewModels.DownloadManager
{
public const string Tag = "PageDownloadManagerDownloading";
+ #region 页面属性申明
+
+ private ObservableCollection downloadingList;
+ public ObservableCollection DownloadingList
+ {
+ get => downloadingList;
+ set => SetProperty(ref downloadingList, value);
+ }
+
+ #endregion
+
public ViewDownloadingViewModel(IEventAggregator eventAggregator) : base(eventAggregator)
{
+ // 初始化DownloadingList
+ DownloadingList = App.DownloadingList;
+ }
+ #region 命令申明
+
+ // 暂停所有下载事件
+ private DelegateCommand pauseAllDownloadingCommand;
+ public DelegateCommand PauseAllDownloadingCommand => pauseAllDownloadingCommand ?? (pauseAllDownloadingCommand = new DelegateCommand(ExecutePauseAllDownloadingCommand));
+
+ ///
+ /// 暂停所有下载事件
+ ///
+ 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");
+ }
}
+
+ // 继续所有下载事件
+ private DelegateCommand continueAllDownloadingCommand;
+ public DelegateCommand ContinueAllDownloadingCommand => continueAllDownloadingCommand ?? (continueAllDownloadingCommand = new DelegateCommand(ExecuteContinueAllDownloadingCommand));
+
+ ///
+ /// 继续所有下载事件
+ ///
+ 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");
+ }
+ }
+
+ // 删除所有下载事件
+ private DelegateCommand deleteAllDownloadingCommand;
+ public DelegateCommand DeleteAllDownloadingCommand => deleteAllDownloadingCommand ?? (deleteAllDownloadingCommand = new DelegateCommand(ExecuteDeleteAllDownloadingCommand));
+
+ ///
+ /// 删除所有下载事件
+ ///
+ private void ExecuteDeleteAllDownloadingCommand()
+ {
+ DownloadingList.Clear();
+ }
+
+ #endregion
+
}
}
diff --git a/src/DownKyi/ViewModels/MainWindowViewModel.cs b/src/DownKyi/ViewModels/MainWindowViewModel.cs
index 2f9f1fa..8066cf0 100644
--- a/src/DownKyi/ViewModels/MainWindowViewModel.cs
+++ b/src/DownKyi/ViewModels/MainWindowViewModel.cs
@@ -304,6 +304,7 @@ namespace DownKyi.ViewModels
///
private void OnClipboardUpdated(object sender, EventArgs e)
{
+ #region 执行第二遍时跳过
times += 1;
DispatcherTimer timer = new DispatcherTimer
{
@@ -319,6 +320,8 @@ namespace DownKyi.ViewModels
return;
}
+ #endregion
+
AllowStatus isListenClipboard = SettingsManager.GetInstance().IsListenClipboard();
if (isListenClipboard != AllowStatus.YES)
{
diff --git a/src/DownKyi/ViewModels/Settings/ViewVideoViewModel.cs b/src/DownKyi/ViewModels/Settings/ViewVideoViewModel.cs
index 46f3e62..5f47ece 100644
--- a/src/DownKyi/ViewModels/Settings/ViewVideoViewModel.cs
+++ b/src/DownKyi/ViewModels/Settings/ViewVideoViewModel.cs
@@ -1,4 +1,5 @@
-using DownKyi.Core.Settings;
+using DownKyi.Core.FileName;
+using DownKyi.Core.Settings;
using DownKyi.Events;
using DownKyi.Models;
using DownKyi.Services;
@@ -6,7 +7,9 @@ using DownKyi.Utils;
using Prism.Commands;
using Prism.Events;
using Prism.Regions;
+using System;
using System.Collections.Generic;
+using System.Collections.ObjectModel;
using System.Linq;
namespace DownKyi.ViewModels.Settings
@@ -22,82 +25,76 @@ namespace DownKyi.ViewModels.Settings
private List videoCodecs;
public List VideoCodecs
{
- get { return videoCodecs; }
- set { SetProperty(ref videoCodecs, value); }
+ get => videoCodecs;
+ set => SetProperty(ref videoCodecs, value);
}
private string selectedVideoCodec;
public string SelectedVideoCodec
{
- get { return selectedVideoCodec; }
- set { SetProperty(ref selectedVideoCodec, value); }
+ get => selectedVideoCodec;
+ set => SetProperty(ref selectedVideoCodec, value);
}
private List videoQualityList;
public List VideoQualityList
{
- get { return videoQualityList; }
- set { SetProperty(ref videoQualityList, value); }
+ get => videoQualityList;
+ set => SetProperty(ref videoQualityList, value);
}
private Resolution selectedVideoQuality;
public Resolution SelectedVideoQuality
{
- get { return selectedVideoQuality; }
- set { SetProperty(ref selectedVideoQuality, value); }
- }
-
- private bool isAddVideoOrder;
- public bool IsAddVideoOrder
- {
- get { return isAddVideoOrder; }
- set { SetProperty(ref isAddVideoOrder, value); }
+ get => selectedVideoQuality;
+ set => SetProperty(ref selectedVideoQuality, value);
}
private bool isTranscodingFlvToMp4;
public bool IsTranscodingFlvToMp4
{
- get { return isTranscodingFlvToMp4; }
- set { SetProperty(ref isTranscodingFlvToMp4, value); }
+ get => isTranscodingFlvToMp4;
+ set => SetProperty(ref isTranscodingFlvToMp4, value);
}
private bool isUseDefaultDirectory;
public bool IsUseDefaultDirectory
{
- get { return isUseDefaultDirectory; }
- set { SetProperty(ref isUseDefaultDirectory, value); }
+ get => isUseDefaultDirectory;
+ set => SetProperty(ref isUseDefaultDirectory, value);
}
private string saveVideoDirectory;
public string SaveVideoDirectory
{
- get { return saveVideoDirectory; }
- set { SetProperty(ref saveVideoDirectory, value); }
+ get => saveVideoDirectory;
+ set => SetProperty(ref saveVideoDirectory, value);
}
- private bool isCreateFolderForMedia;
- public bool IsCreateFolderForMedia
+ private ObservableCollection selectedFileName;
+ public ObservableCollection SelectedFileName
{
- get { return isCreateFolderForMedia; }
- set { SetProperty(ref isCreateFolderForMedia, value); }
+ get => selectedFileName;
+ set => SetProperty(ref selectedFileName, value);
}
- private bool isDownloadDanmaku;
- public bool IsDownloadDanmaku
+ private ObservableCollection optionalFields;
+ public ObservableCollection OptionalFields
{
- get { return isDownloadDanmaku; }
- set { SetProperty(ref isDownloadDanmaku, value); }
+ get => optionalFields;
+ set => SetProperty(ref optionalFields, value);
}
- private bool isDownloadCover;
- public bool IsDownloadCover
+ private int selectedOptionalField;
+ public int SelectedOptionalField
{
- get { return isDownloadCover; }
- set { SetProperty(ref isDownloadCover, value); }
+ get => selectedOptionalField;
+ set => SetProperty(ref selectedOptionalField, value);
}
#endregion
+
public ViewVideoViewModel(IEventAggregator eventAggregator) : base(eventAggregator)
{
@@ -113,6 +110,17 @@ namespace DownKyi.ViewModels.Settings
// 优先下载画质
VideoQualityList = new ResolutionService().GetResolution();
+ // 文件命名格式
+ SelectedFileName = new ObservableCollection();
+ OptionalFields = new ObservableCollection();
+ foreach (FileNamePart item in Enum.GetValues(typeof(FileNamePart)))
+ {
+ string display = DisplayFileNamePart(item);
+ OptionalFields.Add(new DisplayFileNamePart { Id = item, Title = display });
+ }
+
+ SelectedOptionalField = -1;
+
#endregion
}
@@ -135,10 +143,6 @@ namespace DownKyi.ViewModels.Settings
int quality = SettingsManager.GetInstance().GetQuality();
SelectedVideoQuality = VideoQualityList.FirstOrDefault(t => { return t.Id == quality; });
- // 是否在下载的视频前增加序号
- AllowStatus isAddOrder = SettingsManager.GetInstance().IsAddOrder();
- IsAddVideoOrder = isAddOrder == AllowStatus.YES;
-
// 是否下载flv视频后转码为mp4
AllowStatus isTranscodingFlvToMp4 = SettingsManager.GetInstance().IsTranscodingFlvToMp4();
IsTranscodingFlvToMp4 = isTranscodingFlvToMp4 == AllowStatus.YES;
@@ -150,17 +154,14 @@ namespace DownKyi.ViewModels.Settings
// 默认下载目录
SaveVideoDirectory = SettingsManager.GetInstance().GetSaveVideoRootPath();
- // 是否为不同视频分别创建文件夹
- AllowStatus isCreateFolderForMedia = SettingsManager.GetInstance().IsCreateFolderForMedia();
- IsCreateFolderForMedia = isCreateFolderForMedia == AllowStatus.YES;
-
- // 是否在下载视频的同时下载弹幕
- AllowStatus isDownloadDanmaku = SettingsManager.GetInstance().IsDownloadDanmaku();
- IsDownloadDanmaku = isDownloadDanmaku == AllowStatus.YES;
-
- // 是否在下载视频的同时下载封面
- AllowStatus isDownloadCover = SettingsManager.GetInstance().IsDownloadCover();
- IsDownloadCover = isDownloadCover == AllowStatus.YES;
+ // 文件命名格式
+ List fileNameParts = SettingsManager.GetInstance().GetFileNameParts();
+ SelectedFileName.Clear();
+ foreach (FileNamePart item in fileNameParts)
+ {
+ string display = DisplayFileNamePart(item);
+ SelectedFileName.Add(new DisplayFileNamePart { Id = item, Title = display });
+ }
isOnNavigatedTo = false;
}
@@ -199,21 +200,6 @@ namespace DownKyi.ViewModels.Settings
PublishTip(isSucceed);
}
- // 是否在下载的视频前增加序号事件
- private DelegateCommand IisAddVideoOrderCommand;
- public DelegateCommand IsAddVideoOrderCommand => IisAddVideoOrderCommand ?? (IisAddVideoOrderCommand = new DelegateCommand(ExecuteIsAddVideoOrderCommand));
-
- ///
- /// 是否在下载的视频前增加序号事件
- ///
- private void ExecuteIsAddVideoOrderCommand()
- {
- AllowStatus isAddOrder = IsAddVideoOrder ? AllowStatus.YES : AllowStatus.NO;
-
- bool isSucceed = SettingsManager.GetInstance().IsAddOrder(isAddOrder);
- PublishTip(isSucceed);
- }
-
// 是否下载flv视频后转码为mp4事件
private DelegateCommand isTranscodingFlvToMp4Command;
public DelegateCommand IsTranscodingFlvToMp4Command => isTranscodingFlvToMp4Command ?? (isTranscodingFlvToMp4Command = new DelegateCommand(ExecuteIsTranscodingFlvToMp4Command));
@@ -265,51 +251,63 @@ namespace DownKyi.ViewModels.Settings
}
}
- // 是否为不同视频分别创建文件夹事件
- private DelegateCommand isCreateFolderForMediaCommand;
- public DelegateCommand IsCreateFolderForMediaCommand => isCreateFolderForMediaCommand ?? (isCreateFolderForMediaCommand = new DelegateCommand(ExecuteIsCreateFolderForMediaCommand));
+ // 选中文件名字段点击事件
+ private DelegateCommand