Compare commits

...

20 Commits

Author SHA1 Message Date
Zhe Fang
0a3dadbd82 Merge pull request #55 from jayfunc/dev
v1.0.26.0
2025-07-22 22:29:55 -04:00
Zhe Fang
da377838e8 fix 2025-07-22 22:29:34 -04:00
Zhe Fang
67cf6e47c8 fix 2025-07-22 21:15:32 -04:00
Zhe Fang
abf4c3498f Merge branch 'dev' of https://github.com/jayfunc/BetterLyrics into dev 2025-07-22 20:36:55 -04:00
Zhe Fang
ff8c85b2d0 fix playbackservice 2025-07-22 20:36:52 -04:00
Zhe Fang
8cbdb32931 Merge pull request #54 from jayfunc/l10n_dev
New Crowdin updates
2025-07-21 22:22:16 -04:00
Zhe Fang
757f9f4156 Merge branch 'dev' of https://github.com/jayfunc/BetterLyrics into dev 2025-07-21 21:41:08 -04:00
Zhe Fang
c632f4b01a update readme 2025-07-21 21:41:07 -04:00
Zhe Fang
a843d0a0e3 New translations resources.resw (English) 2025-07-21 20:38:58 -04:00
Zhe Fang
905df92b05 New translations resources.resw (Chinese Traditional) 2025-07-21 19:38:04 -04:00
Zhe Fang
7445299537 New translations resources.resw (Chinese Simplified) 2025-07-21 19:38:03 -04:00
Zhe Fang
ba4958f837 New translations resources.resw (Korean) 2025-07-21 19:38:02 -04:00
Zhe Fang
8ca5245bf5 New translations resources.resw (Japanese) 2025-07-21 19:38:00 -04:00
Zhe Fang
89aa97552a Update Crowdin configuration file 2025-07-21 19:36:41 -04:00
Zhe Fang
fa6da81988 Merge pull request #53 from jayfunc/dev
fix
2025-07-21 17:18:37 -04:00
Zhe Fang
aa692e2735 fix auto transparent issue after restart and lock when hover on non-transparent window 2025-07-21 17:17:57 -04:00
Zhe Fang
c7ee26f284 fix timeline update strategy 2025-07-21 11:29:52 -04:00
Zhe Fang
b103e6efd1 update readme 2025-07-21 10:28:44 -04:00
Zhe Fang
09e8cce69a Merge pull request #52 from jayfunc/dev
fix
2025-07-21 09:51:41 -04:00
Zhe Fang
16cd12e22b fix 2025-07-21 09:51:14 -04:00
43 changed files with 892 additions and 661 deletions

View File

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

View File

@@ -302,7 +302,7 @@
<Grid.Resources>
<Style x:Key="SliderThumbStyle" TargetType="Thumb">
<Setter Property="BorderThickness" Value="0" />
<Setter Property="Background" Value="{ThemeResource TextFillColorPrimaryBrush}" />
<Setter Property="Background" Value="Transparent" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Thumb">
@@ -354,12 +354,12 @@
x:Name="HorizontalTrackRect"
Grid.Row="1"
Grid.ColumnSpan="3"
Height="2"
Fill="{TemplateBinding Background}" />
Height="8"
Fill="Transparent" />
<Rectangle
x:Name="HorizontalDecreaseRect"
Grid.Row="1"
Fill="{TemplateBinding Foreground}" />
Fill="Transparent" />
<TickBar
x:Name="TopTickBar"
Grid.ColumnSpan="3"
@@ -372,7 +372,7 @@
x:Name="HorizontalInlineTickBar"
Grid.Row="1"
Grid.ColumnSpan="3"
Height="2"
Height="8"
Fill="{ThemeResource SliderInlineTickBarFill}"
Visibility="Collapsed" />
<TickBar
@@ -389,8 +389,8 @@
Grid.Row="0"
Grid.RowSpan="3"
Grid.Column="1"
Width="2"
Height="2"
Width="8"
Height="8"
AutomationProperties.AccessibilityView="Raw"
DataContext="{TemplateBinding Value}"
FocusVisualMargin="-14,-6,-14,-6"
@@ -452,7 +452,7 @@
Grid.Row="1"
Grid.Column="0"
Grid.ColumnSpan="3"
Width="24"
Width="8"
Height="8"
AutomationProperties.AccessibilityView="Raw"
DataContext="{TemplateBinding Value}"

View File

@@ -16,6 +16,7 @@ using ShadowViewer.Controls;
using System;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace BetterLyrics.WinUI3

View File

@@ -0,0 +1,43 @@
using ATL;
using BetterLyrics.WinUI3.Models;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace BetterLyrics.WinUI3.Helper
{
public static class CollectionHelper
{
public static ObservableCollection<GroupInfoList> GetGroupedByTitleAsync(this ICollection<Track> tracks)
{
// Grab Contact objects from pre-existing list (list is returned from function GetContactsAsync())
var query = from item in tracks
// Group the items returned from the query, sort and select the ones you want to keep
group item by item.Title.Substring(0, 1).ToUpper() into g
orderby g.Key
// GroupInfoList is a simple custom class that has an IEnumerable type attribute, and
// a key attribute. The IGrouping-typed variable g now holds the Contact objects,
// and these objects will be used to create a new GroupInfoList object.
select new GroupInfoList(g) { Key = g.Key };
return new ObservableCollection<GroupInfoList>(query);
}
public static void AddRange<T>(this ICollection<T> collection, IEnumerable<T> items)
{
if (collection == null) throw new ArgumentNullException(nameof(collection));
if (items == null) throw new ArgumentNullException(nameof(items));
foreach (var item in items)
{
collection.Add(item);
}
}
}
}

View File

@@ -73,6 +73,14 @@ namespace BetterLyrics.WinUI3.Helper
int targetX = _settingsService.DesktopWindowLeft;
int targetY = _settingsService.DesktopWindowTop;
if (targetWidth <= 0 || targetHeight <= 0 || targetX < 0 || targetY < 0)
{
targetWidth = 1200;
targetHeight = 600;
targetX = 200;
targetY = 200;
}
// <20><><EFBFBD>ô<EFBFBD><C3B4>ڴ<EFBFBD>С<EFBFBD><D0A1>λ<EFBFBD><CEBB>
window.AppWindow.MoveAndResize(
new Windows.Graphics.RectInt32(targetX, targetY, targetWidth, targetHeight)

View File

@@ -4,6 +4,7 @@ using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
using CommunityToolkit.WinUI;
using Microsoft.UI.Xaml;
using Vanara.PInvoke;
using Windows.System;
@@ -16,6 +17,7 @@ namespace BetterLyrics.WinUI3.Helper
private readonly List<User32.HWINEVENTHOOK> _hooks = new();
private HWND _currentForeground = HWND.NULL;
private readonly IntPtr _selfHwnd;
private readonly ThrottleHelper _winEventProcThrottle = new(TimeSpan.FromSeconds(1));
public delegate void WindowChangedHandler(HWND hwnd);
private readonly WindowChangedHandler _onWindowChanged;

View File

@@ -63,7 +63,7 @@ namespace BetterLyrics.WinUI3.Helper
{
var data = pNotify.ToStructure<AUDIO_VOLUME_NOTIFICATION_DATA>();
_masterVolume = (int)(data.fMasterVolume * 100);
_dispatcherQueue.TryEnqueue(() =>
_dispatcherQueue.TryEnqueue(DispatcherQueuePriority.Low, () =>
{
VolumeChanged?.Invoke(_masterVolume);
});

View File

@@ -0,0 +1,37 @@
using System;
namespace BetterLyrics.WinUI3.Helper
{
public class ThrottleHelper
{
private DateTime _lastTriggerTime = DateTime.MinValue;
private readonly TimeSpan _interval;
public ThrottleHelper(TimeSpan interval)
{
_interval = interval;
}
/// <summary>
/// 判断是否可以触发(距离上次触发已超过设定间隔),如果可以则更新时间戳并返回 true否则返回 false。
/// </summary>
public bool CanTrigger()
{
var now = DateTime.Now;
if ((now - _lastTriggerTime) >= _interval)
{
_lastTriggerTime = now;
return true;
}
return false;
}
/// <summary>
/// 重置触发时间
/// </summary>
public void Reset()
{
_lastTriggerTime = DateTime.MinValue;
}
}
}

View File

@@ -26,15 +26,6 @@ namespace BetterLyrics.WinUI3.Helper
}
}
public static void ExitAllWindows()
{
while (_activeWindows.Count > 0)
{
var window = (Window)_activeWindows[0];
window.Close();
}
}
public static T? GetWindowByWindowType<T>()
{
foreach (var window in _activeWindows)

View File

@@ -0,0 +1,18 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace BetterLyrics.WinUI3.Models
{
public partial class GroupInfoList(IEnumerable<object> items) : List<object>(items)
{
public required object Key { get; set; }
public override string ToString()
{
return "Group " + Key.ToString();
}
}
}

View File

@@ -6,18 +6,17 @@ using System.IO;
using System.Linq;
using BetterLyrics.WinUI3.Events;
using BetterLyrics.WinUI3.Models;
using BetterLyrics.WinUI3.ViewModels;
using Microsoft.UI.Dispatching;
namespace BetterLyrics.WinUI3.Services
{
public class LibWatcherService : IDisposable, ILibWatcherService
public class LibWatcherService : BaseViewModel, IDisposable, ILibWatcherService
{
private readonly ISettingsService _settingsService;
private readonly Dictionary<string, FileSystemWatcher> _watchers = [];
public LibWatcherService(ISettingsService settingsService)
public LibWatcherService(ISettingsService settingsService) : base(settingsService)
{
_settingsService = settingsService;
UpdateWatchers(_settingsService.LocalMediaFolders);
}
@@ -69,16 +68,13 @@ namespace BetterLyrics.WinUI3.Services
private void OnChanged(string folder, FileSystemEventArgs e)
{
App.DispatcherQueue!.TryEnqueue(
Microsoft.UI.Dispatching.DispatcherQueuePriority.High,
() =>
{
MusicLibraryFilesChanged?.Invoke(
this,
new LibChangedEventArgs(folder, e.FullPath, e.ChangeType)
);
}
);
_dispatcherQueue.TryEnqueue(DispatcherQueuePriority.Low, () =>
{
MusicLibraryFilesChanged?.Invoke(
this,
new LibChangedEventArgs(folder, e.FullPath, e.ChangeType)
);
});
}
}
}

View File

@@ -89,75 +89,79 @@ namespace BetterLyrics.WinUI3.Services
{
_logger.LogInformation("Searching img for: {Title} - {Artist} (Album: {Album}, Duration: {DurationMs}ms)", title, artist, album, durationMs);
foreach (var provider in _settingsService.LyricsSearchProvidersInfo)
try
{
if (!provider.IsEnabled)
foreach (var provider in _settingsService.LyricsSearchProvidersInfo)
{
continue;
}
string? cachedLyrics;
LyricsFormat lyricsFormat = provider.Provider.GetLyricsFormat();
// Check cache first
if (provider.Provider.IsRemote())
{
cachedLyrics = FileHelper.ReadLyricsCache(title, artist, lyricsFormat, provider.Provider.GetCacheDirectory());
if (!string.IsNullOrWhiteSpace(cachedLyrics))
if (!provider.IsEnabled)
{
return (cachedLyrics, provider.Provider);
continue;
}
}
string? searchedLyrics = null;
string? cachedLyrics;
LyricsFormat lyricsFormat = provider.Provider.GetLyricsFormat();
if (provider.Provider.IsLocal())
{
if (provider.Provider == LyricsSearchProvider.LocalMusicFile)
// Check cache first
if (provider.Provider.IsRemote())
{
searchedLyrics = SearchEmbedded(title, artist);
cachedLyrics = FileHelper.ReadLyricsCache(title, artist, lyricsFormat, provider.Provider.GetCacheDirectory());
if (!string.IsNullOrWhiteSpace(cachedLyrics))
{
return (cachedLyrics, provider.Provider);
}
}
string? searchedLyrics = null;
if (provider.Provider.IsLocal())
{
if (provider.Provider == LyricsSearchProvider.LocalMusicFile)
{
searchedLyrics = SearchEmbedded(title, artist);
}
else
{
searchedLyrics = await SearchFile(title, artist, lyricsFormat);
}
}
else
{
searchedLyrics = await SearchFile(title, artist, lyricsFormat);
switch (provider.Provider)
{
case LyricsSearchProvider.LrcLib:
searchedLyrics = await SearchLrcLibAsync(title, artist, album, (int)(durationMs / 1000));
break;
case LyricsSearchProvider.QQ:
searchedLyrics = await SearchQQNeteaseKugouAsync(title, artist, album, (int)durationMs, Searchers.QQMusic);
break;
case LyricsSearchProvider.Kugou:
searchedLyrics = await SearchQQNeteaseKugouAsync(title, artist, album, (int)durationMs, Searchers.Kugou);
break;
case LyricsSearchProvider.Netease:
searchedLyrics = await SearchQQNeteaseKugouAsync(title, artist, album, (int)durationMs, Searchers.Netease);
break;
case LyricsSearchProvider.AmllTtmlDb:
searchedLyrics = await SearchAmllTtmlDbAsync(title, artist);
break;
default:
break;
}
}
}
else
{
switch (provider.Provider)
token.ThrowIfCancellationRequested();
if (!string.IsNullOrWhiteSpace(searchedLyrics))
{
case LyricsSearchProvider.LrcLib:
searchedLyrics = await SearchLrcLibAsync(title, artist, album, (int)(durationMs / 1000));
break;
case LyricsSearchProvider.QQ:
searchedLyrics = await SearchQQNeteaseKugouAsync(title, artist, album, (int)durationMs, Searchers.QQMusic);
break;
case LyricsSearchProvider.Kugou:
searchedLyrics = await SearchQQNeteaseKugouAsync(title, artist, album, (int)durationMs, Searchers.Kugou);
break;
case LyricsSearchProvider.Netease:
searchedLyrics = await SearchQQNeteaseKugouAsync(title, artist, album, (int)durationMs, Searchers.Netease);
break;
case LyricsSearchProvider.AmllTtmlDb:
searchedLyrics = await SearchAmllTtmlDbAsync(title, artist);
break;
default:
break;
if (provider.Provider.IsRemote())
{
FileHelper.WriteLyricsCache(title, artist, searchedLyrics, lyricsFormat, provider.Provider.GetCacheDirectory());
}
return (searchedLyrics, provider.Provider);
}
}
token.ThrowIfCancellationRequested();
if (!string.IsNullOrWhiteSpace(searchedLyrics))
{
if (provider.Provider.IsRemote())
{
FileHelper.WriteLyricsCache(title, artist, searchedLyrics, lyricsFormat, provider.Provider.GetCacheDirectory());
}
return (searchedLyrics, provider.Provider);
}
}
catch (Exception) { }
return (null, null);
}

View File

@@ -10,6 +10,7 @@ using CommunityToolkit.Mvvm.Messaging.Messages;
using EvtSource;
using Microsoft.Extensions.Logging;
using Microsoft.UI.Dispatching;
using Microsoft.UI.Xaml;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
@@ -22,7 +23,9 @@ using System.Threading.Tasks;
using Windows.Graphics.Imaging;
using Windows.Media.Control;
using Windows.Storage.Streams;
using Windows.UI.Shell;
using WindowsMediaController;
using static WindowsMediaController.MediaManager;
namespace BetterLyrics.WinUI3.Services
{
@@ -40,6 +43,7 @@ namespace BetterLyrics.WinUI3.Services
private EventSourceReader? _sse = null;
private readonly MediaManager _mediaManager = new();
private MediaManager.MediaSession? _focusedSession = null;
private readonly LatestOnlyTaskRunner _albumArtRefreshRunner = new();
private readonly LatestOnlyTaskRunner _onAnyMediaPropertyChangedRunner = new();
@@ -73,8 +77,6 @@ namespace BetterLyrics.WinUI3.Services
private void InitMediaManager()
{
_mediaManager.Start();
_mediaManager.OnAnySessionOpened += MediaManager_OnAnySessionOpened;
_mediaManager.OnAnySessionClosed += MediaManager_OnAnySessionClosed;
_mediaManager.OnFocusedSessionChanged += MediaManager_OnFocusedSessionChanged;
@@ -82,47 +84,45 @@ namespace BetterLyrics.WinUI3.Services
_mediaManager.OnAnyPlaybackStateChanged += MediaManager_OnAnyPlaybackStateChanged;
_mediaManager.OnAnyTimelinePropertyChanged += MediaManager_OnAnyTimelinePropertyChanged;
MediaManager_OnFocusedSessionChanged(_mediaManager.GetFocusedSession());
_mediaManager.Start();
Task.Run(() =>
{
MediaManager_OnFocusedSessionChanged(null);
});
}
private void MediaManager_OnFocusedSessionChanged(MediaManager.MediaSession mediaSession)
private void MediaManager_OnFocusedSessionChanged(MediaManager.MediaSession? mediaSession)
{
if (mediaSession == null || !IsMediaSourceEnabled(mediaSession.ControlSession.SourceAppUserModelId))
if (!_mediaManager.IsStarted) return;
_focusedSession = mediaSession ?? _mediaManager.GetFocusedSession();
if (_focusedSession == null || !IsMediaSourceEnabled(_focusedSession.Id))
{
SendNullMessages();
}
else
{
Task.Run(async () =>
{
try
{
var props = await mediaSession.ControlSession.TryGetMediaPropertiesAsync();
MediaManager_OnAnyMediaPropertyChanged(mediaSession, props);
MediaManager_OnAnyPlaybackStateChanged(mediaSession, mediaSession.ControlSession.GetPlaybackInfo());
}
catch (Exception) { }
});
SendFocusedMessagesAsync().ConfigureAwait(false);
}
}
private void MediaManager_OnAnyTimelinePropertyChanged(MediaManager.MediaSession mediaSession, GlobalSystemMediaTransportControlsSessionTimelineProperties timelineProperties)
{
if (!IsMediaSourceEnabled(mediaSession.ControlSession.SourceAppUserModelId) || mediaSession != _mediaManager.GetFocusedSession()) return;
if (!_mediaManager.IsStarted) return;
if (!IsMediaSourceEnabled(mediaSession.Id) || mediaSession != _focusedSession) return;
_dispatcherQueue.TryEnqueue(
DispatcherQueuePriority.High,
() =>
{
PositionChanged?.Invoke(this, new PositionChangedEventArgs(timelineProperties.Position));
}
);
_dispatcherQueue.TryEnqueue(DispatcherQueuePriority.Low, () =>
{
PositionChanged?.Invoke(this, new PositionChangedEventArgs(timelineProperties.Position));
});
}
private void MediaManager_OnAnyPlaybackStateChanged(MediaManager.MediaSession mediaSession, GlobalSystemMediaTransportControlsSessionPlaybackInfo playbackInfo)
{
if (!_mediaManager.IsStarted) return;
RecordMediaSourceProviderInfo(mediaSession);
if (!IsMediaSourceEnabled(mediaSession.ControlSession.SourceAppUserModelId) || mediaSession != _mediaManager.GetFocusedSession()) return;
if (!IsMediaSourceEnabled(mediaSession.Id) || mediaSession != _focusedSession) return;
_cachedIsPlaying = playbackInfo.PlaybackStatus switch
{
@@ -130,20 +130,19 @@ namespace BetterLyrics.WinUI3.Services
_ => false,
};
_dispatcherQueue.TryEnqueue(DispatcherQueuePriority.High,
() =>
{
IsPlayingChanged?.Invoke(this, new IsPlayingChangedEventArgs(_cachedIsPlaying));
}
);
_dispatcherQueue.TryEnqueue(DispatcherQueuePriority.Low, () =>
{
IsPlayingChanged?.Invoke(this, new IsPlayingChangedEventArgs(_cachedIsPlaying));
});
}
private async void MediaManager_OnAnyMediaPropertyChanged(MediaManager.MediaSession mediaSession, GlobalSystemMediaTransportControlsSessionMediaProperties mediaProperties)
private void MediaManager_OnAnyMediaPropertyChanged(MediaManager.MediaSession mediaSession, GlobalSystemMediaTransportControlsSessionMediaProperties mediaProperties)
{
string id = mediaSession.ControlSession.SourceAppUserModelId;
if (!_mediaManager.IsStarted) return;
string id = mediaSession.Id;
RecordMediaSourceProviderInfo(mediaSession);
if (!IsMediaSourceEnabled(id) || mediaSession != _mediaManager.GetFocusedSession()) return;
if (!IsMediaSourceEnabled(id) || mediaSession != _focusedSession) return;
_cachedSongInfo = new SongInfo
{
@@ -156,7 +155,7 @@ namespace BetterLyrics.WinUI3.Services
_cachedSongInfo.Duration = (int)(_cachedSongInfo.DurationMs / 1000f);
await _onAnyMediaPropertyChangedRunner.RunAsync(async token =>
_onAnyMediaPropertyChangedRunner.RunAsync(async token =>
{
_logger.LogInformation("Media properties changed: Title: {Title}, Artist: {Artist}, Album: {Album}",
mediaProperties.Title, mediaProperties.Artist, mediaProperties.AlbumTitle);
@@ -173,7 +172,6 @@ namespace BetterLyrics.WinUI3.Services
if (mediaProperties.Thumbnail is IRandomAccessStreamReference streamReference)
{
_SMTCAlbumArtBytes = await ImageHelper.ToByteArrayAsync(streamReference);
token.ThrowIfCancellationRequested();
}
else
{
@@ -187,16 +185,17 @@ namespace BetterLyrics.WinUI3.Services
if (!token.IsCancellationRequested)
{
_dispatcherQueue.TryEnqueue(() =>
_dispatcherQueue.TryEnqueue(DispatcherQueuePriority.Low, () =>
{
SongInfoChanged?.Invoke(this, new SongInfoChangedEventArgs(_cachedSongInfo));
});
}
});
}).ConfigureAwait(false);
}
private void MediaManager_OnAnySessionClosed(MediaManager.MediaSession mediaSession)
{
if (!_mediaManager.IsStarted) return;
if (_mediaManager.CurrentMediaSessions.Count == 0)
{
SendNullMessages();
@@ -205,12 +204,16 @@ namespace BetterLyrics.WinUI3.Services
private void MediaManager_OnAnySessionOpened(MediaManager.MediaSession mediaSession)
{
if (!_mediaManager.IsStarted) return;
RecordMediaSourceProviderInfo(mediaSession);
_focusedSession = _mediaManager.GetFocusedSession();
SendFocusedMessagesAsync().ConfigureAwait(false);
}
private void RecordMediaSourceProviderInfo(MediaManager.MediaSession mediaSession)
{
var id = mediaSession?.ControlSession?.SourceAppUserModelId;
if (!_mediaManager.IsStarted) return;
var id = mediaSession?.Id;
if (string.IsNullOrEmpty(id)) return;
var found = _mediaSourceProvidersInfo.FirstOrDefault(x => x.Provider == id);
@@ -218,8 +221,7 @@ namespace BetterLyrics.WinUI3.Services
{
_mediaSourceProvidersInfo.Add(new MediaSourceProviderInfo(id, true));
_settingsService.MediaSourceProvidersInfo = _mediaSourceProvidersInfo;
_dispatcherQueue.TryEnqueue(DispatcherQueuePriority.High,
() =>
_dispatcherQueue.TryEnqueue(DispatcherQueuePriority.Low, () =>
{
MediaSourceProvidersInfoChanged?.Invoke(this, new MediaSourceProvidersInfoEventArgs(_mediaSourceProvidersInfo));
});
@@ -228,8 +230,7 @@ namespace BetterLyrics.WinUI3.Services
private void SendNullMessages()
{
_dispatcherQueue.TryEnqueue(DispatcherQueuePriority.High,
() =>
_dispatcherQueue.TryEnqueue(DispatcherQueuePriority.Low, () =>
{
_cachedSongInfo = null;
_cachedIsPlaying = false;
@@ -239,6 +240,16 @@ namespace BetterLyrics.WinUI3.Services
});
}
private async Task SendFocusedMessagesAsync()
{
if (_focusedSession == null) return;
var mediaProps = await _focusedSession.ControlSession.TryGetMediaPropertiesAsync();
MediaManager_OnAnyMediaPropertyChanged(_focusedSession, mediaProps);
MediaManager_OnAnyPlaybackStateChanged(_focusedSession, _focusedSession.ControlSession.GetPlaybackInfo());
MediaManager_OnAnyTimelinePropertyChanged(_focusedSession, _focusedSession.ControlSession.GetTimelineProperties());
}
private async Task UpdateAlbumArtRelated(CancellationToken token)
{
if (_cachedSongInfo == null)
@@ -275,7 +286,7 @@ namespace BetterLyrics.WinUI3.Services
var _albumArtAccentColor = ImageHelper.GetAccentColorsFromByte(bytes).FirstOrDefault();
_dispatcherQueue.TryEnqueue(() =>
_dispatcherQueue.TryEnqueue(DispatcherQueuePriority.Low, () =>
{
AlbumArtChangedChanged?.Invoke(this, new AlbumArtChangedEventArgs(_albumArtSwBitmap, _albumArtAccentColor));
});
@@ -292,7 +303,7 @@ namespace BetterLyrics.WinUI3.Services
catch (Exception)
{
_logger.LogError("Failed to start SSE connection for LX Music.");
_dispatcherQueue.TryEnqueue(() =>
_dispatcherQueue.TryEnqueue(DispatcherQueuePriority.Low, () =>
{
App.Current.LyricsWindowNotificationPanel?.Notify(App.ResourceLoader!.GetString("FailToStartLXMusicServer"), Microsoft.UI.Xaml.Controls.InfoBarSeverity.Error);
});
@@ -335,47 +346,27 @@ namespace BetterLyrics.WinUI3.Services
public async Task PlayAsync()
{
var focusedSession = _mediaManager.GetFocusedSession();
if (focusedSession != null)
{
await focusedSession.ControlSession.TryPlayAsync();
}
await _focusedSession?.ControlSession.TryPlayAsync();
}
public async Task PauseAsync()
{
var focusedSession = _mediaManager.GetFocusedSession();
if (focusedSession != null)
{
await focusedSession.ControlSession.TryPauseAsync();
}
await _focusedSession?.ControlSession.TryPauseAsync();
}
public async Task PreviousAsync()
{
var focusedSession = _mediaManager.GetFocusedSession();
if (focusedSession != null)
{
await focusedSession.ControlSession.TrySkipPreviousAsync();
}
await _focusedSession?.ControlSession.TrySkipPreviousAsync();
}
public async Task NextAsync()
{
var focusedSession = _mediaManager.GetFocusedSession();
if (focusedSession != null)
{
await focusedSession.ControlSession.TrySkipNextAsync();
}
await _focusedSession?.ControlSession.TrySkipNextAsync();
}
public async Task ChangePosition(double seconds)
{
var focusedSession = _mediaManager.GetFocusedSession();
if (focusedSession != null)
{
await focusedSession.ControlSession.TryChangePlaybackPositionAsync(TimeSpan.FromSeconds(seconds).Ticks);
}
await _focusedSession?.ControlSession.TryChangePlaybackPositionAsync(TimeSpan.FromSeconds(seconds).Ticks);
}
public void Receive(PropertyChangedMessage<ObservableCollection<MediaSourceProviderInfo>> message)

View File

@@ -3,6 +3,7 @@ using BetterLyrics.WinUI3.Models;
using BetterLyrics.WinUI3.Serialization;
using BetterLyrics.WinUI3.ViewModels;
using Lyricify.Lyrics.Helpers.General;
using Microsoft.UI.Dispatching;
using System;
using System.Collections.Generic;
using System.Linq;
@@ -27,7 +28,7 @@ namespace BetterLyrics.WinUI3.Services
{
if (string.IsNullOrWhiteSpace(text))
{
throw new ArgumentException("Text and target language must be provided.");
throw new Exception(text + " is empty or null.");
}
string? originalLangCode = LanguageHelper.DetectLanguageCode(text);
@@ -46,15 +47,7 @@ namespace BetterLyrics.WinUI3.Services
if (string.IsNullOrEmpty(_settingsService.LibreTranslateServer))
{
_dispatcherQueue.TryEnqueue(() =>
{
App.Current.LyricsWindowNotificationPanel?.Notify(
App.ResourceLoader!.GetString("TranslateServerNotSet"),
Microsoft.UI.Xaml.Controls.InfoBarSeverity.Warning
);
});
throw new InvalidOperationException("LibreTranslate server URL is not configured.");
throw new Exception("LibreTranslate server URL is not set in settings.");
}
var url = $"{_settingsService.LibreTranslateServer}/translate";

View File

@@ -59,46 +59,46 @@
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:schema xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" id="root">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace"/>
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
<xsd:element name="value" type="xsd:string" minOccurs="0"/>
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
<xsd:attribute name="name" use="required" type="xsd:string"/>
<xsd:attribute name="type" type="xsd:string"/>
<xsd:attribute name="mimetype" type="xsd:string"/>
<xsd:attribute ref="xml:space"/>
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
<xsd:attribute name="alias" type="xsd:string"/>
<xsd:attribute name="name" type="xsd:string"/>
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1"/>
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2"/>
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1"/>
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3"/>
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4"/>
<xsd:attribute ref="xml:space"/>
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1"/>
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
<xsd:attribute name="name" type="xsd:string" use="required"/>
</xsd:complexType>
</xsd:element>
</xsd:choice>
@@ -118,43 +118,43 @@
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="SettingsPageMusicLib.Header" xml:space="preserve">
<value>Local media library</value>
<value>本地媒体库</value>
</data>
<data name="SettingsPageMusicLib.Description" xml:space="preserve">
<value>Add folders storing music or lyrics</value>
<value>添加存放音乐或歌词的文件夹</value>
</data>
<data name="SettingsPageOpenLogFolderButton.Content" xml:space="preserve">
<value>Open in file explorer</value>
<value>在文件资源管理器中打开</value>
</data>
<data name="SettingsPageRemovePath.Content" xml:space="preserve">
<value>Remove from app</value>
<value>从应用中移除</value>
</data>
<data name="SettingsPageRemoveInfo.Title" xml:space="preserve">
<value>You are safe to remove the following items</value>
<value>您可以安全删除以下项目</value>
</data>
<data name="SettingsPageRemoveInfo.Message" xml:space="preserve">
<value>Original files and folders in this path will not be deleted when removing it from this app</value>
<value>路径中的原始文件和文件夹不会被删除</value>
</data>
<data name="SettingsPageAddFolder.Header" xml:space="preserve">
<value>Add a folder</value>
<value>添加文件夹</value>
</data>
<data name="SettingsPageTheme.Header" xml:space="preserve">
<value>Lyrics background theme</value>
<value>歌词背景主题</value>
</data>
<data name="SettingsPageLanguage.Header" xml:space="preserve">
<value>Language</value>
<value>语言</value>
</data>
<data name="SettingsPageLyricsFontColorAdaptiveColored.Content" xml:space="preserve">
<value>Adaptive to lyrics background (Colored)</value>
<value>适应歌词背景(彩色)</value>
</data>
<data name="SettingsPageLyricsFgFontColorAdaptiveColored.Content" xml:space="preserve">
<value>Adaptive to lyrics background (Colored)</value>
<value>适应歌词背景(彩色)</value>
</data>
<data name="SettingsPageLight.Content" xml:space="preserve">
<value>Light</value>
<value>浅色</value>
</data>
<data name="SettingsPageDark.Content" xml:space="preserve">
<value>Dark</value>
<value>深色</value>
</data>
<data name="SettingsPageSC.Content" xml:space="preserve">
<value>简体中文</value>
@@ -166,320 +166,320 @@
<value>English</value>
</data>
<data name="SettingsPageGitHub.Header" xml:space="preserve">
<value>This is an open source app</value>
<value>此应用已开源</value>
</data>
<data name="SettingsPageGitHub.ActionIconToolTip" xml:space="preserve">
<value>Open in new window</value>
<value>在新窗口中打开</value>
</data>
<data name="SettingsPageGitHub.Description" xml:space="preserve">
<value>See source code on GitHub</value>
<value>在 GitHub 上查看源代码</value>
</data>
<data name="SettingsPageVersion.Text" xml:space="preserve">
<value>Version</value>
<value>版本号</value>
</data>
<data name="SettingsPageNoBackdrop.Content" xml:space="preserve">
<value>None</value>
<value></value>
</data>
<data name="SettingsPageMica.Content" xml:space="preserve">
<value>Mica</value>
<value>云母</value>
</data>
<data name="SettingsPageMicaAlt.Content" xml:space="preserve">
<value>Mica Alt</value>
<value>云母(替代样式)</value>
</data>
<data name="SettingsPageDesktopAcrylic.Content" xml:space="preserve">
<value>Desktop Acrylic</value>
<value>亚克力(桌面)</value>
</data>
<data name="SettingsPageAcrylicBase.Content" xml:space="preserve">
<value>Acrylic Base</value>
<value>亚克力(基础)</value>
</data>
<data name="SettingsPageAcrylicThin.Content" xml:space="preserve">
<value>Acrylic Thin</value>
<value>亚克力(薄层)</value>
</data>
<data name="SettingsPageTransparent.Content" xml:space="preserve">
<value>Transparent</value>
<value>透明</value>
</data>
<data name="SettingsPageBackdrop.Header" xml:space="preserve">
<value>Lyrics backdrop</value>
<value>歌词背景材质</value>
</data>
<data name="SettingsPageSystemLanguage.Content" xml:space="preserve">
<value>Default</value>
<value>默认</value>
</data>
<data name="SettingsPageRestart.Content" xml:space="preserve">
<value>Restart app to apply change</value>
<value>重启应用以应用更改</value>
</data>
<data name="SettingsPagePathNotFound.Text" xml:space="preserve">
<value>The path cannot be found on your computer</value>
<value>无法在您的计算机中找到该路径</value>
</data>
<data name="SettingsPagePathExistedInfo" xml:space="preserve">
<value>The folder has been added. Please do not add it again.</value>
<value>已添加过该文件夹,请勿重复添加</value>
</data>
<data name="SettingsPageLyricsBackground.Header" xml:space="preserve">
<value>Lyrics background</value>
<value>歌词背景</value>
</data>
<data name="SettingsPageDynamicLyricsBackground.Header" xml:space="preserve">
<value>Dynamic lyrics background</value>
<value>动态歌词背景</value>
</data>
<data name="SettingsPageLyricsBackgroundOpacity.Header" xml:space="preserve">
<value>Lyrics background opacity</value>
<value>歌词背景不透明度</value>
</data>
<data name="SettingsPageTitle" xml:space="preserve">
<value>Settings - BetterLyrics</value>
<value>设置 - BetterLyrics</value>
</data>
<data name="LyricsPageTitle" xml:space="preserve">
<value>BetterLyrics</value>
</data>
<data name="SettingsPageLyricsAlignment.Header" xml:space="preserve">
<value>Alignment</value>
<value>对齐方式</value>
</data>
<data name="SettingsPageLyricsCenter.Content" xml:space="preserve">
<value>Center</value>
<value>居中</value>
</data>
<data name="SettingsPageLyricsLeft.Content" xml:space="preserve">
<value>Left</value>
<value>靠左</value>
</data>
<data name="SettingsPageLyricsRight.Content" xml:space="preserve">
<value>Right</value>
<value>靠右</value>
</data>
<data name="SettingsPageLyricsBackgroundBlurAmount.Header" xml:space="preserve">
<value>Lyrics background blur amount</value>
<value>歌词背景模糊度</value>
</data>
<data name="SettingsPageLyricsBlurAmount.Header" xml:space="preserve">
<value>Blur amount</value>
<value>模糊度</value>
</data>
<data name="SettingsPageLyricsBlurAmountSideEffect.Text" xml:space="preserve">
<value>Adjusting this value will also increase the background blur intensity of the album image.</value>
<value>调整该数值将同步提高专辑图片背景模糊强度</value>
</data>
<data name="SettingsPageSliderPrefix.Text" xml:space="preserve">
<value>Current value: </value>
<value>当前值:</value>
</data>
<data name="SettingsPageLyricsBlurHighGPUUsage.Text" xml:space="preserve">
<value>Significantly higher GPU usage when blur is enabled (&gt; 0)</value>
<value>启用模糊(&gt; 0时将显著提升 GPU 占用率</value>
</data>
<data name="SettingsPageLyricsVerticalEdgeOpacity.Header" xml:space="preserve">
<value>Top and bottom edge opacity</value>
<value>上下边缘不透明度</value>
</data>
<data name="SettingsPageLyricsLineSpacingFactor.Header" xml:space="preserve">
<value>Line spacing</value>
<value>行间距</value>
</data>
<data name="SettingsPageLyricsLineSpacingFactorUnit.Text" xml:space="preserve">
<value>x line height</value>
<value> 倍行高</value>
</data>
<data name="SettingsPageLyricsFontSize.Header" xml:space="preserve">
<value>Font size</value>
<value>字体大小</value>
</data>
<data name="MainPageLyriscOnly.Content" xml:space="preserve">
<value>Lyrics only</value>
<value>仅显示歌词</value>
</data>
<data name="MainWindowImmersiveMode.ToolTipService.ToolTip" xml:space="preserve">
<value>Immersive mode</value>
<value>沉浸模式</value>
</data>
<data name="SettingsPageBackgroundOverlay.Content" xml:space="preserve">
<value>Lyrics background</value>
<value>歌词背景</value>
</data>
<data name="SettingsPageAbout.Content" xml:space="preserve">
<value>About</value>
<value>关于</value>
</data>
<data name="SettingsPageLyricsLib.Content" xml:space="preserve">
<value>Lyrics library</value>
<value>歌词源</value>
</data>
<data name="SettingsPageAppAppearance.Text" xml:space="preserve">
<value>App appearance</value>
<value>应用外观</value>
</data>
<data name="SettingsPageLyricsGlowEffect.Header" xml:space="preserve">
<value>Glow effect</value>
<value>辉光效果</value>
</data>
<data name="SettingsPageLyricsSearchProvidersConfig.Header" xml:space="preserve">
<value>Configure lyrics source</value>
<value>配置歌词源</value>
</data>
<data name="SettingsPageLyricsSearchProvidersConfig.Description" xml:space="preserve">
<value>Drag to sort, the lyrics search order will be in the following order</value>
<value>拖动排序,歌词搜索顺序将按以下顺序</value>
</data>
<data name="SettingsPageAddFolderButton.Content" xml:space="preserve">
<value>Add</value>
<value>添加</value>
</data>
<data name="MainPageWelcomeTeachingTip.Title" xml:space="preserve">
<value>Welcome to BetterLyrics</value>
<value>欢迎使用 BetterLyrics</value>
</data>
<data name="MainPageWelcomeTeachingTip.Subtitle" xml:space="preserve">
<value>Click on the top-left button to enable immersive mode.
If you encounter any problems, please go to the Settings page, About tab, and view the FAQ or contact the author for feedback</value>
<value>单击左上按钮以启用沉浸式模式。
如果遇到任何问题,请转到“设置”页面,关于标签,查看常见问题解答或联系作者以获取反馈</value>
</data>
<data name="MainPageNoMusicPlaying.Text" xml:space="preserve">
<value>No music playing now</value>
<value>当前没有正在播放的音乐</value>
</data>
<data name="SettingsPageDev.Content" xml:space="preserve">
<value>Advanced options</value>
<value>高级选项</value>
</data>
<data name="SettingsPageMockMusicPlaying.Header" xml:space="preserve">
<value>Play test music</value>
<value>播放测试音乐</value>
</data>
<data name="SettingsPagePlayingMockMusicButton.Content" xml:space="preserve">
<value>Play "Cut To The Feeling" on "soundcloud.com"</value>
<value>在 “soundcloud.com” 上播放 “Cut to the Feeling</value>
</data>
<data name="SettingsPageCache.Header" xml:space="preserve">
<value>Cache</value>
<value>缓存</value>
</data>
<data name="SettingsPageCache.Description" xml:space="preserve">
<value>Including log files, network lyrics cache</value>
<value>包括日志文件,网络歌词缓存</value>
</data>
<data name="SettingsPageLyricsBgFontColor.Header" xml:space="preserve">
<value>Font color (Non-current playback area)</value>
<value>字体颜色(非当前播放区域)</value>
</data>
<data name="SettingsPageLyricsFgFontColor.Header" xml:space="preserve">
<value>Font color (Current playback area)</value>
<value>字体颜色(当前播放区域)</value>
</data>
<data name="SettingsPageLyricsFontColorAdaptiveGrayed.Content" xml:space="preserve">
<value>Adaptive to lyrics background (Grayed)</value>
<value>适应歌词背景(灰色)</value>
</data>
<data name="SettingsPageLyricsFgFontColorAdaptiveGrayed.Content" xml:space="preserve">
<value>Adaptive to lyrics background (Grayed)</value>
<value>适应歌词背景(灰色)</value>
</data>
<data name="SettingsPageAlbumStyle.Content" xml:space="preserve">
<value>Album art area style</value>
<value>专辑区域样式</value>
</data>
<data name="SettingsPageAlbumRadius.Header" xml:space="preserve">
<value>Corner radius</value>
<value>圆角半径</value>
</data>
<data name="SettingsPageTitleBarType.Header" xml:space="preserve">
<value>Title bar size</value>
<value>标题栏大小</value>
</data>
<data name="SettingsPageCompactTitleBar.Content" xml:space="preserve">
<value>Compact</value>
<value>紧凑</value>
</data>
<data name="SettingsPageExtendedTitleBar.Content" xml:space="preserve">
<value>Extended</value>
<value>扩展</value>
</data>
<data name="BaseWindowAOTFlyoutItem.Text" xml:space="preserve">
<value>Always on top</value>
<value>将应用置于顶层</value>
</data>
<data name="BaseWindowFullScreenFlyoutItem.Text" xml:space="preserve">
<value>Full screen</value>
<value>全屏</value>
</data>
<data name="BaseWindowEnterFullScreenHint" xml:space="preserve">
<value>Press Esc to exit full screen mode</value>
<value>按 Esc 退出全屏模式</value>
</data>
<data name="MainPageEnterImmersiveModeHint" xml:space="preserve">
<value>Hover back again to show the toggle button</value>
<value>再次悬停以显示切换按钮</value>
</data>
<data name="BaseWindowHostInfoBarCheckBox.Content" xml:space="preserve">
<value>Do not show this message again</value>
<value>不再显示此消息</value>
</data>
<data name="MainPageNoLocalFilesMatched.Text" xml:space="preserve">
<value>No local files matched</value>
<value>找不到匹配的本地文件</value>
</data>
<data name="MainPageAlbumArtOnly.Content" xml:space="preserve">
<value>Album art only</value>
<value>仅显示专辑封面</value>
</data>
<data name="MainPageSplitView.Content" xml:space="preserve">
<value>Split view</value>
<value>分屏视图</value>
</data>
<data name="MainPageDisplayTypeSwitcher.ToolTipService.ToolTip" xml:space="preserve">
<value>Change display type</value>
<value>切换显示模式</value>
</data>
<data name="MainPageDesktopLyricsToggler.ToolTipService.ToolTip" xml:space="preserve">
<value>Switch to desktop lyrics mode</value>
<value>切换到桌面歌词模式</value>
</data>
<data name="BaseWindowMiniFlyoutItem.Text" xml:space="preserve">
<value>Picture-in-picture mode</value>
<value>画中画模式</value>
</data>
<data name="BaseWindowUnMiniFlyoutItem.Text" xml:space="preserve">
<value>Exit picture-in-picture mode</value>
<value>退出画中画模式</value>
</data>
<data name="LyricsNotFound" xml:space="preserve">
<value>Lyrics not found</value>
<value>未找到歌词</value>
</data>
<data name="SettingsPageLyricsEffect.Text" xml:space="preserve">
<value>Lyrics effect</value>
<value>歌词动效</value>
</data>
<data name="SettingsPageLyricsStyle.Text" xml:space="preserve">
<value>Lyrics style</value>
<value>歌词样式</value>
</data>
<data name="SettingsPagePathBeIncludedInfo" xml:space="preserve">
<value>This folder is already included in the existing folder and does not need to be added again</value>
<value>该文件夹已包含在已有文件夹中,无需再次添加</value>
</data>
<data name="SettingsPageAppBehavior.Text" xml:space="preserve">
<value>App behavior</value>
<value>应用行为</value>
</data>
<data name="SettingsPageAutoStartWindow.Header" xml:space="preserve">
<value>When starting the app</value>
<value>启动应用时</value>
</data>
<data name="SettingsPageAutoStartInAppLyrics.Content" xml:space="preserve">
<value>Activate standard mode</value>
<value>启动标准模式</value>
</data>
<data name="SettingsPageAutoStartDesktopLyrics.Content" xml:space="preserve">
<value>Activate desktop mode</value>
<value>启动桌面模式</value>
</data>
<data name="SettingsPageAutoStartDockLyrics.Content" xml:space="preserve">
<value>Activate dock mode</value>
<value>启动停靠模式</value>
</data>
<data name="SystemTrayPageTitle" xml:space="preserve">
<value>System tray - BetterLyrics</value>
<value>系统托盘 - BetterLyrics</value>
</data>
<data name="HostWindowDockFlyoutItem.Text" xml:space="preserve">
<value>Dock mode</value>
<value>停靠模式</value>
</data>
<data name="SettingsPageAppDock.Text" xml:space="preserve">
<value>Dock mode</value>
<value>停靠模式</value>
</data>
<data name="HostWindowDesktopFlyoutItem.Text" xml:space="preserve">
<value>Desktop mode</value>
<value>桌面模式</value>
</data>
<data name="SettingsPageAppDesktop.Text" xml:space="preserve">
<value>Desktop mode</value>
<value>桌面模式</value>
</data>
<data name="SettingsPageLyricsFontWeight.Header" xml:space="preserve">
<value>Font weight</value>
<value>字体粗细</value>
</data>
<data name="SettingsPageLyricsThin.Content" xml:space="preserve">
<value>Thin</value>
<value>极细</value>
</data>
<data name="SettingsPageLyricsExtraLight.Content" xml:space="preserve">
<value>Extra Light</value>
<value>超细</value>
</data>
<data name="SettingsPageLyricsLight.Content" xml:space="preserve">
<value>Light</value>
<value>细体</value>
</data>
<data name="SettingsPageLyricsSemiLight.Content" xml:space="preserve">
<value>Semi Light</value>
<value>次细</value>
</data>
<data name="SettingsPageLyricsNormal.Content" xml:space="preserve">
<value>Normal</value>
<value>常规</value>
</data>
<data name="SettingsPageLyricsMedium.Content" xml:space="preserve">
<value>Medium</value>
<value>中等</value>
</data>
<data name="SettingsPageLyricsSemiBold.Content" xml:space="preserve">
<value>Semi Bold</value>
<value>次粗</value>
</data>
<data name="SettingsPageLyricsBold.Content" xml:space="preserve">
<value>Bold</value>
<value>粗体</value>
</data>
<data name="SettingsPageLyricsExtraBold.Content" xml:space="preserve">
<value>Extra Bold</value>
<value>特粗</value>
</data>
<data name="SettingsPageLyricsBlack.Content" xml:space="preserve">
<value>Black</value>
<value>黑体</value>
</data>
<data name="SettingsPageLyricsExtraBlack.Content" xml:space="preserve">
<value>Extra Black</value>
<value>超黑</value>
</data>
<data name="SettingsPageLyricsRendingScopeCurrentLine.Content" xml:space="preserve">
<value>Current line</value>
<value>当前行</value>
</data>
<data name="SettingsPageLyricsRendingScopeCurrentChar.Content" xml:space="preserve">
<value>Current char</value>
<value>当前字符</value>
</data>
<data name="HostWindowSettingsFlyoutItem.Text" xml:space="preserve">
<value>Settings</value>
<value>设置</value>
</data>
<data name="LyricsLoading" xml:space="preserve">
<value>Loading lyrics...</value>
<value>加载歌词中...</value>
</data>
<data name="LyricsSearchProviderLocalLrcFile" xml:space="preserve">
<value>Local .LRC files</value>
<value>本地 .LRC 文件</value>
</data>
<data name="LyricsSearchProviderLocalMusicFile" xml:space="preserve">
<value>Local music files</value>
<value>本地音乐文件</value>
</data>
<data name="LyricsSearchProviderLrcLib" xml:space="preserve">
<value>LRCLIB</value>
@@ -491,13 +491,13 @@ If you encounter any problems, please go to the Settings page, About tab, and vi
<value>한국어</value>
</data>
<data name="LyricsSearchProviderEslrcFile" xml:space="preserve">
<value>Local .ESLRC files</value>
<value>本地 .ESLRC 文件</value>
</data>
<data name="LyricsSearchProviderTtmlFile" xml:space="preserve">
<value>Local .TTML files</value>
<value>本地 .TTML 文件</value>
</data>
<data name="SettingsPagePathIncludingOthersInfo" xml:space="preserve">
<value>This folder contains added folders, please delete these folders to add the folder</value>
<value>该文件夹包含已添加文件夹,请删除这些文件夹以添加该文件夹</value>
</data>
<data name="LyricsSearchProviderAmllTtmlDb" xml:space="preserve">
<value>amll-ttml-db</value>
@@ -512,288 +512,288 @@ If you encounter any problems, please go to the Settings page, About tab, and vi
<value>Kugou</value>
</data>
<data name="SettingsPageDebugOverlay.Header" xml:space="preserve">
<value>Show debug overlay</value>
<value>显示调试覆盖层</value>
</data>
<data name="DependenciesSettingsExpander.Header" xml:space="preserve">
<value>Dependencies</value>
<value>依赖</value>
</data>
<data name="HostWindowClickThroughFlyoutItem.Text" xml:space="preserve">
<value>Lock</value>
<value>锁定</value>
</data>
<data name="SystemTraySettings.Text" xml:space="preserve">
<value>Settings</value>
<value>打开设置</value>
</data>
<data name="SystemTrayExit.Text" xml:space="preserve">
<value>Exit</value>
<value>退出程序</value>
</data>
<data name="SystemTrayUnlock.Text" xml:space="preserve">
<value>Unlock the window</value>
<value>解锁窗口</value>
</data>
<data name="HostWindowClickThroughButton.Content" xml:space="preserve">
<value>Lock</value>
<value>锁定</value>
</data>
<data name="HostWindowLockToolTip.Text" xml:space="preserve">
<value>To unlock after locking, go to the system tray to unlock or press</value>
<value>锁定后解锁,请转到系统托盘解锁或按下</value>
</data>
<data name="SettingsPageFan.Header" xml:space="preserve">
<value>Fan lyrics</value>
<value>扇形歌词</value>
</data>
<data name="SettingsPageLyricsFontColorCustom.Content" xml:space="preserve">
<value>Custom</value>
<value>自定义</value>
</data>
<data name="SettingsPageLyricsFgFontColorCustom.Content" xml:space="preserve">
<value>Custom</value>
<value>自定义</value>
</data>
<data name="SettingsPageLyrics.Content" xml:space="preserve">
<value>Lyrics style and effect</value>
<value>歌词样式与动效</value>
</data>
<data name="SettingsPageApp.Content" xml:space="preserve">
<value>App appearance and behavior</value>
<value>应用外观与行为</value>
</data>
<data name="SettingsPageAutoLock.Header" xml:space="preserve">
<value>Auto-lock when activating desktop mode</value>
<value>启动桌面模式时随即锁定窗口</value>
</data>
<data name="SettingsPageSongInfoAlignment.Header" xml:space="preserve">
<value>Alignment</value>
<value>对齐方式</value>
</data>
<data name="SettingsPageSongInfoCenter.Content" xml:space="preserve">
<value>Center</value>
<value>居中</value>
</data>
<data name="SettingsPageSongInfoLeft.Content" xml:space="preserve">
<value>Left</value>
<value>靠左</value>
</data>
<data name="SettingsPageSongInfoRight.Content" xml:space="preserve">
<value>Right</value>
<value>靠右</value>
</data>
<data name="SettingsPageAlbumArt.Text" xml:space="preserve">
<value>Album art</value>
<value>专辑</value>
</data>
<data name="SettingsPageSongInfo.Text" xml:space="preserve">
<value>Song title &amp; artist</value>
<value>歌曲标题和艺术家</value>
</data>
<data name="SettingsPageEasingFuncType.Header" xml:space="preserve">
<value>Easing animation type</value>
<value>缓动动画类型</value>
</data>
<data name="SettingsPagePlaybackLib.Content" xml:space="preserve">
<value>Playback sources</value>
<value>播放源</value>
</data>
<data name="SettingsPageMediaSourceProvidersConfig.Header" xml:space="preserve">
<value>Playback sources</value>
<value>播放源</value>
</data>
<data name="SettingsPageMediaSourceProvidersConfig.Description" xml:space="preserve">
<value>Enable or disable lyrics display for a specified media source</value>
<value>为指定媒体源启用或禁用歌词显示</value>
</data>
<data name="SettingsPageLog.Header" xml:space="preserve">
<value>Log record</value>
<value>日志记录</value>
</data>
<data name="SettingsPageTranslation.Content" xml:space="preserve">
<value>Lyrics translation</value>
<value>歌词翻译</value>
</data>
<data name="MainPagePositionOffsetSlider.Header" xml:space="preserve">
<value>Lyrics timeline offset (ms)</value>
<value>歌词时间轴偏移(毫秒)</value>
</data>
<data name="SettingsPageTranslationConfig.Header" xml:space="preserve">
<value>Configure translation services</value>
<value>配置翻译服务</value>
</data>
<data name="SettingsPageLibreTranslateServer.Header" xml:space="preserve">
<value>Server address</value>
<value>服务器地址</value>
</data>
<data name="SettingsPageServerTestButton.Content" xml:space="preserve">
<value>Test server</value>
<value>测试服务器</value>
</data>
<data name="SettingsPageTargetLanguage.Header" xml:space="preserve">
<value>Target language</value>
<value>目标语言</value>
</data>
<data name="SettingsPageTranslationInfo.Header" xml:space="preserve">
<value>Translation service powered by LibreTranslate</value>
<value>翻译服务由 LibreTranslate 驱动</value>
</data>
<data name="SettingsPageTranslationInfoLink.Text" xml:space="preserve">
<value>Visit https://github.com/LibreTranslate/LibreTranslate for installation instructions and more information (this software is not affiliated with this translation service in any way)</value>
<value>访问 https://github.com/LibreTranslate/LibreTranslate 获取安装教程及更多信息(本软件与该翻译服务无任何联系)</value>
</data>
<data name="SettingsPageServerTestSuccessInfo" xml:space="preserve">
<value>Server test successful</value>
<value>服务器测试成功</value>
</data>
<data name="SettingsPageServerTestFailedInfo" xml:space="preserve">
<value>Server test failed</value>
<value>服务器测试失败</value>
</data>
<data name="SettingsPageLyricsFontStrokeWidth.Header" xml:space="preserve">
<value>Lyrics stroke width (Desktop mode only)</value>
<value>歌词描边宽度(仅桌面模式)</value>
</data>
<data name="SettingsPageFollowSystem.Content" xml:space="preserve">
<value>Follow system</value>
<value>跟随系统</value>
</data>
<data name="SettingsPageAutoStart.Header" xml:space="preserve">
<value>Automatic startup</value>
<value>自动启动</value>
</data>
<data name="SettingsPageLyricsStrokeFontColor.Header" xml:space="preserve">
<value>Lyrics stroke color (Desktop mode only)</value>
<value>歌词描边颜色(仅桌面模式)</value>
</data>
<data name="SettingsPageIgnoreFullscreenWindow.Header" xml:space="preserve">
<value>Always stay on top of fullscreen applications</value>
<value>始终显示在全屏应用上方</value>
</data>
<data name="SettingsPageIgnoreFullscreenWindow.Description" xml:space="preserve">
<value>Force this app to appear on top of full-screen apps when docked or desktop mode is enabled</value>
<value>当启用停靠模式或桌面模式时强制将本应用显示在全屏应用的上方</value>
</data>
<data name="HostWindowMoreButtonToolTip.Content" xml:space="preserve">
<value>More</value>
<value>更多</value>
</data>
<data name="LyricsPageTimelineOffsetButtonToolTip.Content" xml:space="preserve">
<value>Lyrics timeline offset</value>
<value>歌词时间偏移</value>
</data>
<data name="LyricsPageTranslationButtonToolTip.Content" xml:space="preserve">
<value>Translate</value>
<value>翻译</value>
</data>
<data name="LyricsPageDisplayTypeButtonToolTip.Content" xml:space="preserve">
<value>Display type</value>
<value>显示类型</value>
</data>
<data name="LyricsPageSettingsButtonToolTip.Content" xml:space="preserve">
<value>Settings</value>
<value>设置</value>
</data>
<data name="TranslateServerNotSet" xml:space="preserve">
<value>Translate server is not set, please configure it in settings first</value>
<value>未设置翻译服务器,请先在设置中进行配置</value>
</data>
<data name="LyricsPagePositionOffsetHint.Text" xml:space="preserve">
<value>Reset to 0 when switching songs</value>
<value>切换歌曲时重置为 0</value>
</data>
<data name="SettingsPageTargetLanguage.Description" xml:space="preserve">
<value>The translation in the lyrics will be read first. If there is no match, the machine translation will be requested from the LibreTranslate server</value>
<value>将优先读取歌词内翻译,若无匹配则向 LibreTranslate 服务器请求机器翻译</value>
</data>
<data name="SettingsPageAlbumArtSearchProvidersConfig.Header" xml:space="preserve">
<value>Configure album cover source</value>
<value>配置专辑封面源</value>
</data>
<data name="SettingsPageAlbumArtSearchProvidersConfig.Description" xml:space="preserve">
<value>Drag to sort, the album art search order will be in the following order</value>
<value>拖动排序,专辑封面搜索顺序将按以下顺序</value>
</data>
<data name="SettingsPageAlbumLib.Content" xml:space="preserve">
<value>Album art source</value>
<value>专辑封面源</value>
</data>
<data name="AlbumArtSearchLocalProvider" xml:space="preserve">
<value>Local music files</value>
<value>本地音乐文件</value>
</data>
<data name="AlbumArtSearchSMTCProvider" xml:space="preserve">
<value>Music player</value>
<value>音乐播放器</value>
</data>
<data name="SettingsPageMediaLib.Content" xml:space="preserve">
<value>Media library</value>
<value>媒体库</value>
</data>
<data name="SettingsPageScrollEasing.Header" xml:space="preserve">
<value>Lyrics scrolling animation type</value>
<value>歌词滚动动画类型</value>
</data>
<data name="SettingsPageScrollDuration.Header" xml:space="preserve">
<value>Lyrics scrolling animation duration</value>
<value>歌词滚动动画持续时间</value>
</data>
<data name="SettingsPageEasingTypeLinear.Content" xml:space="preserve">
<value>Linear</value>
<value>线性</value>
</data>
<data name="SettingsPageEasingTypeSmoothStep.Content" xml:space="preserve">
<value>Smooth step</value>
<value>平滑步进</value>
</data>
<data name="SettingsPageEasingTypeEaseInOutSine.Content" xml:space="preserve">
<value>Ease-in-out sine</value>
<value>正弦缓入缓出</value>
</data>
<data name="SettingsPageEasingTypeEaseInOutQuad.Content" xml:space="preserve">
<value>Ease-in-out quad</value>
<value>二次缓入缓出</value>
</data>
<data name="SettingsPageEasingTypeEaseInOutElastic.Content" xml:space="preserve">
<value>Ease-in-out elastic</value>
<value>弹性缓入缓出</value>
</data>
<data name="SettingsPageEasingTypeEaseInOutBack.Content" xml:space="preserve">
<value>Ease-in-out back</value>
<value>回弹缓入缓出</value>
</data>
<data name="SettingsPageEasingTypeEaseInOutBounce.Content" xml:space="preserve">
<value>Ease-in-out bounce</value>
<value>弹跳缓入缓出</value>
</data>
<data name="SettingsPageEasingTypeEaseInOutCirc.Content" xml:space="preserve">
<value>Ease-in-out circ</value>
<value>圆形缓入缓出</value>
</data>
<data name="SettingsPageEasingTypeEaseInOutExpo.Content" xml:space="preserve">
<value>Ease-in-out expo</value>
<value>指数缓入缓出</value>
</data>
<data name="SettingsPageEasingTypeEaseInOutQuint.Content" xml:space="preserve">
<value>Ease-in-out quint</value>
<value>五次缓入缓出</value>
</data>
<data name="SettingsPageEasingTypeEaseInOutQuart.Content" xml:space="preserve">
<value>Ease-in-out quart</value>
<value>四次缓入缓出</value>
</data>
<data name="SettingsPageEasingTypeEaseInOutCubic.Content" xml:space="preserve">
<value>Ease-in-out cubic</value>
<value>三次缓入缓出</value>
</data>
<data name="SettingsPageLyricsRendingScopeLineStartToCurrentChar.Content" xml:space="preserve">
<value>Current line start to current char</value>
<value>当前歌词开始到当前字符</value>
</data>
<data name="SettingsPageLyricsHighlightScope.Header" xml:space="preserve">
<value>Highlight scope</value>
<value>高亮显示范围</value>
</data>
<data name="SettingsPageLyricsTimelineThreshold.Header" xml:space="preserve">
<value>Lyrics timeline sync threshold</value>
<value>歌词时间轴同步阈值</value>
</data>
<data name="SettingsPageLyricsTimelineThreshold.Description" xml:space="preserve">
<value>If the lyrics progress is jittery, try increasing this threshold; changing this value can cause lyrics synchronization to deviate</value>
<value>当歌词进度抖动时,请尝试增加该阈值;更改此值会导致歌词同步有偏差</value>
</data>
<data name="SettingsPageQQGroup.Header" xml:space="preserve">
<value>QQ feedback &amp; chat group</value>
<value>QQ 反馈交流群</value>
</data>
<data name="SettingsPageDiscord.Header" xml:space="preserve">
<value>Discord</value>
</data>
<data name="SettingsPageJoinNowButton.Content" xml:space="preserve">
<value>Join now</value>
<value>立即加入</value>
</data>
<data name="SettingsPageLyricsFloatAnimation.Header" xml:space="preserve">
<value>Floating animation</value>
<value>浮动动画</value>
</data>
<data name="SettingsPageScope.Header" xml:space="preserve">
<value>Scope</value>
<value>范围</value>
</data>
<data name="SettingsPageLockHotKey.Header" xml:space="preserve">
<value>Unlock and lock shortcut keys</value>
<value>解锁和锁定快捷键</value>
</data>
<data name="SettingsPageLXMusicServer.Header" xml:space="preserve">
<value>LX Music Server</value>
<value>LX 音乐服务器</value>
</data>
<data name="SettingsPageDockPlacement.Header" xml:space="preserve">
<value>Dock mode placement</value>
<value>停靠模式位置</value>
</data>
<data name="SettingsPageDockPlacementTop.Content" xml:space="preserve">
<value>Top</value>
<value>顶部</value>
</data>
<data name="SettingsPageDockPlacementBottom.Content" xml:space="preserve">
<value>Bottom</value>
<value>底部</value>
</data>
<data name="LyricsPageTranslationEnabled.Header" xml:space="preserve">
<value>Enable translation</value>
<value>启用翻译</value>
</data>
<data name="LyricsPageTranslationOnly.Header" xml:space="preserve">
<value>Show translation only</value>
<value>仅显示翻译</value>
</data>
<data name="SettingsPageLyricsBgFontOpacity.Header" xml:space="preserve">
<value>Font opacity (Non-current playback area)</value>
<value>字体不透明度(非当前播放区域)</value>
</data>
<data name="SettingsPageFAQ.Header" xml:space="preserve">
<value>Frequently asked questions</value>
<value>常见问题与解答</value>
</data>
<data name="FailToStartLXMusicServer" xml:space="preserve">
<value>Unable to connect to LX Music server, please go to Settings - Advanced options to check if the link is entered correctly</value>
<value>无法连接到 LX 音乐服务器,请转到设置 - 高级选项以检查是否正确输入链接</value>
</data>
<data name="SettingsPageHideWindow.Header" xml:space="preserve">
<value>Auto-hide window</value>
<value>自动隐藏窗口</value>
</data>
<data name="SettingsPageHideWindow.Description" xml:space="preserve">
<value>Automatically hide the window when no songs are playing in dock mode or desktop mode</value>
<value>停靠模式或桌面模式下,无歌曲正在播放时,自动隐藏窗口</value>
</data>
<data name="SettingsPageDockWindowHeight.Header" xml:space="preserve">
<value>Window height</value>
<value>窗口高度</value>
</data>
<data name="SettingsPageLyricsFontFamily.Header" xml:space="preserve">
<value>Lyrics font family</value>
<value>歌词字体</value>
</data>
<data name="SettingsPageGlobalDrag.Header" xml:space="preserve">
<value>Global drag</value>
<value>全局拖拽</value>
</data>
<data name="SettingsPageGlobalDrag.Description" xml:space="preserve">
<value>Extend the title bar to the entire page so that the window can be dragged in any non-interactive area</value>
<value>将标题栏扩展至整个页面使得在任意非交互区域均可拖拽窗口</value>
</data>
<data name="MusicGalleryPageTitle" xml:space="preserve">
<value>Music gallery</value>
<value>音乐库</value>
</data>
</root>

View File

@@ -59,46 +59,46 @@
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:schema xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" id="root">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace"/>
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
<xsd:element name="value" type="xsd:string" minOccurs="0"/>
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
<xsd:attribute name="name" use="required" type="xsd:string"/>
<xsd:attribute name="type" type="xsd:string"/>
<xsd:attribute name="mimetype" type="xsd:string"/>
<xsd:attribute ref="xml:space"/>
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
<xsd:attribute name="alias" type="xsd:string"/>
<xsd:attribute name="name" type="xsd:string"/>
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1"/>
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2"/>
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1"/>
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3"/>
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4"/>
<xsd:attribute ref="xml:space"/>
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1"/>
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
<xsd:attribute name="name" type="xsd:string" use="required"/>
</xsd:complexType>
</xsd:element>
</xsd:choice>

View File

@@ -59,46 +59,46 @@
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:schema xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" id="root">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace"/>
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
<xsd:element name="value" type="xsd:string" minOccurs="0"/>
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
<xsd:attribute name="name" use="required" type="xsd:string"/>
<xsd:attribute name="type" type="xsd:string"/>
<xsd:attribute name="mimetype" type="xsd:string"/>
<xsd:attribute ref="xml:space"/>
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
<xsd:attribute name="alias" type="xsd:string"/>
<xsd:attribute name="name" type="xsd:string"/>
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1"/>
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2"/>
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1"/>
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3"/>
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4"/>
<xsd:attribute ref="xml:space"/>
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1"/>
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
<xsd:attribute name="name" type="xsd:string" use="required"/>
</xsd:complexType>
</xsd:element>
</xsd:choice>

View File

@@ -59,46 +59,46 @@
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:schema xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" id="root">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace"/>
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
<xsd:element name="value" type="xsd:string" minOccurs="0"/>
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
<xsd:attribute name="name" use="required" type="xsd:string"/>
<xsd:attribute name="type" type="xsd:string"/>
<xsd:attribute name="mimetype" type="xsd:string"/>
<xsd:attribute ref="xml:space"/>
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
<xsd:attribute name="alias" type="xsd:string"/>
<xsd:attribute name="name" type="xsd:string"/>
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1"/>
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2"/>
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1"/>
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3"/>
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4"/>
<xsd:attribute ref="xml:space"/>
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1"/>
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
<xsd:attribute name="name" type="xsd:string" use="required"/>
</xsd:complexType>
</xsd:element>
</xsd:choice>

View File

@@ -59,46 +59,46 @@
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:schema xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" id="root">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace"/>
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
<xsd:element name="value" type="xsd:string" minOccurs="0"/>
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
<xsd:attribute name="name" use="required" type="xsd:string"/>
<xsd:attribute name="type" type="xsd:string"/>
<xsd:attribute name="mimetype" type="xsd:string"/>
<xsd:attribute ref="xml:space"/>
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
<xsd:attribute name="alias" type="xsd:string"/>
<xsd:attribute name="name" type="xsd:string"/>
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1"/>
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2"/>
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1"/>
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3"/>
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4"/>
<xsd:attribute ref="xml:space"/>
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1"/>
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
<xsd:attribute name="name" type="xsd:string" use="required"/>
</xsd:complexType>
</xsd:element>
</xsd:choice>

View File

@@ -7,22 +7,20 @@ using Microsoft.UI.Dispatching;
namespace BetterLyrics.WinUI3.ViewModels
{
public partial class BaseViewModel : ObservableRecipient, IDisposable
public partial class BaseViewModel : ObservableRecipient
{
private protected readonly DispatcherQueue _dispatcherQueue =
DispatcherQueue.GetForCurrentThread();
private protected readonly DispatcherQueue _dispatcherQueue;
private protected readonly DispatcherQueueTimer _dispatcherQueueTimer;
private protected readonly ISettingsService _settingsService;
public BaseViewModel(ISettingsService settingsService)
{
IsActive = true;
_dispatcherQueue = DispatcherQueue.GetForCurrentThread();
_dispatcherQueueTimer = _dispatcherQueue.CreateTimer();
_settingsService = settingsService;
}
public void Dispose()
{
GC.SuppressFinalize(this);
}
}
}

View File

@@ -9,9 +9,11 @@ using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using CommunityToolkit.Mvvm.Messaging;
using CommunityToolkit.Mvvm.Messaging.Messages;
using CommunityToolkit.WinUI;
using Microsoft.UI.Dispatching;
using Microsoft.UI.Xaml;
using System;
using System.Diagnostics;
using System.Numerics;
using System.Threading.Tasks;
@@ -24,6 +26,7 @@ namespace BetterLyrics.WinUI3.ViewModels
IRecipient<PropertyChangedMessage<TimeSpan>>
{
private readonly IPlaybackService _playbackService;
private readonly ThrottleHelper _timelineThrottle = new(TimeSpan.FromSeconds(1));
public LyricsPageViewModel(ISettingsService settingsService, IPlaybackService playbackService) : base(settingsService)
{
@@ -49,10 +52,10 @@ namespace BetterLyrics.WinUI3.ViewModels
IsSongPlaying = _playbackService.IsPlaying;
}
private void SystemVolumeHelper_VolumeChanged(int volume)
{
Volume = volume;
}
//private void SystemVolumeHelper_VolumeChanged(int volume)
//{
// Volume = volume;
//}
private void PlaybackService_IsPlayingChanged(object? sender, Events.IsPlayingChangedEventArgs e)
{
@@ -245,10 +248,10 @@ namespace BetterLyrics.WinUI3.ViewModels
}
}
partial void OnVolumeChanged(int value)
{
SystemVolumeHelper.SetMasterVolume(value);
}
//partial void OnVolumeChanged(int value)
//{
// SystemVolumeHelper.SetMasterVolume(value);
//}
public void Receive(PropertyChangedMessage<TimeSpan> message)
{
@@ -256,10 +259,13 @@ namespace BetterLyrics.WinUI3.ViewModels
{
if (message.PropertyName == nameof(LyricsRendererViewModel.TotalTime))
{
_dispatcherQueue.TryEnqueue(DispatcherQueuePriority.Low, () =>
if (_timelineThrottle.CanTrigger())
{
TimelinePositionSeconds = message.NewValue.TotalSeconds;
});
_dispatcherQueue.TryEnqueue(DispatcherQueuePriority.Low, () =>
{
TimelinePositionSeconds = message.NewValue.TotalSeconds;
});
}
}
}
}

View File

@@ -133,8 +133,7 @@ namespace BetterLyrics.WinUI3.ViewModels
BlackPoint = new Vector2(blackX, blackY),
},
Opacity = opacity,
}, new Vector2(x, y)
);
}, new Vector2(x, y));
}
private void DrawForegroundImgae(ICanvasAnimatedControl control, CanvasDrawingSession ds, CanvasBitmap canvasBitmap, float opacity)

View File

@@ -91,20 +91,23 @@ namespace BetterLyrics.WinUI3.ViewModels
{
_isDockMode = message.NewValue;
UpdateColorConfig();
UpdateImmersiveBackgroundOpacity();
}
else if (message.PropertyName == nameof(LyricsWindowViewModel.IsDesktopMode))
{
_isDesktopMode = message.NewValue;
UpdateColorConfig();
UpdateImmersiveBackgroundOpacity();
}
else if (message.PropertyName == nameof(LyricsWindowViewModel.IsLyricsWindowLocked))
{
_isLyricsWindowLocked = message.NewValue;
UpdateImmersiveBackgroundOpacity();
}
else if (message.PropertyName == nameof(LyricsWindowViewModel.IsMouseWithinWindow))
{
_isMouseWithinWindow = message.NewValue;
_immersiveBgOpacityTransition.StartTransition(_isDesktopMode ? (_isMouseWithinWindow ? 1f : 0f) : 1f);
UpdateImmersiveBackgroundOpacity();
}
}
else if (message.Sender is LyricsPageViewModel)
@@ -334,6 +337,5 @@ namespace BetterLyrics.WinUI3.ViewModels
}
}
}
}
}

View File

@@ -411,5 +411,33 @@ namespace BetterLyrics.WinUI3.ViewModels
line.HighlightOpacityTransition.Update(_elapsedTime);
}
}
private void UpdateImmersiveBackgroundOpacity()
{
float targetOpacity;
if (_isDesktopMode)
{
if (_isLyricsWindowLocked)
{
targetOpacity = 0;
}
else
{
if (_isMouseWithinWindow)
{
targetOpacity = 1f;
}
else
{
targetOpacity = 0f;
}
}
}
else
{
targetOpacity = 1f;
}
_immersiveBgOpacityTransition.StartTransition(targetOpacity);
}
}
}

View File

@@ -446,10 +446,12 @@ namespace BetterLyrics.WinUI3.ViewModels
}
else
{
string translated = string.Empty;
try
{
var translated = await _translateService.TranslateTextAsync(originalText, targetLangCode, token);
token.ThrowIfCancellationRequested();
translated = await _translateService.TranslateTextAsync(originalText, targetLangCode, token);
if (translated == string.Empty) return;
if (_showTranslationOnly)
{
_lyricsDataArr[^1] = _lyricsDataArr[0].CreateLyricsDataFrom(translated);
@@ -461,6 +463,7 @@ namespace BetterLyrics.WinUI3.ViewModels
_lyricsDataArr[0].SetDisplayedTextAlongWith(translated);
_langIndex = 0;
}
token.ThrowIfCancellationRequested();
}
catch (Exception) { }
}

View File

@@ -11,6 +11,7 @@ using CommunityToolkit.Mvvm.DependencyInjection;
using CommunityToolkit.Mvvm.Input;
using CommunityToolkit.Mvvm.Messaging;
using CommunityToolkit.Mvvm.Messaging.Messages;
using CommunityToolkit.WinUI;
using Microsoft.UI;
using Microsoft.UI.Windowing;
using Microsoft.UI.Xaml;
@@ -231,11 +232,14 @@ namespace BetterLyrics.WinUI3
hwnd,
onWindowChanged =>
{
if (_ignoreFullscreenWindow && window.AppWindow.Presenter is OverlappedPresenter presenter)
_dispatcherQueueTimer.Debounce(() =>
{
presenter.IsAlwaysOnTop = true;
}
UpdateAccentColor(hwnd);
if (_ignoreFullscreenWindow && window.AppWindow.Presenter is OverlappedPresenter presenter)
{
presenter.IsAlwaysOnTop = true;
}
UpdateAccentColor(hwnd);
}, TimeSpan.FromMilliseconds(300));
}
);
_windowWatcher.Start();

View File

@@ -5,6 +5,7 @@ using BetterLyrics.WinUI3.Services;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Messaging;
using CommunityToolkit.Mvvm.Messaging.Messages;
using Microsoft.UI.Dispatching;
using Microsoft.UI.Xaml.Controls;
using System;
using System.Collections.Generic;
@@ -25,16 +26,20 @@ namespace BetterLyrics.WinUI3.ViewModels
{
private readonly ILibWatcherService _libWatcherService;
private readonly MediaPlayer _mediaPlayer = new();
private readonly MediaTimelineController _timelineController = new();
private readonly SystemMediaTransportControls _smtc;
private List<Track> _tracks = [];
[ObservableProperty]
public partial ObservableCollection<Track> Tracks { get; set; } = [];
public partial ObservableCollection<GroupInfoList> TracksByTitle { get; set; } = [];
[ObservableProperty]
public partial bool IsDataLoading { get; set; } = false;
public MusicGalleryViewModel(ISettingsService settingsService, ILibWatcherService libWatcherService) : base(settingsService)
{
_timelineController = _mediaPlayer.TimelineController = new();
_timelineController.PositionChanged += TimelineController_PositionChanged;
_smtc = _mediaPlayer.SystemMediaTransportControls;
_mediaPlayer.CommandManager.IsEnabled = false;
_smtc.IsEnabled = true;
@@ -43,15 +48,18 @@ namespace BetterLyrics.WinUI3.ViewModels
_smtc.IsNextEnabled = true;
_smtc.IsPreviousEnabled = true;
_smtc.ButtonPressed += Smtc_ButtonPressed;
_smtc.PlaybackPositionChangeRequested += Smtc_PlaybackPositionChangeRequested;
_libWatcherService = libWatcherService;
_libWatcherService.MusicLibraryFilesChanged += LibWatcherService_MusicLibraryFilesChanged;
}
private void Smtc_PlaybackPositionChangeRequested(SystemMediaTransportControls sender, PlaybackPositionChangeRequestedEventArgs args)
private void TimelineController_PositionChanged(MediaTimelineController sender, object args)
{
_mediaPlayer.TimelineController.Position = args.RequestedPlaybackPosition;
_smtc.UpdateTimelineProperties(new SystemMediaTransportControlsTimelineProperties()
{
Position = sender.Position,
EndTime = sender.Duration ?? TimeSpan.Zero
});
}
private void Smtc_ButtonPressed(SystemMediaTransportControls sender, SystemMediaTransportControlsButtonPressedEventArgs args)
@@ -83,7 +91,7 @@ namespace BetterLyrics.WinUI3.ViewModels
public void RefreshSongs()
{
IsDataLoading = true;
Tracks.Clear();
_tracks.Clear();
Task.Run(() =>
{
@@ -94,16 +102,17 @@ namespace BetterLyrics.WinUI3.ViewModels
foreach (var file in Directory.GetFiles(folder.Path, $"*.*", SearchOption.AllDirectories))
{
Track track = new(file);
_dispatcherQueue.TryEnqueue(() =>
_dispatcherQueue.TryEnqueue(DispatcherQueuePriority.Low, () =>
{
Tracks.Add(track);
_tracks.Add(track);
});
}
}
}
_dispatcherQueue.TryEnqueue(() =>
_dispatcherQueue.TryEnqueue(DispatcherQueuePriority.Low, () =>
{
TracksByTitle.AddRange(_tracks.GetGroupedByTitleAsync());
IsDataLoading = false;
});
});
@@ -113,7 +122,7 @@ namespace BetterLyrics.WinUI3.ViewModels
{
if (index.HasValue)
{
var track = Tracks.ElementAtOrDefault(index.Value);
var track = _tracks.ElementAtOrDefault(index.Value);
if (track != null)
{
_mediaPlayer.Source = MediaSource.CreateFromUri(new Uri(track.Path));
@@ -127,8 +136,9 @@ namespace BetterLyrics.WinUI3.ViewModels
{
updater.Thumbnail = ImageHelper.ByteArrayToRandomAccessStreamReference(pictureData);
}
_timelineController.Duration = TimeSpan.FromSeconds(track.Duration);
_timelineController.Start();
updater.Update();
_mediaPlayer.Play();
_smtc.PlaybackStatus = MediaPlaybackStatus.Playing;
}
}

View File

@@ -7,6 +7,7 @@ using BetterLyrics.WinUI3.Services;
using BetterLyrics.WinUI3.Views;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using Microsoft.UI.Dispatching;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using System;
@@ -16,7 +17,6 @@ using System.Linq;
using System.Threading.Tasks;
using Windows.ApplicationModel;
using Windows.Globalization;
using Windows.System;
using Windows.UI;
using WinRT.Interop;
using MetadataHelper = BetterLyrics.WinUI3.Helper.MetadataHelper;
@@ -102,7 +102,7 @@ namespace BetterLyrics.WinUI3.ViewModels
Task.Run(async () =>
{
BuildDate = (await Helper.MetadataHelper.GetBuildDate()).ToString("(yyyy/MM/dd HH:mm:ss)");
BuildDate = (await MetadataHelper.GetBuildDate()).ToString("(yyyy/MM/dd HH:mm:ss)");
});
}
@@ -258,7 +258,7 @@ namespace BetterLyrics.WinUI3.ViewModels
public partial int LyricsVerticalEdgeOpacity { get; set; }
[ObservableProperty]
public partial object NavViewSelectedItemTag { get; set; }
public partial object NavViewSelectedItemTag { get; set; } = "App";
[ObservableProperty]
[NotifyPropertyChangedRecipients]
@@ -411,13 +411,13 @@ namespace BetterLyrics.WinUI3.ViewModels
[RelayCommand]
private async Task LaunchProjectGitHubPageAsync()
{
await Launcher.LaunchUriAsync(new Uri(MetadataHelper.GithubUrl));
await Windows.System.Launcher.LaunchUriAsync(new Uri(MetadataHelper.GithubUrl));
}
[RelayCommand]
private static async Task OpenCacheFolderAsync()
{
await Launcher.LaunchFolderPathAsync(PathHelper.CacheFolder);
await Windows.System.Launcher.LaunchFolderPathAsync(PathHelper.CacheFolder);
}
[RelayCommand]
@@ -456,7 +456,7 @@ namespace BetterLyrics.WinUI3.ViewModels
{
string targetLangCode = LanguageHelper.SupportedTargetLanguages[SelectedTargetLanguageIndex].Code;
string result = await _libreTranslateService.TranslateTextAsync("Hello, world!", targetLangCode, null);
_dispatcherQueue.TryEnqueue(() =>
_dispatcherQueue.TryEnqueue(DispatcherQueuePriority.Low, () =>
{
App.Current.SettingsWindowNotificationPanel?.Notify(App.ResourceLoader!.GetString("SettingsPageServerTestSuccessInfo"), Microsoft.UI.Xaml.Controls.InfoBarSeverity.Success);
IsLibreTranslateServerTesting = false;
@@ -464,7 +464,7 @@ namespace BetterLyrics.WinUI3.ViewModels
}
catch (Exception)
{
_dispatcherQueue.TryEnqueue(() =>
_dispatcherQueue.TryEnqueue(DispatcherQueuePriority.Low, () =>
{
App.Current.SettingsWindowNotificationPanel?.Notify(App.ResourceLoader!.GetString("SettingsPageServerTestFailedInfo"), Microsoft.UI.Xaml.Controls.InfoBarSeverity.Error);
IsLibreTranslateServerTesting = false;
@@ -480,7 +480,7 @@ namespace BetterLyrics.WinUI3.ViewModels
Task.Run(async () =>
{
bool testResult = await NetHelper.CheckConnectivity($"{LXMusicServer}/status");
_dispatcherQueue.TryEnqueue(() =>
_dispatcherQueue.TryEnqueue(DispatcherQueuePriority.Low, () =>
{
App.Current.SettingsWindowNotificationPanel?.Notify(
App.ResourceLoader!.GetString($"SettingsPageServerTest{(testResult ? "Success" : "Failed")}Info"),

View File

@@ -5,6 +5,7 @@ using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using CommunityToolkit.Mvvm.Messaging;
using CommunityToolkit.Mvvm.Messaging.Messages;
using Microsoft.UI.Xaml;
namespace BetterLyrics.WinUI3.ViewModels
{
@@ -34,7 +35,12 @@ namespace BetterLyrics.WinUI3.ViewModels
[RelayCommand]
private static void ExitApp()
{
WindowHelper.ExitAllWindows();
LyricsWindow? lyricsWindow = WindowHelper.GetWindowByWindowType<LyricsWindow>();
if (lyricsWindow != null)
{
DockModeHelper.Disable(lyricsWindow);
}
Application.Current.Exit();
}
[RelayCommand]

View File

@@ -193,13 +193,12 @@
Spacing="3">
<!-- Volume -->
<Button
Click="VolumeButton_Click"
Style="{StaticResource GhostButtonStyle}"
Visibility="Collapsed">
<!--<Button Click="VolumeButton_Click" Style="{StaticResource GhostButtonStyle}">
<Grid>
<!-- Volumn: 0 -->
-->
<!-- Volumn: 0 -->
<!--
<FontIcon
x:Name="VolumeLevel0"
FontFamily="{StaticResource IconFontFamily}"
@@ -209,7 +208,9 @@
</FontIcon.OpacityTransition>
</FontIcon>
<!-- Volumn: 1-32 -->
-->
<!-- Volumn: 1-32 -->
<!--
<FontIcon
x:Name="VolumeLevel1"
FontFamily="{StaticResource IconFontFamily}"
@@ -219,7 +220,9 @@
</FontIcon.OpacityTransition>
</FontIcon>
<!-- Volumn: 33-65 -->
-->
<!-- Volumn: 33-65 -->
<!--
<FontIcon
x:Name="VolumeLevel2"
FontFamily="{StaticResource IconFontFamily}"
@@ -229,7 +232,9 @@
</FontIcon.OpacityTransition>
</FontIcon>
<!-- Volumn: 66-100 -->
-->
<!-- Volumn: 66-100 -->
<!--
<FontIcon
x:Name="VolumeLevel3"
FontFamily="{StaticResource IconFontFamily}"
@@ -257,7 +262,7 @@
</StackPanel>
</Flyout>
</Button.DataContext>
</Button>
</Button>-->
<!-- Translation -->
<Button
@@ -340,11 +345,11 @@
Margin="0,-32,0,0"
HorizontalAlignment="Stretch"
VerticalAlignment="Top"
Maximum="{x:Bind ViewModel.SongDurationSeconds, Mode=OneWay}"
Maximum="{Binding ElementName=TimelineSlider, Path=Maximum}"
Minimum="0"
Style="{StaticResource TransparentSliderStyle}"
ThumbToolTipValueConverter="{StaticResource SecondsToFormattedTimeConverter}"
ValueChanged="TimelineSliderOverlay_ValueChanged" />
Tapped="TimelineSliderOverlay_Tapped"
ThumbToolTipValueConverter="{StaticResource SecondsToFormattedTimeConverter}" />
</Grid>
</Grid>
@@ -395,10 +400,10 @@
IsOpen="{x:Bind ViewModel.IsWelcomeTeachingTipOpen, Mode=OneWay}"
Target="{x:Bind RootGrid}" />
<!--<uc:SystemTray />-->
<uc:SystemTray />
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="VolumeState">
<!--<VisualStateGroup x:Name="VolumeState">
<VisualState x:Name="Volume0">
<VisualState.StateTriggers>
<ui:CompareStateTrigger
@@ -455,7 +460,7 @@
<Setter Target="VolumeLevel3.Opacity" Value="1" />
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateGroup>-->
</VisualStateManager.VisualStateGroups>
</Grid>
</Page>

View File

@@ -111,15 +111,10 @@ namespace BetterLyrics.WinUI3.Views
}
}
private async void TimelineSliderOverlay_ValueChanged(object sender, Microsoft.UI.Xaml.Controls.Primitives.RangeBaseValueChangedEventArgs e)
{
await _playbackService.ChangePosition(e.NewValue);
}
private void VolumeButton_Click(object sender, RoutedEventArgs e)
{
VolumeFlyout.ShowAt(BottomRightCommandStackPanel);
}
//private void VolumeButton_Click(object sender, RoutedEventArgs e)
//{
// VolumeFlyout.ShowAt(BottomRightCommandStackPanel);
//}
private void BottomCommandFlyoutTrigger_PointerEntered(object sender, Microsoft.UI.Xaml.Input.PointerRoutedEventArgs e)
{
@@ -144,5 +139,10 @@ namespace BetterLyrics.WinUI3.Views
BottomCommandFlyout.ShowAt(BottomCommandFlyoutTrigger);
}
}
private void TimelineSliderOverlay_Tapped(object sender, Microsoft.UI.Xaml.Input.TappedRoutedEventArgs e)
{
_playbackService.ChangePosition(TimelineSliderOverlay.Value);
}
}
}

View File

@@ -26,6 +26,7 @@
<Grid
x:Name="TopCommandGrid"
VerticalAlignment="Top"
Background="Transparent"
Opacity="{x:Bind ViewModel.TopCommandGridOpacity, Mode=OneWay}"
PointerEntered="TopCommandGrid_PointerEntered"
PointerExited="TopCommandGrid_PointerExited">

View File

@@ -69,6 +69,14 @@ namespace BetterLyrics.WinUI3.Views
switch (type!)
{
case AutoStartWindowType.StandardMode:
if (_settingsService.StandardWindowLeft < 0 || _settingsService.StandardWindowTop < 0 ||
_settingsService.StandardWindowWidth <= 0 || _settingsService.StandardWindowHeight <= 0)
{
_settingsService.StandardWindowLeft = 200;
_settingsService.StandardWindowTop = 200;
_settingsService.StandardWindowWidth = 1600;
_settingsService.StandardWindowHeight = 800;
}
AppWindow.MoveAndResize(new Windows.Graphics.RectInt32(
_settingsService.StandardWindowLeft,
_settingsService.StandardWindowTop,
@@ -262,7 +270,7 @@ namespace BetterLyrics.WinUI3.Views
private void CloseButton_Click(object sender, RoutedEventArgs e)
{
DockModeHelper.Disable(this);
App.Current.Exit();
Application.Current.Exit();
}
private void MaximiseButton_Click(object sender, RoutedEventArgs e)

View File

@@ -3,6 +3,7 @@
x:Class="BetterLyrics.WinUI3.Views.MusicGalleryPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:atl="using:ATL"
xmlns:controls="using:CommunityToolkit.WinUI.Controls"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:interactivity="using:Microsoft.Xaml.Interactivity"
@@ -10,16 +11,32 @@
xmlns:local="using:BetterLyrics.WinUI3.Views"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:media="using:CommunityToolkit.WinUI.Media"
xmlns:models="using:BetterLyrics.WinUI3.Models"
xmlns:ui="using:CommunityToolkit.WinUI"
Loaded="Page_Loaded"
mc:Ignorable="d">
<Page.Resources>
<CollectionViewSource
x:Name="TracksByTitleCVS"
IsSourceGrouped="True"
Source="{x:Bind ViewModel.TracksByTitle, Mode=OneWay}" />
</Page.Resources>
<Grid>
<Grid Padding="0,12,0,0">
<Grid Margin="0,12,0,0">
<AutoSuggestBox
x:Name="SongSearchBox"
Margin="36,0"
VerticalAlignment="Top"
PlaceholderText="搜索歌曲"
QueryIcon="Find"
QuerySubmitted="SongSearchBox_QuerySubmitted"
SuggestionChosen="SongSearchBox_SuggestionChosen"
TextChanged="SongSearchBox_TextChanged" />
<controls:Segmented
x:Name="Segmented"
Margin="36,0"
Margin="36,48,36,0"
HorizontalAlignment="Stretch"
VerticalAlignment="Top"
SelectedIndex="0"
@@ -49,121 +66,136 @@
<controls:SegmentedItem Content="艺术家" Icon="{ui:FontIcon FontFamily={StaticResource IconFontFamily}, Glyph=&#xEFA9;}" />
</controls:Segmented>
<controls:OpacityMaskView Margin="0,36,0,0" HorizontalContentAlignment="Stretch">
<controls:OpacityMaskView.OpacityMask>
<Rectangle>
<Rectangle.Fill>
<LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
<GradientStop Offset="0" Color="Transparent" />
<GradientStop Offset="0.05" Color="White" />
<GradientStop Offset="0.95" Color="White" />
<GradientStop Offset="1" Color="Transparent" />
</LinearGradientBrush>
</Rectangle.Fill>
</Rectangle>
</controls:OpacityMaskView.OpacityMask>
<Grid>
<ScrollViewer>
<controls:SwitchPresenter Padding="36,6" Value="{Binding ElementName=Segmented, Path=Tag, Mode=OneWay}">
<controls:SwitchPresenter.ContentTransitions>
<TransitionCollection>
<PopupThemeTransition />
</TransitionCollection>
</controls:SwitchPresenter.ContentTransitions>
<controls:SwitchPresenter Margin="0,96,0,0" Value="{Binding ElementName=Segmented, Path=Tag, Mode=OneWay}">
<controls:SwitchPresenter.ContentTransitions>
<TransitionCollection>
<PopupThemeTransition />
</TransitionCollection>
</controls:SwitchPresenter.ContentTransitions>
<controls:Case Value="Song">
<ListView ItemsSource="{x:Bind ViewModel.Tracks, Mode=OneWay}" SelectionChanged="SongListView_SelectionChanged">
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
<Setter Property="Margin" Value="0" />
<Setter Property="Padding" Value="0" />
</Style>
</ListView.ItemContainerStyle>
<ListView.ItemTemplate>
<DataTemplate>
<Grid Padding="12">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="2*" />
<!-- 歌曲名 -->
<ColumnDefinition Width="1.5*" />
<!-- 歌手名 -->
<ColumnDefinition Width="1.5*" />
<!-- 专辑名 -->
<ColumnDefinition Width="1*" />
<!-- 年份 -->
<ColumnDefinition Width="1.2*" />
<!-- 流派 -->
<ColumnDefinition Width="1*" />
<!-- 歌曲时长 -->
</Grid.ColumnDefinitions>
<controls:Case Value="Song">
<SemanticZoom>
<SemanticZoom.ZoomedInView>
<GridView
ItemsSource="{x:Bind TracksByTitleCVS.View, Mode=OneWay}"
ScrollViewer.IsHorizontalScrollChainingEnabled="False"
SelectionMode="None">
<GridView.GroupStyle>
<GroupStyle />
</GridView.GroupStyle>
</GridView>
</SemanticZoom.ZoomedInView>
<SemanticZoom.ZoomedOutView>
<ListView ItemsSource="{x:Bind TracksByTitleCVS.View.CollectionGroups, Mode=OneWay}" SelectionChanged="SongListView_SelectionChanged">
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<ItemsStackPanel AreStickyGroupHeadersEnabled="True" />
</ItemsPanelTemplate>
</ListView.ItemsPanel>
<ListView.GroupStyle>
<GroupStyle>
<GroupStyle.HeaderTemplate>
<DataTemplate x:DataType="models:GroupInfoList">
<Border AutomationProperties.AccessibilityView="Raw">
<TextBlock
AutomationProperties.AccessibilityView="Raw"
Style="{ThemeResource TitleTextBlockStyle}"
Text="{x:Bind Key}" />
</Border>
</DataTemplate>
</GroupStyle.HeaderTemplate>
</GroupStyle>
</ListView.GroupStyle>
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
<Setter Property="Margin" Value="0" />
<Setter Property="Padding" Value="36,0" />
</Style>
</ListView.ItemContainerStyle>
<ListView.ItemTemplate>
<DataTemplate x:DataType="atl:Track">
<Grid Padding="12">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="2*" />
<!-- 歌曲名 -->
<TextBlock
Grid.Column="0"
VerticalAlignment="Center"
Text="{Binding Title}"
TextWrapping="Wrap" />
<ColumnDefinition Width="1.5*" />
<!-- 歌手名 -->
<TextBlock
Grid.Column="1"
VerticalAlignment="Center"
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
Text="{Binding Artist}"
TextWrapping="Wrap" />
<ColumnDefinition Width="1.5*" />
<!-- 专辑名 -->
<TextBlock
Grid.Column="2"
VerticalAlignment="Center"
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
Text="{Binding Album}"
TextWrapping="Wrap" />
<ColumnDefinition Width="1*" />
<!-- 年份 -->
<TextBlock
Grid.Column="3"
VerticalAlignment="Center"
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
Text="{Binding Year}"
TextWrapping="Wrap" />
<ColumnDefinition Width="1.2*" />
<!-- 流派 -->
<TextBlock
Grid.Column="4"
VerticalAlignment="Center"
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
Text="{Binding Genre}"
TextWrapping="Wrap" />
<ColumnDefinition Width="1*" />
<!-- 歌曲时长 -->
<TextBlock
Grid.Column="5"
VerticalAlignment="Center"
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
Text="{Binding Duration, Converter={StaticResource SecondsToFormattedTimeConverter}}"
TextAlignment="Right"
TextWrapping="Wrap" />
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</controls:Case>
</Grid.ColumnDefinitions>
<controls:Case Value="Album">
<ListView />
</controls:Case>
<!-- 歌曲名 -->
<TextBlock
Grid.Column="0"
VerticalAlignment="Center"
Text="{Binding Title}"
TextWrapping="Wrap" />
<controls:Case Value="Artist">
<ListView />
</controls:Case>
<!-- 歌手名 -->
<TextBlock
Grid.Column="1"
VerticalAlignment="Center"
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
Text="{Binding Artist}"
TextWrapping="Wrap" />
</controls:SwitchPresenter>
<!-- 专辑名 -->
<TextBlock
Grid.Column="2"
VerticalAlignment="Center"
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
Text="{Binding Album}"
TextWrapping="Wrap" />
</ScrollViewer>
</Grid>
</controls:OpacityMaskView>
<!-- 年份 -->
<TextBlock
Grid.Column="3"
VerticalAlignment="Center"
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
Text="{Binding Year}"
TextWrapping="Wrap" />
<!-- 流派 -->
<TextBlock
Grid.Column="4"
VerticalAlignment="Center"
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
Text="{Binding Genre}"
TextWrapping="Wrap" />
<!-- 歌曲时长 -->
<TextBlock
Grid.Column="5"
VerticalAlignment="Center"
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
Text="{Binding Duration, Converter={StaticResource SecondsToFormattedTimeConverter}}"
TextAlignment="Right"
TextWrapping="Wrap" />
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</SemanticZoom.ZoomedOutView>
</SemanticZoom>
</controls:Case>
<controls:Case Value="Album">
<ListView />
</controls:Case>
<controls:Case Value="Artist">
<ListView />
</controls:Case>
</controls:SwitchPresenter>
</Grid>
<Grid Background="{ThemeResource SolidBackgroundFillColorBaseBrush}" Visibility="{x:Bind ViewModel.IsDataLoading, Mode=OneWay, Converter={StaticResource BoolToVisibilityConverter}}">
@@ -184,6 +216,5 @@
</Grid>
<ProgressRing IsActive="{x:Bind ViewModel.IsDataLoading, Mode=OneWay}" />
</Grid>
</Grid>
</Page>

View File

@@ -41,5 +41,20 @@ namespace BetterLyrics.WinUI3.Views
{
ViewModel.RefreshSongs();
}
private void SongSearchBox_QuerySubmitted(AutoSuggestBox sender, AutoSuggestBoxQuerySubmittedEventArgs args)
{
}
private void SongSearchBox_SuggestionChosen(AutoSuggestBox sender, AutoSuggestBoxSuggestionChosenEventArgs args)
{
}
private void SongSearchBox_TextChanged(AutoSuggestBox sender, AutoSuggestBoxTextChangedEventArgs args)
{
}
}
}

View File

@@ -1,10 +1,28 @@
### I couldn't see any button that I can interact with
### I couldn't see any button that I can interact with / 我找不到任何可交互的按钮
This app is built with immersive experience, just hover your mouse on the top/bottom area of the app and then you'll see everything.
By default, all the buttons are shown but if you click on the "Immersive" button (as shown in the picture), all the buttons will disappear. But don't worry, one you hover again, it will shown up to you!
![alt text](image-3.png)
默认情况下,所有按钮都会显示,但如果您点击 “沉浸” 按钮(如图左上位置所示),所有按钮都会消失。不过不用担心,当您再次将鼠标悬停在按钮上时,它们就会重新显示出来!
![alt text](image-4.png)
![alt text](image-9.png)
It is important to note that when you enter "Docked Mode", the action buttons are hidden. Hover your mouse over the top to access the "Immerse", "More", and "Close" buttons.
需要特别注意的是,当您进入 “停靠模式” 时,会隐藏操作按钮。将鼠标悬停在顶部以访问 “沉浸” 按钮、“更多” 按钮以及 “关闭” 按钮
![alt text](image-10.png)
Hover the mouse slightly above the bottom edge of the window to display the white control floating window at the bottom
将鼠标悬浮在窗口下边缘稍靠上位置以显示底部控制浮窗小白条
![alt text](image-11.png)
Tap the "little white bar" to display the bottom control bar in floating window form (including current playback progress view, timeline offset adjustment; previous song, pause/play, next song; translation, layout, settings)
点按 “小白条” 即可以浮窗形式显示底部控制栏(包括当前播放进度查看、时间轴偏移调整;上一首、暂停/播放、下一首; 翻译、布局、设置)
![alt text](image-12.png)
### I have set up all the settings related to translation but there is no translation at all
@@ -32,11 +50,7 @@ Hover you mouse on the very bottom of the app,
And then click on the first icon button (Lyrics timeline offset), here you can adjust the offset freely.
### I'm using Apple Music, the lyrics is moving forward and afterward constantly
![alt text](image-1.png)
Hover your mouse at the very bottom of the app and then click on the very last icon button to go to settings page.
### The lyrics jump back and forth frequently / 歌词频繁前后跳动
![alt text](image-2.png)

BIN
FAQ/image-10.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 224 KiB

BIN
FAQ/image-11.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 221 KiB

BIN
FAQ/image-12.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 367 KiB

BIN
FAQ/image-9.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 250 KiB

View File

@@ -1,8 +1,8 @@
> 注:以下内容使用 https://claude.ai/ 依照英文原文翻译
<a href="https://github.com/jayfunc/BetterLyrics/blob/dev/README.md">_**🌐Click here to see the English version**_</a>
<a href="https://github.com/jayfunc/BetterLyrics/blob/dev/README.md">_**🌐 Click here to see the English version**_</a>
<a href="https://github.com/jayfunc/BetterLyrics/blob/dev/FAQ/FAQ.md">_**❓点击查看常见问题FAQ**_</a>
<a href="https://github.com/jayfunc/BetterLyrics/blob/dev/FAQ/FAQ.md">_**❓ 点击查看常见问题FAQ**_</a>
<div align="center">
<img src="BetterLyrics.WinUI3/BetterLyrics.WinUI3/Assets/Logo.png" alt="" width="64"/>
@@ -87,6 +87,7 @@ BetterLyrics
- 请先安装 [BetterNCM 插件](https://microblock.cc/betterncm) 安装完成后如若弹出降级指引,请根据指引完成网易云音乐的降级操作(降级至 2.10.13
- 之后请在 PluginMarket 内安装 InfLink 插件,安装完成后请重启网易云音乐。至此,所有预备操作均已完成,尽情享用吧!
- 酷狗音乐
- 请确保酷狗音乐设置项 “支持系统播放控件,如锁屏界面” 已开启
- 不会广播时间线信息这意味着当您在酷狗音乐中更改播放进度时BetterLyrics无法检测到此更改。
- Apple Music
- 确保您在设置中将时间线阈值设置为约600毫秒进入"设置"-"高级选项"进行更改),否则歌词会不断前后跳动。
@@ -153,6 +154,12 @@ BetterLyrics
- [椒盐音乐 Salt Player](https://moriafly.com/program/salt-player)
- [MyToolBar](https://github.com/TwilightLemon/MyToolBar)
## ✍️ 帮助我们翻译成您的语言
找不到您的语言?
别担心!立即开始翻译,成为贡献者!😆
点击[链接](https://crowdin.com/project/betterlyrics/invite?h=d767e4f2dbd832d8fcdb6f7e5a198b402502866),立即通过 Crowdin 将这款应用翻译成您的语言!
## Star 历史
[![Star History Chart](https://api.star-history.com/svg?repos=jayfunc/BetterLyrics&type=Date)](https://www.star-history.com/#jayfunc/BetterLyrics&Date)

View File

@@ -1,6 +1,6 @@
<a href="https://github.com/jayfunc/BetterLyrics/blob/dev/README.CN.md">_**🌐点此处查看中文说明**_</a>
<a href="https://github.com/jayfunc/BetterLyrics/blob/dev/README.CN.md">_**🌐 点此处查看中文说明**_</a>
<a href="https://github.com/jayfunc/BetterLyrics/blob/dev/FAQ/FAQ.md">_**❓Click here to view frequently asked questions (FAQ)**_</a>
<a href="https://github.com/jayfunc/BetterLyrics/blob/dev/FAQ/FAQ.md">_**❓ Click here to view frequently asked questions (FAQ)**_</a>
<div align="center">
<img src="BetterLyrics.WinUI3/BetterLyrics.WinUI3/Assets/Logo.png" alt="" width="64"/>
@@ -85,12 +85,13 @@ Watch our introduction video (uploaded on 7 July 2025) on Bilibili [here](https:
- 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!
- Kugou Music
- 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.
- Apple Music
- Make sure you have set timeline threshold to around 600 ms in settings (Go to "Settings" - "Advanced option" to change), otherwise, the lyrics will be moving forward and afterward constantly.
- foobar2000
- Make sure you have https://github.com/dumbie/foo_mediacontrol installed with it
- Spofity
- Spotify
- QQ Music
- PotPlayer
- Media Player (System)
@@ -151,10 +152,16 @@ Wanna try the **latest** version? get it from Google Drive (see [release](https:
- [椒盐音乐 Salt Player](https://moriafly.com/program/salt-player)
- [MyToolBar](https://github.com/TwilightLemon/MyToolBar)
## ✍️ Help us translate into your language
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!
## Star History
[![Star History Chart](https://api.star-history.com/svg?repos=jayfunc/BetterLyrics&type=Date)](https://www.star-history.com/#jayfunc/BetterLyrics&Date)
## Any issues and PRs are welcomed
If you find a bug please file it in issues or if you have any ideas feel free to share it here.
If you find a bug please file it in issues or if you have any ideas feel free to share it here.

3
crowdin.yml Normal file
View File

@@ -0,0 +1,3 @@
files:
- source: /BetterLyrics.WinUI3/BetterLyrics.WinUI3/Strings/zh-CN/Resources.resw
translation: /BetterLyrics.WinUI3/BetterLyrics.WinUI3/Strings/%locale%/Resources.resw