adding db update on token refresh, more efficient walking, adding playlist generator
This commit is contained in:
parent
5f535c0929
commit
b700d94afb
@ -8,6 +8,6 @@
|
|||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\Mixonomer.Fire\Mixonomer.Fire.csproj" />
|
<ProjectReference Include="..\Mixonomer.Fire\Mixonomer.Fire.csproj" />
|
||||||
<ProjectReference Include="..\Mixonomer.Playlist\Mixonomer.Playlist.csproj" />
|
<ProjectReference Include="..\Mixonomer\Mixonomer.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
@ -2,8 +2,10 @@
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Mixonomer.Fire;
|
using Mixonomer.Fire;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Microsoft.Extensions.Logging.Abstractions;
|
||||||
using Mixonomer.Fire.Extensions;
|
using Mixonomer.Fire.Extensions;
|
||||||
using Mixonomer.Playlist;
|
using Mixonomer;
|
||||||
|
|
||||||
namespace Mixonomer.CLI;
|
namespace Mixonomer.CLI;
|
||||||
|
|
||||||
@ -19,5 +21,11 @@ class Program
|
|||||||
|
|
||||||
var walker = new PartTreeWalker(repo);
|
var walker = new PartTreeWalker(repo);
|
||||||
var partPlaylists = await walker.GetPlaylistParts("andy", "RAP");
|
var partPlaylists = await walker.GetPlaylistParts("andy", "RAP");
|
||||||
|
|
||||||
|
var spotifyNetwork = new SpotifyNetworkProvider(repo, null, NullLogger<SpotifyNetworkProvider>.Instance);
|
||||||
|
|
||||||
|
var generator = new PlaylistGenerator(repo, spotifyNetwork, walker, NullLogger<PlaylistGenerator>.Instance);
|
||||||
|
|
||||||
|
await generator.GeneratePlaylist("RAP", "andy");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,7 +15,7 @@ public class UserRepo
|
|||||||
|
|
||||||
public UserRepo(FirestoreDb db = null, string projectId = null)
|
public UserRepo(FirestoreDb db = null, string projectId = null)
|
||||||
{
|
{
|
||||||
this.db = db ?? FirestoreDb.Create(projectId);
|
this.db = db ?? FirestoreDb.Create(projectId ?? Environment.GetEnvironmentVariable("GOOGLE_CLOUD_PROJECT"));
|
||||||
userCollection = this.db.Collection(USER_COLLECTION);
|
userCollection = this.db.Collection(USER_COLLECTION);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15,7 +15,7 @@
|
|||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\Mixonomer.Fire\Mixonomer.Fire.csproj" />
|
<ProjectReference Include="..\Mixonomer.Fire\Mixonomer.Fire.csproj" />
|
||||||
<ProjectReference Include="..\Mixonomer.Playlist\Mixonomer.Playlist.csproj" />
|
<ProjectReference Include="..\Mixonomer\Mixonomer.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
@ -10,6 +10,7 @@ using Microsoft.AspNetCore.Hosting;
|
|||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using Mixonomer.Fire;
|
using Mixonomer.Fire;
|
||||||
|
using SpotifyAPI.Web;
|
||||||
|
|
||||||
namespace Mixonomer.Func;
|
namespace Mixonomer.Func;
|
||||||
|
|
||||||
@ -17,10 +18,14 @@ namespace Mixonomer.Func;
|
|||||||
public class RunUserPlaylist : ICloudEventFunction<MessagePublishedData>
|
public class RunUserPlaylist : ICloudEventFunction<MessagePublishedData>
|
||||||
{
|
{
|
||||||
private readonly ILogger _logger;
|
private readonly ILogger _logger;
|
||||||
|
private readonly UserRepo _userRepo;
|
||||||
|
private readonly SpotifyNetworkProvider _spotifyMetworkProvider;
|
||||||
|
|
||||||
public RunUserPlaylist(ILogger<RunUserPlaylist> logger)
|
public RunUserPlaylist(ILogger<RunUserPlaylist> logger, UserRepo userRepo, SpotifyNetworkProvider spotifyMetworkProvider)
|
||||||
{
|
{
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
|
_userRepo = userRepo;
|
||||||
|
_spotifyMetworkProvider = spotifyMetworkProvider;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -28,9 +33,10 @@ public class RunUserPlaylist : ICloudEventFunction<MessagePublishedData>
|
|||||||
{
|
{
|
||||||
_logger.LogInformation($"Received message in C# {data.Message}, {cloudEvent.GetPopulatedAttributes()}");
|
_logger.LogInformation($"Received message in C# {data.Message}, {cloudEvent.GetPopulatedAttributes()}");
|
||||||
|
|
||||||
var userRepo = new UserRepo(projectId: System.Environment.GetEnvironmentVariable("GOOGLE_CLOUD_PROJECT"));
|
var user = await _userRepo.GetUser(data.Message.Attributes["username"]);
|
||||||
|
|
||||||
var user = await userRepo.GetUser(data.Message.Attributes["username"]);
|
var spotifyConfig = await _spotifyMetworkProvider.GetUserConfig(user);
|
||||||
|
var spotifyClient = new SpotifyClient(spotifyConfig);
|
||||||
|
|
||||||
_logger.LogInformation($"{user.username} was last refreshed at {user.last_refreshed}");
|
_logger.LogInformation($"{user.username} was last refreshed at {user.last_refreshed}");
|
||||||
}
|
}
|
||||||
@ -43,5 +49,10 @@ public class RunUserPlaylistStartup : FunctionsStartup
|
|||||||
base.ConfigureServices(context, services);
|
base.ConfigureServices(context, services);
|
||||||
|
|
||||||
services.AddSecretManagerServiceClient();
|
services.AddSecretManagerServiceClient();
|
||||||
|
// services.AddFirestoreClient();
|
||||||
|
|
||||||
|
services.AddTransient<SpotifyNetworkProvider>()
|
||||||
|
.AddTransient<PlaylistGenerator>()
|
||||||
|
.AddSingleton<UserRepo>();
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -11,7 +11,7 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Mixonomer.Tests", "Mixonome
|
|||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Mixonomer.CLI", "Mixonomer.CLI\Mixonomer.CLI.csproj", "{7469F571-DBF2-4D60-85FE-4041C445A490}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Mixonomer.CLI", "Mixonomer.CLI\Mixonomer.CLI.csproj", "{7469F571-DBF2-4D60-85FE-4041C445A490}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Mixonomer.Playlist", "Mixonomer.Playlist\Mixonomer.Playlist.csproj", "{274560D0-2EBB-4C6D-BA45-2270DB92F12C}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Mixonomer", "Mixonomer\Mixonomer.csproj", "{274560D0-2EBB-4C6D-BA45-2270DB92F12C}"
|
||||||
EndProject
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
|
@ -12,7 +12,12 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Inflatable.Lastfm" Version="1.2.0" />
|
||||||
<PackageReference Include="SpotifyAPI.Web" Version="7.0.2" />
|
<PackageReference Include="SpotifyAPI.Web" Version="7.0.2" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Folder Include="Last.fm\" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
@ -1,4 +1,4 @@
|
|||||||
namespace Mixonomer.Playlist;
|
namespace Mixonomer;
|
||||||
|
|
||||||
public static class Months
|
public static class Months
|
||||||
{
|
{
|
@ -2,7 +2,7 @@ using Google.Cloud.Firestore;
|
|||||||
using Mixonomer.Fire;
|
using Mixonomer.Fire;
|
||||||
using Mixonomer.Fire.Extensions;
|
using Mixonomer.Fire.Extensions;
|
||||||
|
|
||||||
namespace Mixonomer.Playlist;
|
namespace Mixonomer;
|
||||||
|
|
||||||
public class PartTreeWalker
|
public class PartTreeWalker
|
||||||
{
|
{
|
||||||
@ -10,22 +10,24 @@ public class PartTreeWalker
|
|||||||
|
|
||||||
private readonly HashSet<string> _processedPlaylists = new();
|
private readonly HashSet<string> _processedPlaylists = new();
|
||||||
public HashSet<string>? SpotifyPlaylistNames { get; private set; }
|
public HashSet<string>? SpotifyPlaylistNames { get; private set; }
|
||||||
|
private List<Playlist> _userPlaylists;
|
||||||
|
|
||||||
public PartTreeWalker(UserRepo userRepo)
|
public PartTreeWalker(UserRepo userRepo)
|
||||||
{
|
{
|
||||||
_userRepo = userRepo;
|
_userRepo = userRepo;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<IEnumerable<string>> GetPlaylistParts(string username, string playlistName)
|
public async Task<IEnumerable<string>?> GetPlaylistParts(string username, string playlistName)
|
||||||
{
|
{
|
||||||
var user = await _userRepo.GetUser(username);
|
var user = await _userRepo.GetUser(username);
|
||||||
|
|
||||||
return await GetPlaylistParts(user, playlistName);
|
return await GetPlaylistParts(user, playlistName);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<IEnumerable<string>> GetPlaylistParts(User user, string playlistName)
|
public async Task<IEnumerable<string>?> GetPlaylistParts(User user, string playlistName)
|
||||||
{
|
{
|
||||||
var playlist = await _userRepo.GetPlaylists(user).Where(x => x.name == playlistName).FirstOrDefaultAsync();
|
_userPlaylists = await _userRepo.GetPlaylists(user).ToListAsync();
|
||||||
|
var playlist = _userPlaylists.SingleOrDefault(x => x.name == playlistName);
|
||||||
|
|
||||||
if (playlist is not null)
|
if (playlist is not null)
|
||||||
{
|
{
|
||||||
@ -33,20 +35,22 @@ public class PartTreeWalker
|
|||||||
|
|
||||||
foreach (var part in playlist.playlist_references)
|
foreach (var part in playlist.playlist_references)
|
||||||
{
|
{
|
||||||
await ProcessPlaylist(part);
|
ProcessPlaylist(part);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return SpotifyPlaylistNames;
|
return SpotifyPlaylistNames;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task ProcessPlaylist(DocumentReference documentReference)
|
private void ProcessPlaylist(DocumentReference documentReference)
|
||||||
{
|
{
|
||||||
if (!_processedPlaylists.Contains(documentReference.Id))
|
if (!_processedPlaylists.Contains(documentReference.Id))
|
||||||
{
|
{
|
||||||
var playlist = (await documentReference.GetSnapshotAsync()).ConvertTo<Fire.Playlist>();
|
var playlist = _userPlaylists.SingleOrDefault(x => x.Reference.Id == documentReference.Id);
|
||||||
|
|
||||||
_processedPlaylists.Add(documentReference.Id);
|
_processedPlaylists.Add(documentReference.Id);
|
||||||
|
if (playlist != null)
|
||||||
|
{
|
||||||
foreach (var p in playlist.parts)
|
foreach (var p in playlist.parts)
|
||||||
{
|
{
|
||||||
SpotifyPlaylistNames?.Add(p);
|
SpotifyPlaylistNames?.Add(p);
|
||||||
@ -54,7 +58,8 @@ public class PartTreeWalker
|
|||||||
|
|
||||||
foreach (var p in playlist.playlist_references)
|
foreach (var p in playlist.playlist_references)
|
||||||
{
|
{
|
||||||
await ProcessPlaylist(p);
|
ProcessPlaylist(p);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
32
Mixonomer/PlaylistGenerator.cs
Normal file
32
Mixonomer/PlaylistGenerator.cs
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Mixonomer.Fire;
|
||||||
|
using SpotifyAPI.Web;
|
||||||
|
|
||||||
|
namespace Mixonomer;
|
||||||
|
|
||||||
|
public class PlaylistGenerator
|
||||||
|
{
|
||||||
|
private readonly ILogger<PlaylistGenerator> _logger;
|
||||||
|
private readonly UserRepo _userRepo;
|
||||||
|
private readonly SpotifyNetworkProvider _spotifyMetworkProvider;
|
||||||
|
private readonly PartTreeWalker _partTreeWalker;
|
||||||
|
|
||||||
|
public PlaylistGenerator(UserRepo userRepo, SpotifyNetworkProvider spotifyMetworkProvider, PartTreeWalker partTreeWalker, ILogger<PlaylistGenerator> logger)
|
||||||
|
{
|
||||||
|
_userRepo = userRepo;
|
||||||
|
_spotifyMetworkProvider = spotifyMetworkProvider;
|
||||||
|
_logger = logger;
|
||||||
|
_partTreeWalker = partTreeWalker;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task GeneratePlaylist(string playlistName, string username)
|
||||||
|
{
|
||||||
|
var user = await _userRepo.GetUser(username);
|
||||||
|
|
||||||
|
var spotifyConfig = await _spotifyMetworkProvider.GetUserConfig(user);
|
||||||
|
var spotifyClient = new SpotifyClient(spotifyConfig);
|
||||||
|
|
||||||
|
var userPlaylists = await spotifyClient.Playlists.CurrentUsers();
|
||||||
|
var allPlaylists = await spotifyClient.PaginateAll(userPlaylists);
|
||||||
|
}
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
namespace Mixonomer.Playlist;
|
namespace Mixonomer;
|
||||||
|
|
||||||
public static class SecretStrings
|
public static class SecretStrings
|
||||||
{
|
{
|
@ -1,17 +1,21 @@
|
|||||||
|
using Google.Cloud.Firestore;
|
||||||
using Google.Cloud.SecretManager.V1;
|
using Google.Cloud.SecretManager.V1;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
using Mixonomer.Fire;
|
using Mixonomer.Fire;
|
||||||
using SpotifyAPI.Web;
|
using SpotifyAPI.Web;
|
||||||
|
|
||||||
namespace Mixonomer.Playlist;
|
namespace Mixonomer;
|
||||||
|
|
||||||
public class SpotifyNetworkProvider
|
public class SpotifyNetworkProvider
|
||||||
{
|
{
|
||||||
private readonly SecretManagerServiceClient _secretClient;
|
private readonly SecretManagerServiceClient _secretClient;
|
||||||
private readonly UserRepo _userRepo;
|
private readonly UserRepo _userRepo;
|
||||||
|
private readonly ILogger<SpotifyNetworkProvider> _logger;
|
||||||
|
|
||||||
public SpotifyNetworkProvider(UserRepo userRepo, SecretManagerServiceClient secretClient)
|
public SpotifyNetworkProvider(UserRepo userRepo, SecretManagerServiceClient? secretClient, ILogger<SpotifyNetworkProvider> logger)
|
||||||
{
|
{
|
||||||
_userRepo = userRepo;
|
_userRepo = userRepo;
|
||||||
|
_logger = logger;
|
||||||
_secretClient = secretClient ?? SecretManagerServiceClient.Create();
|
_secretClient = secretClient ?? SecretManagerServiceClient.Create();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -25,8 +29,8 @@ public class SpotifyNetworkProvider
|
|||||||
var spotifyClient = await _secretClient.AccessSecretVersionAsync(SecretStrings.SPOT_CLIENT_URI);
|
var spotifyClient = await _secretClient.AccessSecretVersionAsync(SecretStrings.SPOT_CLIENT_URI);
|
||||||
var spotifySecret = await _secretClient.AccessSecretVersionAsync(SecretStrings.SPOT_SECRET_URI);
|
var spotifySecret = await _secretClient.AccessSecretVersionAsync(SecretStrings.SPOT_SECRET_URI);
|
||||||
|
|
||||||
var spotifyClientStr = spotifyClient.Payload.Data.ToString() ?? throw new ArgumentException("No Spotify Client ID returned");
|
var spotifyClientStr = spotifyClient.Payload.Data.ToStringUtf8() ?? throw new ArgumentException("No Spotify Client ID returned");
|
||||||
var spotifySecretStr = spotifySecret.Payload.Data.ToString() ?? throw new ArgumentException("No Spotify Secret ID returned");
|
var spotifySecretStr = spotifySecret.Payload.Data.ToStringUtf8() ?? throw new ArgumentException("No Spotify Secret returned");
|
||||||
|
|
||||||
var refreshed = await new OAuthClient()
|
var refreshed = await new OAuthClient()
|
||||||
.RequestToken(new AuthorizationCodeRefreshRequest(spotifyClientStr, spotifySecretStr, user.refresh_token));
|
.RequestToken(new AuthorizationCodeRefreshRequest(spotifyClientStr, spotifySecretStr, user.refresh_token));
|
||||||
@ -41,9 +45,23 @@ public class SpotifyNetworkProvider
|
|||||||
CreatedAt = refreshed.CreatedAt
|
CreatedAt = refreshed.CreatedAt
|
||||||
});
|
});
|
||||||
|
|
||||||
authenticator.TokenRefreshed += (sender, resp) =>
|
authenticator.TokenRefreshed += async (sender, resp) =>
|
||||||
{
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_logger.LogInformation("Token refreshed for [{}], writing to database", user.username);
|
||||||
|
await user.Reference.SetAsync(new
|
||||||
|
{
|
||||||
|
access_token = resp.AccessToken,
|
||||||
|
refresh_token = resp.RefreshToken,
|
||||||
|
last_refreshed = resp.CreatedAt,
|
||||||
|
token_expiry = resp.ExpiresIn
|
||||||
|
}, SetOptions.MergeAll);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
_logger.LogError(e, "Failed to write updated Spotify tokens to database for [{}]", user.username);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
var config = SpotifyClientConfig
|
var config = SpotifyClientConfig
|
Loading…
Reference in New Issue
Block a user