Compare commits

...
Author SHA1 Message Date
leiurayer e69fd47e30 发布v1.5.0-alpha4 2022-03-13 00:05:56 +08:00
leiurayer 99f8bb6ca5 配置文件目录改为当前程序目录 2022-03-12 23:20:25 +08:00
leiurayer bbfa57f407 优化UI显示 2022-03-12 23:19:39 +08:00
leiurayer 6652d27bfe 去除重复参数 2022-03-12 23:19:17 +08:00
leiurayer c5e277941a Merge pull request #253 from wtywtykk/v1.5.x
另一些改进
2022-03-12 23:14:29 +08:00
wty f2804b3cd7 改进aria启动关闭逻辑,启动关闭完成之后才进行下一步操作
处理文件被意外删掉的情况
创建下载线程后立刻设置标志位,防止重复创建
关闭时主动结束下载线程
ffmpeg参数加-y避免文件重复下载时卡死
2022-03-12 21:19:33 +08:00
leiurayer c42147cf28 修复因下载列表中的重复内容导致的闪退问题 2022-03-12 13:26:34 +08:00
leiurayer 577631c295 Merge branch '252' into v1.5.x
# Conflicts:
#	src/DownKyi/ViewModels/ViewMySpaceViewModel.cs
2022-03-12 13:12:48 +08:00
wty 4cf196cdc2 修复性别保密导致崩溃
暂停时结束任务线程
修复重复创建aria任务的问题
修复大量报WebException问题
2022-03-12 01:37:57 +08:00
12 changed files with 444 additions and 367 deletions
+6
View File
@@ -1,5 +1,11 @@
# 更新日志
* `2022/03/13` v1.5.0-alpha4
1. [优化] 配置文件目录改为当前程序目录。
2. [修复] 部分UI显示。
3. [修复] 因数据库中的重复内容导致的闪退问题。
4. [修复] 其他已知问题。
* `2022/03/12` v1.5.0-alpha3
1. [修复] 当用户性别为保密时,程序闪退的问题。
2. [修复] 批量下载UP主视频时,程序偶尔闪退的问题。
+12 -5
View File
@@ -9,14 +9,15 @@
[全部更新日志](CHANGELOG.md)
* `2022/03/12` v1.5.0-alpha3
1. [修复] 当用户性别为保密时,程序闪退的问题。
2. [修复] 批量下载UP主视频时,程序偶尔闪退的问题。
3. [修复] 其他已知问题。
* `2022/03/13` v1.5.0-alpha4
1. [优化] 配置文件目录改为当前程序目录。
2. [修复] 部分UI显示。
3. [修复] 因数据库中的重复内容导致的闪退问题。
4. [修复] 其他已知问题。
## 下载
- [哔哩下载姬最新版](https://github.com/FlySelfLog/downkyi/releases/download/v1.5.0-alpha3/DownKyi-1.5.0-alpha3.zip)
- [哔哩下载姬最新版](https://github.com/FlySelfLog/downkyi/releases/download/v1.5.0-alpha4/DownKyi-1.5.0-alpha4.zip)
- [下载页面](https://github.com/FlySelfLog/downkyi/releases)
@@ -26,6 +27,12 @@
![Alipay](images/Alipay.png)![WeChat](images/WeChat.png)
## 开发
### 相关项目
- [哔哩哔哩-API收集整理](https://github.com/SocialSisterYi/bilibili-API-collect)
## 免责申明
1. 本软件只提供视频解析,不提供任何资源上传、存储到服务器的功能。
+51 -32
View File
@@ -137,6 +137,18 @@ namespace DownKyi.Core.Aria2cNet.Server
public static async Task<bool> CloseServerAsync()
{
await AriaClient.ShutdownAsync();
// 等待进程结束
await Task.Run(() =>
{
Server.WaitForExit(30000);
try
{
Server.Kill();
}
catch (Exception)
{
}
});
return true;
}
@@ -166,7 +178,18 @@ namespace DownKyi.Core.Aria2cNet.Server
{
var task = AriaClient.ShutdownAsync();
if (task.Result != null && task.Result.Result != null && task.Result.Result == "OK")
{ return true; }
{
// 等待进程结束
Server.WaitForExit(30000);
try
{
Server.Kill();
}
catch (Exception)
{
}
return true;
}
return false;
}
@@ -208,39 +231,35 @@ namespace DownKyi.Core.Aria2cNet.Server
private static void ExcuteProcess(string exe, string arg, string workingDirectory, DataReceivedEventHandler output)
{
using (var p = new Process())
var p = new Process();
Server = p;
p.StartInfo.FileName = exe;
p.StartInfo.Arguments = arg;
// 工作目录
if (workingDirectory != null)
{
Server = p;
p.StartInfo.FileName = exe;
p.StartInfo.Arguments = arg;
// 工作目录
if (workingDirectory != null)
{
p.StartInfo.WorkingDirectory = workingDirectory;
}
// 输出信息重定向
p.StartInfo.UseShellExecute = false;
p.StartInfo.CreateNoWindow = true;
p.StartInfo.RedirectStandardError = true;
p.StartInfo.RedirectStandardOutput = true;
// 将 StandardErrorEncoding 改为 UTF-8 才不会出现中文乱码
p.StartInfo.StandardOutputEncoding = Encoding.UTF8;
p.StartInfo.StandardErrorEncoding = Encoding.UTF8;
p.OutputDataReceived += output;
p.ErrorDataReceived += output;
// 启动线程
p.Start();
p.BeginOutputReadLine();
p.BeginErrorReadLine();
// 等待进程结束
p.WaitForExit();
p.StartInfo.WorkingDirectory = workingDirectory;
}
// 输出信息重定向
p.StartInfo.UseShellExecute = false;
p.StartInfo.CreateNoWindow = true;
p.StartInfo.RedirectStandardError = true;
p.StartInfo.RedirectStandardOutput = true;
// 将 StandardErrorEncoding 改为 UTF-8 才不会出现中文乱码
p.StartInfo.StandardOutputEncoding = Encoding.UTF8;
p.StartInfo.StandardErrorEncoding = Encoding.UTF8;
p.OutputDataReceived += output;
p.ErrorDataReceived += output;
// 启动线程
p.Start();
p.BeginOutputReadLine();
p.BeginErrorReadLine();
}
}
+7 -7
View File
@@ -17,14 +17,14 @@ namespace DownKyi.Core.FFmpeg
/// <param name="destVideo"></param>
public static bool MergeVideo(string video1, string video2, string destVideo)
{
string param = $"-i \"{video1}\" -i \"{video2}\" -acodec copy -vcodec copy -f mp4 \"{destVideo}\"";
string param = $"-y -i \"{video1}\" -i \"{video2}\" -acodec copy -vcodec copy -f mp4 \"{destVideo}\"";
if (video1 == null || !File.Exists(video1))
{
param = $"-i \"{video2}\" -acodec copy -vcodec copy -f mp4 \"{destVideo}\"";
param = $"-y -i \"{video2}\" -acodec copy -vcodec copy -f mp4 \"{destVideo}\"";
}
if (video2 == null || !File.Exists(video2))
{
param = $"-i \"{video1}\" -acodec copy -f aac \"{destVideo}\"";
param = $"-y -i \"{video1}\" -acodec copy -f aac \"{destVideo}\"";
}
if (!File.Exists(video1) && !File.Exists(video2)) { return false; }
@@ -89,9 +89,9 @@ namespace DownKyi.Core.FFmpeg
return false;
}
// ffmpeg -f concat -safe 0 -i filelist.txt -c copy output.mkv
// ffmpeg -y -f concat -safe 0 -i filelist.txt -c copy output.mkv
// 加上-y,表示如果有同名文件,则默认覆盖
string param = $"-f concat -safe 0 -i {concatFileName} -c copy \"{destVideo}\" -y";
string param = $"-y -f concat -safe 0 -i {concatFileName} -c copy \"{destVideo}\" -y";
ExcuteProcess("ffmpeg.exe", param, workingDirectory, (s, e) => Console.WriteLine(e.Data));
// 删除临时文件
@@ -126,8 +126,8 @@ namespace DownKyi.Core.FFmpeg
/// <param name="action"></param>
public static void Delogo(string video, string destVideo, int x, int y, int width, int height, Action<string> action)
{
// ffmpeg -i "video.mp4" -vf delogo=x=1670:y=50:w=180:h=70:show=1 "delogo.mp4"
string param = $"-i \"{video}\" -vf delogo=x={x}:y={y}:w={width}:h={height}:show=0 \"{destVideo}\" -hide_banner -y";
// ffmpeg -y -i "video.mp4" -vf delogo=x=1670:y=50:w=180:h=70:show=1 "delogo.mp4"
string param = $"-y -i \"{video}\" -vf delogo=x={x}:y={y}:w={width}:h={height}:show=0 \"{destVideo}\" -hide_banner";
ExcuteProcess("ffmpeg.exe", param, null, (s, e) =>
{
Console.WriteLine(e.Data);
+2 -1
View File
@@ -8,7 +8,8 @@ namespace DownKyi.Core.Storage
internal static class Constant
{
// 根目录
private static string Root { get; } = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + "/Downkyi";
//private static string Root { get; } = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + "/Downkyi";
private static string Root { get; } = Environment.CurrentDirectory;
// Aria
public static string Aria { get; } = $"{Root}/Aria";
@@ -160,11 +160,11 @@ namespace DownKyi.Core.Storage.Database.Download
{
Dictionary<string, object> objects = new Dictionary<string, object>();
try
dbHelper.ExecuteQuery(sql, reader =>
{
dbHelper.ExecuteQuery(sql, reader =>
while (reader.Read())
{
while (reader.Read())
try
{
// 读取字节数组
byte[] array = (byte[])reader["data"];
@@ -177,18 +177,17 @@ namespace DownKyi.Core.Storage.Database.Download
objects.Add((string)reader["id"], obj);
}
});
}
catch (Exception e)
{
Utils.Debugging.Console.PrintLine("Query()发生异常: {0}", e);
LogManager.Error($"{tableName}", e);
}
catch (Exception e)
{
Utils.Debugging.Console.PrintLine("Query()发生异常: {0}", e);
LogManager.Error($"{tableName}", e);
}
}
});
return objects;
}
/// <summary>
/// 如果表不存在则创建表
/// </summary>
+3 -3
View File
@@ -3,12 +3,12 @@
public class AppInfo
{
public string Name { get; } = "哔哩下载姬";
public int VersionCode { get; } = 502;
public int VersionCode { get; } = 503;
#if DEBUG
public string VersionName { get; } = "1.5.0-alpha3 Debug";
public string VersionName { get; } = "1.5.0-alpha4 Debug";
#else
public string VersionName { get; } = "1.5.0-alpha3";
public string VersionName { get; } = "1.5.0-alpha4";
#endif
}
+5 -1
View File
@@ -1,4 +1,4 @@
using DownKyi.Core.BiliApi.VideoStream;
using DownKyi.Core.BiliApi.VideoStream;
using System;
using System.Collections.Generic;
@@ -11,6 +11,7 @@ namespace DownKyi.Models
{
// 初始化下载的文件列表
DownloadFiles = new Dictionary<string, string>();
DownloadedFiles = new List<string>();
}
// Aria相关
@@ -19,6 +20,9 @@ namespace DownKyi.Models
// 下载的文件
public Dictionary<string, string> DownloadFiles { get; set; }
// 已下载的文件
public List<string> DownloadedFiles { get; set; }
// 视频类别
public PlayStreamType PlayStreamType { get; set; }
@@ -1,4 +1,4 @@
using DownKyi.Core.Aria2cNet;
using DownKyi.Core.Aria2cNet;
using DownKyi.Core.Aria2cNet.Client;
using DownKyi.Core.Aria2cNet.Client.Entity;
using DownKyi.Core.Aria2cNet.Server;
@@ -30,10 +30,13 @@ namespace DownKyi.Services.Download
/// </summary>
public class AriaDownloadService : DownloadService, IDownloadService
{
private Task workTask;
private CancellationTokenSource tokenSource;
private CancellationToken cancellationToken;
private List<Task> downloadingTasks = new List<Task>();
private int retry = 5;
private string nullMark = "<null>";
private readonly int retry = 5;
private readonly string nullMark = "<null>";
public AriaDownloadService(ObservableCollection<DownloadingItem> downloadingList, ObservableCollection<DownloadedItem> downloadedList) : base(downloadingList, downloadedList)
{
@@ -141,6 +144,13 @@ namespace DownKyi.Services.Download
string fileName = Guid.NewGuid().ToString("N");
string key = $"{downloadVideo.Id}_{downloadVideo.Codecs}";
// 老版本数据库没有这一项,会变成null
if (downloading.Downloading.DownloadedFiles == null)
downloading.Downloading.DownloadedFiles = new List<string>();
// 还要检查一下文件有没有被人删掉,删掉的话重新下载
// 如果下载视频之后音频文件被人删了。此时gid还是视频的,会下错文件
if (downloading.Downloading.DownloadedFiles.Contains(key) && File.Exists(Path.Combine(path, fileName)))
return Path.Combine(path, fileName);
if (downloading.Downloading.DownloadFiles.ContainsKey(key))
{
// 如果存在,表示下载过,
@@ -155,6 +165,9 @@ namespace DownKyi.Services.Download
downloading.Downloading.DownloadFiles.Add(key, fileName);
}
catch (ArgumentException) { }
// Gid最好能是每个文件单独存储,现在复用有可能会混
// 不过好消息是下载是按固定顺序的,而且下载了两个音频会混流不过
downloading.Downloading.Gid = null;
}
// 开始下载
@@ -162,6 +175,8 @@ namespace DownKyi.Services.Download
switch (downloadStatus)
{
case DownloadResult.SUCCESS:
downloading.Downloading.DownloadedFiles.Add(key);
downloading.Downloading.Gid = null;
return Path.Combine(path, fileName);
case DownloadResult.FAILED:
case DownloadResult.ABORT:
@@ -410,10 +425,17 @@ namespace DownKyi.Services.Download
}
/// <summary>
/// 停止下载服务
/// 停止下载服务(转换await和Task.Wait两种调用形式)
/// </summary>
public void End()
private async Task EndTask()
{
// 结束任务
tokenSource.Cancel();
await workTask;
//先简单等待一下
// 下载数据存储服务
DownloadStorageService downloadStorageService = new DownloadStorageService();
// 保存数据
@@ -432,7 +454,7 @@ namespace DownKyi.Services.Download
case DownloadStatus.DOWNLOADING:
// TODO 添加设置让用户选择重启后是否自动开始下载
item.Downloading.DownloadStatus = DownloadStatus.WAIT_FOR_DOWNLOAD;
item.Downloading.DownloadStatus = DownloadStatus.PAUSE;
//item.Downloading.DownloadStatus = DownloadStatus.PAUSE;
break;
case DownloadStatus.DOWNLOAD_SUCCEED:
case DownloadStatus.DOWNLOAD_FAILED:
@@ -451,29 +473,35 @@ namespace DownKyi.Services.Download
}
// 关闭Aria服务器
CloseAriaServer();
await CloseAriaServer();
}
// 结束任务
tokenSource.Cancel();
/// <summary>
/// 停止下载服务
/// </summary>
public void End()
{
Task.Run(EndTask).Wait();
}
/// <summary>
/// 启动下载服务
/// </summary>
public async void Start()
public void Start()
{
// 启动Aria服务器
StartAriaServer();
await Task.Run(DoWork, (tokenSource = new CancellationTokenSource()).Token);
tokenSource = new CancellationTokenSource();
cancellationToken = tokenSource.Token;
workTask = Task.Run(DoWork);
}
/// <summary>
/// 执行任务
/// </summary>
private void DoWork()
private async Task DoWork()
{
CancellationToken cancellationToken = tokenSource.Token;
while (true)
{
int maxDownloading = SettingsManager.GetInstance().GetAriaMaxConcurrentDownloads();
@@ -481,6 +509,7 @@ namespace DownKyi.Services.Download
try
{
downloadingTasks.RemoveAll((m) => m.IsCompleted);
foreach (DownloadingItem downloading in downloadingList)
{
if (downloading.Downloading.DownloadStatus == DownloadStatus.DOWNLOADING)
@@ -499,7 +528,9 @@ namespace DownKyi.Services.Download
// 开始下载
if (downloading.Downloading.DownloadStatus == DownloadStatus.NOT_STARTED || downloading.Downloading.DownloadStatus == DownloadStatus.WAIT_FOR_DOWNLOAD)
{
SingleDownload(downloading);
//这里需要立刻设置状态,否则如果SingleDownload没有及时执行,会重复创建任务
downloading.Downloading.DownloadStatus = DownloadStatus.DOWNLOADING;
downloadingTasks.Add(SingleDownload(downloading));
downloadingCount++;
}
}
@@ -524,7 +555,14 @@ namespace DownKyi.Services.Download
}
// 降低CPU占用
Thread.Sleep(500);
await Task.Delay(500);
}
await Task.WhenAny(Task.WhenAll(downloadingTasks), Task.Delay(30000));
foreach (Task tsk in downloadingTasks.FindAll((m) => !m.IsCompleted))
{
Core.Utils.Debugging.Console.PrintLine("AriaDownloadService: 任务结束超时");
LogManager.Debug(Tag, "任务结束超时");
}
}
@@ -533,7 +571,7 @@ namespace DownKyi.Services.Download
/// </summary>
/// <param name="downloading"></param>
/// <returns></returns>
private async void SingleDownload(DownloadingItem downloading)
private async Task SingleDownload(DownloadingItem downloading)
{
// 路径
string[] temp = downloading.DownloadBase.FilePath.Split('/');
@@ -544,256 +582,221 @@ namespace DownKyi.Services.Download
Directory.CreateDirectory(path);
}
await Task.Run(new Action(() =>
try
{
// 初始化
downloading.DownloadStatusTitle = string.Empty;
downloading.DownloadContent = string.Empty;
//downloading.Downloading.DownloadFiles.Clear();
// 解析并依次下载音频、视频、弹幕、字幕、封面等内容
Parse(downloading);
// 设置下载状态
// 必须在解析之后设置为DOWNLOADING
downloading.Downloading.DownloadStatus = DownloadStatus.DOWNLOADING;
// 暂停
Pause(downloading);
// 是否存在
var isExist = IsExist(downloading);
if (!isExist.Result)
await Task.Run(new Action(() =>
{
return;
}
// 初始化
downloading.DownloadStatusTitle = string.Empty;
downloading.DownloadContent = string.Empty;
//downloading.Downloading.DownloadFiles.Clear();
string audioUid = null;
// 如果需要下载音频
if (downloading.DownloadBase.NeedDownloadContent["downloadAudio"])
{
//audioUid = DownloadAudio(downloading);
for (int i = 0; i < retry; i++)
// 解析并依次下载音频、视频、弹幕、字幕、封面等内容
Parse(downloading);
// 暂停
Pause(downloading);
string audioUid = null;
// 如果需要下载音频
if (downloading.DownloadBase.NeedDownloadContent["downloadAudio"])
{
audioUid = DownloadAudio(downloading);
if (audioUid != null && audioUid != "<null>")
//audioUid = DownloadAudio(downloading);
for (int i = 0; i < retry; i++)
{
break;
}
}
}
if (audioUid == "<null>")
{
DownloadFailed(downloading);
return;
}
// 暂停
Pause(downloading);
// 是否存在
isExist = IsExist(downloading);
if (!isExist.Result)
{
return;
}
string videoUid = null;
// 如果需要下载视频
if (downloading.DownloadBase.NeedDownloadContent["downloadVideo"])
{
//videoUid = DownloadVideo(downloading);
for (int i = 0; i < retry; i++)
{
videoUid = DownloadVideo(downloading);
if (videoUid != null && videoUid != "<null>")
{
break;
}
}
}
if (videoUid == "<null>")
{
DownloadFailed(downloading);
return;
}
// 暂停
Pause(downloading);
// 是否存在
isExist = IsExist(downloading);
if (!isExist.Result)
{
return;
}
string outputDanmaku = null;
// 如果需要下载弹幕
if (downloading.DownloadBase.NeedDownloadContent["downloadDanmaku"])
{
outputDanmaku = DownloadDanmaku(downloading);
}
// 暂停
Pause(downloading);
// 是否存在
isExist = IsExist(downloading);
if (!isExist.Result)
{
return;
}
List<string> outputSubtitles = null;
// 如果需要下载字幕
if (downloading.DownloadBase.NeedDownloadContent["downloadSubtitle"])
{
outputSubtitles = DownloadSubtitle(downloading);
}
// 暂停
Pause(downloading);
// 是否存在
isExist = IsExist(downloading);
if (!isExist.Result)
{
return;
}
string outputCover = null;
string outputPageCover = null;
// 如果需要下载封面
if (downloading.DownloadBase.NeedDownloadContent["downloadCover"])
{
string fileName = $"{downloading.DownloadBase.FilePath}.{GetImageExtension(downloading.DownloadBase.PageCoverUrl)}";
// page的封面
outputPageCover = DownloadCover(downloading, downloading.DownloadBase.PageCoverUrl, fileName);
// 封面
outputCover = DownloadCover(downloading, downloading.DownloadBase.CoverUrl, $"{path}/Cover.{GetImageExtension(downloading.DownloadBase.CoverUrl)}");
}
// 暂停
Pause(downloading);
// 是否存在
isExist = IsExist(downloading);
if (!isExist.Result)
{
return;
}
// 混流
string outputMedia = string.Empty;
if (downloading.DownloadBase.NeedDownloadContent["downloadAudio"] || downloading.DownloadBase.NeedDownloadContent["downloadVideo"])
{
outputMedia = MixedFlow(downloading, audioUid, videoUid);
}
// 暂停
//Pause(downloading);
// 是否存在
isExist = IsExist(downloading);
if (!isExist.Result)
{
return;
}
// 检测音频、视频是否下载成功
bool isMediaSuccess = true;
if (downloading.DownloadBase.NeedDownloadContent["downloadAudio"] || downloading.DownloadBase.NeedDownloadContent["downloadVideo"])
{
// 只有下载音频不下载视频时才输出aac
// 只要下载视频就输出mp4
if (File.Exists(outputMedia))
{
// 成功
isMediaSuccess = true;
}
else
{
isMediaSuccess = false;
}
}
// 检测弹幕是否下载成功
bool isDanmakuSuccess = true;
if (downloading.DownloadBase.NeedDownloadContent["downloadDanmaku"])
{
if (File.Exists(outputDanmaku))
{
// 成功
isDanmakuSuccess = true;
}
else
{
isDanmakuSuccess = false;
}
}
// 检测字幕是否下载成功
bool isSubtitleSuccess = true;
if (downloading.DownloadBase.NeedDownloadContent["downloadSubtitle"])
{
if (outputSubtitles == null)
{
// 为null时表示不存在字幕
}
else
{
foreach (string subtitle in outputSubtitles)
{
if (!File.Exists(subtitle))
audioUid = DownloadAudio(downloading);
if (audioUid != null && audioUid != nullMark)
{
// 如果有一个不存在则失败
isSubtitleSuccess = false;
break;
}
}
}
}
// 检测封面是否下载成功
bool isCover = true;
if (downloading.DownloadBase.NeedDownloadContent["downloadCover"])
{
if (File.Exists(outputCover) || File.Exists(outputPageCover))
if (audioUid == nullMark)
{
// 成功
isCover = true;
DownloadFailed(downloading);
return;
}
else
// 暂停
Pause(downloading);
string videoUid = null;
// 如果需要下载视频
if (downloading.DownloadBase.NeedDownloadContent["downloadVideo"])
{
isCover = false;
//videoUid = DownloadVideo(downloading);
for (int i = 0; i < retry; i++)
{
videoUid = DownloadVideo(downloading);
if (videoUid != null && videoUid != nullMark)
{
break;
}
}
}
if (videoUid == nullMark)
{
DownloadFailed(downloading);
return;
}
}
if (!isMediaSuccess || !isDanmakuSuccess || !isSubtitleSuccess || !isCover)
{
DownloadFailed(downloading);
return;
}
// 暂停
Pause(downloading);
// 下载完成后处理
Downloaded downloaded = new Downloaded
{
MaxSpeedDisplay = Format.FormatSpeed(downloading.Downloading.MaxSpeed),
};
// 设置完成时间
downloaded.SetFinishedTimestamp(new DateTimeOffset(DateTime.UtcNow).ToUnixTimeSeconds());
string outputDanmaku = null;
// 如果需要下载弹幕
if (downloading.DownloadBase.NeedDownloadContent["downloadDanmaku"])
{
outputDanmaku = DownloadDanmaku(downloading);
}
DownloadedItem downloadedItem = new DownloadedItem
{
DownloadBase = downloading.DownloadBase,
Downloaded = downloaded
};
// 暂停
Pause(downloading);
App.PropertyChangeAsync(new Action(() =>
{
// 加入到下载完成list中,并从下载中list去除
downloadedList.Add(downloadedItem);
downloadingList.Remove(downloading);
List<string> outputSubtitles = null;
// 如果需要下载字幕
if (downloading.DownloadBase.NeedDownloadContent["downloadSubtitle"])
{
outputSubtitles = DownloadSubtitle(downloading);
}
// 下载完成列表排序
DownloadFinishedSort finishedSort = SettingsManager.GetInstance().GetDownloadFinishedSort();
App.SortDownloadedList(finishedSort);
// 暂停
Pause(downloading);
string outputCover = null;
string outputPageCover = null;
// 如果需要下载封面
if (downloading.DownloadBase.NeedDownloadContent["downloadCover"])
{
string fileName = $"{downloading.DownloadBase.FilePath}.{GetImageExtension(downloading.DownloadBase.PageCoverUrl)}";
// page的封面
outputPageCover = DownloadCover(downloading, downloading.DownloadBase.PageCoverUrl, fileName);
// 封面
outputCover = DownloadCover(downloading, downloading.DownloadBase.CoverUrl, $"{path}/Cover.{GetImageExtension(downloading.DownloadBase.CoverUrl)}");
}
// 暂停
Pause(downloading);
// 混流
string outputMedia = string.Empty;
if (downloading.DownloadBase.NeedDownloadContent["downloadAudio"] || downloading.DownloadBase.NeedDownloadContent["downloadVideo"])
{
outputMedia = MixedFlow(downloading, audioUid, videoUid);
}
// 这里本来只有IsExist,没有pause,不知道怎么处理
// 是否存在
//isExist = IsExist(downloading);
//if (!isExist.Result)
//{
// return;
//}
// 检测音频、视频是否下载成功
bool isMediaSuccess = true;
if (downloading.DownloadBase.NeedDownloadContent["downloadAudio"] || downloading.DownloadBase.NeedDownloadContent["downloadVideo"])
{
// 只有下载音频不下载视频时才输出aac
// 只要下载视频就输出mp4
if (File.Exists(outputMedia))
{
// 成功
isMediaSuccess = true;
}
else
{
isMediaSuccess = false;
}
}
// 检测弹幕是否下载成功
bool isDanmakuSuccess = true;
if (downloading.DownloadBase.NeedDownloadContent["downloadDanmaku"])
{
if (File.Exists(outputDanmaku))
{
// 成功
isDanmakuSuccess = true;
}
else
{
isDanmakuSuccess = false;
}
}
// 检测字幕是否下载成功
bool isSubtitleSuccess = true;
if (downloading.DownloadBase.NeedDownloadContent["downloadSubtitle"])
{
if (outputSubtitles == null)
{
// 为null时表示不存在字幕
}
else
{
foreach (string subtitle in outputSubtitles)
{
if (!File.Exists(subtitle))
{
// 如果有一个不存在则失败
isSubtitleSuccess = false;
}
}
}
}
// 检测封面是否下载成功
bool isCover = true;
if (downloading.DownloadBase.NeedDownloadContent["downloadCover"])
{
if (File.Exists(outputCover) || File.Exists(outputPageCover))
{
// 成功
isCover = true;
}
else
{
isCover = false;
}
}
if (!isMediaSuccess || !isDanmakuSuccess || !isSubtitleSuccess || !isCover)
{
DownloadFailed(downloading);
return;
}
// 下载完成后处理
Downloaded downloaded = new Downloaded
{
MaxSpeedDisplay = Format.FormatSpeed(downloading.Downloading.MaxSpeed),
};
// 设置完成时间
downloaded.SetFinishedTimestamp(new DateTimeOffset(DateTime.UtcNow).ToUnixTimeSeconds());
DownloadedItem downloadedItem = new DownloadedItem
{
DownloadBase = downloading.DownloadBase,
Downloaded = downloaded
};
App.PropertyChangeAsync(new Action(() =>
{
// 加入到下载完成list中,并从下载中list去除
downloadedList.Add(downloadedItem);
downloadingList.Remove(downloading);
// 下载完成列表排序
DownloadFinishedSort finishedSort = SettingsManager.GetInstance().GetDownloadFinishedSort();
App.SortDownloadedList(finishedSort);
}));
}));
}));
}
catch (OperationCanceledException)
{
}
}
/// <summary>
@@ -836,33 +839,19 @@ namespace DownKyi.Services.Download
/// <param name="downloading"></param>
private void Pause(DownloadingItem downloading)
{
string oldStatus = downloading.DownloadStatusTitle;
cancellationToken.ThrowIfCancellationRequested();
downloading.DownloadStatusTitle = DictionaryResource.GetString("Pausing");
while (downloading.Downloading.DownloadStatus == DownloadStatus.PAUSE)
if (downloading.Downloading.DownloadStatus == DownloadStatus.PAUSE)
{
// 降低CPU占用
Thread.Sleep(100);
throw new OperationCanceledException("Stop thread by pause");
}
downloading.DownloadStatusTitle = DictionaryResource.GetString("Waiting");
int maxDownloading = SettingsManager.GetInstance().GetAriaMaxConcurrentDownloads();
int downloadingCount;
do
// 是否存在
var isExist = IsExist(downloading);
if (!isExist.Result)
{
downloadingCount = 0;
foreach (DownloadingItem item in downloadingList)
{
if (item.Downloading.DownloadStatus == DownloadStatus.DOWNLOADING)
{
downloadingCount++;
}
}
// 降低CPU占用
Thread.Sleep(100);
} while (downloadingCount > maxDownloading);
downloading.DownloadStatusTitle = oldStatus;
throw new OperationCanceledException("Task is deleted");
}
}
/// <summary>
@@ -926,26 +915,28 @@ namespace DownKyi.Services.Download
};
var task = await AriaServer.StartServerAsync(config);
if (task) { Console.WriteLine("Start ServerAsync Completed"); }
for (int i = 0; i < 10; i++)
{
var globOpt = await AriaClient.GetGlobalOptionAsync();
if (globOpt != null)
break;
await Task.Delay(1000);
}
Console.WriteLine("Start ServerAsync end");
}
/// <summary>
/// 关闭Aria服务器
/// </summary>
private void CloseAriaServer()
private async Task CloseAriaServer()
{
new Thread(() =>
{
// 暂停所有下载
var ariaPause = AriaClient.PauseAllAsync();
Core.Utils.Debugging.Console.PrintLine(ariaPause.ToString());
// 暂停所有下载
var ariaPause = await AriaClient.PauseAllAsync();
Core.Utils.Debugging.Console.PrintLine(ariaPause.ToString());
// 关闭服务器
bool close = AriaServer.CloseServer();
Core.Utils.Debugging.Console.PrintLine(close);
})
{ IsBackground = false }
.Start();
// 关闭服务器
bool close = AriaServer.CloseServer();
Core.Utils.Debugging.Console.PrintLine(close);
}
/// <summary>
@@ -958,39 +949,65 @@ namespace DownKyi.Services.Download
// 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()
};
//检查gid对应任务,如果已创建那么直接使用
//但是代理设置会出现不能随时更新的问题
// 如果设置了代理,则增加HttpProxy
if (SettingsManager.GetInstance().IsAriaHttpProxy() == AllowStatus.YES)
if (downloading.Downloading.Gid != null)
{
option.HttpProxy = $"http://{SettingsManager.GetInstance().GetAriaHttpProxy()}:{SettingsManager.GetInstance().GetAriaHttpProxyListenPort()}";
Task<AriaTellStatus> status = AriaClient.TellStatus(downloading.Downloading.Gid);
if (status == null || status.Result == null)
downloading.Downloading.Gid = null;
else if (status.Result.Result == null && status.Result.Error != null)
{
if (status.Result.Error.Message.Contains("is not found"))
{
downloading.Downloading.Gid = null;
}
}
}
// 添加一个下载
Task<AriaAddUri> ariaAddUri = AriaClient.AddUriAsync(urls, option);
if (ariaAddUri == null || ariaAddUri.Result == null || ariaAddUri.Result.Result == null)
if (downloading.Downloading.Gid == null)
{
return DownloadResult.FAILED;
}
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()
};
// 保存gid
string gid = ariaAddUri.Result.Result;
downloading.Downloading.Gid = gid;
// 如果设置了代理,则增加HttpProxy
if (SettingsManager.GetInstance().IsAriaHttpProxy() == AllowStatus.YES)
{
option.HttpProxy = $"http://{SettingsManager.GetInstance().GetAriaHttpProxy()}:{SettingsManager.GetInstance().GetAriaHttpProxyListenPort()}";
}
// 添加一个下载
Task<AriaAddUri> 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.Downloading.Gid = gid;
}
else
{
Task<AriaPause> ariaUnpause = AriaClient.UnpauseAsync(downloading.Downloading.Gid);
}
// 管理下载
AriaManager ariaManager = new AriaManager();
ariaManager.TellStatus += AriaTellStatus;
ariaManager.DownloadFinish += AriaDownloadFinish;
return ariaManager.GetDownloadStatus(gid, new Action(() =>
return ariaManager.GetDownloadStatus(downloading.Downloading.Gid, new Action(() =>
{
cancellationToken.ThrowIfCancellationRequested();
switch (downloading.Downloading.DownloadStatus)
{
case DownloadStatus.PAUSE:
@@ -999,7 +1016,6 @@ namespace DownKyi.Services.Download
Pause(downloading);
break;
case DownloadStatus.DOWNLOADING:
Task<AriaPause> ariaUnpause = AriaClient.UnpauseAsync(downloading.Downloading.Gid);
break;
}
}));
@@ -84,6 +84,8 @@ namespace DownKyi.ViewModels.DownloadManager
/// </summary>
private void ExecuteOpenFolderCommand()
{
if (DownloadBase == null) { return; }
string videoPath = $"{DownloadBase.FilePath}.mp4";
FileInfo fileInfo = new FileInfo(videoPath);
if (File.Exists(fileInfo.FullName))
@@ -105,6 +107,8 @@ namespace DownKyi.ViewModels.DownloadManager
/// </summary>
private void ExecuteOpenVideoCommand()
{
if (DownloadBase == null) { return; }
string videoPath = $"{DownloadBase.FilePath}.mp4";
var fileInfo = new FileInfo(videoPath);
if (File.Exists(fileInfo.FullName))
+23 -2
View File
@@ -12,6 +12,7 @@ using Prism.Events;
using Prism.Regions;
using System;
using System.Collections.ObjectModel;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Media.Imaging;
@@ -22,6 +23,8 @@ namespace DownKyi.ViewModels
{
public const string Tag = "PageMySpace";
private CancellationTokenSource tokenSource;
// mid
private long mid = -1;
@@ -291,6 +294,9 @@ namespace DownKyi.ViewModels
/// </summary>
private void ExecuteBackSpace()
{
// 结束任务
tokenSource?.Cancel();
NavigationParam parameter = new NavigationParam
{
ViewName = ParentView,
@@ -415,6 +421,7 @@ namespace DownKyi.ViewModels
/// </summary>
private async void UpdateSpaceInfo()
{
bool isCancel = false;
bool isNoData = true;
Uri toutuUri = null;
string headerUri = null;
@@ -423,6 +430,8 @@ namespace DownKyi.ViewModels
await Task.Run(() =>
{
CancellationToken cancellationToken = tokenSource.Token;
// 背景图片
SpaceSettings spaceSettings = Core.BiliApi.Users.UserSpace.GetSpaceSettings(mid);
if (spaceSettings != null)
@@ -505,7 +514,19 @@ namespace DownKyi.ViewModels
// 没有数据
isNoData = true;
}
});
// 判断是否该结束线程
if (cancellationToken.IsCancellationRequested)
{
isCancel = true;
}
}, (tokenSource = new CancellationTokenSource()).Token);
// 是否该结束线程
if (isCancel)
{
return;
}
// 是否获取到数据
if (isNoData)
@@ -528,7 +549,7 @@ namespace DownKyi.ViewModels
// 性别
Sex = sexUri == null ? null : new BitmapImage(sexUri);
// 等级
Level = new BitmapImage(levelUri);
Level = levelUri == null ? null : new BitmapImage(levelUri);
ArrowBack.Fill = DictionaryResource.GetColor("ColorText");
Logout.Fill = DictionaryResource.GetColor("ColorText");
@@ -353,7 +353,7 @@ namespace DownKyi.ViewModels
// 性别
Sex = sexUri == null ? null : new BitmapImage(sexUri);
// 等级
Level = new BitmapImage(levelUri);
Level = levelUri == null ? null : new BitmapImage(levelUri);
ArrowBack.Fill = DictionaryResource.GetColor("ColorText");
TopNavigationBg = DictionaryResource.GetColor("ColorMask100");