Skip to content

Commit

Permalink
Merge pull request #374 from Fooxboy/68-last-fm
Browse files Browse the repository at this point in the history
#68 last fm scrobbling
  • Loading branch information
Fooxboy authored Apr 27, 2024
2 parents 9e1c77f + 9126248 commit 07bc276
Show file tree
Hide file tree
Showing 18 changed files with 1,424 additions and 6 deletions.
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[submodule "lastfm"]
path = lastfm
url = https://github.com/tolbxela/lastfm.git
6 changes: 6 additions & 0 deletions MusicX.sln
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FFMediaToolkit", "FFMediaTo
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PsCompiler", "PsCompiler\PsCompiler.csproj", "{449B9F89-C7C2-4C7E-9F2F-28CBB731E7F5}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "IF.Lastfm.Core", "lastfm\src\IF.Lastfm.Core\IF.Lastfm.Core.csproj", "{57464921-52F3-4172-83E9-0B7D75F9DDDF}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x64 = Debug|x64
Expand Down Expand Up @@ -74,6 +76,10 @@ Global
{449B9F89-C7C2-4C7E-9F2F-28CBB731E7F5}.Debug|x64.Build.0 = Debug|Any CPU
{449B9F89-C7C2-4C7E-9F2F-28CBB731E7F5}.Release|x64.ActiveCfg = Release|Any CPU
{449B9F89-C7C2-4C7E-9F2F-28CBB731E7F5}.Release|x64.Build.0 = Release|Any CPU
{57464921-52F3-4172-83E9-0B7D75F9DDDF}.Debug|x64.ActiveCfg = Debug|Any CPU
{57464921-52F3-4172-83E9-0B7D75F9DDDF}.Debug|x64.Build.0 = Debug|Any CPU
{57464921-52F3-4172-83E9-0B7D75F9DDDF}.Release|x64.ActiveCfg = Release|Any CPU
{57464921-52F3-4172-83E9-0B7D75F9DDDF}.Release|x64.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down
5 changes: 5 additions & 0 deletions MusicX/Models/ConfigModel.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using IF.Lastfm.Core.Objects;
using MusicX.Services.Player;

namespace MusicX.Models
Expand Down Expand Up @@ -58,5 +59,9 @@ public class ConfigModel
public bool? SavePlayerState { get; set; }

public PlayerState? LastPlayerState { get; set; }

public LastUserSession? LastFmSession { get; set; }

public bool? SendLastFmScrobbles { get; set; }
}
}
1 change: 1 addition & 0 deletions MusicX/MusicX.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@

<ItemGroup>
<ProjectReference Include="..\FFMediaToolkit\FFMediaToolkit.csproj" />
<ProjectReference Include="..\lastfm\src\IF.Lastfm.Core\IF.Lastfm.Core.csproj" />
<ProjectReference Include="..\MusicX.Core\MusicX.Core.csproj" />
</ItemGroup>

Expand Down
2 changes: 1 addition & 1 deletion MusicX/Services/Player/TrackStats/DiscordTrackStats.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ public DiscordTrackStats(DiscordService discordService, ConfigService configServ
_configService = configService;
}

public Task TrackChangedAsync(PlaylistTrack? previousTrack, PlaylistTrack newTrack, ChangeReason reason)
public Task TrackChangedAsync(PlaylistTrack? previousTrack, PlaylistTrack newTrack, ChangeReason reason, TimeSpan? position = null)
{
if (_configService.Config.ShowRPC == true)
SetTrack(newTrack);
Expand Down
2 changes: 1 addition & 1 deletion MusicX/Services/Player/TrackStats/ITrackStatsListener.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,6 @@ namespace MusicX.Services.Player.TrackStats;

public interface ITrackStatsListener
{
Task TrackChangedAsync(PlaylistTrack? previousTrack, PlaylistTrack newTrack, ChangeReason reason);
Task TrackChangedAsync(PlaylistTrack? previousTrack, PlaylistTrack newTrack, ChangeReason reason, TimeSpan? position = null);
Task TrackPlayStateChangedAsync(PlaylistTrack track, TimeSpan position, bool paused);
}
49 changes: 49 additions & 0 deletions MusicX/Services/Player/TrackStats/LastFmStats.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
using System;
using System.Linq;
using System.Threading.Tasks;
using IF.Lastfm.Core.Api;
using IF.Lastfm.Core.Objects;
using IF.Lastfm.Core.Scrobblers;
using MusicX.Models.Enums;
using MusicX.Shared.Player;

namespace MusicX.Services.Player.TrackStats;

public class LastFmStats : ITrackStatsListener
{
private readonly IScrobbler _scrobbler;
private readonly ITrackApi _trackApi;
private readonly ConfigService _configService;

public LastFmStats(IScrobbler scrobbler, ITrackApi trackApi, ConfigService configService)
{
_scrobbler = scrobbler;
_trackApi = trackApi;
_configService = configService;
}

public async Task TrackChangedAsync(PlaylistTrack? previousTrack, PlaylistTrack newTrack, ChangeReason reason, TimeSpan? position = null)
{
if (_configService.Config.LastFmSession is null || _configService.Config.SendLastFmScrobbles is not true)
return;

if (previousTrack is not null && previousTrack.Data.Duration > TimeSpan.FromSeconds(30) &&
position.HasValue && (position.Value > TimeSpan.FromMinutes(4) || position.Value > previousTrack.Data.Duration / 2))
await _scrobbler.ScrobbleAsync(new Scrobble(previousTrack.MainArtists.First().Name,
previousTrack.AlbumId?.Name, previousTrack.Title, DateTimeOffset.Now - position.Value)
{
Duration = previousTrack.Data.Duration
});

await _trackApi.UpdateNowPlayingAsync(new Scrobble(newTrack.MainArtists.First().Name, newTrack.AlbumId?.Name,
newTrack.Title, DateTimeOffset.Now)
{
Duration = newTrack.Data.Duration
});
}

public Task TrackPlayStateChangedAsync(PlaylistTrack track, TimeSpan position, bool paused)
{
return Task.CompletedTask;
}
}
2 changes: 1 addition & 1 deletion MusicX/Services/Player/TrackStats/ListenTogetherStats.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ public ListenTogetherStats(ListenTogetherService listenTogetherService)
this._listenTogetherService = listenTogetherService;
}

public async Task TrackChangedAsync(PlaylistTrack? previousTrack, PlaylistTrack newTrack, ChangeReason reason)
public async Task TrackChangedAsync(PlaylistTrack? previousTrack, PlaylistTrack newTrack, ChangeReason reason, TimeSpan? position = null)
{
if (_listenTogetherService.PlayerMode != PlayerMode.Owner) return;

Expand Down
2 changes: 1 addition & 1 deletion MusicX/Services/Player/TrackStats/VkTrackBroadcastStats.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ public VkTrackBroadcastStats(VkService vkService, ConfigService configService, L
_snackbarService = snackbarService;
}

public Task TrackChangedAsync(PlaylistTrack? previousTrack, PlaylistTrack newTrack, ChangeReason reason)
public Task TrackChangedAsync(PlaylistTrack? previousTrack, PlaylistTrack newTrack, ChangeReason reason, TimeSpan? position = null)
{
try
{
Expand Down
4 changes: 2 additions & 2 deletions MusicX/Services/Player/TrackStats/VkTrackStats.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ public VkTrackStats(VkService vkService)
_vkService = vkService;
}

public async Task TrackChangedAsync(PlaylistTrack? previousTrack, PlaylistTrack newTrack, ChangeReason reason)
public async Task TrackChangedAsync(PlaylistTrack? previousTrack, PlaylistTrack newTrack, ChangeReason reason, TimeSpan? position = null)
{
if (newTrack.Data is not VkTrackData newTrackData)
return;
Expand Down Expand Up @@ -55,7 +55,7 @@ public async Task TrackChangedAsync(PlaylistTrack? previousTrack, PlaylistTrack
PlaybackStartedAt = "0",
TrackCode = previousTrackData.TrackCode,
StreamingType = "online",
Duration = previousTrackData.Duration.TotalSeconds.ToString(),
Duration = (position ?? previousTrackData.Duration).TotalSeconds.ToString(),
Repeat = "all",
State = "app",
Source = newTrackData.ParentBlockId!,
Expand Down
64 changes: 64 additions & 0 deletions MusicX/ViewModels/Modals/LastFmAuthModalViewModel.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
using System.Diagnostics;
using System.Threading.Tasks;
using System.Windows.Input;
using AsyncAwaitBestPractices.MVVM;
using IF.Lastfm.Core.Api;
using IF.Lastfm.Core.Api.Helpers;
using MusicX.Services;
using Wpf.Ui;
using Wpf.Ui.Controls;
using Wpf.Ui.Extensions;
using NavigationService = MusicX.Services.NavigationService;

namespace MusicX.ViewModels.Modals;

public class LastFmAuthModalViewModel : BaseViewModel
{
private readonly ILastAuth _auth;
private readonly NavigationService _navigationService;
private readonly ISnackbarService _snackbarService;
private readonly ConfigService _configService;
private string? _token;

public ICommand ConfirmCommand { get; }

public LastFmAuthModalViewModel(ILastAuth auth, NavigationService navigationService, ISnackbarService snackbarService, ConfigService configService)
{
_auth = auth;
_navigationService = navigationService;
_snackbarService = snackbarService;
_configService = configService;

ConfirmCommand = new AsyncCommand(ConfirmAsync);
}

private async Task ConfirmAsync()
{
if (_token is null)
return;

var response = await _auth.GetSessionTokenAsync(_token);

if (!response.Success)
{
_snackbarService.Show("Ошибка авторизации", $"Сервис вернул: {response.Status}", ControlAppearance.Danger);
return;
}

_navigationService.CloseModal();

_configService.Config.LastFmSession = _auth.UserSession;
await _configService.SetConfig(_configService.Config);
}

public async Task OpenAuthPageAsync()
{
_token = ((LastResponse<string>)await _auth.GetAuthTokenAsync()).Content;

Process.Start(new ProcessStartInfo
{
FileName = $"https://last.fm/api/auth/?api_key={_auth.ApiKey}&token={_token}",
UseShellExecute = true
});
}
}
35 changes: 35 additions & 0 deletions MusicX/Views/Modals/LastFmAuthModal.xaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<Page x:Class="MusicX.Views.Modals.LastFmAuthModal"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:MusicX.Views.Modals"
xmlns:ui="http://schemas.lepo.co/wpfui/2022/xaml"
xmlns:modals="clr-namespace:MusicX.ViewModels.Modals"
mc:Ignorable="d"
Title="Last.Fm" Height="300" Width="200"
d:DataContext="{d:DesignInstance modals:LastFmAuthModalViewModel}"
Loaded="LastFmAuthModal_OnLoaded">
<Grid VerticalAlignment="Center">
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<ui:Card>
<ui:SymbolIcon Symbol="Globe24"
FontSize="48"/>
<ui:Card.Footer>
<TextBlock TextWrapping="Wrap" TextAlignment="Center"
FontWeight="SemiBold">Подтвердите вход в аккаунт в браузере</TextBlock>
</ui:Card.Footer>
</ui:Card>

<ui:Button Grid.Row="1" HorizontalAlignment="Center" Margin="0,15"
Command="{Binding ConfirmCommand}">
<ui:Button.Icon>
<ui:SymbolIcon Symbol="Checkmark24" />
</ui:Button.Icon>
Готово
</ui:Button>
</Grid>
</Page>
19 changes: 19 additions & 0 deletions MusicX/Views/Modals/LastFmAuthModal.xaml.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
using System.Windows;
using System.Windows.Controls;
using MusicX.ViewModels.Modals;

namespace MusicX.Views.Modals;

public partial class LastFmAuthModal : Page
{
public LastFmAuthModal()
{
InitializeComponent();
}

private async void LastFmAuthModal_OnLoaded(object sender, RoutedEventArgs e)
{
if (DataContext is LastFmAuthModalViewModel viewModel)
await viewModel.OpenAuthPageAsync();
}
}
8 changes: 8 additions & 0 deletions MusicX/Views/SettingsView.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,14 @@
Content="Показывать трек в статусе ВКонтакте"
FontSize="15"
Unchecked="BroacastVK_Unchecked" />
<wpfui:ToggleSwitch
x:Name="SendLastFm"
Margin="0,5,0,0"
Checked="SendLastFm_OnChanged"
FontSize="15"
Content="Отправлять треки в Last.Fm"
Unchecked="SendLastFm_OnChanged">
</wpfui:ToggleSwitch>
</StackPanel>

<Rectangle Margin="0,10,0,10" Fill="White" HorizontalAlignment="Stretch" Height="1" Opacity="0.2" VerticalAlignment="Center"/>
Expand Down
13 changes: 13 additions & 0 deletions MusicX/Views/SettingsView.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
using MusicX.Models;
using MusicX.Services;
using MusicX.ViewModels;
using MusicX.ViewModels.Modals;
using MusicX.Views.Login;
using MusicX.Views.Modals;
using NLog;
Expand Down Expand Up @@ -72,6 +73,7 @@ private async void SettingsView_Loaded(object sender, RoutedEventArgs e)
MinimizeToTray.IsChecked = config.MinimizeToTray.GetValueOrDefault();
GetBetaUpdates.IsChecked = config.GetBetaUpdates.GetValueOrDefault();
SavePlayerState.IsChecked = config.SavePlayerState.GetValueOrDefault();
SendLastFm.IsChecked = config.SendLastFmScrobbles.GetValueOrDefault();

UserName.Text = config.UserName;

Expand Down Expand Up @@ -571,5 +573,16 @@ private async void SavePlayerState_OnCheckChanged(object sender, RoutedEventArgs

await configService.SetConfig(config);
}

private async void SendLastFm_OnChanged(object sender, RoutedEventArgs e)
{
config.SendLastFmScrobbles = SendLastFm.IsChecked;

await configService.SetConfig(config);

if (config.LastFmSession is null)
StaticService.Container.GetRequiredService<NavigationService>()
.OpenModal<LastFmAuthModal>(StaticService.Container.GetRequiredService<LastFmAuthModalViewModel>());
}
}
}
15 changes: 15 additions & 0 deletions MusicX/Views/StartingWindow.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
using System.Text.Json;
using System.Threading.Tasks;
using System.Windows;
using IF.Lastfm.Core.Api;
using IF.Lastfm.Core.Scrobblers;
using Microsoft.AppCenter.Analytics;
using Microsoft.Extensions.DependencyInjection;
using MusicX.Core.Services;
Expand Down Expand Up @@ -87,6 +89,7 @@ await Task.Run(async () =>
collection.AddSingleton<ITrackStatsListener, VkTrackBroadcastStats>();
collection.AddSingleton<ITrackStatsListener, VkTrackStats>();
collection.AddSingleton<ITrackStatsListener, ListenTogetherStats>();
collection.AddSingleton<ITrackStatsListener, LastFmStats>();

collection.AddTransient<SectionViewModel>();
collection.AddTransient<PlaylistViewModel>();
Expand All @@ -102,6 +105,7 @@ await Task.Run(async () =>
collection.AddTransient<CaptchaModalViewModel>();
collection.AddTransient<AccountsWindowViewModel>();
collection.AddTransient<LoginVerificationMethodsModalViewModel>();
collection.AddTransient<LastFmAuthModalViewModel>();

collection.AddSingleton<NavigationService>();
collection.AddSingleton<ConfigService>();
Expand All @@ -113,6 +117,17 @@ await Task.Run(async () =>
collection.AddSingleton<ICustomSectionsService, CustomSectionsService>();
collection.AddSingleton<ISnackbarService, MusicXSnackbarService>();
collection.AddSingleton<UpdateService>();
collection.AddSingleton<ILastAuth>(s =>
{
var auth = new LastAuth("ff3b1adf454799a760ad29a0b71bd6b3", "74ef981214417381716f72a46677a802");

if (s.GetRequiredService<ConfigService>().Config.LastFmSession is { } session)
auth.LoadSession(session);

return auth;
});
collection.AddSingleton<IScrobbler, MemoryScrobbler>();
collection.AddSingleton<ITrackApi, TrackApi>();

var container = StaticService.Container = collection.BuildServiceProvider();

Expand Down
Loading

0 comments on commit 07bc276

Please sign in to comment.