diff --git a/src/BiliLite.Packages/BiliLite.Packages.wapproj b/src/BiliLite.Packages/BiliLite.Packages.wapproj index 2c6d46f1b..a1751f234 100644 --- a/src/BiliLite.Packages/BiliLite.Packages.wapproj +++ b/src/BiliLite.Packages/BiliLite.Packages.wapproj @@ -57,7 +57,7 @@ True $(NoWarn);NU1702 ..\BiliLite.UWP\BiliLite.UWP.csproj - C4FA34A878A0528F84922932FEC6746BCBCA5CEA + FD3F8B8872537B6FE4A2319C0534D6A8AEE6D419 False SHA256 False diff --git a/src/BiliLite.UWP/App.xaml.cs b/src/BiliLite.UWP/App.xaml.cs index 6bf51b5d5..3c970052b 100644 --- a/src/BiliLite.UWP/App.xaml.cs +++ b/src/BiliLite.UWP/App.xaml.cs @@ -7,6 +7,7 @@ using Microsoft.Toolkit.Uwp.Helpers; using System; using System.Runtime.InteropServices; +using System.Threading.Tasks; using Windows.ApplicationModel; using Windows.ApplicationModel.Activation; using Windows.ApplicationModel.Core; @@ -18,7 +19,6 @@ using Windows.UI.Xaml.Controls; using Windows.UI.Xaml.Navigation; using BiliLite.Pages; -using BiliLite.ViewModels.Download; using Microsoft.Extensions.DependencyInjection; namespace BiliLite @@ -205,12 +205,26 @@ private async void InitBili() //圆角 App.Current.Resources["ImageCornerRadius"] = new CornerRadius(SettingService.GetValue(SettingConstants.UI.IMAGE_CORNER_RADIUS, 0)); await AppHelper.SetRegions(); - var downloadViewModel = ServiceProvider.GetRequiredService(); - downloadViewModel.LoadDownloading(); - downloadViewModel.LoadDownloaded(); + await InitDb(); + try + { + var downloadService = ServiceProvider.GetRequiredService(); + downloadService.LoadDownloading(); + downloadService.LoadDownloaded(); + } + catch (Exception ex) + { + logger.Error("初始化加载下载视频错误", ex); + } VideoPlayHistoryHelper.LoadABPlayHistories(true); } + private async Task InitDb() + { + var sqlMigrateService = ServiceProvider.GetRequiredService(); + await sqlMigrateService.MigrateDatabase(); + } + /// /// 导航到特定页失败时调用 /// diff --git a/src/BiliLite.UWP/Assets/DbMigrations/01-0_40704.sql b/src/BiliLite.UWP/Assets/DbMigrations/01-0_40704.sql new file mode 100644 index 000000000..4d0e3f985 --- /dev/null +++ b/src/BiliLite.UWP/Assets/DbMigrations/01-0_40704.sql @@ -0,0 +1,28 @@ +-- 创建 DownloadedItems 表 +CREATE TABLE IF NOT EXISTS DownloadedItems ( + "ID" TEXT NOT NULL CONSTRAINT "PK_DownloadedItems" PRIMARY KEY, + "IsSeason" INTEGER NOT NULL, + "CoverPath" TEXT NULL, + "Title" TEXT NULL, + "UpdateTime" TEXT NOT NULL, + "Path" TEXT NULL +); + +-- 创建 DownloadedSubItems 表 +CREATE TABLE IF NOT EXISTS "DownloadedSubItems" ( + "CID" TEXT NOT NULL CONSTRAINT "PK_DownloadedSubItems" PRIMARY KEY, + "EpisodeID" TEXT NULL, + "Title" TEXT NULL, + "IsDash" INTEGER NOT NULL, + "QualityID" INTEGER NOT NULL, + "QualityName" TEXT NULL, + "PathList" TEXT NULL, + "DanmakuPath" TEXT NULL, + "Index" INTEGER NOT NULL, + "FilePath" TEXT NULL, + "SubtitlePaths" TEXT NULL, + "DownloadedItemDTOID" TEXT NULL, + CONSTRAINT "FK_DownloadedSubItems_DownloadedItems_DownloadedItemDTOID" FOREIGN KEY ("DownloadedItemDTOID") REFERENCES "DownloadedItems" ("ID") ON DELETE RESTRICT +); + +CREATE INDEX "IX_DownloadedSubItems_DownloadedItemDTOID" ON "DownloadedSubItems" ("DownloadedItemDTOID"); \ No newline at end of file diff --git a/src/BiliLite.UWP/Assets/Icon/icon_view_point.png b/src/BiliLite.UWP/Assets/Icon/icon_view_point.png new file mode 100644 index 000000000..29d825be8 Binary files /dev/null and b/src/BiliLite.UWP/Assets/Icon/icon_view_point.png differ diff --git a/src/BiliLite.UWP/Assets/Text/bili-merge.md b/src/BiliLite.UWP/Assets/Text/bili-merge.md new file mode 100644 index 000000000..1a3229b19 --- /dev/null +++ b/src/BiliLite.UWP/Assets/Text/bili-merge.md @@ -0,0 +1,21 @@ +## BiliLite 导出视频 +此功能需要BiliLite版本4.4.0及以上 + +1. 打开BiliLite UWP -『下载』-『下载完成』选项卡 + +2. 右键或长按需要导出的视频,选择『导出视频』 + +![](ms-appx:///Assets/Text/img/3382929121.png) + +如果是多集视频,可以点击『保存』图标按钮 + +![](ms-appx:///Assets/Text/img/1433952792.png) + +3. 选择导出视频的保存位置,等待导出完成即可。 +PS:首次使用需要解压FFmpeg,可能会耗时久一点。 + +![](ms-appx:///Assets/Text/img/2655001972.png) + +### 其他工具 + +- [基于python的视频合并脚本 https://github.com/TPAXcc/biliuwp-lite.Mixer](https://github.com/TPAXcc/biliuwp-lite.Mixer) \ No newline at end of file diff --git a/src/BiliLite.UWP/Assets/Text/img/1433952792.png b/src/BiliLite.UWP/Assets/Text/img/1433952792.png new file mode 100644 index 000000000..84abd212d Binary files /dev/null and b/src/BiliLite.UWP/Assets/Text/img/1433952792.png differ diff --git a/src/BiliLite.UWP/Assets/Text/img/2655001972.png b/src/BiliLite.UWP/Assets/Text/img/2655001972.png new file mode 100644 index 000000000..01d94a533 Binary files /dev/null and b/src/BiliLite.UWP/Assets/Text/img/2655001972.png differ diff --git a/src/BiliLite.UWP/Assets/Text/img/3382929121.png b/src/BiliLite.UWP/Assets/Text/img/3382929121.png new file mode 100644 index 000000000..062a423d3 Binary files /dev/null and b/src/BiliLite.UWP/Assets/Text/img/3382929121.png differ diff --git a/src/BiliLite.UWP/BiliLite.UWP.csproj b/src/BiliLite.UWP/BiliLite.UWP.csproj index ec8ba788a..e27069185 100644 --- a/src/BiliLite.UWP/BiliLite.UWP.csproj +++ b/src/BiliLite.UWP/BiliLite.UWP.csproj @@ -206,7 +206,11 @@ + + + + @@ -222,6 +226,7 @@ + @@ -258,7 +263,12 @@ + + MarkdownViewerPage.xaml + + + @@ -937,8 +947,10 @@ + + @@ -1040,6 +1052,9 @@ + + + @@ -1052,6 +1067,7 @@ + Designer @@ -1159,6 +1175,10 @@ MSBuild:Compile Designer + + Designer + MSBuild:Compile + Designer MSBuild:Compile diff --git a/src/BiliLite.UWP/Controls/Player.xaml.cs b/src/BiliLite.UWP/Controls/Player.xaml.cs index c92487750..2e87919e3 100644 --- a/src/BiliLite.UWP/Controls/Player.xaml.cs +++ b/src/BiliLite.UWP/Controls/Player.xaml.cs @@ -325,6 +325,7 @@ private MediaSourceConfig CreateFFmpegInteropConfig(string userAgent, string ref if (referer != null && referer.Length > 0) { _ffmpegConfig.FFmpegOptions.Add("referer", referer); + _ffmpegConfig.FFmpegOptions.Add("headers", $"Referer: {referer}"); } _ffmpegConfig.VideoDecoderMode = passthrough ? VideoDecoderMode.ForceSystemDecoder : VideoDecoderMode.ForceFFmpegSoftwareDecoder; @@ -520,7 +521,7 @@ public async Task PlayerDashUseNative(BiliDashPlayUrlInfo dash message = "创建MediaSource失败" }; } - + m_playerVideo.Source = MediaSource.CreateFromAdaptiveMediaSource(mediaSource); Buffering = true; @@ -665,6 +666,7 @@ public async Task PlayDashUseFFmpegInterop(BiliDashPlayUrlInfo //关闭正在播放的视频 ClosePlay(); var _ffmpegConfig = CreateFFmpegInteropConfig(userAgent, referer); + if (isLocal) { diff --git a/src/BiliLite.UWP/Controls/PlayerControl.xaml b/src/BiliLite.UWP/Controls/PlayerControl.xaml index 47ff1489f..cd047621e 100644 --- a/src/BiliLite.UWP/Controls/PlayerControl.xaml +++ b/src/BiliLite.UWP/Controls/PlayerControl.xaml @@ -13,6 +13,7 @@ xmlns:fa="using:FontAwesome5" xmlns:player="using:BiliLite.Modules.Player" xmlns:common="using:BiliLite.Models.Common" xmlns:player1="using:BiliLite.Models.Common.Player" + xmlns:modules="using:BiliLite.Modules" d:DesignHeight="800" d:DesignWidth="1000"> @@ -302,7 +303,7 @@ - + 弹幕同屏密度(0为不限制) - 弹幕字体 + 弹幕字体 @@ -446,7 +447,7 @@ 合并重复弹幕 - + 屏蔽等级(0为不屏蔽) @@ -605,10 +606,10 @@ DoubleTapped="Grid_DoubleTapped"> - + - + @@ -666,7 +667,7 @@ - + @@ -702,9 +703,15 @@ 第一集 + + + diff --git a/src/BiliLite.UWP/Controls/PlayerControl.xaml.cs b/src/BiliLite.UWP/Controls/PlayerControl.xaml.cs index 4f1c05275..9502f6d01 100644 --- a/src/BiliLite.UWP/Controls/PlayerControl.xaml.cs +++ b/src/BiliLite.UWP/Controls/PlayerControl.xaml.cs @@ -114,6 +114,8 @@ public BiliPlayUrlQualitesInfo playUrlInfo set { _playUrlInfo = value; DoPropertyChanged("playUrlInfo"); } } + private readonly bool m_autoSkipOpEdFlag = false; + private DispatcherTimer m_positionTimer; DispatcherTimer danmuTimer; /// /// 弹幕信息 @@ -149,6 +151,12 @@ public PlayerControl() danmuTimer = new DispatcherTimer(); danmuTimer.Interval = TimeSpan.FromSeconds(1); danmuTimer.Tick += DanmuTimer_Tick; + + m_positionTimer = new DispatcherTimer() { Interval = TimeSpan.FromSeconds(1) }; + m_positionTimer.Tick += PositionTimer_Tick; + m_autoSkipOpEdFlag = SettingService.GetValue(SettingConstants.Player.AUTO_SKIP_OP_ED, + SettingConstants.Player.DEFAULT_AUTO_SKIP_OP_ED); + this.Loaded += PlayerControl_Loaded; this.Unloaded += PlayerControl_Unloaded; m_playerKeyRightAction = (PlayerKeyRightAction)SettingService.GetValue(SettingConstants.Player.PLAYER_KEY_RIGHT_ACTION, (int)PlayerKeyRightAction.ControlProgress); @@ -241,6 +249,7 @@ private async void PlayerControl_Loaded(object sender, RoutedEventArgs e) danmuTimer.Start(); timer_focus.Start(); + m_positionTimer.Start(); } private async void _systemMediaTransportControls_ButtonPressed(SystemMediaTransportControls sender, SystemMediaTransportControlsButtonPressedEventArgs args) @@ -814,6 +823,28 @@ private async void DanmuTimer_Tick(object sender, object e) } } + private void PositionTimer_Tick(object sender, object e) + { + if (!m_autoSkipOpEdFlag) return; + if (CurrentPlayItem == null) return; + if (CurrentPlayItem.EpisodeSkip == null) return; + SkipSection(CurrentPlayItem.EpisodeSkip.Op, "SkipOp", "自动跳过OP"); + SkipSection(CurrentPlayItem.EpisodeSkip.Ed, "SkipEd", "自动跳过ED"); + } + + private void SkipSection(PlayerSkipItem section, string toastId, string message) + { + if (section == null) return; + if (!IsSectionValid(section) || (int)Player.Position != section.Start) return; + m_playerToastService.Show(toastId, message); + SetPosition(section.End); + } + + private bool IsSectionValid(PlayerSkipItem section) + { + return section.Start != 0 && section.End != 0 && section.Start != section.End; + } + private async Task SetPlayItem(int index) { if (PlayInfos == null || PlayInfos.Count == 0) @@ -1476,8 +1507,13 @@ private async Task GetPlayerInfo() } } - TopOnline.Text = await playerHelper.GetOnline(CurrentPlayItem.avid, CurrentPlayItem.cid); + if (player_info.ViewPoints != null && player_info.ViewPoints.Any()) + { + m_viewModel.ViewPoints = player_info.ViewPoints; + m_viewModel.ShowViewPointsBtn = true; + } + TopOnline.Text = await playerHelper.GetOnline(CurrentPlayItem.avid, CurrentPlayItem.cid); } public async Task ReportHistory(double progress = double.NaN) @@ -1530,31 +1566,29 @@ private async Task ChangeQualityPlayVideo(BiliPlayUrlInfo qual }; if (quality.PlayUrlType == BiliPlayUrlType.DASH) { - var audio = audioQuality == null ? quality.DashInfo.Audio : audioQuality.Audio; - var video = quality.DashInfo.Video; - - result = await Player.PlayerDashUseNative(quality.DashInfo, quality.UserAgent, quality.Referer, positon: _postion); + var realPlayerType = (RealPlayerType)SettingService.GetValue(SettingConstants.Player.USE_REAL_PLAYER_TYPE, (int)SettingConstants.Player.DEFAULT_USE_REAL_PLAYER_TYPE); + if (realPlayerType==RealPlayerType.Native) + { + result = await Player.PlayerDashUseNative(quality.DashInfo, quality.UserAgent, quality.Referer, positon: _postion); - if (!result.result) + if (!result.result) + { + result = await Player.PlayDashUseFFmpegInterop(quality.DashInfo, quality.UserAgent, quality.Referer, + positon: _postion); + } + } + else { - var mpd_url = new PlayerAPI().GenerateMPD(new Models.GenerateMPDModel() + result = await Player.PlayDashUseFFmpegInterop(quality.DashInfo, quality.UserAgent, quality.Referer, + positon: _postion); + + if (!result.result) { - AudioBandwidth = audio.BandWidth.ToString(), - AudioCodec = audio.Codecs, - AudioID = audio.ID.ToString(), - AudioUrl = audio.Url, - Duration = quality.DashInfo.Duration, - DurationMS = quality.Timelength, - VideoBandwidth = video.BandWidth.ToString(), - VideoCodec = video.Codecs, - VideoID = video.ID.ToString(), - VideoFrameRate = video.FrameRate.ToString(), - VideoHeight = video.Height, - VideoWidth = video.Width, - VideoUrl = video.Url, - }); - result = await Player.PlayDashUrlUseFFmpegInterop(mpd_url, quality.UserAgent, quality.Referer, positon: _postion); + + result = await Player.PlayerDashUseNative(quality.DashInfo, quality.UserAgent, quality.Referer, positon: _postion); + } } + } else if (quality.PlayUrlType == BiliPlayUrlType.SingleFLV) { @@ -2546,6 +2580,11 @@ public async void Dispose() danmuTimer.Stop(); danmuTimer = null; } + if (m_positionTimer != null) + { + m_positionTimer.Stop(); + m_positionTimer = null; + } danmakuPool = null; if (dispRequest != null) { @@ -2879,5 +2918,22 @@ private void Player_SizeChanged(object sender, SizeChangedEventArgs e) // 更新画面比例 Player.SetRatioMode(PlayerSettingRatio.SelectedIndex); } + + private void TopBtnViewPoints_OnClick(object sender, RoutedEventArgs e) + { + m_viewModel.ShowViewPointsView = true; + } + + private void ViewPointsGrid_OnTapped(object sender, TappedRoutedEventArgs e) + { + m_viewModel.ShowViewPointsView = false; + } + + private void ViewPoint_OnTapped(object sender, TappedRoutedEventArgs e) + { + if (!(sender is FrameworkElement element)) return; + if (!(element.DataContext is PlayerInfoViewPoint viewPoint)) return; + SetPosition(viewPoint.From); + } } } diff --git a/src/BiliLite.UWP/Controls/Settings/DownloadSettingsControl.xaml b/src/BiliLite.UWP/Controls/Settings/DownloadSettingsControl.xaml index 80e269afd..c7cc63000 100644 --- a/src/BiliLite.UWP/Controls/Settings/DownloadSettingsControl.xaml +++ b/src/BiliLite.UWP/Controls/Settings/DownloadSettingsControl.xaml @@ -99,5 +99,19 @@ + + + + + + + + + + + + + diff --git a/src/BiliLite.UWP/Controls/Settings/DownloadSettingsControl.xaml.cs b/src/BiliLite.UWP/Controls/Settings/DownloadSettingsControl.xaml.cs index 1e6530505..d049892dc 100644 --- a/src/BiliLite.UWP/Controls/Settings/DownloadSettingsControl.xaml.cs +++ b/src/BiliLite.UWP/Controls/Settings/DownloadSettingsControl.xaml.cs @@ -5,7 +5,6 @@ using Windows.UI.Xaml.Controls; using BiliLite.Models.Common; using BiliLite.Services; -using BiliLite.ViewModels.Download; using Microsoft.Extensions.DependencyInjection; //https://go.microsoft.com/fwlink/?LinkId=234236 上介绍了“用户控件”项模板 @@ -14,11 +13,11 @@ namespace BiliLite.Controls.Settings { public sealed partial class DownloadSettingsControl : UserControl { - private readonly DownloadPageViewModel m_downloadPageViewModel; + private readonly DownloadService m_downloadService; public DownloadSettingsControl() { - m_downloadPageViewModel = App.ServiceProvider.GetRequiredService(); + m_downloadService = App.ServiceProvider.GetRequiredService(); this.InitializeComponent(); LoadDownlaod(); } @@ -52,7 +51,7 @@ private void LoadDownlaod() SettingService.SetValue(SettingConstants.Download.DOWNLOAD_PATH, folder.Path); txtDownloadPath.Text = folder.Path; Windows.Storage.AccessCache.StorageApplicationPermissions.FutureAccessList.Add(folder); - m_downloadPageViewModel.RefreshDownloaded(); + m_downloadService.RefreshDownloaded(); } }); //旧版下载目录 @@ -89,14 +88,14 @@ private void LoadDownlaod() swDownloadParallelDownload.Toggled += new RoutedEventHandler((e, args) => { SettingService.SetValue(SettingConstants.Download.PARALLEL_DOWNLOAD, swDownloadParallelDownload.IsOn); - m_downloadPageViewModel.UpdateSetting(); + m_downloadService.UpdateSetting(); }); //付费网络下载 swDownloadAllowCostNetwork.IsOn = SettingService.GetValue(SettingConstants.Download.ALLOW_COST_NETWORK, false); swDownloadAllowCostNetwork.Toggled += new RoutedEventHandler((e, args) => { SettingService.SetValue(SettingConstants.Download.ALLOW_COST_NETWORK, swDownloadAllowCostNetwork.IsOn); - m_downloadPageViewModel.UpdateSetting(); + m_downloadService.UpdateSetting(); }); //下载完成发送通知 swDownloadSendToast.IsOn = SettingService.GetValue(SettingConstants.Download.SEND_TOAST, false); @@ -104,6 +103,13 @@ private void LoadDownlaod() { SettingService.SetValue(SettingConstants.Download.SEND_TOAST, swDownloadSendToast.IsOn); }); + //使用下载索引 + SwUseDownloadIndex.IsOn = SettingService.GetValue(SettingConstants.Download.USE_DOWNLOAD_INDEX, + SettingConstants.Download.DEFAULT_USE_DOWNLOAD_INDEX); + SwUseDownloadIndex.Toggled += (e, args) => + { + SettingService.SetValue(SettingConstants.Download.USE_DOWNLOAD_INDEX, SwUseDownloadIndex.IsOn); + }; //下载类型 var selectedValue = (PlayUrlCodecMode)SettingService.GetValue(SettingConstants.Download.DEFAULT_VIDEO_TYPE, (int)DefaultVideoTypeOptions.DEFAULT_VIDEO_TYPE); cbDownloadVideoType.SelectedItem = DefaultVideoTypeOptions.GetOption(selectedValue); @@ -121,5 +127,11 @@ private void LoadDownlaod() SettingService.SetValue(SettingConstants.Download.LOAD_OLD_DOWNLOAD, swDownloadLoadOld.IsOn); }); } + + private void BtnRebuildDownloadIndex_OnClick(object sender, RoutedEventArgs e) + { + m_downloadService.ClearIndex(); + m_downloadService.LoadDownloaded(true); + } } } diff --git a/src/BiliLite.UWP/Controls/Settings/PlaySettingsControl.xaml b/src/BiliLite.UWP/Controls/Settings/PlaySettingsControl.xaml index 8f1af6eef..611e367ae 100644 --- a/src/BiliLite.UWP/Controls/Settings/PlaySettingsControl.xaml +++ b/src/BiliLite.UWP/Controls/Settings/PlaySettingsControl.xaml @@ -42,6 +42,18 @@ DisplayMemberPath="Name"> + + + + + + + +