using DownKyi.Core.BiliApi.BiliUtils; using DownKyi.Core.Logging; using DownKyi.Core.Settings; using DownKyi.CustomControl; using DownKyi.Events; using DownKyi.Images; using DownKyi.Models; using DownKyi.Services; using DownKyi.Utils; using DownKyi.ViewModels.Dialogs; using Prism.Commands; using Prism.Events; using Prism.Regions; using Prism.Services.Dialogs; using System; using System.Collections.ObjectModel; using System.IO; using System.Linq; using System.Threading.Tasks; using System.Windows; namespace DownKyi.ViewModels { public class ViewVideoDetailViewModel : BaseViewModel { public const string Tag = "PageVideoDetail"; private readonly IDialogService dialogService; #region 页面属性申明 private VectorImage arrowBack; public VectorImage ArrowBack { get { return arrowBack; } set { SetProperty(ref arrowBack, value); } } private string inputText; public string InputText { get { return inputText; } set { SetProperty(ref inputText, value); } } private GifImage loading; public GifImage Loading { get { return loading; } set { SetProperty(ref loading, value); } } private Visibility loadingVisibility; public Visibility LoadingVisibility { get { return loadingVisibility; } set { SetProperty(ref loadingVisibility, value); } } private VectorImage downloadManage; public VectorImage DownloadManage { get { return downloadManage; } set { SetProperty(ref downloadManage, value); } } private VideoInfoView videoInfoView; public VideoInfoView VideoInfoView { get { return videoInfoView; } set { SetProperty(ref videoInfoView, value); } } private ObservableCollection videoSections; public ObservableCollection VideoSections { get { return videoSections; } set { SetProperty(ref videoSections, value); } } private bool isSelectAll; public bool IsSelectAll { get { return isSelectAll; } set { SetProperty(ref isSelectAll, value); } } private Visibility contentVisibility; public Visibility ContentVisibility { get { return contentVisibility; } set { SetProperty(ref contentVisibility, value); } } private Visibility noDataVisibility; public Visibility NoDataVisibility { get { return noDataVisibility; } set { SetProperty(ref noDataVisibility, value); } } #endregion public ViewVideoDetailViewModel(IEventAggregator eventAggregator, IDialogService dialogService) : base(eventAggregator) { this.dialogService = dialogService; #region 属性初始化 Loading = new GifImage(Properties.Resources.loading); Loading.StartAnimate(); LoadingVisibility = Visibility.Collapsed; ArrowBack = NavigationIcon.Instance().ArrowBack; ArrowBack.Fill = DictionaryResource.GetColor("ColorTextDark"); DownloadManage = ButtonIcon.Instance().DownloadManage; DownloadManage.Height = 24; DownloadManage.Width = 24; DownloadManage.Fill = DictionaryResource.GetColor("ColorPrimary"); VideoSections = new ObservableCollection(); #endregion } #region 命令申明 // 返回 private DelegateCommand backSpaceCommand; public DelegateCommand BackSpaceCommand => backSpaceCommand ?? (backSpaceCommand = new DelegateCommand(ExecuteBackSpace)); /// /// 返回 /// private void ExecuteBackSpace() { NavigationParam parameter = new NavigationParam { ViewName = ParentView, ParentViewName = null, Parameter = null }; eventAggregator.GetEvent().Publish(parameter); } // 前往下载管理页面 private DelegateCommand downloadManagerCommand; public DelegateCommand DownloadManagerCommand => downloadManagerCommand ?? (downloadManagerCommand = new DelegateCommand(ExecuteDownloadManagerCommand)); /// /// 前往下载管理页面 /// private void ExecuteDownloadManagerCommand() { NavigationParam parameter = new NavigationParam { ViewName = ViewDownloadManagerViewModel.Tag, ParentViewName = Tag, Parameter = null }; eventAggregator.GetEvent().Publish(parameter); } // 输入确认事件 private DelegateCommand inputCommand; public DelegateCommand InputCommand => inputCommand ?? (inputCommand = new DelegateCommand(ExecuteInputCommand, CanExecuteInputCommand)); /// /// 处理输入事件 /// private async void ExecuteInputCommand() { InitView(); try { await Task.Run(() => { if (InputText == null || InputText == string.Empty) { return; } LogManager.Debug(Tag, $"InputText: {InputText}"); // 更新页面 UnityUpdateView(UpdateView, InputText, null); // 是否自动解析视频 if (SettingsManager.GetInstance().IsAutoParseVideo() == AllowStatus.YES) { PropertyChangeAsync(ExecuteParseAllVideoCommand); } }); } catch (Exception e) { Core.Utils.Debugging.Console.PrintLine("InputCommand()发生异常: {0}", e); LogManager.Error(Tag, e); LoadingVisibility = Visibility.Collapsed; ContentVisibility = Visibility.Collapsed; NoDataVisibility = Visibility.Visible; } } /// /// 输入事件是否允许执行 /// /// private bool CanExecuteInputCommand() { return LoadingVisibility != Visibility.Visible; } // 复制封面事件 private DelegateCommand copyCoverCommand; public DelegateCommand CopyCoverCommand => copyCoverCommand ?? (copyCoverCommand = new DelegateCommand(ExecuteCopyCoverCommand)); /// /// 复制封面事件 /// private void ExecuteCopyCoverCommand() { // 复制封面图片到剪贴板 Clipboard.SetImage(VideoInfoView.Cover); LogManager.Info(Tag, "复制封面图片到剪贴板"); } // 复制封面URL事件 private DelegateCommand copyCoverUrlCommand; public DelegateCommand CopyCoverUrlCommand => copyCoverUrlCommand ?? (copyCoverUrlCommand = new DelegateCommand(ExecuteCopyCoverUrlCommand)); /// /// 复制封面URL事件 /// private void ExecuteCopyCoverUrlCommand() { // 复制封面url到剪贴板 Clipboard.SetText(VideoInfoView.CoverUrl); LogManager.Info(Tag, "复制封面url到剪贴板"); } // 前往UP主页事件 private DelegateCommand upperCommand; public DelegateCommand UpperCommand => upperCommand ?? (upperCommand = new DelegateCommand(ExecuteUpperCommand)); /// /// 前往UP主页事件 /// private void ExecuteUpperCommand() { NavigateToView.NavigateToViewUserSpace(eventAggregator, Tag, VideoInfoView.UpperMid); } // 视频章节选择事件 private DelegateCommand videoSectionsCommand; public DelegateCommand VideoSectionsCommand => videoSectionsCommand ?? (videoSectionsCommand = new DelegateCommand(ExecuteVideoSectionsCommand)); /// /// 视频章节选择事件 /// /// private void ExecuteVideoSectionsCommand(object parameter) { if (!(parameter is VideoSection section)) { return; } bool isSelectAll = true; foreach (var page in section.VideoPages) { if (!page.IsSelected) { isSelectAll = false; break; } } IsSelectAll = section.VideoPages.Count != 0 && isSelectAll; } // 视频page选择事件 private DelegateCommand videoPagesCommand; public DelegateCommand VideoPagesCommand => videoPagesCommand ?? (videoPagesCommand = new DelegateCommand(ExecuteVideoPagesCommand)); /// /// 视频page选择事件 /// /// private void ExecuteVideoPagesCommand(object parameter) { if (!(parameter is ObservableCollection videoPages)) { return; } VideoSection section = VideoSections.FirstOrDefault(item => item.IsSelected); if (section == null) { return; } IsSelectAll = section.VideoPages.Count == videoPages.Count && section.VideoPages.Count != 0; } // Ctrl+A 全选事件 private DelegateCommand keySelectAllCommand; public DelegateCommand KeySelectAllCommand => keySelectAllCommand ?? (keySelectAllCommand = new DelegateCommand(ExecuteKeySelectAllCommand)); /// /// Ctrl+A 全选事件 /// private void ExecuteKeySelectAllCommand(object parameter) { if (!(parameter is VideoSection section)) { return; } foreach (var page in section.VideoPages) { page.IsSelected = true; } } // 全选事件 private DelegateCommand selectAllCommand; public DelegateCommand SelectAllCommand => selectAllCommand ?? (selectAllCommand = new DelegateCommand(ExecuteSelectAllCommand)); /// /// 全选事件 /// /// private void ExecuteSelectAllCommand(object parameter) { if (!(parameter is VideoSection section)) { return; } if (IsSelectAll) { foreach (var page in section.VideoPages) { page.IsSelected = true; } } else { foreach (var page in section.VideoPages) { page.IsSelected = false; } } } // 解析视频流事件 private DelegateCommand parseCommand; public DelegateCommand ParseCommand => parseCommand ?? (parseCommand = new DelegateCommand(ExecuteParseCommand, CanExecuteParseCommand)); /// /// 解析视频流事件 /// /// private async void ExecuteParseCommand(object parameter) { if (!(parameter is VideoPage videoPage)) { return; } LoadingVisibility = Visibility.Visible; try { await Task.Run(() => { LogManager.Debug(Tag, $"Video Page: {videoPage.Cid}"); UnityUpdateView(ParseVideo, null, videoPage); }); } catch (Exception e) { Core.Utils.Debugging.Console.PrintLine("ParseCommand()发生异常: {0}", e); LogManager.Error(Tag, e); LoadingVisibility = Visibility.Collapsed; } LoadingVisibility = Visibility.Collapsed; } /// /// 解析视频流事件是否允许执行 /// /// /// private bool CanExecuteParseCommand(object parameter) { return LoadingVisibility != Visibility.Visible; } // 解析所有视频流事件 private DelegateCommand parseAllVideoCommand; public DelegateCommand ParseAllVideoCommand => parseAllVideoCommand ?? (parseAllVideoCommand = new DelegateCommand(ExecuteParseAllVideoCommand, CanExecuteParseAllVideoCommand)); /// /// 解析所有视频流事件 /// private async void ExecuteParseAllVideoCommand() { LoadingVisibility = Visibility.Visible; // 解析范围 ParseScope parseScope = SettingsManager.GetInstance().GetParseScope(); // 是否选择了解析范围 if (parseScope == ParseScope.NONE) { // 打开解析选择器 dialogService.ShowDialog(ViewParsingSelectorViewModel.Tag, null, result => { if (result.Result == ButtonResult.OK) { // 选择的解析范围 parseScope = result.Parameters.GetValue("parseScope"); } }); } LogManager.Debug(Tag, $"ParseScope: {parseScope:G}"); try { await Task.Run(() => { LogManager.Debug(Tag, "Parse video"); switch (parseScope) { case ParseScope.NONE: break; case ParseScope.SELECTED_ITEM: foreach (var section in VideoSections) { foreach (var page in section.VideoPages) { var videoPage = section.VideoPages.FirstOrDefault(t => t == page); if (videoPage.IsSelected) { // 执行解析任务 UnityUpdateView(ParseVideo, null, videoPage); } } } break; case ParseScope.CURRENT_SECTION: foreach (var section in VideoSections) { if (section.IsSelected) { foreach (var page in section.VideoPages) { var videoPage = section.VideoPages.FirstOrDefault(t => t == page); // 执行解析任务 UnityUpdateView(ParseVideo, null, videoPage); } } } break; case ParseScope.ALL: foreach (var section in VideoSections) { foreach (var page in section.VideoPages) { var videoPage = section.VideoPages.FirstOrDefault(t => t == page); // 执行解析任务 UnityUpdateView(ParseVideo, null, videoPage); } } break; default: break; } }); } catch (Exception e) { Core.Utils.Debugging.Console.PrintLine("ParseCommand()发生异常: {0}", e); LogManager.Error(Tag, e); LoadingVisibility = Visibility.Collapsed; } LoadingVisibility = Visibility.Collapsed; } /// /// 解析所有视频流事件是否允许执行 /// /// private bool CanExecuteParseAllVideoCommand() { return LoadingVisibility != Visibility.Visible; } // 添加到下载列表事件 private DelegateCommand addToDownloadCommand; public DelegateCommand AddToDownloadCommand => addToDownloadCommand ?? (addToDownloadCommand = new DelegateCommand(ExecuteAddToDownloadCommand, CanExecuteAddToDownloadCommand)); /// /// 添加到下载列表事件 /// private void ExecuteAddToDownloadCommand() { // 选择的下载文件夹 string directory = string.Empty; // 下载内容 bool downloadAudio = true; bool downloadVideo = true; bool downloadDanmaku = true; bool downloadSubtitle = true; bool downloadCover = true; // 是否使用默认下载目录 if (SettingsManager.GetInstance().IsUseSaveVideoRootPath() == AllowStatus.YES) { directory = SettingsManager.GetInstance().GetSaveVideoRootPath(); } else { // 打开文件夹选择器 dialogService.ShowDialog(ViewDownloadSetterViewModel.Tag, null, result => { if (result.Result == ButtonResult.OK) { // 选择的下载文件夹 directory = result.Parameters.GetValue("directory"); // 下载内容 downloadAudio = result.Parameters.GetValue("downloadAudio"); downloadVideo = result.Parameters.GetValue("downloadVideo"); downloadDanmaku = result.Parameters.GetValue("downloadDanmaku"); downloadSubtitle = result.Parameters.GetValue("downloadSubtitle"); downloadCover = result.Parameters.GetValue("downloadCover"); // 文件夹不存在则创建 if (!Directory.Exists(directory)) { Directory.CreateDirectory(directory); } } }); } // 下载设置dialog中如果点击取消或者关闭窗口, // 会返回空字符串, // 这时直接退出 if (directory == string.Empty) { return; } // 添加到下载 eventAggregator.GetEvent().Publish(directory); } /// /// 添加到下载列表事件是否允许执行 /// /// private bool CanExecuteAddToDownloadCommand() { return LoadingVisibility != Visibility.Visible; } #endregion #region 业务逻辑 /// /// 初始化页面元素 /// private void InitView() { LogManager.Debug(Tag, "初始化页面元素"); LoadingVisibility = Visibility.Visible; ContentVisibility = Visibility.Collapsed; NoDataVisibility = Visibility.Collapsed; VideoSections.Clear(); } /// /// 更新页面的统一方法 /// /// /// /// private void UnityUpdateView(Action action, string input, VideoPage page) { // 视频 if (ParseEntrance.IsAvUrl(InputText) || ParseEntrance.IsBvUrl(InputText)) { action(new VideoInfoService(input), page); } // 番剧(电影、电视剧) if (ParseEntrance.IsBangumiSeasonUrl(InputText) || ParseEntrance.IsBangumiEpisodeUrl(InputText) || ParseEntrance.IsBangumiMediaUrl(InputText)) { action(new BangumiInfoService(input), page); } // 课程 if (ParseEntrance.IsCheeseSeasonUrl(InputText) || ParseEntrance.IsCheeseEpisodeUrl(InputText)) { action(new CheeseInfoService(input), page); } } /// /// 更新页面 /// /// private void UpdateView(IInfoService videoInfoService, VideoPage param) { VideoInfoView = videoInfoService.GetVideoView(); if (VideoInfoView == null) { LogManager.Debug(Tag, "VideoInfoView is null."); LoadingVisibility = Visibility.Collapsed; ContentVisibility = Visibility.Collapsed; NoDataVisibility = Visibility.Visible; return; } else { LoadingVisibility = Visibility.Collapsed; ContentVisibility = Visibility.Visible; NoDataVisibility = Visibility.Collapsed; } var videoSections = videoInfoService.GetVideoSections(); if (videoSections == null) { LogManager.Debug(Tag, "videoSections is not exist."); var pages = videoInfoService.GetVideoPages(); PropertyChangeAsync(new Action(() => { VideoSections.Add(new VideoSection { Id = 0, Title = "default", IsSelected = true, VideoPages = pages }); })); } else { PropertyChangeAsync(new Action(() => { VideoSections.AddRange(videoSections); })); } } /// /// 解析视频流 /// /// private void ParseVideo(IInfoService videoInfoService, VideoPage videoPage) { videoInfoService.GetVideoStream(videoPage); } #endregion /// /// 导航到VideoDetail页面时执行 /// /// public override void OnNavigatedTo(NavigationContext navigationContext) { base.OnNavigatedTo(navigationContext); DownloadManage = ButtonIcon.Instance().DownloadManage; DownloadManage.Height = 24; DownloadManage.Width = 24; DownloadManage.Fill = DictionaryResource.GetColor("ColorPrimary"); // Parent参数为null时,表示是从下一个页面返回到本页面,不需要执行任务 if (navigationContext.Parameters.GetValue("Parent") != null) { // 正在执行任务时不开启新任务 if (LoadingVisibility != Visibility.Visible) { InputText = navigationContext.Parameters.GetValue("Parameter"); PropertyChangeAsync(ExecuteInputCommand); } } } } }