Compare commits

...

13 Commits

Author SHA1 Message Date
Zhe Fang
7fbc8fbfe7 Merge branch 'dev' of https://github.com/jayfunc/BetterLyrics into dev 2025-09-02 07:38:47 -04:00
Zhe Fang
4a00bb2ddf 更新版本并添加对 Apple Music 的支持
- 将 `Package.appxmanifest` 的版本号更新为 `1.0.71.0`。
- 移除 `BetterLyrics.WinUI3.csproj` 中对 `TinyPinyin.Net` 的引用。
- 在 `PlaybackSettingsControl.xaml` 中更新目标语言选择,移除中文选项并添加多个新语言。
- 新增 Apple Music 令牌输入框和按钮,允许用户保存令牌。
- 在 `LyricsSearchProviderToDisplayNameConverter.cs` 和 `TranslationSearchProviderToDisplayNameConverter.cs` 中添加对 Apple Music 的支持。
- 在 `LyricsSearchProvider.cs` 中新增 `AppleMusic` 作为歌词搜索提供者,并添加相关缓存目录和格式。
- 更新 `LanguageHelper.cs` 中的目标语言列表。
- 将 `TranslationSettings.cs` 中的 `SelectedTargetLanguageIndex` 属性更改为 `SelectedTargetLanguageCode`。
- 在 `LyricsSearchService.cs` 中添加 Apple Music 的歌词搜索功能。
- 更新 `MediaSessionsService.cs` 中的翻译和歌词更新逻辑。
- 移除 `SettingsPageViewModel.cs` 中的库信息支持,添加对媒体会话服务的引用。
- 新增 `AppleMusic.cs` 文件,包含与 Apple Music API 交互的逻辑。
2025-09-02 07:38:45 -04:00
Zhe Fang
7472aa048f 更新 README.md 2025-08-27 22:07:14 -04:00
Zhe Fang
49b0f7a692 Merge branch 'dev' of https://github.com/jayfunc/BetterLyrics into dev 2025-08-27 08:28:44 -04:00
Zhe Fang
4d0602ebef 更新版本号并优化歌词窗口边界计算
在 `Package.appxmanifest` 文件中,将应用程序版本号从 `1.0.66.0` 更新为 `1.0.67.0`。
在 `LiveStates.cs` 文件中,修改了 `DemoLyricsWindowBounds` 的计算方式,以更准确地反映歌词窗口在监视器上的位置。
2025-08-27 08:28:42 -04:00
Zhe Fang
626395d93a Update instructions from 'Clone' to 'Fork' 2025-08-26 22:34:18 -04:00
Zhe Fang
0ab5602569 Revise translation contribution instructions
Updated translation instructions for contributors.
2025-08-26 16:50:34 -04:00
Zhe Fang
37a7528762 更新 FAQ.md 2025-08-25 08:56:13 -04:00
Zhe Fang
3ca391a509 更新 FAQ.md 2025-08-25 08:54:43 -04:00
Zhe Fang
f5e542d2f3 更新 How2Install.md 2025-08-25 08:53:10 -04:00
Zhe Fang
107bdf8bee 更新 README.md 2025-08-25 08:51:19 -04:00
Zhe Fang
3e9e56f5cc ignore ttml x-roman span 2025-08-25 07:12:24 -04:00
Zhe Fang
b0fd43ead5 fix input issue in search panel; fix display issue in lyrics window manager 2025-08-24 16:29:28 -04:00
37 changed files with 519 additions and 271 deletions

View File

@@ -12,7 +12,7 @@
<Identity
Name="37412.BetterLyrics"
Publisher="CN=E1428B0E-DC1D-4EA4-ACB1-4556569D5BA9"
Version="1.0.62.0" />
Version="1.0.71.0" />
<mp:PhoneIdentity PhoneProductId="ca4a4830-fc19-40d9-b823-53e2bff3d816" PhonePublisherId="00000000-0000-0000-0000-000000000000"/>

View File

@@ -32,6 +32,7 @@
<None Remove="Controls\PlaybackSettingsControl.xaml" />
<None Remove="Controls\ShortcutTextBox.xaml" />
<None Remove="Controls\SystemTray.xaml" />
<None Remove="Views\LyricsSearchWindow.xaml" />
<None Remove="Views\MusicGalleryPage.xaml" />
<None Remove="Views\MusicGalleryWindow.xaml" />
<None Remove="Views\SettingsWindow.xaml" />
@@ -80,7 +81,6 @@
<PackageReference Include="System.Drawing.Common" Version="9.0.8" />
<PackageReference Include="System.Text.Encoding.CodePages" Version="9.0.8" />
<PackageReference Include="TagLibSharp" Version="2.3.0" />
<PackageReference Include="TinyPinyin.Net" Version="1.0.2" />
<PackageReference Include="Ude.NetStandard" Version="1.2.0" />
<PackageReference Include="Vanara.PInvoke.CoreAudio" Version="4.1.6" />
<PackageReference Include="Vanara.PInvoke.DwmApi" Version="4.1.6" />
@@ -181,6 +181,11 @@
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
</ItemGroup>
<ItemGroup>
<Page Update="Views\LyricsSearchWindow.xaml">
<Generator>MSBuild:Compile</Generator>
</Page>
</ItemGroup>
<ItemGroup>
<Page Update="Controls\LyricsSearchControl.xaml">
<Generator>MSBuild:Compile</Generator>

View File

@@ -0,0 +1,13 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace BetterLyrics.WinUI3.Constants
{
public static class AppleMusic
{
public const string MediaUserTokenKey = "AppleMusicMediaUserToken";
}
}

View File

@@ -13,61 +13,61 @@
<Grid Padding="16" RowSpacing="6">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid Grid.Row="0" Margin="0,0,0,6">
<StackPanel Spacing="{StaticResource SettingsCardSpacing}">
<controls:SettingsExpander x:Uid="LyricsSearchControlSongInfoMapping" IsExpanded="True">
<controls:SettingsExpander.Items>
<controls:SettingsCard x:Uid="LyricsSearchControlTitle" Description="{x:Bind ViewModel.MappedSongSearchQuery.OriginalTitle, Mode=OneWay}">
<StackPanel Orientation="Horizontal" Spacing="6">
<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=&#xE777;}"
Style="{StaticResource GhostButtonStyle}" />
</StackPanel>
</controls:SettingsCard>
<controls:SettingsCard x:Uid="LyricsSearchControlArtist" Description="{x:Bind ViewModel.MappedSongSearchQuery.OriginalArtist, Mode=OneWay}">
<StackPanel Orientation="Horizontal" Spacing="6">
<TextBlock x:Uid="LyricsSearchControlMappedAs" VerticalAlignment="Center" />
<TextBox Text="{x:Bind ViewModel.MappedSongSearchQuery.MappedArtist, Mode=TwoWay}" TextWrapping="Wrap" />
<Button
VerticalAlignment="Center"
Command="{x:Bind ViewModel.ResetMappedArtistCommand}"
Content="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
FontSize=12,
Glyph=&#xE777;}"
Style="{StaticResource GhostButtonStyle}" />
</StackPanel>
</controls:SettingsCard>
<controls:SettingsCard>
<CheckBox x:Uid="LyricsSearchControlMarkAsPureMusic" IsChecked="{x:Bind ViewModel.MappedSongSearchQuery.IsMarkedAsPureMusic, Mode=TwoWay}" />
</controls:SettingsCard>
</controls:SettingsExpander.Items>
</controls:SettingsExpander>
<controls:SettingsCard x:Uid="LyricsSearchControlTargetSearchProvider">
<Button
x:Uid="LyricsSearchControlSearch"
Command="{x:Bind ViewModel.SearchCommand}"
Style="{StaticResource AccentButtonStyle}" />
</controls:SettingsCard>
</StackPanel>
</Grid>
<!-- 结果列表及原始歌词文件展示区域 -->
<Grid Grid.Row="1">
<Grid Grid.Row="0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="2*" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid Grid.Column="0" RowSpacing="12">
<Grid Grid.Column="0">
<StackPanel Spacing="{StaticResource SettingsCardSpacing}">
<controls:SettingsExpander x:Uid="LyricsSearchControlSongInfoMapping" IsExpanded="True">
<controls:SettingsExpander.Items>
<controls:SettingsCard x:Uid="LyricsSearchControlTitle" Description="{x:Bind ViewModel.MappedSongSearchQuery.OriginalTitle, Mode=OneWay}">
<StackPanel Orientation="Horizontal" Spacing="6">
<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=&#xE777;}"
Style="{StaticResource GhostButtonStyle}" />
</StackPanel>
</controls:SettingsCard>
<controls:SettingsCard x:Uid="LyricsSearchControlArtist" Description="{x:Bind ViewModel.MappedSongSearchQuery.OriginalArtist, Mode=OneWay}">
<StackPanel Orientation="Horizontal" Spacing="6">
<TextBlock x:Uid="LyricsSearchControlMappedAs" VerticalAlignment="Center" />
<TextBox Text="{x:Bind ViewModel.MappedSongSearchQuery.MappedArtist, Mode=TwoWay}" TextWrapping="Wrap" />
<Button
VerticalAlignment="Center"
Command="{x:Bind ViewModel.ResetMappedArtistCommand}"
Content="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
FontSize=12,
Glyph=&#xE777;}"
Style="{StaticResource GhostButtonStyle}" />
</StackPanel>
</controls:SettingsCard>
<controls:SettingsCard>
<CheckBox x:Uid="LyricsSearchControlMarkAsPureMusic" IsChecked="{x:Bind ViewModel.MappedSongSearchQuery.IsMarkedAsPureMusic, Mode=TwoWay}" />
</controls:SettingsCard>
</controls:SettingsExpander.Items>
</controls:SettingsExpander>
<controls:SettingsCard x:Uid="LyricsSearchControlTargetSearchProvider">
<Button
x:Uid="LyricsSearchControlSearch"
Command="{x:Bind ViewModel.SearchCommand}"
Style="{StaticResource AccentButtonStyle}" />
</controls:SettingsCard>
</StackPanel>
</Grid>
<Grid Grid.Column="1">
<ListView ItemsSource="{x:Bind ViewModel.LyricsSearchResults, Mode=OneWay}" SelectedItem="{x:Bind ViewModel.SelectedLyricsSearchResult, Mode=TwoWay}">
<ListView.ItemTemplate>
<DataTemplate>
@@ -144,7 +144,7 @@
ShowPaused="False"
Visibility="{x:Bind ViewModel.IsSearching, Converter={StaticResource BoolToVisibilityConverter}, Mode=OneWay}" />
</Grid>
<Grid Grid.Column="1">
<Grid Grid.Column="2">
<ListView ItemsSource="{x:Bind ViewModel.LyricsData.LyricsLines, Mode=OneWay}" SelectedItem="{x:Bind ViewModel.SelectedLyricsLine, Mode=TwoWay}">
<interactivity:Interaction.Behaviors>
<interactivity:DataTriggerBehavior
@@ -197,7 +197,7 @@
</StackPanel>
</Grid>
</Grid>
<Grid Grid.Row="2">
<Grid Grid.Row="1">
<RelativePanel>
<TextBlock
x:Uid="LyricsSearchControlHelp"

View File

@@ -232,37 +232,56 @@
<ToggleSwitch IsOn="{x:Bind ViewModel.AppSettings.TranslationSettings.IsTranslationEnabled, Mode=TwoWay}" />
<controls:SettingsExpander.Items>
<controls:SettingsCard x:Uid="SettingsPageTargetLanguage" IsEnabled="{x:Bind ViewModel.AppSettings.TranslationSettings.IsTranslationEnabled, Mode=OneWay}">
<ComboBox SelectedIndex="{x:Bind ViewModel.AppSettings.TranslationSettings.SelectedTargetLanguageIndex, Mode=TwoWay}">
<ComboBox SelectedIndex="{x:Bind ViewModel.SelectedTargetLanguageIndex, Mode=TwoWay}">
<ComboBoxItem Content="العربية" Tag="ar" />
<ComboBoxItem Content="Azərbaycan dili" Tag="az" />
<ComboBoxItem Content="中文" Tag="zh" />
<ComboBoxItem Content="Български" Tag="bg" />
<ComboBoxItem Content="বাংলা" Tag="bn" />
<ComboBoxItem Content="Català" Tag="ca" />
<ComboBoxItem Content="Čeština" Tag="cs" />
<ComboBoxItem Content="Dansk" Tag="da" />
<ComboBoxItem Content="Nederlands" Tag="nl" />
<ComboBoxItem Content="English" Tag="en" />
<ComboBoxItem Content="Esperanto" Tag="eo" />
<ComboBoxItem Content="Suomi" Tag="fi" />
<ComboBoxItem Content="Français" Tag="fr" />
<ComboBoxItem Content="Deutsch" Tag="de" />
<ComboBoxItem Content="Ελληνικά" Tag="el" />
<ComboBoxItem Content="English" Tag="en" />
<ComboBoxItem Content="Esperanto" Tag="eo" />
<ComboBoxItem Content="Español" Tag="es" />
<ComboBoxItem Content="Eesti" Tag="et" />
<ComboBoxItem Content="Euskara" Tag="eu" />
<ComboBoxItem Content="فارسی" Tag="fa" />
<ComboBoxItem Content="Suomi" Tag="fi" />
<ComboBoxItem Content="Français" Tag="fr" />
<ComboBoxItem Content="Gaeilge" Tag="ga" />
<ComboBoxItem Content="Galego" Tag="gl" />
<ComboBoxItem Content="עברית" Tag="he" />
<ComboBoxItem Content="हिन्दी" Tag="hi" />
<ComboBoxItem Content="Magyar" Tag="hu" />
<ComboBoxItem Content="Bahasa Indonesia" Tag="id" />
<ComboBoxItem Content="Gaeilge" Tag="ga" />
<ComboBoxItem Content="Italiano" Tag="it" />
<ComboBoxItem Content="日本語" Tag="ja" />
<ComboBoxItem Content="한국어" Tag="ko" />
<ComboBoxItem Content="فارسی" Tag="fa" />
<ComboBoxItem Content="Кыргызча" Tag="ky" />
<ComboBoxItem Content="Lietuvių" Tag="lt" />
<ComboBoxItem Content="Latviešu" Tag="lv" />
<ComboBoxItem Content="Bahasa Melayu" Tag="ms" />
<ComboBoxItem Content="Norsk bokmål" Tag="nb" />
<ComboBoxItem Content="Nederlands" Tag="nl" />
<ComboBoxItem Content="Português (Brasil)" Tag="pt-BR" />
<ComboBoxItem Content="Polski" Tag="pl" />
<ComboBoxItem Content="Português" Tag="pt" />
<ComboBoxItem Content="Română" Tag="ro" />
<ComboBoxItem Content="Русский" Tag="ru" />
<ComboBoxItem Content="Slovenčina" Tag="sk" />
<ComboBoxItem Content="Español" Tag="es" />
<ComboBoxItem Content="Slovenščina" Tag="sl" />
<ComboBoxItem Content="Shqip" Tag="sq" />
<ComboBoxItem Content="Српски" Tag="sr" />
<ComboBoxItem Content="Svenska" Tag="sv" />
<ComboBoxItem Content="ไทย" Tag="th" />
<ComboBoxItem Content="Filipino" Tag="tl" />
<ComboBoxItem Content="Türkçe" Tag="tr" />
<ComboBoxItem Content="Українська" Tag="uk" />
<ComboBoxItem Content="اردو" Tag="ur" />
<ComboBoxItem Content="Tiếng Việt" Tag="vi" />
<ComboBoxItem Content="中文" Tag="zh" />
</ComboBox>
</controls:SettingsCard>
<controls:SettingsCard x:Uid="SettingsPageTranslationConfig" IsEnabled="{x:Bind ViewModel.AppSettings.TranslationSettings.IsTranslationEnabled, Mode=OneWay}">
@@ -337,6 +356,26 @@
IsEnabled="{x:Bind ViewModel.IsLXMusicServerTesting, Converter={StaticResource BoolNegationConverter}, Mode=OneWay}" />
</StackPanel>
</controls:SettingsCard>
<!-- Apple Music token -->
<TextBlock Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" Text="media-user-token (for Apple Muisc)" />
<controls:SettingsCard
Background="{ThemeResource SystemFillColorCautionBackgroundBrush}"
Foreground="{ThemeResource SystemFillColorCautionBrush}"
Header="Use at your own risk">
<StackPanel Orientation="Horizontal" Spacing="6">
<TextBox
MaxWidth="250"
PlaceholderText="media-user-token"
Text="{x:Bind ViewModel.AppleMusicMediaUserToken, Mode=TwoWay}"
TextWrapping="Wrap" />
<Button
Command="{x:Bind ViewModel.SaveAppleMusicMediaUserTokenCommand}"
Content="{ui:FontIcon FontFamily={StaticResource IconFontFamily},
FontSize=12,
Glyph=&#xE8FB;}"
Style="{StaticResource GhostButtonStyle}" />
</StackPanel>
</controls:SettingsCard>
</StackPanel>
</Grid>
</ScrollViewer>

View File

@@ -19,6 +19,7 @@ namespace BetterLyrics.WinUI3.Converter
LyricsSearchProvider.Netease => "网易云音乐",
LyricsSearchProvider.Kugou => "酷狗音乐",
LyricsSearchProvider.AmllTtmlDb => "amll-ttml-db",
LyricsSearchProvider.AppleMusic => "Apple Music",
LyricsSearchProvider.LocalLrcFile => App.ResourceLoader!.GetString("LyricsSearchProviderLocalLrcFile"),
LyricsSearchProvider.LocalMusicFile => App.ResourceLoader!.GetString("LyricsSearchProviderLocalMusicFile"),
LyricsSearchProvider.LocalEslrcFile => App.ResourceLoader!.GetString("LyricsSearchProviderEslrcFile"),

View File

@@ -19,6 +19,7 @@ namespace BetterLyrics.WinUI3.Converter
TranslationSearchProvider.Netease => "网易云音乐",
TranslationSearchProvider.Kugou => "酷狗音乐",
TranslationSearchProvider.AmllTtmlDb => "amll-ttml-db",
TranslationSearchProvider.AppleMusic => "Apple Music",
TranslationSearchProvider.LocalLrcFile => App.ResourceLoader!.GetString("LyricsSearchProviderLocalLrcFile"),
TranslationSearchProvider.LocalMusicFile => App.ResourceLoader!.GetString("LyricsSearchProviderLocalMusicFile"),
TranslationSearchProvider.LocalEslrcFile => App.ResourceLoader!.GetString("LyricsSearchProviderEslrcFile"),

View File

@@ -15,6 +15,7 @@ namespace BetterLyrics.WinUI3.Enums
LocalLrcFile,
LocalEslrcFile,
LocalTtmlFile,
AppleMusic,
}
public static class LyricsSearchProviderExtensions
@@ -28,6 +29,7 @@ namespace BetterLyrics.WinUI3.Enums
LyricsSearchProvider.Netease => PathHelper.NeteaseLyricsCacheDirectory,
LyricsSearchProvider.Kugou => PathHelper.KugouLyricsCacheDirectory,
LyricsSearchProvider.AmllTtmlDb => PathHelper.AmllTtmlDbLyricsCacheDirectory,
LyricsSearchProvider.AppleMusic => PathHelper.AppleMusicCacheDirectory,
_ => throw new System.ArgumentOutOfRangeException(nameof(provider)),
};
}
@@ -41,6 +43,7 @@ namespace BetterLyrics.WinUI3.Enums
LyricsSearchProvider.Kugou => LyricsFormat.Krc,
LyricsSearchProvider.Netease => LyricsFormat.Lrc,
LyricsSearchProvider.AmllTtmlDb => LyricsFormat.Ttml,
LyricsSearchProvider.AppleMusic => LyricsFormat.Ttml,
LyricsSearchProvider.LocalLrcFile => LyricsFormat.Lrc,
LyricsSearchProvider.LocalEslrcFile => LyricsFormat.Eslrc,
LyricsSearchProvider.LocalTtmlFile => LyricsFormat.Ttml,
@@ -71,6 +74,7 @@ namespace BetterLyrics.WinUI3.Enums
LyricsSearchProvider.Kugou => TranslationSearchProvider.Kugou,
LyricsSearchProvider.Netease => TranslationSearchProvider.Netease,
LyricsSearchProvider.AmllTtmlDb => TranslationSearchProvider.AmllTtmlDb,
LyricsSearchProvider.AppleMusic => TranslationSearchProvider.AppleMusic,
LyricsSearchProvider.LocalMusicFile => TranslationSearchProvider.LocalMusicFile,
LyricsSearchProvider.LocalLrcFile => TranslationSearchProvider.LocalLrcFile,
LyricsSearchProvider.LocalEslrcFile => TranslationSearchProvider.LocalEslrcFile,

View File

@@ -13,6 +13,7 @@ namespace BetterLyrics.WinUI3.Enums
Netease,
LrcLib,
AmllTtmlDb,
AppleMusic,
LocalMusicFile,
LocalLrcFile,
LocalEslrcFile,

View File

@@ -0,0 +1,126 @@
using BetterLyrics.WinUI3.Constants;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Text;
using System.Text.Json;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
namespace BetterLyrics.WinUI3.Helper
{
public class AppleMusic
{
private readonly HttpClient _client;
private string _accessToken = "";
private string _storefront = "";
private string _language = "";
public AppleMusic()
{
_client = new HttpClient();
_client.DefaultRequestHeaders.Add("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36");
_client.DefaultRequestHeaders.Add("Accept", "application/json");
_client.DefaultRequestHeaders.Add("Origin", "https://music.apple.com");
_client.DefaultRequestHeaders.Add("Referer", "https://music.apple.com/");
}
public async Task<bool> InitAsync()
{
await GetAccessTokenAsync();
await SetMediaUserTokenAsync();
return
!string.IsNullOrEmpty(_accessToken) &&
!string.IsNullOrEmpty(PasswordVaultHelper.Get(Constants.App.AppName, Constants.AppleMusic.MediaUserTokenKey));
}
private async Task GetAccessTokenAsync()
{
var resp = await _client.GetStringAsync("https://music.apple.com/us/browse");
var jsMatch = Regex.Match(resp, "(?<=index)(.*?)(?=\\.js\")");
if (!jsMatch.Success) throw new Exception("Failed to find index.js");
var jsUrl = $"https://music.apple.com/assets/index{jsMatch.Value}.js";
var jsResp = await _client.GetStringAsync(jsUrl);
var tokenMatch = Regex.Match(jsResp, "(?=eyJh)(.*?)(?=\")");
if (!tokenMatch.Success) throw new Exception("Failed to find access token");
_accessToken = tokenMatch.Value;
_client.DefaultRequestHeaders.Remove("Authorization");
_client.DefaultRequestHeaders.Add("Authorization", $"Bearer {_accessToken}");
}
private async Task SetMediaUserTokenAsync()
{
_client.DefaultRequestHeaders.Remove("media-user-token");
_client.DefaultRequestHeaders.Add("media-user-token",
PasswordVaultHelper.Get(Constants.App.AppName, Constants.AppleMusic.MediaUserTokenKey));
var resp = await _client.GetStringAsync("https://amp-api.music.apple.com/v1/me/storefront");
var json = JsonSerializer.Deserialize(resp, Serialization.SourceGenerationContext.Default.JsonElement);
_storefront = json.GetProperty("data")[0].GetProperty("id").ToString();
_language = json.GetProperty("data")[0].GetProperty("attributes").GetProperty("defaultLanguageTag").ToString();
_client.DefaultRequestHeaders.Remove("Accept-Language");
_client.DefaultRequestHeaders.Add("Accept-Language", $"{_language},en;q=0.9");
}
public async Task<string?> GetLyricsAsync(string title, string artist)
{
string id = await SearchSongInfoAsync(artist, title);
var apiUrl = $"https://amp-api.music.apple.com/v1/catalog/{_storefront}/songs/{id}";
var url = apiUrl + $"?include[songs]=lyrics,syllable-lyrics&l={_language}";
var resp = await _client.GetStringAsync(url);
var json = JsonSerializer.Deserialize(resp, Serialization.SourceGenerationContext.Default.JsonElement);
var data = json.GetProperty("data");
if (data.GetArrayLength() == 0) return string.Empty;
var song = data[0];
if (!song.TryGetProperty("relationships", out var relationships))
return string.Empty;
if (relationships.TryGetProperty("syllable-lyrics", out var syllableLyrics) &&
syllableLyrics.GetProperty("data").GetArrayLength() > 0)
{
var syllableLyric = syllableLyrics.GetProperty("data")[0];
if (syllableLyric.TryGetProperty("attributes", out var attributes) &&
attributes.TryGetProperty("ttml", out var ttml))
{
string? raw = ttml.GetString();
if (raw != null && raw.Contains("begin=") && raw.Contains("end="))
{
return raw;
}
}
}
//if (relationships.TryGetProperty("lyrics", out var lyrics) &&
// lyrics.GetProperty("data").GetArrayLength() > 0)
//{
// var lyric = lyrics.GetProperty("data")[0];
// if (lyric.TryGetProperty("attributes", out var attributes) &&
// attributes.TryGetProperty("ttml", out var ttml))
// {
// return ttml.GetString();
// }
//}
return null;
}
private async Task<string> SearchSongInfoAsync(string artist, string title)
{
var query = $"{artist} {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);
var json = JsonSerializer.Deserialize(resp, Serialization.SourceGenerationContext.Default.JsonElement);
var results = json.GetProperty("results");
if (results.TryGetProperty("songs", out var songs) && songs.GetProperty("data").GetArrayLength() > 0)
{
var song = songs.GetProperty("data")[0];
return song.GetProperty("id").ToString();
}
return string.Empty;
}
}
}

View File

@@ -94,6 +94,11 @@ namespace BetterLyrics.WinUI3.Helper
return Color.FromArgb(alpha, color.R, color.G, color.B);
}
public static Color WithOpacity(this Color color, float opacity)
{
return Color.FromArgb((byte)(opacity * 255), color.R, color.G, color.B);
}
public static Color WithBrightness(this Color color, double brightness)
{
// 确保亮度因子在合理范围内

View File

@@ -1,14 +1,7 @@
using BetterLyrics.WinUI3.Helper;
using BetterLyrics.WinUI3.Services.SettingsService;
using CommunityToolkit.Mvvm.DependencyInjection;
using Lyricify.Lyrics.Helpers.General;
using NTextCat;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using TinyPinyin;
using Windows.Globalization;
namespace BetterLyrics.WinUI3.Services
@@ -18,38 +11,77 @@ namespace BetterLyrics.WinUI3.Services
private static readonly RankedLanguageIdentifierFactory _factory = new();
private static readonly RankedLanguageIdentifier _identifier;
public static List<Models.LanguageInfo> SupportedTargetLanguages =>
public static List<Models.LanguageInfo> SupportedTargetLanguages { get; set; } =
[
new Models.LanguageInfo("ar", "العربية"),
new Models.LanguageInfo("az", "Azərbaycan dili"),
new Models.LanguageInfo("zh", "中文"),
new Models.LanguageInfo("bg", "Български"),
new Models.LanguageInfo("bn", "বাংলা"),
new Models.LanguageInfo("ca", "Català"),
new Models.LanguageInfo("cs", "Čeština"),
new Models.LanguageInfo("da", "Dansk"),
new Models.LanguageInfo("nl", "Nederlands"),
new Models.LanguageInfo("de", "Deutsch"),
new Models.LanguageInfo("el", "Ελληνικά"),
new Models.LanguageInfo("en", "English"),
new Models.LanguageInfo("eo", "Esperanto"),
new Models.LanguageInfo("es", "Español"),
new Models.LanguageInfo("et", "Eesti"),
new Models.LanguageInfo("eu", "Euskara"),
new Models.LanguageInfo("fa", "فارسی"),
new Models.LanguageInfo("fi", "Suomi"),
new Models.LanguageInfo("fr", "Français"),
new Models.LanguageInfo("de", "Deutsch"),
new Models.LanguageInfo("el", "Ελληνικά"),
new Models.LanguageInfo("ga", "Gaeilge"),
new Models.LanguageInfo("gl", "Galego"),
new Models.LanguageInfo("he", "עברית"),
new Models.LanguageInfo("hi", "हिन्दी"),
new Models.LanguageInfo("hu", "Magyar"),
new Models.LanguageInfo("id", "Bahasa Indonesia"),
new Models.LanguageInfo("ga", "Gaeilge"),
new Models.LanguageInfo("it", "Italiano"),
new Models.LanguageInfo("ja", "日本語"),
new Models.LanguageInfo("ko", "한국어"),
new Models.LanguageInfo("fa", "فارسی"),
new Models.LanguageInfo("ky", "Кыргызча"),
new Models.LanguageInfo("lt", "Lietuvių"),
new Models.LanguageInfo("lv", "Latviešu"),
new Models.LanguageInfo("ms", "Bahasa Melayu"),
new Models.LanguageInfo("nb", "Norsk bokmål"),
new Models.LanguageInfo("nl", "Nederlands"),
new Models.LanguageInfo("pt-BR", "Português (Brasil)"),
new Models.LanguageInfo("pl", "Polski"),
new Models.LanguageInfo("pt", "Português"),
new Models.LanguageInfo("ro", "Română"),
new Models.LanguageInfo("ru", "Русский"),
new Models.LanguageInfo("sk", "Slovenčina"),
new Models.LanguageInfo("es", "Español"),
new Models.LanguageInfo("sl", "Slovenščina"),
new Models.LanguageInfo("sq", "Shqip"),
new Models.LanguageInfo("sr", "Српски"),
new Models.LanguageInfo("sv", "Svenska"),
new Models.LanguageInfo("th", "ไทย"),
new Models.LanguageInfo("tl", "Filipino"),
new Models.LanguageInfo("tr", "Türkçe"),
new Models.LanguageInfo("uk", "Українська"),
new Models.LanguageInfo("ur", "اردو"),
new Models.LanguageInfo("vi", "Tiếng Việt"),
new Models.LanguageInfo("zh", "中文"),
];
static LanguageHelper()
@@ -83,11 +115,17 @@ namespace BetterLyrics.WinUI3.Services
};
}
public static int GetDefaultTargetLanguageIndex()
public static string GetDefaultTargetLanguageCode()
{
int found = SupportedTargetLanguages.FindIndex(x => ApplicationLanguages.Languages.FirstOrDefault()?.Contains(x.Code) == true);
if (found == -1) found = 7; // 默认使用英语
return found;
var found = SupportedTargetLanguages.Find(x => ApplicationLanguages.Languages.FirstOrDefault()?.Contains(x.Code) == true);
if (found == null)
{
return "en";
}
else
{
return found.Code;
}
}
public static string GetOrderChar(string text)
@@ -97,9 +135,9 @@ namespace BetterLyrics.WinUI3.Services
if (char.IsLetter(c) && c < 128)
return char.ToUpper(c).ToString();
if (PinyinHelper.IsChinese(c))
if (Pinyin.Pinyin.Instance.IsHanzi(c.ToString()))
{
return PinyinHelper.GetPinyinInitials($"{c}");
return Pinyin.Pinyin.Instance.HanziToPinyin(c.ToString(), Pinyin.ManTone.Style.NORMAL).ToStr().ToUpper().FirstOrDefault().ToString();
}
return "#";
@@ -107,7 +145,7 @@ namespace BetterLyrics.WinUI3.Services
public static string ToRomaji(string text)
{
return string.Join(" ", RomajiConverter.Core.Helpers.RomajiHelper.SentenceToRomaji(text).Select(x=>x.Romaji));
return string.Join(" ", RomajiConverter.Core.Helpers.RomajiHelper.SentenceToRomaji(text).Select(x => x.Romaji));
}
}
}

View File

@@ -3,7 +3,6 @@
using BetterLyrics.WinUI3.Enums;
using BetterLyrics.WinUI3.Models;
using BetterLyrics.WinUI3.Services;
using Lyricify.Lyrics.Helpers.General;
using Lyricify.Lyrics.Models;
using System;
using System.Collections.Generic;
@@ -246,10 +245,11 @@ namespace BetterLyrics.WinUI3.Helper
int pStartMs = ParseTtmlTime(pBegin);
int pEndMs = ParseTtmlTime(pEnd);
// 只获取一级span且排除ttm:role="x-bg"span
// 只获取一级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-bg" &&
s.Attribute(XName.Get("role", "http://www.w3.org/ns/ttml#metadata"))?.Value != "x-roman")
.ToList();
// 原文和翻译分离

View File

@@ -46,6 +46,7 @@ namespace BetterLyrics.WinUI3.Helper
public static string QQLyricsCacheDirectory => Path.Combine(LyricsCacheDirectory, "qq");
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 AmllTtmlDbIndexPath => Path.Combine(LyricsCacheDirectory, "amll-ttml-db-index.json");
public static string AmllTtmlDbLastUpdatedPath => Path.Combine(LyricsCacheDirectory, "amll-ttml-db-last-updated.txt");
@@ -53,6 +54,7 @@ namespace BetterLyrics.WinUI3.Helper
public static string QQTranslationCacheDirectory => Path.Combine(TranslationCacheDirectory, "qq");
public static string NeteaseTranslationCacheDirectory => Path.Combine(TranslationCacheDirectory, "netease");
public static string KugouTranslationCacheDirectory => Path.Combine(TranslationCacheDirectory, "kugou");
public static string AlbumArtCacheDirectory => Path.Combine(CacheFolder, "album-art");
@@ -69,9 +71,11 @@ namespace BetterLyrics.WinUI3.Helper
Directory.CreateDirectory(KugouLyricsCacheDirectory);
Directory.CreateDirectory(NeteaseLyricsCacheDirectory);
Directory.CreateDirectory(AmllTtmlDbLyricsCacheDirectory);
Directory.CreateDirectory(AppleMusicCacheDirectory);
Directory.CreateDirectory(QQTranslationCacheDirectory);
Directory.CreateDirectory(NeteaseTranslationCacheDirectory);
Directory.CreateDirectory(KugouTranslationCacheDirectory);
Directory.CreateDirectory(iTunesAlbumArtCacheDirectory);
}

View File

@@ -59,6 +59,10 @@ namespace BetterLyrics.WinUI3.Helper
{
window = new MusicGalleryWindow();
}
else if (typeof(T) == typeof(LyricsSearchWindow))
{
window = new LyricsSearchWindow();
}
else
{
throw new ArgumentException("Unsupported window type", nameof(T));

View File

@@ -1,14 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace BetterLyrics.WinUI3.Models
{
public class LibInfo
{
public string Name { get; set; }
public string Url => $"https://www.nuget.org/packages/{Name}/";
}
}

View File

@@ -49,27 +49,27 @@ namespace BetterLyrics.WinUI3.Models
partial void OnLyricsWindowBoundsChanged(Rect value)
{
double factor = 0.1;
DemoLyricsWindowBounds = new Rect(
value.X * factor,
value.Y * factor,
value.Width * factor,
value.Height * factor
);
var lyricsWindow = WindowHelper.GetWindowByWindowType<Views.LyricsWindow>();
if (lyricsWindow == null) return;
var mointor = MonitorHelper.GetMonitorInfoExFromWindow(lyricsWindow);
LyricsWindowMonitorName = mointor.szDevice;
LyricsWindowMonitorBounds = new Rect(
mointor.rcWork.Left,
mointor.rcWork.Top,
mointor.rcWork.Width,
mointor.rcWork.Height
mointor.rcMonitor.Left,
mointor.rcMonitor.Top,
mointor.rcMonitor.Width,
mointor.rcMonitor.Height
);
DemoLyricsWindowBounds = new Rect(
(value.X - mointor.rcMonitor.Left) * factor,
(value.Y - mointor.rcMonitor.Top) * factor,
value.Width * factor,
value.Height * factor
);
DemoLyricsWindowMonitorBounds = new Rect(
mointor.rcWork.Left * factor,
mointor.rcWork.Top * factor,
mointor.rcWork.Width * factor,
mointor.rcWork.Height * factor
mointor.rcMonitor.Left * factor,
mointor.rcMonitor.Top * factor,
mointor.rcMonitor.Width * factor,
mointor.rcMonitor.Height * factor
);
}
}

View File

@@ -15,7 +15,7 @@ namespace BetterLyrics.WinUI3.Models.Settings
[ObservableProperty][NotifyPropertyChangedRecipients] public partial string LibreTranslateServer { get; set; } = string.Empty;
[ObservableProperty][NotifyPropertyChangedRecipients] public partial bool IsTranslationEnabled { get; set; } = true;
[ObservableProperty][NotifyPropertyChangedRecipients] public partial bool ShowTranslationOnly { get; set; } = false;
[ObservableProperty][NotifyPropertyChangedRecipients] public partial int SelectedTargetLanguageIndex { get; set; } = LanguageHelper.GetDefaultTargetLanguageIndex();
[ObservableProperty][NotifyPropertyChangedRecipients] public partial string SelectedTargetLanguageCode { get; set; } = LanguageHelper.GetDefaultTargetLanguageCode();
[ObservableProperty][NotifyPropertyChangedRecipients] public partial bool IsJyutpingEnabled { get; set; } = false;
[ObservableProperty][NotifyPropertyChangedRecipients] public partial ChineseRomanization ChineseRomanization { get; set; }
[ObservableProperty][NotifyPropertyChangedRecipients] public partial bool IsChineseRomanizationEnabled { get; set; } = false;

View File

@@ -28,6 +28,7 @@ namespace BetterLyrics.WinUI3.Services.LyricsSearchService
{
private readonly HttpClient _amllTtmlDbHttpClient;
private readonly HttpClient _lrcLibHttpClient;
private readonly AppleMusic _appleMusic;
private readonly ISettingsService _settingsService;
private readonly ILogger _logger;
@@ -43,6 +44,7 @@ namespace BetterLyrics.WinUI3.Services.LyricsSearchService
$"{Constants.App.AppName} {MetadataHelper.AppVersion} ({Constants.Link.GitHubUrl})"
);
_amllTtmlDbHttpClient = new();
_appleMusic = new AppleMusic();
}
private static bool IsAmllTtmlDbIndexInvalid()
@@ -209,6 +211,9 @@ namespace BetterLyrics.WinUI3.Services.LyricsSearchService
case LyricsSearchProvider.AmllTtmlDb:
lyricsSearchResult = await SearchAmllTtmlDbAsync(title, artist);
break;
case LyricsSearchProvider.AppleMusic:
lyricsSearchResult = await SearchAppleMusicAsync(title, artist, album, (int)durationMs);
break;
default:
break;
}
@@ -508,7 +513,21 @@ namespace BetterLyrics.WinUI3.Services.LyricsSearchService
string? original = null;
if (response?.Candidates.FirstOrDefault() is SearchLyricsResponse.Candidate candidate)
{
original = Lyricify.Lyrics.Decrypter.Krc.Helper.GetLyrics(candidate.Id, candidate.AccessKey);
original = await Lyricify.Lyrics.Decrypter.Krc.Helper.GetLyricsAsync(candidate.Id, candidate.AccessKey);
if (candidate.TransId != null)
{
string? translated = await Lyricify.Lyrics.Decrypter.Krc.Helper.GetLyricsAsync(candidate.TransId, candidate.AccessKey);
if (!string.IsNullOrEmpty(translated))
{
FileHelper.WriteLyricsCache(
title,
artist,
translated,
LyricsFormat.Lrc,
PathHelper.KugouTranslationCacheDirectory
);
}
}
}
lyricsSearchResult.Raw = original;
@@ -518,5 +537,24 @@ namespace BetterLyrics.WinUI3.Services.LyricsSearchService
return lyricsSearchResult;
}
private async Task<LyricsSearchResult> SearchAppleMusicAsync(string title, string artist, string album, int durationMs)
{
var lyricsSearchResult = new LyricsSearchResult
{
Provider = LyricsSearchProvider.AppleMusic,
};
if (await _appleMusic.InitAsync())
{
var raw = await _appleMusic.GetLyricsAsync(title, artist);
_logger.LogInformation("Apple Music lyrics search result for {Title} - {Artist}: {Raw}", title, artist, raw ?? "null");
lyricsSearchResult.Raw = raw;
lyricsSearchResult.Title = title;
lyricsSearchResult.Artist = artist;
}
return lyricsSearchResult;
}
}
}

View File

@@ -27,6 +27,9 @@ namespace BetterLyrics.WinUI3.Services.MediaSessionsService
MediaSourceProviderInfo? GetCurrentMediaSourceProviderInfo();
void UpdateLyrics();
void UpdateTranslations();
bool IsPlaying { get; }
SongInfo? SongInfo { get; }
TimeSpan Position { get; }

View File

@@ -58,7 +58,7 @@ namespace BetterLyrics.WinUI3.Services.MediaSessionsService
private async Task SetDisplayedAlongWithTranslationsAsync(CancellationToken token)
{
_logger.LogInformation("Showing translation for lyrics...");
string targetLangCode = LanguageHelper.SupportedTargetLanguages[_settingsService.AppSettings.TranslationSettings.SelectedTargetLanguageIndex].Code;
string targetLangCode = _settingsService.AppSettings.TranslationSettings.SelectedTargetLanguageCode;
_logger.LogInformation("Target language code: {TargetLangCode}", targetLangCode);
string? originalText = _lyricsDataArr.FirstOrDefault()?.WrappedOriginalText;
if (originalText == null) return;
@@ -208,6 +208,7 @@ namespace BetterLyrics.WinUI3.Services.MediaSessionsService
translationRaw = FileHelper.ReadLyricsCache(SongInfo!.Title, SongInfo.Artist, LyricsFormat.Lrc, PathHelper.QQTranslationCacheDirectory);
break;
case Enums.LyricsSearchProvider.Kugou:
translationRaw = FileHelper.ReadLyricsCache(SongInfo!.Title, SongInfo.Artist, LyricsFormat.Lrc, PathHelper.KugouTranslationCacheDirectory);
break;
case Enums.LyricsSearchProvider.Netease:
translationRaw = FileHelper.ReadLyricsCache(SongInfo!.Title, SongInfo.Artist, LyricsFormat.Lrc, PathHelper.NeteaseTranslationCacheDirectory);
@@ -248,12 +249,12 @@ namespace BetterLyrics.WinUI3.Services.MediaSessionsService
}
}
private void UpdateLyrics()
public void UpdateLyrics()
{
_refreshLyricsRunner.RunAsync(RefreshLyricsAsync);
}
private void UpdateTranslations()
public void UpdateTranslations()
{
_refreshTranslationRunner.RunAsync(RefreshTranslationAsync);
}

View File

@@ -7,7 +7,6 @@ using BetterLyrics.WinUI3.Helper;
using BetterLyrics.WinUI3.Models;
using BetterLyrics.WinUI3.Models.Settings;
using BetterLyrics.WinUI3.Services.AlbumArtSearchService;
using BetterLyrics.WinUI3.Services.LastFMService;
using BetterLyrics.WinUI3.Services.LibWatcherService;
using BetterLyrics.WinUI3.Services.LiveStatesService;
using BetterLyrics.WinUI3.Services.LyricsSearchService;
@@ -20,19 +19,12 @@ using CommunityToolkit.Mvvm.Messaging;
using CommunityToolkit.Mvvm.Messaging.Messages;
using EvtSource;
using Microsoft.Extensions.Logging;
using Microsoft.Graphics.Canvas;
using Microsoft.UI.Dispatching;
using SixLabors.ImageSharp.PixelFormats;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Linq;
using System.Runtime.InteropServices.WindowsRuntime;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using Windows.Graphics.Imaging;
using Windows.Media.Control;
using Windows.Storage.Streams;
using WindowsMediaController;
@@ -40,7 +32,6 @@ using WindowsMediaController;
namespace BetterLyrics.WinUI3.Services.MediaSessionsService
{
public partial class MediaSessionsService : BaseViewModel, IMediaSessionsService,
IRecipient<PropertyChangedMessage<int>>,
IRecipient<PropertyChangedMessage<bool>>,
IRecipient<PropertyChangedMessage<string>>,
IRecipient<PropertyChangedMessage<LyricsWindowMode>>,
@@ -631,18 +622,6 @@ namespace BetterLyrics.WinUI3.Services.MediaSessionsService
}
}
public void Receive(PropertyChangedMessage<int> message)
{
if (message.Sender is TranslationSettings)
{
if (message.PropertyName == nameof(TranslationSettings.SelectedTargetLanguageIndex))
{
_logger.LogInformation("Target language index changed: {Index}", _settingsService.AppSettings.TranslationSettings.SelectedTargetLanguageIndex);
UpdateTranslations();
}
}
}
public void Receive(PropertyChangedMessage<string> message)
{
if (message.Sender is LyricsStyleSettings)
@@ -652,6 +631,15 @@ namespace BetterLyrics.WinUI3.Services.MediaSessionsService
UpdateTranslations();
}
}
else if (message.Sender is TranslationSettings)
{
if (message.PropertyName == nameof(TranslationSettings.SelectedTargetLanguageCode))
{
_logger.LogInformation("Target language code changed: {code}", _settingsService.AppSettings.TranslationSettings.SelectedTargetLanguageCode);
UpdateTranslations();
}
}
}
public void Receive(PropertyChangedMessage<LyricsWindowMode> message)

View File

@@ -285,6 +285,9 @@
<data name="LyricsSearchControlTitle.Header" xml:space="preserve">
<value>Title</value>
</data>
<data name="LyricsSearchPageTitle" xml:space="preserve">
<value>Lyrics search - BetterLyrics</value>
</data>
<data name="LyricsSearchProviderEslrcFile" xml:space="preserve">
<value>Local .ESLRC files</value>
</data>
@@ -445,9 +448,6 @@ If you encounter any problems, please go to the Settings page, About tab, and vi
<data name="SetingsPageFeedback.Text" xml:space="preserve">
<value>Feedback</value>
</data>
<data name="SetingsPageRef.Text" xml:space="preserve">
<value>Library used</value>
</data>
<data name="SetingsPageThanks.Text" xml:space="preserve">
<value>If you like this project, please consider supporting it by donating. Your support will help keep the project alive and encourage further development.</value>
</data>

View File

@@ -285,6 +285,9 @@
<data name="LyricsSearchControlTitle.Header" xml:space="preserve">
<value>タイトル</value>
</data>
<data name="LyricsSearchPageTitle" xml:space="preserve">
<value>歌詞検索 - BetterLyrics</value>
</data>
<data name="LyricsSearchProviderEslrcFile" xml:space="preserve">
<value>ローカル.ESLRCファイル</value>
</data>
@@ -445,9 +448,6 @@
<data name="SetingsPageFeedback.Text" xml:space="preserve">
<value>フィードバック</value>
</data>
<data name="SetingsPageRef.Text" xml:space="preserve">
<value>使用されているライブラリ</value>
</data>
<data name="SetingsPageThanks.Text" xml:space="preserve">
<value>このプロジェクトが気に入っている場合は、寄付してサポートすることを検討してください。あなたのサポートは、プロジェクトを生かし続け、さらなる開発を促進するのに役立ちます。</value>
</data>

View File

@@ -285,6 +285,9 @@
<data name="LyricsSearchControlTitle.Header" xml:space="preserve">
<value>제목</value>
</data>
<data name="LyricsSearchPageTitle" xml:space="preserve">
<value>가사 검색 - BetterLyrics</value>
</data>
<data name="LyricsSearchProviderEslrcFile" xml:space="preserve">
<value>로컬 .ESLRC 파일</value>
</data>
@@ -445,9 +448,6 @@
<data name="SetingsPageFeedback.Text" xml:space="preserve">
<value>피드백</value>
</data>
<data name="SetingsPageRef.Text" xml:space="preserve">
<value>사용 된 라이브러리</value>
</data>
<data name="SetingsPageThanks.Text" xml:space="preserve">
<value>이 프로젝트가 마음에 들면 기부하여 지원을 고려하십시오. 귀하의 지원은 프로젝트를 계속 유지하고 추가 개발을 장려하는 데 도움이 될 것입니다.</value>
</data>

View File

@@ -285,6 +285,9 @@
<data name="LyricsSearchControlTitle.Header" xml:space="preserve">
<value>标题</value>
</data>
<data name="LyricsSearchPageTitle" xml:space="preserve">
<value>歌词搜索 - BetterLyrics</value>
</data>
<data name="LyricsSearchProviderEslrcFile" xml:space="preserve">
<value>本地 .ESLRC 文件</value>
</data>
@@ -445,9 +448,6 @@
<data name="SetingsPageFeedback.Text" xml:space="preserve">
<value>反馈</value>
</data>
<data name="SetingsPageRef.Text" xml:space="preserve">
<value>使用的库</value>
</data>
<data name="SetingsPageThanks.Text" xml:space="preserve">
<value>如果您喜欢这个项目,请考虑通过捐赠来支持它。您的支持将有助于保持项目的生命并鼓励进一步的发展。</value>
</data>

View File

@@ -285,6 +285,9 @@
<data name="LyricsSearchControlTitle.Header" xml:space="preserve">
<value>標題</value>
</data>
<data name="LyricsSearchPageTitle" xml:space="preserve">
<value>歌詞搜尋 - BetterLyrics</value>
</data>
<data name="LyricsSearchProviderEslrcFile" xml:space="preserve">
<value>本地 .ESLRC 文件</value>
</data>
@@ -445,9 +448,6 @@
<data name="SetingsPageFeedback.Text" xml:space="preserve">
<value>回饋</value>
</data>
<data name="SetingsPageRef.Text" xml:space="preserve">
<value>使用的庫</value>
</data>
<data name="SetingsPageThanks.Text" xml:space="preserve">
<value>如果您喜歡這個項目,請考慮通過捐贈來支持它。您的支持將有助於保持項目的生命並鼓勵進一步的發展。</value>
</data>

View File

@@ -56,7 +56,11 @@ namespace BetterLyrics.WinUI3.ViewModels
[ObservableProperty]
public partial TranslationSearchProvider? TranslationSearchProvider { get; set; } = null;
[ObservableProperty]
public partial int SelectedTargetLanguageIndex { get; set; }
[ObservableProperty]
public partial string AppleMusicMediaUserToken { get; set; }
public PlaybackSettingsControlViewModel(
ISettingsService settingsService,
@@ -77,6 +81,10 @@ namespace BetterLyrics.WinUI3.ViewModels
AppSettings = _settingsService.AppSettings;
AppSettings.MediaSourceProvidersInfo.CollectionChanged += MediaSourceProvidersInfo_CollectionChanged;
AppleMusicMediaUserToken = PasswordVaultHelper.Get(Constants.App.AppName, Constants.AppleMusic.MediaUserTokenKey) ?? "";
SelectedTargetLanguageIndex = LanguageHelper.SupportedTargetLanguages.ToList().FindIndex(x => x.Code == AppSettings.TranslationSettings.SelectedTargetLanguageCode);
IsLastFMAuthenticated = _lastFMService.IsAuthenticated;
LastFMUser = _lastFMService.User;
@@ -123,8 +131,8 @@ namespace BetterLyrics.WinUI3.ViewModels
{
try
{
string targetLangCode = LanguageHelper.SupportedTargetLanguages[AppSettings.TranslationSettings.SelectedTargetLanguageIndex].Code;
string result = await _libreTranslateService.TranslateTextAsync("Hello, world!", targetLangCode, new System.Threading.CancellationToken());
string result = await _libreTranslateService.TranslateTextAsync(
"Hello, world!", AppSettings.TranslationSettings.SelectedTargetLanguageCode, new System.Threading.CancellationToken());
_dispatcherQueue.TryEnqueue(DispatcherQueuePriority.Low, () =>
{
App.Current.SettingsWindowNotificationPanel?.Notify(App.ResourceLoader!.GetString("SettingsPageServerTestSuccessInfo"), InfoBarSeverity.Success);
@@ -184,6 +192,13 @@ namespace BetterLyrics.WinUI3.ViewModels
});
}
[RelayCommand]
private void SaveAppleMusicMediaUserToken()
{
PasswordVaultHelper.Save(Constants.App.AppName, Constants.AppleMusic.MediaUserTokenKey, AppleMusicMediaUserToken);
_mediaSessionsService.UpdateLyrics();
}
public void Receive(PropertyChangedMessage<LyricsSearchProvider?> message)
{
if (message.Sender is MediaSessionsService)
@@ -205,5 +220,10 @@ namespace BetterLyrics.WinUI3.ViewModels
}
}
}
partial void OnSelectedTargetLanguageIndexChanged(int value)
{
AppSettings.TranslationSettings.SelectedTargetLanguageCode = LanguageHelper.SupportedTargetLanguages[value].Code;
}
}
}

View File

@@ -30,6 +30,7 @@ namespace BetterLyrics.WinUI3.ViewModels
public partial class SettingsPageViewModel : BaseViewModel
{
private readonly ISettingsService _settingsService;
private readonly IMediaSessionsService _mediaSessionsService;
public string Version { get; set; } = MetadataHelper.AppVersion;
@@ -43,63 +44,11 @@ namespace BetterLyrics.WinUI3.ViewModels
[ObservableProperty]
public partial object NavViewSelectedItemTag { get; set; } = "App";
[ObservableProperty]
public partial ObservableCollection<LibInfo> Libs { get; set; }
public SettingsPageViewModel(ISettingsService settingsService)
public SettingsPageViewModel(ISettingsService settingsService, IMediaSessionsService mediaSessionsService)
{
_settingsService = settingsService;
_mediaSessionsService = mediaSessionsService;
AppSettings = _settingsService.AppSettings;
Libs =
[
new LibInfo { Name = "3v.EvtSource" },
new LibInfo { Name = "ColorThief.ImageSharp" },
new LibInfo { Name = "CommunityToolkit.Labs.WinUI.MarqueeText" },
new LibInfo { Name = "CommunityToolkit.Labs.WinUI.OpacityMaskView" },
new LibInfo { Name = "CommunityToolkit.Labs.WinUI.Shimmer" },
new LibInfo { Name = "CommunityToolkit.Mvvm" },
new LibInfo { Name = "CommunityToolkit.WinUI.Behaviors" },
new LibInfo { Name = "CommunityToolkit.WinUI.Controls.MetadataControl" },
new LibInfo { Name = "CommunityToolkit.WinUI.Controls.Primitives" },
new LibInfo { Name = "CommunityToolkit.WinUI.Controls.Segmented" },
new LibInfo { Name = "CommunityToolkit.WinUI.Controls.SettingsControls" },
new LibInfo { Name = "CommunityToolkit.WinUI.Controls.Sizers" },
new LibInfo { Name = "CommunityToolkit.WinUI.Converters" },
new LibInfo { Name = "CommunityToolkit.WinUI.Extensions" },
new LibInfo { Name = "CommunityToolkit.WinUI.Helpers" },
new LibInfo { Name = "CommunityToolkit.WinUI.Media" },
new LibInfo { Name = "csharp-pinyin" },
new LibInfo { Name = "Dubya.WindowsMediaController" },
new LibInfo { Name = "H.NotifyIcon.WinUI"},
new LibInfo { Name = "Hqub.Last.fm"},
new LibInfo { Name = "Lyricify.Lyrics.Helper-NativeAot"},
new LibInfo { Name = "Microsoft.Extensions.DependencyInjection"},
new LibInfo { Name = "Microsoft.Extensions.Logging"},
new LibInfo { Name = "Microsoft.Graphics.Win2D"},
new LibInfo { Name = "Microsoft.Windows.SDK.BuildTools"},
new LibInfo { Name = "Microsoft.WindowsAppSDK"},
new LibInfo { Name = "Nito.AsyncEx"},
new LibInfo { Name = "Nito.AsyncEx.Tasks"},
new LibInfo { Name = "NTextCat"},
new LibInfo { Name = "RomajiConverter.Core"},
new LibInfo { Name = "Serilog.Extensions.Logging"},
new LibInfo { Name = "Serilog.Sinks.File"},
new LibInfo { Name = "ShadowViewer.Controls.Notification"},
new LibInfo { Name = "SixLabors.ImageSharp" },
new LibInfo { Name = "System.Drawing.Common"},
new LibInfo { Name = "System.Text.Encoding.CodePages"},
new LibInfo { Name = "TagLibSharp" },
new LibInfo { Name = "TinyPinyin.Net"},
new LibInfo { Name = "Ude.NetStandard"},
new LibInfo { Name = "Vanara.PInvoke.CoreAudio"},
new LibInfo { Name = "Vanara.PInvoke.DwmApi"},
new LibInfo { Name = "Vanara.PInvoke.Gdi32"},
new LibInfo { Name = "Vanara.PInvoke.Shell32"},
new LibInfo { Name = "Vanara.PInvoke.User32"},
new LibInfo { Name = "WinUIEx" },
new LibInfo { Name = "z440.atl.core" },
];
}
[RelayCommand]

View File

@@ -245,14 +245,6 @@
<ToolTipService.ToolTip>
<ToolTip x:Uid="LyricsPageLyricsSearchButtonToolTip" />
</ToolTipService.ToolTip>
<Button.ContextFlyout>
<Flyout
x:Name="LyricsSearchFlyout"
Closed="LyricsSearchFlyout_Closed"
FlyoutPresenterStyle="{StaticResource FlyoutPageStyle}"
Placement="Right"
ShouldConstrainToRootBounds="False" />
</Button.ContextFlyout>
</Button>
<!-- Playback shortcut settings -->

View File

@@ -2,6 +2,7 @@
using BetterLyrics.WinUI3.Controls;
using BetterLyrics.WinUI3.Enums;
using BetterLyrics.WinUI3.Helper;
using BetterLyrics.WinUI3.Models.Settings;
using BetterLyrics.WinUI3.Services.MediaSessionsService;
using BetterLyrics.WinUI3.Services.SettingsService;
@@ -133,17 +134,7 @@ namespace BetterLyrics.WinUI3.Views
private void LyricsSearchShortcutButton_Click(object sender, RoutedEventArgs e)
{
LyricsSearchFlyout.Content = new LyricsSearchControl
{
MaxHeight = 500,
MaxWidth = 850,
};
LyricsSearchFlyout.ShowAt(BottomRightCommandStackPanel);
}
private void LyricsSearchFlyout_Closed(object sender, object e)
{
LyricsSearchFlyout.Content = null;
WindowHelper.OpenWindow<LyricsSearchWindow>();
}
private void TimelineSliderOverlay_PointerPressed(object sender, Microsoft.UI.Xaml.Input.PointerRoutedEventArgs e)

View File

@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="utf-8" ?>
<Window
x:Class="BetterLyrics.WinUI3.Views.LyricsSearchWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="using:BetterLyrics.WinUI3.Controls"
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:media="using:CommunityToolkit.WinUI.Media"
mc:Ignorable="d">
<Grid Background="{ThemeResource SystemControlAcrylicWindowBrush}" Opacity="0.85">
<controls:LyricsSearchControl />
</Grid>
</Window>

View File

@@ -0,0 +1,47 @@
using BetterLyrics.WinUI3.Helper;
using Microsoft.UI.Windowing;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Controls.Primitives;
using Microsoft.UI.Xaml.Data;
using Microsoft.UI.Xaml.Input;
using Microsoft.UI.Xaml.Media;
using Microsoft.UI.Xaml.Navigation;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices.WindowsRuntime;
using Windows.Foundation;
using Windows.Foundation.Collections;
// 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.Views
{
/// <summary>
/// An empty window that can be used on its own or navigated to within a Frame.
/// </summary>
public sealed partial class LyricsSearchWindow : Window
{
public LyricsSearchWindow()
{
InitializeComponent();
Title = App.ResourceLoader?.GetString("LyricsSearchPageTitle");
AppWindow.TitleBar.PreferredTheme = TitleBarTheme.UseDefaultAppMode;
AppWindow.SetIcons();
AppWindow.Closing += AppWindow_Closing;
SystemBackdrop = SystemBackdropHelper.CreateSystemBackdrop(Enums.BackdropType.Transparent);
}
private void AppWindow_Closing(AppWindow sender, AppWindowClosingEventArgs args)
{
WindowHelper.CloseWindow<LyricsSearchWindow>();
}
}
}

View File

@@ -220,32 +220,6 @@
</controls:SettingsExpander.ItemsHeader>
</controls:SettingsExpander>
<TextBlock x:Uid="SetingsPageRef" Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" />
<ListView
Padding="0,6"
Background="{ThemeResource CardBackgroundFillColorDefault}"
CornerRadius="4"
ItemsSource="{x:Bind ViewModel.Libs, Mode=OneWay}"
SelectionMode="None">
<ListView.ItemTemplate>
<DataTemplate>
<RelativePanel Padding="42,0">
<HyperlinkButton
Margin="-12,0,0,0"
Content="{Binding Name}"
NavigateUri="{Binding Url}"
RelativePanel.AlignLeftWithPanel="True"
RelativePanel.AlignVerticalCenterWithPanel="True" />
<TextBlock
RelativePanel.AlignRightWithPanel="True"
RelativePanel.AlignVerticalCenterWithPanel="True"
Text="{Binding Version}" />
</RelativePanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackPanel>
</Grid>
</ScrollViewer>

View File

@@ -1,7 +1,7 @@
### Where I can find the logs?
`C:\Users\%USERNAME%\AppData\Local\Packages\37412.BetterLyrics_rd1g0rsrrtxw8\LocalCache\logs`
### How to install ".msixbundle" package?
### How to install ".msixbundle" package? (for test package only)
[See this doc](https://github.com/jayfunc/BetterLyrics/blob/dev/How2Install/How2Install.md)
### Lyrics are moving back and forth constantly, how to fix it?

View File

@@ -1,4 +1,4 @@
# How to install ".msixbundle" package
# How to install ".msixbundle" test package
## Pre-steps

View File

@@ -59,8 +59,9 @@ Check out the article: [BetterLyrics An immersive and smooth lyrics display
- <details><summary>⚠️ 网易云音乐</summary>
- Please install the [BetterNCM plugin](https://microblock.cc/betterncm) first. If a downgrade guide pops up after the installation, please follow the guide to complete the downgrade of NetEase Cloud Music (downgrade to 2.10.13);
- After that, please install the InfLink plugin in PluginMarket. After the installation is complete, please restart NetEase Cloud Music. At this point, all preparatory operations have been completed, enjoy it!
- Please be sure that that your Netease Cloud Music is at 3.1.4 version
- Install the [BetterNCM plugin](https://microblock.cc/betterncm) first
- After that, install the InfLink plugin in PluginMarket. After the installation is complete, please restart NetEase Cloud Music. At this point, all preparatory operations have been completed, enjoy it!
- ⚠️ Please note that there is issues with timeline due to plugin issue
</details>
@@ -68,7 +69,7 @@ Check out the article: [BetterLyrics An immersive and smooth lyrics display
- <details><summary>⚠️ 酷狗音乐</summary>
- Please make sure that the Kugou Music setting "Support system playback controls, such as lock screen interface" is turned on
- No timeline information broadcasted, which means when you change timeline position in Kugou Music, BetterLyrics has no way to detect this change.
- No timeline information broadcasted, which means when you change timeline position in Kugou Music, BetterLyrics has no way to detect this change
- ⚠️ Please note that there is issues with timeline due to Kugou itself
</details>
@@ -161,17 +162,17 @@ Watch our introduction video (uploaded on 18 Aug 2025) on Bilibili [here](https:
<img src="https://get.microsoft.com/images/en-us%20dark.svg" width="200"/>
</a>
**Easiest** way to get it. **Unlimited** free trail or purchase (there is **no difference** between free and paid version)
**Unlimited** free trail or purchase (there is **no difference** between free and paid version)
☕ If you find it useful, please consider [donating](#donations) or purchasing 🧧 it in **Microsoft Store**, I'll appreciate it! 🥰
> When there's a stable version built, Microsoft Store will be the first channel to get updated.
### Unable to download from the Microsoft Store?
### Google Drive
Or get it from Google Drive (see [release](https://github.com/jayfunc/BetterLyrics/releases) page for the link)
> Please note you are downloading ".zip" file, for guide on how to install it, please kindly follow [this doc](How2Install/How2Install.md).
1. Visit https://store.rg-adguard.net/
2. Type https://apps.microsoft.com/detail/9p1wcd1p597r in the link input area
3. Select Retail from the drop-down list
4. Click the check mark
5. Select the largest installation package in the resulting list to download and install
## 💖 Many thanks to
@@ -208,7 +209,8 @@ Or get it from Google Drive (see [release](https://github.com/jayfunc/BetterLyri
Cannot find your language?
Don't worry! Start translating and become one of the contributors! 😆
Click the [link](https://crowdin.com/project/betterlyrics/invite?h=d767e4f2dbd832d8fcdb6f7e5a198b402502866) to translate this app into your language via Crowdin now!
Fork this project and navigate to `BetterLyrics\BetterLyrics.WinUI3\BetterLyrics.WinUI3\Strings\` then open any `.resw` file to start contributing!
## Star history