diff --git a/src/DownKyi.Core/BiliApi/BiliUtils/Constant.cs b/src/DownKyi.Core/BiliApi/BiliUtils/Constant.cs index 23dba62b..c8a3fa2c 100644 --- a/src/DownKyi.Core/BiliApi/BiliUtils/Constant.cs +++ b/src/DownKyi.Core/BiliApi/BiliUtils/Constant.cs @@ -19,12 +19,20 @@ public static class Constant new Quality { Name = "360P 流畅", Id = 16 }, }; + private static readonly List codecIds = new List + { + new Quality { Name = "H.264/AVC", Id = 7 }, + new Quality { Name = "H.265/HEVC", Id = 12 }, + new Quality { Name = "AV1", Id = 13 }, + }; + private static readonly List qualities = new List { new Quality { Name = "64K", Id = 30216 }, new Quality { Name = "132K", Id = 30232 }, new Quality { Name = "192K", Id = 30280 }, new Quality { Name = "Dolby Atmos", Id = 30250 }, + new Quality { Name = "Hi-Res无损", Id = 30251 }, }; /// @@ -39,6 +47,18 @@ public static List GetResolutions() return new List(resolutions); } + /// + /// 获取视频编码代码 + /// + /// + public static List GetCodecIds() + { + // 使用深复制, + // 保证外部修改list后, + // 不会影响其他调用处 + return new List(codecIds); + } + /// /// 获取支持的视频音质 /// diff --git a/src/DownKyi.Core/BiliApi/VideoStream/Models/PlayUrlDash.cs b/src/DownKyi.Core/BiliApi/VideoStream/Models/PlayUrlDash.cs index ff545d1a..e81a9ffc 100644 --- a/src/DownKyi.Core/BiliApi/VideoStream/Models/PlayUrlDash.cs +++ b/src/DownKyi.Core/BiliApi/VideoStream/Models/PlayUrlDash.cs @@ -18,5 +18,7 @@ public class PlayUrlDash : BaseModel public List Audio { get; set; } [JsonProperty("dolby")] public PlayUrlDashDolby Dolby { get; set; } + [JsonProperty("flac")] + public PlayUrlDashFlac Flac { get; set; } } } diff --git a/src/DownKyi.Core/BiliApi/VideoStream/Models/PlayUrlDashFlac.cs b/src/DownKyi.Core/BiliApi/VideoStream/Models/PlayUrlDashFlac.cs new file mode 100644 index 00000000..4c54786d --- /dev/null +++ b/src/DownKyi.Core/BiliApi/VideoStream/Models/PlayUrlDashFlac.cs @@ -0,0 +1,12 @@ +using DownKyi.Core.BiliApi.Models; +using Newtonsoft.Json; + +namespace DownKyi.Core.BiliApi.VideoStream.Models +{ + public class PlayUrlDashFlac : BaseModel + { + [JsonProperty("audio")] + public PlayUrlDashVideo Audio { get; set; } + //bool display { get; set; } + } +} diff --git a/src/DownKyi.Core/BiliApi/VideoStream/Models/PlayUrlDashVideo.cs b/src/DownKyi.Core/BiliApi/VideoStream/Models/PlayUrlDashVideo.cs index 098f9bca..fab80440 100644 --- a/src/DownKyi.Core/BiliApi/VideoStream/Models/PlayUrlDashVideo.cs +++ b/src/DownKyi.Core/BiliApi/VideoStream/Models/PlayUrlDashVideo.cs @@ -30,6 +30,7 @@ public class PlayUrlDashVideo : BaseModel // start_with_sap // SegmentBase // segment_base - // codecid + [JsonProperty("codecid")] + public int CodecId { get; set; } } } diff --git a/src/DownKyi.Core/DownKyi.Core.csproj b/src/DownKyi.Core/DownKyi.Core.csproj index ba9c2767..0c61a495 100644 --- a/src/DownKyi.Core/DownKyi.Core.csproj +++ b/src/DownKyi.Core/DownKyi.Core.csproj @@ -249,6 +249,7 @@ + diff --git a/src/DownKyi.Core/FFmpeg/FFmpegHelper.cs b/src/DownKyi.Core/FFmpeg/FFmpegHelper.cs index 1eb34fba..32d23955 100644 --- a/src/DownKyi.Core/FFmpeg/FFmpegHelper.cs +++ b/src/DownKyi.Core/FFmpeg/FFmpegHelper.cs @@ -34,15 +34,19 @@ static FFmpegHelper() /// public static bool MergeVideo(string video1, string video2, string destVideo) { - string param = $"-y -i \"{video1}\" -i \"{video2}\" -acodec copy -vcodec copy -f mp4 \"{destVideo}\""; + string param = $"-y -i \"{video1}\" -i \"{video2}\" -strict -2 -acodec copy -vcodec copy -f mp4 \"{destVideo}\""; if (video1 == null || !File.Exists(video1)) { - param = $"-y -i \"{video2}\" -acodec copy -vcodec copy -f mp4 \"{destVideo}\""; + param = $"-y -i \"{video2}\" -strict -2 -acodec copy -vcodec copy -f mp4 \"{destVideo}\""; } if (video2 == null || !File.Exists(video2)) { - param = $"-y -i \"{video1}\" -acodec copy \"{destVideo}\""; + param = $"-y -i \"{video1}\" -strict -2 -acodec copy \"{destVideo}\""; } + + // 支持flac格式音频 + //param += " -strict -2"; + if (!File.Exists(video1) && !File.Exists(video2)) { return false; } // 如果存在 diff --git a/src/DownKyi.Core/Settings/Models/VideoSettings.cs b/src/DownKyi.Core/Settings/Models/VideoSettings.cs index 0cf431ef..0eeec390 100644 --- a/src/DownKyi.Core/Settings/Models/VideoSettings.cs +++ b/src/DownKyi.Core/Settings/Models/VideoSettings.cs @@ -8,7 +8,7 @@ namespace DownKyi.Core.Settings.Models /// public class VideoSettings { - public VideoCodecs VideoCodecs { get; set; } = VideoCodecs.NONE; // AVC or HEVC + public int VideoCodecs { get; set; } = -1; // AVC or HEVC public int Quality { get; set; } = -1; // 画质 public int AudioQuality { get; set; } = -1; // 音质 public AllowStatus IsTranscodingFlvToMp4 { get; set; } = AllowStatus.NONE; // 是否将flv转为mp4 diff --git a/src/DownKyi.Core/Settings/SettingsManager.Video.cs b/src/DownKyi.Core/Settings/SettingsManager.Video.cs index 7a88cc40..c8133f85 100644 --- a/src/DownKyi.Core/Settings/SettingsManager.Video.cs +++ b/src/DownKyi.Core/Settings/SettingsManager.Video.cs @@ -9,7 +9,7 @@ namespace DownKyi.Core.Settings public partial class SettingsManager { // 设置优先下载的视频编码 - private readonly VideoCodecs videoCodecs = VideoCodecs.AVC; + private readonly int videoCodecs = 7; // 设置优先下载画质 private readonly int quality = 120; @@ -58,10 +58,10 @@ public partial class SettingsManager /// 获取优先下载的视频编码 /// /// - public VideoCodecs GetVideoCodecs() + public int GetVideoCodecs() { appSettings = GetSettings(); - if (appSettings.Video.VideoCodecs == VideoCodecs.NONE) + if (appSettings.Video.VideoCodecs == -1) { // 第一次获取,先设置默认值 SetVideoCodecs(videoCodecs); @@ -75,7 +75,7 @@ public VideoCodecs GetVideoCodecs() /// /// /// - public bool SetVideoCodecs(VideoCodecs videoCodecs) + public bool SetVideoCodecs(int videoCodecs) { appSettings.Video.VideoCodecs = videoCodecs; return SetSettings(); diff --git a/src/DownKyi/Services/Download/DownloadService.cs b/src/DownKyi/Services/Download/DownloadService.cs index 2af551bb..49a7c604 100644 --- a/src/DownKyi/Services/Download/DownloadService.cs +++ b/src/DownKyi/Services/Download/DownloadService.cs @@ -1,4 +1,5 @@ -using DownKyi.Core.BiliApi.VideoStream; +using DownKyi.Core.BiliApi.BiliUtils; +using DownKyi.Core.BiliApi.VideoStream; using DownKyi.Core.BiliApi.VideoStream.Models; using DownKyi.Core.Danmaku2Ass; using DownKyi.Core.FFmpeg; @@ -14,6 +15,7 @@ using System.Collections.Generic; using System.Collections.ObjectModel; using System.IO; +using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -82,6 +84,11 @@ protected PlayUrlDashVideo BaseDownloadAudio(DownloadingItem downloading) { downloadAudio = downloading.PlayUrl.Dash.Dolby.Audio[0]; } + // Hi-Res无损 + if (downloading.AudioCodec.Id == 30251) + { + downloadAudio = downloading.PlayUrl.Dash.Flac.Audio; + } } catch (Exception) { } @@ -110,7 +117,8 @@ protected PlayUrlDashVideo BaseDownloadVideo(DownloadingItem downloading) PlayUrlDashVideo downloadVideo = null; foreach (PlayUrlDashVideo video in downloading.PlayUrl.Dash.Video) { - if (video.Id == downloading.Resolution.Id && Utils.GetVideoCodecName(video.Codecs) == downloading.VideoCodecName) + Quality codecs = Constant.GetCodecIds().FirstOrDefault(t => t.Id == video.CodecId); + if (video.Id == downloading.Resolution.Id && codecs.Name == downloading.VideoCodecName) { downloadVideo = video; break; diff --git a/src/DownKyi/Services/Utils.cs b/src/DownKyi/Services/Utils.cs index 6161ba65..b3e055ad 100644 --- a/src/DownKyi/Services/Utils.cs +++ b/src/DownKyi/Services/Utils.cs @@ -30,7 +30,7 @@ internal static void VideoPageInfo(PlayUrl playUrl, VideoPage page) // 获取设置 UserInfoSettings userInfo = SettingsManager.GetInstance().GetUserInfo(); int defaultQuality = SettingsManager.GetInstance().GetQuality(); - VideoCodecs videoCodecs = SettingsManager.GetInstance().GetVideoCodecs(); + int videoCodecs = SettingsManager.GetInstance().GetVideoCodecs(); int defaultAudioQuality = SettingsManager.GetInstance().GetAudioQuality(); // 未登录时,最高仅720P @@ -132,15 +132,16 @@ private static ObservableCollection GetAudioQualityFormatList_old(PlayUr private static ObservableCollection GetAudioQualityFormatList(PlayUrl playUrl, int defaultAudioQuality) { List audioQualityFormatList = new List(); + List audioQualities = Constant.GetAudioQualities(); if (playUrl.Dash.Audio != null && playUrl.Dash.Audio.Count > 0) { foreach (PlayUrlDashVideo audio in playUrl.Dash.Audio) { - // 音质id大于设置画质时,跳过 + // 音质id大于设置音质时,跳过 if (audio.Id > defaultAudioQuality) { continue; } - Quality audioQuality = Constant.GetAudioQualities().FirstOrDefault(t => { return t.Id == audio.Id; }); + Quality audioQuality = audioQualities.FirstOrDefault(t => { return t.Id == audio.Id; }); if (audioQuality != null) { ListHelper.AddUnique(audioQualityFormatList, audioQuality.Name); @@ -148,9 +149,20 @@ private static ObservableCollection GetAudioQualityFormatList(PlayUrl pl } } - if (playUrl.Dash.Dolby.Audio != null && playUrl.Dash.Dolby.Audio.Count > 0) + if (audioQualities[3].Id <= defaultAudioQuality - 1000 && playUrl.Dash.Dolby != null) { - ListHelper.AddUnique(audioQualityFormatList, Constant.GetAudioQualities().Last().Name); + if (playUrl.Dash.Dolby.Audio != null && playUrl.Dash.Dolby.Audio.Count > 0) + { + ListHelper.AddUnique(audioQualityFormatList, audioQualities[3].Name); + } + } + + if (audioQualities[4].Id <= defaultAudioQuality - 1000 && playUrl.Dash.Flac != null) + { + if (playUrl.Dash.Flac.Audio != null) + { + ListHelper.AddUnique(audioQualityFormatList, audioQualities[4].Name); + } } audioQualityFormatList.Sort(new StringLogicalComparer()); @@ -167,9 +179,10 @@ private static ObservableCollection GetAudioQualityFormatList(PlayUrl pl /// /// /// - private static List GetVideoQualityList(PlayUrl playUrl, UserInfoSettings userInfo, int defaultQuality, VideoCodecs videoCodecs) + private static List GetVideoQualityList(PlayUrl playUrl, UserInfoSettings userInfo, int defaultQuality, int videoCodecs) { List videoQualityList = new List(); + List codeIds = Constant.GetCodecIds(); if (playUrl.Dash.Video == null) { @@ -197,7 +210,8 @@ private static List GetVideoQualityList(PlayUrl playUrl, UserInfoS // 寻找是否已存在这个画质 // 不存在则添加,存在则修改 - string codecName = GetVideoCodecName(video.Codecs); + //string codecName = GetVideoCodecName(video.Codecs); + string codecName = codeIds.FirstOrDefault(t => t.Id == video.CodecId).Name; VideoQuality videoQualityExist = videoQualityList.FirstOrDefault(t => t.Quality == video.Id); if (videoQualityExist == null) { @@ -237,24 +251,29 @@ private static List GetVideoQualityList(PlayUrl playUrl, UserInfoS } // 设置选中的视频编码 - switch (videoCodecs) + //switch (videoCodecs) + //{ + // case VideoCodecs.AVC: + // if (videoQualityList[videoQualityList.IndexOf(selectedVideoQuality)].VideoCodecList.Contains("H.264/AVC")) + // { + // videoQualityList[videoQualityList.IndexOf(selectedVideoQuality)].SelectedVideoCodec = "H.264/AVC"; + // } + // break; + // case VideoCodecs.HEVC: + // if (videoQualityList[videoQualityList.IndexOf(selectedVideoQuality)].VideoCodecList.Contains("H.265/HEVC")) + // { + // videoQualityList[videoQualityList.IndexOf(selectedVideoQuality)].SelectedVideoCodec = "H.265/HEVC"; + // } + // break; + // case VideoCodecs.NONE: + // break; + // default: + // break; + //} + string videoCodecsName = codeIds.FirstOrDefault(t => t.Id == videoCodecs).Name; + if (videoQualityList[videoQualityList.IndexOf(selectedVideoQuality)].VideoCodecList.Contains(videoCodecsName)) { - case VideoCodecs.AVC: - if (videoQualityList[videoQualityList.IndexOf(selectedVideoQuality)].VideoCodecList.Contains("H.264/AVC")) - { - videoQualityList[videoQualityList.IndexOf(selectedVideoQuality)].SelectedVideoCodec = "H.264/AVC"; - } - break; - case VideoCodecs.HEVC: - if (videoQualityList[videoQualityList.IndexOf(selectedVideoQuality)].VideoCodecList.Contains("H.265/HEVC")) - { - videoQualityList[videoQualityList.IndexOf(selectedVideoQuality)].SelectedVideoCodec = "H.265/HEVC"; - } - break; - case VideoCodecs.NONE: - break; - default: - break; + videoQualityList[videoQualityList.IndexOf(selectedVideoQuality)].SelectedVideoCodec = videoCodecsName; } } @@ -267,10 +286,10 @@ private static List GetVideoQualityList(PlayUrl playUrl, UserInfoS /// /// /// - internal static string GetVideoCodecName(string origin) - { - return origin.Contains("avc") ? "H.264/AVC" : origin.Contains("hev") ? "H.265/HEVC" : origin.Contains("dvh") || origin.Contains("hvc") ? "Dolby Vision" : ""; - } + //internal static string GetVideoCodecName(string origin) + //{ + // return origin.Contains("avc") ? "H.264/AVC" : origin.Contains("hev") ? "H.265/HEVC" : origin.Contains("dvh") || origin.Contains("hvc") ? "Dolby Vision" : ""; + //} } } diff --git a/src/DownKyi/ViewModels/Settings/ViewVideoViewModel.cs b/src/DownKyi/ViewModels/Settings/ViewVideoViewModel.cs index a7ffd52b..3007a94c 100644 --- a/src/DownKyi/ViewModels/Settings/ViewVideoViewModel.cs +++ b/src/DownKyi/ViewModels/Settings/ViewVideoViewModel.cs @@ -24,15 +24,15 @@ public class ViewVideoViewModel : BaseViewModel #region 页面属性申明 - private List videoCodecs; - public List VideoCodecs + private List videoCodecs; + public List VideoCodecs { get => videoCodecs; set => SetProperty(ref videoCodecs, value); } - private string selectedVideoCodec; - public string SelectedVideoCodec + private Quality selectedVideoCodec; + public Quality SelectedVideoCodec { get => selectedVideoCodec; set => SetProperty(ref selectedVideoCodec, value); @@ -186,18 +186,21 @@ public ViewVideoViewModel(IEventAggregator eventAggregator) : base(eventAggregat #region 属性初始化 // 优先下载的视频编码 - VideoCodecs = new List - { - "H.264/AVC", - "H.265/HEVC", - }; + VideoCodecs = Constant.GetCodecIds(); + //VideoCodecs = new List + //{ + // "H.264/AVC", + // "H.265/HEVC", + //}; // 优先下载画质 VideoQualityList = Constant.GetResolutions(); // 优先下载音质 AudioQualityList = Constant.GetAudioQualities(); - AudioQualityList.RemoveAt(3); + //AudioQualityList.RemoveAt(3); + AudioQualityList[3].Id = AudioQualityList[3].Id + 1000; + AudioQualityList[4].Id = AudioQualityList[4].Id + 1000; // 文件命名格式 SelectedFileName = new ObservableCollection(); @@ -253,8 +256,9 @@ public override void OnNavigatedTo(NavigationContext navigationContext) isOnNavigatedTo = true; // 优先下载的视频编码 - VideoCodecs videoCodecs = SettingsManager.GetInstance().GetVideoCodecs(); - SelectedVideoCodec = GetVideoCodecsString(videoCodecs); + int videoCodecs = SettingsManager.GetInstance().GetVideoCodecs(); + //SelectedVideoCodec = GetVideoCodecsString(videoCodecs); + SelectedVideoCodec = VideoCodecs.FirstOrDefault(t => { return t.Id == videoCodecs; }); // 优先下载画质 int quality = SettingsManager.GetInstance().GetQuality(); @@ -315,18 +319,20 @@ public override void OnNavigatedTo(NavigationContext navigationContext) #region 命令申明 // 优先下载的视频编码事件 - private DelegateCommand videoCodecsCommand; - public DelegateCommand VideoCodecsCommand => videoCodecsCommand ?? (videoCodecsCommand = new DelegateCommand(ExecuteVideoCodecsCommand)); + private DelegateCommand videoCodecsCommand; + public DelegateCommand VideoCodecsCommand => videoCodecsCommand ?? (videoCodecsCommand = new DelegateCommand(ExecuteVideoCodecsCommand)); /// /// 优先下载的视频编码事件 /// /// - private void ExecuteVideoCodecsCommand(string parameter) + private void ExecuteVideoCodecsCommand(object parameter) { - VideoCodecs videoCodecs = GetVideoCodecs(parameter); + //VideoCodecs videoCodecs = GetVideoCodecs(parameter); - bool isSucceed = SettingsManager.GetInstance().SetVideoCodecs(videoCodecs); + if (!(parameter is Quality videoCodecs)) { return; } + + bool isSucceed = SettingsManager.GetInstance().SetVideoCodecs(videoCodecs.Id); PublishTip(isSucceed); } @@ -674,49 +680,49 @@ private void ExecuteOrderFormatCommandCommand(object parameter) /// /// /// - private string GetVideoCodecsString(VideoCodecs videoCodecs) - { - string codec; - switch (videoCodecs) - { - case Core.Settings.VideoCodecs.NONE: - codec = ""; - break; - case Core.Settings.VideoCodecs.AVC: - codec = "H.264/AVC"; - break; - case Core.Settings.VideoCodecs.HEVC: - codec = "H.265/HEVC"; - break; - default: - codec = ""; - break; - } - return codec; - } + //private string GetVideoCodecsString(VideoCodecs videoCodecs) + //{ + // string codec; + // switch (videoCodecs) + // { + // case Core.Settings.VideoCodecs.NONE: + // codec = ""; + // break; + // case Core.Settings.VideoCodecs.AVC: + // codec = "H.264/AVC"; + // break; + // case Core.Settings.VideoCodecs.HEVC: + // codec = "H.265/HEVC"; + // break; + // default: + // codec = ""; + // break; + // } + // return codec; + //} /// /// 返回VideoCodecs /// /// /// - private VideoCodecs GetVideoCodecs(string str) - { - VideoCodecs videoCodecs; - switch (str) - { - case "H.264/AVC": - videoCodecs = Core.Settings.VideoCodecs.AVC; - break; - case "H.265/HEVC": - videoCodecs = Core.Settings.VideoCodecs.HEVC; - break; - default: - videoCodecs = Core.Settings.VideoCodecs.NONE; - break; - } - return videoCodecs; - } + //private VideoCodecs GetVideoCodecs(string str) + //{ + // VideoCodecs videoCodecs; + // switch (str) + // { + // case "H.264/AVC": + // videoCodecs = Core.Settings.VideoCodecs.AVC; + // break; + // case "H.265/HEVC": + // videoCodecs = Core.Settings.VideoCodecs.HEVC; + // break; + // default: + // videoCodecs = Core.Settings.VideoCodecs.NONE; + // break; + // } + // return videoCodecs; + //} /// /// 保存下载视频内容到设置 diff --git a/src/DownKyi/Views/Settings/ViewVideo.xaml b/src/DownKyi/Views/Settings/ViewVideo.xaml index 4511b48f..10eca519 100644 --- a/src/DownKyi/Views/Settings/ViewVideo.xaml +++ b/src/DownKyi/Views/Settings/ViewVideo.xaml @@ -23,6 +23,7 @@ Name="nameVideoCodecs" Width="120" VerticalContentAlignment="Center" + DisplayMemberPath="Name" ItemsSource="{Binding VideoCodecs}" SelectedValue="{Binding SelectedVideoCodec}"> diff --git a/src/DownKyi/Views/Toolbox/ViewExtractMedia.xaml b/src/DownKyi/Views/Toolbox/ViewExtractMedia.xaml index 13675c63..b3d01b2f 100644 --- a/src/DownKyi/Views/Toolbox/ViewExtractMedia.xaml +++ b/src/DownKyi/Views/Toolbox/ViewExtractMedia.xaml @@ -27,10 +27,10 @@ Orientation="Horizontal">