From 61dc9826b92cb6391ecc9cd3d039afc464d485da Mon Sep 17 00:00:00 2001
From: matt1432 <matt@nelim.org>
Date: Wed, 18 Dec 2024 19:25:22 -0500
Subject: [PATCH] feat(netd): improve album search

---
 .../apps/Spotify/PauseUnpause/PauseUnpause.cs | 10 ++-
 .../apps/Spotify/PlayAlbum/PlayAlbum.cs       | 82 +++++++++++++++----
 .../apps/Spotify/PlayArtist/PlayArtist.cs     |  8 +-
 .../apps/Spotify/PlayPlaylist/PlayPlaylist.cs | 10 ++-
 .../apps/Spotify/PlaySong/PlaySong.cs         |  8 +-
 .../Spotify/Types/GetArtistAlbumsResponse.cs  | 42 ++++++++++
 .../apps/Spotify/{Types.cs => Types/Main.cs}  |  4 +-
 .../PlaylistResponse.cs}                      |  2 +-
 .../SearchAlbumsResponse.cs}                  |  2 +-
 .../SearchArtistsResponse.cs}                 |  2 +-
 .../SearchTracksResponse.cs}                  |  2 +-
 homeManagerModules/neovim/langs/csharp.nix    |  1 -
 12 files changed, 137 insertions(+), 36 deletions(-)
 create mode 100644 configurations/homie/modules/home-assistant/netdaemon/apps/Spotify/Types/GetArtistAlbumsResponse.cs
 rename configurations/homie/modules/home-assistant/netdaemon/apps/Spotify/{Types.cs => Types/Main.cs} (97%)
 rename configurations/homie/modules/home-assistant/netdaemon/apps/Spotify/{PlayPlaylist/SpotifyplusPlaylistResponse.cs => Types/PlaylistResponse.cs} (95%)
 rename configurations/homie/modules/home-assistant/netdaemon/apps/Spotify/{PlayAlbum/SpotifyplusSearchAlbumsResponse.cs => Types/SearchAlbumsResponse.cs} (96%)
 rename configurations/homie/modules/home-assistant/netdaemon/apps/Spotify/{PlayArtist/SpotifyplusSearchArtistsResponse.cs => Types/SearchArtistsResponse.cs} (95%)
 rename configurations/homie/modules/home-assistant/netdaemon/apps/Spotify/{PlaySong/SpotifyplusSearchTracksResponse.cs => Types/SearchTracksResponse.cs} (96%)

diff --git a/configurations/homie/modules/home-assistant/netdaemon/apps/Spotify/PauseUnpause/PauseUnpause.cs b/configurations/homie/modules/home-assistant/netdaemon/apps/Spotify/PauseUnpause/PauseUnpause.cs
index 400b5c9a..a2d86ffe 100644
--- a/configurations/homie/modules/home-assistant/netdaemon/apps/Spotify/PauseUnpause/PauseUnpause.cs
+++ b/configurations/homie/modules/home-assistant/netdaemon/apps/Spotify/PauseUnpause/PauseUnpause.cs
@@ -6,6 +6,8 @@ using NetDaemon.AppModel;
 using NetDaemon.HassModel;
 using NetDaemon.HassModel.Integration;
 
+using NetDaemonConfig.Apps.Spotify.Types;
+
 
 namespace NetDaemonConfig.Apps.Spotify.PauseUnpause
 {
@@ -25,14 +27,14 @@ namespace NetDaemonConfig.Apps.Spotify.PauseUnpause
                         if (e.pause)
                         {
                             services.Spotifyplus.PlayerMediaPause(
-                                entityId: SpotifyTypes.DefaultEntityId,
-                                deviceId: SpotifyTypes.DefaultDevId);
+                                entityId: Globals.DefaultEntityId,
+                                deviceId: Globals.DefaultDevId);
                         }
                         else
                         {
                             services.Spotifyplus.PlayerMediaResume(
-                                entityId: SpotifyTypes.DefaultEntityId,
-                                deviceId: SpotifyTypes.DefaultDevId);
+                                entityId: Globals.DefaultEntityId,
+                                deviceId: Globals.DefaultDevId);
                         }
                     }
                     catch (Exception error)
diff --git a/configurations/homie/modules/home-assistant/netdaemon/apps/Spotify/PlayAlbum/PlayAlbum.cs b/configurations/homie/modules/home-assistant/netdaemon/apps/Spotify/PlayAlbum/PlayAlbum.cs
index f07860d2..9a24b9a5 100644
--- a/configurations/homie/modules/home-assistant/netdaemon/apps/Spotify/PlayAlbum/PlayAlbum.cs
+++ b/configurations/homie/modules/home-assistant/netdaemon/apps/Spotify/PlayAlbum/PlayAlbum.cs
@@ -1,13 +1,20 @@
 using System;
+using System.Collections.Generic;
+using System.Globalization;
 using System.Reflection;
 using System.Text.Json;
 
+using FuzzySharp;
+using FuzzySharp.Extractor;
+
 using HomeAssistantGenerated;
 
 using NetDaemon.AppModel;
 using NetDaemon.HassModel;
 using NetDaemon.HassModel.Integration;
 
+using NetDaemonConfig.Apps.Spotify.Types;
+
 
 namespace NetDaemonConfig.Apps.Spotify.PlayAlbum
 {
@@ -16,6 +23,8 @@ namespace NetDaemonConfig.Apps.Spotify.PlayAlbum
     [NetDaemonApp]
     public class PlayAlbum
     {
+        private readonly CultureInfo _cultureInfo = new("fr-CA", false);
+
         // Snake-case json options
         private readonly JsonSerializerOptions _jsonOptions = new()
         {
@@ -30,26 +39,69 @@ namespace NetDaemonConfig.Apps.Spotify.PlayAlbum
                 {
                     try
                     {
-                        SpotifyplusSearchAlbumsResponse? result = (
-                            await services.Spotifyplus.SearchAlbumsAsync(
-                                criteria: $"{e?.artist} {e?.album}",
-                                limitTotal: 1,
-                                entityId: SpotifyTypes.DefaultEntityId,
-                                // My Defaults
-                                market: "CA",
-                                includeExternal: "audio"
-                            )
-                        ).Value.Deserialize<SpotifyplusSearchAlbumsResponse>(_jsonOptions);
+                        string uri;
 
-                        string uri = result?.Result?.Items?[0]?.Uri ??
-                            throw new TargetException(
-                                $"The album {e?.album}{(e?.artist is null ? "" : $" by {e?.artist}")} could not be found."
+                        if (e.artist is not null)
+                        {
+                            SpotifyplusSearchArtistsResponse? artistResult = (
+                                await services.Spotifyplus.SearchArtistsAsync(
+                                    criteria: e?.artist ??
+                                        throw new TargetException($"The artist {e?.artist} could not be found."),
+                                    limitTotal: 1,
+                                    entityId: Globals.DefaultEntityId,
+                                    // My Defaults
+                                    market: "CA",
+                                    includeExternal: "audio"
+                                )
+                            ).Value.Deserialize<SpotifyplusSearchArtistsResponse>(_jsonOptions);
+
+                            string artistId = artistResult?.Result?.Items?[0]?.Id ??
+                                throw new TargetException($"The artist {e?.artist} could not be found.");
+
+                            SpotifyPlusGetArtistAlbumsResponse? result = (
+                                await services.Spotifyplus.GetArtistAlbumsAsync(
+                                    artistId: artistId,
+                                    entityId: Globals.DefaultEntityId,
+                                    market: "CA"
+                                )
+                            ).Value.Deserialize<SpotifyPlusGetArtistAlbumsResponse>(_jsonOptions);
+
+                            List<ArtistAlbumItem> albums = result?.Result?.Items ??
+                                throw new TargetException($"No albums found for artist {e.artist}");
+
+                            ExtractedResult<ArtistAlbumItem> match = Process.ExtractOne(
+                                new ArtistAlbumItem { Name = e.album?.ToLower(_cultureInfo) },
+                                albums,
+                                new Func<ArtistAlbumItem, string>((item) =>
+                                    (item.Name ?? "").ToLower(_cultureInfo))
                             );
 
+                            uri = match.Value?.Uri ??
+                                throw new TargetException($"No matches found for album {e.album}");
+                        }
+                        else
+                        {
+                            SpotifyplusSearchAlbumsResponse? result = (
+                                await services.Spotifyplus.SearchAlbumsAsync(
+                                    criteria: $"{e?.album}",
+                                    limitTotal: 1,
+                                    entityId: Globals.DefaultEntityId,
+                                    // My Defaults
+                                    market: "CA",
+                                    includeExternal: "audio"
+                                )
+                            ).Value.Deserialize<SpotifyplusSearchAlbumsResponse>(_jsonOptions);
+
+                            uri = result?.Result?.Items?[0]?.Uri ??
+                                throw new TargetException(
+                                    $"The album {e?.album}{(e?.artist is null ? "" : $" by {e?.artist}")} could not be found."
+                                );
+                        }
+
                         services.Spotifyplus.PlayerMediaPlayContext(
                             contextUri: uri,
-                            entityId: SpotifyTypes.DefaultEntityId,
-                            deviceId: SpotifyTypes.DefaultDevId,
+                            entityId: Globals.DefaultEntityId,
+                            deviceId: Globals.DefaultDevId,
                             // My Defaults
                             positionMs: 0,
                             delay: 0.50
diff --git a/configurations/homie/modules/home-assistant/netdaemon/apps/Spotify/PlayArtist/PlayArtist.cs b/configurations/homie/modules/home-assistant/netdaemon/apps/Spotify/PlayArtist/PlayArtist.cs
index b9a05a16..98ac3725 100644
--- a/configurations/homie/modules/home-assistant/netdaemon/apps/Spotify/PlayArtist/PlayArtist.cs
+++ b/configurations/homie/modules/home-assistant/netdaemon/apps/Spotify/PlayArtist/PlayArtist.cs
@@ -8,6 +8,8 @@ using NetDaemon.AppModel;
 using NetDaemon.HassModel;
 using NetDaemon.HassModel.Integration;
 
+using NetDaemonConfig.Apps.Spotify.Types;
+
 
 namespace NetDaemonConfig.Apps.Spotify.PlayArtist
 {
@@ -34,7 +36,7 @@ namespace NetDaemonConfig.Apps.Spotify.PlayArtist
                             await services.Spotifyplus.SearchArtistsAsync(
                                 criteria: e?.artist ?? throw new TargetException($"The artist {e?.artist} could not be found."),
                                 limitTotal: 1,
-                                entityId: SpotifyTypes.DefaultEntityId,
+                                entityId: Globals.DefaultEntityId,
                                 // My Defaults
                                 market: "CA",
                                 includeExternal: "audio"
@@ -46,8 +48,8 @@ namespace NetDaemonConfig.Apps.Spotify.PlayArtist
 
                         services.Spotifyplus.PlayerMediaPlayContext(
                             contextUri: uri,
-                            entityId: SpotifyTypes.DefaultEntityId,
-                            deviceId: SpotifyTypes.DefaultDevId,
+                            entityId: Globals.DefaultEntityId,
+                            deviceId: Globals.DefaultDevId,
                             // My Defaults
                             positionMs: 0,
                             delay: 0.50
diff --git a/configurations/homie/modules/home-assistant/netdaemon/apps/Spotify/PlayPlaylist/PlayPlaylist.cs b/configurations/homie/modules/home-assistant/netdaemon/apps/Spotify/PlayPlaylist/PlayPlaylist.cs
index f7f4d84d..568d126a 100644
--- a/configurations/homie/modules/home-assistant/netdaemon/apps/Spotify/PlayPlaylist/PlayPlaylist.cs
+++ b/configurations/homie/modules/home-assistant/netdaemon/apps/Spotify/PlayPlaylist/PlayPlaylist.cs
@@ -13,6 +13,8 @@ using NetDaemon.AppModel;
 using NetDaemon.HassModel;
 using NetDaemon.HassModel.Integration;
 
+using NetDaemonConfig.Apps.Spotify.Types;
+
 
 namespace NetDaemonConfig.Apps.Spotify.PlayPlaylist
 {
@@ -43,7 +45,7 @@ namespace NetDaemonConfig.Apps.Spotify.PlayPlaylist
                             await services.Spotifyplus.GetPlaylistFavoritesAsync(
                                 limitTotal: 200,
                                 sortResult: true,
-                                entityId: SpotifyTypes.DefaultEntityId
+                                entityId: Globals.DefaultEntityId
                             )
                         ).Value.Deserialize<SpotifyplusPlaylistResponse>(_jsonOptions);
 
@@ -65,7 +67,7 @@ namespace NetDaemonConfig.Apps.Spotify.PlayPlaylist
                                 await services.Spotifyplus.SearchPlaylistsAsync(
                                     criteria: query,
                                     limitTotal: 1,
-                                    entityId: SpotifyTypes.DefaultEntityId,
+                                    entityId: Globals.DefaultEntityId,
                                     // My Defaults
                                     market: "CA",
                                     includeExternal: "audio"
@@ -80,8 +82,8 @@ namespace NetDaemonConfig.Apps.Spotify.PlayPlaylist
 
                         services.Spotifyplus.PlayerMediaPlayContext(
                             contextUri: uri,
-                            entityId: SpotifyTypes.DefaultEntityId,
-                            deviceId: SpotifyTypes.DefaultDevId,
+                            entityId: Globals.DefaultEntityId,
+                            deviceId: Globals.DefaultDevId,
                             // My Defaults
                             positionMs: 0,
                             delay: 0.50
diff --git a/configurations/homie/modules/home-assistant/netdaemon/apps/Spotify/PlaySong/PlaySong.cs b/configurations/homie/modules/home-assistant/netdaemon/apps/Spotify/PlaySong/PlaySong.cs
index b9092b87..f4812039 100644
--- a/configurations/homie/modules/home-assistant/netdaemon/apps/Spotify/PlaySong/PlaySong.cs
+++ b/configurations/homie/modules/home-assistant/netdaemon/apps/Spotify/PlaySong/PlaySong.cs
@@ -8,6 +8,8 @@ using NetDaemon.AppModel;
 using NetDaemon.HassModel;
 using NetDaemon.HassModel.Integration;
 
+using NetDaemonConfig.Apps.Spotify.Types;
+
 
 namespace NetDaemonConfig.Apps.Spotify.PlaySong
 {
@@ -34,7 +36,7 @@ namespace NetDaemonConfig.Apps.Spotify.PlaySong
                             await services.Spotifyplus.SearchTracksAsync(
                                 criteria: $"{e?.artist} {e?.song}",
                                 limitTotal: 1,
-                                entityId: SpotifyTypes.DefaultEntityId,
+                                entityId: Globals.DefaultEntityId,
                                 // My Defaults
                                 market: "CA",
                                 includeExternal: "audio"
@@ -47,8 +49,8 @@ namespace NetDaemonConfig.Apps.Spotify.PlaySong
 
                         services.Spotifyplus.PlayerMediaPlayTracks(
                             uris: uri,
-                            entityId: SpotifyTypes.DefaultEntityId,
-                            deviceId: SpotifyTypes.DefaultDevId,
+                            entityId: Globals.DefaultEntityId,
+                            deviceId: Globals.DefaultDevId,
                             // My Defaults
                             positionMs: 0,
                             delay: 0.50
diff --git a/configurations/homie/modules/home-assistant/netdaemon/apps/Spotify/Types/GetArtistAlbumsResponse.cs b/configurations/homie/modules/home-assistant/netdaemon/apps/Spotify/Types/GetArtistAlbumsResponse.cs
new file mode 100644
index 00000000..3213584e
--- /dev/null
+++ b/configurations/homie/modules/home-assistant/netdaemon/apps/Spotify/Types/GetArtistAlbumsResponse.cs
@@ -0,0 +1,42 @@
+using System.Collections.Generic;
+
+
+namespace NetDaemonConfig.Apps.Spotify.Types
+{
+    public record ArtistAlbumItem
+    {
+        public string? AlbumType { get; set; }
+        public List<Artist>? Artists { get; set; }
+        public List<object>? AvailableMarkets { get; set; }
+        public ExternalUrls? ExternalUrls { get; set; }
+        public string? Href { get; set; }
+        public string? Id { get; set; }
+        public string? ImageUrl { get; set; }
+        public List<Image>? Images { get; set; }
+        public string? Name { get; set; }
+        public string? ReleaseDate { get; set; }
+        public string? ReleaseDatePrecision { get; set; }
+        public Restrictions? Restrictions { get; set; }
+        public int? TotalTracks { get; set; }
+        public string? Type { get; set; }
+        public string? Uri { get; set; }
+    }
+
+    public record ArtistAlbumResult
+    {
+        public double? DateLastRefreshed { get; set; }
+        public string? Href { get; set; }
+        public int? Limit { get; set; }
+        public object? Next { get; set; }
+        public int? Offset { get; set; }
+        public object? Previous { get; set; }
+        public int? Total { get; set; }
+        public List<ArtistAlbumItem>? Items { get; set; }
+    }
+
+    public record SpotifyPlusGetArtistAlbumsResponse
+    {
+        public UserProfile? UserProfile { get; set; }
+        public ArtistAlbumResult? Result { get; set; }
+    }
+}
diff --git a/configurations/homie/modules/home-assistant/netdaemon/apps/Spotify/Types.cs b/configurations/homie/modules/home-assistant/netdaemon/apps/Spotify/Types/Main.cs
similarity index 97%
rename from configurations/homie/modules/home-assistant/netdaemon/apps/Spotify/Types.cs
rename to configurations/homie/modules/home-assistant/netdaemon/apps/Spotify/Types/Main.cs
index 9d214020..b67ee637 100644
--- a/configurations/homie/modules/home-assistant/netdaemon/apps/Spotify/Types.cs
+++ b/configurations/homie/modules/home-assistant/netdaemon/apps/Spotify/Types/Main.cs
@@ -1,8 +1,8 @@
 using System.Collections.Generic;
 
-namespace NetDaemonConfig.Apps.Spotify
+namespace NetDaemonConfig.Apps.Spotify.Types
 {
-    public static class SpotifyTypes
+    public static class Globals
     {
         public const string DefaultDevId = "homie";
         public const string DefaultEntityId = "media_player.spotifyplus";
diff --git a/configurations/homie/modules/home-assistant/netdaemon/apps/Spotify/PlayPlaylist/SpotifyplusPlaylistResponse.cs b/configurations/homie/modules/home-assistant/netdaemon/apps/Spotify/Types/PlaylistResponse.cs
similarity index 95%
rename from configurations/homie/modules/home-assistant/netdaemon/apps/Spotify/PlayPlaylist/SpotifyplusPlaylistResponse.cs
rename to configurations/homie/modules/home-assistant/netdaemon/apps/Spotify/Types/PlaylistResponse.cs
index d5d9ab5c..c3608cde 100644
--- a/configurations/homie/modules/home-assistant/netdaemon/apps/Spotify/PlayPlaylist/SpotifyplusPlaylistResponse.cs
+++ b/configurations/homie/modules/home-assistant/netdaemon/apps/Spotify/Types/PlaylistResponse.cs
@@ -1,7 +1,7 @@
 using System.Collections.Generic;
 
 
-namespace NetDaemonConfig.Apps.Spotify.PlayPlaylist
+namespace NetDaemonConfig.Apps.Spotify.Types
 {
     public record PlaylistsItem
     {
diff --git a/configurations/homie/modules/home-assistant/netdaemon/apps/Spotify/PlayAlbum/SpotifyplusSearchAlbumsResponse.cs b/configurations/homie/modules/home-assistant/netdaemon/apps/Spotify/Types/SearchAlbumsResponse.cs
similarity index 96%
rename from configurations/homie/modules/home-assistant/netdaemon/apps/Spotify/PlayAlbum/SpotifyplusSearchAlbumsResponse.cs
rename to configurations/homie/modules/home-assistant/netdaemon/apps/Spotify/Types/SearchAlbumsResponse.cs
index d35680af..01375e4d 100644
--- a/configurations/homie/modules/home-assistant/netdaemon/apps/Spotify/PlayAlbum/SpotifyplusSearchAlbumsResponse.cs
+++ b/configurations/homie/modules/home-assistant/netdaemon/apps/Spotify/Types/SearchAlbumsResponse.cs
@@ -1,7 +1,7 @@
 using System.Collections.Generic;
 
 
-namespace NetDaemonConfig.Apps.Spotify.PlayAlbum
+namespace NetDaemonConfig.Apps.Spotify.Types
 {
     public record AlbumItem
     {
diff --git a/configurations/homie/modules/home-assistant/netdaemon/apps/Spotify/PlayArtist/SpotifyplusSearchArtistsResponse.cs b/configurations/homie/modules/home-assistant/netdaemon/apps/Spotify/Types/SearchArtistsResponse.cs
similarity index 95%
rename from configurations/homie/modules/home-assistant/netdaemon/apps/Spotify/PlayArtist/SpotifyplusSearchArtistsResponse.cs
rename to configurations/homie/modules/home-assistant/netdaemon/apps/Spotify/Types/SearchArtistsResponse.cs
index 452dc5d3..ca2cd87b 100644
--- a/configurations/homie/modules/home-assistant/netdaemon/apps/Spotify/PlayArtist/SpotifyplusSearchArtistsResponse.cs
+++ b/configurations/homie/modules/home-assistant/netdaemon/apps/Spotify/Types/SearchArtistsResponse.cs
@@ -1,7 +1,7 @@
 using System.Collections.Generic;
 
 
-namespace NetDaemonConfig.Apps.Spotify.PlayArtist
+namespace NetDaemonConfig.Apps.Spotify.Types
 {
     public record ArtistItem
     {
diff --git a/configurations/homie/modules/home-assistant/netdaemon/apps/Spotify/PlaySong/SpotifyplusSearchTracksResponse.cs b/configurations/homie/modules/home-assistant/netdaemon/apps/Spotify/Types/SearchTracksResponse.cs
similarity index 96%
rename from configurations/homie/modules/home-assistant/netdaemon/apps/Spotify/PlaySong/SpotifyplusSearchTracksResponse.cs
rename to configurations/homie/modules/home-assistant/netdaemon/apps/Spotify/Types/SearchTracksResponse.cs
index 049d740d..9e8074be 100644
--- a/configurations/homie/modules/home-assistant/netdaemon/apps/Spotify/PlaySong/SpotifyplusSearchTracksResponse.cs
+++ b/configurations/homie/modules/home-assistant/netdaemon/apps/Spotify/Types/SearchTracksResponse.cs
@@ -1,7 +1,7 @@
 using System.Collections.Generic;
 
 
-namespace NetDaemonConfig.Apps.Spotify.PlaySong
+namespace NetDaemonConfig.Apps.Spotify.Types
 {
     public record SongItem
     {
diff --git a/homeManagerModules/neovim/langs/csharp.nix b/homeManagerModules/neovim/langs/csharp.nix
index f97a562c..a83256dc 100644
--- a/homeManagerModules/neovim/langs/csharp.nix
+++ b/homeManagerModules/neovim/langs/csharp.nix
@@ -35,7 +35,6 @@ in {
                 end,
             });
 
-            -- FIXME: make editorconfig and format work
             require('roslyn').setup({
                 config = {
                     capabilities = require('cmp_nvim_lsp').default_capabilities(),