mirror of
https://github.com/jayfunc/BetterLyrics.git
synced 2026-01-12 10:54:55 +08:00
fix playbackservice
This commit is contained in:
@@ -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}"
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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,33 +84,33 @@ 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.Low, () =>
|
||||
{
|
||||
@@ -118,8 +120,9 @@ namespace BetterLyrics.WinUI3.Services
|
||||
|
||||
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
|
||||
{
|
||||
@@ -127,19 +130,21 @@ namespace BetterLyrics.WinUI3.Services
|
||||
_ => false,
|
||||
};
|
||||
|
||||
MediaManager_OnAnyTimelinePropertyChanged(mediaSession, mediaSession.ControlSession.GetTimelineProperties());
|
||||
|
||||
_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
|
||||
{
|
||||
@@ -152,7 +157,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);
|
||||
@@ -188,11 +193,15 @@ namespace BetterLyrics.WinUI3.Services
|
||||
SongInfoChanged?.Invoke(this, new SongInfoChangedEventArgs(_cachedSongInfo));
|
||||
});
|
||||
}
|
||||
});
|
||||
MediaManager_OnAnyTimelinePropertyChanged(mediaSession, mediaSession.ControlSession.GetTimelineProperties());
|
||||
MediaManager_OnAnyPlaybackStateChanged(mediaSession, mediaSession.ControlSession.GetPlaybackInfo());
|
||||
|
||||
}).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
private void MediaManager_OnAnySessionClosed(MediaManager.MediaSession mediaSession)
|
||||
{
|
||||
if (!_mediaManager.IsStarted) return;
|
||||
if (_mediaManager.CurrentMediaSessions.Count == 0)
|
||||
{
|
||||
SendNullMessages();
|
||||
@@ -201,12 +210,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);
|
||||
@@ -233,6 +246,14 @@ namespace BetterLyrics.WinUI3.Services
|
||||
});
|
||||
}
|
||||
|
||||
private async Task SendFocusedMessagesAsync()
|
||||
{
|
||||
if (_focusedSession == null) return;
|
||||
|
||||
var mediaProps = await _focusedSession.ControlSession.TryGetMediaPropertiesAsync();
|
||||
MediaManager_OnAnyMediaPropertyChanged(_focusedSession, mediaProps);
|
||||
}
|
||||
|
||||
private async Task UpdateAlbumArtRelated(CancellationToken token)
|
||||
{
|
||||
if (_cachedSongInfo == null)
|
||||
@@ -329,47 +350,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)
|
||||
|
||||
@@ -28,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);
|
||||
@@ -47,15 +47,7 @@ namespace BetterLyrics.WinUI3.Services
|
||||
|
||||
if (string.IsNullOrEmpty(_settingsService.LibreTranslateServer))
|
||||
{
|
||||
_dispatcherQueue.TryEnqueue(DispatcherQueuePriority.Low, () =>
|
||||
{
|
||||
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";
|
||||
|
||||
@@ -13,6 +13,7 @@ using CommunityToolkit.WinUI;
|
||||
using Microsoft.UI.Dispatching;
|
||||
using Microsoft.UI.Xaml;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Numerics;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -337,6 +337,5 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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) { }
|
||||
}
|
||||
|
||||
@@ -28,16 +28,16 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
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)
|
||||
{
|
||||
_mediaPlayer.MediaOpened += MediaPlayer_MediaOpened;
|
||||
_timelineController = _mediaPlayer.TimelineController = new();
|
||||
_timelineController.PositionChanged += TimelineController_PositionChanged;
|
||||
_smtc = _mediaPlayer.SystemMediaTransportControls;
|
||||
@@ -53,11 +53,6 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
_libWatcherService.MusicLibraryFilesChanged += LibWatcherService_MusicLibraryFilesChanged;
|
||||
}
|
||||
|
||||
private void MediaPlayer_MediaOpened(MediaPlayer sender, object args)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
private void TimelineController_PositionChanged(MediaTimelineController sender, object args)
|
||||
{
|
||||
_smtc.UpdateTimelineProperties(new SystemMediaTransportControlsTimelineProperties()
|
||||
@@ -96,7 +91,7 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
public void RefreshSongs()
|
||||
{
|
||||
IsDataLoading = true;
|
||||
Tracks.Clear();
|
||||
_tracks.Clear();
|
||||
|
||||
Task.Run(() =>
|
||||
{
|
||||
@@ -109,7 +104,7 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
Track track = new(file);
|
||||
_dispatcherQueue.TryEnqueue(DispatcherQueuePriority.Low, () =>
|
||||
{
|
||||
Tracks.Add(track);
|
||||
_tracks.Add(track);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -117,6 +112,7 @@ namespace BetterLyrics.WinUI3.ViewModels
|
||||
|
||||
_dispatcherQueue.TryEnqueue(DispatcherQueuePriority.Low, () =>
|
||||
{
|
||||
TracksByTitle.AddRange(_tracks.GetGroupedByTitleAsync());
|
||||
IsDataLoading = false;
|
||||
});
|
||||
});
|
||||
@@ -126,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));
|
||||
|
||||
@@ -345,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>
|
||||
|
||||
@@ -111,11 +111,6 @@ 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);
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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=}" />
|
||||
</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>
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user