Compare commits

...

3 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
27 changed files with 345 additions and 175 deletions

View File

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

View File

@@ -81,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" />

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

@@ -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

@@ -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;

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

@@ -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

@@ -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

@@ -448,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

@@ -448,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

@@ -448,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

@@ -448,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

@@ -448,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

@@ -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

@@ -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>