mirror of
https://github.com/jayfunc/BetterLyrics.git
synced 2026-01-13 03:34:55 +08:00
Compare commits
24 Commits
v1.0.135.0
...
v1.0.147.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a5b3671ce3 | ||
|
|
181a06c932 | ||
|
|
93d567e21d | ||
|
|
9da8510de6 | ||
|
|
3ed9e599be | ||
|
|
e277faea9e | ||
|
|
21dcd7de4b | ||
|
|
31540beaa0 | ||
|
|
63ffe6b661 | ||
|
|
5cb880021c | ||
|
|
b00f2b5865 | ||
|
|
90a75f1b96 | ||
|
|
735f03542f | ||
|
|
b7853ded26 | ||
|
|
88fc0adbec | ||
|
|
a3366422a2 | ||
|
|
8f6e106282 | ||
|
|
08d4f4ce90 | ||
|
|
212041a509 | ||
|
|
c0f2b4106a | ||
|
|
c704edde19 | ||
|
|
35de1ef7db | ||
|
|
1c8840e053 | ||
|
|
2d0839d777 |
@@ -143,7 +143,7 @@
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.26100.6901" />
|
||||
<PackageReference Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.26100.7175" />
|
||||
<PackageReference Include="Microsoft.WindowsAppSDK" Version="1.8.251106002" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(WapProjPath)\Microsoft.DesktopBridge.targets" />
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
<Identity
|
||||
Name="37412.BetterLyrics"
|
||||
Publisher="CN=E1428B0E-DC1D-4EA4-ACB1-4556569D5BA9"
|
||||
Version="1.0.135.0" />
|
||||
Version="1.0.147.0" />
|
||||
|
||||
<mp:PhoneIdentity PhoneProductId="ca4a4830-fc19-40d9-b823-53e2bff3d816" PhonePublisherId="00000000-0000-0000-0000-000000000000"/>
|
||||
|
||||
|
||||
@@ -69,6 +69,7 @@
|
||||
<converter:IndexToDisplayConverter x:Key="IndexToDisplayConverter" />
|
||||
<converter:IntToDoubleConverter x:Key="IntToDoubleConverter" />
|
||||
<converter:MillisecondsToSecondsConverter x:Key="MillisecondsToSecondsConverter" />
|
||||
<converter:PictureInfosToImageSourceConverter x:Key="PictureInfosToImageSourceConverter" />
|
||||
|
||||
<converters:BoolToVisibilityConverter x:Key="BoolToVisibilityConverter" />
|
||||
<converters:BoolNegationConverter x:Key="BoolNegationConverter" />
|
||||
|
||||
@@ -34,6 +34,7 @@
|
||||
<None Remove="Controls\LyricsWindowSwitchControl.xaml" />
|
||||
<None Remove="Controls\MediaSettingsControl.xaml" />
|
||||
<None Remove="Controls\PlaybackSettingsControl.xaml" />
|
||||
<None Remove="Controls\PropertyRow.xaml" />
|
||||
<None Remove="Controls\ShortcutTextBox.xaml" />
|
||||
<None Remove="Controls\SystemTray.xaml" />
|
||||
<None Remove="Views\LyricsSearchWindow.xaml" />
|
||||
@@ -65,7 +66,6 @@
|
||||
<PackageReference Include="csharp-kana" Version="1.0.2" />
|
||||
<PackageReference Include="csharp-pinyin" Version="1.0.1" />
|
||||
<PackageReference Include="DevWinUI.Controls" Version="9.5.0" />
|
||||
<PackageReference Include="DiscordRichPresence" Version="1.6.1.70" />
|
||||
<PackageReference Include="Dubya.WindowsMediaController" Version="2.5.5" />
|
||||
<PackageReference Include="F23.StringSimilarity" Version="7.0.0" />
|
||||
<PackageReference Include="H.NotifyIcon.WinUI" Version="2.3.2" />
|
||||
@@ -74,7 +74,7 @@
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="10.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging" Version="10.0.0" />
|
||||
<PackageReference Include="Microsoft.Graphics.Win2D" Version="1.3.2" />
|
||||
<PackageReference Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.26100.6901" />
|
||||
<PackageReference Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.26100.7175" />
|
||||
<PackageReference Include="Microsoft.WindowsAppSDK" Version="1.8.251106002" />
|
||||
<PackageReference Include="NAudio.Wasapi" Version="2.2.1" />
|
||||
<PackageReference Include="Nito.AsyncEx" Version="5.1.2" />
|
||||
@@ -91,6 +91,7 @@
|
||||
<PackageReference Include="Vanara.PInvoke.Shell32" Version="4.2.1" />
|
||||
<PackageReference Include="Vanara.PInvoke.User32" Version="4.2.1" />
|
||||
<PackageReference Include="Vanara.Windows.Shell" Version="4.2.1" />
|
||||
<PackageReference Include="VCollab.DiscordRichPresence" Version="1.7.0" />
|
||||
<PackageReference Include="WinUIEx" Version="2.9.0" />
|
||||
<PackageReference Include="z440.atl.core" Version="7.8.0" />
|
||||
</ItemGroup>
|
||||
@@ -333,6 +334,11 @@
|
||||
<ItemGroup>
|
||||
<Folder Include="TemplateSelector\" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Page Update="Controls\PropertyRow.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Page Update="Controls\AboutControl.xaml">
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
|
||||
@@ -2,16 +2,17 @@
|
||||
{
|
||||
public static class Link
|
||||
{
|
||||
public const string MicrosoftStoreUrl = "https://apps.microsoft.com/detail/9p1wcd1p597r";
|
||||
public const string GitHubUrl = "https://github.com/jayfunc/BetterLyrics";
|
||||
public const string ShareHubUrl = $"{GitHubUrl}/blob/dev/ShareHub/index.md";
|
||||
public const string TermsOfServiceUrl = $"{GitHubUrl}/blob/dev/TermsofService.md";
|
||||
public const string PrivacyPolicy = $"{GitHubUrl}/blob/dev/PrivacyPolicy.md";
|
||||
public const string WikiUrl = "https://jayfunc.blog/work/betterlyrics";
|
||||
public const string AppleMusicCfgUrl = $"{WikiUrl}#lyrics-sources-configuration";
|
||||
public const string FAQUrl = $"{WikiUrl}#faq";
|
||||
public const string QQGroupUrl = "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";
|
||||
public const string DiscordUrl = "https://discord.gg/5yAQPnyCKv";
|
||||
public const string TelegramUrl = "https://t.me/+svhSLZ7awPsxNGY1";
|
||||
public const string MicrosoftStore = "https://apps.microsoft.com/detail/9p1wcd1p597r";
|
||||
|
||||
public const string GitHub = "https://github.com/jayfunc/BetterLyrics";
|
||||
public const string ShareHub = $"{GitHub}/blob/dev/ShareHub/index.md";
|
||||
public const string TermsOfService = $"{GitHub}/blob/dev/TermsofService.md";
|
||||
public const string PrivacyPolicy = $"{GitHub}/blob/dev/PrivacyPolicy.md";
|
||||
public const string UserGuide = $"{GitHub}/wiki/User-Guide";
|
||||
public const string AppleMusicCfg = $"{UserGuide}#lyrics-source-configuration";
|
||||
|
||||
public const string QQGroup = "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";
|
||||
public const string Discord = "https://discord.gg/5yAQPnyCKv";
|
||||
public const string Telegram = "https://t.me/+svhSLZ7awPsxNGY1";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,4 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace BetterLyrics.WinUI3.Constants
|
||||
namespace BetterLyrics.WinUI3.Constants
|
||||
{
|
||||
public class PlayerName
|
||||
{
|
||||
|
||||
@@ -5,5 +5,6 @@ namespace BetterLyrics.WinUI3.Constants
|
||||
public static class Time
|
||||
{
|
||||
public static readonly TimeSpan DebounceTimeout = TimeSpan.FromMilliseconds(300);
|
||||
public static readonly TimeSpan AnimationDuration = TimeSpan.FromMilliseconds(300);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,11 +51,10 @@
|
||||
<dev:SettingsCard HorizontalContentAlignment="Left" ContentAlignment="Left">
|
||||
<StackPanel Spacing="6">
|
||||
<StackPanel Margin="-12,0,0,0" Orientation="Horizontal">
|
||||
<HyperlinkButton Content="GitHub" NavigateUri="{x:Bind const:Link.GitHubUrl}" />
|
||||
<HyperlinkButton Content="Wiki" NavigateUri="{x:Bind const:Link.WikiUrl}" />
|
||||
<HyperlinkButton Content="FAQ" NavigateUri="{x:Bind const:Link.FAQUrl}" />
|
||||
<HyperlinkButton Content="GitHub" NavigateUri="{x:Bind const:Link.GitHub}" />
|
||||
<HyperlinkButton x:Uid="UserGuide" NavigateUri="{x:Bind const:Link.UserGuide}" />
|
||||
<HyperlinkButton x:Uid="PrivacyPolicy" NavigateUri="{x:Bind const:Link.PrivacyPolicy}" />
|
||||
<HyperlinkButton x:Uid="TermsOfService" NavigateUri="{x:Bind const:Link.TermsOfServiceUrl}" />
|
||||
<HyperlinkButton x:Uid="TermsOfService" NavigateUri="{x:Bind const:Link.TermsOfService}" />
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
|
||||
@@ -65,9 +64,9 @@
|
||||
<StackPanel Spacing="6">
|
||||
<TextBlock x:Uid="SetingsPageFeedback" />
|
||||
<StackPanel Margin="-12,0,0,0" Orientation="Horizontal">
|
||||
<HyperlinkButton x:Uid="SettingsPageQQGroup" NavigateUri="{x:Bind const:Link.QQGroupUrl}" />
|
||||
<HyperlinkButton x:Uid="SettingsPageDiscord" NavigateUri="{x:Bind const:Link.DiscordUrl}" />
|
||||
<HyperlinkButton x:Uid="SettingsPageTelegram" NavigateUri="{x:Bind const:Link.TelegramUrl}" />
|
||||
<HyperlinkButton x:Uid="SettingsPageQQGroup" NavigateUri="{x:Bind const:Link.QQGroup}" />
|
||||
<HyperlinkButton x:Uid="SettingsPageDiscord" NavigateUri="{x:Bind const:Link.Discord}" />
|
||||
<HyperlinkButton x:Uid="SettingsPageTelegram" NavigateUri="{x:Bind const:Link.Telegram}" />
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</dev:SettingsCard>
|
||||
|
||||
174
BetterLyrics.WinUI3/BetterLyrics.WinUI3/Controls/InstantTip.cs
Normal file
174
BetterLyrics.WinUI3/BetterLyrics.WinUI3/Controls/InstantTip.cs
Normal file
@@ -0,0 +1,174 @@
|
||||
using Microsoft.UI.Xaml;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using Microsoft.UI.Xaml.Controls.Primitives;
|
||||
using Microsoft.UI.Xaml.Input;
|
||||
using Microsoft.UI.Xaml.Media;
|
||||
using System.Collections.Generic;
|
||||
using System.Numerics;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Windows.Foundation;
|
||||
|
||||
namespace BetterLyrics.WinUI3.Controls
|
||||
{
|
||||
public static class InstantTip
|
||||
{
|
||||
public static readonly DependencyProperty ContentProperty =
|
||||
DependencyProperty.RegisterAttached("Content", typeof(object), typeof(InstantTip), new PropertyMetadata(null, OnContentChanged));
|
||||
|
||||
public static void SetContent(DependencyObject element, object value) => element.SetValue(ContentProperty, value);
|
||||
public static object GetContent(DependencyObject element) => element.GetValue(ContentProperty);
|
||||
|
||||
private static Dictionary<int, Popup> _activePopups = [];
|
||||
|
||||
private static void OnContentChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
if (d is FrameworkElement element)
|
||||
{
|
||||
element.PointerEntered -= Element_PointerEntered;
|
||||
element.PointerExited -= Element_PointerExited;
|
||||
element.Unloaded -= Element_Unloaded;
|
||||
|
||||
if (e.NewValue != null)
|
||||
{
|
||||
element.PointerEntered += Element_PointerEntered;
|
||||
element.PointerExited += Element_PointerExited;
|
||||
element.Unloaded += Element_Unloaded;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void Element_PointerEntered(object sender, PointerRoutedEventArgs e)
|
||||
{
|
||||
if (sender is FrameworkElement element)
|
||||
{
|
||||
int hashCode = element.GetHashCode();
|
||||
|
||||
if (_activePopups.ContainsKey(hashCode))
|
||||
{
|
||||
_activePopups.TryGetValue(hashCode, out var popup);
|
||||
if (popup != null)
|
||||
{
|
||||
popup.IsOpen = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var rawContent = GetContent(element);
|
||||
|
||||
if (rawContent == null) return;
|
||||
|
||||
// 创建可视卡片容器 (Visual Card)
|
||||
var visualCard = new Grid
|
||||
{
|
||||
Background = (Brush)Application.Current.Resources["AcrylicBackgroundFillColorDefaultBrush"],
|
||||
CornerRadius = new CornerRadius(4),
|
||||
Shadow = new ThemeShadow(),
|
||||
Translation = new Vector3(0, 0, 32),
|
||||
Padding = new Thickness(8, 4, 8, 4)
|
||||
};
|
||||
|
||||
var popupContent = new Grid
|
||||
{
|
||||
IsHitTestVisible = false,
|
||||
Opacity = 0,
|
||||
Padding = new Thickness(16)
|
||||
};
|
||||
popupContent.Children.Add(visualCard);
|
||||
|
||||
var popup = new Popup
|
||||
{
|
||||
Child = popupContent,
|
||||
IsHitTestVisible = false,
|
||||
ShouldConstrainToRootBounds = false,
|
||||
XamlRoot = element.XamlRoot,
|
||||
};
|
||||
|
||||
object finalContent = rawContent;
|
||||
|
||||
if (rawContent is ToolTip toolTipWrapper)
|
||||
{
|
||||
finalContent = toolTipWrapper.Content;
|
||||
}
|
||||
|
||||
if (finalContent is string text)
|
||||
{
|
||||
var textBlock = new TextBlock
|
||||
{
|
||||
Text = text,
|
||||
FontSize = 12,
|
||||
MaxWidth = 320,
|
||||
TextWrapping = TextWrapping.Wrap,
|
||||
Foreground = (Brush)Application.Current.Resources["TextFillColorPrimaryBrush"]
|
||||
};
|
||||
visualCard.Children.Add(textBlock);
|
||||
}
|
||||
else if (finalContent != null)
|
||||
{
|
||||
var textBlock = new TextBlock
|
||||
{
|
||||
Text = finalContent.ToString(),
|
||||
FontSize = 12,
|
||||
MaxWidth = 320,
|
||||
TextWrapping = TextWrapping.Wrap,
|
||||
Foreground = (Brush)Application.Current.Resources["TextFillColorPrimaryBrush"]
|
||||
};
|
||||
visualCard.Children.Add(textBlock);
|
||||
}
|
||||
|
||||
var transform = element.TransformToVisual(null);
|
||||
var point = transform.TransformPoint(new Point(0, element.ActualHeight));
|
||||
|
||||
popup.VerticalOffset = point.Y;
|
||||
popup.HorizontalOffset = point.X - popupContent.Padding.Left;
|
||||
|
||||
popup.IsOpen = true;
|
||||
|
||||
App.Current.Resources.DispatcherQueue.TryEnqueue(Microsoft.UI.Dispatching.DispatcherQueuePriority.Low, () =>
|
||||
{
|
||||
popupContent.OpacityTransition = new ScalarTransition();
|
||||
popupContent.Opacity = 1;
|
||||
});
|
||||
|
||||
_activePopups.Add(hashCode, popup);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void Element_PointerExited(object sender, PointerRoutedEventArgs e)
|
||||
{
|
||||
var element = sender as FrameworkElement;
|
||||
|
||||
if (element != null)
|
||||
{
|
||||
int hashCode = element.GetHashCode();
|
||||
|
||||
if (!_activePopups.ContainsKey(hashCode))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_activePopups.TryGetValue(hashCode, out var popup);
|
||||
if (popup != null)
|
||||
{
|
||||
if (popup.Child is Grid popupContent)
|
||||
{
|
||||
popupContent.Opacity = 0;
|
||||
}
|
||||
popup.IsOpen = false;
|
||||
_activePopups.Remove(hashCode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void Element_Unloaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (sender is FrameworkElement element)
|
||||
{
|
||||
element.PointerEntered -= Element_PointerEntered;
|
||||
element.PointerExited -= Element_PointerExited;
|
||||
element.Unloaded -= Element_Unloaded;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -224,7 +224,7 @@
|
||||
</dev:SettingsExpander.Items>
|
||||
</dev:SettingsExpander>
|
||||
|
||||
<!-- 滚动动画 -->
|
||||
<!-- 歌词动画 -->
|
||||
<dev:SettingsExpander x:Uid="SettingsPageScrollEasing" HeaderIcon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=}">
|
||||
<ComboBox SelectedIndex="{x:Bind LyricsEffectSettings.LyricsScrollEasingType, Mode=TwoWay, Converter={StaticResource EnumToIntConverter}}">
|
||||
<ComboBoxItem x:Uid="SettingsPageEasingTypeLinear" />
|
||||
@@ -246,7 +246,7 @@
|
||||
Default="500"
|
||||
Frequency="50"
|
||||
Maximum="1000"
|
||||
Minimum="50"
|
||||
Minimum="0"
|
||||
Unit="ms"
|
||||
Value="{x:Bind LyricsEffectSettings.LyricsScrollTopDuration, Mode=TwoWay}" />
|
||||
</dev:SettingsCard>
|
||||
@@ -255,7 +255,7 @@
|
||||
Default="500"
|
||||
Frequency="50"
|
||||
Maximum="1000"
|
||||
Minimum="50"
|
||||
Minimum="0"
|
||||
Unit="ms"
|
||||
Value="{x:Bind LyricsEffectSettings.LyricsScrollDuration, Mode=TwoWay}" />
|
||||
</dev:SettingsCard>
|
||||
@@ -264,7 +264,7 @@
|
||||
Default="500"
|
||||
Frequency="50"
|
||||
Maximum="1000"
|
||||
Minimum="50"
|
||||
Minimum="0"
|
||||
Unit="ms"
|
||||
Value="{x:Bind LyricsEffectSettings.LyricsScrollBottomDuration, Mode=TwoWay}" />
|
||||
</dev:SettingsCard>
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
<Grid Grid.Column="0">
|
||||
<ScrollViewer>
|
||||
<StackPanel Spacing="{StaticResource SettingsCardSpacing}">
|
||||
|
||||
<TextBlock x:Uid="LyricsSearchControlSongInfoMapping" Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" />
|
||||
|
||||
<Grid
|
||||
@@ -34,22 +35,32 @@
|
||||
Background="{ThemeResource CardBackgroundFillColorDefaultBrush}"
|
||||
CornerRadius="4">
|
||||
<StackPanel Spacing="6">
|
||||
<TextBlock x:Uid="LyricsSearchControlTitle" />
|
||||
<TextBlock
|
||||
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
|
||||
IsTextSelectionEnabled="True"
|
||||
Text="{x:Bind ViewModel.MappedSongSearchQuery.OriginalTitle, Mode=OneWay}"
|
||||
TextWrapping="Wrap" />
|
||||
|
||||
<TextBlock x:Uid="LyricsSearchControlMappedAs" VerticalAlignment="Center" />
|
||||
<TextBox Text="{x:Bind ViewModel.MappedSongSearchQuery.MappedTitle, Mode=TwoWay}" TextWrapping="Wrap" />
|
||||
<Button
|
||||
VerticalAlignment="Center"
|
||||
Command="{x:Bind ViewModel.ResetMappedTitleCommand}"
|
||||
Content="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
|
||||
FontSize=12,
|
||||
Glyph=}"
|
||||
Style="{StaticResource GhostButtonStyle}" />
|
||||
<local:PropertyRow x:Uid="LyricsSearchControlTitle" Value="{x:Bind ViewModel.MappedSongSearchQuery.OriginalTitle, Mode=OneWay}" />
|
||||
|
||||
<Grid ColumnSpacing="6">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock
|
||||
x:Uid="LyricsSearchControlMappedAs"
|
||||
Grid.Column="0"
|
||||
VerticalAlignment="Center" />
|
||||
<TextBox
|
||||
Grid.Column="1"
|
||||
Text="{x:Bind ViewModel.MappedSongSearchQuery.MappedTitle, Mode=TwoWay}"
|
||||
TextWrapping="Wrap" />
|
||||
<Button
|
||||
Grid.Column="2"
|
||||
VerticalAlignment="Center"
|
||||
Command="{x:Bind ViewModel.ResetMappedTitleCommand}"
|
||||
Content="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
|
||||
FontSize=12,
|
||||
Glyph=}"
|
||||
Style="{StaticResource GhostButtonStyle}" />
|
||||
</Grid>
|
||||
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
@@ -60,33 +71,52 @@
|
||||
CornerRadius="4">
|
||||
<StackPanel Spacing="6">
|
||||
|
||||
<TextBlock x:Uid="LyricsSearchControlArtist" />
|
||||
<TextBlock
|
||||
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
|
||||
IsTextSelectionEnabled="True"
|
||||
Text="{x:Bind ViewModel.MappedSongSearchQuery.OriginalArtist, Mode=OneWay}"
|
||||
TextWrapping="Wrap" />
|
||||
<local:PropertyRow x:Uid="LyricsSearchControlArtist" Value="{x:Bind ViewModel.MappedSongSearchQuery.OriginalArtist, Mode=OneWay}" />
|
||||
|
||||
<TextBlock x:Uid="LyricsSearchControlMappedAs" VerticalAlignment="Center" />
|
||||
<TextBox Text="{x:Bind ViewModel.MappedSongSearchQuery.MappedArtist, Mode=TwoWay}" TextWrapping="Wrap" />
|
||||
<RichTextBlock Foreground="{ThemeResource TextFillColorSecondaryBrush}" TextWrapping="Wrap">
|
||||
<Grid ColumnSpacing="6">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock
|
||||
x:Uid="LyricsSearchControlMappedAs"
|
||||
Grid.Column="0"
|
||||
VerticalAlignment="Center" />
|
||||
<TextBox
|
||||
Grid.Column="1"
|
||||
Text="{x:Bind ViewModel.MappedSongSearchQuery.MappedArtist, Mode=TwoWay}"
|
||||
TextWrapping="Wrap" />
|
||||
<Button
|
||||
Grid.Column="2"
|
||||
VerticalAlignment="Center"
|
||||
Command="{x:Bind ViewModel.ResetMappedArtistCommand}"
|
||||
Content="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
|
||||
FontSize=12,
|
||||
Glyph=}"
|
||||
Style="{StaticResource GhostButtonStyle}" />
|
||||
</Grid>
|
||||
|
||||
<RichTextBlock
|
||||
FontSize="12"
|
||||
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
|
||||
TextWrapping="Wrap">
|
||||
<Paragraph>
|
||||
<Run Text="*" />
|
||||
<Run x:Uid="ArtistsSplitHint" />
|
||||
<Run Text=";" />
|
||||
<Run Text="," />
|
||||
<Run Text="/" />
|
||||
<Run Text=";" />
|
||||
<Run Text="、" />
|
||||
<Run Text="," />
|
||||
</Paragraph>
|
||||
</RichTextBlock>
|
||||
<Button
|
||||
VerticalAlignment="Center"
|
||||
Command="{x:Bind ViewModel.ResetMappedArtistCommand}"
|
||||
Content="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
|
||||
FontSize=12,
|
||||
Glyph=}"
|
||||
Style="{StaticResource GhostButtonStyle}" />
|
||||
|
||||
<RichTextBlock
|
||||
FontSize="12"
|
||||
FontWeight="Bold"
|
||||
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
|
||||
Loaded="ArtistsSplitHintRichTextBlock_Loaded"
|
||||
TextWrapping="Wrap">
|
||||
<Paragraph>
|
||||
<Run Text="; , / ; 、 ," />
|
||||
</Paragraph>
|
||||
</RichTextBlock>
|
||||
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
@@ -97,22 +127,32 @@
|
||||
CornerRadius="4">
|
||||
<StackPanel Spacing="6">
|
||||
|
||||
<TextBlock x:Uid="LyricsSearchControlAlbum" />
|
||||
<TextBlock
|
||||
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
|
||||
IsTextSelectionEnabled="True"
|
||||
Text="{x:Bind ViewModel.MappedSongSearchQuery.OriginalAlbum, Mode=OneWay}"
|
||||
TextWrapping="Wrap" />
|
||||
<local:PropertyRow x:Uid="LyricsSearchControlAlbum" Value="{x:Bind ViewModel.MappedSongSearchQuery.OriginalAlbum, Mode=OneWay}" />
|
||||
|
||||
<TextBlock x:Uid="LyricsSearchControlMappedAs" VerticalAlignment="Center" />
|
||||
<TextBox Text="{x:Bind ViewModel.MappedSongSearchQuery.MappedAlbum, Mode=TwoWay}" TextWrapping="Wrap" />
|
||||
<Button
|
||||
VerticalAlignment="Center"
|
||||
Command="{x:Bind ViewModel.ResetMappedAlbumCommand}"
|
||||
Content="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
|
||||
FontSize=12,
|
||||
Glyph=}"
|
||||
Style="{StaticResource GhostButtonStyle}" />
|
||||
<Grid ColumnSpacing="6">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<TextBlock
|
||||
x:Uid="LyricsSearchControlMappedAs"
|
||||
Grid.Column="0"
|
||||
VerticalAlignment="Center" />
|
||||
<TextBox
|
||||
Grid.Column="1"
|
||||
Text="{x:Bind ViewModel.MappedSongSearchQuery.MappedAlbum, Mode=TwoWay}"
|
||||
TextWrapping="Wrap" />
|
||||
<Button
|
||||
Grid.Column="2"
|
||||
VerticalAlignment="Center"
|
||||
Command="{x:Bind ViewModel.ResetMappedAlbumCommand}"
|
||||
Content="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
|
||||
FontSize=12,
|
||||
Glyph=}"
|
||||
Style="{StaticResource GhostButtonStyle}" />
|
||||
</Grid>
|
||||
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
@@ -136,66 +176,29 @@
|
||||
<ListView.ItemTemplate>
|
||||
<DataTemplate x:DataType="models:LyricsSearchResult">
|
||||
<ListViewItem IsEnabled="{x:Bind IsFound}">
|
||||
<StackPanel Padding="3,6" Opacity="{x:Bind IsFound, Converter={StaticResource BoolToPartialOpacityConverter}}">
|
||||
<HyperlinkButton
|
||||
Padding="0"
|
||||
Content="{x:Bind Provider, Converter={StaticResource LyricsSearchProviderToDisplayNameConverter}}"
|
||||
NavigateUri="{x:Bind Reference, FallbackValue=about:blank}" />
|
||||
<!-- Title -->
|
||||
<Grid ColumnSpacing="12" Visibility="{x:Bind IsFound, Converter={StaticResource BoolToVisibilityConverter}}">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock x:Uid="LyricsSearchControlTitle" Grid.Column="0" />
|
||||
<RichTextBlock Grid.Column="1" Foreground="{ThemeResource TextFillColorSecondaryBrush}">
|
||||
<Paragraph>
|
||||
<Run Text="{x:Bind Title, TargetNullValue=N/A, Mode=OneWay}" />
|
||||
</Paragraph>
|
||||
</RichTextBlock>
|
||||
</Grid>
|
||||
<!-- Artist -->
|
||||
<Grid ColumnSpacing="12" Visibility="{x:Bind IsFound, Converter={StaticResource BoolToVisibilityConverter}}">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock x:Uid="LyricsSearchControlArtist" Grid.Column="0" />
|
||||
<RichTextBlock Grid.Column="1" Foreground="{ThemeResource TextFillColorSecondaryBrush}">
|
||||
<Paragraph>
|
||||
<Run Text="{x:Bind DisplayArtists, TargetNullValue=N/A, Mode=OneWay}" />
|
||||
</Paragraph>
|
||||
</RichTextBlock>
|
||||
</Grid>
|
||||
<!-- Album -->
|
||||
<Grid ColumnSpacing="12" Visibility="{x:Bind IsFound, Converter={StaticResource BoolToVisibilityConverter}}">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock x:Uid="LyricsSearchControlAlbum" Grid.Column="0" />
|
||||
<RichTextBlock Grid.Column="1" Foreground="{ThemeResource TextFillColorSecondaryBrush}">
|
||||
<Paragraph>
|
||||
<Run Text="{x:Bind Album, TargetNullValue=N/A, Mode=OneWay}" />
|
||||
</Paragraph>
|
||||
</RichTextBlock>
|
||||
</Grid>
|
||||
<!-- Duration -->
|
||||
<StackPanel
|
||||
Orientation="Horizontal"
|
||||
Spacing="6"
|
||||
Visibility="{x:Bind IsFound, Converter={StaticResource BoolToVisibilityConverter}}">
|
||||
<TextBlock x:Uid="LyricsSearchControlDurauion" />
|
||||
<TextBlock Foreground="{ThemeResource TextFillColorSecondaryBrush}" Text="{x:Bind Duration}" />
|
||||
<TextBlock Foreground="{ThemeResource TextFillColorSecondaryBrush}" Text="s" />
|
||||
</StackPanel>
|
||||
<!-- Match percentage -->
|
||||
<StackPanel
|
||||
Orientation="Horizontal"
|
||||
Spacing="6"
|
||||
Visibility="{x:Bind IsFound, Converter={StaticResource BoolToVisibilityConverter}}">
|
||||
<TextBlock x:Uid="LyricsPageMatchPercentage" />
|
||||
<TextBlock Foreground="{ThemeResource TextFillColorSecondaryBrush}" Text="{x:Bind MatchPercentage}" />
|
||||
<StackPanel Padding="0,6" Opacity="{x:Bind IsFound, Converter={StaticResource BoolToPartialOpacityConverter}}">
|
||||
<local:PropertyRow
|
||||
Margin="-8,0,0,0"
|
||||
Link="{x:Bind Reference, Mode=OneWay}"
|
||||
ToolTipService.ToolTip="{x:Bind Reference, TargetNullValue=N/A, Mode=OneWay}"
|
||||
Value="{x:Bind Provider, Mode=OneWay, Converter={StaticResource LyricsSearchProviderToDisplayNameConverter}}" />
|
||||
<!-- Lyrics search result -->
|
||||
<StackPanel Visibility="{x:Bind IsFound, Converter={StaticResource BoolToVisibilityConverter}}">
|
||||
<local:PropertyRow x:Uid="SettingsPageSongTitle" Value="{x:Bind Title, TargetNullValue=N/A, Mode=OneWay}" />
|
||||
<local:PropertyRow x:Uid="SettingsPageArtist" Value="{x:Bind DisplayArtists, TargetNullValue=N/A, Mode=OneWay}" />
|
||||
<local:PropertyRow x:Uid="SettingsPageAlbum" Value="{x:Bind Album, TargetNullValue=N/A, Mode=OneWay}" />
|
||||
<local:PropertyRow
|
||||
x:Uid="LyricsSearchControlDurauion"
|
||||
Unit="s"
|
||||
Value="{x:Bind Duration, TargetNullValue=N/A, Mode=OneWay}" />
|
||||
<local:PropertyRow
|
||||
x:Uid="LyricsPageMatchPercentage"
|
||||
Unit="%"
|
||||
Value="{x:Bind MatchPercentage, Mode=OneWay}" />
|
||||
<local:PropertyRow
|
||||
x:Uid="LyricsPageCachePath"
|
||||
Link="{x:Bind SelfPath, TargetNullValue=N/A, Mode=OneWay}"
|
||||
ToolTipService.ToolTip="{x:Bind SelfPath, TargetNullValue=N/A, Mode=OneWay}" />
|
||||
</StackPanel>
|
||||
<!-- NOT FOUND -->
|
||||
<TextBlock
|
||||
@@ -266,23 +269,30 @@
|
||||
</interactivity:DataTriggerBehavior>
|
||||
</interactivity:Interaction.Behaviors>
|
||||
<Pivot.HeaderTemplate>
|
||||
<DataTemplate>
|
||||
<TextBlock Style="{StaticResource BodyTextBlockStyle}" Text="{Binding LanguageCode, Mode=OneWay, Converter={StaticResource LanguageCodeToDisplayedNameConverter}}" />
|
||||
<DataTemplate x:DataType="models:LyricsData">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBlock Style="{StaticResource BodyTextBlockStyle}" Text="{x:Bind LanguageCode, Mode=OneWay, Converter={StaticResource LanguageCodeToDisplayedNameConverter}}" />
|
||||
<InfoBadge
|
||||
x:Uid="LyricsSearchControlAutoGenerated"
|
||||
Margin="6,0,0,0"
|
||||
Style="{StaticResource StringInfoBadgeStyle}"
|
||||
Visibility="{x:Bind AutoGenerated, Converter={StaticResource BoolToVisibilityConverter}, Mode=OneWay}" />
|
||||
</StackPanel>
|
||||
</DataTemplate>
|
||||
</Pivot.HeaderTemplate>
|
||||
<Pivot.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<ListView ItemsSource="{Binding LyricsLines, Mode=OneWay}" SelectionChanged="ListView_SelectionChanged">
|
||||
<DataTemplate x:DataType="models:LyricsData">
|
||||
<ListView ItemsSource="{x:Bind LyricsLines, Mode=OneWay}" SelectionChanged="ListView_SelectionChanged">
|
||||
<ListView.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<DataTemplate x:DataType="models:LyricsLine">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<TextBlock Foreground="{ThemeResource SystemFillColorNeutralBrush}" Text="{Binding StartMs, Converter={StaticResource MillisecondsToFormattedTimeConverter}}" />
|
||||
<TextBlock Foreground="{ThemeResource SystemFillColorNeutralBrush}" Text="{x:Bind StartMs, Mode=OneWay, Converter={StaticResource MillisecondsToFormattedTimeConverter}}" />
|
||||
<TextBlock
|
||||
Margin="1,0"
|
||||
Foreground="{ThemeResource SystemFillColorNeutralBrush}"
|
||||
Text="-" />
|
||||
<TextBlock Foreground="{ThemeResource SystemFillColorNeutralBrush}" Text="{Binding EndMs, Converter={StaticResource MillisecondsToFormattedTimeConverter}}" />
|
||||
<TextBlock Margin="6,0" Text="{Binding OriginalText}" />
|
||||
<TextBlock Foreground="{ThemeResource SystemFillColorNeutralBrush}" Text="{x:Bind EndMs, Mode=OneWay, Converter={StaticResource MillisecondsToFormattedTimeConverter}}" />
|
||||
<TextBlock Margin="6,0" Text="{x:Bind OriginalText, Mode=OneWay}" />
|
||||
</StackPanel>
|
||||
</DataTemplate>
|
||||
</ListView.ItemTemplate>
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
using BetterLyrics.WinUI3.Models;
|
||||
using BetterLyrics.WinUI3.ViewModels;
|
||||
using CommunityToolkit.Mvvm.DependencyInjection;
|
||||
using Microsoft.UI;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using Microsoft.UI.Xaml.Documents;
|
||||
using Microsoft.UI.Xaml.Media;
|
||||
|
||||
// To learn more about WinUI, the WinUI project structure,
|
||||
// and more about our project templates, see: http://aka.ms/winui-project-info.
|
||||
@@ -22,5 +25,26 @@ namespace BetterLyrics.WinUI3.Controls
|
||||
{
|
||||
ViewModel.SelectedLyricsLine = e.OriginalSource as LyricsLine;
|
||||
}
|
||||
|
||||
private void ArtistsSplitHintRichTextBlock_Loaded(object sender, Microsoft.UI.Xaml.RoutedEventArgs e)
|
||||
{
|
||||
if (sender is RichTextBlock richTextBlock)
|
||||
{
|
||||
TextHighlighter highlighter = new()
|
||||
{
|
||||
Background = App.Current.Resources["AccentTextFillColorPrimaryBrush"] as SolidColorBrush,
|
||||
Ranges =
|
||||
{
|
||||
new() { StartIndex = 0, Length = 1 },
|
||||
new() { StartIndex = 5, Length = 1 },
|
||||
new() { StartIndex = 10, Length = 1 },
|
||||
new() { StartIndex = 15, Length = 1 },
|
||||
new() { StartIndex = 20, Length = 1 },
|
||||
new() { StartIndex = 25, Length = 1 },
|
||||
}
|
||||
};
|
||||
richTextBlock.TextHighlighters.Add(highlighter);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,7 +60,7 @@
|
||||
</Button>
|
||||
|
||||
<!-- Sharing hub -->
|
||||
<HyperlinkButton x:Uid="SettingsPageShareHub" NavigateUri="{x:Bind constants:Link.ShareHubUrl}" />
|
||||
<HyperlinkButton x:Uid="SettingsPageShareHub" NavigateUri="{x:Bind constants:Link.ShareHub}" />
|
||||
|
||||
</StackPanel>
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@ using BetterLyrics.WinUI3.Helper;
|
||||
using BetterLyrics.WinUI3.Models;
|
||||
using BetterLyrics.WinUI3.Serialization;
|
||||
using BetterLyrics.WinUI3.Services.LiveStatesService;
|
||||
using BetterLyrics.WinUI3.Services.ResourceService;
|
||||
using BetterLyrics.WinUI3.Services.SettingsService;
|
||||
using BetterLyrics.WinUI3.ViewModels;
|
||||
using BetterLyrics.WinUI3.Views;
|
||||
@@ -27,7 +26,6 @@ namespace BetterLyrics.WinUI3.Controls
|
||||
|
||||
private readonly ISettingsService _settingsService = Ioc.Default.GetRequiredService<ISettingsService>();
|
||||
private readonly ILiveStatesService _liveStatesService = Ioc.Default.GetRequiredService<ILiveStatesService>();
|
||||
private readonly IResourceService _resourceService = Ioc.Default.GetRequiredService<IResourceService>();
|
||||
|
||||
public LyricsWindowSettingsControl()
|
||||
{
|
||||
@@ -87,7 +85,7 @@ namespace BetterLyrics.WinUI3.Controls
|
||||
clonedData.IsDefault = false;
|
||||
var json = System.Text.Json.JsonSerializer.Serialize(clonedData, SourceGenerationContext.Default.LyricsWindowStatus);
|
||||
File.WriteAllText(file.Path, json);
|
||||
DevWinUI.Growl.Success(_resourceService.GetLocalizedString("ExportSettingsSuccess"));
|
||||
ToastHelper.ShowToast("ExportSettingsSuccess", null, InfoBarSeverity.Success);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -147,7 +145,7 @@ namespace BetterLyrics.WinUI3.Controls
|
||||
if (data != null)
|
||||
{
|
||||
ViewModel.AppSettings.WindowBoundsRecords.Add(data);
|
||||
DevWinUI.Growl.Success(_resourceService.GetLocalizedString("ImportSettingsSuccess"));
|
||||
ToastHelper.ShowToast("ImportSettingsSuccess", null, InfoBarSeverity.Success);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -188,6 +188,14 @@
|
||||
<ComboBoxItem x:Uid="SettingsPageLyricsSearchBestMatch" />
|
||||
</ComboBox>
|
||||
</dev:SettingsCard>
|
||||
<dev:SettingsCard x:Uid="SettingsPageMatchingThreshold">
|
||||
<local:ExtendedSlider
|
||||
Default="0"
|
||||
Maximum="100"
|
||||
Minimum="0"
|
||||
Unit="%"
|
||||
Value="{x:Bind ViewModel.SelectedMediaSourceProvider.MatchingThreshold, Mode=TwoWay}" />
|
||||
</dev:SettingsCard>
|
||||
|
||||
<ListView
|
||||
x:Name="LyricsSearchProvidersListView"
|
||||
@@ -209,12 +217,48 @@
|
||||
</ListView.ItemContainerStyle>
|
||||
<ListView.ItemTemplate>
|
||||
<DataTemplate x:DataType="models:LyricsSearchProviderInfo">
|
||||
<dev:SettingsCard Header="{Binding Provider, Converter={StaticResource LyricsSearchProviderToDisplayNameConverter}, Mode=OneWay}">
|
||||
<dev:SettingsCard.HeaderIcon>
|
||||
<FontIcon FontFamily="Segoe UI Symbol" Glyph="⠿" />
|
||||
</dev:SettingsCard.HeaderIcon>
|
||||
<ToggleSwitch IsOn="{Binding IsEnabled, Mode=TwoWay}" />
|
||||
</dev:SettingsCard>
|
||||
<Grid>
|
||||
<dev:SettingsExpander Header="{Binding Provider, Converter={StaticResource LyricsSearchProviderToDisplayNameConverter}, Mode=OneWay}" IsExpanded="{Binding IsMatchingThresholdOverwritten, Mode=OneWay}">
|
||||
<dev:SettingsExpander.HeaderIcon>
|
||||
<FontIcon FontFamily="Segoe UI Symbol" Glyph="⠿" />
|
||||
</dev:SettingsExpander.HeaderIcon>
|
||||
<ToggleSwitch IsOn="{Binding IsEnabled, Mode=TwoWay}" />
|
||||
<dev:SettingsExpander.Items>
|
||||
<dev:SettingsCard x:Uid="SettingsPageOverwriteMatchingThreshold">
|
||||
<ToggleSwitch IsOn="{Binding IsMatchingThresholdOverwritten, Mode=TwoWay}" />
|
||||
</dev:SettingsCard>
|
||||
<dev:SettingsCard x:Uid="SettingsPageMatchingThreshold" IsEnabled="{Binding IsMatchingThresholdOverwritten, Mode=OneWay}">
|
||||
<local:ExtendedSlider
|
||||
Default="0"
|
||||
Maximum="100"
|
||||
Minimum="0"
|
||||
Unit="%"
|
||||
Value="{Binding MatchingThreshold, Mode=TwoWay}" />
|
||||
</dev:SettingsCard>
|
||||
</dev:SettingsExpander.Items>
|
||||
</dev:SettingsExpander>
|
||||
<Grid
|
||||
Width="48"
|
||||
HorizontalAlignment="Left"
|
||||
Background="{ThemeResource ControlStrokeColorDefaultBrush}"
|
||||
CornerRadius="4,0,0,4"
|
||||
Opacity="0">
|
||||
<interactivity:Interaction.Behaviors>
|
||||
<interactivity:EventTriggerBehavior EventName="PointerEntered">
|
||||
<interactivity:ChangePropertyAction PropertyName="Opacity" Value="1" />
|
||||
</interactivity:EventTriggerBehavior>
|
||||
<interactivity:EventTriggerBehavior EventName="PointerExited">
|
||||
<interactivity:ChangePropertyAction PropertyName="Opacity" Value="0" />
|
||||
</interactivity:EventTriggerBehavior>
|
||||
</interactivity:Interaction.Behaviors>
|
||||
<Grid.OpacityTransition>
|
||||
<ScalarTransition />
|
||||
</Grid.OpacityTransition>
|
||||
<ToolTipService.ToolTip>
|
||||
<ToolTip x:Uid="SettingsPageHoldDragSort" />
|
||||
</ToolTipService.ToolTip>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</ListView.ItemTemplate>
|
||||
</ListView>
|
||||
@@ -255,96 +299,65 @@
|
||||
<Grid Style="{StaticResource SettingsGridStyle}">
|
||||
<StackPanel Spacing="{StaticResource SettingsCardSpacing}">
|
||||
|
||||
<!-- Provider info -->
|
||||
<!-- Realtime info -->
|
||||
<TextBlock x:Uid="SettingsPageRealtimeStatus" Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" />
|
||||
<dev:SettingsCard ContentAlignment="Left">
|
||||
|
||||
<!-- Playback source info -->
|
||||
<Expander
|
||||
x:Uid="SettingsPagePlaybackStatus"
|
||||
HorizontalAlignment="Stretch"
|
||||
HorizontalContentAlignment="Left">
|
||||
<StackPanel Spacing="6">
|
||||
<!-- Playback source -->
|
||||
<Grid ColumnSpacing="12">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock x:Uid="SettingsPagePlaybackSource" Grid.Column="0" />
|
||||
<RichTextBlock Grid.Column="1" Foreground="{ThemeResource TextFillColorSecondaryBrush}">
|
||||
<Paragraph>
|
||||
<Run Text="{x:Bind ViewModel.MediaSessionsService.CurrentMediaSourceProviderInfo.DisplayName, Mode=OneWay}" />
|
||||
</Paragraph>
|
||||
</RichTextBlock>
|
||||
</Grid>
|
||||
<!-- Playback source ID -->
|
||||
<Grid ColumnSpacing="12">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock x:Uid="SettingsPagePlaybackSourceID" Grid.Column="0" />
|
||||
<RichTextBlock Grid.Column="1" Foreground="{ThemeResource TextFillColorSecondaryBrush}">
|
||||
<Paragraph>
|
||||
<Run Text="{x:Bind ViewModel.MediaSessionsService.CurrentSongInfo.PlayerId, TargetNullValue=N/A, Mode=OneWay}" />
|
||||
</Paragraph>
|
||||
</RichTextBlock>
|
||||
</Grid>
|
||||
<!-- Song title -->
|
||||
<Grid ColumnSpacing="12">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock x:Uid="LyricsSearchControlTitle" Grid.Column="0" />
|
||||
<RichTextBlock Grid.Column="1" Foreground="{ThemeResource TextFillColorSecondaryBrush}">
|
||||
<Paragraph>
|
||||
<Run Text="{x:Bind ViewModel.MediaSessionsService.CurrentSongInfo.Title, TargetNullValue=N/A, Mode=OneWay}" />
|
||||
</Paragraph>
|
||||
</RichTextBlock>
|
||||
</Grid>
|
||||
<!-- Song artists -->
|
||||
<Grid ColumnSpacing="12">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock x:Uid="LyricsSearchControlArtist" Grid.Column="0" />
|
||||
<RichTextBlock Grid.Column="1" Foreground="{ThemeResource TextFillColorSecondaryBrush}">
|
||||
<Paragraph>
|
||||
<Run Text="{x:Bind ViewModel.MediaSessionsService.CurrentSongInfo.DisplayArtists, TargetNullValue=N/A, Mode=OneWay}" />
|
||||
</Paragraph>
|
||||
</RichTextBlock>
|
||||
</Grid>
|
||||
<!-- Song album -->
|
||||
<Grid ColumnSpacing="12">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock x:Uid="LyricsSearchControlAlbum" Grid.Column="0" />
|
||||
<RichTextBlock Grid.Column="1" Foreground="{ThemeResource TextFillColorSecondaryBrush}">
|
||||
<Paragraph>
|
||||
<Run Text="{x:Bind ViewModel.MediaSessionsService.CurrentSongInfo.Album, TargetNullValue=N/A, Mode=OneWay}" />
|
||||
</Paragraph>
|
||||
</RichTextBlock>
|
||||
</Grid>
|
||||
<!-- Lyrics source -->
|
||||
<StackPanel Orientation="Horizontal" Spacing="12">
|
||||
<TextBlock x:Uid="LyricsPageLyricsProviderPrefix" />
|
||||
<HyperlinkButton
|
||||
Padding="0"
|
||||
Content="{x:Bind ViewModel.MediaSessionsService.CurrentLyricsSearchResult.ProviderIfFound, Mode=OneWay, Converter={StaticResource LyricsSearchProviderToDisplayNameConverter}}"
|
||||
IsEnabled="{x:Bind ViewModel.MediaSessionsService.CurrentLyricsSearchResult.IsFound, Mode=OneWay}"
|
||||
NavigateUri="{x:Bind ViewModel.MediaSessionsService.CurrentLyricsSearchResult.Reference, Mode=OneWay}" />
|
||||
</StackPanel>
|
||||
<!-- Translation source -->
|
||||
<StackPanel Orientation="Horizontal" Spacing="12">
|
||||
<TextBlock x:Uid="LyricsPageTranslationProviderPrefix" />
|
||||
<TextBlock Foreground="{ThemeResource TextFillColorSecondaryBrush}" Text="{x:Bind ViewModel.MediaSessionsService.TranslationSearchProvider, Mode=OneWay, Converter={StaticResource TranslationSearchProviderToDisplayNameConverter}}" />
|
||||
</StackPanel>
|
||||
<!-- Match percentage -->
|
||||
<StackPanel Orientation="Horizontal" Spacing="6">
|
||||
<TextBlock x:Uid="LyricsPageMatchPercentage" />
|
||||
<TextBlock Foreground="{ThemeResource TextFillColorSecondaryBrush}" Text="{x:Bind ViewModel.MediaSessionsService.CurrentLyricsSearchResult.MatchPercentage, Mode=OneWay}" />
|
||||
</StackPanel>
|
||||
<local:PropertyRow x:Uid="SettingsPagePlaybackSource" Value="{x:Bind ViewModel.MediaSessionsService.CurrentMediaSourceProviderInfo.DisplayName, Mode=OneWay}" />
|
||||
<local:PropertyRow x:Uid="SettingsPagePlaybackSourceID" Value="{x:Bind ViewModel.MediaSessionsService.CurrentSongInfo.PlayerId, TargetNullValue=N/A, Mode=OneWay}" />
|
||||
</StackPanel>
|
||||
</dev:SettingsCard>
|
||||
</Expander>
|
||||
|
||||
<!-- Song info -->
|
||||
<Expander
|
||||
x:Uid="SettingsPageSongStatus"
|
||||
HorizontalAlignment="Stretch"
|
||||
HorizontalContentAlignment="Left">
|
||||
<StackPanel Spacing="6">
|
||||
<local:PropertyRow x:Uid="SettingsPageSongTitle" Value="{x:Bind ViewModel.MediaSessionsService.CurrentSongInfo.Title, TargetNullValue=N/A, Mode=OneWay}" />
|
||||
<local:PropertyRow x:Uid="SettingsPageArtist" Value="{x:Bind ViewModel.MediaSessionsService.CurrentSongInfo.DisplayArtists, TargetNullValue=N/A, Mode=OneWay}" />
|
||||
<local:PropertyRow x:Uid="SettingsPageAlbum" Value="{x:Bind ViewModel.MediaSessionsService.CurrentSongInfo.Album, TargetNullValue=N/A, Mode=OneWay}" />
|
||||
<local:PropertyRow
|
||||
x:Uid="LyricsSearchControlDurauion"
|
||||
Unit="s"
|
||||
Value="{x:Bind ViewModel.MediaSessionsService.CurrentSongInfo.Duration, TargetNullValue=N/A, Mode=OneWay}" />
|
||||
</StackPanel>
|
||||
</Expander>
|
||||
|
||||
<!-- Search result info -->
|
||||
<Expander
|
||||
x:Uid="SettingsPageSearchResultStatus"
|
||||
HorizontalAlignment="Stretch"
|
||||
HorizontalContentAlignment="Left">
|
||||
<StackPanel Spacing="6">
|
||||
<local:PropertyRow x:Uid="SettingsPageSongTitle" Value="{x:Bind ViewModel.MediaSessionsService.CurrentLyricsSearchResult.Title, TargetNullValue=N/A, Mode=OneWay}" />
|
||||
<local:PropertyRow x:Uid="SettingsPageArtist" Value="{x:Bind ViewModel.MediaSessionsService.CurrentLyricsSearchResult.DisplayArtists, TargetNullValue=N/A, Mode=OneWay}" />
|
||||
<local:PropertyRow x:Uid="SettingsPageAlbum" Value="{x:Bind ViewModel.MediaSessionsService.CurrentLyricsSearchResult.Album, TargetNullValue=N/A, Mode=OneWay}" />
|
||||
<local:PropertyRow
|
||||
x:Uid="LyricsSearchControlDurauion"
|
||||
Unit="s"
|
||||
Value="{x:Bind ViewModel.MediaSessionsService.CurrentLyricsSearchResult.Duration, TargetNullValue=N/A, Mode=OneWay}" />
|
||||
<local:PropertyRow
|
||||
x:Uid="LyricsPageLyricsProviderPrefix"
|
||||
Link="{x:Bind ViewModel.MediaSessionsService.CurrentLyricsSearchResult.Reference, Mode=OneWay}"
|
||||
ToolTipService.ToolTip="{x:Bind ViewModel.MediaSessionsService.CurrentLyricsSearchResult.Reference, TargetNullValue=N/A, Mode=OneWay}"
|
||||
Value="{x:Bind ViewModel.MediaSessionsService.CurrentLyricsSearchResult.ProviderIfFound, Mode=OneWay, Converter={StaticResource LyricsSearchProviderToDisplayNameConverter}}" />
|
||||
<local:PropertyRow x:Uid="LyricsPageTranslationProviderPrefix" Value="{x:Bind ViewModel.MediaSessionsService.TranslationSearchProvider, Mode=OneWay, Converter={StaticResource TranslationSearchProviderToDisplayNameConverter}}" />
|
||||
<local:PropertyRow
|
||||
x:Uid="LyricsPageMatchPercentage"
|
||||
Unit="%"
|
||||
Value="{x:Bind ViewModel.MediaSessionsService.CurrentLyricsSearchResult.MatchPercentage, Mode=OneWay}" />
|
||||
<local:PropertyRow
|
||||
x:Uid="LyricsPageCachePath"
|
||||
Link="{x:Bind ViewModel.MediaSessionsService.CurrentLyricsSearchResult.SelfPath, TargetNullValue=N/A, Mode=OneWay}"
|
||||
ToolTipService.ToolTip="{x:Bind ViewModel.MediaSessionsService.CurrentLyricsSearchResult.SelfPath, TargetNullValue=N/A, Mode=OneWay}" />
|
||||
</StackPanel>
|
||||
</Expander>
|
||||
|
||||
<dev:SettingsCard x:Uid="SettingsPageForceWordByWordEffect">
|
||||
<ToggleSwitch IsOn="{x:Bind ViewModel.AppSettings.GeneralSettings.IsForceWordByWordEffect, Mode=TwoWay}" />
|
||||
@@ -486,7 +499,7 @@
|
||||
Content="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
|
||||
FontSize=12,
|
||||
Glyph=}"
|
||||
NavigateUri="{x:Bind constants:Link.AppleMusicCfgUrl}" />
|
||||
NavigateUri="{x:Bind constants:Link.AppleMusicCfg}" />
|
||||
<Button
|
||||
Grid.Column="2"
|
||||
Command="{x:Bind ViewModel.SaveAppleMusicMediaUserTokenCommand}"
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<UserControl
|
||||
x:Class="BetterLyrics.WinUI3.Controls.PropertyRow"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="using:BetterLyrics.WinUI3.Controls"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:ui="using:CommunityToolkit.WinUI"
|
||||
mc:Ignorable="d">
|
||||
|
||||
<Grid HorizontalAlignment="Left" ColumnSpacing="6">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<TextBlock
|
||||
Grid.Column="0"
|
||||
VerticalAlignment="Top"
|
||||
Text="{x:Bind Header, Mode=OneWay}" />
|
||||
|
||||
<Grid
|
||||
Grid.Column="1"
|
||||
VerticalAlignment="Top"
|
||||
PointerEntered="OnPointerEntered"
|
||||
PointerExited="OnPointerExited">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Grid Grid.Column="0">
|
||||
<RichTextBlock Foreground="{ThemeResource TextFillColorSecondaryBrush}" Visibility="{x:Bind TextVisibility, Mode=OneWay}">
|
||||
<Paragraph>
|
||||
<Run Text="{x:Bind Value, Mode=OneWay}" />
|
||||
<Run Text="{x:Bind Unit, Mode=OneWay}" />
|
||||
</Paragraph>
|
||||
</RichTextBlock>
|
||||
|
||||
<HyperlinkButton
|
||||
Margin="0,-2,0,0"
|
||||
Padding="0"
|
||||
HorizontalContentAlignment="Left"
|
||||
Click="OnLinkClicked"
|
||||
Visibility="{x:Bind LinkVisibility, Mode=OneWay}">
|
||||
<TextBlock Text="{x:Bind Value, Mode=OneWay}" TextWrapping="Wrap" />
|
||||
</HyperlinkButton>
|
||||
</Grid>
|
||||
|
||||
<Button
|
||||
x:Name="CopyButton"
|
||||
Grid.Column="1"
|
||||
Margin="8,-2,0,0"
|
||||
Padding="4"
|
||||
VerticalAlignment="Top"
|
||||
Click="OnCopyClicked"
|
||||
Opacity="0">
|
||||
<ToolTipService.ToolTip>
|
||||
<ToolTip x:Uid="Copy" />
|
||||
</ToolTipService.ToolTip>
|
||||
<Button.OpacityTransition>
|
||||
<ScalarTransition />
|
||||
</Button.OpacityTransition>
|
||||
<Grid>
|
||||
<FontIcon
|
||||
x:Name="CopyIcon"
|
||||
FontFamily="{StaticResource IconFontFamily}"
|
||||
FontSize="16"
|
||||
Glyph=""
|
||||
Opacity="1">
|
||||
<FontIcon.OpacityTransition>
|
||||
<ScalarTransition />
|
||||
</FontIcon.OpacityTransition>
|
||||
</FontIcon>
|
||||
<FontIcon
|
||||
x:Name="CheckIcon"
|
||||
FontFamily="{StaticResource IconFontFamily}"
|
||||
FontSize="16"
|
||||
Glyph=""
|
||||
Opacity="0">
|
||||
<FontIcon.OpacityTransition>
|
||||
<ScalarTransition />
|
||||
</FontIcon.OpacityTransition>
|
||||
</FontIcon>
|
||||
</Grid>
|
||||
</Button>
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
||||
</UserControl>
|
||||
@@ -0,0 +1,115 @@
|
||||
using BetterLyrics.WinUI3.Helper;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using Microsoft.UI.Xaml.Input;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Windows.ApplicationModel.DataTransfer;
|
||||
|
||||
// To learn more about WinUI, the WinUI project structure,
|
||||
// and more about our project templates, see: http://aka.ms/winui-project-info.
|
||||
|
||||
namespace BetterLyrics.WinUI3.Controls
|
||||
{
|
||||
public sealed partial class PropertyRow : UserControl
|
||||
{
|
||||
public PropertyRow()
|
||||
{
|
||||
this.InitializeComponent();
|
||||
}
|
||||
|
||||
public string Header
|
||||
{
|
||||
get => (string)GetValue(HeaderProperty);
|
||||
set => SetValue(HeaderProperty, value);
|
||||
}
|
||||
public static readonly DependencyProperty HeaderProperty =
|
||||
DependencyProperty.Register(nameof(Header), typeof(string), typeof(PropertyRow), new PropertyMetadata(string.Empty));
|
||||
|
||||
public string Value
|
||||
{
|
||||
get => (string)GetValue(ValueProperty);
|
||||
set => SetValue(ValueProperty, value);
|
||||
}
|
||||
public static readonly DependencyProperty ValueProperty =
|
||||
DependencyProperty.Register(nameof(Value), typeof(string), typeof(PropertyRow), new PropertyMetadata(string.Empty));
|
||||
|
||||
public string Link
|
||||
{
|
||||
get => (string)GetValue(LinkProperty);
|
||||
set => SetValue(LinkProperty, value);
|
||||
}
|
||||
public static readonly DependencyProperty LinkProperty =
|
||||
DependencyProperty.Register(nameof(Link), typeof(string), typeof(PropertyRow), new PropertyMetadata(string.Empty));
|
||||
|
||||
public string Unit
|
||||
{
|
||||
get => (string)GetValue(UnitProperty);
|
||||
set => SetValue(UnitProperty, value);
|
||||
}
|
||||
public static readonly DependencyProperty UnitProperty =
|
||||
DependencyProperty.Register(nameof(Unit), typeof(string), typeof(PropertyRow), new PropertyMetadata(string.Empty));
|
||||
|
||||
private Visibility TextVisibility => Link == string.Empty ? Visibility.Visible : Visibility.Collapsed;
|
||||
private Visibility LinkVisibility => Link != string.Empty ? Visibility.Visible : Visibility.Collapsed;
|
||||
|
||||
private void OnPointerEntered(object sender, PointerRoutedEventArgs e)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(Value))
|
||||
{
|
||||
CopyButton.Opacity = 1;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnPointerExited(object sender, PointerRoutedEventArgs e)
|
||||
{
|
||||
CopyButton.Opacity = 0;
|
||||
}
|
||||
|
||||
private void OnCopyClicked(object sender, RoutedEventArgs e)
|
||||
{
|
||||
var targetValue = string.IsNullOrEmpty(Link) ? Value : Link;
|
||||
|
||||
if (string.IsNullOrEmpty(targetValue)) return;
|
||||
|
||||
try
|
||||
{
|
||||
DataPackage dataPackage = new DataPackage();
|
||||
dataPackage.SetText(targetValue);
|
||||
Clipboard.SetContent(dataPackage);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine($"Copy failed: {ex.Message}");
|
||||
return;
|
||||
}
|
||||
|
||||
CheckIcon.Opacity = 1;
|
||||
CopyIcon.Opacity = 0;
|
||||
|
||||
this.DispatcherQueue.TryEnqueue(async () =>
|
||||
{
|
||||
await Task.Delay(1000);
|
||||
|
||||
CheckIcon.Opacity = 0;
|
||||
CopyIcon.Opacity = 1;
|
||||
});
|
||||
}
|
||||
|
||||
private async void OnLinkClicked(object sender, RoutedEventArgs e)
|
||||
{
|
||||
Uri.TryCreate(Link, UriKind.Absolute, out var uri);
|
||||
if (uri != null)
|
||||
{
|
||||
if (uri.Scheme == Uri.UriSchemeHttp || uri.Scheme == Uri.UriSchemeHttps)
|
||||
{
|
||||
await Windows.System.Launcher.LaunchUriAsync(uri);
|
||||
}
|
||||
else if (uri.Scheme == Uri.UriSchemeFile)
|
||||
{
|
||||
await LauncherHelper.SelectAndShowFile(uri.LocalPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
using BetterLyrics.WinUI3.Helper;
|
||||
using BetterLyrics.WinUI3.Hooks;
|
||||
using BetterLyrics.WinUI3.Services.ResourceService;
|
||||
using CommunityToolkit.Mvvm.DependencyInjection;
|
||||
@@ -92,11 +93,11 @@ namespace BetterLyrics.WinUI3.Controls
|
||||
bool registered = GlobalHotKeyHook.IsHotKeyRegistered(Shortcut);
|
||||
if (registered)
|
||||
{
|
||||
DevWinUI.Growl.Success(_resourceService.GetLocalizedString("SettingsPageShortcutRegSuccessInfo"));
|
||||
ToastHelper.ShowToast("SettingsPageShortcutRegSuccessInfo", null, InfoBarSeverity.Success);
|
||||
}
|
||||
else
|
||||
{
|
||||
DevWinUI.Growl.Error(_resourceService.GetLocalizedString("SettingsPageShortcutRegFailInfo"));
|
||||
ToastHelper.ShowToast("SettingsPageShortcutRegFailInfo", null, InfoBarSeverity.Error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
using ATL;
|
||||
using BetterLyrics.WinUI3.Helper;
|
||||
using Microsoft.UI.Xaml.Data;
|
||||
using Microsoft.UI.Xaml.Media.Imaging;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace BetterLyrics.WinUI3.Converter
|
||||
{
|
||||
public partial class PictureInfosToImageSourceConverter : IValueConverter
|
||||
{
|
||||
public object Convert(object value, Type targetType, object parameter, string language)
|
||||
{
|
||||
BitmapImage bitmapImage = new();
|
||||
if (value is IList<PictureInfo> list && list.FirstOrDefault()?.PictureData is byte[] pictureData)
|
||||
{
|
||||
bitmapImage.SetSource(ImageHelper.ToIRandomAccessStream(pictureData));
|
||||
}
|
||||
else
|
||||
{
|
||||
bitmapImage.UriSource = new Uri(PathHelper.AlbumArtPlaceholderPath);
|
||||
}
|
||||
return bitmapImage;
|
||||
}
|
||||
|
||||
public object ConvertBack(object value, Type targetType, object parameter, string language)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,5 @@
|
||||
// 2025/6/23 by Zhe Fang
|
||||
|
||||
using BetterLyrics.WinUI3.Helper;
|
||||
|
||||
namespace BetterLyrics.WinUI3.Enums
|
||||
{
|
||||
public enum LyricsSearchProvider
|
||||
|
||||
@@ -1,8 +1,4 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace BetterLyrics.WinUI3.Enums
|
||||
namespace BetterLyrics.WinUI3.Enums
|
||||
{
|
||||
public enum LyricsSearchType
|
||||
{
|
||||
|
||||
@@ -1,7 +1,4 @@
|
||||
using BetterLyrics.WinUI3.Enums;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace BetterLyrics.WinUI3.Extensions
|
||||
{
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
using BetterLyrics.WinUI3.Enums;
|
||||
using BetterLyrics.WinUI3.Helper;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace BetterLyrics.WinUI3.Extensions
|
||||
{
|
||||
@@ -18,6 +16,10 @@ namespace BetterLyrics.WinUI3.Extensions
|
||||
LyricsSearchProvider.Kugou => PathHelper.KugouLyricsCacheDirectory,
|
||||
LyricsSearchProvider.AmllTtmlDb => PathHelper.AmllTtmlDbLyricsCacheDirectory,
|
||||
LyricsSearchProvider.AppleMusic => PathHelper.AppleMusicCacheDirectory,
|
||||
LyricsSearchProvider.LocalMusicFile => PathHelper.LocalMusicCacheDirectory,
|
||||
LyricsSearchProvider.LocalLrcFile => PathHelper.LocalLrcCacheDirectory,
|
||||
LyricsSearchProvider.LocalEslrcFile => PathHelper.LocalEslrcCacheDirectory,
|
||||
LyricsSearchProvider.LocalTtmlFile => PathHelper.LocalTtmlCacheDirectory,
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(provider)),
|
||||
};
|
||||
|
||||
|
||||
@@ -1,9 +1,5 @@
|
||||
using BetterLyrics.WinUI3.Enums;
|
||||
using NTextCat.Commons;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace BetterLyrics.WinUI3.Extensions
|
||||
{
|
||||
|
||||
@@ -5,6 +5,7 @@ using Microsoft.Graphics.Canvas.Brushes;
|
||||
using Microsoft.Graphics.Canvas.Effects;
|
||||
using Microsoft.Graphics.Canvas.UI.Xaml;
|
||||
using Microsoft.UI;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
@@ -55,11 +56,11 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
Source = new GaussianBlurEffect
|
||||
{
|
||||
Source = backgroundFontEffect,
|
||||
BlurAmount = (float)lyricsLine.BlurAmountTransition.Value,
|
||||
BlurAmount = (float)Math.Max(lyricsLine.BlurAmountTransition.Value, 0),
|
||||
BorderMode = EffectBorderMode.Soft,
|
||||
Optimization = EffectOptimization.Speed,
|
||||
},
|
||||
Opacity = (float)(lyricsLine.OpacityTransition.Value * lyricsLayerOpacity),
|
||||
Opacity = (float)Math.Max(lyricsLine.OpacityTransition.Value * lyricsLayerOpacity, 0),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
using BetterLyrics.WinUI3.Enums;
|
||||
using BetterLyrics.WinUI3.Extensions;
|
||||
using BetterLyrics.WinUI3.Models;
|
||||
using BetterLyrics.WinUI3.Models.Settings;
|
||||
using BetterLyrics.WinUI3.Serialization;
|
||||
using System;
|
||||
using System.IO;
|
||||
@@ -49,6 +48,7 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
{
|
||||
var json = File.ReadAllText(cacheFilePath);
|
||||
var data = System.Text.Json.JsonSerializer.Deserialize(json, SourceGenerationContext.Default.LyricsSearchResult);
|
||||
data?.SelfPath = cacheFilePath;
|
||||
return data;
|
||||
}
|
||||
return null;
|
||||
@@ -69,6 +69,7 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
var cacheFilePath = Path.Combine(
|
||||
lyricsSearchResult.Provider.GetCacheDirectory(),
|
||||
SanitizeFileName($"{songInfo.ToFileName()}.json"));
|
||||
lyricsSearchResult.SelfPath = cacheFilePath;
|
||||
var json = System.Text.Json.JsonSerializer.Serialize(lyricsSearchResult, SourceGenerationContext.Default.LyricsSearchResult);
|
||||
File.WriteAllText(cacheFilePath, json);
|
||||
}
|
||||
|
||||
@@ -185,5 +185,11 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
{
|
||||
return buffer.AsStream().AsRandomAccessStream();
|
||||
}
|
||||
|
||||
public static IRandomAccessStream ToIRandomAccessStream(byte[] arr)
|
||||
{
|
||||
MemoryStream stream = new MemoryStream(arr);
|
||||
return stream.AsRandomAccessStream();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,6 +14,9 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
private static readonly RankedLanguageIdentifier _identifier;
|
||||
private static readonly IResourceService _resourceService = Ioc.Default.GetRequiredService<IResourceService>();
|
||||
|
||||
public const string ChineseCode = "zh";
|
||||
public const string JapaneseCode = "ja";
|
||||
|
||||
public static List<ExtendedLanguage> SupportedTranslationTargetLanguages { get; set; } =
|
||||
[
|
||||
new ExtendedLanguage("ar"),
|
||||
@@ -105,7 +108,6 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
public static string? DetectLanguageCode(string? text)
|
||||
{
|
||||
if (text == null) return null;
|
||||
|
||||
var guessList = _identifier.Identify(text);
|
||||
string? code = guessList?.FirstOrDefault()?.Item1.Iso639_2T;
|
||||
code = code switch
|
||||
|
||||
@@ -1,475 +0,0 @@
|
||||
// 2025/6/23 by Zhe Fang
|
||||
|
||||
using BetterLyrics.WinUI3.Enums;
|
||||
using BetterLyrics.WinUI3.Extensions;
|
||||
using BetterLyrics.WinUI3.Models;
|
||||
using Lyricify.Lyrics.Parsers;
|
||||
using NTextCat.Commons;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace BetterLyrics.WinUI3.Helper
|
||||
{
|
||||
public partial class LyricsParser
|
||||
{
|
||||
public List<LyricsData> LyricsDataArr { get; private set; } = [];
|
||||
|
||||
public void Parse(SongInfo? songInfo, LyricsSearchResult? lyricsSearchResult)
|
||||
{
|
||||
LyricsDataArr = [];
|
||||
if (lyricsSearchResult?.Raw == null)
|
||||
{
|
||||
LyricsDataArr.Add(LyricsData.GetNotfoundPlaceholder((int)(songInfo?.DurationMs ?? 0)));
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (lyricsSearchResult.Raw.DetectFormat())
|
||||
{
|
||||
case LyricsFormat.Lrc:
|
||||
case LyricsFormat.Eslrc:
|
||||
ParseLrc(lyricsSearchResult.Raw);
|
||||
break;
|
||||
case LyricsFormat.Qrc:
|
||||
ParseQrcKrc(QrcParser.Parse(lyricsSearchResult.Raw).Lines);
|
||||
break;
|
||||
case LyricsFormat.Krc:
|
||||
ParseQrcKrc(KrcParser.Parse(lyricsSearchResult.Raw).Lines);
|
||||
break;
|
||||
case LyricsFormat.Ttml:
|
||||
ParseTtml(lyricsSearchResult.Raw);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
FillRomanizationLyricsData();
|
||||
FillTranslationFromCache(lyricsSearchResult);
|
||||
}
|
||||
|
||||
private void FillTranslationFromCache(LyricsSearchResult? lyricsSearchResult)
|
||||
{
|
||||
if (lyricsSearchResult?.Translation != null)
|
||||
{
|
||||
switch (lyricsSearchResult.Provider)
|
||||
{
|
||||
case LyricsSearchProvider.QQ:
|
||||
case LyricsSearchProvider.Kugou:
|
||||
case LyricsSearchProvider.Netease:
|
||||
ParseLrc(lyricsSearchResult.Translation);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void FillRomanizationLyricsData()
|
||||
{
|
||||
var chinese = LyricsDataArr.FirstOrDefault(x => x.LanguageCode == "zh");
|
||||
if (chinese != null)
|
||||
{
|
||||
LyricsDataArr.Add(new LyricsData
|
||||
{
|
||||
LanguageCode = PhoneticHelper.PinyinCode,
|
||||
LyricsLines = chinese.LyricsLines.Select(line => new LyricsLine
|
||||
{
|
||||
StartMs = line.StartMs,
|
||||
EndMs = line.EndMs,
|
||||
OriginalText = PhoneticHelper.ToPinyin(line.OriginalText),
|
||||
LyricsChars = line.LyricsChars.Select(c => new LyricsChar
|
||||
{
|
||||
StartMs = c.StartMs,
|
||||
EndMs = c.EndMs,
|
||||
Text = PhoneticHelper.ToPinyin(c.Text),
|
||||
StartIndex = c.StartIndex
|
||||
}).ToList()
|
||||
}).ToList()
|
||||
});
|
||||
LyricsDataArr.Add(new LyricsData
|
||||
{
|
||||
LanguageCode = PhoneticHelper.JyutpingCode,
|
||||
LyricsLines = chinese.LyricsLines.Select(line => new LyricsLine
|
||||
{
|
||||
StartMs = line.StartMs,
|
||||
EndMs = line.EndMs,
|
||||
OriginalText = PhoneticHelper.ToJyutping(line.OriginalText),
|
||||
LyricsChars = line.LyricsChars.Select(c => new LyricsChar
|
||||
{
|
||||
StartMs = c.StartMs,
|
||||
EndMs = c.EndMs,
|
||||
Text = PhoneticHelper.ToJyutping(c.Text),
|
||||
StartIndex = c.StartIndex
|
||||
}).ToList()
|
||||
}).ToList()
|
||||
});
|
||||
}
|
||||
var japanese = LyricsDataArr.FirstOrDefault(x => x.LanguageCode == "ja");
|
||||
if (japanese != null)
|
||||
{
|
||||
LyricsDataArr.Add(new LyricsData
|
||||
{
|
||||
LanguageCode = PhoneticHelper.RomajiCode,
|
||||
LyricsLines = japanese.LyricsLines.Select(line => new LyricsLine
|
||||
{
|
||||
StartMs = line.StartMs,
|
||||
EndMs = line.EndMs,
|
||||
OriginalText = PhoneticHelper.ToRomaji(line.OriginalText),
|
||||
LyricsChars = line.LyricsChars.Select(c => new LyricsChar
|
||||
{
|
||||
StartMs = c.StartMs,
|
||||
EndMs = c.EndMs,
|
||||
Text = PhoneticHelper.ToRomaji(c.Text),
|
||||
StartIndex = c.StartIndex
|
||||
}).ToList()
|
||||
}).ToList()
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void ParseLrc(string raw)
|
||||
{
|
||||
var lines = raw.Split(["\r\n", "\n"], StringSplitOptions.RemoveEmptyEntries);
|
||||
var lrcLines =
|
||||
new List<(int time, string text, List<(int time, string text)> syllables)>();
|
||||
|
||||
// 支持 [mm:ss.xx]字、<mm:ss.xx>字,毫秒两位或三位
|
||||
var syllableRegex = SyllableRegex();
|
||||
|
||||
foreach (var line in lines)
|
||||
{
|
||||
var matches = syllableRegex.Matches(line);
|
||||
var syllables = new List<(int, string)>();
|
||||
for (int i = 0; i < matches.Count; i++)
|
||||
{
|
||||
var m = matches[i];
|
||||
int min = int.Parse(m.Groups[2].Value);
|
||||
int sec = int.Parse(m.Groups[3].Value);
|
||||
int ms = int.Parse(m.Groups[4].Value.PadRight(3, '0'));
|
||||
int totalMs = min * 60_000 + sec * 1000 + ms;
|
||||
string text = m.Groups[6].Value;
|
||||
|
||||
syllables.Add((totalMs, text));
|
||||
}
|
||||
if (syllables.Count > 1)
|
||||
{
|
||||
lrcLines.Add(
|
||||
(
|
||||
syllables[0].Item1,
|
||||
string.Concat(syllables.Select(s => s.Item2)),
|
||||
syllables
|
||||
)
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
// 普通LRC行
|
||||
Regex? bracketRegex = LrcRegex();
|
||||
var bracketMatches = bracketRegex.Matches(line);
|
||||
|
||||
string content = line;
|
||||
int? lineStartTime = null;
|
||||
if (bracketMatches.Count > 0)
|
||||
{
|
||||
var m = bracketMatches[0];
|
||||
int min = int.Parse(m.Groups[1].Value);
|
||||
int sec = int.Parse(m.Groups[2].Value);
|
||||
int ms = int.Parse(m.Groups[4].Value.PadRight(3, '0'));
|
||||
lineStartTime = min * 60_000 + sec * 1000 + ms;
|
||||
content = bracketRegex!.Replace(line, "").Trim();
|
||||
if (content == "//") content = "";
|
||||
lrcLines.Add((lineStartTime.Value, content, new List<(int, string)>()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 按时间分组
|
||||
var grouped = lrcLines.GroupBy(l => l.time).OrderBy(g => g.Key).ToList();
|
||||
int languageCount = 0;
|
||||
if (grouped != null && grouped.Count > 0)
|
||||
{
|
||||
// 计算最大语言数量
|
||||
languageCount = grouped.Max(g => g.Count());
|
||||
}
|
||||
|
||||
// 初始化每种语言的歌词列表
|
||||
//LyricsDataArr.Clear();
|
||||
int langStartIndex = LyricsDataArr.Count;
|
||||
for (int i = 0; i < languageCount; i++) LyricsDataArr.Add(new LyricsData());
|
||||
|
||||
// 遍历每个时间分组
|
||||
if (grouped != null)
|
||||
{
|
||||
foreach (var group in grouped)
|
||||
{
|
||||
var linesInGroup = group.ToList();
|
||||
for (int langIdx = 0; langIdx < languageCount; langIdx++)
|
||||
{
|
||||
// 只添加有对应行的语言,否则跳过
|
||||
if (langIdx < linesInGroup.Count)
|
||||
{
|
||||
var (start, text, syllables) = linesInGroup[langIdx];
|
||||
var line = new LyricsLine
|
||||
{
|
||||
StartMs = start,
|
||||
OriginalText = text,
|
||||
LyricsChars = [],
|
||||
};
|
||||
if (syllables != null && syllables.Count > 0)
|
||||
{
|
||||
int currentIndex = 0;
|
||||
for (int j = 0; j < syllables.Count; j++)
|
||||
{
|
||||
var (charStart, charText) = syllables[j];
|
||||
int startIndex = currentIndex;
|
||||
line.LyricsChars.Add(
|
||||
new LyricsChar
|
||||
{
|
||||
StartMs = charStart,
|
||||
Text = charText ?? "",
|
||||
StartIndex = startIndex,
|
||||
}
|
||||
);
|
||||
currentIndex += charText?.Length ?? 0;
|
||||
}
|
||||
}
|
||||
LyricsDataArr[langStartIndex + langIdx].LyricsLines.Add(line);
|
||||
}
|
||||
// 没有翻译行则不补原文,直接跳过
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ParseTtml(string raw)
|
||||
{
|
||||
try
|
||||
{
|
||||
List<LyricsLine> originalLines = [];
|
||||
List<LyricsLine> translationLines = [];
|
||||
var xdoc = XDocument.Parse(raw, LoadOptions.PreserveWhitespace);
|
||||
var body = xdoc.Descendants().FirstOrDefault(e => e.Name.LocalName == "body");
|
||||
if (body == null) return;
|
||||
var ps = body.Descendants().Where(e => e.Name.LocalName == "p");
|
||||
foreach (var p in ps)
|
||||
{
|
||||
// 句级时间
|
||||
string? pBegin = p.Attribute("begin")?.Value;
|
||||
string? pEnd = p.Attribute("end")?.Value;
|
||||
int pStartMs = ParseTtmlTime(pBegin);
|
||||
int pEndMs = ParseTtmlTime(pEnd);
|
||||
|
||||
// 只获取一级span,且排除 ttm:role="x-bg" 的 span 和 ttm:role="x-roman"
|
||||
var spans = p.Elements()
|
||||
.Where(s => s.Name.LocalName == "span" &&
|
||||
s.Attribute(XName.Get("role", "http://www.w3.org/ns/ttml#metadata"))?.Value != "x-bg" &&
|
||||
s.Attribute(XName.Get("role", "http://www.w3.org/ns/ttml#metadata"))?.Value != "x-roman")
|
||||
.ToList();
|
||||
|
||||
// 原文和翻译分离
|
||||
var originalTextSpans = spans
|
||||
.Where(s => s.Attribute(XName.Get("role", "http://www.w3.org/ns/ttml#metadata"))?.Value != "x-translation")
|
||||
.ToList();
|
||||
var translationTextSpans = spans
|
||||
.Where(s => s.Attribute(XName.Get("role", "http://www.w3.org/ns/ttml#metadata"))?.Value == "x-translation")
|
||||
.ToList();
|
||||
|
||||
// 处理原文span后的空白
|
||||
for (int i = 0; i < originalTextSpans.Count; i++)
|
||||
{
|
||||
var span = originalTextSpans[i];
|
||||
var nextNode = span.NodesAfterSelf().FirstOrDefault();
|
||||
if (nextNode is XText textNode)
|
||||
{
|
||||
span.Value += textNode.Value;
|
||||
}
|
||||
}
|
||||
// 拼接空白字符后的原文
|
||||
string originalText = string.Concat(originalTextSpans.Select(s => s.Value));
|
||||
|
||||
var originalCharTimings = new List<LyricsChar>();
|
||||
int originalStartIndex = 0;
|
||||
foreach (var span in originalTextSpans)
|
||||
{
|
||||
string? sBegin = span.Attribute("begin")?.Value;
|
||||
string? sEnd = span.Attribute("end")?.Value;
|
||||
int sStartMs = ParseTtmlTime(sBegin);
|
||||
int sEndMs = ParseTtmlTime(sEnd);
|
||||
originalCharTimings.Add(new LyricsChar
|
||||
{
|
||||
StartMs = sStartMs,
|
||||
EndMs = sEndMs,
|
||||
StartIndex = originalStartIndex,
|
||||
Text = span.Value
|
||||
});
|
||||
originalStartIndex += span.Value.Length;
|
||||
}
|
||||
if (originalTextSpans.Count == 0)
|
||||
originalText = p.Value;
|
||||
|
||||
originalLines.Add(new LyricsLine
|
||||
{
|
||||
StartMs = pStartMs,
|
||||
EndMs = pEndMs,
|
||||
OriginalText = originalText,
|
||||
LyricsChars = originalCharTimings,
|
||||
});
|
||||
|
||||
// 翻译
|
||||
string translationText = string.Concat(translationTextSpans.Select(s => s.Value));
|
||||
var translationCharTimings = new List<LyricsChar>();
|
||||
int translationStartIndex = 0;
|
||||
foreach (var span in translationTextSpans)
|
||||
{
|
||||
string? sBegin = span.Attribute("begin")?.Value;
|
||||
string? sEnd = span.Attribute("end")?.Value;
|
||||
int sStartMs = ParseTtmlTime(sBegin);
|
||||
int sEndMs = ParseTtmlTime(sEnd);
|
||||
translationCharTimings.Add(new LyricsChar
|
||||
{
|
||||
StartMs = sStartMs,
|
||||
EndMs = sEndMs,
|
||||
StartIndex = translationStartIndex,
|
||||
Text = span.Value
|
||||
});
|
||||
translationStartIndex += span.Value.Length;
|
||||
}
|
||||
if (translationTextSpans.Count > 0)
|
||||
{
|
||||
translationLines.Add(new LyricsLine
|
||||
{
|
||||
StartMs = pStartMs,
|
||||
EndMs = pEndMs,
|
||||
OriginalText = translationText,
|
||||
LyricsChars = translationCharTimings,
|
||||
});
|
||||
}
|
||||
}
|
||||
LyricsDataArr.Add(new LyricsData(originalLines));
|
||||
if (translationLines.Count > 0)
|
||||
LyricsDataArr.Add(new LyricsData(translationLines));
|
||||
}
|
||||
catch
|
||||
{
|
||||
// 解析失败,忽略
|
||||
}
|
||||
}
|
||||
|
||||
private static int ParseTtmlTime(string? t)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(t))
|
||||
return 0;
|
||||
|
||||
t = t.Trim();
|
||||
|
||||
// 支持 "1.000s"
|
||||
if (t.EndsWith("s"))
|
||||
{
|
||||
if (
|
||||
double.TryParse(
|
||||
t.TrimEnd('s'),
|
||||
System.Globalization.NumberStyles.Float,
|
||||
System.Globalization.CultureInfo.InvariantCulture,
|
||||
out double seconds
|
||||
)
|
||||
)
|
||||
return (int)(seconds * 1000);
|
||||
}
|
||||
else
|
||||
{
|
||||
var parts = t.Split(':');
|
||||
if (parts.Length == 3)
|
||||
{
|
||||
// hh:mm:ss.xxx
|
||||
int h = int.Parse(parts[0]);
|
||||
int m = int.Parse(parts[1]);
|
||||
double s = double.Parse(
|
||||
parts[2],
|
||||
System.Globalization.CultureInfo.InvariantCulture
|
||||
);
|
||||
return (int)((h * 3600 + m * 60 + s) * 1000);
|
||||
}
|
||||
else if (parts.Length == 2)
|
||||
{
|
||||
// mm:ss.xxx
|
||||
int m = int.Parse(parts[0]);
|
||||
double s = double.Parse(
|
||||
parts[1],
|
||||
System.Globalization.CultureInfo.InvariantCulture
|
||||
);
|
||||
return (int)((m * 60 + s) * 1000);
|
||||
}
|
||||
else if (parts.Length == 1)
|
||||
{
|
||||
// ss.xxx
|
||||
if (
|
||||
double.TryParse(
|
||||
parts[0],
|
||||
System.Globalization.NumberStyles.Float,
|
||||
System.Globalization.CultureInfo.InvariantCulture,
|
||||
out double s
|
||||
)
|
||||
)
|
||||
return (int)(s * 1000);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
private void ParseQrcKrc(List<Lyricify.Lyrics.Models.ILineInfo>? lines)
|
||||
{
|
||||
lines = lines?.Where(x => x.Text != string.Empty).ToList();
|
||||
List<LyricsLine> lyricsLines = [];
|
||||
|
||||
if (lines != null && lines.Count > 0)
|
||||
{
|
||||
lyricsLines = [];
|
||||
for (int lineIndex = 0; lineIndex < lines.Count; lineIndex++)
|
||||
{
|
||||
var lineRead = lines[lineIndex];
|
||||
var lineWrite = new LyricsLine
|
||||
{
|
||||
StartMs = lineRead.StartTime ?? 0,
|
||||
EndMs = lineRead.EndTime ?? 0,
|
||||
OriginalText = lineRead.Text,
|
||||
LyricsChars = [],
|
||||
};
|
||||
|
||||
var syllables = (lineRead as Lyricify.Lyrics.Models.SyllableLineInfo)?.Syllables;
|
||||
if (syllables != null)
|
||||
{
|
||||
int startIndex = 0;
|
||||
for (
|
||||
int syllableIndex = 0;
|
||||
syllableIndex < syllables.Count;
|
||||
syllableIndex++
|
||||
)
|
||||
{
|
||||
var syllable = syllables[syllableIndex];
|
||||
var charTiming = new LyricsChar
|
||||
{
|
||||
StartMs = syllable.StartTime,
|
||||
EndMs = syllable.EndTime,
|
||||
Text = syllable.Text,
|
||||
StartIndex = startIndex,
|
||||
};
|
||||
lineWrite.LyricsChars.Add(charTiming);
|
||||
startIndex += syllable.Text.Length;
|
||||
}
|
||||
}
|
||||
|
||||
lyricsLines.Add(lineWrite);
|
||||
}
|
||||
}
|
||||
|
||||
LyricsDataArr.Add(new LyricsData(lyricsLines));
|
||||
}
|
||||
|
||||
[GeneratedRegex(@"\[(\d*):(\d*)(\.|\:)(\d*)\]")]
|
||||
private static partial Regex LrcRegex();
|
||||
[GeneratedRegex(@"(\[|\<)(\d*):(\d*)\.(\d*)(\]|\>)([^\[\]\<\>]*)")]
|
||||
private static partial Regex SyllableRegex();
|
||||
}
|
||||
}
|
||||
@@ -2,53 +2,67 @@
|
||||
using F23.StringSimilarity;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace BetterLyrics.WinUI3.Helper
|
||||
{
|
||||
public static class MetadataComparer
|
||||
public static partial class MetadataComparer
|
||||
{
|
||||
// 权重配置 (总和 1.0)
|
||||
private const double WeightTitle = 0.40;
|
||||
private const double WeightArtist = 0.40;
|
||||
private const double WeightAlbum = 0.10;
|
||||
private const double WeightDuration = 0.10;
|
||||
|
||||
// 实例化算法 (JaroWinkler 适合短字符串匹配)
|
||||
private static readonly JaroWinkler _algo = new JaroWinkler();
|
||||
// JaroWinkler 适合短字符串匹配
|
||||
private static readonly JaroWinkler _algo = new();
|
||||
|
||||
/// <summary>
|
||||
/// 计算 SongInfo 和 LyricsSearchResult 的相似度 (0-100)
|
||||
/// </summary>
|
||||
public static int CalculateScore(SongInfo local, LyricsSearchResult remote)
|
||||
{
|
||||
if (local == null || remote == null) return 0;
|
||||
|
||||
// 1. 标题相似度
|
||||
double titleScore = GetStringSimilarity(local.Title, remote.Title);
|
||||
double totalScore = 0;
|
||||
|
||||
// 2. 艺术家相似度 (需要处理数组顺序)
|
||||
double artistScore = GetArtistSimilarity(local.Artists, remote.Artists);
|
||||
bool localHasMetadata = !string.IsNullOrWhiteSpace(local.Title);
|
||||
bool remoteHasMetadata = !string.IsNullOrWhiteSpace(remote.Title);
|
||||
|
||||
// 3. 专辑相似度
|
||||
double albumScore = GetStringSimilarity(local.Album, remote.Album);
|
||||
if (localHasMetadata && remoteHasMetadata)
|
||||
{
|
||||
double titleScore = GetStringSimilarity(local.Title, remote.Title);
|
||||
double artistScore = GetArtistSimilarity(local.Artists, remote.Artists);
|
||||
double albumScore = GetStringSimilarity(local.Album, remote.Album);
|
||||
double durationScore = GetDurationSimilarity(local.DurationMs, remote.Duration);
|
||||
|
||||
// 4. 时长相似度 (基于毫秒 vs 秒的转换和容差)
|
||||
double durationScore = GetDurationSimilarity(local.DurationMs, remote.Duration);
|
||||
totalScore = (titleScore * WeightTitle) +
|
||||
(artistScore * WeightArtist) +
|
||||
(albumScore * WeightAlbum) +
|
||||
(durationScore * WeightDuration);
|
||||
}
|
||||
else
|
||||
{
|
||||
string? localQuery = localHasMetadata
|
||||
? $"{local.Title} {string.Join(" ", local.Artists ?? [])}"
|
||||
: Path.GetFileNameWithoutExtension(local.LinkedFileName);
|
||||
|
||||
// 5. 加权汇总
|
||||
double totalScore = (titleScore * WeightTitle) +
|
||||
(artistScore * WeightArtist) +
|
||||
(albumScore * WeightAlbum) +
|
||||
(durationScore * WeightDuration);
|
||||
string remoteQuery = remoteHasMetadata
|
||||
? $"{remote.Title} {string.Join(" ", remote.Artists ?? [])}"
|
||||
: Path.GetFileNameWithoutExtension(remote.Reference);
|
||||
|
||||
string fp1 = CreateSortedFingerprint(localQuery);
|
||||
string fp2 = CreateSortedFingerprint(remoteQuery);
|
||||
|
||||
if (string.IsNullOrWhiteSpace(fp1) || string.IsNullOrWhiteSpace(fp2))
|
||||
totalScore = 0;
|
||||
else
|
||||
totalScore = _algo.Similarity(fp1, fp2);
|
||||
}
|
||||
|
||||
return (int)Math.Round(totalScore * 100);
|
||||
}
|
||||
|
||||
private static double GetStringSimilarity(string? s1, string? s2)
|
||||
{
|
||||
// 归一化:转小写,去空白
|
||||
s1 = s1?.Trim().ToLowerInvariant() ?? "";
|
||||
s2 = s2?.Trim().ToLowerInvariant() ?? "";
|
||||
|
||||
@@ -63,8 +77,7 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
if (localArtists == null || localArtists.Length == 0) return 0.0;
|
||||
if (remoteArtists == null || remoteArtists.Length == 0) return 0.0;
|
||||
|
||||
// 技巧:将艺术家数组排序并连接,避免顺序不同导致的不匹配
|
||||
// 例如: ["Jay-Z", "Linkin Park"] 和 ["Linkin Park", "Jay-Z"] 应该是一样的
|
||||
// 将艺术家数组排序并连接,避免顺序不同导致的不匹配
|
||||
var s1 = string.Join(" ", localArtists.OrderBy(a => a).Select(a => a.Trim().ToLowerInvariant()));
|
||||
var s2 = string.Join(" ", remoteArtists.OrderBy(a => a).Select(a => a.Trim().ToLowerInvariant()));
|
||||
|
||||
@@ -78,7 +91,6 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
double localSeconds = localMs / 1000.0;
|
||||
double diff = Math.Abs(localSeconds - remoteSeconds.Value);
|
||||
|
||||
// 容差逻辑:
|
||||
// 差距 <= 3秒:100% 相似
|
||||
// 差距 >= 20秒:0% 相似
|
||||
// 中间线性插值
|
||||
@@ -89,8 +101,24 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
if (diff <= PerfectTolerance) return 1.0;
|
||||
if (diff >= MaxTolerance) return 0.0;
|
||||
|
||||
// 线性递减公式
|
||||
return 1.0 - ((diff - PerfectTolerance) / (MaxTolerance - PerfectTolerance));
|
||||
}
|
||||
|
||||
private static string CreateSortedFingerprint(string? input)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(input)) return "";
|
||||
|
||||
input = input.ToLowerInvariant();
|
||||
|
||||
string cleaned = NonWordCharactersRegex().Replace(input, " ");
|
||||
|
||||
var tokens = cleaned.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries)
|
||||
.OrderBy(t => t); // 排序
|
||||
|
||||
return string.Join(" ", tokens);
|
||||
}
|
||||
|
||||
[GeneratedRegex(@"[\p{P}\p{S}]")]
|
||||
private static partial Regex NonWordCharactersRegex();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,23 +12,23 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
/// <param name="value">要保存的值</param>
|
||||
public static void Save(string resource, string key, string value)
|
||||
{
|
||||
var vault = new PasswordVault();
|
||||
|
||||
// 删除旧值(避免重复存储)
|
||||
try
|
||||
{
|
||||
var vault = new PasswordVault();
|
||||
|
||||
var oldCredential = vault.Retrieve(resource, key);
|
||||
if (oldCredential != null)
|
||||
{
|
||||
vault.Remove(oldCredential);
|
||||
}
|
||||
|
||||
vault.Add(new PasswordCredential(resource, key, value));
|
||||
}
|
||||
catch
|
||||
{
|
||||
// 没有旧值就忽略
|
||||
}
|
||||
|
||||
vault.Add(new PasswordCredential(resource, key, value));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -39,9 +39,10 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
/// <returns>存储的值,若不存在则返回 null</returns>
|
||||
public static string? Get(string resource, string key)
|
||||
{
|
||||
var vault = new PasswordVault();
|
||||
try
|
||||
{
|
||||
var vault = new PasswordVault();
|
||||
|
||||
var credential = vault.Retrieve(resource, key);
|
||||
credential.RetrievePassword();
|
||||
return credential.Password;
|
||||
@@ -57,9 +58,10 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
/// </summary>
|
||||
public static void Delete(string resource, string key)
|
||||
{
|
||||
var vault = new PasswordVault();
|
||||
try
|
||||
{
|
||||
var vault = new PasswordVault();
|
||||
|
||||
var credential = vault.Retrieve(resource, key);
|
||||
vault.Remove(credential);
|
||||
}
|
||||
|
||||
@@ -45,6 +45,10 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
public static string KugouLyricsCacheDirectory => Path.Combine(LyricsCacheDirectory, "kugou");
|
||||
public static string AmllTtmlDbLyricsCacheDirectory => Path.Combine(LyricsCacheDirectory, "amll-ttml-db");
|
||||
public static string AppleMusicCacheDirectory => Path.Combine(LyricsCacheDirectory, "apple-music");
|
||||
public static string LocalMusicCacheDirectory => Path.Combine(LyricsCacheDirectory, "local-music");
|
||||
public static string LocalLrcCacheDirectory => Path.Combine(LyricsCacheDirectory, "local-lrc");
|
||||
public static string LocalEslrcCacheDirectory => Path.Combine(LyricsCacheDirectory, "local-eslrc");
|
||||
public static string LocalTtmlCacheDirectory => Path.Combine(LyricsCacheDirectory, "local-ttml");
|
||||
public static string AmllTtmlDbIndexPath => Path.Combine(LyricsCacheDirectory, "amll-ttml-db-index.jsonl");
|
||||
public static string AmllTtmlDbLastUpdatedPath => Path.Combine(LyricsCacheDirectory, "amll-ttml-db-last-updated.txt");
|
||||
|
||||
@@ -65,6 +69,10 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
Directory.CreateDirectory(NeteaseLyricsCacheDirectory);
|
||||
Directory.CreateDirectory(AmllTtmlDbLyricsCacheDirectory);
|
||||
Directory.CreateDirectory(AppleMusicCacheDirectory);
|
||||
Directory.CreateDirectory(LocalMusicCacheDirectory);
|
||||
Directory.CreateDirectory(LocalLrcCacheDirectory);
|
||||
Directory.CreateDirectory(LocalEslrcCacheDirectory);
|
||||
Directory.CreateDirectory(LocalTtmlCacheDirectory);
|
||||
|
||||
Directory.CreateDirectory(iTunesAlbumArtCacheDirectory);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
using BetterLyrics.WinUI3.Services.ResourceService;
|
||||
using CommunityToolkit.Mvvm.DependencyInjection;
|
||||
using System;
|
||||
using System.Linq;
|
||||
|
||||
namespace BetterLyrics.WinUI3.Helper
|
||||
{
|
||||
@@ -9,13 +8,13 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
{
|
||||
private static readonly IResourceService _resourceService = Ioc.Default.GetRequiredService<IResourceService>();
|
||||
|
||||
public const string PinyinCode = "zh-pinyin";
|
||||
public const string JyutpingCode = "zh-jyutping";
|
||||
public const string RomajiCode = "ja-romaji";
|
||||
public const string PinyinCode = "zh-cmn-pinyin";
|
||||
public const string JyutpingCode = "zh-yue-jyutping";
|
||||
public const string RomanCode = "ja-latin";
|
||||
|
||||
public static bool IsPhoneticCode(string code)
|
||||
{
|
||||
return code == PinyinCode || code == JyutpingCode || code == RomajiCode;
|
||||
return code == PinyinCode || code == JyutpingCode || code == RomanCode;
|
||||
}
|
||||
|
||||
public static string GetDisplayName(string code)
|
||||
@@ -26,7 +25,7 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
return _resourceService.GetLocalizedString("Pinyin");
|
||||
case JyutpingCode:
|
||||
return _resourceService.GetLocalizedString("Jyutping");
|
||||
case RomajiCode:
|
||||
case RomanCode:
|
||||
return _resourceService.GetLocalizedString("Romaji");
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException(nameof(code));
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using BetterLyrics.WinUI3.Constants;
|
||||
using Lyricify.Lyrics.Providers;
|
||||
using System.Collections.Generic;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
using BetterLyrics.WinUI3.Services.ResourceService;
|
||||
using CommunityToolkit.Mvvm.DependencyInjection;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using Microsoft.Windows.AppNotifications;
|
||||
using Microsoft.Windows.AppNotifications.Builder;
|
||||
|
||||
namespace BetterLyrics.WinUI3.Helper
|
||||
{
|
||||
public class ToastHelper
|
||||
{
|
||||
private static readonly IResourceService _resourceService = Ioc.Default.GetRequiredService<IResourceService>();
|
||||
|
||||
public static void ShowToast(string localizedTitleKey, string? description, InfoBarSeverity severity)
|
||||
{
|
||||
AppNotification notification = new AppNotificationBuilder()
|
||||
.AddText(_resourceService.GetLocalizedString(localizedTitleKey))
|
||||
.AddText(description)
|
||||
.BuildNotification();
|
||||
|
||||
AppNotificationManager.Default.Show(notification);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -57,7 +57,7 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
|
||||
public void SetDuration(double seconds)
|
||||
{
|
||||
if (seconds <= 0)
|
||||
if (seconds < 0)
|
||||
throw new ArgumentOutOfRangeException(nameof(seconds), "Duration must be positive.");
|
||||
_durationSeconds = seconds;
|
||||
}
|
||||
@@ -145,7 +145,7 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
}
|
||||
}
|
||||
|
||||
private Func<T, T, double, T> GetInterpolatorByEasingType(EasingType type)
|
||||
private Func<T, T, double, T> GetInterpolatorByEasingType(EasingType? type)
|
||||
{
|
||||
if (typeof(T) == typeof(double))
|
||||
{
|
||||
@@ -193,6 +193,7 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
t = EasingHelper.Linear(t);
|
||||
break;
|
||||
default:
|
||||
t = EasingHelper.EaseInOutQuad(t);
|
||||
break;
|
||||
}
|
||||
return (T)(object)(s + (e - s) * t);
|
||||
@@ -201,7 +202,7 @@ namespace BetterLyrics.WinUI3.Helper
|
||||
throw new NotSupportedException($"Easing type {type} is not supported for type {typeof(T)}.");
|
||||
}
|
||||
|
||||
public void SetEasingType(EasingType easingType)
|
||||
public void SetEasingType(EasingType? easingType)
|
||||
{
|
||||
_easingType = easingType;
|
||||
_interpolator = GetInterpolatorByEasingType(easingType);
|
||||
|
||||
@@ -1,14 +1,9 @@
|
||||
using DevWinUI;
|
||||
using Microsoft.UI.Xaml.Media.Imaging;
|
||||
using Microsoft.UI.Xaml.Media.Imaging;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
using System.Drawing;
|
||||
using System.Drawing.Imaging;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Vanara.PInvoke;
|
||||
using Vanara.Windows.Shell;
|
||||
|
||||
@@ -14,7 +14,6 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.InteropServices;
|
||||
using Vanara.PInvoke;
|
||||
using Vanara.Windows.Shell;
|
||||
using Windows.ApplicationModel.Core;
|
||||
using Windows.Foundation;
|
||||
using WinRT.Interop;
|
||||
@@ -119,6 +118,7 @@ namespace BetterLyrics.WinUI3.Hooks
|
||||
{
|
||||
throw new ArgumentException("Unsupported window type", nameof(T));
|
||||
}
|
||||
|
||||
TrackWindow(window);
|
||||
var castedWindow = (Window)window;
|
||||
castedWindow.Restore();
|
||||
|
||||
@@ -17,6 +17,7 @@ namespace BetterLyrics.WinUI3.Models
|
||||
get => field ?? LanguageHelper.DetectLanguageCode(WrappedOriginalText);
|
||||
set => field = value;
|
||||
}
|
||||
public bool AutoGenerated { get; set; } = false;
|
||||
public string WrappedOriginalText => string.Join(StringHelper.NewLine, LyricsLines.Select(line => line.OriginalText));
|
||||
public bool IsWordByWord => LyricsLines.Any(x => x.LyricsChars.Count != 0);
|
||||
|
||||
@@ -130,14 +131,23 @@ namespace BetterLyrics.WinUI3.Models
|
||||
return result;
|
||||
}
|
||||
|
||||
public static LyricsData GetNotfoundPlaceholder(int durationMs)
|
||||
public static LyricsData GetNotfoundPlaceholder()
|
||||
{
|
||||
return new LyricsData([new LyricsLine
|
||||
{
|
||||
StartMs = 0,
|
||||
EndMs = durationMs,
|
||||
EndMs = (int)TimeSpan.FromMinutes(99).TotalMilliseconds,
|
||||
OriginalText = _resourceService.GetLocalizedString("LyricsNotFound"),
|
||||
LyricsChars = [],
|
||||
}]);
|
||||
}
|
||||
|
||||
public static LyricsData GetParseErrorPlaceholder()
|
||||
{
|
||||
return new LyricsData([new LyricsLine
|
||||
{
|
||||
StartMs = 0,
|
||||
EndMs = (int)TimeSpan.FromMinutes(99).TotalMilliseconds,
|
||||
OriginalText = _resourceService.GetLocalizedString("LyricsParseError"),
|
||||
}]);
|
||||
}
|
||||
|
||||
@@ -148,10 +158,7 @@ namespace BetterLyrics.WinUI3.Models
|
||||
{
|
||||
StartMs = 0,
|
||||
EndMs = (int)TimeSpan.FromMinutes(99).TotalMilliseconds,
|
||||
PhoneticText = "",
|
||||
OriginalText = "● ● ●",
|
||||
TranslatedText = "",
|
||||
LyricsChars = [],
|
||||
},
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
using BetterLyrics.WinUI3.Enums;
|
||||
using BetterLyrics.WinUI3.Extensions;
|
||||
using BetterLyrics.WinUI3.Helper;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using Microsoft.Graphics.Canvas.Geometry;
|
||||
using Microsoft.Graphics.Canvas.Text;
|
||||
using Microsoft.Graphics.Canvas.UI.Xaml;
|
||||
@@ -13,37 +14,13 @@ namespace BetterLyrics.WinUI3.Models
|
||||
{
|
||||
public class LyricsLine
|
||||
{
|
||||
private const double _animationDuration = 0.3;
|
||||
public ValueTransition<double> AngleTransition { get; set; } = new(
|
||||
initialValue: 0,
|
||||
durationSeconds: _animationDuration,
|
||||
easingType: EasingType.EaseInOutQuad
|
||||
);
|
||||
public ValueTransition<double> BlurAmountTransition { get; set; } = new(
|
||||
initialValue: 0,
|
||||
durationSeconds: _animationDuration,
|
||||
easingType: EasingType.EaseInOutQuad
|
||||
);
|
||||
public ValueTransition<double> HighlightOpacityTransition { get; set; } = new(
|
||||
initialValue: 0,
|
||||
durationSeconds: _animationDuration,
|
||||
easingType: EasingType.EaseInOutQuad
|
||||
);
|
||||
public ValueTransition<double> OpacityTransition { get; set; } = new(
|
||||
initialValue: 0,
|
||||
durationSeconds: _animationDuration,
|
||||
easingType: EasingType.EaseInOutQuad
|
||||
);
|
||||
public ValueTransition<double> ScaleTransition { get; set; } = new(
|
||||
initialValue: 0,
|
||||
durationSeconds: _animationDuration,
|
||||
easingType: EasingType.EaseInOutQuad
|
||||
);
|
||||
public ValueTransition<double> YOffsetTransition { get; set; } = new(
|
||||
initialValue: 0,
|
||||
durationSeconds: 0.5,
|
||||
easingType: EasingType.EaseInOutQuad
|
||||
);
|
||||
public double AnimationDuration { get; set; } = 0.3;
|
||||
public ValueTransition<double> AngleTransition { get; set; }
|
||||
public ValueTransition<double> BlurAmountTransition { get; set; }
|
||||
public ValueTransition<double> HighlightOpacityTransition { get; set; }
|
||||
public ValueTransition<double> OpacityTransition { get; set; }
|
||||
public ValueTransition<double> ScaleTransition { get; set; }
|
||||
public ValueTransition<double> YOffsetTransition { get; set; }
|
||||
|
||||
public CanvasTextLayout? OriginalCanvasTextLayout { get; private set; }
|
||||
public CanvasTextLayout? TranslatedCanvasTextLayout { get; private set; }
|
||||
@@ -86,6 +63,40 @@ namespace BetterLyrics.WinUI3.Models
|
||||
public CanvasGeometry? TranslatedCanvasGeometry { get; private set; }
|
||||
public CanvasGeometry? PhoneticCanvasGeometry { get; private set; }
|
||||
|
||||
public LyricsLine()
|
||||
{
|
||||
AngleTransition = new(
|
||||
initialValue: 0,
|
||||
durationSeconds: AnimationDuration,
|
||||
easingType: EasingType.EaseInOutQuad
|
||||
);
|
||||
BlurAmountTransition = new(
|
||||
initialValue: 0,
|
||||
durationSeconds: AnimationDuration,
|
||||
easingType: EasingType.EaseInOutQuad
|
||||
);
|
||||
HighlightOpacityTransition = new(
|
||||
initialValue: 0,
|
||||
durationSeconds: AnimationDuration,
|
||||
easingType: EasingType.EaseInOutQuad
|
||||
);
|
||||
OpacityTransition = new(
|
||||
initialValue: 0,
|
||||
durationSeconds: AnimationDuration,
|
||||
easingType: EasingType.EaseInOutQuad
|
||||
);
|
||||
ScaleTransition = new(
|
||||
initialValue: 0,
|
||||
durationSeconds: AnimationDuration,
|
||||
easingType: EasingType.EaseInOutQuad
|
||||
);
|
||||
YOffsetTransition = new(
|
||||
initialValue: 0,
|
||||
durationSeconds: AnimationDuration,
|
||||
easingType: EasingType.EaseInOutQuad
|
||||
);
|
||||
}
|
||||
|
||||
public void UpdateCenterPosition(double maxWidth, TextAlignmentType type)
|
||||
{
|
||||
if (OriginalCanvasTextLayout == null)
|
||||
|
||||
@@ -8,8 +8,9 @@ namespace BetterLyrics.WinUI3.Models
|
||||
public partial class LyricsSearchProviderInfo : ObservableRecipient
|
||||
{
|
||||
[ObservableProperty][NotifyPropertyChangedRecipients] public partial bool IsEnabled { get; set; }
|
||||
|
||||
[ObservableProperty][NotifyPropertyChangedRecipients] public partial LyricsSearchProvider Provider { get; set; }
|
||||
[ObservableProperty][NotifyPropertyChangedRecipients] public partial bool IsMatchingThresholdOverwritten { get; set; } = false;
|
||||
[ObservableProperty][NotifyPropertyChangedRecipients] public partial int MatchingThreshold { get; set; } = 0;
|
||||
|
||||
public LyricsSearchProviderInfo() { }
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using BetterLyrics.WinUI3.Enums;
|
||||
using BetterLyrics.WinUI3.Extensions;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using NTextCat.Commons;
|
||||
using System;
|
||||
@@ -10,8 +11,17 @@ namespace BetterLyrics.WinUI3.Models
|
||||
public LyricsSearchProvider Provider { get; set; }
|
||||
|
||||
public string? Raw { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 翻译也可能位于 <see cref="Raw"/>
|
||||
/// </summary>
|
||||
public string? Translation { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 音译也可能位于 <see cref="Raw"/>
|
||||
/// </summary>
|
||||
public string? Transliteration { get; set; }
|
||||
|
||||
public string? Title { get; set; }
|
||||
public string[]? Artists { get; set; }
|
||||
public string? Album { get; set; }
|
||||
@@ -19,6 +29,8 @@ namespace BetterLyrics.WinUI3.Models
|
||||
[ObservableProperty] public partial int MatchPercentage { get; set; } = -1;
|
||||
[ObservableProperty] public partial string Reference { get; set; } = "about:blank";
|
||||
|
||||
public string? SelfPath { get; set; }
|
||||
|
||||
public bool IsFound => !string.IsNullOrEmpty(Raw);
|
||||
|
||||
public LyricsSearchProvider? ProviderIfFound => IsFound ? Provider : null;
|
||||
|
||||
@@ -1,17 +1,11 @@
|
||||
// 2025/6/23 by Zhe Fang
|
||||
|
||||
using BetterLyrics.WinUI3.Collections;
|
||||
using BetterLyrics.WinUI3.Constants;
|
||||
using BetterLyrics.WinUI3.Enums;
|
||||
using BetterLyrics.WinUI3.Helper;
|
||||
using BetterLyrics.WinUI3.Hooks;
|
||||
using BetterLyrics.WinUI3.ViewModels;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using CommunityToolkit.Mvvm.DependencyInjection;
|
||||
using Microsoft.UI.Xaml.Media.Imaging;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace BetterLyrics.WinUI3.Models
|
||||
{
|
||||
@@ -38,6 +32,7 @@ namespace BetterLyrics.WinUI3.Models
|
||||
|
||||
[ObservableProperty][NotifyPropertyChangedRecipients] public partial FullyObservableCollection<AlbumArtSearchProviderInfo> AlbumArtSearchProvidersInfo { get; set; } = [.. Enum.GetValues<AlbumArtSearchProvider>().Select(p => new AlbumArtSearchProviderInfo(p, true))];
|
||||
[ObservableProperty][NotifyPropertyChangedRecipients] public partial LyricsSearchType LyricsSearchType { get; set; } = LyricsSearchType.Sequential;
|
||||
[ObservableProperty][NotifyPropertyChangedRecipients] public partial int MatchingThreshold { get; set; } = 0;
|
||||
|
||||
public string LogoPath => PlayerIDHelper.GetLogoPath(Provider);
|
||||
|
||||
|
||||
@@ -0,0 +1,114 @@
|
||||
using BetterLyrics.WinUI3.Models;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace BetterLyrics.WinUI3.Parsers.LyricsParser
|
||||
{
|
||||
public partial class LyricsParser
|
||||
{
|
||||
[GeneratedRegex(@"\[(\d*):(\d*)(\.|\:)(\d*)\]")]
|
||||
private static partial Regex LrcRegex();
|
||||
[GeneratedRegex(@"(\[|\<)(\d*):(\d*)\.(\d*)(\]|\>)([^\[\]\<\>]*)")]
|
||||
private static partial Regex SyllableRegex();
|
||||
|
||||
private void ParseLrc(string raw, bool single)
|
||||
{
|
||||
var lines = raw.Split(["\r\n", "\n"], StringSplitOptions.RemoveEmptyEntries);
|
||||
var lrcLines = new List<LyricsLine>();
|
||||
|
||||
// 支持 [mm:ss.xx]字、<mm:ss.xx>字,毫秒两位或三位
|
||||
var syllableRegex = SyllableRegex();
|
||||
|
||||
foreach (var line in lines)
|
||||
{
|
||||
var matches = syllableRegex.Matches(line);
|
||||
var syllables = new List<LyricsChar>();
|
||||
|
||||
int startIndex = 0;
|
||||
for (int i = 0; i < matches.Count; i++)
|
||||
{
|
||||
var match = matches[i];
|
||||
int min = int.Parse(match.Groups[2].Value);
|
||||
int sec = int.Parse(match.Groups[3].Value);
|
||||
int ms = int.Parse(match.Groups[4].Value.PadRight(3, '0'));
|
||||
int totalMs = min * 60_000 + sec * 1000 + ms;
|
||||
string text = match.Groups[6].Value;
|
||||
|
||||
syllables.Add(new LyricsChar { StartMs = totalMs, Text = text, StartIndex = startIndex });
|
||||
startIndex += text.Length;
|
||||
}
|
||||
|
||||
if (syllables.Count > 1)
|
||||
{
|
||||
lrcLines.Add(new LyricsLine
|
||||
{
|
||||
StartMs = syllables[0].StartMs,
|
||||
OriginalText = string.Concat(syllables.Select(s => s.Text)),
|
||||
LyricsChars = syllables
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
// 普通LRC行
|
||||
Regex? bracketRegex = LrcRegex();
|
||||
var bracketMatches = bracketRegex.Matches(line);
|
||||
|
||||
string content = line;
|
||||
int? lineStartTime = null;
|
||||
if (bracketMatches.Count > 0)
|
||||
{
|
||||
var match = bracketMatches[0];
|
||||
int min = int.Parse(match.Groups[1].Value);
|
||||
int sec = int.Parse(match.Groups[2].Value);
|
||||
int ms = int.Parse(match.Groups[4].Value.PadRight(3, '0'));
|
||||
lineStartTime = min * 60_000 + sec * 1000 + ms;
|
||||
content = bracketRegex!.Replace(line, "").Trim();
|
||||
if (content == "//") content = "";
|
||||
lrcLines.Add(new LyricsLine { StartMs = lineStartTime.Value, OriginalText = content });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (single)
|
||||
{
|
||||
LyricsDataArr.Add(new LyricsData(lrcLines));
|
||||
}
|
||||
else
|
||||
{
|
||||
// 按时间分组
|
||||
var grouped = lrcLines.GroupBy(l => l.StartMs).OrderBy(g => g.Key).ToList();
|
||||
int languageCount = 0;
|
||||
if (grouped != null && grouped.Count > 0)
|
||||
{
|
||||
// 计算最大语言数量
|
||||
languageCount = grouped.Max(g => g.Count());
|
||||
}
|
||||
|
||||
// 初始化每种语言的歌词列表
|
||||
int langStartIndex = LyricsDataArr.Count;
|
||||
for (int i = 0; i < languageCount; i++) LyricsDataArr.Add(new LyricsData());
|
||||
|
||||
// 遍历每个时间分组
|
||||
if (grouped != null)
|
||||
{
|
||||
foreach (var group in grouped)
|
||||
{
|
||||
var linesInGroup = group.ToList();
|
||||
for (int langIdx = 0; langIdx < languageCount; langIdx++)
|
||||
{
|
||||
// 只添加有对应行的语言,否则跳过
|
||||
if (langIdx < linesInGroup.Count)
|
||||
{
|
||||
var lyricsLine = linesInGroup[langIdx];
|
||||
LyricsDataArr[langStartIndex + langIdx].LyricsLines.Add(lyricsLine);
|
||||
}
|
||||
// 没有翻译行则不补原文,直接跳过
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
using BetterLyrics.WinUI3.Models;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace BetterLyrics.WinUI3.Parsers.LyricsParser
|
||||
{
|
||||
public partial class LyricsParser
|
||||
{
|
||||
private void ParseQrcKrc(List<Lyricify.Lyrics.Models.ILineInfo>? lines)
|
||||
{
|
||||
lines = lines?.Where(x => x.Text != string.Empty).ToList();
|
||||
List<LyricsLine> lyricsLines = [];
|
||||
|
||||
if (lines != null && lines.Count > 0)
|
||||
{
|
||||
lyricsLines = [];
|
||||
for (int lineIndex = 0; lineIndex < lines.Count; lineIndex++)
|
||||
{
|
||||
var lineRead = lines[lineIndex];
|
||||
var lineWrite = new LyricsLine
|
||||
{
|
||||
StartMs = lineRead.StartTime ?? 0,
|
||||
EndMs = lineRead.EndTime ?? 0,
|
||||
OriginalText = lineRead.Text,
|
||||
LyricsChars = [],
|
||||
};
|
||||
|
||||
var syllables = (lineRead as Lyricify.Lyrics.Models.SyllableLineInfo)?.Syllables;
|
||||
if (syllables != null)
|
||||
{
|
||||
int startIndex = 0;
|
||||
for (
|
||||
int syllableIndex = 0;
|
||||
syllableIndex < syllables.Count;
|
||||
syllableIndex++
|
||||
)
|
||||
{
|
||||
var syllable = syllables[syllableIndex];
|
||||
var charTiming = new LyricsChar
|
||||
{
|
||||
StartMs = syllable.StartTime,
|
||||
EndMs = syllable.EndTime,
|
||||
Text = syllable.Text,
|
||||
StartIndex = startIndex,
|
||||
};
|
||||
lineWrite.LyricsChars.Add(charTiming);
|
||||
startIndex += syllable.Text.Length;
|
||||
}
|
||||
}
|
||||
|
||||
lyricsLines.Add(lineWrite);
|
||||
}
|
||||
}
|
||||
|
||||
LyricsDataArr.Add(new LyricsData(lyricsLines));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,202 @@
|
||||
using BetterLyrics.WinUI3.Helper;
|
||||
using BetterLyrics.WinUI3.Models;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace BetterLyrics.WinUI3.Parsers.LyricsParser
|
||||
{
|
||||
public partial class LyricsParser
|
||||
{
|
||||
private void ParseTtml(string raw)
|
||||
{
|
||||
try
|
||||
{
|
||||
List<LyricsLine> originalLines = [];
|
||||
List<LyricsLine> translationLines = [];
|
||||
List<LyricsLine> romanLines = [];
|
||||
|
||||
var xdoc = XDocument.Parse(raw, LoadOptions.PreserveWhitespace);
|
||||
var body = xdoc.Descendants().FirstOrDefault(e => e.Name.LocalName == "body");
|
||||
if (body == null) return;
|
||||
var ps = body.Descendants().Where(e => e.Name.LocalName == "p");
|
||||
foreach (var p in ps)
|
||||
{
|
||||
// 句级时间
|
||||
string? pBegin = p.Attribute("begin")?.Value;
|
||||
string? pEnd = p.Attribute("end")?.Value;
|
||||
int pStartMs = ParseTtmlTime(pBegin);
|
||||
int pEndMs = ParseTtmlTime(pEnd);
|
||||
|
||||
// 只获取一级span
|
||||
var spans = p.Elements()
|
||||
.Where(s => s.Name.LocalName == "span")
|
||||
.ToList();
|
||||
|
||||
var originalTextSpans = spans
|
||||
.Where(s => s.Attribute(XName.Get("role", "http://www.w3.org/ns/ttml#metadata"))?.Value == null)
|
||||
.ToList();
|
||||
|
||||
// 处理原文span后的空白
|
||||
for (int i = 0; i < originalTextSpans.Count; i++)
|
||||
{
|
||||
var span = originalTextSpans[i];
|
||||
var nextNode = span.NodesAfterSelf().FirstOrDefault();
|
||||
if (nextNode is XText textNode)
|
||||
{
|
||||
span.Value += textNode.Value;
|
||||
}
|
||||
}
|
||||
// 拼接空白字符后的原文
|
||||
string originalText = string.Concat(originalTextSpans.Select(s => s.Value));
|
||||
|
||||
var originalCharTimings = new List<LyricsChar>();
|
||||
int originalStartIndex = 0;
|
||||
foreach (var span in originalTextSpans)
|
||||
{
|
||||
string? sBegin = span.Attribute("begin")?.Value;
|
||||
string? sEnd = span.Attribute("end")?.Value;
|
||||
int sStartMs = ParseTtmlTime(sBegin);
|
||||
int sEndMs = ParseTtmlTime(sEnd);
|
||||
originalCharTimings.Add(new LyricsChar
|
||||
{
|
||||
StartMs = sStartMs,
|
||||
EndMs = sEndMs,
|
||||
StartIndex = originalStartIndex,
|
||||
Text = span.Value
|
||||
});
|
||||
originalStartIndex += span.Value.Length;
|
||||
}
|
||||
if (originalTextSpans.Count == 0)
|
||||
{
|
||||
originalText = p.Value;
|
||||
}
|
||||
|
||||
originalLines.Add(new LyricsLine
|
||||
{
|
||||
StartMs = pStartMs,
|
||||
EndMs = pEndMs,
|
||||
OriginalText = originalText,
|
||||
LyricsChars = originalCharTimings,
|
||||
});
|
||||
|
||||
// 解析 x-role
|
||||
ParseTtmlXRole(spans, translationLines, "x-translation", pStartMs, pEndMs);
|
||||
ParseTtmlXRole(spans, romanLines, "x-roman", pStartMs, pEndMs);
|
||||
}
|
||||
|
||||
LyricsDataArr.Add(new LyricsData(originalLines));
|
||||
if (translationLines.Count > 0)
|
||||
{
|
||||
LyricsDataArr.Add(new LyricsData(translationLines));
|
||||
}
|
||||
if (romanLines.Count > 0)
|
||||
{
|
||||
LyricsDataArr.Add(new LyricsData(romanLines) { LanguageCode = PhoneticHelper.RomanCode });
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// 解析失败,忽略
|
||||
}
|
||||
}
|
||||
|
||||
private void ParseTtmlXRole(List<XElement> sourceSpans, List<LyricsLine> saveLyricsLines, string xRole, int pStartMs, int? pEndMs)
|
||||
{
|
||||
var textSpans = sourceSpans
|
||||
.Where(s => s.Attribute(XName.Get("role", "http://www.w3.org/ns/ttml#metadata"))?.Value == xRole)
|
||||
.ToList();
|
||||
|
||||
string text = string.Concat(textSpans.Select(s => s.Value));
|
||||
var charTimings = new List<LyricsChar>();
|
||||
int startIndex = 0;
|
||||
foreach (var span in textSpans)
|
||||
{
|
||||
string? sBegin = span.Attribute("begin")?.Value;
|
||||
string? sEnd = span.Attribute("end")?.Value;
|
||||
int sStartMs = ParseTtmlTime(sBegin);
|
||||
int sEndMs = ParseTtmlTime(sEnd);
|
||||
charTimings.Add(new LyricsChar
|
||||
{
|
||||
StartMs = sStartMs,
|
||||
EndMs = sEndMs,
|
||||
StartIndex = startIndex,
|
||||
Text = span.Value
|
||||
});
|
||||
startIndex += span.Value.Length;
|
||||
}
|
||||
if (textSpans.Count > 0)
|
||||
{
|
||||
saveLyricsLines.Add(new LyricsLine
|
||||
{
|
||||
StartMs = pStartMs,
|
||||
EndMs = pEndMs,
|
||||
OriginalText = text,
|
||||
LyricsChars = charTimings,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private static int ParseTtmlTime(string? t)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(t))
|
||||
return 0;
|
||||
|
||||
t = t.Trim();
|
||||
|
||||
// 支持 "1.000s"
|
||||
if (t.EndsWith("s"))
|
||||
{
|
||||
if (
|
||||
double.TryParse(
|
||||
t.TrimEnd('s'),
|
||||
System.Globalization.NumberStyles.Float,
|
||||
System.Globalization.CultureInfo.InvariantCulture,
|
||||
out double seconds
|
||||
)
|
||||
)
|
||||
return (int)(seconds * 1000);
|
||||
}
|
||||
else
|
||||
{
|
||||
var parts = t.Split(':');
|
||||
if (parts.Length == 3)
|
||||
{
|
||||
// hh:mm:ss.xxx
|
||||
int h = int.Parse(parts[0]);
|
||||
int m = int.Parse(parts[1]);
|
||||
double s = double.Parse(
|
||||
parts[2],
|
||||
System.Globalization.CultureInfo.InvariantCulture
|
||||
);
|
||||
return (int)((h * 3600 + m * 60 + s) * 1000);
|
||||
}
|
||||
else if (parts.Length == 2)
|
||||
{
|
||||
// mm:ss.xxx
|
||||
int m = int.Parse(parts[0]);
|
||||
double s = double.Parse(
|
||||
parts[1],
|
||||
System.Globalization.CultureInfo.InvariantCulture
|
||||
);
|
||||
return (int)((m * 60 + s) * 1000);
|
||||
}
|
||||
else if (parts.Length == 1)
|
||||
{
|
||||
// ss.xxx
|
||||
if (
|
||||
double.TryParse(
|
||||
parts[0],
|
||||
System.Globalization.NumberStyles.Float,
|
||||
System.Globalization.CultureInfo.InvariantCulture,
|
||||
out double s
|
||||
)
|
||||
)
|
||||
return (int)(s * 1000);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,174 @@
|
||||
// 2025/6/23 by Zhe Fang
|
||||
|
||||
using BetterLyrics.WinUI3.Enums;
|
||||
using BetterLyrics.WinUI3.Extensions;
|
||||
using BetterLyrics.WinUI3.Helper;
|
||||
using BetterLyrics.WinUI3.Models;
|
||||
using CommunityToolkit.Mvvm.DependencyInjection;
|
||||
using Lyricify.Lyrics.Parsers;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace BetterLyrics.WinUI3.Parsers.LyricsParser
|
||||
{
|
||||
public partial class LyricsParser
|
||||
{
|
||||
private static readonly ILogger<LyricsParser> _logger = Ioc.Default.GetRequiredService<ILogger<LyricsParser>>();
|
||||
|
||||
public List<LyricsData> LyricsDataArr { get; private set; } = [];
|
||||
|
||||
public void Parse(SongInfo? songInfo, LyricsSearchResult? lyricsSearchResult)
|
||||
{
|
||||
_logger.LogInformation("LyricsParser.Parse");
|
||||
LyricsDataArr = [];
|
||||
if (string.IsNullOrWhiteSpace(lyricsSearchResult?.Raw))
|
||||
{
|
||||
LyricsDataArr.Add(LyricsData.GetNotfoundPlaceholder());
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (lyricsSearchResult.Raw.DetectFormat())
|
||||
{
|
||||
case LyricsFormat.Lrc:
|
||||
case LyricsFormat.Eslrc:
|
||||
ParseLrc(lyricsSearchResult.Raw, lyricsSearchResult.Provider.IsRemote());
|
||||
break;
|
||||
case LyricsFormat.Qrc:
|
||||
ParseQrcKrc(QrcParser.Parse(lyricsSearchResult.Raw).Lines);
|
||||
break;
|
||||
case LyricsFormat.Krc:
|
||||
ParseQrcKrc(KrcParser.Parse(lyricsSearchResult.Raw).Lines);
|
||||
break;
|
||||
case LyricsFormat.Ttml:
|
||||
ParseTtml(lyricsSearchResult.Raw);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (LyricsDataArr.Count == 0)
|
||||
{
|
||||
LyricsDataArr.Add(LyricsData.GetParseErrorPlaceholder());
|
||||
}
|
||||
}
|
||||
LoadTranslation(lyricsSearchResult);
|
||||
LoadTransliteration(lyricsSearchResult);
|
||||
GenerateTransliterationLyricsData();
|
||||
}
|
||||
|
||||
private void LoadTranslation(LyricsSearchResult? lyricsSearchResult)
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(lyricsSearchResult?.Translation))
|
||||
{
|
||||
switch (lyricsSearchResult.Provider)
|
||||
{
|
||||
case LyricsSearchProvider.QQ:
|
||||
case LyricsSearchProvider.Kugou:
|
||||
case LyricsSearchProvider.Netease:
|
||||
ParseLrc(lyricsSearchResult.Translation, true);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void LoadTransliteration(LyricsSearchResult? lyricsSearchResult)
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(lyricsSearchResult?.Transliteration))
|
||||
{
|
||||
switch (lyricsSearchResult.Provider)
|
||||
{
|
||||
case LyricsSearchProvider.Netease:
|
||||
ParseLrc(lyricsSearchResult.Transliteration, true);
|
||||
LyricsDataArr.LastOrDefault()?.LanguageCode = PhoneticHelper.RomanCode;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 在音译不存在的情况下生成音译歌词
|
||||
/// </summary>
|
||||
private void GenerateTransliterationLyricsData()
|
||||
{
|
||||
var main = LyricsDataArr.FirstOrDefault();
|
||||
if (main != null)
|
||||
{
|
||||
string? languageCode = main.LanguageCode;
|
||||
if (languageCode == LanguageHelper.ChineseCode)
|
||||
{
|
||||
if (!LyricsDataArr.Any(x => x.LanguageCode == PhoneticHelper.PinyinCode))
|
||||
{
|
||||
LyricsDataArr.Add(new LyricsData
|
||||
{
|
||||
LanguageCode = PhoneticHelper.PinyinCode,
|
||||
AutoGenerated = true,
|
||||
LyricsLines = main.LyricsLines.Select(line => new LyricsLine
|
||||
{
|
||||
StartMs = line.StartMs,
|
||||
EndMs = line.EndMs,
|
||||
OriginalText = PhoneticHelper.ToPinyin(line.OriginalText),
|
||||
LyricsChars = line.LyricsChars.Select(c => new LyricsChar
|
||||
{
|
||||
StartMs = c.StartMs,
|
||||
EndMs = c.EndMs,
|
||||
Text = PhoneticHelper.ToPinyin(c.Text),
|
||||
StartIndex = c.StartIndex
|
||||
}).ToList()
|
||||
}).ToList()
|
||||
});
|
||||
}
|
||||
if (!LyricsDataArr.Any(x => x.LanguageCode == PhoneticHelper.JyutpingCode))
|
||||
{
|
||||
LyricsDataArr.Add(new LyricsData
|
||||
{
|
||||
LanguageCode = PhoneticHelper.JyutpingCode,
|
||||
AutoGenerated = true,
|
||||
LyricsLines = main.LyricsLines.Select(line => new LyricsLine
|
||||
{
|
||||
StartMs = line.StartMs,
|
||||
EndMs = line.EndMs,
|
||||
OriginalText = PhoneticHelper.ToJyutping(line.OriginalText),
|
||||
LyricsChars = line.LyricsChars.Select(c => new LyricsChar
|
||||
{
|
||||
StartMs = c.StartMs,
|
||||
EndMs = c.EndMs,
|
||||
Text = PhoneticHelper.ToJyutping(c.Text),
|
||||
StartIndex = c.StartIndex
|
||||
}).ToList()
|
||||
}).ToList()
|
||||
});
|
||||
}
|
||||
}
|
||||
else if (languageCode == LanguageHelper.JapaneseCode)
|
||||
{
|
||||
if (!LyricsDataArr.Any(x => x.LanguageCode == PhoneticHelper.RomanCode))
|
||||
{
|
||||
LyricsDataArr.Add(new LyricsData
|
||||
{
|
||||
LanguageCode = PhoneticHelper.RomanCode,
|
||||
AutoGenerated = true,
|
||||
LyricsLines = main.LyricsLines.Select(line => new LyricsLine
|
||||
{
|
||||
StartMs = line.StartMs,
|
||||
EndMs = line.EndMs,
|
||||
OriginalText = PhoneticHelper.ToRomaji(line.OriginalText),
|
||||
LyricsChars = line.LyricsChars.Select(c => new LyricsChar
|
||||
{
|
||||
StartMs = c.StartMs,
|
||||
EndMs = c.EndMs,
|
||||
Text = PhoneticHelper.ToRomaji(c.Text),
|
||||
StartIndex = c.StartIndex
|
||||
}).ToList()
|
||||
}).ToList()
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,6 @@
|
||||
using BetterLyrics.WinUI3.Helper;
|
||||
using BetterLyrics.WinUI3.Extensions;
|
||||
using BetterLyrics.WinUI3.Helper;
|
||||
using BetterLyrics.WinUI3.Models;
|
||||
using System;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
@@ -67,7 +69,7 @@ namespace BetterLyrics.WinUI3.Providers
|
||||
_client.DefaultRequestHeaders.Add("Accept-Language", $"{_language},en;q=0.9");
|
||||
}
|
||||
|
||||
public async Task<string?> GetLyricsAsync(string id)
|
||||
private async Task<string?> GetLyricsAsync(string id)
|
||||
{
|
||||
var apiUrl = $"https://amp-api.music.apple.com/v1/catalog/{_storefront}/songs/{id}";
|
||||
var url = apiUrl + $"?include[songs]=lyrics,syllable-lyrics&l={_language}";
|
||||
@@ -109,9 +111,14 @@ namespace BetterLyrics.WinUI3.Providers
|
||||
return null;
|
||||
}
|
||||
|
||||
public async Task<string> SearchSongInfoAsync(string artist, string title)
|
||||
public async Task<LyricsSearchResult> SearchSongInfoAsync(Models.SongInfo songInfo)
|
||||
{
|
||||
var query = $"{artist} {title}";
|
||||
LyricsSearchResult lyricsSearchResult = new()
|
||||
{
|
||||
Provider = Enums.LyricsSearchProvider.AppleMusic
|
||||
};
|
||||
|
||||
var query = $"{songInfo.DisplayArtists} {songInfo.Title}";
|
||||
var apiUrl = $"https://amp-api.music.apple.com/v1/catalog/{_storefront}/search";
|
||||
var url = apiUrl + $"?term={WebUtility.UrlEncode(query)}&types=songs&limit=1&l={_language}";
|
||||
var resp = await _client.GetStringAsync(url);
|
||||
@@ -120,9 +127,26 @@ namespace BetterLyrics.WinUI3.Providers
|
||||
if (results.TryGetProperty("songs", out var songs) && songs.GetProperty("data").GetArrayLength() > 0)
|
||||
{
|
||||
var song = songs.GetProperty("data")[0];
|
||||
return song.GetProperty("id").ToString();
|
||||
|
||||
var id = song.GetProperty("id").ToString();
|
||||
|
||||
var attr = song.GetProperty("attributes");
|
||||
|
||||
lyricsSearchResult.Title = attr.GetProperty("name").ToString();
|
||||
lyricsSearchResult.Artists = attr.GetProperty("artistName").ToString().SplitByCommonSplitter();
|
||||
lyricsSearchResult.Album = attr.GetProperty("albumName").ToString();
|
||||
lyricsSearchResult.Duration = attr.GetProperty("durationInMillis").GetInt32() / 1000.0;
|
||||
|
||||
lyricsSearchResult.Reference = $"https://music.apple.com/song/{id}";
|
||||
lyricsSearchResult.MatchPercentage = MetadataComparer.CalculateScore(songInfo, lyricsSearchResult);
|
||||
|
||||
if (id != null)
|
||||
{
|
||||
lyricsSearchResult.Raw = await GetLyricsAsync(id);
|
||||
}
|
||||
}
|
||||
return string.Empty;
|
||||
|
||||
return lyricsSearchResult;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ namespace BetterLyrics.WinUI3.Services.DiscordService
|
||||
{
|
||||
StatusDisplay = StatusDisplayType.Details,
|
||||
Type = ActivityType.Listening,
|
||||
Buttons = new Button[] { new() { Label = "Get this status", Url = Constants.Link.MicrosoftStoreUrl } },
|
||||
Buttons = new Button[] { new() { Label = "Get this status", Url = Constants.Link.MicrosoftStore } },
|
||||
Assets = new Assets
|
||||
{
|
||||
LargeImageKey = "banner",
|
||||
|
||||
@@ -47,7 +47,7 @@ namespace BetterLyrics.WinUI3.Services.LastFMService
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
DevWinUI.Growl.Error(_resourceService.GetLocalizedString("LastFMAuthFailed") ?? "");
|
||||
ToastHelper.ShowToast("LastFMAuthFailed", null, InfoBarSeverity.Error);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -18,10 +18,8 @@ using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Nodes;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Documents;
|
||||
|
||||
namespace BetterLyrics.WinUI3.Services.LyricsSearchService
|
||||
{
|
||||
@@ -42,7 +40,7 @@ namespace BetterLyrics.WinUI3.Services.LyricsSearchService
|
||||
_lrcLibHttpClient = new();
|
||||
_lrcLibHttpClient.DefaultRequestHeaders.Add(
|
||||
"User-Agent",
|
||||
$"{Constants.App.AppName} {MetadataHelper.AppVersion} ({Constants.Link.GitHubUrl})"
|
||||
$"{Constants.App.AppName} {MetadataHelper.AppVersion} ({Constants.Link.GitHub})"
|
||||
);
|
||||
_amllTtmlDbHttpClient = new();
|
||||
_appleMusic = new AppleMusic();
|
||||
@@ -146,32 +144,42 @@ namespace BetterLyrics.WinUI3.Services.LyricsSearchService
|
||||
|
||||
List<LyricsSearchResult> lyricsSearchResults = [];
|
||||
|
||||
// 曲目没有被映射
|
||||
foreach (var provider in _settingsService.AppSettings.MediaSourceProvidersInfo.FirstOrDefault(x => x.Provider == songInfo.PlayerId)?.LyricsSearchProvidersInfo ?? [])
|
||||
var mediaSourceProviderInfo = _settingsService.AppSettings.MediaSourceProvidersInfo.FirstOrDefault(x => x.Provider == songInfo.PlayerId);
|
||||
if (mediaSourceProviderInfo != null)
|
||||
{
|
||||
if (!provider.IsEnabled)
|
||||
// 曲目没有被映射
|
||||
foreach (var provider in mediaSourceProviderInfo.LyricsSearchProvidersInfo)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
lyricsSearchResult = await SearchSingleAsync(
|
||||
((SongInfo)songInfo.Clone())
|
||||
.WithTitle(overridenTitle)
|
||||
.WithArtist(overridenArtists)
|
||||
.WithAlbum(overridenAlbum),
|
||||
provider.Provider, checkCache, token);
|
||||
|
||||
if (lyricsSearchResult.IsFound)
|
||||
{
|
||||
switch (lyricsSearchType)
|
||||
if (!provider.IsEnabled)
|
||||
{
|
||||
case LyricsSearchType.Sequential:
|
||||
return lyricsSearchResult;
|
||||
case LyricsSearchType.BestMatch:
|
||||
lyricsSearchResults.Add((LyricsSearchResult)lyricsSearchResult.Clone());
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
continue;
|
||||
}
|
||||
|
||||
lyricsSearchResult = await SearchSingleAsync(
|
||||
((SongInfo)songInfo.Clone())
|
||||
.WithTitle(overridenTitle)
|
||||
.WithArtist(overridenArtists)
|
||||
.WithAlbum(overridenAlbum),
|
||||
provider.Provider, checkCache, token);
|
||||
|
||||
int matchingThreshold = mediaSourceProviderInfo.MatchingThreshold;
|
||||
if (provider.IsMatchingThresholdOverwritten)
|
||||
{
|
||||
matchingThreshold = provider.MatchingThreshold;
|
||||
}
|
||||
|
||||
if (lyricsSearchResult.IsFound && lyricsSearchResult.MatchPercentage >= matchingThreshold)
|
||||
{
|
||||
switch (lyricsSearchType)
|
||||
{
|
||||
case LyricsSearchType.Sequential:
|
||||
return lyricsSearchResult;
|
||||
case LyricsSearchType.BestMatch:
|
||||
lyricsSearchResults.Add((LyricsSearchResult)lyricsSearchResult.Clone());
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -205,8 +213,6 @@ namespace BetterLyrics.WinUI3.Services.LyricsSearchService
|
||||
|
||||
try
|
||||
{
|
||||
LyricsFormat lyricsFormat = provider.GetLyricsFormat();
|
||||
|
||||
// Check cache first if allowed
|
||||
if (checkCache && provider.IsRemote())
|
||||
{
|
||||
@@ -218,47 +224,40 @@ namespace BetterLyrics.WinUI3.Services.LyricsSearchService
|
||||
}
|
||||
}
|
||||
|
||||
if (provider.IsLocal())
|
||||
switch (provider)
|
||||
{
|
||||
if (provider == LyricsSearchProvider.LocalMusicFile)
|
||||
{
|
||||
case LyricsSearchProvider.QQ:
|
||||
lyricsSearchResult = await SearchQQNeteaseKugouAsync(songInfo, Searchers.QQMusic);
|
||||
break;
|
||||
case LyricsSearchProvider.Kugou:
|
||||
lyricsSearchResult = await SearchQQNeteaseKugouAsync(songInfo, Searchers.Kugou);
|
||||
break;
|
||||
case LyricsSearchProvider.Netease:
|
||||
lyricsSearchResult = await SearchQQNeteaseKugouAsync(songInfo, Searchers.Netease);
|
||||
break;
|
||||
case LyricsSearchProvider.LrcLib:
|
||||
lyricsSearchResult = await SearchLrcLibAsync(songInfo);
|
||||
break;
|
||||
case LyricsSearchProvider.AmllTtmlDb:
|
||||
lyricsSearchResult = await SearchAmllTtmlDbAsync(songInfo);
|
||||
break;
|
||||
case LyricsSearchProvider.LocalMusicFile:
|
||||
lyricsSearchResult = SearchEmbedded(songInfo);
|
||||
}
|
||||
else
|
||||
{
|
||||
lyricsSearchResult = await SearchFile(songInfo, lyricsFormat);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (provider)
|
||||
{
|
||||
case LyricsSearchProvider.LrcLib:
|
||||
lyricsSearchResult = await SearchLrcLibAsync(songInfo);
|
||||
break;
|
||||
case LyricsSearchProvider.QQ:
|
||||
lyricsSearchResult = await SearchQQNeteaseKugouAsync(songInfo, Searchers.QQMusic);
|
||||
break;
|
||||
case LyricsSearchProvider.Kugou:
|
||||
lyricsSearchResult = await SearchQQNeteaseKugouAsync(songInfo, Searchers.Kugou);
|
||||
break;
|
||||
case LyricsSearchProvider.Netease:
|
||||
lyricsSearchResult = await SearchQQNeteaseKugouAsync(songInfo, Searchers.Netease);
|
||||
break;
|
||||
case LyricsSearchProvider.AmllTtmlDb:
|
||||
lyricsSearchResult = await SearchAmllTtmlDbAsync(songInfo);
|
||||
break;
|
||||
case LyricsSearchProvider.AppleMusic:
|
||||
lyricsSearchResult = await SearchAppleMusicAsync(songInfo);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case LyricsSearchProvider.LocalLrcFile:
|
||||
case LyricsSearchProvider.LocalEslrcFile:
|
||||
case LyricsSearchProvider.LocalTtmlFile:
|
||||
lyricsSearchResult = await SearchFile(songInfo, provider.GetLyricsFormat());
|
||||
break;
|
||||
case LyricsSearchProvider.AppleMusic:
|
||||
lyricsSearchResult = await SearchAppleMusicAsync(songInfo);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (token.IsCancellationRequested)
|
||||
{
|
||||
lyricsSearchResult.MatchPercentage = MetadataComparer.CalculateScore(songInfo, lyricsSearchResult);
|
||||
return lyricsSearchResult;
|
||||
}
|
||||
|
||||
@@ -267,14 +266,9 @@ namespace BetterLyrics.WinUI3.Services.LyricsSearchService
|
||||
{
|
||||
}
|
||||
|
||||
lyricsSearchResult.MatchPercentage = MetadataComparer.CalculateScore(songInfo, lyricsSearchResult);
|
||||
|
||||
if (lyricsSearchResult.IsFound)
|
||||
if (provider.IsRemote())
|
||||
{
|
||||
if (provider.IsRemote())
|
||||
{
|
||||
FileHelper.WriteLyricsCache(songInfo, lyricsSearchResult);
|
||||
}
|
||||
FileHelper.WriteLyricsCache(songInfo, lyricsSearchResult);
|
||||
}
|
||||
|
||||
return lyricsSearchResult;
|
||||
@@ -282,6 +276,9 @@ namespace BetterLyrics.WinUI3.Services.LyricsSearchService
|
||||
|
||||
private async Task<LyricsSearchResult> SearchFile(SongInfo songInfo, LyricsFormat format)
|
||||
{
|
||||
int maxScore = 0;
|
||||
string? bestFile = null;
|
||||
|
||||
var lyricsSearchResult = new LyricsSearchResult();
|
||||
|
||||
if (format.ToLyricsSearchProvider() is LyricsSearchProvider lyricsSearchProvider)
|
||||
@@ -297,18 +294,11 @@ namespace BetterLyrics.WinUI3.Services.LyricsSearchService
|
||||
{
|
||||
foreach (var file in DirectoryHelper.GetAllFiles(folder.Path, $"*{format.ToFileExtension()}"))
|
||||
{
|
||||
var fileName = Path.GetFileNameWithoutExtension(file);
|
||||
if (StringHelper.IsSwitchableNormalizedMatch(fileName, songInfo.Title, songInfo.DisplayArtists) || songInfo.LinkedFileName == fileName)
|
||||
int score = MetadataComparer.CalculateScore(songInfo, new LyricsSearchResult { Reference = file });
|
||||
if (score > maxScore)
|
||||
{
|
||||
string? raw = await File.ReadAllTextAsync(file, FileHelper.GetEncoding(file));
|
||||
if (raw != null)
|
||||
{
|
||||
lyricsSearchResult.Raw = raw;
|
||||
lyricsSearchResult.CopyFromSongInfo(songInfo);
|
||||
lyricsSearchResult.Reference = file;
|
||||
|
||||
return lyricsSearchResult;
|
||||
}
|
||||
bestFile = file;
|
||||
maxScore = score;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -317,11 +307,28 @@ namespace BetterLyrics.WinUI3.Services.LyricsSearchService
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (bestFile != null)
|
||||
{
|
||||
lyricsSearchResult.Reference = bestFile;
|
||||
lyricsSearchResult.MatchPercentage = maxScore;
|
||||
|
||||
string? raw = await File.ReadAllTextAsync(bestFile, FileHelper.GetEncoding(bestFile));
|
||||
if (raw != null)
|
||||
{
|
||||
lyricsSearchResult.Raw = raw;
|
||||
}
|
||||
}
|
||||
|
||||
return lyricsSearchResult;
|
||||
}
|
||||
|
||||
private LyricsSearchResult SearchEmbedded(SongInfo songInfo)
|
||||
{
|
||||
int bestScore = 0;
|
||||
string? bestFile = null;
|
||||
string? bestRaw = null;
|
||||
|
||||
var lyricsSearchResult = new LyricsSearchResult
|
||||
{
|
||||
Provider = LyricsSearchProvider.LocalMusicFile,
|
||||
@@ -336,24 +343,45 @@ namespace BetterLyrics.WinUI3.Services.LyricsSearchService
|
||||
if (FileHelper.MusicExtensions.Contains(Path.GetExtension(file)))
|
||||
{
|
||||
var track = new Track(file);
|
||||
if ((songInfo.Album != "" && track.Title == songInfo.Title && track.Artist == songInfo.DisplayArtists && track.Album == songInfo.Album)
|
||||
|| (songInfo.Album == "" && track.Title == songInfo.Title && track.Artist == songInfo.DisplayArtists)
|
||||
|| (songInfo.Album == "" && StringHelper.IsSwitchableNormalizedMatch(Path.GetFileNameWithoutExtension(file), songInfo.Title, songInfo.DisplayArtists)))
|
||||
{
|
||||
var plain = track.GetRawLyrics();
|
||||
if (!plain.IsNullOrEmpty())
|
||||
{
|
||||
lyricsSearchResult.Raw = plain;
|
||||
lyricsSearchResult.CopyFromSongInfo(songInfo);
|
||||
lyricsSearchResult.Reference = file;
|
||||
var raw = track.GetRawLyrics();
|
||||
|
||||
return lyricsSearchResult;
|
||||
if (!string.IsNullOrEmpty(raw))
|
||||
{
|
||||
int score = MetadataComparer.CalculateScore(songInfo, new LyricsSearchResult
|
||||
{
|
||||
Title = track.Title,
|
||||
Artists = track.Artist.Split(ATL.Settings.DisplayValueSeparator),
|
||||
Album = track.Album,
|
||||
Duration = track.Duration,
|
||||
Reference = file,
|
||||
});
|
||||
|
||||
if (score > bestScore)
|
||||
{
|
||||
bestScore = score;
|
||||
bestFile = file;
|
||||
bestRaw = raw;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (bestFile != null)
|
||||
{
|
||||
var track = new Track(bestFile);
|
||||
|
||||
lyricsSearchResult.Title = track.Title;
|
||||
lyricsSearchResult.Artists = track.Artist.Split(ATL.Settings.DisplayValueSeparator);
|
||||
lyricsSearchResult.Album = track.Album;
|
||||
lyricsSearchResult.Duration = track.Duration;
|
||||
|
||||
lyricsSearchResult.Raw = bestRaw;
|
||||
lyricsSearchResult.Reference = bestFile;
|
||||
lyricsSearchResult.MatchPercentage = bestScore;
|
||||
}
|
||||
|
||||
return lyricsSearchResult;
|
||||
}
|
||||
|
||||
@@ -425,6 +453,8 @@ namespace BetterLyrics.WinUI3.Services.LyricsSearchService
|
||||
catch { }
|
||||
}
|
||||
|
||||
lyricsSearchResult.MatchPercentage = MetadataComparer.CalculateScore(songInfo, lyricsSearchResult);
|
||||
|
||||
if (string.IsNullOrWhiteSpace(rawLyricFile))
|
||||
{
|
||||
return lyricsSearchResult;
|
||||
@@ -503,6 +533,8 @@ namespace BetterLyrics.WinUI3.Services.LyricsSearchService
|
||||
|
||||
lyricsSearchResult.Reference = url;
|
||||
|
||||
lyricsSearchResult.MatchPercentage = MetadataComparer.CalculateScore(songInfo, lyricsSearchResult);
|
||||
|
||||
return lyricsSearchResult;
|
||||
}
|
||||
|
||||
@@ -560,6 +592,7 @@ namespace BetterLyrics.WinUI3.Services.LyricsSearchService
|
||||
|
||||
lyricsSearchResult.Raw = response?.Lrc?.Lyric;
|
||||
lyricsSearchResult.Translation = response?.Tlyric?.Lyric;
|
||||
lyricsSearchResult.Transliteration = response?.Romalrc.Lyric;
|
||||
lyricsSearchResult.Reference = $"https://music.163.com/song?id={neteaseResult.Id}";
|
||||
}
|
||||
else if (result is KugouSearchResult kugouResult)
|
||||
@@ -602,26 +635,23 @@ namespace BetterLyrics.WinUI3.Services.LyricsSearchService
|
||||
lyricsSearchResult.Album = result?.Album;
|
||||
lyricsSearchResult.Duration = result?.DurationMs / 1000;
|
||||
|
||||
lyricsSearchResult.MatchPercentage = MetadataComparer.CalculateScore(songInfo, lyricsSearchResult);
|
||||
|
||||
return lyricsSearchResult;
|
||||
}
|
||||
|
||||
private async Task<LyricsSearchResult> SearchAppleMusicAsync(SongInfo songInfo)
|
||||
{
|
||||
var lyricsSearchResult = new LyricsSearchResult
|
||||
LyricsSearchResult lyricsSearchResult = new()
|
||||
{
|
||||
Provider = LyricsSearchProvider.AppleMusic,
|
||||
Provider = LyricsSearchProvider.AppleMusic
|
||||
};
|
||||
|
||||
_logger.LogInformation("SearchAppleMusicAsync");
|
||||
|
||||
if (await _appleMusic.InitAsync())
|
||||
{
|
||||
string id = await _appleMusic.SearchSongInfoAsync(songInfo.DisplayArtists, songInfo.Title);
|
||||
string? raw = await _appleMusic.GetLyricsAsync(id);
|
||||
_logger.LogInformation("SearchAppleMusicAsync");
|
||||
lyricsSearchResult.Raw = raw;
|
||||
lyricsSearchResult.Title = songInfo.Title;
|
||||
lyricsSearchResult.Artists = songInfo.Artists;
|
||||
lyricsSearchResult.Album = "";
|
||||
lyricsSearchResult.Reference = $"https://music.apple.com/song/{id}";
|
||||
lyricsSearchResult = await _appleMusic.SearchSongInfoAsync(songInfo);
|
||||
}
|
||||
|
||||
return lyricsSearchResult;
|
||||
|
||||
@@ -3,10 +3,12 @@ using BetterLyrics.WinUI3.Events;
|
||||
using BetterLyrics.WinUI3.Extensions;
|
||||
using BetterLyrics.WinUI3.Helper;
|
||||
using BetterLyrics.WinUI3.Models;
|
||||
using BetterLyrics.WinUI3.Parsers.LyricsParser;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using Lyricify.Lyrics.Helpers.General;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.UI.Dispatching;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
@@ -103,7 +105,7 @@ namespace BetterLyrics.WinUI3.Services.MediaSessionsService
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
DevWinUI.Growl.Error(_resourceService.GetLocalizedString("LibreTranslateFailed")!);
|
||||
ToastHelper.ShowToast("LibreTranslateFailed", null, InfoBarSeverity.Error);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -126,7 +128,7 @@ namespace BetterLyrics.WinUI3.Services.MediaSessionsService
|
||||
}
|
||||
else if (originalLangCode == "ja" && _settingsService.AppSettings.TranslationSettings.IsJapaneseRomanizationEnabled)
|
||||
{
|
||||
targetPhoneticCode = PhoneticHelper.RomajiCode;
|
||||
targetPhoneticCode = PhoneticHelper.RomanCode;
|
||||
}
|
||||
|
||||
if (targetPhoneticCode == "")
|
||||
|
||||
@@ -23,17 +23,15 @@ using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using CommunityToolkit.Mvvm.Messaging;
|
||||
using CommunityToolkit.Mvvm.Messaging.Messages;
|
||||
using CommunityToolkit.WinUI;
|
||||
using DevWinUI;
|
||||
using EvtSource;
|
||||
using Lyricify.Lyrics.Providers;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.UI.Dispatching;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices.WindowsRuntime;
|
||||
using System.Text.Json;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Vanara.Windows.Shell;
|
||||
using Windows.Media.Control;
|
||||
@@ -185,6 +183,9 @@ namespace BetterLyrics.WinUI3.Services.MediaSessionsService
|
||||
case nameof(MediaSourceProviderInfo.LyricsSearchType):
|
||||
UpdateLyrics();
|
||||
break;
|
||||
case nameof(MediaSourceProviderInfo.MatchingThreshold):
|
||||
UpdateLyrics();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -320,14 +321,16 @@ namespace BetterLyrics.WinUI3.Services.MediaSessionsService
|
||||
currentMediaSourceProviderInfo?.PositionOffset = 0;
|
||||
}
|
||||
|
||||
string fixedArtist = mediaProperties?.Artist ?? "N/A";
|
||||
string fixedAlbum = mediaProperties?.AlbumTitle ?? "N/A";
|
||||
string? fixedArtist = mediaProperties?.Artist;
|
||||
string? fixedAlbum = mediaProperties?.AlbumTitle;
|
||||
string? songId = null;
|
||||
|
||||
if (PlayerIDHelper.IsAppleMusic(sessionId))
|
||||
{
|
||||
fixedArtist = mediaProperties?.Artist.Split(" — ").FirstOrDefault() ?? (mediaProperties?.Artist ?? "N/A");
|
||||
fixedAlbum = mediaProperties?.Artist.Split(" — ").LastOrDefault() ?? (mediaProperties?.AlbumTitle ?? "N/A");
|
||||
fixedArtist = mediaProperties?.Artist.Split(" — ").FirstOrDefault();
|
||||
fixedAlbum = mediaProperties?.Artist.Split(" — ").LastOrDefault();
|
||||
fixedAlbum = fixedAlbum?.Replace(" - Single", "");
|
||||
fixedAlbum = fixedAlbum?.Replace(" - EP", "");
|
||||
}
|
||||
else if (PlayerIDHelper.IsNeteaseFamily(sessionId))
|
||||
{
|
||||
@@ -342,18 +345,15 @@ namespace BetterLyrics.WinUI3.Services.MediaSessionsService
|
||||
|
||||
CurrentSongInfo = new SongInfo
|
||||
{
|
||||
Title = mediaProperties?.Title ?? "N/A",
|
||||
Artists = fixedArtist.SplitByCommonSplitter(),
|
||||
Album = fixedAlbum,
|
||||
Title = mediaProperties?.Title ?? "",
|
||||
Artists = fixedArtist?.SplitByCommonSplitter() ?? [],
|
||||
Album = fixedAlbum ?? "",
|
||||
DurationMs = mediaSession?.ControlSession?.GetTimelineProperties().EndTime.TotalMilliseconds ?? 0,
|
||||
PlayerId = sessionId,
|
||||
SongId = songId,
|
||||
LinkedFileName = linkedFileName
|
||||
};
|
||||
|
||||
_logger.LogInformation("Media properties changed: Title: {Title}, Artist: {Artist}, Album: {Album}",
|
||||
mediaProperties?.Title, mediaProperties?.Artist, mediaProperties?.AlbumTitle);
|
||||
|
||||
if (PlayerIDHelper.IsLXMusic(sessionId))
|
||||
{
|
||||
StartSSE();
|
||||
@@ -526,7 +526,7 @@ namespace BetterLyrics.WinUI3.Services.MediaSessionsService
|
||||
_logger.LogError("Failed to start SSE connection for LX Music.");
|
||||
_dispatcherQueue.TryEnqueue(DispatcherQueuePriority.Low, () =>
|
||||
{
|
||||
DevWinUI.Growl.Error(_resourceService.GetLocalizedString("FailToStartLXMusicServer"));
|
||||
ToastHelper.ShowToast("FailToStartLXMusicServer", null, InfoBarSeverity.Error);
|
||||
});
|
||||
StopSSE();
|
||||
}
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
using ComputeSharp;
|
||||
using ComputeSharp.D2D1;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace BetterLyrics.WinUI3.Shaders
|
||||
{
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
using ComputeSharp;
|
||||
using ComputeSharp.D2D1;
|
||||
using static Vanara.PInvoke.Ole32.PROPERTYKEY.System;
|
||||
|
||||
namespace BetterLyrics.WinUI3.Shaders
|
||||
{
|
||||
|
||||
@@ -144,6 +144,9 @@
|
||||
<data name="Cancel" xml:space="preserve">
|
||||
<value>Cancel</value>
|
||||
</data>
|
||||
<data name="Copy.Content" xml:space="preserve">
|
||||
<value>Copy</value>
|
||||
</data>
|
||||
<data name="CreatePlaylistSuccessfully" xml:space="preserve">
|
||||
<value>Playlist was created successfully</value>
|
||||
</data>
|
||||
@@ -180,7 +183,7 @@
|
||||
<data name="HostWindowMusicGalleryButtonToolTip.Content" xml:space="preserve">
|
||||
<value>Music gallery</value>
|
||||
</data>
|
||||
<data name="HostWindowSettingsButtonToolTip.Text" xml:space="preserve">
|
||||
<data name="HostWindowSettingsButtonToolTip.Content" xml:space="preserve">
|
||||
<value>Settings</value>
|
||||
</data>
|
||||
<data name="ImportPlaylistSuccessfully" xml:space="preserve">
|
||||
@@ -228,7 +231,13 @@
|
||||
<data name="LyricsNotFound" xml:space="preserve">
|
||||
<value>Lyrics not found</value>
|
||||
</data>
|
||||
<data name="LyricsPageLyricsProviderPrefix.Text" xml:space="preserve">
|
||||
<data name="LyricsPageCachePath.Header" xml:space="preserve">
|
||||
<value>Cache path</value>
|
||||
</data>
|
||||
<data name="LyricsPageCachePath.Value" xml:space="preserve">
|
||||
<value>Cache path</value>
|
||||
</data>
|
||||
<data name="LyricsPageLyricsProviderPrefix.Header" xml:space="preserve">
|
||||
<value>Lyrics provider</value>
|
||||
</data>
|
||||
<data name="LyricsPageLyricsSearchButtonToolTip.Content" xml:space="preserve">
|
||||
@@ -237,7 +246,7 @@
|
||||
<data name="LyricsPageLyricsSettingsButtonToolTip.Content" xml:space="preserve">
|
||||
<value>Lyrics Window Management Shortcut Settings</value>
|
||||
</data>
|
||||
<data name="LyricsPageMatchPercentage.Text" xml:space="preserve">
|
||||
<data name="LyricsPageMatchPercentage.Header" xml:space="preserve">
|
||||
<value>Match percentage</value>
|
||||
</data>
|
||||
<data name="LyricsPagePlaybackSourceButtonToolTip.Content" xml:space="preserve">
|
||||
@@ -264,16 +273,22 @@
|
||||
<data name="LyricsPageTranslationOnly.Header" xml:space="preserve">
|
||||
<value>Show translation only</value>
|
||||
</data>
|
||||
<data name="LyricsPageTranslationProviderPrefix.Text" xml:space="preserve">
|
||||
<data name="LyricsPageTranslationProviderPrefix.Header" xml:space="preserve">
|
||||
<value>Translation provider</value>
|
||||
</data>
|
||||
<data name="LyricsSearchControlAlbum.Text" xml:space="preserve">
|
||||
<data name="LyricsParseError" xml:space="preserve">
|
||||
<value>Lyrics parsing failed</value>
|
||||
</data>
|
||||
<data name="LyricsSearchControlAlbum.Header" xml:space="preserve">
|
||||
<value>Album</value>
|
||||
</data>
|
||||
<data name="LyricsSearchControlArtist.Text" xml:space="preserve">
|
||||
<data name="LyricsSearchControlArtist.Header" xml:space="preserve">
|
||||
<value>Artist</value>
|
||||
</data>
|
||||
<data name="LyricsSearchControlDurauion.Text" xml:space="preserve">
|
||||
<data name="LyricsSearchControlAutoGenerated.Tag" xml:space="preserve">
|
||||
<value>Auto generated</value>
|
||||
</data>
|
||||
<data name="LyricsSearchControlDurauion.Header" xml:space="preserve">
|
||||
<value>Duration</value>
|
||||
</data>
|
||||
<data name="LyricsSearchControlHelp.Text" xml:space="preserve">
|
||||
@@ -306,7 +321,7 @@
|
||||
<data name="LyricsSearchControlTargetSearchProvider.Header" xml:space="preserve">
|
||||
<value>Target lyrics search provider</value>
|
||||
</data>
|
||||
<data name="LyricsSearchControlTitle.Text" xml:space="preserve">
|
||||
<data name="LyricsSearchControlTitle.Header" xml:space="preserve">
|
||||
<value>Title</value>
|
||||
</data>
|
||||
<data name="LyricsSearchPageTitle" xml:space="preserve">
|
||||
@@ -394,43 +409,43 @@ If you encounter any problems, please go to the Settings page, About tab, and vi
|
||||
<data name="MusicGalleryPageEmptyPlayingQueue.Text" xml:space="preserve">
|
||||
<value>Clear play queue</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageFileAlbum.Text" xml:space="preserve">
|
||||
<data name="MusicGalleryPageFileAlbum.Header" xml:space="preserve">
|
||||
<value>Album</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageFileArtist.Text" xml:space="preserve">
|
||||
<data name="MusicGalleryPageFileArtist.Header" xml:space="preserve">
|
||||
<value>Artist</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageFileInfo.Text" xml:space="preserve">
|
||||
<value>File info</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageFileInfoBitDepth.Text" xml:space="preserve">
|
||||
<data name="MusicGalleryPageFileInfoBitDepth.Header" xml:space="preserve">
|
||||
<value>Bit depth</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageFileInfoBitrate.Text" xml:space="preserve">
|
||||
<data name="MusicGalleryPageFileInfoBitrate.Header" xml:space="preserve">
|
||||
<value>Bitrate</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageFileInfoDuration.Text" xml:space="preserve">
|
||||
<data name="MusicGalleryPageFileInfoDuration.Header" xml:space="preserve">
|
||||
<value>Duration</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageFileInfoEncoder.Text" xml:space="preserve">
|
||||
<data name="MusicGalleryPageFileInfoEncoder.Header" xml:space="preserve">
|
||||
<value>Encoder</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageFileInfoFormat.Text" xml:space="preserve">
|
||||
<data name="MusicGalleryPageFileInfoFormat.Header" xml:space="preserve">
|
||||
<value>Format</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageFileInfoLyrics.Text" xml:space="preserve">
|
||||
<data name="MusicGalleryPageFileInfoLyrics.Header" xml:space="preserve">
|
||||
<value>Lyrics</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageFileInfoPath.Text" xml:space="preserve">
|
||||
<data name="MusicGalleryPageFileInfoPath.Header" xml:space="preserve">
|
||||
<value>Path</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageFileInfoSampleRate.Text" xml:space="preserve">
|
||||
<data name="MusicGalleryPageFileInfoSampleRate.Header" xml:space="preserve">
|
||||
<value>Sample rate</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageFileInfoTitle.Text" xml:space="preserve">
|
||||
<data name="MusicGalleryPageFileInfoTitle.Header" xml:space="preserve">
|
||||
<value>Title</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageFileInfoYear.Text" xml:space="preserve">
|
||||
<data name="MusicGalleryPageFileInfoYear.Header" xml:space="preserve">
|
||||
<value>Year</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageFileNotFound.Text" xml:space="preserve">
|
||||
@@ -565,6 +580,9 @@ If you encounter any problems, please go to the Settings page, About tab, and vi
|
||||
<data name="SettingsPageAdvanced.Text" xml:space="preserve">
|
||||
<value>Advanced options</value>
|
||||
</data>
|
||||
<data name="SettingsPageAlbum.Header" xml:space="preserve">
|
||||
<value>Album</value>
|
||||
</data>
|
||||
<data name="SettingsPageAlbumArt.Text" xml:space="preserve">
|
||||
<value>Album art</value>
|
||||
</data>
|
||||
@@ -610,6 +628,9 @@ If you encounter any problems, please go to the Settings page, About tab, and vi
|
||||
<data name="SettingsPageApply.Content" xml:space="preserve">
|
||||
<value>Apply</value>
|
||||
</data>
|
||||
<data name="SettingsPageArtist.Header" xml:space="preserve">
|
||||
<value>Artist</value>
|
||||
</data>
|
||||
<data name="SettingsPageAutoAdjust.Header" xml:space="preserve">
|
||||
<value>Automatic adjustment</value>
|
||||
</data>
|
||||
@@ -847,6 +868,9 @@ If you encounter any problems, please go to the Settings page, About tab, and vi
|
||||
<data name="SettingsPageHideWindow.Header" xml:space="preserve">
|
||||
<value>Automatically hide/show windows</value>
|
||||
</data>
|
||||
<data name="SettingsPageHoldDragSort.Content" xml:space="preserve">
|
||||
<value>Hold here and drag to sort</value>
|
||||
</data>
|
||||
<data name="SettingsPageImport.Content" xml:space="preserve">
|
||||
<value>Import</value>
|
||||
</data>
|
||||
@@ -1108,6 +1132,15 @@ If you encounter any problems, please go to the Settings page, About tab, and vi
|
||||
<data name="SettingsPageLyricsWindowSwitchHotKey.Header" xml:space="preserve">
|
||||
<value>Lyrics window status switcher shortcut</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsWindowToolTip.Content" xml:space="preserve">
|
||||
<value>Lyrics window</value>
|
||||
</data>
|
||||
<data name="SettingsPageMatchingThreshold.Description" xml:space="preserve">
|
||||
<value>Adjusting this value will affect sequential search and best match search results, but will not affect search results in the manual lyrics search interface</value>
|
||||
</data>
|
||||
<data name="SettingsPageMatchingThreshold.Header" xml:space="preserve">
|
||||
<value>Minimum matching threshold</value>
|
||||
</data>
|
||||
<data name="SettingsPageMediaLib.Content" xml:space="preserve">
|
||||
<value>Media library</value>
|
||||
</data>
|
||||
@@ -1156,6 +1189,9 @@ If you encounter any problems, please go to the Settings page, About tab, and vi
|
||||
<data name="SettingsPageOriginalText.Header" xml:space="preserve">
|
||||
<value>Original text</value>
|
||||
</data>
|
||||
<data name="SettingsPageOverwriteMatchingThreshold.Header" xml:space="preserve">
|
||||
<value>Set the minimum match threshold individually</value>
|
||||
</data>
|
||||
<data name="SettingsPagePaletteGeneratorType.Header" xml:space="preserve">
|
||||
<value>Color picker style</value>
|
||||
</data>
|
||||
@@ -1189,12 +1225,15 @@ If you encounter any problems, please go to the Settings page, About tab, and vi
|
||||
<data name="SettingsPagePlaybackShortcut.Text" xml:space="preserve">
|
||||
<value>Play</value>
|
||||
</data>
|
||||
<data name="SettingsPagePlaybackSource.Text" xml:space="preserve">
|
||||
<data name="SettingsPagePlaybackSource.Header" xml:space="preserve">
|
||||
<value>Playback source</value>
|
||||
</data>
|
||||
<data name="SettingsPagePlaybackSourceID.Text" xml:space="preserve">
|
||||
<data name="SettingsPagePlaybackSourceID.Header" xml:space="preserve">
|
||||
<value>Playback source ID</value>
|
||||
</data>
|
||||
<data name="SettingsPagePlaybackStatus.Header" xml:space="preserve">
|
||||
<value>Current playback source</value>
|
||||
</data>
|
||||
<data name="SettingsPagePlayingMockMusicButton.Content" xml:space="preserve">
|
||||
<value>Play "Cut To The Feeling" on "soundcloud.com"</value>
|
||||
</data>
|
||||
@@ -1219,6 +1258,9 @@ If you encounter any problems, please go to the Settings page, About tab, and vi
|
||||
<data name="SettingsPageRecordedWindowStatus.Text" xml:space="preserve">
|
||||
<value>Recorded window status</value>
|
||||
</data>
|
||||
<data name="SettingsPageReference.Header" xml:space="preserve">
|
||||
<value>Reference link</value>
|
||||
</data>
|
||||
<data name="SettingsPageRemoveInfo.Message" xml:space="preserve">
|
||||
<value>Original files and folders in this path will not be deleted when removing it from this app</value>
|
||||
</data>
|
||||
@@ -1250,7 +1292,7 @@ If you encounter any problems, please go to the Settings page, About tab, and vi
|
||||
<value>Current line duration</value>
|
||||
</data>
|
||||
<data name="SettingsPageScrollEasing.Header" xml:space="preserve">
|
||||
<value>Lyrics scrolling animation type</value>
|
||||
<value>Lyric animation type</value>
|
||||
</data>
|
||||
<data name="SettingsPageScrollTopDelay.Header" xml:space="preserve">
|
||||
<value>First line delay</value>
|
||||
@@ -1258,6 +1300,9 @@ If you encounter any problems, please go to the Settings page, About tab, and vi
|
||||
<data name="SettingsPageScrollTopDuration.Header" xml:space="preserve">
|
||||
<value>The duration of the first line</value>
|
||||
</data>
|
||||
<data name="SettingsPageSearchResultStatus.Header" xml:space="preserve">
|
||||
<value>Current lyrics search result</value>
|
||||
</data>
|
||||
<data name="SettingsPageServerTestButton.Content" xml:space="preserve">
|
||||
<value>Test server</value>
|
||||
</data>
|
||||
@@ -1321,6 +1366,12 @@ If you encounter any problems, please go to the Settings page, About tab, and vi
|
||||
<data name="SettingsPageSongInfoRight.Content" xml:space="preserve">
|
||||
<value>Right</value>
|
||||
</data>
|
||||
<data name="SettingsPageSongStatus.Header" xml:space="preserve">
|
||||
<value>Current song</value>
|
||||
</data>
|
||||
<data name="SettingsPageSongTitle.Header" xml:space="preserve">
|
||||
<value>Title</value>
|
||||
</data>
|
||||
<data name="SettingsPageSpectrumLayer.Header" xml:space="preserve">
|
||||
<value>[Experimental] Spectrum Layer</value>
|
||||
</data>
|
||||
@@ -1456,4 +1507,7 @@ If you encounter any problems, please go to the Settings page, About tab, and vi
|
||||
<data name="TryRunMultipleInstance" xml:space="preserve">
|
||||
<value>BetterLyrics is already running</value>
|
||||
</data>
|
||||
<data name="UserGuide.Content" xml:space="preserve">
|
||||
<value>User Guide</value>
|
||||
</data>
|
||||
</root>
|
||||
@@ -144,6 +144,9 @@
|
||||
<data name="Cancel" xml:space="preserve">
|
||||
<value>キャンセル</value>
|
||||
</data>
|
||||
<data name="Copy.Content" xml:space="preserve">
|
||||
<value>コピー</value>
|
||||
</data>
|
||||
<data name="CreatePlaylistSuccessfully" xml:space="preserve">
|
||||
<value>正常に作成されました</value>
|
||||
</data>
|
||||
@@ -180,7 +183,7 @@
|
||||
<data name="HostWindowMusicGalleryButtonToolTip.Content" xml:space="preserve">
|
||||
<value>ミュージックギャラリー</value>
|
||||
</data>
|
||||
<data name="HostWindowSettingsButtonToolTip.Text" xml:space="preserve">
|
||||
<data name="HostWindowSettingsButtonToolTip.Content" xml:space="preserve">
|
||||
<value>設定</value>
|
||||
</data>
|
||||
<data name="ImportPlaylistSuccessfully" xml:space="preserve">
|
||||
@@ -228,7 +231,13 @@
|
||||
<data name="LyricsNotFound" xml:space="preserve">
|
||||
<value>歌詞が見つかりません</value>
|
||||
</data>
|
||||
<data name="LyricsPageLyricsProviderPrefix.Text" xml:space="preserve">
|
||||
<data name="LyricsPageCachePath.Header" xml:space="preserve">
|
||||
<value>キャッシュパス</value>
|
||||
</data>
|
||||
<data name="LyricsPageCachePath.Value" xml:space="preserve">
|
||||
<value>キャッシュパス</value>
|
||||
</data>
|
||||
<data name="LyricsPageLyricsProviderPrefix.Header" xml:space="preserve">
|
||||
<value>歌詞プロバイダー</value>
|
||||
</data>
|
||||
<data name="LyricsPageLyricsSearchButtonToolTip.Content" xml:space="preserve">
|
||||
@@ -237,7 +246,7 @@
|
||||
<data name="LyricsPageLyricsSettingsButtonToolTip.Content" xml:space="preserve">
|
||||
<value>歌詞ウィンドウ管理ショートカット設定</value>
|
||||
</data>
|
||||
<data name="LyricsPageMatchPercentage.Text" xml:space="preserve">
|
||||
<data name="LyricsPageMatchPercentage.Header" xml:space="preserve">
|
||||
<value>マッチ率</value>
|
||||
</data>
|
||||
<data name="LyricsPagePlaybackSourceButtonToolTip.Content" xml:space="preserve">
|
||||
@@ -264,16 +273,22 @@
|
||||
<data name="LyricsPageTranslationOnly.Header" xml:space="preserve">
|
||||
<value>翻訳のみを表示します</value>
|
||||
</data>
|
||||
<data name="LyricsPageTranslationProviderPrefix.Text" xml:space="preserve">
|
||||
<data name="LyricsPageTranslationProviderPrefix.Header" xml:space="preserve">
|
||||
<value>翻訳プロバイダー</value>
|
||||
</data>
|
||||
<data name="LyricsSearchControlAlbum.Text" xml:space="preserve">
|
||||
<data name="LyricsParseError" xml:space="preserve">
|
||||
<value>歌詞の解析に失敗しました</value>
|
||||
</data>
|
||||
<data name="LyricsSearchControlAlbum.Header" xml:space="preserve">
|
||||
<value>アルバム</value>
|
||||
</data>
|
||||
<data name="LyricsSearchControlArtist.Text" xml:space="preserve">
|
||||
<data name="LyricsSearchControlArtist.Header" xml:space="preserve">
|
||||
<value>アーティスト</value>
|
||||
</data>
|
||||
<data name="LyricsSearchControlDurauion.Text" xml:space="preserve">
|
||||
<data name="LyricsSearchControlAutoGenerated.Tag" xml:space="preserve">
|
||||
<value>自動生成されました</value>
|
||||
</data>
|
||||
<data name="LyricsSearchControlDurauion.Header" xml:space="preserve">
|
||||
<value>間隔</value>
|
||||
</data>
|
||||
<data name="LyricsSearchControlHelp.Text" xml:space="preserve">
|
||||
@@ -306,7 +321,7 @@
|
||||
<data name="LyricsSearchControlTargetSearchProvider.Header" xml:space="preserve">
|
||||
<value>ターゲット歌詞検索プロバイダー</value>
|
||||
</data>
|
||||
<data name="LyricsSearchControlTitle.Text" xml:space="preserve">
|
||||
<data name="LyricsSearchControlTitle.Header" xml:space="preserve">
|
||||
<value>タイトル</value>
|
||||
</data>
|
||||
<data name="LyricsSearchPageTitle" xml:space="preserve">
|
||||
@@ -394,43 +409,43 @@
|
||||
<data name="MusicGalleryPageEmptyPlayingQueue.Text" xml:space="preserve">
|
||||
<value>クリアプレイキュー</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageFileAlbum.Text" xml:space="preserve">
|
||||
<data name="MusicGalleryPageFileAlbum.Header" xml:space="preserve">
|
||||
<value>アルバム</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageFileArtist.Text" xml:space="preserve">
|
||||
<data name="MusicGalleryPageFileArtist.Header" xml:space="preserve">
|
||||
<value>アーティスト</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageFileInfo.Text" xml:space="preserve">
|
||||
<value>ファイル情報</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageFileInfoBitDepth.Text" xml:space="preserve">
|
||||
<data name="MusicGalleryPageFileInfoBitDepth.Header" xml:space="preserve">
|
||||
<value>ビットの深さ</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageFileInfoBitrate.Text" xml:space="preserve">
|
||||
<data name="MusicGalleryPageFileInfoBitrate.Header" xml:space="preserve">
|
||||
<value>ビットレート</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageFileInfoDuration.Text" xml:space="preserve">
|
||||
<data name="MusicGalleryPageFileInfoDuration.Header" xml:space="preserve">
|
||||
<value>間隔</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageFileInfoEncoder.Text" xml:space="preserve">
|
||||
<data name="MusicGalleryPageFileInfoEncoder.Header" xml:space="preserve">
|
||||
<value>エンコーダー</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageFileInfoFormat.Text" xml:space="preserve">
|
||||
<data name="MusicGalleryPageFileInfoFormat.Header" xml:space="preserve">
|
||||
<value>形式</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageFileInfoLyrics.Text" xml:space="preserve">
|
||||
<data name="MusicGalleryPageFileInfoLyrics.Header" xml:space="preserve">
|
||||
<value>歌詞</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageFileInfoPath.Text" xml:space="preserve">
|
||||
<data name="MusicGalleryPageFileInfoPath.Header" xml:space="preserve">
|
||||
<value>パス</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageFileInfoSampleRate.Text" xml:space="preserve">
|
||||
<data name="MusicGalleryPageFileInfoSampleRate.Header" xml:space="preserve">
|
||||
<value>サンプルレート</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageFileInfoTitle.Text" xml:space="preserve">
|
||||
<data name="MusicGalleryPageFileInfoTitle.Header" xml:space="preserve">
|
||||
<value>タイトル</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageFileInfoYear.Text" xml:space="preserve">
|
||||
<data name="MusicGalleryPageFileInfoYear.Header" xml:space="preserve">
|
||||
<value>年</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageFileNotFound.Text" xml:space="preserve">
|
||||
@@ -565,6 +580,9 @@
|
||||
<data name="SettingsPageAdvanced.Text" xml:space="preserve">
|
||||
<value>高度なオプション</value>
|
||||
</data>
|
||||
<data name="SettingsPageAlbum.Header" xml:space="preserve">
|
||||
<value>アルバム</value>
|
||||
</data>
|
||||
<data name="SettingsPageAlbumArt.Text" xml:space="preserve">
|
||||
<value>アルバムアート</value>
|
||||
</data>
|
||||
@@ -610,6 +628,9 @@
|
||||
<data name="SettingsPageApply.Content" xml:space="preserve">
|
||||
<value>適用する</value>
|
||||
</data>
|
||||
<data name="SettingsPageArtist.Header" xml:space="preserve">
|
||||
<value>アーティスト</value>
|
||||
</data>
|
||||
<data name="SettingsPageAutoAdjust.Header" xml:space="preserve">
|
||||
<value>自動調整</value>
|
||||
</data>
|
||||
@@ -847,6 +868,9 @@
|
||||
<data name="SettingsPageHideWindow.Header" xml:space="preserve">
|
||||
<value>Windowsを自動的に非表示/表示します</value>
|
||||
</data>
|
||||
<data name="SettingsPageHoldDragSort.Content" xml:space="preserve">
|
||||
<value>ここを長押ししてドラッグして並べ替えましょう</value>
|
||||
</data>
|
||||
<data name="SettingsPageImport.Content" xml:space="preserve">
|
||||
<value>インポート</value>
|
||||
</data>
|
||||
@@ -1108,6 +1132,15 @@
|
||||
<data name="SettingsPageLyricsWindowSwitchHotKey.Header" xml:space="preserve">
|
||||
<value>歌詞ウィンドウステータススイッチャーショートカット</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsWindowToolTip.Content" xml:space="preserve">
|
||||
<value>歌詞ウィンドウ</value>
|
||||
</data>
|
||||
<data name="SettingsPageMatchingThreshold.Description" xml:space="preserve">
|
||||
<value>この値を調整すると、シーケンシャル検索と最適な検索結果に影響しますが、手動歌詞検索インターフェースの検索結果には影響しません</value>
|
||||
</data>
|
||||
<data name="SettingsPageMatchingThreshold.Header" xml:space="preserve">
|
||||
<value>最小マッチングしきい値</value>
|
||||
</data>
|
||||
<data name="SettingsPageMediaLib.Content" xml:space="preserve">
|
||||
<value>メディアライブラリ</value>
|
||||
</data>
|
||||
@@ -1156,6 +1189,9 @@
|
||||
<data name="SettingsPageOriginalText.Header" xml:space="preserve">
|
||||
<value>原文</value>
|
||||
</data>
|
||||
<data name="SettingsPageOverwriteMatchingThreshold.Header" xml:space="preserve">
|
||||
<value>最低マッチしきい値を個別に設定してください</value>
|
||||
</data>
|
||||
<data name="SettingsPagePaletteGeneratorType.Header" xml:space="preserve">
|
||||
<value>カラーピックスタイル</value>
|
||||
</data>
|
||||
@@ -1189,12 +1225,15 @@
|
||||
<data name="SettingsPagePlaybackShortcut.Text" xml:space="preserve">
|
||||
<value>遊ぶ</value>
|
||||
</data>
|
||||
<data name="SettingsPagePlaybackSource.Text" xml:space="preserve">
|
||||
<data name="SettingsPagePlaybackSource.Header" xml:space="preserve">
|
||||
<value>再生ソース</value>
|
||||
</data>
|
||||
<data name="SettingsPagePlaybackSourceID.Text" xml:space="preserve">
|
||||
<data name="SettingsPagePlaybackSourceID.Header" xml:space="preserve">
|
||||
<value>再生ソースID</value>
|
||||
</data>
|
||||
<data name="SettingsPagePlaybackStatus.Header" xml:space="preserve">
|
||||
<value>現在の再生ソース</value>
|
||||
</data>
|
||||
<data name="SettingsPagePlayingMockMusicButton.Content" xml:space="preserve">
|
||||
<value>「SoundCloud.com」で「Cut to the Feeling」を再生する</value>
|
||||
</data>
|
||||
@@ -1219,6 +1258,9 @@
|
||||
<data name="SettingsPageRecordedWindowStatus.Text" xml:space="preserve">
|
||||
<value>記録されたウィンドウステータス</value>
|
||||
</data>
|
||||
<data name="SettingsPageReference.Header" xml:space="preserve">
|
||||
<value>参照リンク</value>
|
||||
</data>
|
||||
<data name="SettingsPageRemoveInfo.Message" xml:space="preserve">
|
||||
<value>このパスの元のファイルとフォルダーは、このアプリから削除するときに削除されません</value>
|
||||
</data>
|
||||
@@ -1250,7 +1292,7 @@
|
||||
<value>現在のライン期間</value>
|
||||
</data>
|
||||
<data name="SettingsPageScrollEasing.Header" xml:space="preserve">
|
||||
<value>歌詞スクロールアニメーションタイプ</value>
|
||||
<value>リリックアニメーションタイプ</value>
|
||||
</data>
|
||||
<data name="SettingsPageScrollTopDelay.Header" xml:space="preserve">
|
||||
<value>最初の行の遅延</value>
|
||||
@@ -1258,6 +1300,9 @@
|
||||
<data name="SettingsPageScrollTopDuration.Header" xml:space="preserve">
|
||||
<value>最初の行の期間</value>
|
||||
</data>
|
||||
<data name="SettingsPageSearchResultStatus.Header" xml:space="preserve">
|
||||
<value>現在の歌詞検索結果</value>
|
||||
</data>
|
||||
<data name="SettingsPageServerTestButton.Content" xml:space="preserve">
|
||||
<value>テストサーバー</value>
|
||||
</data>
|
||||
@@ -1321,6 +1366,12 @@
|
||||
<data name="SettingsPageSongInfoRight.Content" xml:space="preserve">
|
||||
<value>右</value>
|
||||
</data>
|
||||
<data name="SettingsPageSongStatus.Header" xml:space="preserve">
|
||||
<value>現在の曲です</value>
|
||||
</data>
|
||||
<data name="SettingsPageSongTitle.Header" xml:space="preserve">
|
||||
<value>タイトル</value>
|
||||
</data>
|
||||
<data name="SettingsPageSpectrumLayer.Header" xml:space="preserve">
|
||||
<value>[実験的] スペクトラムレイヤー</value>
|
||||
</data>
|
||||
@@ -1456,4 +1507,7 @@
|
||||
<data name="TryRunMultipleInstance" xml:space="preserve">
|
||||
<value>BetterLyrics はすでに実行されています</value>
|
||||
</data>
|
||||
<data name="UserGuide.Content" xml:space="preserve">
|
||||
<value>利用案内</value>
|
||||
</data>
|
||||
</root>
|
||||
@@ -144,6 +144,9 @@
|
||||
<data name="Cancel" xml:space="preserve">
|
||||
<value>취소</value>
|
||||
</data>
|
||||
<data name="Copy.Content" xml:space="preserve">
|
||||
<value>접수</value>
|
||||
</data>
|
||||
<data name="CreatePlaylistSuccessfully" xml:space="preserve">
|
||||
<value>재생 목록이 성공적으로 생성되었습니다</value>
|
||||
</data>
|
||||
@@ -180,7 +183,7 @@
|
||||
<data name="HostWindowMusicGalleryButtonToolTip.Content" xml:space="preserve">
|
||||
<value>음악 갤러리</value>
|
||||
</data>
|
||||
<data name="HostWindowSettingsButtonToolTip.Text" xml:space="preserve">
|
||||
<data name="HostWindowSettingsButtonToolTip.Content" xml:space="preserve">
|
||||
<value>설정</value>
|
||||
</data>
|
||||
<data name="ImportPlaylistSuccessfully" xml:space="preserve">
|
||||
@@ -228,7 +231,13 @@
|
||||
<data name="LyricsNotFound" xml:space="preserve">
|
||||
<value>가사를 찾을 수 없습니다</value>
|
||||
</data>
|
||||
<data name="LyricsPageLyricsProviderPrefix.Text" xml:space="preserve">
|
||||
<data name="LyricsPageCachePath.Header" xml:space="preserve">
|
||||
<value>캐시</value>
|
||||
</data>
|
||||
<data name="LyricsPageCachePath.Value" xml:space="preserve">
|
||||
<value>캐시</value>
|
||||
</data>
|
||||
<data name="LyricsPageLyricsProviderPrefix.Header" xml:space="preserve">
|
||||
<value>가사 제공자</value>
|
||||
</data>
|
||||
<data name="LyricsPageLyricsSearchButtonToolTip.Content" xml:space="preserve">
|
||||
@@ -237,7 +246,7 @@
|
||||
<data name="LyricsPageLyricsSettingsButtonToolTip.Content" xml:space="preserve">
|
||||
<value>가사 창 관리 바로 가기 설정</value>
|
||||
</data>
|
||||
<data name="LyricsPageMatchPercentage.Text" xml:space="preserve">
|
||||
<data name="LyricsPageMatchPercentage.Header" xml:space="preserve">
|
||||
<value>매치 퍼센트</value>
|
||||
</data>
|
||||
<data name="LyricsPagePlaybackSourceButtonToolTip.Content" xml:space="preserve">
|
||||
@@ -264,16 +273,22 @@
|
||||
<data name="LyricsPageTranslationOnly.Header" xml:space="preserve">
|
||||
<value>번역 만 표시하십시오</value>
|
||||
</data>
|
||||
<data name="LyricsPageTranslationProviderPrefix.Text" xml:space="preserve">
|
||||
<data name="LyricsPageTranslationProviderPrefix.Header" xml:space="preserve">
|
||||
<value>번역 제공자</value>
|
||||
</data>
|
||||
<data name="LyricsSearchControlAlbum.Text" xml:space="preserve">
|
||||
<data name="LyricsParseError" xml:space="preserve">
|
||||
<value>가사 구문 분석에 실패했습니다</value>
|
||||
</data>
|
||||
<data name="LyricsSearchControlAlbum.Header" xml:space="preserve">
|
||||
<value>앨범</value>
|
||||
</data>
|
||||
<data name="LyricsSearchControlArtist.Text" xml:space="preserve">
|
||||
<data name="LyricsSearchControlArtist.Header" xml:space="preserve">
|
||||
<value>아티스트</value>
|
||||
</data>
|
||||
<data name="LyricsSearchControlDurauion.Text" xml:space="preserve">
|
||||
<data name="LyricsSearchControlAutoGenerated.Tag" xml:space="preserve">
|
||||
<value>생성됨</value>
|
||||
</data>
|
||||
<data name="LyricsSearchControlDurauion.Header" xml:space="preserve">
|
||||
<value>지속</value>
|
||||
</data>
|
||||
<data name="LyricsSearchControlHelp.Text" xml:space="preserve">
|
||||
@@ -306,7 +321,7 @@
|
||||
<data name="LyricsSearchControlTargetSearchProvider.Header" xml:space="preserve">
|
||||
<value>가사 검색 공급자를 타겟팅하십시오</value>
|
||||
</data>
|
||||
<data name="LyricsSearchControlTitle.Text" xml:space="preserve">
|
||||
<data name="LyricsSearchControlTitle.Header" xml:space="preserve">
|
||||
<value>제목</value>
|
||||
</data>
|
||||
<data name="LyricsSearchPageTitle" xml:space="preserve">
|
||||
@@ -394,43 +409,43 @@
|
||||
<data name="MusicGalleryPageEmptyPlayingQueue.Text" xml:space="preserve">
|
||||
<value>플레이 대기열을 클리어합니다</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageFileAlbum.Text" xml:space="preserve">
|
||||
<data name="MusicGalleryPageFileAlbum.Header" xml:space="preserve">
|
||||
<value>앨범</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageFileArtist.Text" xml:space="preserve">
|
||||
<data name="MusicGalleryPageFileArtist.Header" xml:space="preserve">
|
||||
<value>아티스트</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageFileInfo.Text" xml:space="preserve">
|
||||
<value>파일 정보</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageFileInfoBitDepth.Text" xml:space="preserve">
|
||||
<data name="MusicGalleryPageFileInfoBitDepth.Header" xml:space="preserve">
|
||||
<value>비트 깊이</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageFileInfoBitrate.Text" xml:space="preserve">
|
||||
<data name="MusicGalleryPageFileInfoBitrate.Header" xml:space="preserve">
|
||||
<value>비트 레이트</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageFileInfoDuration.Text" xml:space="preserve">
|
||||
<data name="MusicGalleryPageFileInfoDuration.Header" xml:space="preserve">
|
||||
<value>지속</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageFileInfoEncoder.Text" xml:space="preserve">
|
||||
<data name="MusicGalleryPageFileInfoEncoder.Header" xml:space="preserve">
|
||||
<value>인코더</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageFileInfoFormat.Text" xml:space="preserve">
|
||||
<data name="MusicGalleryPageFileInfoFormat.Header" xml:space="preserve">
|
||||
<value>체재</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageFileInfoLyrics.Text" xml:space="preserve">
|
||||
<data name="MusicGalleryPageFileInfoLyrics.Header" xml:space="preserve">
|
||||
<value>가사</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageFileInfoPath.Text" xml:space="preserve">
|
||||
<data name="MusicGalleryPageFileInfoPath.Header" xml:space="preserve">
|
||||
<value>길</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageFileInfoSampleRate.Text" xml:space="preserve">
|
||||
<data name="MusicGalleryPageFileInfoSampleRate.Header" xml:space="preserve">
|
||||
<value>샘플 속도</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageFileInfoTitle.Text" xml:space="preserve">
|
||||
<data name="MusicGalleryPageFileInfoTitle.Header" xml:space="preserve">
|
||||
<value>제목</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageFileInfoYear.Text" xml:space="preserve">
|
||||
<data name="MusicGalleryPageFileInfoYear.Header" xml:space="preserve">
|
||||
<value>년도</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageFileNotFound.Text" xml:space="preserve">
|
||||
@@ -565,6 +580,9 @@
|
||||
<data name="SettingsPageAdvanced.Text" xml:space="preserve">
|
||||
<value>고급 옵션</value>
|
||||
</data>
|
||||
<data name="SettingsPageAlbum.Header" xml:space="preserve">
|
||||
<value>앨범</value>
|
||||
</data>
|
||||
<data name="SettingsPageAlbumArt.Text" xml:space="preserve">
|
||||
<value>앨범 아트</value>
|
||||
</data>
|
||||
@@ -610,6 +628,9 @@
|
||||
<data name="SettingsPageApply.Content" xml:space="preserve">
|
||||
<value>적용하다</value>
|
||||
</data>
|
||||
<data name="SettingsPageArtist.Header" xml:space="preserve">
|
||||
<value>아티스트</value>
|
||||
</data>
|
||||
<data name="SettingsPageAutoAdjust.Header" xml:space="preserve">
|
||||
<value>자동 조정</value>
|
||||
</data>
|
||||
@@ -847,6 +868,9 @@
|
||||
<data name="SettingsPageHideWindow.Header" xml:space="preserve">
|
||||
<value>Windows를 자동으로 숨기고 표시합니다</value>
|
||||
</data>
|
||||
<data name="SettingsPageHoldDragSort.Content" xml:space="preserve">
|
||||
<value>여기를 누른 상태에서 드래그하여 정렬하세요</value>
|
||||
</data>
|
||||
<data name="SettingsPageImport.Content" xml:space="preserve">
|
||||
<value>가져오기</value>
|
||||
</data>
|
||||
@@ -1108,6 +1132,15 @@
|
||||
<data name="SettingsPageLyricsWindowSwitchHotKey.Header" xml:space="preserve">
|
||||
<value>가사 창 상태 전환기 바로 가기</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsWindowToolTip.Content" xml:space="preserve">
|
||||
<value>가사 창</value>
|
||||
</data>
|
||||
<data name="SettingsPageMatchingThreshold.Description" xml:space="preserve">
|
||||
<value>이 값을 조정하면 순차 검색 및 검색 결과와 가장 일치하는 검색 결과에 영향을 미치지만 수동 가사 검색 인터페이스의 검색 결과에는 영향을 미치지 않습니다</value>
|
||||
</data>
|
||||
<data name="SettingsPageMatchingThreshold.Header" xml:space="preserve">
|
||||
<value>최소 일치 임계값</value>
|
||||
</data>
|
||||
<data name="SettingsPageMediaLib.Content" xml:space="preserve">
|
||||
<value>미디어 라이브러리</value>
|
||||
</data>
|
||||
@@ -1156,6 +1189,9 @@
|
||||
<data name="SettingsPageOriginalText.Header" xml:space="preserve">
|
||||
<value>원본</value>
|
||||
</data>
|
||||
<data name="SettingsPageOverwriteMatchingThreshold.Header" xml:space="preserve">
|
||||
<value>최소 일치 임계값을 개별적으로 설정하십시오</value>
|
||||
</data>
|
||||
<data name="SettingsPagePaletteGeneratorType.Header" xml:space="preserve">
|
||||
<value>색상 선택 스타일</value>
|
||||
</data>
|
||||
@@ -1189,12 +1225,15 @@
|
||||
<data name="SettingsPagePlaybackShortcut.Text" xml:space="preserve">
|
||||
<value>놀다</value>
|
||||
</data>
|
||||
<data name="SettingsPagePlaybackSource.Text" xml:space="preserve">
|
||||
<data name="SettingsPagePlaybackSource.Header" xml:space="preserve">
|
||||
<value>재생 소스</value>
|
||||
</data>
|
||||
<data name="SettingsPagePlaybackSourceID.Text" xml:space="preserve">
|
||||
<data name="SettingsPagePlaybackSourceID.Header" xml:space="preserve">
|
||||
<value>재생 소스 ID</value>
|
||||
</data>
|
||||
<data name="SettingsPagePlaybackStatus.Header" xml:space="preserve">
|
||||
<value>현재 재생 소스</value>
|
||||
</data>
|
||||
<data name="SettingsPagePlayingMockMusicButton.Content" xml:space="preserve">
|
||||
<value>"soundcloud.com"에서 "Fut to the Feeling"을 재생하십시오.</value>
|
||||
</data>
|
||||
@@ -1219,6 +1258,9 @@
|
||||
<data name="SettingsPageRecordedWindowStatus.Text" xml:space="preserve">
|
||||
<value>기록 된 창 상태</value>
|
||||
</data>
|
||||
<data name="SettingsPageReference.Header" xml:space="preserve">
|
||||
<value>참고문헌</value>
|
||||
</data>
|
||||
<data name="SettingsPageRemoveInfo.Message" xml:space="preserve">
|
||||
<value>이 경로의 원본 파일과 폴더는이 앱에서 제거 할 때 삭제되지 않습니다.</value>
|
||||
</data>
|
||||
@@ -1250,7 +1292,7 @@
|
||||
<value>현재 라인 기간</value>
|
||||
</data>
|
||||
<data name="SettingsPageScrollEasing.Header" xml:space="preserve">
|
||||
<value>가사 스크롤링 애니메이션 유형</value>
|
||||
<value>가사 애니메이션 유형</value>
|
||||
</data>
|
||||
<data name="SettingsPageScrollTopDelay.Header" xml:space="preserve">
|
||||
<value>첫 번째 줄 지연</value>
|
||||
@@ -1258,6 +1300,9 @@
|
||||
<data name="SettingsPageScrollTopDuration.Header" xml:space="preserve">
|
||||
<value>첫 번째 줄의 기간</value>
|
||||
</data>
|
||||
<data name="SettingsPageSearchResultStatus.Header" xml:space="preserve">
|
||||
<value>현재 가사 검색 결과</value>
|
||||
</data>
|
||||
<data name="SettingsPageServerTestButton.Content" xml:space="preserve">
|
||||
<value>테스트 서버</value>
|
||||
</data>
|
||||
@@ -1321,6 +1366,12 @@
|
||||
<data name="SettingsPageSongInfoRight.Content" xml:space="preserve">
|
||||
<value>오른쪽</value>
|
||||
</data>
|
||||
<data name="SettingsPageSongStatus.Header" xml:space="preserve">
|
||||
<value>현재 노래</value>
|
||||
</data>
|
||||
<data name="SettingsPageSongTitle.Header" xml:space="preserve">
|
||||
<value>제목</value>
|
||||
</data>
|
||||
<data name="SettingsPageSpectrumLayer.Header" xml:space="preserve">
|
||||
<value>[실험] 스펙트럼 레이어</value>
|
||||
</data>
|
||||
@@ -1456,4 +1507,7 @@
|
||||
<data name="TryRunMultipleInstance" xml:space="preserve">
|
||||
<value>BetterLyrics 가 이미 실행 중입니다</value>
|
||||
</data>
|
||||
<data name="UserGuide.Content" xml:space="preserve">
|
||||
<value>사용 안내서</value>
|
||||
</data>
|
||||
</root>
|
||||
@@ -144,6 +144,9 @@
|
||||
<data name="Cancel" xml:space="preserve">
|
||||
<value>取消</value>
|
||||
</data>
|
||||
<data name="Copy.Content" xml:space="preserve">
|
||||
<value>拷贝</value>
|
||||
</data>
|
||||
<data name="CreatePlaylistSuccessfully" xml:space="preserve">
|
||||
<value>播放列表创建成功</value>
|
||||
</data>
|
||||
@@ -180,7 +183,7 @@
|
||||
<data name="HostWindowMusicGalleryButtonToolTip.Content" xml:space="preserve">
|
||||
<value>音乐库</value>
|
||||
</data>
|
||||
<data name="HostWindowSettingsButtonToolTip.Text" xml:space="preserve">
|
||||
<data name="HostWindowSettingsButtonToolTip.Content" xml:space="preserve">
|
||||
<value>设置</value>
|
||||
</data>
|
||||
<data name="ImportPlaylistSuccessfully" xml:space="preserve">
|
||||
@@ -228,7 +231,13 @@
|
||||
<data name="LyricsNotFound" xml:space="preserve">
|
||||
<value>未找到歌词</value>
|
||||
</data>
|
||||
<data name="LyricsPageLyricsProviderPrefix.Text" xml:space="preserve">
|
||||
<data name="LyricsPageCachePath.Header" xml:space="preserve">
|
||||
<value>缓存路径</value>
|
||||
</data>
|
||||
<data name="LyricsPageCachePath.Value" xml:space="preserve">
|
||||
<value>缓存路径</value>
|
||||
</data>
|
||||
<data name="LyricsPageLyricsProviderPrefix.Header" xml:space="preserve">
|
||||
<value>歌词来源</value>
|
||||
</data>
|
||||
<data name="LyricsPageLyricsSearchButtonToolTip.Content" xml:space="preserve">
|
||||
@@ -237,7 +246,7 @@
|
||||
<data name="LyricsPageLyricsSettingsButtonToolTip.Content" xml:space="preserve">
|
||||
<value>歌词窗口管理快捷设置</value>
|
||||
</data>
|
||||
<data name="LyricsPageMatchPercentage.Text" xml:space="preserve">
|
||||
<data name="LyricsPageMatchPercentage.Header" xml:space="preserve">
|
||||
<value>匹配百分比</value>
|
||||
</data>
|
||||
<data name="LyricsPagePlaybackSourceButtonToolTip.Content" xml:space="preserve">
|
||||
@@ -264,16 +273,22 @@
|
||||
<data name="LyricsPageTranslationOnly.Header" xml:space="preserve">
|
||||
<value>仅显示翻译</value>
|
||||
</data>
|
||||
<data name="LyricsPageTranslationProviderPrefix.Text" xml:space="preserve">
|
||||
<data name="LyricsPageTranslationProviderPrefix.Header" xml:space="preserve">
|
||||
<value>翻译来源</value>
|
||||
</data>
|
||||
<data name="LyricsSearchControlAlbum.Text" xml:space="preserve">
|
||||
<data name="LyricsParseError" xml:space="preserve">
|
||||
<value>歌词解析失败</value>
|
||||
</data>
|
||||
<data name="LyricsSearchControlAlbum.Header" xml:space="preserve">
|
||||
<value>专辑</value>
|
||||
</data>
|
||||
<data name="LyricsSearchControlArtist.Text" xml:space="preserve">
|
||||
<data name="LyricsSearchControlArtist.Header" xml:space="preserve">
|
||||
<value>艺术家</value>
|
||||
</data>
|
||||
<data name="LyricsSearchControlDurauion.Text" xml:space="preserve">
|
||||
<data name="LyricsSearchControlAutoGenerated.Tag" xml:space="preserve">
|
||||
<value>自动生成</value>
|
||||
</data>
|
||||
<data name="LyricsSearchControlDurauion.Header" xml:space="preserve">
|
||||
<value>时长</value>
|
||||
</data>
|
||||
<data name="LyricsSearchControlHelp.Text" xml:space="preserve">
|
||||
@@ -306,7 +321,7 @@
|
||||
<data name="LyricsSearchControlTargetSearchProvider.Header" xml:space="preserve">
|
||||
<value>目标歌词搜索提供商</value>
|
||||
</data>
|
||||
<data name="LyricsSearchControlTitle.Text" xml:space="preserve">
|
||||
<data name="LyricsSearchControlTitle.Header" xml:space="preserve">
|
||||
<value>标题</value>
|
||||
</data>
|
||||
<data name="LyricsSearchPageTitle" xml:space="preserve">
|
||||
@@ -394,43 +409,43 @@
|
||||
<data name="MusicGalleryPageEmptyPlayingQueue.Text" xml:space="preserve">
|
||||
<value>清除播放队列</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageFileAlbum.Text" xml:space="preserve">
|
||||
<data name="MusicGalleryPageFileAlbum.Header" xml:space="preserve">
|
||||
<value>专辑</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageFileArtist.Text" xml:space="preserve">
|
||||
<data name="MusicGalleryPageFileArtist.Header" xml:space="preserve">
|
||||
<value>艺术家</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageFileInfo.Text" xml:space="preserve">
|
||||
<value>文件信息</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageFileInfoBitDepth.Text" xml:space="preserve">
|
||||
<data name="MusicGalleryPageFileInfoBitDepth.Header" xml:space="preserve">
|
||||
<value>位深度</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageFileInfoBitrate.Text" xml:space="preserve">
|
||||
<data name="MusicGalleryPageFileInfoBitrate.Header" xml:space="preserve">
|
||||
<value>比特率</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageFileInfoDuration.Text" xml:space="preserve">
|
||||
<data name="MusicGalleryPageFileInfoDuration.Header" xml:space="preserve">
|
||||
<value>期间</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageFileInfoEncoder.Text" xml:space="preserve">
|
||||
<data name="MusicGalleryPageFileInfoEncoder.Header" xml:space="preserve">
|
||||
<value>编码器</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageFileInfoFormat.Text" xml:space="preserve">
|
||||
<data name="MusicGalleryPageFileInfoFormat.Header" xml:space="preserve">
|
||||
<value>格式</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageFileInfoLyrics.Text" xml:space="preserve">
|
||||
<data name="MusicGalleryPageFileInfoLyrics.Header" xml:space="preserve">
|
||||
<value>歌词</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageFileInfoPath.Text" xml:space="preserve">
|
||||
<data name="MusicGalleryPageFileInfoPath.Header" xml:space="preserve">
|
||||
<value>路径</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageFileInfoSampleRate.Text" xml:space="preserve">
|
||||
<data name="MusicGalleryPageFileInfoSampleRate.Header" xml:space="preserve">
|
||||
<value>样本率</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageFileInfoTitle.Text" xml:space="preserve">
|
||||
<data name="MusicGalleryPageFileInfoTitle.Header" xml:space="preserve">
|
||||
<value>标题</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageFileInfoYear.Text" xml:space="preserve">
|
||||
<data name="MusicGalleryPageFileInfoYear.Header" xml:space="preserve">
|
||||
<value>年</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageFileNotFound.Text" xml:space="preserve">
|
||||
@@ -565,6 +580,9 @@
|
||||
<data name="SettingsPageAdvanced.Text" xml:space="preserve">
|
||||
<value>高级选项</value>
|
||||
</data>
|
||||
<data name="SettingsPageAlbum.Header" xml:space="preserve">
|
||||
<value>专辑</value>
|
||||
</data>
|
||||
<data name="SettingsPageAlbumArt.Text" xml:space="preserve">
|
||||
<value>专辑</value>
|
||||
</data>
|
||||
@@ -610,6 +628,9 @@
|
||||
<data name="SettingsPageApply.Content" xml:space="preserve">
|
||||
<value>应用</value>
|
||||
</data>
|
||||
<data name="SettingsPageArtist.Header" xml:space="preserve">
|
||||
<value>艺术家</value>
|
||||
</data>
|
||||
<data name="SettingsPageAutoAdjust.Header" xml:space="preserve">
|
||||
<value>自动调整</value>
|
||||
</data>
|
||||
@@ -847,6 +868,9 @@
|
||||
<data name="SettingsPageHideWindow.Header" xml:space="preserve">
|
||||
<value>自动隐藏/显示窗口</value>
|
||||
</data>
|
||||
<data name="SettingsPageHoldDragSort.Content" xml:space="preserve">
|
||||
<value>按住此处并拖动以排序</value>
|
||||
</data>
|
||||
<data name="SettingsPageImport.Content" xml:space="preserve">
|
||||
<value>导入</value>
|
||||
</data>
|
||||
@@ -1108,6 +1132,15 @@
|
||||
<data name="SettingsPageLyricsWindowSwitchHotKey.Header" xml:space="preserve">
|
||||
<value>歌词窗口状态切换器快捷方式</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsWindowToolTip.Content" xml:space="preserve">
|
||||
<value>歌词窗口</value>
|
||||
</data>
|
||||
<data name="SettingsPageMatchingThreshold.Description" xml:space="preserve">
|
||||
<value>调整此值将影响顺序搜索和最佳匹配搜索结果,但不会影响手动歌词搜索界面中的搜索结果</value>
|
||||
</data>
|
||||
<data name="SettingsPageMatchingThreshold.Header" xml:space="preserve">
|
||||
<value>最小匹配阈值</value>
|
||||
</data>
|
||||
<data name="SettingsPageMediaLib.Content" xml:space="preserve">
|
||||
<value>媒体库</value>
|
||||
</data>
|
||||
@@ -1156,6 +1189,9 @@
|
||||
<data name="SettingsPageOriginalText.Header" xml:space="preserve">
|
||||
<value>原文</value>
|
||||
</data>
|
||||
<data name="SettingsPageOverwriteMatchingThreshold.Header" xml:space="preserve">
|
||||
<value>单独设置最小匹配阈值</value>
|
||||
</data>
|
||||
<data name="SettingsPagePaletteGeneratorType.Header" xml:space="preserve">
|
||||
<value>取色风格</value>
|
||||
</data>
|
||||
@@ -1189,12 +1225,15 @@
|
||||
<data name="SettingsPagePlaybackShortcut.Text" xml:space="preserve">
|
||||
<value>播放</value>
|
||||
</data>
|
||||
<data name="SettingsPagePlaybackSource.Text" xml:space="preserve">
|
||||
<data name="SettingsPagePlaybackSource.Header" xml:space="preserve">
|
||||
<value>播放源</value>
|
||||
</data>
|
||||
<data name="SettingsPagePlaybackSourceID.Text" xml:space="preserve">
|
||||
<data name="SettingsPagePlaybackSourceID.Header" xml:space="preserve">
|
||||
<value>播放源 ID</value>
|
||||
</data>
|
||||
<data name="SettingsPagePlaybackStatus.Header" xml:space="preserve">
|
||||
<value>当前播放源</value>
|
||||
</data>
|
||||
<data name="SettingsPagePlayingMockMusicButton.Content" xml:space="preserve">
|
||||
<value>在 “soundcloud.com” 上播放 “Cut to the Feeling”</value>
|
||||
</data>
|
||||
@@ -1219,6 +1258,9 @@
|
||||
<data name="SettingsPageRecordedWindowStatus.Text" xml:space="preserve">
|
||||
<value>记录的窗口状态</value>
|
||||
</data>
|
||||
<data name="SettingsPageReference.Header" xml:space="preserve">
|
||||
<value>参考链接</value>
|
||||
</data>
|
||||
<data name="SettingsPageRemoveInfo.Message" xml:space="preserve">
|
||||
<value>路径中的原始文件和文件夹不会被删除</value>
|
||||
</data>
|
||||
@@ -1250,7 +1292,7 @@
|
||||
<value>当前行持续时间</value>
|
||||
</data>
|
||||
<data name="SettingsPageScrollEasing.Header" xml:space="preserve">
|
||||
<value>歌词滚动动画类型</value>
|
||||
<value>歌词动画类型</value>
|
||||
</data>
|
||||
<data name="SettingsPageScrollTopDelay.Header" xml:space="preserve">
|
||||
<value>首行延时</value>
|
||||
@@ -1258,6 +1300,9 @@
|
||||
<data name="SettingsPageScrollTopDuration.Header" xml:space="preserve">
|
||||
<value>首行持续时间</value>
|
||||
</data>
|
||||
<data name="SettingsPageSearchResultStatus.Header" xml:space="preserve">
|
||||
<value>当前歌词搜索结果</value>
|
||||
</data>
|
||||
<data name="SettingsPageServerTestButton.Content" xml:space="preserve">
|
||||
<value>测试服务器</value>
|
||||
</data>
|
||||
@@ -1321,6 +1366,12 @@
|
||||
<data name="SettingsPageSongInfoRight.Content" xml:space="preserve">
|
||||
<value>靠右</value>
|
||||
</data>
|
||||
<data name="SettingsPageSongStatus.Header" xml:space="preserve">
|
||||
<value>当前歌曲</value>
|
||||
</data>
|
||||
<data name="SettingsPageSongTitle.Header" xml:space="preserve">
|
||||
<value>标题</value>
|
||||
</data>
|
||||
<data name="SettingsPageSpectrumLayer.Header" xml:space="preserve">
|
||||
<value>[实验性] 频谱层</value>
|
||||
</data>
|
||||
@@ -1456,4 +1507,7 @@
|
||||
<data name="TryRunMultipleInstance" xml:space="preserve">
|
||||
<value>BetterLyrics 已经在运行</value>
|
||||
</data>
|
||||
<data name="UserGuide.Content" xml:space="preserve">
|
||||
<value>使用指南</value>
|
||||
</data>
|
||||
</root>
|
||||
@@ -144,6 +144,9 @@
|
||||
<data name="Cancel" xml:space="preserve">
|
||||
<value>取消</value>
|
||||
</data>
|
||||
<data name="Copy.Content" xml:space="preserve">
|
||||
<value>複製</value>
|
||||
</data>
|
||||
<data name="CreatePlaylistSuccessfully" xml:space="preserve">
|
||||
<value>已成功建立播放清單</value>
|
||||
</data>
|
||||
@@ -180,7 +183,7 @@
|
||||
<data name="HostWindowMusicGalleryButtonToolTip.Content" xml:space="preserve">
|
||||
<value>音樂庫</value>
|
||||
</data>
|
||||
<data name="HostWindowSettingsButtonToolTip.Text" xml:space="preserve">
|
||||
<data name="HostWindowSettingsButtonToolTip.Content" xml:space="preserve">
|
||||
<value>設定</value>
|
||||
</data>
|
||||
<data name="ImportPlaylistSuccessfully" xml:space="preserve">
|
||||
@@ -228,7 +231,13 @@
|
||||
<data name="LyricsNotFound" xml:space="preserve">
|
||||
<value>找不到歌詞</value>
|
||||
</data>
|
||||
<data name="LyricsPageLyricsProviderPrefix.Text" xml:space="preserve">
|
||||
<data name="LyricsPageCachePath.Header" xml:space="preserve">
|
||||
<value>檔案路徑緩存</value>
|
||||
</data>
|
||||
<data name="LyricsPageCachePath.Value" xml:space="preserve">
|
||||
<value>檔案路徑緩存</value>
|
||||
</data>
|
||||
<data name="LyricsPageLyricsProviderPrefix.Header" xml:space="preserve">
|
||||
<value>歌詞來源</value>
|
||||
</data>
|
||||
<data name="LyricsPageLyricsSearchButtonToolTip.Content" xml:space="preserve">
|
||||
@@ -237,7 +246,7 @@
|
||||
<data name="LyricsPageLyricsSettingsButtonToolTip.Content" xml:space="preserve">
|
||||
<value>歌詞視窗管理快捷設定</value>
|
||||
</data>
|
||||
<data name="LyricsPageMatchPercentage.Text" xml:space="preserve">
|
||||
<data name="LyricsPageMatchPercentage.Header" xml:space="preserve">
|
||||
<value>匹配百分比</value>
|
||||
</data>
|
||||
<data name="LyricsPagePlaybackSourceButtonToolTip.Content" xml:space="preserve">
|
||||
@@ -264,16 +273,22 @@
|
||||
<data name="LyricsPageTranslationOnly.Header" xml:space="preserve">
|
||||
<value>僅顯示翻譯</value>
|
||||
</data>
|
||||
<data name="LyricsPageTranslationProviderPrefix.Text" xml:space="preserve">
|
||||
<data name="LyricsPageTranslationProviderPrefix.Header" xml:space="preserve">
|
||||
<value>翻譯來源</value>
|
||||
</data>
|
||||
<data name="LyricsSearchControlAlbum.Text" xml:space="preserve">
|
||||
<data name="LyricsParseError" xml:space="preserve">
|
||||
<value>歌詞解析失敗</value>
|
||||
</data>
|
||||
<data name="LyricsSearchControlAlbum.Header" xml:space="preserve">
|
||||
<value>專輯</value>
|
||||
</data>
|
||||
<data name="LyricsSearchControlArtist.Text" xml:space="preserve">
|
||||
<data name="LyricsSearchControlArtist.Header" xml:space="preserve">
|
||||
<value>藝術家</value>
|
||||
</data>
|
||||
<data name="LyricsSearchControlDurauion.Text" xml:space="preserve">
|
||||
<data name="LyricsSearchControlAutoGenerated.Tag" xml:space="preserve">
|
||||
<value>已產生</value>
|
||||
</data>
|
||||
<data name="LyricsSearchControlDurauion.Header" xml:space="preserve">
|
||||
<value>時長</value>
|
||||
</data>
|
||||
<data name="LyricsSearchControlHelp.Text" xml:space="preserve">
|
||||
@@ -306,7 +321,7 @@
|
||||
<data name="LyricsSearchControlTargetSearchProvider.Header" xml:space="preserve">
|
||||
<value>目標歌詞搜尋提供者</value>
|
||||
</data>
|
||||
<data name="LyricsSearchControlTitle.Text" xml:space="preserve">
|
||||
<data name="LyricsSearchControlTitle.Header" xml:space="preserve">
|
||||
<value>標題</value>
|
||||
</data>
|
||||
<data name="LyricsSearchPageTitle" xml:space="preserve">
|
||||
@@ -394,43 +409,43 @@
|
||||
<data name="MusicGalleryPageEmptyPlayingQueue.Text" xml:space="preserve">
|
||||
<value>清除播放隊列</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageFileAlbum.Text" xml:space="preserve">
|
||||
<data name="MusicGalleryPageFileAlbum.Header" xml:space="preserve">
|
||||
<value>專輯</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageFileArtist.Text" xml:space="preserve">
|
||||
<data name="MusicGalleryPageFileArtist.Header" xml:space="preserve">
|
||||
<value>藝術家</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageFileInfo.Text" xml:space="preserve">
|
||||
<value>文件信息</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageFileInfoBitDepth.Text" xml:space="preserve">
|
||||
<data name="MusicGalleryPageFileInfoBitDepth.Header" xml:space="preserve">
|
||||
<value>位深度</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageFileInfoBitrate.Text" xml:space="preserve">
|
||||
<data name="MusicGalleryPageFileInfoBitrate.Header" xml:space="preserve">
|
||||
<value>比特率</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageFileInfoDuration.Text" xml:space="preserve">
|
||||
<data name="MusicGalleryPageFileInfoDuration.Header" xml:space="preserve">
|
||||
<value>期間</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageFileInfoEncoder.Text" xml:space="preserve">
|
||||
<data name="MusicGalleryPageFileInfoEncoder.Header" xml:space="preserve">
|
||||
<value>編碼器</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageFileInfoFormat.Text" xml:space="preserve">
|
||||
<data name="MusicGalleryPageFileInfoFormat.Header" xml:space="preserve">
|
||||
<value>格式</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageFileInfoLyrics.Text" xml:space="preserve">
|
||||
<data name="MusicGalleryPageFileInfoLyrics.Header" xml:space="preserve">
|
||||
<value>歌词</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageFileInfoPath.Text" xml:space="preserve">
|
||||
<data name="MusicGalleryPageFileInfoPath.Header" xml:space="preserve">
|
||||
<value>路徑</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageFileInfoSampleRate.Text" xml:space="preserve">
|
||||
<data name="MusicGalleryPageFileInfoSampleRate.Header" xml:space="preserve">
|
||||
<value>樣本率</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageFileInfoTitle.Text" xml:space="preserve">
|
||||
<data name="MusicGalleryPageFileInfoTitle.Header" xml:space="preserve">
|
||||
<value>標題</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageFileInfoYear.Text" xml:space="preserve">
|
||||
<data name="MusicGalleryPageFileInfoYear.Header" xml:space="preserve">
|
||||
<value>年</value>
|
||||
</data>
|
||||
<data name="MusicGalleryPageFileNotFound.Text" xml:space="preserve">
|
||||
@@ -565,6 +580,9 @@
|
||||
<data name="SettingsPageAdvanced.Text" xml:space="preserve">
|
||||
<value>高級選項</value>
|
||||
</data>
|
||||
<data name="SettingsPageAlbum.Header" xml:space="preserve">
|
||||
<value>專輯</value>
|
||||
</data>
|
||||
<data name="SettingsPageAlbumArt.Text" xml:space="preserve">
|
||||
<value>專輯</value>
|
||||
</data>
|
||||
@@ -610,6 +628,9 @@
|
||||
<data name="SettingsPageApply.Content" xml:space="preserve">
|
||||
<value>應用</value>
|
||||
</data>
|
||||
<data name="SettingsPageArtist.Header" xml:space="preserve">
|
||||
<value>藝術家</value>
|
||||
</data>
|
||||
<data name="SettingsPageAutoAdjust.Header" xml:space="preserve">
|
||||
<value>自動調整</value>
|
||||
</data>
|
||||
@@ -847,6 +868,9 @@
|
||||
<data name="SettingsPageHideWindow.Header" xml:space="preserve">
|
||||
<value>自動隱藏/顯示窗口</value>
|
||||
</data>
|
||||
<data name="SettingsPageHoldDragSort.Content" xml:space="preserve">
|
||||
<value>按住這裡並拖曳以進行排序</value>
|
||||
</data>
|
||||
<data name="SettingsPageImport.Content" xml:space="preserve">
|
||||
<value>匯入</value>
|
||||
</data>
|
||||
@@ -1108,6 +1132,15 @@
|
||||
<data name="SettingsPageLyricsWindowSwitchHotKey.Header" xml:space="preserve">
|
||||
<value>歌詞視窗狀態切換器捷徑</value>
|
||||
</data>
|
||||
<data name="SettingsPageLyricsWindowToolTip.Content" xml:space="preserve">
|
||||
<value>歌詞視窗</value>
|
||||
</data>
|
||||
<data name="SettingsPageMatchingThreshold.Description" xml:space="preserve">
|
||||
<value>調整此值將影響順序搜索和最佳匹配搜索結果,但不會影響手動歌詞搜索界面中的搜索結果</value>
|
||||
</data>
|
||||
<data name="SettingsPageMatchingThreshold.Header" xml:space="preserve">
|
||||
<value>最低匹配閾值</value>
|
||||
</data>
|
||||
<data name="SettingsPageMediaLib.Content" xml:space="preserve">
|
||||
<value>媒體庫</value>
|
||||
</data>
|
||||
@@ -1156,6 +1189,9 @@
|
||||
<data name="SettingsPageOriginalText.Header" xml:space="preserve">
|
||||
<value>原文</value>
|
||||
</data>
|
||||
<data name="SettingsPageOverwriteMatchingThreshold.Header" xml:space="preserve">
|
||||
<value>單獨設置最低匹配閾值</value>
|
||||
</data>
|
||||
<data name="SettingsPagePaletteGeneratorType.Header" xml:space="preserve">
|
||||
<value>取色風格</value>
|
||||
</data>
|
||||
@@ -1189,12 +1225,15 @@
|
||||
<data name="SettingsPagePlaybackShortcut.Text" xml:space="preserve">
|
||||
<value>播放</value>
|
||||
</data>
|
||||
<data name="SettingsPagePlaybackSource.Text" xml:space="preserve">
|
||||
<data name="SettingsPagePlaybackSource.Header" xml:space="preserve">
|
||||
<value>播放源</value>
|
||||
</data>
|
||||
<data name="SettingsPagePlaybackSourceID.Text" xml:space="preserve">
|
||||
<data name="SettingsPagePlaybackSourceID.Header" xml:space="preserve">
|
||||
<value>播放源 ID</value>
|
||||
</data>
|
||||
<data name="SettingsPagePlaybackStatus.Header" xml:space="preserve">
|
||||
<value>當前播放源</value>
|
||||
</data>
|
||||
<data name="SettingsPagePlayingMockMusicButton.Content" xml:space="preserve">
|
||||
<value>在 “soundcloud.com” 上播放 “Cut to the Feeling”</value>
|
||||
</data>
|
||||
@@ -1219,6 +1258,9 @@
|
||||
<data name="SettingsPageRecordedWindowStatus.Text" xml:space="preserve">
|
||||
<value>記錄的窗口狀態</value>
|
||||
</data>
|
||||
<data name="SettingsPageReference.Header" xml:space="preserve">
|
||||
<value>參考數據</value>
|
||||
</data>
|
||||
<data name="SettingsPageRemoveInfo.Message" xml:space="preserve">
|
||||
<value>路徑中的原始檔案和資料夾不會被刪除</value>
|
||||
</data>
|
||||
@@ -1250,7 +1292,7 @@
|
||||
<value>當前行持續時間</value>
|
||||
</data>
|
||||
<data name="SettingsPageScrollEasing.Header" xml:space="preserve">
|
||||
<value>歌詞滾動動畫類型</value>
|
||||
<value>歌詞動畫類型</value>
|
||||
</data>
|
||||
<data name="SettingsPageScrollTopDelay.Header" xml:space="preserve">
|
||||
<value>首行延時</value>
|
||||
@@ -1258,6 +1300,9 @@
|
||||
<data name="SettingsPageScrollTopDuration.Header" xml:space="preserve">
|
||||
<value>首行持續時間</value>
|
||||
</data>
|
||||
<data name="SettingsPageSearchResultStatus.Header" xml:space="preserve">
|
||||
<value>目前歌詞搜尋結果</value>
|
||||
</data>
|
||||
<data name="SettingsPageServerTestButton.Content" xml:space="preserve">
|
||||
<value>測試服務器</value>
|
||||
</data>
|
||||
@@ -1321,6 +1366,12 @@
|
||||
<data name="SettingsPageSongInfoRight.Content" xml:space="preserve">
|
||||
<value>靠右</value>
|
||||
</data>
|
||||
<data name="SettingsPageSongStatus.Header" xml:space="preserve">
|
||||
<value>當前歌曲</value>
|
||||
</data>
|
||||
<data name="SettingsPageSongTitle.Header" xml:space="preserve">
|
||||
<value>標題</value>
|
||||
</data>
|
||||
<data name="SettingsPageSpectrumLayer.Header" xml:space="preserve">
|
||||
<value>[實驗性] 頻譜層</value>
|
||||
</data>
|
||||
@@ -1456,4 +1507,7 @@
|
||||
<data name="TryRunMultipleInstance" xml:space="preserve">
|
||||
<value>BetterLyrics 已經在運作</value>
|
||||
</data>
|
||||
<data name="UserGuide.Content" xml:space="preserve">
|
||||
<value>使用手冊</value>
|
||||
</data>
|
||||
</root>
|
||||
@@ -7,6 +7,7 @@ using BetterLyrics.WinUI3.Services.SettingsService;
|
||||
using BetterLyrics.WinUI3.Views;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
@@ -35,7 +36,7 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
[RelayCommand]
|
||||
private async Task LaunchProjectGitHubPageAsync()
|
||||
{
|
||||
await Windows.System.Launcher.LaunchUriAsync(new Uri(Constants.Link.GitHubUrl));
|
||||
await Windows.System.Launcher.LaunchUriAsync(new Uri(Constants.Link.GitHub));
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
@@ -64,7 +65,7 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
}
|
||||
else
|
||||
{
|
||||
DevWinUI.Growl.Error(_resourceService.GetLocalizedString("ImportSettingsFailed") ?? "");
|
||||
ToastHelper.ShowToast("ImportSettingsFailed", null, InfoBarSeverity.Error);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -77,7 +78,7 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
if (folder != null)
|
||||
{
|
||||
_settingsService.ExportSettings(folder.Path);
|
||||
DevWinUI.Growl.Success(_resourceService.GetLocalizedString("ExportSettingsSuccess") ?? "");
|
||||
ToastHelper.ShowToast("ExportSettingsSuccess", null, InfoBarSeverity.Success);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -95,7 +96,7 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
|
||||
DirectoryHelper.DeleteAllFiles(PathHelper.iTunesAlbumArtCacheDirectory);
|
||||
|
||||
DevWinUI.Growl.Success(_resourceService.GetLocalizedString("ActionCompleted"));
|
||||
ToastHelper.ShowToast("ActionCompleted", null, InfoBarSeverity.Success);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
using BetterLyrics.WinUI3.Enums;
|
||||
using BetterLyrics.WinUI3.Extensions;
|
||||
using BetterLyrics.WinUI3.Helper;
|
||||
using BetterLyrics.WinUI3.Shaders;
|
||||
using CommunityToolkit.WinUI;
|
||||
using ComputeSharp.D2D1.WinUI;
|
||||
using Microsoft.Graphics.Canvas;
|
||||
using Microsoft.Graphics.Canvas.Brushes;
|
||||
using Microsoft.Graphics.Canvas.Effects;
|
||||
@@ -28,6 +26,7 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
|
||||
using (var blurredLyricsDs = blurredLyrics.CreateDrawingSession())
|
||||
{
|
||||
DrawBlurredLyrics(control, blurredLyricsDs);
|
||||
//DrawBlurredLyrics2(control, blurredLyricsDs);
|
||||
}
|
||||
|
||||
using var combined = new CanvasCommandList(control);
|
||||
@@ -299,17 +298,18 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
|
||||
|
||||
//// 组合变换:缩放 -> 旋转 -> 平移
|
||||
ds.Transform =
|
||||
Matrix3x2.CreateScale((float)line.ScaleTransition.Value, line.CenterPosition)
|
||||
* Matrix3x2.CreateRotation((float)line.AngleTransition.Value,
|
||||
currentPlayingLine.OriginalPosition.WithX(_liveStatesService.LiveStates.LyricsWindowStatus.LyricsEffectSettings.FanLyricsAngle < 0 ? (float)_maxLyricsWidth : 0))
|
||||
* Matrix3x2.CreateTranslation((float)_lyricsX, (float)yOffset);
|
||||
Matrix3x2.CreateScale((float)line.ScaleTransition.Value, line.CenterPosition) *
|
||||
Matrix3x2.CreateRotation(
|
||||
(float)line.AngleTransition.Value,
|
||||
currentPlayingLine.OriginalPosition.WithX(_liveStatesService.LiveStates.LyricsWindowStatus.LyricsEffectSettings.FanLyricsAngle < 0 ? (float)_maxLyricsWidth : 0)) *
|
||||
Matrix3x2.CreateTranslation((float)_lyricsX, (float)yOffset);
|
||||
|
||||
using var combined = new CanvasCommandList(control);
|
||||
using var combinedDs = combined.CreateDrawingSession();
|
||||
|
||||
// 先铺一层带默认透明度的已经加了模糊效果的歌词作为最底层(背景歌词层次)
|
||||
using var backgroundFontEffect = CanvasHelper.CreateFontEffect(line, control, _strokeFontColor,
|
||||
_liveStatesService.LiveStates.LyricsWindowStatus.LyricsStyleSettings.LyricsFontStrokeWidth, _bgFontColor);
|
||||
_liveStatesService.LiveStates.LyricsWindowStatus.LyricsStyleSettings.LyricsFontStrokeWidth, _bgFontColor);
|
||||
|
||||
using var backgroundEffect = CanvasHelper.CreateBackgroundEffect(line, backgroundFontEffect, _lyricsOpacityTransition.Value);
|
||||
combinedDs.DrawImage(backgroundEffect);
|
||||
@@ -370,7 +370,7 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
|
||||
combinedDs.DrawImage(new OpacityEffect
|
||||
{
|
||||
Source = effectLayer,
|
||||
Opacity = (float)(line.HighlightOpacityTransition.Value * _lyricsOpacityTransition.Value),
|
||||
Opacity = (float)Math.Max(line.HighlightOpacityTransition.Value * _lyricsOpacityTransition.Value, 0),
|
||||
});
|
||||
|
||||
if (i == _playingLineIndex)
|
||||
@@ -406,6 +406,125 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
|
||||
}
|
||||
}
|
||||
|
||||
public void DrawBlurredLyrics2(ICanvasAnimatedControl control, CanvasDrawingSession ds)
|
||||
{
|
||||
var currentPlayingLine = _currentLyricsData?.LyricsLines.ElementAtOrDefault(_playingLineIndex);
|
||||
if (currentPlayingLine == null) return;
|
||||
|
||||
var settings = _liveStatesService.LiveStates.LyricsWindowStatus;
|
||||
var effectSettings = settings.LyricsEffectSettings;
|
||||
bool isKaraokeEnabled = effectSettings.IsLyricsLineFadeEnabled;
|
||||
var styleSettings = settings.LyricsStyleSettings; // 获取样式设置(描边宽等)
|
||||
|
||||
for (int i = _startVisibleLineIndex; i <= _endVisibleLineIndex; i++)
|
||||
{
|
||||
var line = _currentLyricsData?.LyricsLines.ElementAtOrDefault(i);
|
||||
if (line == null || line.OriginalCanvasTextLayout == null) continue;
|
||||
|
||||
var textLayout = line.OriginalCanvasTextLayout;
|
||||
|
||||
// === 1. 恢复您原始的矩阵变换逻辑 ===
|
||||
// 不要减去 CenterPosition,保持和您原始代码一致
|
||||
double yOffset = line.YOffsetTransition.Value + _canvasHeight / 2 + _lyricsYTransition.Value;
|
||||
float fanAngleX = effectSettings.FanLyricsAngle < 0 ? (float)_maxLyricsWidth : 0;
|
||||
|
||||
ds.Transform =
|
||||
Matrix3x2.CreateScale((float)line.ScaleTransition.Value, line.CenterPosition)
|
||||
* Matrix3x2.CreateRotation((float)line.AngleTransition.Value,
|
||||
currentPlayingLine.OriginalPosition.WithX(fanAngleX))
|
||||
* Matrix3x2.CreateTranslation((float)_lyricsX, (float)yOffset);
|
||||
|
||||
// === 2. 坐标关键:必须使用 line.OriginalPosition ===
|
||||
// CanvasHelper 里是画在 OriginalPosition 上的,我们这里必须一致
|
||||
var drawPos = line.OriginalPosition;
|
||||
|
||||
// A. 绘制阴影 (优化:直接绘制,替代 ShadowEffect)
|
||||
if (effectSettings.IsLyricsShadowEnabled)
|
||||
{
|
||||
var shadowColor = _albumArtAccentColor1Transition.Value.WithAlpha((byte)(effectSettings.LyricsShadowAmount * 255));
|
||||
// 偏移 2px 绘制阴影
|
||||
ds.DrawTextLayout(textLayout, drawPos.X + 2f, drawPos.Y + 2f, shadowColor);
|
||||
}
|
||||
|
||||
// B. 绘制描边 (如果设置里有)
|
||||
// 对应 CanvasHelper.CreateFontEffect 里的 stroke 逻辑
|
||||
if (styleSettings.LyricsFontStrokeWidth > 0)
|
||||
{
|
||||
// 注意:DrawTextLayout 不支持直接描边,需要用 DrawGeometry
|
||||
// 如果这一步很卡,可以考虑去掉描边,或者只对当前行描边
|
||||
if (line.OriginalCanvasGeometry != null)
|
||||
{
|
||||
ds.DrawGeometry(line.OriginalCanvasGeometry, drawPos, _strokeFontColor, styleSettings.LyricsFontStrokeWidth);
|
||||
}
|
||||
}
|
||||
|
||||
// C. 绘制底层文本 (底色)
|
||||
// 对应 CanvasHelper 里的 DrawTextLayout
|
||||
var baseColor = _strokeFontColor.WithAlpha((byte)(_strokeFontColor.A * _lyricsOpacityTransition.Value));
|
||||
ds.DrawTextLayout(textLayout, drawPos, baseColor);
|
||||
|
||||
|
||||
// D. 绘制高亮/卡拉OK效果 (核心优化点)
|
||||
if (line.HighlightOpacityTransition.Value > 0)
|
||||
{
|
||||
GetLinePlayingProgress(i, out int charStartIndex, out int charLength, out double charProgress);
|
||||
|
||||
// 整行高亮或非逐字模式
|
||||
if (charStartIndex >= line.OriginalText.Length || !isKaraokeEnabled)
|
||||
{
|
||||
var hlColor = _fgFontColor.WithAlpha((byte)(_fgFontColor.A * line.HighlightOpacityTransition.Value));
|
||||
ds.DrawTextLayout(textLayout, drawPos, hlColor);
|
||||
}
|
||||
else
|
||||
{
|
||||
// === 计算裁剪区域 (照搬 CreateCharMask 的逻辑) ===
|
||||
var regions = textLayout.GetCharacterRegions(charStartIndex, 1);
|
||||
|
||||
// 默认裁剪宽度覆盖到当前字之前
|
||||
double validWidth = 0;
|
||||
if (regions.Length > 0)
|
||||
{
|
||||
var region = regions[0];
|
||||
// CanvasHelper 里的逻辑:region.LayoutBounds.X 是相对于 Layout 左上角的
|
||||
// highlightWidth = TotalWidth * Progress
|
||||
double highlightWidth = region.LayoutBounds.Width * charProgress;
|
||||
|
||||
// 当前高亮的总右边界 = 当前字左边 + 当前字播放过的宽度
|
||||
validWidth = region.LayoutBounds.X + highlightWidth;
|
||||
}
|
||||
else if (charStartIndex > 0)
|
||||
{
|
||||
// 容错:如果取不到当前字区域(例如空格),取上一个字的右边缘
|
||||
var prevRegions = textLayout.GetCharacterRegions(charStartIndex - 1, 1);
|
||||
if (prevRegions.Length > 0) validWidth = prevRegions[0].LayoutBounds.Right;
|
||||
}
|
||||
|
||||
if (validWidth > 0)
|
||||
{
|
||||
// === 创建 Layer 进行裁剪 ===
|
||||
// 裁剪矩形的 X/Y 必须加上 drawPos (OriginalPosition)
|
||||
// 因为 CreateLayer 是基于当前 Transform 的全局坐标
|
||||
var clipRect = new Rect(
|
||||
drawPos.X, // 从文字绘制起点的 X 开始
|
||||
drawPos.Y, // 从文字绘制起点的 Y 开始
|
||||
validWidth, // 宽度
|
||||
textLayout.LayoutBounds.Height // 高度
|
||||
);
|
||||
|
||||
using (ds.CreateLayer(1.0f, clipRect))
|
||||
{
|
||||
var hlColor = _fgFontColor.WithAlpha((byte)(_fgFontColor.A * line.HighlightOpacityTransition.Value));
|
||||
ds.DrawTextLayout(textLayout, drawPos, hlColor);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 重置变换,准备画下一行
|
||||
ds.Transform = Matrix3x2.Identity;
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawSnowEffect(CanvasDrawingSession ds)
|
||||
{
|
||||
if (_snowEffect != null && _liveStatesService.LiveStates.LyricsWindowStatus.LyricsBackgroundSettings.IsSnowFlakeOverlayEnabled)
|
||||
|
||||
@@ -892,7 +892,27 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
|
||||
yScrollDelay = distanceFactor * _liveStatesService.LiveStates.LyricsWindowStatus.LyricsEffectSettings.LyricsScrollBottomDelay / 1000.0;
|
||||
}
|
||||
|
||||
line.YOffsetTransition.SetEasingType(_canvasYScrollTransition.EasingType ?? EasingType.Linear);
|
||||
line.AngleTransition.SetEasingType(_canvasYScrollTransition.EasingType);
|
||||
line.AngleTransition.SetDuration(yScrollDuration);
|
||||
line.AngleTransition.SetDelay(yScrollDelay);
|
||||
|
||||
line.ScaleTransition.SetEasingType(_canvasYScrollTransition.EasingType);
|
||||
line.ScaleTransition.SetDuration(yScrollDuration);
|
||||
line.ScaleTransition.SetDelay(yScrollDelay);
|
||||
|
||||
line.BlurAmountTransition.SetEasingType(_canvasYScrollTransition.EasingType);
|
||||
line.BlurAmountTransition.SetDuration(yScrollDuration);
|
||||
line.BlurAmountTransition.SetDelay(yScrollDelay);
|
||||
|
||||
line.OpacityTransition.SetEasingType(_canvasYScrollTransition.EasingType);
|
||||
line.OpacityTransition.SetDuration(yScrollDuration);
|
||||
line.OpacityTransition.SetDelay(yScrollDelay);
|
||||
|
||||
line.HighlightOpacityTransition.SetEasingType(_canvasYScrollTransition.EasingType);
|
||||
line.HighlightOpacityTransition.SetDuration(yScrollDuration);
|
||||
line.HighlightOpacityTransition.SetDelay(yScrollDelay);
|
||||
|
||||
line.YOffsetTransition.SetEasingType(_canvasYScrollTransition.EasingType);
|
||||
line.YOffsetTransition.SetDuration(yScrollDuration);
|
||||
line.YOffsetTransition.SetDelay(yScrollDelay);
|
||||
line.YOffsetTransition.StartTransition(_canvasTargetYScrollOffset, _isLayoutChanged);
|
||||
|
||||
@@ -16,7 +16,6 @@ using Microsoft.Graphics.Canvas;
|
||||
using Microsoft.Graphics.Canvas.Geometry;
|
||||
using Microsoft.Graphics.Canvas.Text;
|
||||
using Microsoft.UI;
|
||||
using Microsoft.UI.Dispatching;
|
||||
using Microsoft.UI.Text;
|
||||
using Microsoft.UI.Xaml;
|
||||
using System;
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
using BetterLyrics.WinUI3.Helper;
|
||||
using BetterLyrics.WinUI3.Models;
|
||||
using BetterLyrics.WinUI3.Models.Settings;
|
||||
using BetterLyrics.WinUI3.Parsers.LyricsParser;
|
||||
using BetterLyrics.WinUI3.Services.LyricsSearchService;
|
||||
using BetterLyrics.WinUI3.Services.MediaSessionsService;
|
||||
using BetterLyrics.WinUI3.Services.SettingsService;
|
||||
@@ -117,8 +118,8 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
((SongInfo?)_mediaSessionsService.CurrentSongInfo?.Clone() ?? new())
|
||||
.WithTitle(MappedSongSearchQuery.MappedTitle)
|
||||
.WithArtist(MappedSongSearchQuery.MappedArtist.SplitByCommonSplitter())
|
||||
.WithAlbum(MappedSongSearchQuery.MappedAlbum),
|
||||
!_settingsService.AppSettings.GeneralSettings.IgnoreCacheWhenSearching,
|
||||
.WithAlbum(MappedSongSearchQuery.MappedAlbum),
|
||||
!_settingsService.AppSettings.GeneralSettings.IgnoreCacheWhenSearching,
|
||||
token);
|
||||
return result;
|
||||
}, token)];
|
||||
|
||||
@@ -7,6 +7,7 @@ using BetterLyrics.WinUI3.Views;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
using Microsoft.UI.Xaml;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
@@ -46,18 +47,18 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
|
||||
if (AppSettings.LocalMediaFolders.Any(x => Path.GetFullPath(x.Path).TrimEnd(Path.DirectorySeparatorChar).Equals(normalizedPath.TrimEnd(Path.DirectorySeparatorChar), StringComparison.OrdinalIgnoreCase)))
|
||||
{
|
||||
DevWinUI.Growl.Warning(_resourceService.GetLocalizedString("SettingsPagePathExistedInfo"));
|
||||
ToastHelper.ShowToast("SettingsPagePathExistedInfo", null, InfoBarSeverity.Warning);
|
||||
}
|
||||
else if (AppSettings.LocalMediaFolders.Any(item => normalizedPath.StartsWith(Path.GetFullPath(item.Path).TrimEnd(Path.DirectorySeparatorChar) + Path.DirectorySeparatorChar, StringComparison.OrdinalIgnoreCase)))
|
||||
{
|
||||
// 添加的文件夹是现有文件夹的子文件夹
|
||||
DevWinUI.Growl.Warning(_resourceService.GetLocalizedString("SettingsPagePathBeIncludedInfo"));
|
||||
ToastHelper.ShowToast("SettingsPagePathBeIncludedInfo", null, InfoBarSeverity.Warning);
|
||||
}
|
||||
else if (AppSettings.LocalMediaFolders.Any(item => Path.GetFullPath(item.Path).TrimEnd(Path.DirectorySeparatorChar).StartsWith(normalizedPath, StringComparison.OrdinalIgnoreCase))
|
||||
)
|
||||
{
|
||||
// 添加的文件夹是现有文件夹的父文件夹
|
||||
DevWinUI.Growl.Warning(_resourceService.GetLocalizedString("SettingsPagePathIncludingOthersInfo"));
|
||||
ToastHelper.ShowToast("SettingsPagePathIncludingOthersInfo", null, InfoBarSeverity.Warning);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using ATL;
|
||||
using BetterLyrics.WinUI3.Collections;
|
||||
using BetterLyrics.WinUI3.Constants;
|
||||
using BetterLyrics.WinUI3.Enums;
|
||||
using BetterLyrics.WinUI3.Extensions;
|
||||
using BetterLyrics.WinUI3.Helper;
|
||||
@@ -14,6 +15,7 @@ using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
using CommunityToolkit.WinUI;
|
||||
using Microsoft.UI.Dispatching;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
@@ -350,7 +352,7 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
else
|
||||
{
|
||||
_playlistTracks = [];
|
||||
DevWinUI.Growl.Error(_resourceService.GetLocalizedString("PlaylistViewFailed"), path);
|
||||
ToastHelper.ShowToast("PlaylistViewFailed", path, InfoBarSeverity.Success);
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -439,15 +441,19 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
else
|
||||
{
|
||||
var track = playQueueItem.Track;
|
||||
|
||||
var updater = _smtc.DisplayUpdater;
|
||||
updater.ClearAll();
|
||||
|
||||
_smtc.IsEnabled = true;
|
||||
_mediaPlayer.Source = MediaSource.CreateFromUri(new Uri(track.Path));
|
||||
updater.AppMediaId = Package.Current.Id.FullName;
|
||||
|
||||
var storageFile = await StorageFile.GetFileFromPathAsync(track.Path);
|
||||
|
||||
await updater.CopyFromFileAsync(MediaPlaybackType.Music, storageFile);
|
||||
updater.AppMediaId = Package.Current.Id.FullName;
|
||||
updater.MusicProperties.AlbumTitle = track.Album;
|
||||
updater.MusicProperties.Genres.Add($"FILENAME-{Path.GetFileNameWithoutExtension(track.Path)}");
|
||||
updater.MusicProperties.Genres.Add($"{ExtendedGenreFiled.FileName}{Path.GetFileNameWithoutExtension(track.Path)}");
|
||||
updater.Update();
|
||||
}
|
||||
}
|
||||
@@ -489,7 +495,7 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
if (file != null)
|
||||
{
|
||||
AddFileToStarredPlaylists(file);
|
||||
DevWinUI.Growl.Success(_resourceService.GetLocalizedString("CreatePlaylistSuccessfully"), file.Path);
|
||||
ToastHelper.ShowToast("CreatePlaylistSuccessfully", file.Path, InfoBarSeverity.Success);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -501,7 +507,7 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
if (file != null)
|
||||
{
|
||||
AddFileToStarredPlaylists(file);
|
||||
DevWinUI.Growl.Success(_resourceService.GetLocalizedString("ImportPlaylistSuccessfully"), file.Path);
|
||||
ToastHelper.ShowToast("ImportPlaylistSuccessfully", file.Path, InfoBarSeverity.Success);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
using Hqub.Lastfm.Entities;
|
||||
using Microsoft.UI.Dispatching;
|
||||
using Microsoft.UI.Xaml.Controls;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
@@ -110,14 +111,14 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
"Hello, world!", AppSettings.TranslationSettings.SelectedTargetLanguageCode, new System.Threading.CancellationToken());
|
||||
_dispatcherQueue.TryEnqueue(DispatcherQueuePriority.Low, () =>
|
||||
{
|
||||
DevWinUI.Growl.Success(_resourceService.GetLocalizedString("SettingsPageServerTestSuccessInfo"));
|
||||
ToastHelper.ShowToast("SettingsPageServerTestSuccessInfo", null, InfoBarSeverity.Success);
|
||||
});
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
_dispatcherQueue.TryEnqueue(DispatcherQueuePriority.Low, () =>
|
||||
{
|
||||
DevWinUI.Growl.Error(_resourceService.GetLocalizedString("SettingsPageServerTestFailedInfo"));
|
||||
ToastHelper.ShowToast("SettingsPageServerTestFailedInfo", null, InfoBarSeverity.Error);
|
||||
});
|
||||
}
|
||||
_dispatcherQueue.TryEnqueue(DispatcherQueuePriority.Low, () =>
|
||||
@@ -156,11 +157,11 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
{
|
||||
if (testResult)
|
||||
{
|
||||
DevWinUI.Growl.Success(_resourceService.GetLocalizedString("SettingsPageServerTestSuccessInfo"));
|
||||
ToastHelper.ShowToast("SettingsPageServerTestSuccessInfo", null, InfoBarSeverity.Success);
|
||||
}
|
||||
else
|
||||
{
|
||||
DevWinUI.Growl.Error(_resourceService.GetLocalizedString("SettingsPageServerTestFailedInfo"));
|
||||
ToastHelper.ShowToast("SettingsPageServerTestFailedInfo", null, InfoBarSeverity.Error);
|
||||
}
|
||||
IsLXMusicServerTesting = false;
|
||||
});
|
||||
|
||||
@@ -27,13 +27,6 @@
|
||||
<!-- Lyrics area -->
|
||||
<renderer:LyricsRenderer />
|
||||
|
||||
<ScrollViewer HorizontalAlignment="Center" VerticalScrollBarVisibility="Hidden">
|
||||
<StackPanel
|
||||
Margin="40"
|
||||
VerticalAlignment="Bottom"
|
||||
dev:Growl.GrowlParent="True" />
|
||||
</ScrollViewer>
|
||||
|
||||
<!-- Bottom command area -->
|
||||
<Grid
|
||||
x:Name="BottomCommandGrid"
|
||||
|
||||
@@ -65,7 +65,7 @@
|
||||
Glyph=}"
|
||||
Style="{StaticResource TitleBarButtonStyle}">
|
||||
<ToolTipService.ToolTip>
|
||||
<TextBlock x:Uid="HostWindowSettingsButtonToolTip" />
|
||||
<ToolTip x:Uid="HostWindowSettingsButtonToolTip" />
|
||||
</ToolTipService.ToolTip>
|
||||
</Button>
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:dev="using:DevWinUI"
|
||||
xmlns:ext="using:BetterLyrics.WinUI3.Extensions"
|
||||
xmlns:helper="using:BetterLyrics.WinUI3.Helper"
|
||||
xmlns:interactivity="using:Microsoft.Xaml.Interactivity"
|
||||
xmlns:labs="using:CommunityToolkit.Labs.WinUI"
|
||||
xmlns:local="using:BetterLyrics.WinUI3.Views"
|
||||
@@ -15,6 +16,7 @@
|
||||
xmlns:media="using:CommunityToolkit.WinUI.Media"
|
||||
xmlns:models="using:BetterLyrics.WinUI3.Models"
|
||||
xmlns:muxm="using:Microsoft.UI.Xaml.Media"
|
||||
xmlns:uc="using:BetterLyrics.WinUI3.Controls"
|
||||
xmlns:ui="using:CommunityToolkit.WinUI"
|
||||
Loaded="Page_Loaded"
|
||||
Unloaded="Page_Unloaded"
|
||||
@@ -45,7 +47,11 @@
|
||||
ShouldConstrainToRootBounds="False">
|
||||
<ScrollViewer MaxWidth="300" MaxHeight="600">
|
||||
<Grid>
|
||||
<controls:OpacityMaskView MaxHeight="300" VerticalAlignment="Top">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
</Grid.RowDefinitions>
|
||||
<controls:OpacityMaskView Grid.Row="0" MaxHeight="300">
|
||||
<controls:OpacityMaskView.OpacityMask>
|
||||
<Rectangle>
|
||||
<Rectangle.Fill>
|
||||
@@ -56,117 +62,27 @@
|
||||
</Rectangle.Fill>
|
||||
</Rectangle>
|
||||
</controls:OpacityMaskView.OpacityMask>
|
||||
<FlipView ItemsSource="{x:Bind ViewModel.TrackRightTapped.EmbeddedPictures, Mode=OneWay}">
|
||||
<FlipView.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<Image Source="{Binding PictureData, Converter={StaticResource ByteArrayToImageConverter}}" />
|
||||
</DataTemplate>
|
||||
</FlipView.ItemTemplate>
|
||||
</FlipView>
|
||||
<Image Source="{x:Bind ViewModel.TrackRightTapped.EmbeddedPictures, Mode=OneWay, Converter={StaticResource PictureInfosToImageSourceConverter}}" Stretch="Uniform" />
|
||||
</controls:OpacityMaskView>
|
||||
<StackPanel
|
||||
Margin="0,200,0,0"
|
||||
Grid.Row="1"
|
||||
Padding="12"
|
||||
Spacing="12">
|
||||
<StackPanel>
|
||||
<TextBlock x:Uid="MusicGalleryPageFileInfoTitle" />
|
||||
<TextBlock
|
||||
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
|
||||
IsTextSelectionEnabled="True"
|
||||
Text="{x:Bind ViewModel.TrackRightTapped.Title, Mode=OneWay}"
|
||||
TextWrapping="Wrap" />
|
||||
</StackPanel>
|
||||
<StackPanel>
|
||||
<TextBlock x:Uid="MusicGalleryPageFileArtist" />
|
||||
<TextBlock
|
||||
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
|
||||
IsTextSelectionEnabled="True"
|
||||
Text="{x:Bind ViewModel.TrackRightTapped.Artist, Mode=OneWay}"
|
||||
TextWrapping="Wrap" />
|
||||
</StackPanel>
|
||||
<StackPanel>
|
||||
<TextBlock x:Uid="MusicGalleryPageFileAlbum" />
|
||||
<TextBlock
|
||||
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
|
||||
IsTextSelectionEnabled="True"
|
||||
Text="{x:Bind ViewModel.TrackRightTapped.Album, Mode=OneWay}"
|
||||
TextWrapping="Wrap" />
|
||||
</StackPanel>
|
||||
<StackPanel>
|
||||
<TextBlock x:Uid="MusicGalleryPageFileInfoYear" />
|
||||
<TextBlock
|
||||
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
|
||||
IsTextSelectionEnabled="True"
|
||||
Text="{x:Bind ViewModel.TrackRightTapped.Year, Mode=OneWay}"
|
||||
TextWrapping="Wrap" />
|
||||
</StackPanel>
|
||||
<StackPanel>
|
||||
<TextBlock x:Uid="MusicGalleryPageFileInfoDuration" />
|
||||
<TextBlock
|
||||
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
|
||||
IsTextSelectionEnabled="True"
|
||||
Text="{x:Bind ViewModel.TrackRightTapped.Duration, Converter={StaticResource SecondsToFormattedTimeConverter}, Mode=OneWay}"
|
||||
TextWrapping="Wrap" />
|
||||
</StackPanel>
|
||||
<StackPanel>
|
||||
<TextBlock x:Uid="MusicGalleryPageFileInfoBitrate" TextWrapping="Wrap" />
|
||||
<TextBlock
|
||||
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
|
||||
IsTextSelectionEnabled="True"
|
||||
Text="{x:Bind ViewModel.TrackRightTapped.Bitrate, Mode=OneWay}"
|
||||
TextWrapping="Wrap" />
|
||||
</StackPanel>
|
||||
<StackPanel>
|
||||
<TextBlock x:Uid="MusicGalleryPageFileInfoSampleRate" />
|
||||
<TextBlock
|
||||
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
|
||||
IsTextSelectionEnabled="True"
|
||||
Text="{x:Bind ViewModel.TrackRightTapped.SampleRate, Mode=OneWay}"
|
||||
TextWrapping="Wrap" />
|
||||
</StackPanel>
|
||||
<StackPanel>
|
||||
<TextBlock x:Uid="MusicGalleryPageFileInfoBitDepth" />
|
||||
<TextBlock
|
||||
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
|
||||
IsTextSelectionEnabled="True"
|
||||
Text="{x:Bind ViewModel.TrackRightTapped.BitDepth, Mode=OneWay}"
|
||||
TextWrapping="Wrap" />
|
||||
</StackPanel>
|
||||
<StackPanel>
|
||||
<TextBlock x:Uid="MusicGalleryPageFileInfoFormat" />
|
||||
<TextBlock
|
||||
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
|
||||
IsTextSelectionEnabled="True"
|
||||
Text="{x:Bind ViewModel.TrackRightTapped.AudioFormat.Name, Mode=OneWay}"
|
||||
TextWrapping="Wrap" />
|
||||
</StackPanel>
|
||||
<StackPanel>
|
||||
<TextBlock x:Uid="MusicGalleryPageFileInfoEncoder" />
|
||||
<TextBlock
|
||||
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
|
||||
IsTextSelectionEnabled="True"
|
||||
Text="{x:Bind ViewModel.TrackRightTapped.Encoder, Mode=OneWay}"
|
||||
TextWrapping="Wrap" />
|
||||
</StackPanel>
|
||||
<StackPanel>
|
||||
<TextBlock x:Uid="MusicGalleryPageFileInfoPath" />
|
||||
<HyperlinkButton Padding="0" Click="SongPathHyperlinkButton_Click">
|
||||
<HyperlinkButton.Content>
|
||||
<TextBlock
|
||||
IsTextSelectionEnabled="True"
|
||||
Text="{x:Bind ViewModel.TrackRightTapped.Path, Mode=OneWay}"
|
||||
TextWrapping="Wrap" />
|
||||
</HyperlinkButton.Content>
|
||||
</HyperlinkButton>
|
||||
</StackPanel>
|
||||
<StackPanel>
|
||||
<TextBlock x:Uid="MusicGalleryPageFileInfoLyrics" />
|
||||
<TextBlock
|
||||
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
|
||||
IsTextSelectionEnabled="True"
|
||||
Text="{x:Bind ViewModel.TrackRightTapped, Converter={StaticResource TrackToLyricsConverter}, Mode=OneWay}"
|
||||
TextWrapping="Wrap" />
|
||||
</StackPanel>
|
||||
Spacing="6">
|
||||
<uc:PropertyRow x:Uid="MusicGalleryPageFileInfoTitle" Value="{x:Bind ViewModel.TrackRightTapped.Title, Mode=OneWay}" />
|
||||
<uc:PropertyRow x:Uid="MusicGalleryPageFileArtist" Value="{x:Bind ViewModel.TrackRightTapped.Artist, Mode=OneWay}" />
|
||||
<uc:PropertyRow x:Uid="MusicGalleryPageFileAlbum" Value="{x:Bind ViewModel.TrackRightTapped.Album, Mode=OneWay}" />
|
||||
<uc:PropertyRow x:Uid="MusicGalleryPageFileInfoYear" Value="{x:Bind ViewModel.TrackRightTapped.Year, Mode=OneWay}" />
|
||||
<uc:PropertyRow x:Uid="MusicGalleryPageFileInfoDuration" Value="{x:Bind ViewModel.TrackRightTapped.Duration, Converter={StaticResource SecondsToFormattedTimeConverter}, Mode=OneWay}" />
|
||||
<uc:PropertyRow x:Uid="MusicGalleryPageFileInfoBitrate" Value="{x:Bind ViewModel.TrackRightTapped.Bitrate, Mode=OneWay}" />
|
||||
<uc:PropertyRow x:Uid="MusicGalleryPageFileInfoSampleRate" Value="{x:Bind ViewModel.TrackRightTapped.SampleRate, Mode=OneWay}" />
|
||||
<uc:PropertyRow x:Uid="MusicGalleryPageFileInfoBitDepth" Value="{x:Bind ViewModel.TrackRightTapped.BitDepth, Mode=OneWay}" />
|
||||
<uc:PropertyRow x:Uid="MusicGalleryPageFileInfoFormat" Value="{x:Bind ViewModel.TrackRightTapped.AudioFormat.Name, Mode=OneWay}" />
|
||||
<uc:PropertyRow x:Uid="MusicGalleryPageFileInfoEncoder" Value="{x:Bind ViewModel.TrackRightTapped.Encoder, Mode=OneWay}" />
|
||||
<uc:PropertyRow
|
||||
x:Uid="MusicGalleryPageFileInfoPath"
|
||||
Link="{x:Bind ViewModel.TrackRightTapped.Path, Mode=OneWay}"
|
||||
Value="{x:Bind ViewModel.TrackRightTapped.Path, Mode=OneWay}" />
|
||||
<uc:PropertyRow x:Uid="MusicGalleryPageFileInfoLyrics" Value="{x:Bind ViewModel.TrackRightTapped, Converter={StaticResource TrackToLyricsConverter}, Mode=OneWay}" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</ScrollViewer>
|
||||
@@ -482,29 +398,29 @@
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Grid Grid.Column="0" CornerRadius="6">
|
||||
<Image Source="{Binding EmbeddedPictures[0].PictureData, Mode=OneWay, Converter={StaticResource ByteArrayToImageConverter}}" Stretch="Uniform" />
|
||||
<Image Source="{x:Bind EmbeddedPictures, Mode=OneWay, Converter={StaticResource PictureInfosToImageSourceConverter}}" Stretch="Uniform" />
|
||||
</Grid>
|
||||
|
||||
<!-- 基本信息 -->
|
||||
<Grid Grid.Column="1">
|
||||
<StackPanel VerticalAlignment="Center" Spacing="6">
|
||||
<TextBlock Text="{Binding Title}" TextWrapping="Wrap" />
|
||||
<TextBlock Text="{x:Bind Title}" TextWrapping="Wrap" />
|
||||
<StackPanel Orientation="Horizontal" Spacing="6">
|
||||
<Grid Background="{ThemeResource AccentAcrylicBackgroundFillColorBaseBrush}" CornerRadius="4">
|
||||
<TextBlock
|
||||
Margin="4,2"
|
||||
FontSize="12"
|
||||
Text="{Binding AudioFormat.ShortName}" />
|
||||
Text="{x:Bind AudioFormat.ShortName}" />
|
||||
</Grid>
|
||||
<HyperlinkButton Padding="0" Click="ArtistHyperlibkButton_Click">
|
||||
<TextBlock Text="{Binding Artist}" TextWrapping="Wrap" />
|
||||
<TextBlock Text="{x:Bind Artist}" TextWrapping="Wrap" />
|
||||
</HyperlinkButton>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
|
||||
<HyperlinkButton Grid.Column="2" Click="AlbumHyperlibkButton_Click">
|
||||
<TextBlock Text="{Binding Album}" TextWrapping="Wrap" />
|
||||
<TextBlock Text="{x:Bind Album}" TextWrapping="Wrap" />
|
||||
</HyperlinkButton>
|
||||
|
||||
<!-- 年份 -->
|
||||
@@ -512,7 +428,7 @@
|
||||
Grid.Column="3"
|
||||
VerticalAlignment="Center"
|
||||
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
|
||||
Text="{Binding Year}"
|
||||
Text="{x:Bind Year}"
|
||||
TextWrapping="Wrap" />
|
||||
|
||||
<!-- 歌曲时长 -->
|
||||
@@ -520,7 +436,7 @@
|
||||
Grid.Column="4"
|
||||
VerticalAlignment="Center"
|
||||
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
|
||||
Text="{Binding Duration, Converter={StaticResource SecondsToFormattedTimeConverter}}"
|
||||
Text="{x:Bind Duration, Converter={StaticResource SecondsToFormattedTimeConverter}}"
|
||||
TextWrapping="Wrap" />
|
||||
|
||||
<!-- 歌曲时长 -->
|
||||
@@ -528,7 +444,7 @@
|
||||
Grid.Column="5"
|
||||
VerticalAlignment="Center"
|
||||
Click="PathHyperlibkButton_Click"
|
||||
Content="{Binding Path, Converter={StaticResource PathToParentFolderConverter}}" />
|
||||
Content="{x:Bind Path, Converter={StaticResource PathToParentFolderConverter}}" />
|
||||
|
||||
<Button
|
||||
Grid.Column="6"
|
||||
@@ -554,7 +470,7 @@
|
||||
<TextBlock
|
||||
AutomationProperties.AccessibilityView="Raw"
|
||||
Style="{ThemeResource SubtitleTextBlockStyle}"
|
||||
Text="{Binding}" />
|
||||
Text="{x:Bind}" />
|
||||
</Border>
|
||||
</DataTemplate>
|
||||
</GroupStyle.HeaderTemplate>
|
||||
@@ -648,13 +564,11 @@
|
||||
Style="{StaticResource GhostButtonStyle}">
|
||||
<ToolTipService.ToolTip>
|
||||
<ToolTip>
|
||||
<ToolTip.Content>
|
||||
<Grid>
|
||||
<TextBlock x:Name="PlaybackRepeatAllHint" x:Uid="MusicGalleryPageQueueLoop" />
|
||||
<TextBlock x:Name="PlaybackRepeatOneHint" x:Uid="MusicGalleryPageSingleLoop" />
|
||||
<TextBlock x:Name="PlaybackShuffleHint" x:Uid="MusicGalleryPageQueueRandom" />
|
||||
</Grid>
|
||||
</ToolTip.Content>
|
||||
<Grid>
|
||||
<TextBlock x:Name="PlaybackRepeatAllHint" x:Uid="MusicGalleryPageQueueLoop" />
|
||||
<TextBlock x:Name="PlaybackRepeatOneHint" x:Uid="MusicGalleryPageSingleLoop" />
|
||||
<TextBlock x:Name="PlaybackShuffleHint" x:Uid="MusicGalleryPageQueueRandom" />
|
||||
</Grid>
|
||||
</ToolTip>
|
||||
</ToolTipService.ToolTip>
|
||||
<Button.Content>
|
||||
@@ -806,13 +720,6 @@
|
||||
<ProgressRing IsActive="{x:Bind ViewModel.IsDataLoading, Mode=OneWay}" />
|
||||
</Grid>
|
||||
|
||||
<ScrollViewer HorizontalAlignment="Center" VerticalScrollBarVisibility="Hidden">
|
||||
<StackPanel
|
||||
Margin="20"
|
||||
VerticalAlignment="Bottom"
|
||||
dev:Growl.GrowlParent="True" />
|
||||
</ScrollViewer>
|
||||
|
||||
<VisualStateManager.VisualStateGroups>
|
||||
<VisualStateGroup x:Name="PlaybackOrderState">
|
||||
<VisualState x:Name="RepeatAll">
|
||||
|
||||
@@ -244,11 +244,11 @@ namespace BetterLyrics.WinUI3.Views
|
||||
}
|
||||
}
|
||||
File.WriteAllText(path, content);
|
||||
DevWinUI.Growl.Success(_resourceService.GetLocalizedString("TracksAddToPlaylistSuccessfully"), path);
|
||||
ToastHelper.ShowToast("TracksAddToPlaylistSuccessfully", null, InfoBarSeverity.Success);
|
||||
}
|
||||
else
|
||||
{
|
||||
DevWinUI.Growl.Error(_resourceService.GetLocalizedString("TracksAddToPlaylistFailed"), path);
|
||||
ToastHelper.ShowToast("TracksAddToPlaylistFailed", null, InfoBarSeverity.Error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="using:BetterLyrics.WinUI3.Views"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:uc="using:BetterLyrics.WinUI3.Controls"
|
||||
xmlns:ui="using:CommunityToolkit.WinUI"
|
||||
mc:Ignorable="d">
|
||||
|
||||
@@ -22,7 +23,7 @@
|
||||
Glyph=}"
|
||||
Style="{StaticResource TitleBarButtonStyle}">
|
||||
<ToolTipService.ToolTip>
|
||||
<TextBlock x:Uid="SystemTrayLyrics" />
|
||||
<ToolTip x:Uid="SettingsPageLyricsWindowToolTip" />
|
||||
</ToolTipService.ToolTip>
|
||||
</Button>
|
||||
<Button
|
||||
@@ -33,7 +34,7 @@
|
||||
Glyph=}"
|
||||
Style="{StaticResource TitleBarButtonStyle}">
|
||||
<ToolTipService.ToolTip>
|
||||
<TextBlock x:Uid="HostWindowSettingsButtonToolTip" />
|
||||
<ToolTip x:Uid="HostWindowSettingsButtonToolTip" />
|
||||
</ToolTipService.ToolTip>
|
||||
</Button>
|
||||
</StackPanel>
|
||||
|
||||
@@ -94,12 +94,5 @@
|
||||
</controls:SwitchPresenter>
|
||||
</NavigationView>
|
||||
|
||||
<ScrollViewer HorizontalAlignment="Center" VerticalScrollBarVisibility="Hidden">
|
||||
<StackPanel
|
||||
Margin="20"
|
||||
VerticalAlignment="Bottom"
|
||||
dev:Growl.GrowlParent="True" />
|
||||
</ScrollViewer>
|
||||
|
||||
</Grid>
|
||||
</Page>
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="using:BetterLyrics.WinUI3.Views"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:uc="using:BetterLyrics.WinUI3.Controls"
|
||||
xmlns:ui="using:CommunityToolkit.WinUI"
|
||||
mc:Ignorable="d">
|
||||
|
||||
@@ -21,7 +22,7 @@
|
||||
Glyph=}"
|
||||
Style="{StaticResource TitleBarButtonStyle}">
|
||||
<ToolTipService.ToolTip>
|
||||
<TextBlock x:Uid="SystemTrayLyrics" />
|
||||
<ToolTip x:Uid="SettingsPageLyricsWindowToolTip" />
|
||||
</ToolTipService.ToolTip>
|
||||
</Button>
|
||||
<Button
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
<UseWinUI>true</UseWinUI>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.26100.6901" />
|
||||
<PackageReference Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.26100.7175" />
|
||||
<PackageReference Include="Microsoft.WindowsAppSDK" Version="1.8.251106002" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ BetterLyrics
|
||||
|
||||
<div align="center">
|
||||
|
||||
[操作指南](https://jayfunc.blog/work/betterlyrics-cn) | [隐私政策](PrivacyPolicy.CN.md) | [服务协议](TermsofService.CN.md)
|
||||
[使用指南](https://github.com/jayfunc/BetterLyrics/wiki/使用指南) | [隐私政策](PrivacyPolicy.CN.md) | [服务协议](TermsofService.CN.md)
|
||||
|
||||
</div>
|
||||
|
||||
@@ -75,7 +75,7 @@ BetterLyrics
|
||||
|
||||
- 🎶 **支持众多音乐播放器**
|
||||
|
||||
- 点击 [此处](https://jayfunc.blog/work/betterlyrics-cn#yi-zhi-zhi-chi-de-yin-le-bo-fang-qi-pei-zhi-zhi-nan) 查看详细信息
|
||||
- 点击 [此处](https://github.com/jayfunc/BetterLyrics/wiki/使用指南#已知支持的音乐播放器配置指南) 查看详细信息
|
||||
|
||||
- 🪟 **多种显示模式**
|
||||
- **标准模式**
|
||||
|
||||
@@ -14,7 +14,7 @@ BetterLyrics
|
||||
|
||||
<div align="center">
|
||||
|
||||
[Wiki](https://jayfunc.blog/work/betterlyrics) | [Privacy Policy](PrivacyPolicy.md) | [Terms of Service](TermsofService.md)
|
||||
[User Guide](https://github.com/jayfunc/BetterLyrics/wiki/User-Guide) | [Privacy Policy](PrivacyPolicy.md) | [Terms of Service](TermsofService.md)
|
||||
|
||||
</div>
|
||||
|
||||
@@ -81,7 +81,7 @@ Check out the article: [BetterLyrics – An immersive and smooth lyrics display
|
||||
|
||||
- 🎶 **Multiple Music Players Supported**
|
||||
|
||||
- Check it out [here](https://jayfunc.blog/work/betterlyrics#known-supported-music-players-configuration-guidance)) for detailed info
|
||||
- Check it out [here](https://github.com/jayfunc/BetterLyrics/wiki/User-Guide#known-supported-music-players-configuration-guide) for detailed info
|
||||
|
||||
- 🪟 **Multiple Display Modes**
|
||||
- **Standard Mode**
|
||||
|
||||
Reference in New Issue
Block a user