chores: Improve

This commit is contained in:
Zhe Fang
2025-11-15 08:30:06 -05:00
parent ae1fa1c54d
commit 4a389d5e33
23 changed files with 320 additions and 370 deletions

View File

@@ -69,6 +69,8 @@
<converter:TrackToLyricsConverter x:Key="TrackToLyricsConverter" />
<converter:IntToBoolConverter x:Key="IntToBoolConverter" />
<converter:IndexToDisplayConverter x:Key="IndexToDisplayConverter" />
<converter:IntToDoubleConverter x:Key="IntToDoubleConverter" />
<converter:MillisecondsToSecondsConverter x:Key="MillisecondsToSecondsConverter" />
<converters:BoolToVisibilityConverter x:Key="BoolToVisibilityConverter" />
<converters:BoolNegationConverter x:Key="BoolNegationConverter" />

View File

@@ -90,6 +90,10 @@
<ToggleSwitch IsOn="{x:Bind ViewModel.SelectedMediaSourceProvider.IsLastFMTrackEnabled, Mode=TwoWay}" />
</dev:SettingsCard>
<dev:SettingsCard Header="Discord Presence">
<ToggleSwitch IsOn="{x:Bind ViewModel.SelectedMediaSourceProvider.IsDiscordPresenceEnabled, Mode=TwoWay}" />
</dev:SettingsCard>
<!-- LX music server -->
<dev:SettingsCard x:Uid="SettingsPageLXMusicServer" Visibility="{x:Bind ViewModel.SelectedMediaSourceProvider.IsLXMusic, Converter={StaticResource BoolToVisibilityConverter}, Mode=OneWay}">
<Grid ColumnSpacing="6">
@@ -240,13 +244,13 @@
<TextBlock x:Uid="SettingsPageRealtimeStatus" Style="{StaticResource SettingsSectionHeaderTextBlockStyle}" />
<dev:SettingsCard x:Uid="LyricsPageLyricsProviderPrefix">
<HyperlinkButton
Content="{x:Bind ViewModel.LyricsSearchProvider, Mode=OneWay, Converter={StaticResource LyricsSearchProviderToDisplayNameConverter}}"
Content="{x:Bind ViewModel.MediaSessionsService.LyricsSearchProvider, Mode=OneWay, Converter={StaticResource LyricsSearchProviderToDisplayNameConverter}}"
IsEnabled="False"
NavigateUri="{x:Bind ViewModel.OriginalLyricsRef, Mode=OneWay}" />
</dev:SettingsCard>
<dev:SettingsCard x:Uid="LyricsPageTranslationProviderPrefix">
<HyperlinkButton
Content="{x:Bind ViewModel.TranslationSearchProvider, Mode=OneWay, Converter={StaticResource TranslationSearchProviderToDisplayNameConverter}}"
Content="{x:Bind ViewModel.MediaSessionsService.TranslationSearchProvider, Mode=OneWay, Converter={StaticResource TranslationSearchProviderToDisplayNameConverter}}"
IsEnabled="False"
NavigateUri="{x:Bind ViewModel.TranslatedLyricsRef, Mode=OneWay}" />
</dev:SettingsCard>

View File

@@ -0,0 +1,24 @@
using Microsoft.UI.Xaml.Data;
using System;
using System.Collections.Generic;
using System.Text;
namespace BetterLyrics.WinUI3.Converter
{
public class IntToDoubleConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language)
{
if (value is int intValue)
{
return (double)intValue;
}
return 0.0;
}
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
throw new NotImplementedException();
}
}
}

View File

@@ -0,0 +1,28 @@
using Microsoft.UI.Xaml.Data;
using System;
using System.Collections.Generic;
using System.Text;
namespace BetterLyrics.WinUI3.Converter
{
public class MillisecondsToSecondsConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language)
{
if (value is int intValue)
{
return intValue / 1000.0;
}
else if (value is double doubleValue)
{
return doubleValue / 1000.0;
}
return 0.0;
}
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
throw new NotImplementedException();
}
}
}

View File

@@ -373,7 +373,7 @@ namespace BetterLyrics.WinUI3.Helper
var window = GetWindowByWindowType<LyricsWindow>();
if (window == null) return;
if (_liveStatesService.LiveStates.LyricsWindowStatus.AutoShowOrHideWindow && !_mediaSessionsService.IsPlaying)
if (_liveStatesService.LiveStates.LyricsWindowStatus.AutoShowOrHideWindow && !_mediaSessionsService.CurrentIsPlaying)
{
if (_liveStatesService.LiveStates.LyricsWindowStatus.IsWorkArea)
{
@@ -383,7 +383,7 @@ namespace BetterLyrics.WinUI3.Helper
}
HideWindow<LyricsWindow>();
}
else if (_liveStatesService.LiveStates.LyricsWindowStatus.AutoShowOrHideWindow && _mediaSessionsService.IsPlaying)
else if (_liveStatesService.LiveStates.LyricsWindowStatus.AutoShowOrHideWindow && _mediaSessionsService.CurrentIsPlaying)
{
if (_liveStatesService.LiveStates.LyricsWindowStatus.IsWorkArea)
{

View File

@@ -272,7 +272,12 @@ namespace BetterLyrics.WinUI3.Models
LyricsAlignmentType = TextAlignmentType.Center,
},
};
status.WindowBounds = status.MonitorBounds;
status.WindowBounds = new Rect(
status.MonitorBounds.X,
status.MonitorBounds.Y - 1,
status.MonitorBounds.Width,
status.MonitorBounds.Height + 1
);
return status;
}

View File

@@ -16,10 +16,14 @@ namespace BetterLyrics.WinUI3.Models
[ObservableProperty][NotifyPropertyChangedRecipients] public partial string Provider { get; set; }
[ObservableProperty][NotifyPropertyChangedRecipients] public partial bool IsLastFMTrackEnabled { get; set; } = false;
[ObservableProperty][NotifyPropertyChangedRecipients] public partial bool IsDiscordPresenceEnabled { get; set; } = false;
[ObservableProperty][NotifyPropertyChangedRecipients] public partial bool IsTimelineSyncEnabled { get; set; } = true;
[ObservableProperty][NotifyPropertyChangedRecipients] public partial int TimelineSyncThreshold { get; set; }
/// <summary>
/// Unit: ms
/// </summary>
[ObservableProperty][NotifyPropertyChangedRecipients] public partial int PositionOffset { get; set; }
[ObservableProperty][NotifyPropertyChangedRecipients] public partial bool ResetPositionOffsetOnSongChanged { get; set; } = false;

View File

@@ -13,10 +13,7 @@ namespace BetterLyrics.WinUI3.Models
public partial string Artist { get; set; }
[ObservableProperty]
public partial int? Duration { get; set; }
[ObservableProperty]
public partial double? DurationMs { get; set; }
public partial double DurationMs { get; set; }
[ObservableProperty]
public partial string? PlayerId { get; set; } = null;
@@ -27,6 +24,8 @@ namespace BetterLyrics.WinUI3.Models
[ObservableProperty]
public partial string? SongId { get; set; } = null;
public double Duration => DurationMs / 1000;
public SongInfo() { }
}

View File

@@ -1,52 +1,53 @@
using BetterLyrics.WinUI3.Models;
using BetterLyrics.WinUI3.Services.MediaSessionsService;
using DiscordRPC;
using Microsoft.Windows.Storage;
using System;
using System.Diagnostics;
using static Vanara.PInvoke.Kernel32.REASON_CONTEXT;
namespace BetterLyrics.WinUI3.Services.DiscordService
{
public class DiscordService : IDiscordService
{
private DiscordRpcClient? _client;
private RichPresence _richPresence;
public DiscordService()
{
_richPresence = new()
{
StatusDisplay = StatusDisplayType.Name,
Type = ActivityType.Listening,
};
}
_client = new DiscordRpcClient(Constants.Discord.AppID);
_client.OnReady += Client_OnReady;
_client.Initialize();
public void Enable()
{
if (_client == null)
{
_client = new DiscordRpcClient(Constants.Discord.AppID);
_client.Initialize();
}
}
public void UpdateRichPresence(SongInfo songInfo)
{
_richPresence.Details = songInfo.Title;
_richPresence.State = songInfo.Artist;
_richPresence.Timestamps = Timestamps.FromTimeSpan(songInfo.Duration ?? 0);
_richPresence.Assets = new Assets
_client?.SetPresence(new RichPresence
{
};
_client?.SetPresence(_richPresence);
StatusDisplay = StatusDisplayType.Details,
Type = ActivityType.Listening,
Buttons = new Button[] { new() { Label = "Get this status", Url = Constants.Link.MicrosoftStoreUrl } },
Assets = new Assets
{
LargeImageKey = "banner",
SmallImageKey = "logo"
},
Details = songInfo.Title,
State = songInfo.Artist,
Timestamps = Timestamps.FromTimeSpan(songInfo.Duration)
});
}
public void UpdateRichPresence(TimeSpan current, TimeSpan duration)
public void Disable()
{
//_richPresence.Timestamps = new(DateTime.Now - current, DateTime.Now - current + duration);
//_client?.SetPresence(_richPresence);
_client?.ClearPresence();
_client?.Dispose();
_client = null;
}
private void Client_OnReady(object sender, DiscordRPC.Message.ReadyMessage args)
{
Debug.WriteLine("Connected to discord with user {0}", args.User.Username);
Debug.WriteLine("Avatar: {0}", args.User.GetAvatarURL(User.AvatarFormat.WebP));
Debug.WriteLine("Decoration: {0}", args.User.GetAvatarDecorationURL());
}
}
}

View File

@@ -7,7 +7,8 @@ namespace BetterLyrics.WinUI3.Services.DiscordService
{
public interface IDiscordService
{
void Enable();
void Disable();
void UpdateRichPresence(SongInfo songInfo);
void UpdateRichPresence(TimeSpan current, TimeSpan duration);
}
}

View File

@@ -3,19 +3,17 @@
using BetterLyrics.WinUI3.Enums;
using BetterLyrics.WinUI3.Events;
using BetterLyrics.WinUI3.Models;
using BetterLyrics.WinUI3.ViewModels;
using System;
using System.ComponentModel;
using System.Threading.Tasks;
namespace BetterLyrics.WinUI3.Services.MediaSessionsService
{
public interface IMediaSessionsService
public interface IMediaSessionsService : INotifyPropertyChanged
{
event EventHandler<IsPlayingChangedEventArgs>? IsPlayingChanged;
event EventHandler<TimelineChangedEventArgs>? TimelineChanged;
event EventHandler<SongInfoChangedEventArgs>? SongInfoChanged;
event EventHandler<AlbumArtChangedEventArgs>? AlbumArtChanged;
event EventHandler<LyricsChangedEventArgs>? LyricsChanged;
event EventHandler<MediaSourceProvidersInfoEventArgs>? MediaSourceProvidersInfoChanged;
Task PlayAsync();
Task PauseAsync();
@@ -23,16 +21,15 @@ namespace BetterLyrics.WinUI3.Services.MediaSessionsService
Task NextAsync();
Task ChangePosition(double seconds);
MediaSourceProviderInfo? GetCurrentMediaSourceProviderInfo();
void UpdateLyrics();
void UpdateTranslations();
void InitPlaybackShortcuts();
bool IsPlaying { get; }
SongInfo? SongInfo { get; }
TimeSpan Position { get; }
MediaSourceProviderInfo? CurrentMediaSourceProviderInfo { get; }
bool CurrentIsPlaying { get; }
SongInfo? CurrentSongInfo { get; }
TimeSpan CurrentPosition { get; }
LyricsData? CurrentLyricsData { get; }
LyricsSearchProvider? LyricsSearchProvider { get; }

View File

@@ -23,17 +23,17 @@ namespace BetterLyrics.WinUI3.Services.MediaSessionsService
private async Task RefreshArtAlbum(CancellationToken token)
{
if (_cachedSongInfo == null)
if (CurrentSongInfo == null)
{
_logger.LogWarning("Cached song info is null, cannot update album art.");
return;
}
IBuffer? buffer = await Task.Run(async () => await _albumArtSearchService.SearchAsync(
SongInfo?.PlayerId ?? "",
_cachedSongInfo.Title,
_cachedSongInfo.Artist,
_cachedSongInfo.Album,
CurrentSongInfo?.PlayerId ?? "",
CurrentSongInfo.Title,
CurrentSongInfo.Artist,
CurrentSongInfo.Album,
_SMTCAlbumArtBuffer,
token
), token);

View File

@@ -26,9 +26,9 @@ namespace BetterLyrics.WinUI3.Services.MediaSessionsService
public event EventHandler<LyricsChangedEventArgs>? LyricsChanged;
[ObservableProperty][NotifyPropertyChangedRecipients] public partial LyricsSearchProvider? LyricsSearchProvider { get; set; }
[ObservableProperty][NotifyPropertyChangedRecipients] public partial LyricsSearchProvider? LyricsSearchProvider { get; private set; }
[ObservableProperty][NotifyPropertyChangedRecipients] public partial TranslationSearchProvider? TranslationSearchProvider { get; set; }
[ObservableProperty][NotifyPropertyChangedRecipients] public partial TranslationSearchProvider? TranslationSearchProvider { get; private set; }
[ObservableProperty] public partial bool IsTranslating { get; set; } = false;
@@ -155,18 +155,18 @@ namespace BetterLyrics.WinUI3.Services.MediaSessionsService
LyricsChanged?.Invoke(this, new LyricsChangedEventArgs(CurrentLyricsData));
});
if (SongInfo != null)
if (CurrentSongInfo != null)
{
_logger.LogInformation("Searching lyrics for: Title={Title}, Artist={Artist}, Album={Album}, DurationMs={DurationMs}",
SongInfo.Title, SongInfo.Artist, SongInfo.Album, SongInfo.DurationMs);
CurrentSongInfo.Title, CurrentSongInfo.Artist, CurrentSongInfo.Album, CurrentSongInfo.DurationMs);
var lyricsSearchResult = await Task.Run(async () => await _lyrcsSearchService.SearchSmartlyAsync(
SongInfo.PlayerId ?? "",
SongInfo.Title,
SongInfo.Artist,
SongInfo.Album,
SongInfo.DurationMs ?? 0,
SongInfo.SongId,
CurrentSongInfo.PlayerId ?? "",
CurrentSongInfo.Title,
CurrentSongInfo.Artist,
CurrentSongInfo.Album,
CurrentSongInfo.DurationMs,
CurrentSongInfo.SongId,
token
), token);
if (token.IsCancellationRequested) return;
@@ -177,7 +177,7 @@ namespace BetterLyrics.WinUI3.Services.MediaSessionsService
var lyricsParser = new LyricsParser();
lyricsParser.Parse(
_settingsService.AppSettings.MappedSongSearchQueries.ToList(),
SongInfo.Title, SongInfo.Artist, SongInfo.Album, lyricsSearchResult?.Raw, (int?)SongInfo?.DurationMs, LyricsSearchProvider);
CurrentSongInfo.Title, CurrentSongInfo.Artist, CurrentSongInfo.Album, lyricsSearchResult?.Raw, (int?)CurrentSongInfo?.DurationMs, LyricsSearchProvider);
_lyricsDataArr = lyricsParser.LyricsDataArr;
ApplyChinesePreference();
}

View File

@@ -16,6 +16,7 @@ using BetterLyrics.WinUI3.Services.SettingsService;
using BetterLyrics.WinUI3.Services.TranslateService;
using BetterLyrics.WinUI3.ViewModels;
using BetterLyrics.WinUI3.Views;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.DependencyInjection;
using CommunityToolkit.Mvvm.Messaging;
using CommunityToolkit.Mvvm.Messaging.Messages;
@@ -42,6 +43,10 @@ namespace BetterLyrics.WinUI3.Services.MediaSessionsService
IRecipient<PropertyChangedMessage<ChineseRomanization>>,
IRecipient<PropertyChangedMessage<List<string>>>
{
private EventSourceReader? _sse = null;
private readonly MediaManager _mediaManager = new();
private IBuffer? _SMTCAlbumArtBuffer = null;
private readonly IAlbumArtSearchService _albumArtSearchService;
private readonly ILyricsSearchService _lyrcsSearchService;
private readonly ITranslateService _translateService;
@@ -53,27 +58,13 @@ namespace BetterLyrics.WinUI3.Services.MediaSessionsService
private readonly ILogger<MediaSessionsService> _logger;
private double _lxMusicPositionSeconds = 0;
private double _lxMusicDurationSeconds = 0;
private byte[]? _lxMusicAlbumArtBytes = null;
private bool _cachedIsPlaying = false;
private TimeSpan _cachedPosition = TimeSpan.Zero;
[ObservableProperty][NotifyPropertyChangedRecipients] public partial bool CurrentIsPlaying { get; private set; } = false;
[ObservableProperty][NotifyPropertyChangedRecipients] public partial TimeSpan CurrentPosition { get; private set; } = TimeSpan.Zero;
[ObservableProperty][NotifyPropertyChangedRecipients] public partial SongInfo? CurrentSongInfo { get; private set; }
private EventSourceReader? _sse = null;
private readonly MediaManager _mediaManager = new();
private SongInfo? _cachedSongInfo;
private IBuffer? _SMTCAlbumArtBuffer = null;
public event EventHandler<IsPlayingChangedEventArgs>? IsPlayingChanged;
public event EventHandler<TimelineChangedEventArgs>? TimelineChanged;
public event EventHandler<SongInfoChangedEventArgs>? SongInfoChanged;
public event EventHandler<MediaSourceProvidersInfoEventArgs>? MediaSourceProvidersInfoChanged;
public bool IsPlaying => _cachedIsPlaying;
public SongInfo? SongInfo => _cachedSongInfo;
public TimeSpan Position => _cachedPosition;
public MediaSourceProviderInfo? CurrentMediaSourceProviderInfo { get; set; }
public MediaSessionsService(
ISettingsService settingsService,
@@ -128,9 +119,9 @@ namespace BetterLyrics.WinUI3.Services.MediaSessionsService
private void UpdatePlayOrPauseSongShortcut()
{
GlobalHotKeyHelper.UpdateHotKey<LyricsWindow>(ShortcutID.PlayOrPauseSong, _settingsService.AppSettings.GeneralSettings.PlayOrPauseShortcut, () =>
GlobalHotKeyHelper.UpdateHotKey<LyricsWindow>(ShortcutID.PlayOrPauseSong, _settingsService.AppSettings.GeneralSettings.PlayOrPauseShortcut, (() =>
{
if (_cachedIsPlaying)
if (CurrentIsPlaying)
{
_ = PauseAsync();
}
@@ -138,7 +129,7 @@ namespace BetterLyrics.WinUI3.Services.MediaSessionsService
{
_ = PlayAsync();
}
});
}));
}
private void UpdatePreviousSongShortcut()
@@ -190,7 +181,7 @@ namespace BetterLyrics.WinUI3.Services.MediaSessionsService
UpdateLyrics();
}
public MediaSourceProviderInfo? GetCurrentMediaSourceProviderInfo()
private MediaSourceProviderInfo? GetCurrentMediaSourceProviderInfo()
{
var desiredSession = GetCurrentSession();
return _settingsService.AppSettings.MediaSourceProvidersInfo.FirstOrDefault(x => x.Provider == desiredSession?.Id);
@@ -239,30 +230,20 @@ namespace BetterLyrics.WinUI3.Services.MediaSessionsService
if (!IsMediaSourceEnabled(mediaSession.Id))
{
_cachedPosition = TimeSpan.Zero;
_dispatcherQueue.TryEnqueue(DispatcherQueuePriority.Low, () =>
{
TimelineChanged?.Invoke(this, new TimelineChangedEventArgs(_cachedPosition, TimeSpan.Zero));
_discordService.UpdateRichPresence(_cachedPosition, TimeSpan.Zero);
});
CurrentPosition = TimeSpan.Zero;
}
else
{
if (IsMediaSourceTimelineSyncEnabled(mediaSession.Id))
{
_cachedPosition = timelineProperties?.Position ?? TimeSpan.Zero;
_dispatcherQueue.TryEnqueue(DispatcherQueuePriority.Low, () =>
{
TimelineChanged?.Invoke(this, new TimelineChangedEventArgs(_cachedPosition, timelineProperties?.EndTime ?? TimeSpan.Zero));
_discordService.UpdateRichPresence(_cachedPosition, timelineProperties?.EndTime ?? TimeSpan.Zero);
});
CurrentPosition = timelineProperties?.Position ?? TimeSpan.Zero;
}
}
}
private void MediaManager_OnAnyPlaybackStateChanged(MediaManager.MediaSession? mediaSession, GlobalSystemMediaTransportControlsSessionPlaybackInfo? playbackInfo)
{
_dispatcherQueue.TryEnqueue(DispatcherQueuePriority.Low, () =>
_dispatcherQueue.TryEnqueue(DispatcherQueuePriority.Low, (() =>
{
if (!_mediaManager.IsStarted) return;
if (mediaSession == null) return;
@@ -274,19 +255,17 @@ namespace BetterLyrics.WinUI3.Services.MediaSessionsService
if (!IsMediaSourceEnabled(mediaSession.Id))
{
_cachedIsPlaying = false;
CurrentIsPlaying = false;
}
else
{
_cachedIsPlaying = playbackInfo?.PlaybackStatus switch
CurrentIsPlaying = playbackInfo?.PlaybackStatus switch
{
GlobalSystemMediaTransportControlsSessionPlaybackStatus.Playing => true,
_ => false,
};
}
IsPlayingChanged?.Invoke(this, new IsPlayingChangedEventArgs(_cachedIsPlaying));
});
}));
}
private void MediaManager_OnAnyMediaPropertyChanged(MediaManager.MediaSession? mediaSession, GlobalSystemMediaTransportControlsSessionMediaProperties? mediaProperties)
@@ -296,7 +275,7 @@ namespace BetterLyrics.WinUI3.Services.MediaSessionsService
if (!_mediaManager.IsStarted) return;
if (mediaSession == null)
{
_cachedSongInfo = SongInfoExtensions.Placeholder;
CurrentSongInfo = SongInfoExtensions.Placeholder;
}
string? sessionId = mediaSession?.Id;
@@ -307,7 +286,7 @@ namespace BetterLyrics.WinUI3.Services.MediaSessionsService
if (sessionId != null && !IsMediaSourceEnabled(sessionId))
{
_cachedSongInfo = SongInfoExtensions.Placeholder;
CurrentSongInfo = SongInfoExtensions.Placeholder;
_logger.LogInformation("Media properties changed: Title: {Title}, Artist: {Artist}, Album: {Album}",
mediaProperties?.Title, mediaProperties?.Artist, mediaProperties?.AlbumTitle);
@@ -341,16 +320,15 @@ namespace BetterLyrics.WinUI3.Services.MediaSessionsService
songId = mediaProperties?.Genres.FirstOrDefault()?.Replace("NCM-", "");
}
_cachedSongInfo = new SongInfo
CurrentSongInfo = new SongInfo
{
Title = mediaProperties?.Title ?? "N/A",
Artist = fixedArtist,
Album = fixedAlbum,
DurationMs = mediaSession?.ControlSession?.GetTimelineProperties().EndTime.TotalMilliseconds,
DurationMs = mediaSession?.ControlSession?.GetTimelineProperties().EndTime.TotalMilliseconds ?? 0,
PlayerId = sessionId,
SongId = songId
};
_cachedSongInfo.Duration = (int)((_cachedSongInfo.DurationMs ?? 0) / 1000f);
_logger.LogInformation("Media properties changed: Title: {Title}, Artist: {Artist}, Album: {Album}",
mediaProperties?.Title, mediaProperties?.Artist, mediaProperties?.AlbumTitle);
@@ -378,11 +356,13 @@ namespace BetterLyrics.WinUI3.Services.MediaSessionsService
}
}
SongInfoChanged?.Invoke(this, new SongInfoChangedEventArgs(_cachedSongInfo));
CurrentMediaSourceProviderInfo = GetCurrentMediaSourceProviderInfo();
UpdateAlbumArt();
UpdateLyrics();
_discordService.UpdateRichPresence(_cachedSongInfo);
UpdateDiscordPresence();
UpdateCurrentMediaSourceProviderInfoPositionOffset();
});
}
@@ -450,18 +430,39 @@ namespace BetterLyrics.WinUI3.Services.MediaSessionsService
private void SendNullMessages()
{
_dispatcherQueue.TryEnqueue(DispatcherQueuePriority.Low, () =>
_dispatcherQueue.TryEnqueue(DispatcherQueuePriority.Low, (() =>
{
_cachedSongInfo = SongInfoExtensions.Placeholder;
_cachedIsPlaying = false;
CurrentSongInfo = SongInfoExtensions.Placeholder;
CurrentIsPlaying = false;
SongInfoChanged?.Invoke(this, new SongInfoChangedEventArgs(_cachedSongInfo));
IsPlayingChanged?.Invoke(this, new IsPlayingChangedEventArgs(_cachedIsPlaying));
TimelineChanged?.Invoke(this, new TimelineChangedEventArgs(TimeSpan.Zero, TimeSpan.Zero));
CurrentMediaSourceProviderInfo = GetCurrentMediaSourceProviderInfo();
_discordService.UpdateRichPresence(_cachedSongInfo);
_discordService.UpdateRichPresence(TimeSpan.Zero, TimeSpan.Zero);
});
CurrentPosition = TimeSpan.Zero;
_discordService.Disable();
UpdateCurrentMediaSourceProviderInfoPositionOffset();
}));
}
private void UpdateCurrentMediaSourceProviderInfoPositionOffset()
{
if (CurrentPosition.TotalSeconds <= 1 && CurrentMediaSourceProviderInfo?.ResetPositionOffsetOnSongChanged == true)
{
CurrentMediaSourceProviderInfo?.PositionOffset = 0;
}
}
private void UpdateDiscordPresence()
{
if (CurrentMediaSourceProviderInfo?.IsDiscordPresenceEnabled == true && CurrentSongInfo != null)
{
_discordService.Enable();
_discordService.UpdateRichPresence(CurrentSongInfo);
}
else
{
_discordService.Disable();
}
}
private async Task SendFocusedMessagesAsync()
@@ -531,7 +532,7 @@ namespace BetterLyrics.WinUI3.Services.MediaSessionsService
{
_dispatcherQueue.TryEnqueue(DispatcherQueuePriority.Low, async () =>
{
if (PlayerIdMatcher.IsLXMusic(_cachedSongInfo?.PlayerId))
if (PlayerIdMatcher.IsLXMusic(CurrentSongInfo?.PlayerId))
{
var data = JsonSerializer.Deserialize(e.Message, Serialization.SourceGenerationContext.Default.JsonElement);
if (data.ValueKind == JsonValueKind.Number)
@@ -542,13 +543,13 @@ namespace BetterLyrics.WinUI3.Services.MediaSessionsService
}
else if (e.Event == "duration")
{
_lxMusicDurationSeconds = data.GetDouble();
CurrentSongInfo?.DurationMs = data.GetDouble() * 1000;
UpdateDiscordPresence();
}
if (IsMediaSourceTimelineSyncEnabled(_cachedSongInfo?.PlayerId))
if (IsMediaSourceTimelineSyncEnabled(CurrentSongInfo?.PlayerId))
{
TimelineChanged?.Invoke(this, new TimelineChangedEventArgs(TimeSpan.FromSeconds(_lxMusicPositionSeconds), TimeSpan.FromSeconds(_lxMusicDurationSeconds)));
_discordService.UpdateRichPresence(TimeSpan.FromSeconds(_lxMusicPositionSeconds), TimeSpan.FromSeconds(_lxMusicDurationSeconds));
CurrentPosition = TimeSpan.FromSeconds(_lxMusicPositionSeconds);
}
}
else if (data.ValueKind == JsonValueKind.String)

View File

@@ -19,28 +19,20 @@ namespace BetterLyrics.WinUI3.ViewModels
public partial class LyricsPageViewModel : BaseViewModel,
IRecipient<PropertyChangedMessage<TimeSpan>>
{
private readonly IMediaSessionsService _mediaSessionsService;
private readonly ISettingsService _settingsService;
public IMediaSessionsService MediaSessionsService { get; private set; }
private readonly ILiveStatesService _liveStatesService;
private readonly ThrottleHelper _timelineThrottle = new(TimeSpan.FromSeconds(1));
public LyricsPageViewModel(ISettingsService settingsService, IMediaSessionsService mediaSessionsService, ILiveStatesService liveStatesService)
public LyricsPageViewModel(IMediaSessionsService mediaSessionsService, ILiveStatesService liveStatesService)
{
_settingsService = settingsService;
_liveStatesService = liveStatesService;
MediaSessionsService = mediaSessionsService;
LiveStates = _liveStatesService.LiveStates;
Volume = SystemVolumeHelper.MasterVolume;
SystemVolumeHelper.VolumeNotification += SystemVolumeHelper_VolumeNotification;
_mediaSessionsService = mediaSessionsService;
_mediaSessionsService.SongInfoChanged += PlaybackService_SongInfoChanged;
_mediaSessionsService.IsPlayingChanged += PlaybackService_IsPlayingChanged;
_mediaSessionsService.TimelineChanged += PlaybackService_TimelineChanged;
IsSongPlaying = _mediaSessionsService.IsPlaying;
}
private void SystemVolumeHelper_VolumeNotification(object? sender, int e)
@@ -48,31 +40,12 @@ namespace BetterLyrics.WinUI3.ViewModels
Volume = e;
}
private void PlaybackService_TimelineChanged(object? sender, Events.TimelineChangedEventArgs e)
{
SongDurationSeconds = (int)e.End.TotalSeconds;
}
private void PlaybackService_IsPlayingChanged(object? sender, Events.IsPlayingChangedEventArgs e)
{
IsSongPlaying = e.IsPlaying;
}
private void PlaybackService_SongInfoChanged(object? sender, Events.SongInfoChangedEventArgs e)
{
SongInfo = e.SongInfo;
SongDurationSeconds = SongInfo?.Duration ?? 0;
}
[ObservableProperty]
public partial LiveStates LiveStates { get; set; }
[ObservableProperty]
public partial double TimelinePositionSeconds { get; set; }
[ObservableProperty]
public partial int SongDurationSeconds { get; set; }
[ObservableProperty]
public partial int Volume { get; set; }
@@ -82,12 +55,6 @@ namespace BetterLyrics.WinUI3.ViewModels
[ObservableProperty]
public partial double BottomCommandFlyoutTriggerOpacity { get; set; }
[ObservableProperty]
public partial SongInfo? SongInfo { get; set; } = null;
[ObservableProperty]
public partial bool IsSongPlaying { get; set; }
[ObservableProperty]
public partial float TimelineSliderThumbOpacity { get; set; } = 0f;
@@ -107,30 +74,30 @@ namespace BetterLyrics.WinUI3.ViewModels
[RelayCommand]
private async Task PlaySongAsync()
{
await _mediaSessionsService.PlayAsync();
await MediaSessionsService.PlayAsync();
}
[RelayCommand]
private async Task PauseSongAsync()
{
await _mediaSessionsService.PauseAsync();
await MediaSessionsService.PauseAsync();
}
[RelayCommand]
private async Task PreviousSongAsync()
{
await _mediaSessionsService.PreviousAsync();
await MediaSessionsService.PreviousAsync();
}
[RelayCommand]
private async Task NextSongAsync()
{
await _mediaSessionsService.NextAsync();
await MediaSessionsService.NextAsync();
}
partial void OnTimelineSliderThumbSecondsChanged(double value)
{
TimelineSliderThumbLyricsLine = _mediaSessionsService.CurrentLyricsData?.GetLyricsLine(value);
TimelineSliderThumbLyricsLine = MediaSessionsService.CurrentLyricsData?.GetLyricsLine(value);
}
public void Receive(PropertyChangedMessage<TimeSpan> message)
@@ -149,5 +116,6 @@ namespace BetterLyrics.WinUI3.ViewModels
}
}
}
}
}

View File

@@ -93,8 +93,8 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
$"Syllable prog: {charProgress}\n" +
$"Visible lines: [{_startVisibleLineIndex}, {_endVisibleLineIndex}]\n" +
$"Total line count: {GetMaxLyricsLineIndexBoundaries().Item2 + 1}\n" +
$"Cur time: {TotalTime + _positionOffset}\n" +
$"Song duration: {TimeSpan.FromMilliseconds(SongInfo?.DurationMs ?? 0)}\n" +
$"Cur time: {TotalTime + TimeSpan.FromMilliseconds(_mediaSessionsService.CurrentMediaSourceProviderInfo?.PositionOffset ?? 0)}\n" +
$"Song duration: {TimeSpan.FromMilliseconds(_mediaSessionsService.CurrentSongInfo?.DurationMs ?? 0)}\n" +
$"Y offset: {_canvasYScrollTransition.Value}",
new Vector2(10, 40),
ThemeTypeSent == Microsoft.UI.Xaml.ElementTheme.Light ? Colors.Black : Colors.White,

View File

@@ -1,9 +1,11 @@
using BetterLyrics.WinUI3.Enums;
using BetterLyrics.WinUI3.Models;
using BetterLyrics.WinUI3.Models.Settings;
using BetterLyrics.WinUI3.Services.MediaSessionsService;
using CommunityToolkit.Mvvm.Messaging;
using CommunityToolkit.Mvvm.Messaging.Messages;
using Microsoft.UI.Xaml;
using System;
using Windows.UI;
namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
@@ -22,9 +24,11 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
IRecipient<PropertyChangedMessage<LineRenderingType>>,
IRecipient<PropertyChangedMessage<ElementTheme>>,
IRecipient<PropertyChangedMessage<EasingType>>,
IRecipient<PropertyChangedMessage<TimeSpan>>,
IRecipient<PropertyChangedMessage<AlbumArtLayoutSettings>>,
IRecipient<PropertyChangedMessage<LyricsBackgroundSettings>>,
IRecipient<PropertyChangedMessage<LyricsWindowStatus>>
IRecipient<PropertyChangedMessage<LyricsWindowStatus>>,
IRecipient<PropertyChangedMessage<SongInfo?>>
{
public void Receive(PropertyChangedMessage<bool> message)
@@ -54,13 +58,6 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
_isLyrics3DMatrixChanged = true;
}
}
else if (message.Sender is MediaSourceProviderInfo)
{
if (message.PropertyName == nameof(MediaSourceProviderInfo.IsLastFMTrackEnabled))
{
UpdateIsLastFMTrackEnabled();
}
}
else if (message.Sender is LyricsStyleSettings)
{
if (message.PropertyName == nameof(LyricsStyleSettings.IsDynamicLyricsFontSize))
@@ -256,17 +253,6 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
_isLayoutChanged = true;
}
}
else if (message.Sender is MediaSourceProviderInfo)
{
if (message.PropertyName == nameof(MediaSourceProviderInfo.TimelineSyncThreshold))
{
UpdateTimelineSyncThreshold();
}
else if (message.PropertyName == nameof(MediaSourceProviderInfo.PositionOffset))
{
UpdatePositionOffset();
}
}
}
public void Receive(PropertyChangedMessage<LineRenderingType> message)
@@ -436,5 +422,60 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
}
}
}
public void Receive(PropertyChangedMessage<SongInfo?> message)
{
if (_mediaSessionsService.CurrentSongInfo?.Title != _songTitle || _mediaSessionsService.CurrentSongInfo?.Artist != _songArtist)
{
_lastSongTitle = _songTitle;
_songTitle = _mediaSessionsService.CurrentSongInfo?.Title;
_isSongTitleChanged = true;
_lastSongArtist = _songArtist;
_songArtist = _mediaSessionsService.CurrentSongInfo?.Artist;
_isSongArtistChanged = true;
_lastSongAlbum = _songAlbum;
_songAlbum = _mediaSessionsService.CurrentSongInfo?.Album;
_isSongAlbumChanged = true;
_songDurationMs = (int)(_mediaSessionsService.CurrentSongInfo?.DurationMs ?? TimeSpan.FromMinutes(99).TotalMilliseconds);
_songInfoOpacityTransition.Reset(0f);
_songInfoOpacityTransition.StartTransition(1f);
TotalTime = TimeSpan.Zero;
// 处理 Last.fm 追踪
_totalPlayingTime = TimeSpan.Zero;
_isLastFMTracked = false;
}
}
public void Receive(PropertyChangedMessage<TimeSpan> message)
{
if (message.Sender is IMediaSessionsService)
{
if (message.PropertyName == nameof(IMediaSessionsService.CurrentPosition))
{
var diff = Math.Abs(TotalTime.TotalMilliseconds - _mediaSessionsService.CurrentPosition.TotalMilliseconds);
var timelineSyncThreshold = _mediaSessionsService.CurrentMediaSourceProviderInfo?.TimelineSyncThreshold ?? 0;
if (diff >= timelineSyncThreshold)
{
TotalTime = _mediaSessionsService.CurrentPosition;
if (TotalTime.TotalSeconds <= 1)
{
_totalPlayingTime = TimeSpan.Zero;
_isLastFMTracked = false;
}
}
// 大跨度,刷新布局,避免歌词不显示
if (diff >= timelineSyncThreshold + 5000)
{
_isLayoutChanged = true;
}
}
}
}
}
}

View File

@@ -77,14 +77,17 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
{
_elapsedTime = args.Timing.ElapsedTime;
if (IsPlaying)
if (_mediaSessionsService.CurrentIsPlaying)
{
TotalTime += _elapsedTime;
_totalPlayingTime += _elapsedTime;
if (_isLastFMTrackEnabled && !_isLastFMTracked && SongInfo?.Duration != null && SongInfo.Duration > 0 && _totalPlayingTime.TotalSeconds >= SongInfo.Duration * 0.5)
if ((_mediaSessionsService.CurrentMediaSourceProviderInfo?.IsLastFMTrackEnabled ?? false) &&
_isLastFMTracked == false &&
_mediaSessionsService.CurrentSongInfo?.Duration > 0 &&
_totalPlayingTime.TotalSeconds >= _mediaSessionsService.CurrentSongInfo.Duration * 0.5)
{
_isLastFMTracked = true;
_lastFMService.TrackAsync(SongInfo);
_lastFMService.TrackAsync(_mediaSessionsService.CurrentSongInfo);
}
}
@@ -859,24 +862,6 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
}
}
private void UpdateTimelineSyncThreshold()
{
var current = _mediaSessionsService.GetCurrentMediaSourceProviderInfo();
_timelineSyncThreshold = current?.TimelineSyncThreshold ?? 0;
}
private void UpdatePositionOffset()
{
var current = _mediaSessionsService.GetCurrentMediaSourceProviderInfo();
_positionOffset = TimeSpan.FromMilliseconds(current?.PositionOffset ?? 0);
}
private void UpdateIsLastFMTrackEnabled()
{
var current = _mediaSessionsService.GetCurrentMediaSourceProviderInfo();
_isLastFMTrackEnabled = current?.IsLastFMTrackEnabled ?? false;
}
private void UpdateSongInfoFontSize()
{
if (_liveStatesService.LiveStates.LyricsWindowStatus.AlbumArtLayoutSettings.IsAutoSongInfoFontSize)

View File

@@ -57,8 +57,6 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
[NotifyPropertyChangedRecipients]
public partial TimeSpan TotalTime { get; set; } = TimeSpan.Zero;
private TimeSpan _positionOffset = TimeSpan.Zero;
private int _songDurationMs = (int)TimeSpan.FromMinutes(99).TotalMilliseconds;
private Stopwatch? _drawFrameStopwatch;
@@ -139,11 +137,6 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
private bool _isDebugOverlayEnabled = false;
[ObservableProperty]
public partial bool IsPlaying { get; set; } = false;
private int _timelineSyncThreshold = 0;
private int _phoneticLyricsFontSize = 18;
private int _originalLyricsFontSize = 36;
private int _translatedLyricsFontSize = 18;
@@ -185,9 +178,6 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
private CanvasGeometry? _spectrumGeometry = null;
[ObservableProperty]
public partial SongInfo? SongInfo { get; set; }
[ObservableProperty]
[NotifyPropertyChangedRecipients]
public partial ElementTheme ThemeTypeSent { get; set; }
@@ -213,15 +203,8 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
UpdateSongInfoFontSize();
_timelineSyncThreshold = 0;
_mediaSessionsService.IsPlayingChanged += MediaSessionsService_IsPlayingChanged;
_mediaSessionsService.SongInfoChanged += MediaSessionsService_SongInfoChanged;
_mediaSessionsService.AlbumArtChanged += MediaSessionsService_AlbumArtChangedChanged;
_mediaSessionsService.LyricsChanged += MediaSessionsService_LyricsChanged;
_mediaSessionsService.TimelineChanged += MediaSessionsService_TimelineChanged;
IsPlaying = _mediaSessionsService.IsPlaying;
UpdateColorConfig();
@@ -236,7 +219,7 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
private int GetCurrentPlayingLineIndex()
{
var totalMs = TotalTime.TotalMilliseconds + _positionOffset.TotalMilliseconds;
var totalMs = TotalTime.TotalMilliseconds + _mediaSessionsService.CurrentMediaSourceProviderInfo?.PositionOffset ?? 0;
if (totalMs < _currentLyricsData?.LyricsLines.FirstOrDefault()?.StartMs) return 0;
for (int i = 0; i < _currentLyricsData?.LyricsLines.Count; i++)
@@ -272,7 +255,7 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
else if (nextLine != null) lineEndMs = nextLine.StartMs;
else lineEndMs = _songDurationMs;
double now = (double)TotalTime.TotalMilliseconds + (double)_positionOffset.TotalMilliseconds;
double now = (double)TotalTime.TotalMilliseconds + (double)(_mediaSessionsService.CurrentMediaSourceProviderInfo?.PositionOffset ?? 0);
// 1. 还没到本句
if (now < line.StartMs)
@@ -356,11 +339,9 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
private Tuple<int, int> GetMaxLyricsLineIndexBoundaries()
{
if (
SongInfo == null
if (_mediaSessionsService.CurrentSongInfo == null
|| _currentLyricsData == null
|| _currentLyricsData.LyricsLines.Count == 0
)
|| _currentLyricsData.LyricsLines.Count == 0)
{
return new Tuple<int, int>(-1, -1);
}
@@ -368,65 +349,6 @@ namespace BetterLyrics.WinUI3.ViewModels.LyricsRendererViewModel
return new Tuple<int, int>(0, _currentLyricsData.LyricsLines.Count - 1);
}
private void MediaSessionsService_IsPlayingChanged(object? sender, IsPlayingChangedEventArgs e)
{
IsPlaying = e.IsPlaying;
}
private void MediaSessionsService_TimelineChanged(object? sender, TimelineChangedEventArgs e)
{
var diff = Math.Abs(TotalTime.TotalMilliseconds - e.Position.TotalMilliseconds);
if (diff >= _timelineSyncThreshold)
{
TotalTime = e.Position;
if (TotalTime.TotalSeconds <= 1)
{
_totalPlayingTime = TimeSpan.Zero;
_isLastFMTracked = false;
}
}
// 大跨度,刷新布局,避免歌词不显示
if (diff >= _timelineSyncThreshold + 5000)
{
_isLayoutChanged = true;
}
}
private void MediaSessionsService_SongInfoChanged(object? sender, SongInfoChangedEventArgs e)
{
SongInfo = e.SongInfo;
UpdateTimelineSyncThreshold();
UpdatePositionOffset();
UpdateIsLastFMTrackEnabled();
if (SongInfo?.Title != _songTitle || SongInfo?.Artist != _songArtist)
{
_lastSongTitle = _songTitle;
_songTitle = SongInfo?.Title;
_isSongTitleChanged = true;
_lastSongArtist = _songArtist;
_songArtist = SongInfo?.Artist;
_isSongArtistChanged = true;
_lastSongAlbum = _songAlbum;
_songAlbum = SongInfo?.Album;
_isSongAlbumChanged = true;
_songDurationMs = (int)(SongInfo?.DurationMs ?? TimeSpan.FromMinutes(99).TotalMilliseconds);
_songInfoOpacityTransition.Reset(0f);
_songInfoOpacityTransition.StartTransition(1f);
TotalTime = TimeSpan.Zero;
// 处理 Last.fm 追踪
_totalPlayingTime = TimeSpan.Zero;
_isLastFMTracked = false;
}
}
private void MediaSessionsService_AlbumArtChangedChanged(object? sender, AlbumArtChangedEventArgs e)
{
_lastAlbumArtCanvasBitmap?.Dispose();

View File

@@ -1,4 +1,5 @@
using BetterLyrics.WinUI3.Helper;
using BetterLyrics.WinUI3.Enums;
using BetterLyrics.WinUI3.Helper;
using BetterLyrics.WinUI3.Models;
using BetterLyrics.WinUI3.Models.Settings;
using BetterLyrics.WinUI3.Services.LyricsSearchService;
@@ -6,13 +7,16 @@ using BetterLyrics.WinUI3.Services.MediaSessionsService;
using BetterLyrics.WinUI3.Services.SettingsService;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using CommunityToolkit.Mvvm.Messaging;
using CommunityToolkit.Mvvm.Messaging.Messages;
using System.Collections.ObjectModel;
using System.Linq;
using System.Threading.Tasks;
namespace BetterLyrics.WinUI3.ViewModels
{
public partial class LyricsSearchControlViewModel : BaseViewModel
public partial class LyricsSearchControlViewModel : BaseViewModel,
IRecipient<PropertyChangedMessage<SongInfo?>>
{
private readonly ILyricsSearchService _lyricsSearchService;
private readonly IMediaSessionsService _mediaSessionsService;
@@ -49,13 +53,6 @@ namespace BetterLyrics.WinUI3.ViewModels
AppSettings = _settingsService.AppSettings;
_mediaSessionsService.SongInfoChanged += MediaSessionsService_SongInfoChanged;
InitMappedSongSearchQuery();
}
private void MediaSessionsService_SongInfoChanged(object? sender, Events.SongInfoChangedEventArgs e)
{
InitMappedSongSearchQuery();
}
@@ -63,19 +60,19 @@ namespace BetterLyrics.WinUI3.ViewModels
{
LyricsSearchResults.Clear();
LyricsDataArr = null;
if (_mediaSessionsService.SongInfo != null)
if (_mediaSessionsService.CurrentSongInfo != null)
{
var found = GetMappedSongSearchQueryFromSettings();
if (found == null)
{
MappedSongSearchQuery = new MappedSongSearchQuery
{
OriginalTitle = _mediaSessionsService.SongInfo.Title,
OriginalArtist = _mediaSessionsService.SongInfo.Artist,
OriginalAlbum = _mediaSessionsService.SongInfo.Album,
MappedTitle = _mediaSessionsService.SongInfo.Title,
MappedArtist = _mediaSessionsService.SongInfo.Artist,
MappedAlbum = _mediaSessionsService.SongInfo.Album,
OriginalTitle = _mediaSessionsService.CurrentSongInfo.Title,
OriginalArtist = _mediaSessionsService.CurrentSongInfo.Artist,
OriginalAlbum = _mediaSessionsService.CurrentSongInfo.Album,
MappedTitle = _mediaSessionsService.CurrentSongInfo.Title,
MappedArtist = _mediaSessionsService.CurrentSongInfo.Artist,
MappedAlbum = _mediaSessionsService.CurrentSongInfo.Album,
};
}
else
@@ -87,13 +84,13 @@ namespace BetterLyrics.WinUI3.ViewModels
private MappedSongSearchQuery? GetMappedSongSearchQueryFromSettings()
{
if (_mediaSessionsService.SongInfo == null)
if (_mediaSessionsService.CurrentSongInfo == null)
{
return null;
}
var found = AppSettings.MappedSongSearchQueries
.Where(x => x.OriginalTitle == _mediaSessionsService.SongInfo.Title && x.OriginalArtist == _mediaSessionsService.SongInfo.Artist && x.OriginalAlbum == _mediaSessionsService.SongInfo.Album);
.Where(x => x.OriginalTitle == _mediaSessionsService.CurrentSongInfo.Title && x.OriginalArtist == _mediaSessionsService.CurrentSongInfo.Artist && x.OriginalAlbum == _mediaSessionsService.CurrentSongInfo.Album);
return found.FirstOrDefault();
}
@@ -117,7 +114,7 @@ namespace BetterLyrics.WinUI3.ViewModels
MappedSongSearchQuery.MappedTitle,
MappedSongSearchQuery.MappedArtist,
MappedSongSearchQuery.MappedAlbum,
_mediaSessionsService.SongInfo?.DurationMs ?? 0, token);
_mediaSessionsService.CurrentSongInfo?.DurationMs ?? 0, token);
}, token)];
IsSearching = false;
});
@@ -181,7 +178,7 @@ namespace BetterLyrics.WinUI3.ViewModels
MappedSongSearchQuery?.OriginalTitle ?? "",
MappedSongSearchQuery?.OriginalArtist ?? "",
MappedSongSearchQuery?.OriginalAlbum ?? "",
value?.Raw, (int?)_mediaSessionsService.SongInfo?.DurationMs, value?.Provider);
value?.Raw, (int?)_mediaSessionsService.CurrentSongInfo?.DurationMs, value?.Provider);
LyricsDataArr = [.. lyricsParser.LyricsDataArr];
}
else
@@ -198,5 +195,16 @@ namespace BetterLyrics.WinUI3.ViewModels
}
_mediaSessionsService.ChangePosition(value.StartMs / 1000.0);
}
public void Receive(PropertyChangedMessage<SongInfo?> message)
{
if (message.Sender is IMediaSessionsService)
{
if (message.PropertyName == nameof(IMediaSessionsService.CurrentSongInfo))
{
InitMappedSongSearchQuery();
}
}
}
}
}

View File

@@ -26,31 +26,23 @@ namespace BetterLyrics.WinUI3
{
public partial class LyricsWindowViewModel
: BaseWindowViewModel,
IRecipient<PropertyChangedMessage<bool>>,
IRecipient<PropertyChangedMessage<List<string>>>,
IRecipient<PropertyChangedMessage<ElementTheme>>
{
private readonly IMediaSessionsService _mediaSessionsService;
private readonly ISettingsService _settingsService;
private readonly ILiveStatesService _liveStatesService;
private ForegroundWindowWatcher? _fgWindowWatcher = null;
private DispatcherQueueTimer? _fgWindowWatcherTimer = null;
public LyricsWindowViewModel(ISettingsService settingsService, IMediaSessionsService mediaSessionsService, ILiveStatesService liveStatesService)
public LyricsWindowViewModel(ISettingsService settingsService, ILiveStatesService liveStatesService)
{
_settingsService = settingsService;
_mediaSessionsService = mediaSessionsService;
_liveStatesService = liveStatesService;
AppSettings = _settingsService.AppSettings;
LiveStates = _liveStatesService.LiveStates;
_mediaSessionsService.IsPlayingChanged += PlaybackService_IsPlayingChanged;
}
private void PlaybackService_IsPlayingChanged(object? sender, Events.IsPlayingChangedEventArgs e)
{
WindowHelper.SetLyricsWindowVisibilityByPlayingStatus(_dispatcherQueue);
}
[ObservableProperty] public partial AppSettings AppSettings { get; set; }
@@ -226,5 +218,16 @@ namespace BetterLyrics.WinUI3
}
}
}
public void Receive(PropertyChangedMessage<bool> message)
{
if (message.Sender is IMediaSessionsService)
{
if (message.PropertyName == nameof(IMediaSessionsService.CurrentIsPlaying))
{
WindowHelper.SetLyricsWindowVisibilityByPlayingStatus(_dispatcherQueue);
}
}
}
}
}

View File

@@ -19,11 +19,9 @@ using System.Threading.Tasks;
namespace BetterLyrics.WinUI3.ViewModels
{
public partial class PlaybackSettingsControlViewModel : BaseViewModel,
IRecipient<PropertyChangedMessage<LyricsSearchProvider?>>,
IRecipient<PropertyChangedMessage<TranslationSearchProvider?>>
public partial class PlaybackSettingsControlViewModel : BaseViewModel
{
private readonly IMediaSessionsService _mediaSessionsService;
public IMediaSessionsService MediaSessionsService;
private readonly ITranslateService _libreTranslateService;
private readonly ILastFMService _lastFMService;
private readonly ISettingsService _settingsService;
@@ -47,12 +45,6 @@ namespace BetterLyrics.WinUI3.ViewModels
[ObservableProperty]
public partial bool IsLXMusicServerTesting { get; set; } = false;
[ObservableProperty]
public partial LyricsSearchProvider? LyricsSearchProvider { get; set; } = null;
[ObservableProperty]
public partial TranslationSearchProvider? TranslationSearchProvider { get; set; } = null;
[ObservableProperty]
public partial string OriginalLyricsRef { get; set; } = "about:blank";
@@ -72,8 +64,9 @@ namespace BetterLyrics.WinUI3.ViewModels
ILastFMService lastFMService,
IResourceService resourceService)
{
MediaSessionsService = mediaSessionsService;
_settingsService = settingsService;
_mediaSessionsService = mediaSessionsService;
_libreTranslateService = libreTranslateService;
_resourceService = resourceService;
@@ -81,8 +74,6 @@ namespace BetterLyrics.WinUI3.ViewModels
_lastFMService.UserChanged += LastFMService_UserChanged;
_lastFMService.IsAuthenticatedChanged += LastFMService_IsAuthenticatedChanged;
_mediaSessionsService.SongInfoChanged += MediaSessionsService_SongInfoChanged;
AppSettings = _settingsService.AppSettings;
AppSettings.MediaSourceProvidersInfo.CollectionChanged += MediaSourceProvidersInfo_CollectionChanged;
@@ -93,9 +84,6 @@ namespace BetterLyrics.WinUI3.ViewModels
IsLastFMAuthenticated = _lastFMService.IsAuthenticated;
LastFMUser = _lastFMService.User;
LyricsSearchProvider = _mediaSessionsService.LyricsSearchProvider;
TranslationSearchProvider = _mediaSessionsService.TranslationSearchProvider;
SelectedMediaSourceProvider = AppSettings.MediaSourceProvidersInfo.FirstOrDefault();
}
@@ -114,15 +102,6 @@ namespace BetterLyrics.WinUI3.ViewModels
LastFMUser = e.User;
}
private void MediaSessionsService_SongInfoChanged(object? sender, Events.SongInfoChangedEventArgs e)
{
var current = AppSettings.MediaSourceProvidersInfo.Where(x => x.Provider == e.SongInfo?.PlayerId)?.FirstOrDefault();
if (_mediaSessionsService.Position.TotalSeconds <= 1 && current?.ResetPositionOffsetOnSongChanged == true)
{
current.PositionOffset = 0;
}
}
private void MediaSessionsService_SessionIdsChanged(object? sender, Events.MediaSourceProvidersInfoEventArgs e)
{
SelectedMediaSourceProvider = AppSettings.MediaSourceProvidersInfo.FirstOrDefault();
@@ -201,29 +180,7 @@ namespace BetterLyrics.WinUI3.ViewModels
private void SaveAppleMusicMediaUserToken()
{
PasswordVaultHelper.Save(Constants.App.AppName, Constants.AppleMusic.MediaUserTokenKey, AppleMusicMediaUserToken);
_mediaSessionsService.UpdateLyrics();
}
public void Receive(PropertyChangedMessage<LyricsSearchProvider?> message)
{
if (message.Sender is MediaSessionsService)
{
if (message.PropertyName == nameof(MediaSessionsService.LyricsSearchProvider))
{
LyricsSearchProvider = message.NewValue;
}
}
}
public void Receive(PropertyChangedMessage<TranslationSearchProvider?> message)
{
if (message.Sender is MediaSessionsService)
{
if (message.PropertyName == nameof(MediaSessionsService.TranslationSearchProvider))
{
TranslationSearchProvider = message.NewValue;
}
}
MediaSessionsService.UpdateLyrics();
}
partial void OnSelectedTargetLanguageIndexChanged(int value)

View File

@@ -201,13 +201,13 @@
Style="{StaticResource GhostButtonStyle}">
<interactivity:Interaction.Behaviors>
<interactivity:DataTriggerBehavior
Binding="{x:Bind ViewModel.IsSongPlaying, Mode=OneWay}"
Binding="{x:Bind ViewModel.MediaSessionsService.CurrentIsPlaying, Mode=OneWay}"
ComparisonCondition="Equal"
Value="True">
<interactivity:ChangePropertyAction PropertyName="Visibility" Value="Visible" />
</interactivity:DataTriggerBehavior>
<interactivity:DataTriggerBehavior
Binding="{x:Bind ViewModel.IsSongPlaying, Mode=OneWay}"
Binding="{x:Bind ViewModel.MediaSessionsService.CurrentIsPlaying, Mode=OneWay}"
ComparisonCondition="Equal"
Value="False">
<interactivity:ChangePropertyAction PropertyName="Visibility" Value="Collapsed" />
@@ -221,13 +221,13 @@
Style="{StaticResource GhostButtonStyle}">
<interactivity:Interaction.Behaviors>
<interactivity:DataTriggerBehavior
Binding="{x:Bind ViewModel.IsSongPlaying, Mode=OneWay}"
Binding="{x:Bind ViewModel.MediaSessionsService.CurrentIsPlaying, Mode=OneWay}"
ComparisonCondition="Equal"
Value="True">
<interactivity:ChangePropertyAction PropertyName="Visibility" Value="Collapsed" />
</interactivity:DataTriggerBehavior>
<interactivity:DataTriggerBehavior
Binding="{x:Bind ViewModel.IsSongPlaying, Mode=OneWay}"
Binding="{x:Bind ViewModel.MediaSessionsService.CurrentIsPlaying, Mode=OneWay}"
ComparisonCondition="Equal"
Value="False">
<interactivity:ChangePropertyAction PropertyName="Visibility" Value="Visible" />
@@ -316,7 +316,7 @@
Margin="0,-32,0,0"
HorizontalAlignment="Stretch"
VerticalAlignment="Top"
Maximum="{x:Bind ViewModel.SongDurationSeconds, Mode=OneWay}"
Maximum="{x:Bind ViewModel.MediaSessionsService.CurrentSongInfo.DurationMs, Mode=OneWay, Converter={StaticResource MillisecondsToSecondsConverter}}"
Minimum="0"
Style="{StaticResource GhostSliderStyle}"
ThumbToolTipValueConverter="{StaticResource SecondsToFormattedTimeConverter}"