From a3628182c96a32b36e0f710cca17d36295617858 Mon Sep 17 00:00:00 2001 From: andy Date: Wed, 10 Nov 2021 09:15:39 +0000 Subject: [PATCH] creating player watcher on register, adding live gif --- README.md | 4 ++- Selector.Cache/Key.cs | 4 +++ Selector.Model/ApplicationDbContext.cs | 31 ++++++++++++++++-- .../Identity/Pages/Account/Register.cshtml.cs | 5 +++ Selector.Web/scripts/Now/NowPlayingCard.ts | 10 ++++-- Selector.Web/wwwroot/live.gif | Bin 0 -> 5674 bytes 6 files changed, 48 insertions(+), 6 deletions(-) create mode 100644 Selector.Web/wwwroot/live.gif diff --git a/README.md b/README.md index 025f0eb..7a589c5 100644 --- a/README.md +++ b/README.md @@ -2,4 +2,6 @@ ![ci](https://github.com/sarsoo/Selector/actions/workflows/ci.yml/badge.svg) -Investigating a Spotify listening agent \ No newline at end of file +Selector is a suite for monitoring and reacting to live changes on a Spotify account. The player watcher keeps an eye on what you're listening to and fires off events when things change. The idea is that various pieces of information will be collated and presented in a now-playing-style dashboard. + +Last.fm play counts will be collected, as will the Spotify audio features. \ No newline at end of file diff --git a/Selector.Cache/Key.cs b/Selector.Cache/Key.cs index 5c46f4d..ef83340 100644 --- a/Selector.Cache/Key.cs +++ b/Selector.Cache/Key.cs @@ -16,6 +16,8 @@ namespace Selector.Cache public const string PlayCountName = "PlayCount"; public const string WorkerName = "Worker"; + public const string WatcherName = "Watcher"; + public const string ReservedName = "Reserved"; /// /// Current playback for a user @@ -29,6 +31,8 @@ namespace Selector.Cache public static string AlbumPlayCount(string name, string artist) => Namespace(AlbumName, artist, name, PlayCountName); public static string ArtistPlayCount(string name) => Namespace(ArtistName, name, PlayCountName); + public static string WatcherReserved(int id) => Namespace(WatcherName, id.ToString(), ReservedName); + public static string Namespace(params string[] args) => string.Join(":", args); } } diff --git a/Selector.Model/ApplicationDbContext.cs b/Selector.Model/ApplicationDbContext.cs index b0211c0..69f565d 100644 --- a/Selector.Model/ApplicationDbContext.cs +++ b/Selector.Model/ApplicationDbContext.cs @@ -1,22 +1,31 @@ using System; using System.IO; +using System.Linq; +using System.Threading.Tasks; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Design; using Microsoft.Extensions.Configuration; using Microsoft.AspNetCore.Identity.EntityFrameworkCore; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; namespace Selector.Model { public class ApplicationDbContext : IdentityDbContext { + private readonly ILogger Logger; + public DbSet Watcher { get; set; } - public ApplicationDbContext(DbContextOptions options) : base(options) + public ApplicationDbContext( + DbContextOptions options, + ILogger logger + ) : base(options) { - + Logger = logger; } protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) @@ -35,6 +44,22 @@ namespace Selector.Model SeedData.Seed(modelBuilder); } + + public void CreatePlayerWatcher(string userId) + { + if(Watcher.Any(w => w.UserId == userId && w.Type == WatcherType.Player)) + { + Logger.LogWarning($"Trying to create more than one player watcher for user [{userId}]"); + return; + } + + Watcher.Add(new Watcher { + UserId = userId, + Type = WatcherType.Player + }); + + SaveChanges(); + } } public class DesignTimeDbContextFactory : IDesignTimeDbContextFactory @@ -49,7 +74,7 @@ namespace Selector.Model var builder = new DbContextOptionsBuilder(); builder.UseNpgsql(configuration.GetConnectionString("Default")); - return new ApplicationDbContext(builder.Options); + return new ApplicationDbContext(builder.Options, NullLogger.Instance); } } } \ No newline at end of file diff --git a/Selector.Web/Areas/Identity/Pages/Account/Register.cshtml.cs b/Selector.Web/Areas/Identity/Pages/Account/Register.cshtml.cs index 142331f..45d8a81 100644 --- a/Selector.Web/Areas/Identity/Pages/Account/Register.cshtml.cs +++ b/Selector.Web/Areas/Identity/Pages/Account/Register.cshtml.cs @@ -23,17 +23,20 @@ namespace Selector.Web.Areas.Identity.Pages.Account { private readonly SignInManager _signInManager; private readonly UserManager _userManager; + private readonly ApplicationDbContext DbContext; private readonly ILogger _logger; private readonly IEmailSender _emailSender; public RegisterModel( UserManager userManager, SignInManager signInManager, + ApplicationDbContext dbContext, ILogger logger, IEmailSender emailSender) { _userManager = userManager; _signInManager = signInManager; + DbContext = dbContext; _logger = logger; _emailSender = emailSender; } @@ -84,6 +87,8 @@ namespace Selector.Web.Areas.Identity.Pages.Account var result = await _userManager.CreateAsync(user, Input.Password); if (result.Succeeded) { + DbContext.CreatePlayerWatcher(user.Id); + _logger.LogInformation("User created a new account with password."); var code = await _userManager.GenerateEmailConfirmationTokenAsync(user); diff --git a/Selector.Web/scripts/Now/NowPlayingCard.ts b/Selector.Web/scripts/Now/NowPlayingCard.ts index ce38228..55f9735 100644 --- a/Selector.Web/scripts/Now/NowPlayingCard.ts +++ b/Selector.Web/scripts/Now/NowPlayingCard.ts @@ -24,7 +24,10 @@ let component: Vue.Component = { {{ artist.name }} - +
+ + +
@@ -36,7 +39,10 @@ let component: Vue.Component = {
{{ episode.show.publisher }}
- +
+ + +
diff --git a/Selector.Web/wwwroot/live.gif b/Selector.Web/wwwroot/live.gif new file mode 100644 index 0000000000000000000000000000000000000000..e1d82b7f1e19f6cdd92dd387c43fce7a5b564ec1 GIT binary patch literal 5674 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