update readme enhance music gallery

This commit is contained in:
Zhe Fang
2025-07-25 09:51:43 -04:00
parent 77f2474562
commit 3f63043150
16 changed files with 404 additions and 249 deletions

View File

@@ -469,6 +469,12 @@
</Setter>
</Style>
<Style x:Key="ListViewStretchedItemContainerStyle" TargetType="ListViewItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
<Setter Property="Margin" Value="0" />
<Setter Property="Padding" Value="0" />
</Style>
<StaticResource x:Key="ToggleButtonBackgroundChecked" ResourceKey="TextFillColorPrimaryBrush" />
<StaticResource x:Key="ToggleButtonBackgroundCheckedPointerOver" ResourceKey="TextFillColorPrimaryBrush" />
<StaticResource x:Key="ToggleButtonBackgroundCheckedPressed" ResourceKey="TextFillColorPrimaryBrush" />

View File

@@ -8,7 +8,7 @@ using System.Threading.Tasks;
namespace BetterLyrics.WinUI3.Converter
{
public class AlbumArtSearchProviderToDisplayNameConverter : IValueConverter
public partial class AlbumArtSearchProviderToDisplayNameConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language)
{

View File

@@ -3,7 +3,7 @@ using Microsoft.UI.Xaml.Data;
namespace BetterLyrics.WinUI3.Converter
{
internal partial class CornerRadiusToDoubleConverter : IValueConverter
public partial class CornerRadiusToDoubleConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language)
{

View File

@@ -5,7 +5,7 @@ using Microsoft.UI.Xaml.Data;
namespace BetterLyrics.WinUI3.Converter
{
internal partial class EnumToIntConverter : IValueConverter
public partial class EnumToIntConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language)
{

View File

@@ -7,7 +7,7 @@ using System.Threading.Tasks;
namespace BetterLyrics.WinUI3.Converter
{
public class SecondsToFormattedTimeConverter : IValueConverter
public partial class SecondsToFormattedTimeConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language)
{

View File

@@ -0,0 +1,15 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace BetterLyrics.WinUI3.Enums
{
public enum PlaybackOrder
{
RepeatAll,
RepeatOne,
Shuffle,
}
}

View File

@@ -7,6 +7,7 @@ using Microsoft.Graphics.Canvas.Geometry;
using Microsoft.Graphics.Canvas.Text;
using Microsoft.Graphics.Canvas.UI.Xaml;
using Microsoft.UI;
using Microsoft.UI.Xaml.Media;
using System;
using System.Collections.Generic;
using System.Diagnostics;

View File

@@ -365,7 +365,6 @@ namespace BetterLyrics.WinUI3.ViewModels
_songInfoOpacityTransition.StartTransition(1f);
_logger.LogInformation("Song info changed: Title={Title}, Artist={Artist}, refreshing lyrics...", _songTitle, _songArtist);
Debug.WriteLine($"Song info changed: Title={_songTitle}, Artist={_songArtist}");
_ = _refreshLyricsRunner.RunAsync(async token =>
{
await RefreshLyricsAsync(token);

View File

@@ -40,7 +40,11 @@ namespace BetterLyrics.WinUI3.ViewModels
public partial ObservableCollection<GroupInfoList> GroupedTracks { get; set; } = [];
[ObservableProperty]
public partial ObservableCollection<Track> TrackSearchSuggestions { get; set; } = [];
public partial ObservableCollection<Track> TrackPlayingQueue { get; set; } = [];
public Track? PlayingTrack => TrackPlayingQueue.ElementAtOrDefault(PlayingSongIndex);
[ObservableProperty]
public partial SongOrderType SongOrderType { get; set; } = SongOrderType.Title;
@@ -52,7 +56,10 @@ namespace BetterLyrics.WinUI3.ViewModels
public partial Track TrackRightTapped { get; set; } = new();
[ObservableProperty]
public partial int SelectedSongIndex { get; set; } = -1;
public partial int PlayingSongIndex { get; set; } = -1;
[ObservableProperty]
public partial int DisplayedPlayingSongIndex { get; set; } = 0;
[ObservableProperty]
public partial string SongSearchQuery { get; set; } = string.Empty;
@@ -108,9 +115,22 @@ namespace BetterLyrics.WinUI3.ViewModels
_timelineController.Pause();
break;
case SystemMediaTransportControlsButton.Next:
_dispatcherQueue.TryEnqueue(DispatcherQueuePriority.Low, () =>
{
if (PlayingSongIndex < TrackPlayingQueue.Count - 1)
{
PlayingSongIndex++;
}
});
break;
case SystemMediaTransportControlsButton.Previous:
//Previous
_dispatcherQueue.TryEnqueue(DispatcherQueuePriority.Low, () =>
{
if (PlayingSongIndex > 0)
{
PlayingSongIndex--;
}
});
break;
}
}
@@ -191,10 +211,15 @@ namespace BetterLyrics.WinUI3.ViewModels
);
break;
}
SelectedSongIndex = -1;
}
public void PlayTrack(Track? track)
public void InitPlayingQueue(Track? track)
{
TrackPlayingQueue = [.. GroupedTracks.SelectMany(g => g.Cast<Track>())];
PlayingSongIndex = track == null ? -1 : TrackPlayingQueue.IndexOf(track);
}
private void PlayTrack(Track? track)
{
if (track == null) return;
@@ -226,6 +251,12 @@ namespace BetterLyrics.WinUI3.ViewModels
ApplySongOrderType();
}
partial void OnPlayingSongIndexChanged(int value)
{
DisplayedPlayingSongIndex = value + 1;
PlayTrack(TrackPlayingQueue.ElementAtOrDefault(value));
}
public void Receive(PropertyChangedMessage<ObservableCollection<LocalMediaFolder>> message)
{
if (message.Sender is SettingsPageViewModel)

View File

@@ -22,11 +22,14 @@
<local:LyricsPage />
<local:MusicGalleryPage x:Name="MusicGalleryPage" Padding="64">
<local:MusicGalleryPage.TranslationTransition>
<Frame
x:Name="OverlayFrame"
Padding="0,24,0,0"
Background="{ThemeResource AcrylicInAppFillColorBaseBrush}">
<Frame.TranslationTransition>
<Vector3Transition />
</local:MusicGalleryPage.TranslationTransition>
</local:MusicGalleryPage>
</Frame.TranslationTransition>
</Frame>
<!-- Top command -->
<Grid

View File

@@ -332,24 +332,25 @@ namespace BetterLyrics.WinUI3.Views
private void RootGrid_SizeChanged(object sender, SizeChangedEventArgs e)
{
UpdateMusicGalleryPageTranslation();
UpdateOverlayFrameTranslation();
}
private void MusicGalleryButton_Click(object sender, RoutedEventArgs e)
{
ViewModel.IsMusicGalleryPageExpanded = !ViewModel.IsMusicGalleryPageExpanded;
UpdateMusicGalleryPageTranslation();
UpdateOverlayFrameTranslation();
}
private void UpdateMusicGalleryPageTranslation()
private void UpdateOverlayFrameTranslation()
{
if (ViewModel.IsMusicGalleryPageExpanded)
{
MusicGalleryPage.Translation = new System.Numerics.Vector3(0, 0, 0);
OverlayFrame.Translation = new System.Numerics.Vector3(0, 0, 0);
if (OverlayFrame.Content == null) OverlayFrame.Navigate(typeof(MusicGalleryPage));
}
else
{
MusicGalleryPage.Translation = new System.Numerics.Vector3(0, (float)RootGrid.ActualHeight, 0);
OverlayFrame.Translation = new System.Numerics.Vector3(0, (float)RootGrid.ActualHeight, 0);
}
}
}

View File

@@ -14,21 +14,20 @@
xmlns:models="using:BetterLyrics.WinUI3.Models"
xmlns:templateselector="using:BetterLyrics.WinUI3.TemplateSelector"
xmlns:ui="using:CommunityToolkit.WinUI"
Background="{ThemeResource AcrylicBackgroundFillColorBaseBrush}"
Loaded="Page_Loaded"
mc:Ignorable="d">
<Page.Resources>
<DataTemplate x:Key="ByTitleTemplate" x:DataType="atl:Track">
<Grid
Padding="12"
RightTapped="SongListVireItemGrid_RightTapped"
RightTapped="SongListViewItemGrid_RightTapped"
Tapped="SongListVireItemGrid_Tapped">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="2*" />
<ColumnDefinition Width="1.5*" />
<ColumnDefinition Width="1.5*" />
<ColumnDefinition Width="1*" />
<ColumnDefinition Width="1.2*" />
<!--<ColumnDefinition Width="1.2*" />-->
<ColumnDefinition Width="1*" />
</Grid.ColumnDefinitions>
@@ -64,12 +63,12 @@
TextWrapping="Wrap" />
<!-- 流派 -->
<TextBlock
<!--<TextBlock
Grid.Column="4"
VerticalAlignment="Center"
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
Text="{Binding Genre}"
TextWrapping="Wrap" />
TextWrapping="Wrap" />-->
<!-- 歌曲时长 -->
<TextBlock
@@ -84,14 +83,14 @@
<DataTemplate x:Key="ByAlbumTemplate" x:DataType="atl:Track">
<Grid
Padding="12"
RightTapped="SongListVireItemGrid_RightTapped"
RightTapped="SongListViewItemGrid_RightTapped"
Tapped="SongListVireItemGrid_Tapped">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="2*" />
<ColumnDefinition Width="1.5*" />
<ColumnDefinition Width="1.5*" />
<ColumnDefinition Width="1*" />
<ColumnDefinition Width="1.2*" />
<!--<ColumnDefinition Width="1.2*" />-->
<ColumnDefinition Width="1*" />
</Grid.ColumnDefinitions>
@@ -127,12 +126,12 @@
TextWrapping="Wrap" />
<!-- 流派 -->
<TextBlock
<!--<TextBlock
Grid.Column="4"
VerticalAlignment="Center"
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
Text="{Binding Genre}"
TextWrapping="Wrap" />
TextWrapping="Wrap" />-->
<!-- 歌曲时长 -->
<TextBlock
@@ -147,14 +146,14 @@
<DataTemplate x:Key="ByArtistTemplate" x:DataType="atl:Track">
<Grid
Padding="12"
RightTapped="SongListVireItemGrid_RightTapped"
RightTapped="SongListViewItemGrid_RightTapped"
Tapped="SongListVireItemGrid_Tapped">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="2*" />
<ColumnDefinition Width="1.5*" />
<ColumnDefinition Width="1.5*" />
<ColumnDefinition Width="1*" />
<ColumnDefinition Width="1.2*" />
<!--<ColumnDefinition Width="1.2*" />-->
<ColumnDefinition Width="1*" />
</Grid.ColumnDefinitions>
@@ -190,12 +189,12 @@
TextWrapping="Wrap" />
<!-- 流派 -->
<TextBlock
<!--<TextBlock
Grid.Column="4"
VerticalAlignment="Center"
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
Text="{Binding Genre}"
TextWrapping="Wrap" />
TextWrapping="Wrap" />-->
<!-- 歌曲时长 -->
<TextBlock
@@ -221,161 +220,247 @@
</Page.Resources>
<Grid>
<Grid.Tag>
<Flyout
x:Name="SongFileInfoFlyout"
Placement="Bottom"
ShouldConstrainToRootBounds="False">
<StackPanel Spacing="12">
<Grid x:Name="SongViewer" Margin="0,36,0,0">
<StackPanel Orientation="Horizontal" Spacing="6">
<!--<FontIcon FontFamily="{StaticResource IconFontFamily}" Glyph="&#xE946;" />-->
<TextBlock Style="{StaticResource BodyStrongTextBlockStyle}" Text="文件信息" />
</StackPanel>
<Grid Margin="12" VerticalAlignment="Top">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="16" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<AutoSuggestBox
x:Name="SongSearchBox"
Grid.Column="0"
HorizontalAlignment="Stretch"
PlaceholderText="搜索歌曲"
QueryIcon="Find"
Text="{x:Bind ViewModel.SongSearchQuery, Mode=TwoWay}" />
<StackPanel
Grid.Column="2"
Orientation="Horizontal"
Spacing="12">
<TextBlock
VerticalAlignment="Center"
Style="{StaticResource BodyStrongTextBlockStyle}"
Text="排序方式" />
<controls:Segmented
x:Name="Segmented"
SelectedIndex="{x:Bind ViewModel.SongOrderType, Converter={StaticResource EnumToIntConverter}, Mode=TwoWay}"
SelectionMode="Single">
<controls:SegmentedItem Content="歌曲" Icon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=&#xEC4F;}" />
<controls:SegmentedItem Content="专辑" Icon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=&#xE93C;}" />
<controls:SegmentedItem Content="艺术家" Icon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=&#xEFA9;}" />
<ScrollViewer Width="300" Height="300">
<StackPanel Spacing="12">
<StackPanel>
<TextBlock Foreground="{ThemeResource TextFillColorSecondaryBrush}" Text="标题" />
<TextBlock Text="{x:Bind ViewModel.TrackRightTapped.Title, Mode=OneWay}" />
</StackPanel>
<StackPanel>
<TextBlock Foreground="{ThemeResource TextFillColorSecondaryBrush}" Text="艺术家" />
<TextBlock Text="{x:Bind ViewModel.TrackRightTapped.Artist, Mode=OneWay}" />
</StackPanel>
<StackPanel>
<TextBlock Foreground="{ThemeResource TextFillColorSecondaryBrush}" Text="专辑" />
<TextBlock Text="{x:Bind ViewModel.TrackRightTapped.Album, Mode=OneWay}" />
</StackPanel>
<StackPanel>
<TextBlock Foreground="{ThemeResource TextFillColorSecondaryBrush}" Text="年份" />
<TextBlock Text="{x:Bind ViewModel.TrackRightTapped.Year, Mode=OneWay}" />
</StackPanel>
<StackPanel>
<TextBlock Foreground="{ThemeResource TextFillColorSecondaryBrush}" Text="时长" />
<TextBlock Text="{x:Bind ViewModel.TrackRightTapped.Duration, Converter={StaticResource SecondsToFormattedTimeConverter}, Mode=OneWay}" />
</StackPanel>
<StackPanel>
<TextBlock Foreground="{ThemeResource TextFillColorSecondaryBrush}" Text="比特率" />
<TextBlock Text="{x:Bind ViewModel.TrackRightTapped.Bitrate, Mode=OneWay}" />
</StackPanel>
<StackPanel>
<TextBlock Foreground="{ThemeResource TextFillColorSecondaryBrush}" Text="采样率" />
<TextBlock Text="{x:Bind ViewModel.TrackRightTapped.SampleRate, Mode=OneWay}" />
</StackPanel>
<StackPanel>
<TextBlock Foreground="{ThemeResource TextFillColorSecondaryBrush}" Text="位深" />
<TextBlock Text="{x:Bind ViewModel.TrackRightTapped.BitDepth, Mode=OneWay}" />
</StackPanel>
<StackPanel>
<TextBlock Foreground="{ThemeResource TextFillColorSecondaryBrush}" Text="格式" />
<TextBlock Text="{x:Bind ViewModel.TrackRightTapped.AudioFormat.Name, Mode=OneWay}" />
</StackPanel>
<StackPanel>
<TextBlock Foreground="{ThemeResource TextFillColorSecondaryBrush}" Text="编码" />
<TextBlock Text="{x:Bind ViewModel.TrackRightTapped.Encoder, Mode=OneWay}" />
</StackPanel>
<StackPanel>
<TextBlock Foreground="{ThemeResource TextFillColorSecondaryBrush}" Text="路径" />
<HyperlinkButton
Margin="-12,0,0,0"
Click="SongPathHyperlinkButton_Click"
Content="{x:Bind ViewModel.TrackRightTapped.Path, Mode=OneWay}" />
</StackPanel>
</StackPanel>
</ScrollViewer>
</StackPanel>
</Flyout>
</Grid.Tag>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="3*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid x:Name="SongViewer" Grid.Column="0">
<Grid Margin="12" VerticalAlignment="Top">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="16" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<AutoSuggestBox
x:Name="SongSearchBox"
Grid.Column="0"
HorizontalAlignment="Stretch"
PlaceholderText="搜索歌曲"
QueryIcon="Find"
Text="{x:Bind ViewModel.SongSearchQuery, Mode=TwoWay}" />
<StackPanel
Grid.Column="2"
Orientation="Horizontal"
Spacing="12">
<TextBlock
VerticalAlignment="Center"
Style="{StaticResource BodyStrongTextBlockStyle}"
Text="排序方式" />
<controls:Segmented
x:Name="Segmented"
SelectedIndex="{x:Bind ViewModel.SongOrderType, Converter={StaticResource EnumToIntConverter}, Mode=TwoWay}"
SelectionMode="Single">
<controls:SegmentedItem Content="歌曲" Icon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=&#xEC4F;}" />
<controls:SegmentedItem Content="专辑" Icon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=&#xE93C;}" />
<controls:SegmentedItem Content="艺术家" Icon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=&#xEFA9;}" />
</controls:Segmented>
</StackPanel>
</Grid>
<SemanticZoom Margin="0,48,0,0">
<SemanticZoom.ZoomedInView>
<ListView
x:Name="SongListView"
Margin="12,0"
ItemTemplateSelector="{StaticResource SongOrderTemplateSelector}"
ItemsSource="{x:Bind TracksByTitleCVS.View, Mode=OneWay}">
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<ItemsStackPanel AreStickyGroupHeadersEnabled="True" />
</ItemsPanelTemplate>
</ListView.ItemsPanel>
<ListView.GroupStyle>
<GroupStyle>
<GroupStyle.HeaderTemplate>
<DataTemplate x:DataType="models:GroupInfoList">
<Border AutomationProperties.AccessibilityView="Raw">
<TextBlock
AutomationProperties.AccessibilityView="Raw"
Style="{ThemeResource TitleTextBlockStyle}"
Text="{Binding}" />
</Border>
</DataTemplate>
</GroupStyle.HeaderTemplate>
</GroupStyle>
</ListView.GroupStyle>
</ListView>
</SemanticZoom.ZoomedInView>
<SemanticZoom.ZoomedOutView>
<GridView
MaxWidth="500"
HorizontalAlignment="Center"
VerticalAlignment="Center"
ItemsSource="{x:Bind TracksByTitleCVS.View.CollectionGroups, Mode=OneWay}"
ScrollViewer.IsHorizontalScrollChainingEnabled="False"
SelectionMode="None">
<GridView.ItemTemplate>
<DataTemplate x:DataType="models:GroupInfoList">
<TextBlock Style="{ThemeResource TitleTextBlockStyle}" Text="{Binding}" />
</DataTemplate>
</GridView.ItemTemplate>
</GridView>
</SemanticZoom.ZoomedOutView>
</SemanticZoom>
<Grid Margin="0,48,0,0" Visibility="{x:Bind ViewModel.IsLocalMediaNotFound, Mode=OneWay, Converter={StaticResource BoolToVisibilityConverter}}">
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
<Image MaxWidth="200" Source="/Assets/Planet.png" />
<TextBlock HorizontalAlignment="Center" Text="未在媒体库内找到任何歌曲" />
</StackPanel>
</Grid>
</Grid>
<Grid
x:Name="PlayQueue"
Grid.Column="1"
Margin="0,0,12,0">
<StackPanel Margin="0,10,0,0" Spacing="12">
<Grid Margin="0,6,0,0" VerticalAlignment="Top">
<TextBlock Style="{StaticResource BodyStrongTextBlockStyle}" Text="播放队列" />
<StackPanel HorizontalAlignment="Right" Orientation="Horizontal">
<TextBlock Foreground="{ThemeResource TextFillColorSecondaryBrush}" Text="{x:Bind ViewModel.DisplayedPlayingSongIndex, Mode=OneWay}" />
<TextBlock Foreground="{ThemeResource TextFillColorSecondaryBrush}" Text="/" />
<TextBlock Foreground="{ThemeResource TextFillColorSecondaryBrush}" Text="{x:Bind ViewModel.TrackPlayingQueue.Count, Mode=OneWay}" />
</StackPanel>
</Grid>
<StackPanel Orientation="Horizontal" Spacing="12">
<Button Click="EmptyPlayingQueueButton_Click" Content="清除全部" />
<Button Click="ScrollToPlayingItemButton_Click" Content="定位到播放项" />
</StackPanel>
<controls:Segmented HorizontalAlignment="Stretch">
<controls:Segmented.Items>
<controls:SegmentedItem Content="列表循环" />
<controls:SegmentedItem Content="单曲循环" />
<controls:SegmentedItem Content="随机" />
</controls:Segmented.Items>
</controls:Segmented>
</StackPanel>
</Grid>
<SemanticZoom Margin="0,48,0,0">
<SemanticZoom.ZoomedInView>
<ListView
x:Name="SongListView"
ItemTemplateSelector="{StaticResource SongOrderTemplateSelector}"
ItemsSource="{x:Bind TracksByTitleCVS.View, Mode=OneWay}">
<ListView.ContextFlyout>
<Flyout Placement="Right" ShouldConstrainToRootBounds="False">
<StackPanel Spacing="12">
<StackPanel Orientation="Horizontal" Spacing="6">
<!--<FontIcon FontFamily="{StaticResource IconFontFamily}" Glyph="&#xE946;" />-->
<TextBlock Style="{StaticResource BodyStrongTextBlockStyle}" Text="文件信息" />
</StackPanel>
<ScrollViewer Width="300" Height="300">
<StackPanel Spacing="12">
<StackPanel>
<TextBlock Foreground="{ThemeResource TextFillColorSecondaryBrush}" Text="标题" />
<TextBlock Text="{x:Bind ViewModel.TrackRightTapped.Title, Mode=OneWay}" />
</StackPanel>
<StackPanel>
<TextBlock Foreground="{ThemeResource TextFillColorSecondaryBrush}" Text="艺术家" />
<TextBlock Text="{x:Bind ViewModel.TrackRightTapped.Artist, Mode=OneWay}" />
</StackPanel>
<StackPanel>
<TextBlock Foreground="{ThemeResource TextFillColorSecondaryBrush}" Text="专辑" />
<TextBlock Text="{x:Bind ViewModel.TrackRightTapped.Album, Mode=OneWay}" />
</StackPanel>
<StackPanel>
<TextBlock Foreground="{ThemeResource TextFillColorSecondaryBrush}" Text="年份" />
<TextBlock Text="{x:Bind ViewModel.TrackRightTapped.Year, Mode=OneWay}" />
</StackPanel>
<StackPanel>
<TextBlock Foreground="{ThemeResource TextFillColorSecondaryBrush}" Text="时长" />
<TextBlock Text="{x:Bind ViewModel.TrackRightTapped.Duration, Converter={StaticResource SecondsToFormattedTimeConverter}, Mode=OneWay}" />
</StackPanel>
<StackPanel>
<TextBlock Foreground="{ThemeResource TextFillColorSecondaryBrush}" Text="比特率" />
<TextBlock Text="{x:Bind ViewModel.TrackRightTapped.Bitrate, Mode=OneWay}" />
</StackPanel>
<StackPanel>
<TextBlock Foreground="{ThemeResource TextFillColorSecondaryBrush}" Text="采样率" />
<TextBlock Text="{x:Bind ViewModel.TrackRightTapped.SampleRate, Mode=OneWay}" />
</StackPanel>
<StackPanel>
<TextBlock Foreground="{ThemeResource TextFillColorSecondaryBrush}" Text="位深" />
<TextBlock Text="{x:Bind ViewModel.TrackRightTapped.BitDepth, Mode=OneWay}" />
</StackPanel>
<StackPanel>
<TextBlock Foreground="{ThemeResource TextFillColorSecondaryBrush}" Text="格式" />
<TextBlock Text="{x:Bind ViewModel.TrackRightTapped.AudioFormat.Name, Mode=OneWay}" />
</StackPanel>
<StackPanel>
<TextBlock Foreground="{ThemeResource TextFillColorSecondaryBrush}" Text="编码" />
<TextBlock Text="{x:Bind ViewModel.TrackRightTapped.Encoder, Mode=OneWay}" />
</StackPanel>
<StackPanel>
<TextBlock Foreground="{ThemeResource TextFillColorSecondaryBrush}" Text="路径" />
<HyperlinkButton Click="SongPathHyperlinkButton_Click" Content="{x:Bind ViewModel.TrackRightTapped.Path, Mode=OneWay}" />
</StackPanel>
</StackPanel>
</ScrollViewer>
<ListView
x:Name="PlayingQueueListView"
Margin="0,136,0,0"
ItemsSource="{x:Bind ViewModel.TrackPlayingQueue, Mode=OneWay}"
SelectedIndex="{x:Bind ViewModel.PlayingSongIndex, Mode=TwoWay}">
<ListView.ItemTemplate>
<DataTemplate>
<Grid Padding="0,6" RightTapped="SongListViewItemGrid_RightTapped">
<StackPanel Margin="0,0,36,0">
<TextBlock Text="{Binding Title}" TextWrapping="Wrap" />
<TextBlock
Foreground="{StaticResource TextFillColorSecondaryBrush}"
Text="{Binding Artist}"
TextWrapping="Wrap" />
</StackPanel>
</Flyout>
</ListView.ContextFlyout>
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<ItemsStackPanel AreStickyGroupHeadersEnabled="True" />
</ItemsPanelTemplate>
</ListView.ItemsPanel>
<ListView.GroupStyle>
<GroupStyle>
<GroupStyle.HeaderTemplate>
<DataTemplate x:DataType="models:GroupInfoList">
<Border AutomationProperties.AccessibilityView="Raw">
<TextBlock
AutomationProperties.AccessibilityView="Raw"
Style="{ThemeResource TitleTextBlockStyle}"
Text="{Binding}" />
</Border>
</DataTemplate>
</GroupStyle.HeaderTemplate>
</GroupStyle>
</ListView.GroupStyle>
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
<Setter Property="Margin" Value="0" />
<Setter Property="Padding" Value="36,0" />
</Style>
</ListView.ItemContainerStyle>
</ListView>
</SemanticZoom.ZoomedInView>
<Button
HorizontalAlignment="Right"
Content="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
Glyph=&#xE712;}"
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
Style="{StaticResource GhostButtonStyle}">
<Button.Flyout>
<MenuFlyout>
<MenuFlyoutItem Icon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=&#xE738;}" Text="从播放队列移除" />
</MenuFlyout>
</Button.Flyout>
</Button>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<SemanticZoom.ZoomedOutView>
<GridView
MaxWidth="500"
HorizontalAlignment="Center"
VerticalAlignment="Center"
ItemsSource="{x:Bind TracksByTitleCVS.View.CollectionGroups, Mode=OneWay}"
ScrollViewer.IsHorizontalScrollChainingEnabled="False"
SelectionMode="None">
<GridView.ItemTemplate>
<DataTemplate x:DataType="models:GroupInfoList">
<TextBlock Style="{ThemeResource TitleTextBlockStyle}" Text="{Binding}" />
</DataTemplate>
</GridView.ItemTemplate>
</GridView>
</SemanticZoom.ZoomedOutView>
</SemanticZoom>
<Grid Margin="0,48,0,0" Visibility="{x:Bind ViewModel.IsLocalMediaNotFound, Mode=OneWay, Converter={StaticResource BoolToVisibilityConverter}}">
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
<Image MaxWidth="200" Source="/Assets/Planet.png" />
<TextBlock HorizontalAlignment="Center" Text="未在媒体库内找到任何歌曲" />
</StackPanel>
<Grid Margin="0,136,0,0">
<interactivity:Interaction.Behaviors>
<interactivity:DataTriggerBehavior
Binding="{x:Bind ViewModel.TrackPlayingQueue.Count, Mode=OneWay}"
ComparisonCondition="Equal"
Value="0">
<interactivity:ChangePropertyAction PropertyName="Visibility" Value="Visible" />
</interactivity:DataTriggerBehavior>
<interactivity:DataTriggerBehavior
Binding="{x:Bind ViewModel.TrackPlayingQueue.Count, Mode=OneWay}"
ComparisonCondition="NotEqual"
Value="0">
<interactivity:ChangePropertyAction PropertyName="Visibility" Value="Collapsed" />
</interactivity:DataTriggerBehavior>
</interactivity:Interaction.Behaviors>
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
<Image MaxWidth="200" Source="/Assets/Planet.png" />
<TextBlock HorizontalAlignment="Center" Text="播放队列为空" />
</StackPanel>
</Grid>
</Grid>
</Grid>
<Grid Background="{ThemeResource SolidBackgroundFillColorBaseBrush}" Visibility="{x:Bind ViewModel.IsDataLoading, Mode=OneWay, Converter={StaticResource BoolToVisibilityConverter}}">

View File

@@ -3,6 +3,7 @@ using BetterLyrics.WinUI3.Enums;
using BetterLyrics.WinUI3.Helper;
using BetterLyrics.WinUI3.ViewModels;
using CommunityToolkit.Mvvm.DependencyInjection;
using CommunityToolkit.WinUI;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Controls.Primitives;
@@ -42,9 +43,10 @@ namespace BetterLyrics.WinUI3.Views
ViewModel.RefreshSongs();
}
private void SongListVireItemGrid_RightTapped(object sender, RightTappedRoutedEventArgs e)
private void SongListViewItemGrid_RightTapped(object sender, RightTappedRoutedEventArgs e)
{
ViewModel.TrackRightTapped = (Track)((FrameworkElement)sender).DataContext;
SongFileInfoFlyout.ShowAt(sender as FrameworkElement);
}
private async void SongPathHyperlinkButton_Click(object sender, RoutedEventArgs e)
@@ -54,7 +56,21 @@ namespace BetterLyrics.WinUI3.Views
private void SongListVireItemGrid_Tapped(object sender, TappedRoutedEventArgs e)
{
ViewModel.PlayTrack((Track)((FrameworkElement)sender).DataContext);
var track = (Track)((FrameworkElement)sender).DataContext;
ViewModel.InitPlayingQueue(track);
PlayingQueueListView.ScrollIntoView(track, ScrollIntoViewAlignment.Leading);
}
private void EmptyPlayingQueueButton_Click(object sender, RoutedEventArgs e)
{
ViewModel.TrackPlayingQueue.Clear();
ViewModel.PlayingSongIndex = -1;
}
private void ScrollToPlayingItemButton_Click(object sender, RoutedEventArgs e)
{
if (ViewModel.PlayingTrack == null) return;
PlayingQueueListView.ScrollIntoView(ViewModel.PlayingTrack, ScrollIntoViewAlignment.Leading);
}
}
}

View File

@@ -351,18 +351,12 @@
CanDragItems="True"
CanReorderItems="True"
DragItemsCompleted="AlbumArtSearchProvidersListView_DragItemsCompleted"
ItemContainerStyle="{StaticResource ListViewStretchedItemContainerStyle}"
ItemsSource="{x:Bind ViewModel.AlbumArtSearchProvidersInfo, Mode=OneWay}"
SelectionMode="None">
<ListView.OpacityTransition>
<ScalarTransition />
</ListView.OpacityTransition>
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
<Setter Property="Margin" Value="0" />
<Setter Property="Padding" Value="0" />
</Style>
</ListView.ItemContainerStyle>
<ListView.ItemTemplate>
<DataTemplate x:DataType="models:AlbumArtSearchProviderInfo">
<controls:SettingsCard Padding="60,0,48,0" Header="{Binding Provider, Converter={StaticResource AlbumArtSearchProviderToDisplayNameConverter}, Mode=OneWay}">

View File

@@ -1,4 +1,4 @@
> 注:以下内容使用 https://claude.ai/ 依照英文原文翻译
> 注:以下内容含有大语言模型翻译内容
<a href="https://github.com/jayfunc/BetterLyrics/blob/dev/README.md">_**🌐 Click here to see the English version**_</a>
@@ -16,40 +16,44 @@ BetterLyrics
</h3>
## 🎉 本项目已获得少数派推荐!
查看文章:[BetterLyrics 专为 Windows 设计的沉浸式流畅歌词显示工具](https://sspai.com/post/101028)
## 反馈交流群
- [「BetterLyrics」反馈交流群简体中文](https://qun.qq.com/universal-share/share?ac=1&authKey=4Q%2BYTq3wZldYpF5SbS5c19ECFsiYoLZFAIcBNNzYpBUtiEjaZ8sZ%2F%2BnFN0qw3lad&busi_data=eyJncm91cENvZGUiOiIxMDU0NzAwMzg4IiwidG9rZW4iOiJiVnhqemVYN0N5QVc3b1ZkR24wWmZOTUtvUkJoWm1JRWlaWW5iZnlBcXJtZUtGc2FFTHNlUlFZMi9iRm03cWF5IiwidWluIjoiMTM5NTczOTY2MCJ9&data=39UmAihyH_o6CZaOs7nk2mO_lz2ruODoDou6pxxh7utcxP4WF5sbDBDOPvZ_Wqfzeey4441anegsLYQJxkrBAA&svctype=4&tempid=h5_group_info) (1054700388) QQ群
- [「BetterLyrics」反馈交流群繁体中文/英语)](https://discord.gg/5yAQPnyCKv) Discord服务器
- [「BetterLyrics」反馈交流群简体中文](https://qun.qq.com/universal-share/share?ac=1&authKey=4Q%2BYTq3wZldYpF5SbS5c19ECFsiYoLZFAIcBNNzYpBUtiEjaZ8sZ%2F%2BnFN0qw3lad&busi_data=eyJncm91cENvZGUiOiIxMDU0NzAwMzg4IiwidG9rZW4iOiJiVnhqemVYN0N5QVc3b1ZkR24wWmZOTUtvUkJoWm1JRWlaWW5iZnlBcXJtZUtGc2FFTHNlUlFZMi9iRm03cWF5IiwidWluIjoiMTM5NTczOTY2MCJ9&data=39UmAihyH_o6CZaOs7nk2mO_lz2ruODoDou6pxxh7utcxP4WF5sbDBDOPvZ_Wqfzeey4441anegsLYQJxkrBAA&svctype=4&tempid=h5_group_info) (1054700388) QQ
- [「BetterLyrics」反馈交流群繁体中文/英语)](https://discord.gg/5yAQPnyCKv) Discord 服务器
## 核心特色功能
🌟 核心亮点功能
- 动态模糊专辑封面作为背景
- 流畅的歌词淡入淡出、缩放效果
- 切换歌曲时界面平滑过渡
- 逐字渐变卡拉OK效果带光晕
- 沉浸式桌面歌词(悬浮模式)
- 本地翻译支持30种语言
- 🌠 美观的用户界面
- 流畅的动画与视觉特效,打造赏心悦目的歌词体验
- ↔️ 强大的歌词翻译功能
- 支持离线机器翻译,涵盖 30 种语言
- 自动读取本地歌词文件中的嵌入翻译
- 🧩 多种歌词来源
- 本地存储
- 音乐文件(支持嵌入歌词)
- [.lrc](<https://en.wikipedia.org/wiki/LRC_(file_format)>) 歌词文件(兼容标准格式和增强格式)
- [.eslrc](https://github.com/ESLyric/release) 格式
- [.ttml](https://en.wikipedia.org/wiki/Timed_Text_Markup_Language) 格式
- 在线歌词服务
- QQ 音乐
- 网易云音乐
- 酷狗音乐
- [amll-ttml-db](https://github.com/Steve-xmh/amll-ttml-db)
- [LRCLIB](https://lrclib.net/)
- 🪟 多种歌词显示模式
- 标准模式
- 沉浸式歌词体验,搭配丰富动画与动态背景,听歌更享受
- 停靠模式
- 智能歌词栏固定在屏幕边缘,简洁美观、互不打扰
- 桌面模式
- 歌词悬浮于其他窗口之上,边听歌边工作两不误
- 🧠 智能行为支持
- 音乐播放器关闭后自动隐藏歌词窗口,干净整洁不打扰
> 本项目仍在开发中最新版本可能存在bug和意外行为。
## 支持的歌词来源
- 本地存储
- 音乐文件(内嵌歌词)
- [.lrc](<https://en.wikipedia.org/wiki/LRC_(file_format)>) 文件(支持标准格式和增强格式)
- [.eslrc](https://github.com/ESLyric/release) 文件
- [.ttml](https://en.wikipedia.org/wiki/Timed_Text_Markup_Language) 文件
(如需下载歌词,可使用 [LDDC](https://github.com/chenmozhijin/LDDC)
- 在线歌词提供商
- QQ音乐
- 网易云音乐
- 酷狗音乐
- [amll-ttml-db](https://github.com/Steve-xmh/amll-ttml-db)
- [LRCLIB](https://lrclib.net/)
> 本项目仍在开发中,最新版本可能存在 bug 和意外行为。
## 应用截图
@@ -79,7 +83,7 @@ BetterLyrics
## 演示视频
观看我们的介绍视频2025年7月7日上传):[B站链接](https://www.bilibili.com/video/BV1zjGjzfEXh)
观看我们的介绍视频2025 年 7 月 7 日上传):[B 站链接](https://www.bilibili.com/video/BV1zjGjzfEXh)
## 已测试的音乐播放器
@@ -88,13 +92,13 @@ BetterLyrics
- 之后请在 PluginMarket 内安装 InfLink 插件,安装完成后请重启网易云音乐。至此,所有预备操作均已完成,尽情享用吧!
- 酷狗音乐
- 请确保酷狗音乐设置项 “支持系统播放控件,如锁屏界面” 已开启
- 不会广播时间线信息这意味着当您在酷狗音乐中更改播放进度时BetterLyrics无法检测到此更改。
- 不会广播时间线信息这意味着当您在酷狗音乐中更改播放进度时BetterLyrics 无法检测到此更改。
- Apple Music
- 确保您在设置中将时间线阈值设置为约600毫秒进入"设置"-"高级选项"进行更改),否则歌词会不断前后跳动。
- 确保您在设置中将时间线阈值设置为约 600 毫秒(进入"设置"-"高级选项"进行更改),否则歌词会不断前后跳动。
- foobar2000
- 确保您安装了 https://github.com/dumbie/foo_mediacontrol 插件
- Spotify
- QQ音乐
- QQ 音乐
- PotPlayer
- 媒体播放器(系统自带)
- LX 音乐
@@ -130,17 +134,17 @@ BetterLyrics
## 特别感谢
- [Lyricify-Lyrics-Helper](https://github.com/WXRIW/Lyricify-Lyrics-Helper)
- 提供QQ、网易、酷狗音源的歌词获取、解密和解析
- 提供 QQ、网易、酷狗音源的歌词获取、解密和解析
- [LRCLIB](https://lrclib.net/)
- LRCLIB歌词API提供商
- LRCLIB 歌词 API 提供商
- [Audio Tools Library (ATL) for .NET](https://github.com/Zeugma440/atldotnet)
- 用于提取音乐文件中的图片
- [WinUIEx](https://github.com/dotMorten/WinUIEx)
- 提供便捷的Win32 API窗口操作方式
- 提供便捷的 Win32 API 窗口操作方式
- [TagLib#](https://github.com/mono/taglib-sharp)
- 用于读取原始歌词内容
- [Vanara](https://github.com/dahall/Vanara)
- Win32 API包装器
- Win32 API 包装器
- [Stackoverflow - How to animate Margin property in WPF](https://stackoverflow.com/a/21542882/11048731)
- [DevWinUI](https://github.com/ghost1372/DevWinUI)
- [Bilibili -【WinUI3】SystemBackdropController定义云母、亚克力效果](https://www.bilibili.com/video/BV1PY4FevEkS)
@@ -168,4 +172,4 @@ BetterLyrics
## 欢迎提交问题和拉取请求
如果您发现bug请在issues中提交如果您有任何想法也欢迎在这里分享。
如果您发现 bug请在 issues 中提交;如果您有任何想法,也欢迎在这里分享。

View File

@@ -21,34 +21,34 @@ Check out the article: [BetterLyrics An immersive and smooth lyrics display
- [「BetterLyrics」反馈交流群简体中文](https://qun.qq.com/universal-share/share?ac=1&authKey=4Q%2BYTq3wZldYpF5SbS5c19ECFsiYoLZFAIcBNNzYpBUtiEjaZ8sZ%2F%2BnFN0qw3lad&busi_data=eyJncm91cENvZGUiOiIxMDU0NzAwMzg4IiwidG9rZW4iOiJiVnhqemVYN0N5QVc3b1ZkR24wWmZOTUtvUkJoWm1JRWlaWW5iZnlBcXJtZUtGc2FFTHNlUlFZMi9iRm03cWF5IiwidWluIjoiMTM5NTczOTY2MCJ9&data=39UmAihyH_o6CZaOs7nk2mO_lz2ruODoDou6pxxh7utcxP4WF5sbDBDOPvZ_Wqfzeey4441anegsLYQJxkrBAA&svctype=4&tempid=h5_group_info) (1054700388) on QQ
- [「BetterLyrics」Feedback Chat Group (Traditional Chinese / English)](https://discord.gg/5yAQPnyCKv) on Discord
## Highlighted features
## 🌟 Highlighted features
- Dynamic blur album art as background
- Smooth lyrics fade in/out, zoom in/out effects
- Smooth user interface change from song to song
- Gradient Karaoke (with glow) effect on every single character
- Immersive desktop lyrics (dock mode)
- Local translation (supporting 30 languages)
- 🌠 **Pleasing User Interface**
- Fluent animations and effects
- ↔️ **Strong Lyrics Translation**
- Offline machine translation (supporting 30 languages)
- Auto reading local lyrics files for embedded translation
- 🧩 **Various Lyrics Source**
- Local storage
- Music files (with embedded lyrics)
- [.lrc](<https://en.wikipedia.org/wiki/LRC_(file_format)>) files (with both core format and enhanced format)
- [.eslrc](https://github.com/ESLyric/release) files
- [.ttml](https://en.wikipedia.org/wiki/Timed_Text_Markup_Language) files
- Online lyrics providers
- QQ Music
- 网易云音乐 NetEase Cloud Music
- 酷狗音乐 Kugou Music
- [amll-ttml-db](https://github.com/Steve-xmh/amll-ttml-db)
- [LRCLIB](https://lrclib.net/)
- 🪟 **Multiple Display Modes**
- **Standard Mode** Enjoy an immersive listening journey with rich lyrics animations and beautifully dynamic backgrounds
- **Dock Mode** A smart animated lyrics bar docked to your screen edge
- **Desktop Mode** Enjoy immersive lyrics floating above your apps
- 🧠 **Smart Behaviors**
- Auto hide when music player closed
> This project is still under development, bugs and unexpected behaviors may be existed in the latest branch.
## Supported lyrics source
- From your local storage
- Music files (with embedded lyrics)
- [.lrc](<https://en.wikipedia.org/wiki/LRC_(file_format)>) files (with both core format and enhanced format)
- [.eslrc](https://github.com/ESLyric/release) files
- [.ttml](https://en.wikipedia.org/wiki/Timed_Text_Markup_Language) files
(For lyrics downloading, you can use [LDDC](https://github.com/chenmozhijin/LDDC))
- From online lyrics providers
- QQ Music
- 网易云音乐 NetEase Cloud Music
- 酷狗音乐 Kugou Music
- [amll-ttml-db](https://github.com/Steve-xmh/amll-ttml-db)
- [LRCLIB](https://lrclib.net/)
## Screenshots
### Standard mode