From e6500583c94cf36ed6dfbca2669e56921a85b8fa Mon Sep 17 00:00:00 2001 From: Nick Whitlock Date: Thu, 16 Apr 2020 19:21:25 -0400 Subject: [PATCH 1/8] Fixed and working. --- .gitignore | 2 + Properties/Resources.Designer.cs | 2 +- Properties/Settings.Designer.cs | 2 +- Spoti15.cs | 201 +++++++++++++++++++++++++------ Spoti15.csproj | 24 ++-- app.config | 14 ++- packages.config | 8 ++ 7 files changed, 202 insertions(+), 51 deletions(-) create mode 100644 packages.config diff --git a/.gitignore b/.gitignore index f8c97df..80e7d53 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,5 @@ bin/ obj/ publish/ *.csproj.user +.vs/* +packages/* \ No newline at end of file diff --git a/Properties/Resources.Designer.cs b/Properties/Resources.Designer.cs index 64c695b..2bb610a 100644 --- a/Properties/Resources.Designer.cs +++ b/Properties/Resources.Designer.cs @@ -19,7 +19,7 @@ namespace Spoti15.Properties { // class via a tool like ResGen or Visual Studio. // To add or remove a member, edit your .ResX file then rerun ResGen // with the /str option, or rebuild your VS project. - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] internal class Resources { diff --git a/Properties/Settings.Designer.cs b/Properties/Settings.Designer.cs index e6e0afe..53fd719 100644 --- a/Properties/Settings.Designer.cs +++ b/Properties/Settings.Designer.cs @@ -12,7 +12,7 @@ namespace Spoti15.Properties { [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "14.0.0.0")] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "15.9.0.0")] internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); diff --git a/Spoti15.cs b/Spoti15.cs index f4f3243..50ff4c5 100644 --- a/Spoti15.cs +++ b/Spoti15.cs @@ -1,18 +1,16 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; using System.Windows.Forms; using System.Drawing; -using SpotifyAPI.Local; -using SpotifyAPI.Local.Enums; -using SpotifyAPI.Local.Models; +using System.Threading.Tasks; +using SpotifyAPI.Web; +using SpotifyAPI.Web.Auth; +using SpotifyAPI.Web.Enums; +using SpotifyAPI.Web.Models; namespace Spoti15 { class Spoti15 { - private SpotifyLocalAPI api; private Exception initExcpt; private LogiLcd lcd; @@ -20,11 +18,20 @@ class Spoti15 private Timer spotTimer; private Timer lcdTimer; private Timer refreshTimer; + private Timer descFlipTimer; private uint scrollStep = 0; + private bool descFlip = false; private bool showAlbum = true; private bool showAnimatedLines = true; + private AuthorizationCodeAuth auth; + private SpotifyWebAPI api; + + private string _clientId = ""; //""; + private string _secretId = ""; //""; + private string refreshToken = ""; + private bool authorized = true; public Spoti15() { @@ -32,7 +39,7 @@ public Spoti15() InitSpot(); - lcd = new LogiLcd("Spoti15"); + lcd = new LogiLcd("Spotify"); spotTimer = new Timer(); spotTimer.Interval = 1000; @@ -49,10 +56,20 @@ public Spoti15() refreshTimer.Enabled = true; refreshTimer.Tick += OnRefreshTimer; + descFlipTimer = new Timer(); + descFlipTimer.Interval = 2000; + descFlipTimer.Enabled = true; + descFlipTimer.Tick += OnDescFlip; + UpdateSpot(); UpdateLcd(); } + private void OnDescFlip(object source, EventArgs e) + { + descFlip = !descFlip; + } + private void OnSpotTimer(object source, EventArgs e) { UpdateSpot(); @@ -63,15 +80,17 @@ private void OnSpotTimer(object source, EventArgs e) private bool btn3Before = false; private void OnLcdTimer(object source, EventArgs e) { + /* bool btn0Now = lcd.IsButtonPressed(LogiLcd.LcdButton.Mono0); if (btn0Now && !btn0Before) InitSpot(); btn0Before = btn0Now; - + */ UpdateLcd(); scrollStep += 1; // toggle between "ARTIST - ALBUM" and "ALBUM" on line 1 + /* bool btn3Now = lcd.IsButtonPressed(LogiLcd.LcdButton.Mono3); if (btn3Now && !btn3Before) showAlbum = !showAlbum; @@ -82,11 +101,12 @@ private void OnLcdTimer(object source, EventArgs e) if (btn2Now && !btn2Before) showAnimatedLines = !showAnimatedLines; btn2Before = btn2Now; + */ } private void OnRefreshTimer(object source, EventArgs e) { - InitSpot(); + //InitSpot(); } public void Dispose() @@ -106,17 +126,87 @@ public void Dispose() refreshTimer = null; initExcpt = null; + auth.Stop(0); + } + + private void UpdateAccessToken(Token token) + { + api = new SpotifyWebAPI + { + AccessToken = token.AccessToken, + TokenType = token.TokenType + }; + authorized = true; + refreshToken = token.RefreshToken; + Environment.SetEnvironmentVariable("SPOTIFY_REFRESH_TOKEN", refreshToken); + } + + private async void OnAuthReceived(object sender, AuthorizationCode payload) + { + try + { + auth.Stop(); + + var token = await auth.ExchangeCode(payload.Code); + UpdateAccessToken(token); + } + catch (Exception e) + { + initExcpt = e; + } + + + } + + private void Authorize() + { + var url = "http://localhost:4002"; + + System.Diagnostics.Debug.Write("Re-authorize\r\n"); + auth?.Stop(); + auth = new AuthorizationCodeAuth(_clientId, _secretId, url, url, Scope.UserReadCurrentlyPlaying | Scope.UserReadPlaybackState); + + if (string.IsNullOrEmpty(refreshToken)) + { + auth.Start(); + auth.AuthReceived += OnAuthReceived; + auth.OpenBrowser(); + } + else + { + RefreshAccessToken(); + } + } + + private async Task RefreshAccessToken() + { + try + { + var token = await auth.RefreshToken(refreshToken); + if(token.HasError()) + { + refreshToken = null; + System.Diagnostics.Debug.Write("Bad token\r\n"); + Authorize(); + return; + } + } + catch(Exception e) + { + initExcpt = e; + } } private void InitSpot() { try { - if (api == null) - api = new SpotifyLocalAPI(); - if (!api.Connect()) - throw new Exception ("Is Spotify Even Running?"); - initExcpt = null; + _clientId = string.IsNullOrEmpty(_clientId) ? Environment.GetEnvironmentVariable("SPOTIFY_CLIENT_ID") : _clientId; + _secretId = string.IsNullOrEmpty(_secretId) ? Environment.GetEnvironmentVariable("SPOTIFY_SECRET_ID") : _secretId; + refreshToken = string.IsNullOrEmpty(refreshToken) ? Environment.GetEnvironmentVariable("SPOTIFY_REFRESH_TOKEN") : refreshToken; + + System.Diagnostics.Debug.Write("InitSpot()\r\n"); + Authorize(); } catch (Exception e) { @@ -132,7 +222,7 @@ public void UpdateSpot() } private Bitmap bgBitmap = new Bitmap(LogiLcd.MonoWidth, LogiLcd.MonoHeight); - private Font mainFont = new Font(Program.GetFontFamily("11pxbus"), 11, GraphicsUnit.Pixel); + private Font mainFont = new Font(Program.GetFontFamily("5pxbus"), 9, GraphicsUnit.Pixel); private Color bgColor = Color.Black; private Color fgColor = Color.White; private Brush bgBrush = Brushes.Black; @@ -232,41 +322,76 @@ public void UpdateLcd() try { - StatusResponse status = api.GetStatus(); - int len = status.Track.Length; - int pos = (int)status.PlayingPosition; - double perc = status.PlayingPosition / status.Track.Length; - - String lineZero = status.Track.ArtistResource.Name; - if (showAlbum) - lineZero += " - " + status.Track.AlbumResource.Name; + var playback = api.GetPlayback(); + int len = playback.Item.DurationMs; + int pos = playback.ProgressMs; + double perc = pos / (double)len; + + String lineZero = ""; + var artists = playback.Item.Artists.ToArray(); + for(int i = 0; i < artists.Length; i++) + { + lineZero = string.Concat(lineZero, artists[i].Name); + if(i != artists.Length - 1) + { + lineZero = string.Concat(lineZero, ", "); + } + } DrawTextScroll(g, 0, lineZero); - DrawTextScroll(g, 1, status.Track.TrackResource.Name); - DrawTextScroll(g, 3, String.Format("{0}:{1:D2} / {2}:{3:D2}", pos / 60, pos % 60, len / 60, len % 60)); + DrawTextScroll(g, 1, playback.Item.Name); + DrawText(g, 2, String.Format("{0}:{1:D2} / {2}:{3:D2}", pos / 60000, (pos % 60000) / 1000, len / 60000, (len % 60000) / 1000), + LogiLcd.MonoWidth - 50); + //DrawTextScroll(g, 2, String.Format("{0}:{1:D2} / {2}:{3:D2}", pos / 60000, (pos % 60000) / 1000, len / 60000, (len % 60000) / 1000)); - // draw progress bar - g.DrawRectangle(Pens.White, 3, 24, LogiLcd.MonoWidth - 6, 4); - g.FillRectangle(Brushes.White, 3, 24, (int)((LogiLcd.MonoWidth - 6) * perc), 4); + // Draw progress bar + g.DrawRectangle(Pens.White, 3, 23, (LogiLcd.MonoWidth - 75), 4); + g.FillRectangle(Brushes.White, 3, 23, (int)((LogiLcd.MonoWidth - 75) * perc), 4); - // draw stylistic pattern lines within progress bar - if (showAnimatedLines) + if (playback.IsPlaying) { + g.FillPolygon(Brushes.White, new Point[] { new Point((LogiLcd.MonoWidth - 69), 30), new Point((LogiLcd.MonoWidth - 69), 20), new Point((LogiLcd.MonoWidth - 64), 25) }); + if (lineTrack > 8) lineTrack = 4; else lineTrack++; - for (int x = lineTrack; x < LogiLcd.MonoWidth - 6; x += 6) - g.DrawLine(Pens.Black, new Point(x, 26), new Point(x + 2, 26)); + for (int x = lineTrack; x < LogiLcd.MonoWidth - 80; x += 6) + g.DrawLine(Pens.Black, new Point(x, 25), new Point(x + 2, 25)); + } + else + { + g.FillRectangle(Brushes.White, new Rectangle((LogiLcd.MonoWidth - 69), 22, 2, 7)); + g.FillRectangle(Brushes.White, new Rectangle((LogiLcd.MonoWidth - 66), 22, 2, 7)); } - - if (status.Playing) + + if (playback.Context.Type == "album") { - g.FillPolygon(Brushes.White, new Point[] { new Point(3, 42), new Point(3, 32), new Point(8, 37) }); + if(descFlip) + { + DrawText(g, 3, "Album"); + } + else + { + DrawText(g, 3, playback.Item.Album.Name); + } + } + else if (playback.Context.Type == "playlist") + { + var split = playback.Context.ExternalUrls["spotify"].Split('/'); + var playlist = api.GetPlaylist(split[4]); + + if(descFlip) + { + DrawText(g, 3, "Playlist"); + } + else + { + DrawText(g, 3, playlist.Name); + } } else { - g.FillRectangle(Brushes.White, new Rectangle(3, 34, 2, 7)); - g.FillRectangle(Brushes.White, new Rectangle(6, 34, 2, 7)); + DrawText(g, 3, "Unknown"); } } catch (NullReferenceException) diff --git a/Spoti15.csproj b/Spoti15.csproj index f9b8011..a87396a 100644 --- a/Spoti15.csproj +++ b/Spoti15.csproj @@ -1,5 +1,5 @@  - + Debug @@ -9,7 +9,7 @@ Properties Spoti15 Spoti15 - v4.5 + v4.6.1 512 @@ -73,13 +73,14 @@ false - - False - SpotifyLocalAPI\Newtonsoft.Json.dll + + packages\Newtonsoft.Json.12.0.3\lib\net45\Newtonsoft.Json.dll - - False - SpotifyLocalAPI\SpotifyAPI.dll + + packages\SpotifyAPI.Web.5.1.0\lib\netstandard2.0\SpotifyAPI.Web.dll + + + packages\SpotifyAPI.Web.Auth.5.1.0\lib\netstandard2.0\SpotifyAPI.Web.Auth.dll @@ -91,6 +92,12 @@ + + packages\EmbedIO.2.9.2\lib\netstandard2.0\Unosquare.Labs.EmbedIO.dll + + + packages\Unosquare.Swan.Lite.1.3.1\lib\net461\Unosquare.Swan.Lite.dll + @@ -129,6 +136,7 @@ + SettingsSingleFileGenerator Settings.Designer.cs diff --git a/app.config b/app.config index 88a4e17..b21ff98 100644 --- a/app.config +++ b/app.config @@ -1,15 +1,23 @@ - + -
+
- + False + + + + + + + + diff --git a/packages.config b/packages.config new file mode 100644 index 0000000..5f5ce7e --- /dev/null +++ b/packages.config @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file From e2eea788114c24131b2e50d827c28915a7de33b0 Mon Sep 17 00:00:00 2001 From: Nick Whitlock Date: Fri, 17 Apr 2020 10:43:24 -0400 Subject: [PATCH 2/8] Fixed a bunch of errors. Pressing button 0 likes/unlikes the song. Pressing button 1 shows the next song on the playlist. --- Program.cs | 20 ++- Spoti15.cs | 369 +++++++++++++++++++++++++++++++++++++++++-------- Spoti15.csproj | 73 +++++++--- 3 files changed, 380 insertions(+), 82 deletions(-) diff --git a/Program.cs b/Program.cs index 81e6875..a0146b8 100644 --- a/Program.cs +++ b/Program.cs @@ -7,6 +7,7 @@ using System.ServiceModel; using System.Windows.Forms; using System.Runtime.InteropServices; +using System.IO; namespace Spoti15 { @@ -74,14 +75,18 @@ public static void LoadFonts() if (resKey == null || !resKey.StartsWith("font_")) continue; + var path = Path.Combine(Application.StartupPath, string.Format("lcdfonts\\{0}.ttf", resKey.Substring(5))); + pFonts.AddFontFile(path); + /* byte[] resVal = (byte[])entry.Value; - - IntPtr data = Marshal.AllocCoTaskMem(resVal.Length); - Marshal.Copy(resVal, 0, data, resVal.Length); - - pFonts.AddMemoryFont(data, resVal.Length); - - Marshal.FreeCoTaskMem(data); + unsafe + { + fixed(byte * pFontData = resVal) + { + pFonts.AddMemoryFont((System.IntPtr)pFontData, resVal.Length); + } + } + */ } } @@ -91,6 +96,7 @@ public static void LoadFonts() static void Main(string[] args) { + Application.SetCompatibleTextRenderingDefault(false); using (ChannelFactory spotFactory = new ChannelFactory(new NetNamedPipeBinding(), new EndpointAddress("net.pipe://localhost/Spoti15WCF"))) { try diff --git a/Spoti15.cs b/Spoti15.cs index 50ff4c5..ac8ecdc 100644 --- a/Spoti15.cs +++ b/Spoti15.cs @@ -2,6 +2,7 @@ using System.Windows.Forms; using System.Drawing; using System.Threading.Tasks; +using System.Collections.Generic; using SpotifyAPI.Web; using SpotifyAPI.Web.Auth; using SpotifyAPI.Web.Enums; @@ -19,12 +20,16 @@ class Spoti15 private Timer lcdTimer; private Timer refreshTimer; private Timer descFlipTimer; + private Timer inputTimer; + private Timer disableLikedSongNotificationTimer; + private Timer upNextTimer; private uint scrollStep = 0; private bool descFlip = false; private bool showAlbum = true; private bool showAnimatedLines = true; + private bool showUpNext = false; private AuthorizationCodeAuth auth; private SpotifyWebAPI api; @@ -32,6 +37,13 @@ class Spoti15 private string _secretId = ""; //""; private string refreshToken = ""; private bool authorized = true; + private bool cachedLikedTrack = false; + private bool likedSongNotification = false; + private bool unlikedSongNotification = false; + private FullTrack likedOrUnlikedSong; + private PlaylistTrack upNextTrack; + private FullPlaylist cachedPlaylist; + private PlaybackContext cachedPlayback; public Spoti15() { @@ -61,6 +73,11 @@ public Spoti15() descFlipTimer.Enabled = true; descFlipTimer.Tick += OnDescFlip; + inputTimer = new Timer(); + inputTimer.Interval = 1; + inputTimer.Enabled = true; + inputTimer.Tick += CheckInput; + UpdateSpot(); UpdateLcd(); } @@ -76,32 +93,126 @@ private void OnSpotTimer(object source, EventArgs e) } private bool btn0Before = false; + private bool btn1Before = false; private bool btn2Before = false; private bool btn3Before = false; - private void OnLcdTimer(object source, EventArgs e) + + private void CheckInput(object source, EventArgs e) { - /* bool btn0Now = lcd.IsButtonPressed(LogiLcd.LcdButton.Mono0); - if (btn0Now && !btn0Before) - InitSpot(); + if(btn0Now && !btn0Before) + { + var thisItem = cachedPlayback; + if(thisItem == null || thisItem.Item == null) + { + return; + } + + if(likedSongNotification || unlikedSongNotification) + { + likedSongNotification = unlikedSongNotification = false; + btn0Before = btn0Now; + disableLikedSongNotificationTimer.Enabled = false; + return; + } + + var ListedItem = new List(1); + + likedOrUnlikedSong = thisItem.Item; + ListedItem.Add(likedOrUnlikedSong.Id); + if(cachedLikedTrack) + { + api.RemoveSavedTracks(ListedItem); + likedSongNotification = false; + unlikedSongNotification = true; + } + else + { + api.SaveTrack(likedOrUnlikedSong.Id); + likedSongNotification = true; + unlikedSongNotification = false; + } + + disableLikedSongNotificationTimer = new Timer(); + disableLikedSongNotificationTimer.Enabled = true; + disableLikedSongNotificationTimer.Interval = 5000; + disableLikedSongNotificationTimer.Tick += OnLikedSongNotificationFinished; + } + btn0Before = btn0Now; - */ + + bool btn1Now = lcd.IsButtonPressed(LogiLcd.LcdButton.Mono1); + if(btn1Now) + { + var playlist = cachedPlaylist; + if(playlist == null) + { + return; + } + + var playback = cachedPlayback; + if(playback == null) + { + return; + } + + /*if(showUpNext) + { + showUpNext = false; + btn1Before = btn1Now; + upNextTimer.Enabled = false; + return; + }*/ + + upNextTrack = null; + for(int i = 0; i < playlist.Tracks.Items.Capacity; i++) + { + if(playlist.Tracks.Items[i].Track.Uri == playback.Item.Uri) + { + // next track is it + if(i == playlist.Tracks.Items.Capacity - 1) + { + upNextTrack = playlist.Tracks.Items[0]; + } + else + { + upNextTrack = playlist.Tracks.Items[i + 1]; + } + break; + } + } + if(upNextTrack == null) + { + return; + } + + showUpNext = true; + /*upNextTimer = new Timer(); + upNextTimer.Enabled = true; + upNextTimer.Interval = 5000; + upNextTimer.Tick += OnUpNextFinished;*/ + } + + showUpNext = btn1Now; + //btn1Before = btn1Now; + } + + private void OnLikedSongNotificationFinished(object source, EventArgs e) + { + likedSongNotification = unlikedSongNotification = false; + disableLikedSongNotificationTimer.Enabled = false; + } + + private void OnUpNextFinished(object source, EventArgs e) + { + showUpNext = false; + upNextTimer.Enabled = false; + } + + private void OnLcdTimer(object source, EventArgs e) + { UpdateLcd(); scrollStep += 1; - - // toggle between "ARTIST - ALBUM" and "ALBUM" on line 1 - /* - bool btn3Now = lcd.IsButtonPressed(LogiLcd.LcdButton.Mono3); - if (btn3Now && !btn3Before) - showAlbum = !showAlbum; - btn3Before = btn3Now; - - // toggle animated lines within progress bar - bool btn2Now = lcd.IsButtonPressed(LogiLcd.LcdButton.Mono2); - if (btn2Now && !btn2Before) - showAnimatedLines = !showAnimatedLines; - btn2Before = btn2Now; - */ } private void OnRefreshTimer(object source, EventArgs e) @@ -164,7 +275,7 @@ private void Authorize() System.Diagnostics.Debug.Write("Re-authorize\r\n"); auth?.Stop(); - auth = new AuthorizationCodeAuth(_clientId, _secretId, url, url, Scope.UserReadCurrentlyPlaying | Scope.UserReadPlaybackState); + auth = new AuthorizationCodeAuth(_clientId, _secretId, url, url, Scope.UserReadCurrentlyPlaying | Scope.UserReadPlaybackState | Scope.UserLibraryRead | Scope.UserLibraryModify); if (string.IsNullOrEmpty(refreshToken)) { @@ -222,7 +333,9 @@ public void UpdateSpot() } private Bitmap bgBitmap = new Bitmap(LogiLcd.MonoWidth, LogiLcd.MonoHeight); - private Font mainFont = new Font(Program.GetFontFamily("5pxbus"), 9, GraphicsUnit.Pixel); + private Font mainFont = new Font(Program.GetFontFamily("6pxbus"), 6, GraphicsUnit.Pixel); + private Font iconFont = new Font(Program.GetFontFamily("5px2bus"), 5, GraphicsUnit.Pixel); + private Font bigFont = new Font(Program.GetFontFamily("11px3bus"), 11, GraphicsUnit.Pixel); private Color bgColor = Color.Black; private Color fgColor = Color.White; private Brush bgBrush = Brushes.Black; @@ -239,15 +352,23 @@ private void SetupGraphics(Graphics g) g.Clear(bgColor); } - private void DrawText(Graphics g, int line, string text, Font fnt, int offset = 0) + private void DrawText(Graphics g, int line, string text, Font fnt, int offset = 0, int vertOffset = 0) { int x = offset; - int y = line * 10; + int y = (line * 6) + vertOffset; if (line == 0) y -= 1; // offset first line 3 pixels up TextRenderer.DrawText(g, text, fnt, new Point(x, y), fgColor, TextFormatFlags.NoPrefix); } + private void DrawTextWithinBounds(Graphics g, int line, string text, Font fnt, int x, int w) + { + int y = (line * 6); + if (line == 0) + y -= 1; + TextRenderer.DrawText(g, text, fnt, new Rectangle(x, y, w, 6), Color.White, TextFormatFlags.NoPrefix); + } + private void DrawTextScroll(Graphics g, int line, string text, Font fnt, bool center = true) { Size textSize = TextRenderer.MeasureText(text, fnt); @@ -298,6 +419,22 @@ private void DoRender() lcd.Update(); } + private string GetStringFromArtists(SimpleArtist[] artists) + { + string returnValue = ""; + + for (int i = 0; i < artists.Length; i++) + { + returnValue = string.Concat(returnValue, artists[i].Name); + if (i != artists.Length - 1) + { + returnValue = string.Concat(returnValue, ", "); + } + } + + return returnValue; + } + //private Byte[] emptyBg = new Byte[LogiLcd.MonoWidth * LogiLcd.MonoHeight]; private int lineTrack = 4; public void UpdateLcd() @@ -322,82 +459,200 @@ public void UpdateLcd() try { - var playback = api.GetPlayback(); + if(api == null) + { + // TODO: draw spotify logo + g.Clear(bgColor); + DrawTextScroll(g, 2, "SPOTIFY"); + DoRender(); + return; + } + else if(likedSongNotification || unlikedSongNotification) + { + g.Clear(bgColor); + + if (likedSongNotification) + { + DrawTextScroll(g, 1, "LIKED SONG!", bigFont); + } + else if (unlikedSongNotification) + { + DrawTextScroll(g, 1, "UNLIKED SONG!", bigFont); + } + + DrawTextScroll(g, 3, likedOrUnlikedSong.Name); + DrawTextScroll(g, 4, GetStringFromArtists(likedOrUnlikedSong.Artists.ToArray())); + DrawTextScroll(g, 5, likedOrUnlikedSong.Album.Name); + DoRender(); + return; + } + else if(showUpNext) + { + g.Clear(bgColor); + + DrawTextScroll(g, 1, "UP NEXT", bigFont); + DrawTextScroll(g, 3, upNextTrack.Track.Name); + DrawTextScroll(g, 4, GetStringFromArtists(upNextTrack.Track.Artists.ToArray())); + DrawTextScroll(g, 5, upNextTrack.Track.Album.Name); + DoRender(); + return; + } + + try + { + var retrievedPlayback = api.GetPlayback(); + if(retrievedPlayback != null && retrievedPlayback.Item != null) + { + cachedPlayback = retrievedPlayback; + } + } + catch(Exception) + { + + } + + var playback = cachedPlayback; + if(playback == null || playback.Item == null) + { + var track = api.GetPlayingTrack(); + if(track == null) + { + g.Clear(bgColor); + DrawTextScroll(g, 1, "ERROR"); + DrawTextScroll(g, 2, "SPOTIFY PLAYBACK NOT DETECTED"); + DoRender(); + return; + } + else if(track.CurrentlyPlayingType == TrackType.Ad) + { + g.Clear(bgColor); + DrawTextScroll(g, 2, "Advertisement"); + DoRender(); + return; + } + else + { + return; + } + } int len = playback.Item.DurationMs; int pos = playback.ProgressMs; double perc = pos / (double)len; - String lineZero = ""; - var artists = playback.Item.Artists.ToArray(); - for(int i = 0; i < artists.Length; i++) + try { - lineZero = string.Concat(lineZero, artists[i].Name); - if(i != artists.Length - 1) - { - lineZero = string.Concat(lineZero, ", "); - } + var ListedItem = new List(1); + ListedItem.Add(playback.Item.Id); + var response = api.CheckSavedTracks(ListedItem); + cachedLikedTrack = response.List[0]; } - DrawTextScroll(g, 0, lineZero); + catch(Exception) + { + + } + + DrawText(g, 6, cachedLikedTrack ? "e" : "f", iconFont, 0, 1); + + DrawTextScroll(g, 2, GetStringFromArtists(playback.Item.Artists.ToArray())); DrawTextScroll(g, 1, playback.Item.Name); - DrawText(g, 2, String.Format("{0}:{1:D2} / {2}:{3:D2}", pos / 60000, (pos % 60000) / 1000, len / 60000, (len % 60000) / 1000), - LogiLcd.MonoWidth - 50); - //DrawTextScroll(g, 2, String.Format("{0}:{1:D2} / {2}:{3:D2}", pos / 60000, (pos % 60000) / 1000, len / 60000, (len % 60000) / 1000)); + DrawTextScroll(g, 5, String.Format("{0}:{1:D2} / {2}:{3:D2}", pos / 60000, (pos % 60000) / 1000, len / 60000, (len % 60000) / 1000)); + DrawTextScroll(g, 3, playback.Item.Album.Name); // Draw progress bar - g.DrawRectangle(Pens.White, 3, 23, (LogiLcd.MonoWidth - 75), 4); - g.FillRectangle(Brushes.White, 3, 23, (int)((LogiLcd.MonoWidth - 75) * perc), 4); + g.DrawRectangle(Pens.White, 8, LogiLcd.MonoHeight - 6, (LogiLcd.MonoWidth - 24), 4); + g.FillRectangle(Brushes.White, 8, LogiLcd.MonoHeight - 6, (int)((LogiLcd.MonoWidth - 24) * perc), 4); if (playback.IsPlaying) { - g.FillPolygon(Brushes.White, new Point[] { new Point((LogiLcd.MonoWidth - 69), 30), new Point((LogiLcd.MonoWidth - 69), 20), new Point((LogiLcd.MonoWidth - 64), 25) }); - - if (lineTrack > 8) - lineTrack = 4; + g.FillPolygon(Brushes.White, new Point[] { + new Point((LogiLcd.MonoWidth - 14), LogiLcd.MonoHeight - 7), + new Point((LogiLcd.MonoWidth - 14), LogiLcd.MonoHeight - 1), + new Point((LogiLcd.MonoWidth - 9), LogiLcd.MonoHeight - 4) + }); + + if (lineTrack > 12) + lineTrack = 6; else lineTrack++; - for (int x = lineTrack; x < LogiLcd.MonoWidth - 80; x += 6) - g.DrawLine(Pens.Black, new Point(x, 25), new Point(x + 2, 25)); + for (int x = lineTrack; x < LogiLcd.MonoWidth - 22; x += 8) + g.DrawLine(Pens.Black, new Point(x, LogiLcd.MonoHeight - 4), new Point(x + 2, LogiLcd.MonoHeight - 4)); } else { - g.FillRectangle(Brushes.White, new Rectangle((LogiLcd.MonoWidth - 69), 22, 2, 7)); - g.FillRectangle(Brushes.White, new Rectangle((LogiLcd.MonoWidth - 66), 22, 2, 7)); + g.FillRectangle(Brushes.White, new Rectangle((LogiLcd.MonoWidth - 10), LogiLcd.MonoHeight - 6, 2, 5)); + g.FillRectangle(Brushes.White, new Rectangle((LogiLcd.MonoWidth - 13), LogiLcd.MonoHeight - 6, 2, 5)); } + if(playback.ShuffleState) + { + DrawText(g, 6, "S", mainFont, LogiLcd.MonoWidth - 7, -1); + } + else if(playback.RepeatState == RepeatState.Context) + { + DrawText(g, 6, "h", iconFont, LogiLcd.MonoWidth - 7); + } + else if(playback.RepeatState == RepeatState.Track) + { + DrawText(g, 6, "g", iconFont, LogiLcd.MonoWidth - 7); + } + if (playback.Context.Type == "album") { if(descFlip) { - DrawText(g, 3, "Album"); + DrawText(g, 0, "Playing Album"); } else { - DrawText(g, 3, playback.Item.Album.Name); + DrawTextWithinBounds(g, 0, playback.Item.Album.Name, mainFont, 0, 120); } } else if (playback.Context.Type == "playlist") { - var split = playback.Context.ExternalUrls["spotify"].Split('/'); - var playlist = api.GetPlaylist(split[4]); - - if(descFlip) + try { - DrawText(g, 3, "Playlist"); + var split = playback.Context.ExternalUrls["spotify"].Split('/'); + var newPlaylist = api.GetPlaylist(split[4]); + if(newPlaylist != null) + { + cachedPlaylist = api.GetPlaylist(split[4]); + } + } - else + catch(Exception) { - DrawText(g, 3, playlist.Name); + + } + + var playlist = cachedPlaylist; + if(!playlist.Equals(null)) + { + if (descFlip) + { + DrawTextWithinBounds(g, 0, playlist.Type, mainFont, 0, 110); + } + else + { + DrawTextWithinBounds(g, 0, playlist.Name, mainFont, 0, 110); + } } } else { DrawText(g, 3, "Unknown"); } + + string currentTime = DateTime.Now.ToString("h:mm:ss tt"); + Size textSize = TextRenderer.MeasureText(currentTime, mainFont); + DrawText(g, 0, currentTime, LogiLcd.MonoWidth - textSize.Width); } - catch (NullReferenceException) + catch (NullReferenceException e) { g.Clear(bgColor); - DrawTextScroll(g, 1, "No track information available", false); + var split = e.StackTrace.Split('\\'); + DrawTextScroll(g, 1, string.Format("Exception: {0}", e.GetType())); + DrawTextScroll(g, 2, split[split.Length - 1]); + //DrawTextScroll(g, 1, "No track information available", false); } } diff --git a/Spoti15.csproj b/Spoti15.csproj index a87396a..8b11ce9 100644 --- a/Spoti15.csproj +++ b/Spoti15.csproj @@ -45,6 +45,7 @@ prompt MinimumRecommendedRules.ruleset false + true bin\x86\Release\ @@ -118,24 +119,60 @@ - - - - - - - - - - - - - - - - - - + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + SettingsSingleFileGenerator From 93147e45a690f9002727436b5acd833c5c3eb104 Mon Sep 17 00:00:00 2001 From: Nick Whitlock Date: Fri, 17 Apr 2020 17:58:18 -0400 Subject: [PATCH 3/8] All features present. --- Spoti15.cs | 685 ++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 518 insertions(+), 167 deletions(-) diff --git a/Spoti15.cs b/Spoti15.cs index ac8ecdc..953a4cd 100644 --- a/Spoti15.cs +++ b/Spoti15.cs @@ -22,7 +22,7 @@ class Spoti15 private Timer descFlipTimer; private Timer inputTimer; private Timer disableLikedSongNotificationTimer; - private Timer upNextTimer; + private Timer hideErrorTimer; private uint scrollStep = 0; private bool descFlip = false; @@ -36,13 +36,18 @@ class Spoti15 private string _clientId = ""; //""; private string _secretId = ""; //""; private string refreshToken = ""; - private bool authorized = true; + private bool authorized = false; private bool cachedLikedTrack = false; private bool likedSongNotification = false; private bool unlikedSongNotification = false; + private bool showingError = false; + private string errorString = ""; private FullTrack likedOrUnlikedSong; - private PlaylistTrack upNextTrack; + private PlaylistTrack upNextPlaylistTrack; + private SimpleTrack upNextAlbumTrack; + private FullTrack upNextAlbumTrackFull; private FullPlaylist cachedPlaylist; + private FullAlbum cachedAlbum; private PlaybackContext cachedPlayback; public Spoti15() @@ -54,7 +59,7 @@ public Spoti15() lcd = new LogiLcd("Spotify"); spotTimer = new Timer(); - spotTimer.Interval = 1000; + spotTimer.Interval = 500; spotTimer.Enabled = true; spotTimer.Tick += OnSpotTimer; @@ -64,7 +69,7 @@ public Spoti15() lcdTimer.Tick += OnLcdTimer; refreshTimer = new Timer(); - refreshTimer.Interval = 5000; + refreshTimer.Interval = 500; refreshTimer.Enabled = true; refreshTimer.Tick += OnRefreshTimer; @@ -96,9 +101,119 @@ private void OnSpotTimer(object source, EventArgs e) private bool btn1Before = false; private bool btn2Before = false; private bool btn3Before = false; + private bool seeking = false; + private bool inControlMenu = false; + private double currentSeekPosition = 0.0f; + private static double seekSpeed = 0.003; private void CheckInput(object source, EventArgs e) { + seeking = lcd.IsButtonPressed(LogiLcd.LcdButton.Mono2); + + inControlMenu = lcd.IsButtonPressed(LogiLcd.LcdButton.Mono3); + if(inControlMenu && !seeking) + { + bool btn2InCtlNow = lcd.IsButtonPressed(LogiLcd.LcdButton.Mono2); + if(btn2InCtlNow && !btn2Before) + { + var error = api.PausePlayback(); + if(error.HasError()) + { + showingError = true; + errorString = error.Error.Message; + hideErrorTimer = new Timer(); + hideErrorTimer.Enabled = true; + hideErrorTimer.Interval = 3000; + hideErrorTimer.Tick += OnErrorHidden; + } + } + btn2Before = btn2InCtlNow; + + bool btn1InCtlNow = lcd.IsButtonPressed(LogiLcd.LcdButton.Mono1); + if(btn1InCtlNow && !btn1Before) + { + var error = api.SkipPlaybackToNext(); + if (error.HasError()) + { + showingError = true; + errorString = error.Error.Message; + hideErrorTimer = new Timer(); + hideErrorTimer.Enabled = true; + hideErrorTimer.Interval = 3000; + hideErrorTimer.Tick += OnErrorHidden; + } + } + btn1Before = btn1InCtlNow; + + bool btn0InCtlNow = lcd.IsButtonPressed(LogiLcd.LcdButton.Mono0); + if(btn0InCtlNow && !btn0Before) + { + var error = api.SkipPlaybackToPrevious(); + if (error.HasError()) + { + showingError = true; + errorString = error.Error.Message; + hideErrorTimer = new Timer(); + hideErrorTimer.Enabled = true; + hideErrorTimer.Interval = 3000; + hideErrorTimer.Tick += OnErrorHidden; + } + } + btn0Before = btn0InCtlNow; + return; + } + + if (seeking && !btn2Before && cachedPlayback != null && cachedPlayback.CurrentlyPlayingType != TrackType.Ad) + { + // set seek position to current + currentSeekPosition = (double)cachedPlayback.ProgressMs / cachedPlayback.Item.DurationMs; + } + + btn2Before = seeking; + + if(seeking) + { + bool btn0InSeekNow = lcd.IsButtonPressed(LogiLcd.LcdButton.Mono0); + if(btn0InSeekNow && !btn0Before) + { + int toMs = (int)(currentSeekPosition * cachedPlayback.Item.DurationMs); + var error = api.SeekPlayback(toMs); + if (error.HasError()) + { + showingError = true; + errorString = error.Error.Message; + hideErrorTimer = new Timer(); + hideErrorTimer.Enabled = true; + hideErrorTimer.Interval = 3000; + hideErrorTimer.Tick += OnErrorHidden; + } + seeking = false; + btn0Before = true; + return; + } + btn0Before = btn0InSeekNow; + + if(lcd.IsButtonPressed(LogiLcd.LcdButton.Mono1)) + { + currentSeekPosition -= seekSpeed; + } + if(lcd.IsButtonPressed(LogiLcd.LcdButton.Mono3)) + { + currentSeekPosition += seekSpeed; + } + + if(currentSeekPosition > 1.0) + { + currentSeekPosition = 1.0; + } + else if(currentSeekPosition < 0.0) + { + currentSeekPosition = 0.0; + } + + return; + } + bool btn0Now = lcd.IsButtonPressed(LogiLcd.LcdButton.Mono0); if(btn0Now && !btn0Before) { @@ -144,69 +259,92 @@ private void CheckInput(object source, EventArgs e) bool btn1Now = lcd.IsButtonPressed(LogiLcd.LcdButton.Mono1); if(btn1Now) { - var playlist = cachedPlaylist; - if(playlist == null) + var playback = cachedPlayback; + if(playback == null) { return; } - var playback = cachedPlayback; - if(playback == null) - { + if(playback.CurrentlyPlayingType == TrackType.Ad) + { // doing calls later down here is bad return; } - /*if(showUpNext) + if(playback.Context == null) { - showUpNext = false; - btn1Before = btn1Now; - upNextTimer.Enabled = false; - return; - }*/ - upNextTrack = null; - for(int i = 0; i < playlist.Tracks.Items.Capacity; i++) + } + else if(playback.Context.Type == "playlist") { - if(playlist.Tracks.Items[i].Track.Uri == playback.Item.Uri) + var playlist = cachedPlaylist; + if (playlist == null) { - // next track is it - if(i == playlist.Tracks.Items.Capacity - 1) - { - upNextTrack = playlist.Tracks.Items[0]; - } - else + return; + } + + upNextPlaylistTrack = null; + for (int i = 0; i < playlist.Tracks.Items.Count; i++) + { + if (playlist.Tracks.Items[i].Track.Uri == playback.Item.Uri) { - upNextTrack = playlist.Tracks.Items[i + 1]; + // next track is it + if (i == playlist.Tracks.Items.Count - 1) + { + upNextPlaylistTrack = playlist.Tracks.Items[0]; + } + else + { + upNextPlaylistTrack = playlist.Tracks.Items[i + 1]; + } + break; } - break; + } + if (upNextPlaylistTrack == null) + { + return; } } - if(upNextTrack == null) + else if(playback.Context.Type == "album") { - return; - } + upNextPlaylistTrack = null; - showUpNext = true; - /*upNextTimer = new Timer(); - upNextTimer.Enabled = true; - upNextTimer.Interval = 5000; - upNextTimer.Tick += OnUpNextFinished;*/ + var newAlbum = cachedAlbum; + if(newAlbum == null) + { + return; + } + + for(int i = 0; i < newAlbum.Tracks.Items.Count; i++) + { + if(playback.Item.Uri == newAlbum.Tracks.Items[i].Uri) + { + if(i == newAlbum.Tracks.Items.Count - 1) + { + upNextAlbumTrack = null; + } + else + { + upNextAlbumTrack = newAlbum.Tracks.Items[i + 1]; + } + } + } + } + } showUpNext = btn1Now; - //btn1Before = btn1Now; } - private void OnLikedSongNotificationFinished(object source, EventArgs e) + private void OnErrorHidden(object source, EventArgs e) { - likedSongNotification = unlikedSongNotification = false; - disableLikedSongNotificationTimer.Enabled = false; + showingError = false; + hideErrorTimer.Enabled = false; } - private void OnUpNextFinished(object source, EventArgs e) + private void OnLikedSongNotificationFinished(object source, EventArgs e) { - showUpNext = false; - upNextTimer.Enabled = false; + likedSongNotification = unlikedSongNotification = false; + disableLikedSongNotificationTimer.Enabled = false; } private void OnLcdTimer(object source, EventArgs e) @@ -265,8 +403,6 @@ private async void OnAuthReceived(object sender, AuthorizationCode payload) { initExcpt = e; } - - } private void Authorize() @@ -275,7 +411,8 @@ private void Authorize() System.Diagnostics.Debug.Write("Re-authorize\r\n"); auth?.Stop(); - auth = new AuthorizationCodeAuth(_clientId, _secretId, url, url, Scope.UserReadCurrentlyPlaying | Scope.UserReadPlaybackState | Scope.UserLibraryRead | Scope.UserLibraryModify); + auth = new AuthorizationCodeAuth(_clientId, _secretId, url, url, Scope.UserReadCurrentlyPlaying | + Scope.UserReadPlaybackState | Scope.UserLibraryRead | Scope.UserLibraryModify | Scope.UserModifyPlaybackState); if (string.IsNullOrEmpty(refreshToken)) { @@ -327,9 +464,75 @@ private void InitSpot() public void UpdateSpot() { - if(initExcpt != null) return; + + if (api == null) + return; + + var retrievedPlayback = api.GetPlayback(); + if(retrievedPlayback != null) + { + cachedPlayback = retrievedPlayback; + if(cachedPlayback.HasError() && cachedPlayback.Error.Message == "The access token expired" && authorized) + { + authorized = false; + Authorize(); + } + } + + if(cachedPlayback != null && cachedPlayback.Item != null) + { + var ListedItem = new List(1); + ListedItem.Add(cachedPlayback.Item.Id); + var response = api.CheckSavedTracks(ListedItem); + if(response.List == null) + { + if (response.HasError() && response.Error.Message == "The access token expired" && authorized) + { + authorized = false; + Authorize(); + } + } + else + { + cachedLikedTrack = response.List[0]; + } + + if(cachedPlayback.Context == null) + { + + } + else if(cachedPlayback.Context.Type == "playlist") + { + var split = cachedPlayback.Context.ExternalUrls["spotify"].Split('/'); + var newPlaylist = api.GetPlaylist(split[4]); + if(newPlaylist != null && !newPlaylist.HasError()) + { + cachedPlaylist = newPlaylist; + } + } + else if(cachedPlayback.Context.Type == "album") + { + var newAlbum = api.GetAlbum(cachedPlayback.Item.Album.Id); + if(newAlbum != null && !newAlbum.HasError()) + { + cachedAlbum = newAlbum; + } + + if(upNextAlbumTrack != null) + { + upNextAlbumTrackFull = api.GetTrack(cachedPlayback.Item.Id); + } + else + { + upNextAlbumTrackFull = null; + } + } + + + } + } private Bitmap bgBitmap = new Bitmap(LogiLcd.MonoWidth, LogiLcd.MonoHeight); @@ -435,6 +638,117 @@ private string GetStringFromArtists(SimpleArtist[] artists) return returnValue; } + private string GetStringFromGenres(string[] genres) + { + string returnValue = ""; + + for (int i = 0; i < genres.Length; i++) + { + returnValue = string.Concat(returnValue, genres[i]); + if (i != genres.Length - 1) + { + returnValue = string.Concat(returnValue, ", "); + } + } + + return returnValue; + } + + private void DrawPlaybackStatus(Graphics g, bool drawTime) + { + var playback = cachedPlayback; + + int len = playback.Item.DurationMs; + int pos = playback.ProgressMs; + double perc = pos / (double)len; + + // Draw following status + DrawText(g, 6, cachedLikedTrack ? "e" : "f", iconFont, 0, 1); + + // Draw progress bar + g.DrawRectangle(Pens.White, 8, LogiLcd.MonoHeight - 6, (LogiLcd.MonoWidth - 24), 4); + g.FillRectangle(Brushes.White, 8, LogiLcd.MonoHeight - 6, (int)((LogiLcd.MonoWidth - 24) * perc), 4); + + if (playback.IsPlaying) + { + g.FillPolygon(Brushes.White, new Point[] { + new Point((LogiLcd.MonoWidth - 14), LogiLcd.MonoHeight - 7), + new Point((LogiLcd.MonoWidth - 14), LogiLcd.MonoHeight - 1), + new Point((LogiLcd.MonoWidth - 9), LogiLcd.MonoHeight - 4) + }); + + if (lineTrack > 12) + lineTrack = 6; + else + lineTrack++; + for (int x = lineTrack; x < LogiLcd.MonoWidth - 22; x += 8) + g.DrawLine(Pens.Black, new Point(x, LogiLcd.MonoHeight - 4), new Point(x + 2, LogiLcd.MonoHeight - 4)); + } + else + { + g.FillRectangle(Brushes.White, new Rectangle((LogiLcd.MonoWidth - 10), LogiLcd.MonoHeight - 6, 2, 5)); + g.FillRectangle(Brushes.White, new Rectangle((LogiLcd.MonoWidth - 13), LogiLcd.MonoHeight - 6, 2, 5)); + } + + if (playback.ShuffleState) + { + DrawText(g, 6, "S", mainFont, LogiLcd.MonoWidth - 7, -1); + } + else if (playback.RepeatState == RepeatState.Context) + { + DrawText(g, 6, "h", iconFont, LogiLcd.MonoWidth - 7); + } + else if (playback.RepeatState == RepeatState.Track) + { + DrawText(g, 6, "g", iconFont, LogiLcd.MonoWidth - 7); + } + + if(drawTime) + { + DrawTextScroll(g, 5, String.Format("{0}:{1:D2} / {2}:{3:D2}", pos / 60000, (pos % 60000) / 1000, len / 60000, (len % 60000) / 1000)); + } + } + + private void DrawPlaylistStatus(Graphics g) + { + var playback = cachedPlayback; + + if (playback.Context == null) + { + + } + else if (playback.Context.Type == "album") + { + if (descFlip) + { + DrawText(g, 0, "Playing Album"); + } + else + { + DrawTextWithinBounds(g, 0, playback.Item.Album.Name, mainFont, 0, 110); + } + } + else if (playback.Context.Type == "playlist") + { + var playlist = cachedPlaylist; + if (playlist != null) + { + if (descFlip) + { + DrawTextWithinBounds(g, 0, playlist.Type, mainFont, 0, 110); + } + else + { + DrawTextWithinBounds(g, 0, playlist.Name, mainFont, 0, 110); + } + } + } + else + { + DrawText(g, 3, "Unknown"); + } + } + //private Byte[] emptyBg = new Byte[LogiLcd.MonoWidth * LogiLcd.MonoHeight]; private int lineTrack = 4; public void UpdateLcd() @@ -464,8 +778,16 @@ public void UpdateLcd() // TODO: draw spotify logo g.Clear(bgColor); DrawTextScroll(g, 2, "SPOTIFY"); - DoRender(); - return; + } + else if(showingError) + { + g.Clear(bgColor); + + DrawTextScroll(g, 1, "ERROR", bigFont); + DrawTextScroll(g, 3, errorString); + + DrawPlaybackStatus(g, true); + DrawPlaylistStatus(g); } else if(likedSongNotification || unlikedSongNotification) { @@ -483,170 +805,199 @@ public void UpdateLcd() DrawTextScroll(g, 3, likedOrUnlikedSong.Name); DrawTextScroll(g, 4, GetStringFromArtists(likedOrUnlikedSong.Artists.ToArray())); DrawTextScroll(g, 5, likedOrUnlikedSong.Album.Name); - DoRender(); - return; + + DrawPlaybackStatus(g, false); } - else if(showUpNext) + else if(seeking && cachedPlayback != null && cachedPlayback.Item != null) { g.Clear(bgColor); - DrawTextScroll(g, 1, "UP NEXT", bigFont); - DrawTextScroll(g, 3, upNextTrack.Track.Name); - DrawTextScroll(g, 4, GetStringFromArtists(upNextTrack.Track.Artists.ToArray())); - DrawTextScroll(g, 5, upNextTrack.Track.Album.Name); - DoRender(); - return; - } - - try - { - var retrievedPlayback = api.GetPlayback(); - if(retrievedPlayback != null && retrievedPlayback.Item != null) - { - cachedPlayback = retrievedPlayback; - } + DrawTextScroll(g, 1, "SEEKING"); + + var posInMs = (int)(cachedPlayback.Item.DurationMs * currentSeekPosition); + DrawTextScroll(g, 3, string.Format("{0:D2}:{1:D2}", posInMs / 60000, (posInMs % 60000) / 1000)); + + // Draw progress bar + g.DrawRectangle(Pens.White, 8, LogiLcd.MonoHeight - 16, (LogiLcd.MonoWidth - 24), 6); + g.FillRectangle(Brushes.White, 8, LogiLcd.MonoHeight - 16, (int)((LogiLcd.MonoWidth - 24) * currentSeekPosition), 6); + + g.FillPolygon(Brushes.White, new Point[] { + new Point((LogiLcd.MonoWidth - 19), LogiLcd.MonoHeight - 7), + new Point((LogiLcd.MonoWidth - 19), LogiLcd.MonoHeight - 1), + new Point((LogiLcd.MonoWidth - 14), LogiLcd.MonoHeight - 4) + }); + + g.FillPolygon(Brushes.White, new Point[] { + new Point(60, LogiLcd.MonoHeight - 7), + new Point(60, LogiLcd.MonoHeight - 1), + new Point(55, LogiLcd.MonoHeight - 4) + }); + + DrawText(g, 6, "OK", iconFont, 8); + DrawPlaylistStatus(g); } - catch(Exception) + else if(inControlMenu && cachedPlayback != null) { + g.Clear(bgColor); - } + g.FillPolygon(Brushes.White, new Point[] { + new Point((LogiLcd.MonoWidth - 100), LogiLcd.MonoHeight - 7), + new Point((LogiLcd.MonoWidth - 100), LogiLcd.MonoHeight - 1), + new Point((LogiLcd.MonoWidth - 95), LogiLcd.MonoHeight - 4) + }); - var playback = cachedPlayback; - if(playback == null || playback.Item == null) - { - var track = api.GetPlayingTrack(); - if(track == null) - { - g.Clear(bgColor); - DrawTextScroll(g, 1, "ERROR"); - DrawTextScroll(g, 2, "SPOTIFY PLAYBACK NOT DETECTED"); - DoRender(); - return; - } - else if(track.CurrentlyPlayingType == TrackType.Ad) + g.FillPolygon(Brushes.White, new Point[] { + new Point((LogiLcd.MonoWidth - 106), LogiLcd.MonoHeight - 7), + new Point((LogiLcd.MonoWidth - 106), LogiLcd.MonoHeight - 1), + new Point((LogiLcd.MonoWidth - 101), LogiLcd.MonoHeight - 4) + }); + + g.FillPolygon(Brushes.White, new Point[] { + new Point(17, LogiLcd.MonoHeight - 7), + new Point(17, LogiLcd.MonoHeight - 1), + new Point(12, LogiLcd.MonoHeight - 4) + }); + + g.FillPolygon(Brushes.White, new Point[] { + new Point(23, LogiLcd.MonoHeight - 7), + new Point(23, LogiLcd.MonoHeight - 1), + new Point(18, LogiLcd.MonoHeight - 4) + }); + + if (cachedPlayback.IsPlaying) { - g.Clear(bgColor); - DrawTextScroll(g, 2, "Advertisement"); - DoRender(); - return; + g.FillRectangle(Brushes.White, new Rectangle((LogiLcd.MonoWidth - 58), LogiLcd.MonoHeight - 6, 2, 5)); + g.FillRectangle(Brushes.White, new Rectangle((LogiLcd.MonoWidth - 61), LogiLcd.MonoHeight - 6, 2, 5)); } else { - return; + g.FillPolygon(Brushes.White, new Point[] { + new Point((LogiLcd.MonoWidth - 64), LogiLcd.MonoHeight - 7), + new Point((LogiLcd.MonoWidth - 64), LogiLcd.MonoHeight - 1), + new Point((LogiLcd.MonoWidth - 59), LogiLcd.MonoHeight - 4) + }); } - } - int len = playback.Item.DurationMs; - int pos = playback.ProgressMs; - double perc = pos / (double)len; - - try - { - var ListedItem = new List(1); - ListedItem.Add(playback.Item.Id); - var response = api.CheckSavedTracks(ListedItem); - cachedLikedTrack = response.List[0]; - } - catch(Exception) - { - } - - DrawText(g, 6, cachedLikedTrack ? "e" : "f", iconFont, 0, 1); + DrawTextScroll(g, 1, "UP NEXT", bigFont); - DrawTextScroll(g, 2, GetStringFromArtists(playback.Item.Artists.ToArray())); - DrawTextScroll(g, 1, playback.Item.Name); - DrawTextScroll(g, 5, String.Format("{0}:{1:D2} / {2}:{3:D2}", pos / 60000, (pos % 60000) / 1000, len / 60000, (len % 60000) / 1000)); - DrawTextScroll(g, 3, playback.Item.Album.Name); + if(cachedPlayback.Context == null) + { - // Draw progress bar - g.DrawRectangle(Pens.White, 8, LogiLcd.MonoHeight - 6, (LogiLcd.MonoWidth - 24), 4); - g.FillRectangle(Brushes.White, 8, LogiLcd.MonoHeight - 6, (int)((LogiLcd.MonoWidth - 24) * perc), 4); + } + else if (cachedPlayback.Context.Type == "playlist" && upNextPlaylistTrack != null) + { + DrawTextScroll(g, 3, upNextPlaylistTrack.Track.Name); + DrawTextScroll(g, 4, GetStringFromArtists(upNextPlaylistTrack.Track.Artists.ToArray())); + DrawTextScroll(g, 5, upNextPlaylistTrack.Track.Album.Name); - if (playback.IsPlaying) - { - g.FillPolygon(Brushes.White, new Point[] { - new Point((LogiLcd.MonoWidth - 14), LogiLcd.MonoHeight - 7), - new Point((LogiLcd.MonoWidth - 14), LogiLcd.MonoHeight - 1), - new Point((LogiLcd.MonoWidth - 9), LogiLcd.MonoHeight - 4) - }); + DrawText(g, 0, string.Format("e {0}", upNextPlaylistTrack.Track.Popularity), iconFont, 5, 5); + } + else if (cachedPlayback.Context.Type == "album" && upNextAlbumTrack != null) + { + DrawTextScroll(g, 3, upNextAlbumTrack.Name); + DrawTextScroll(g, 4, string.Format("Track {0}", upNextAlbumTrack.TrackNumber)); - if (lineTrack > 12) - lineTrack = 6; + if (upNextAlbumTrackFull != null) + { + DrawText(g, 0, string.Format("e {0}", upNextAlbumTrackFull.Popularity), iconFont, 5, 5); + } + } else - lineTrack++; - for (int x = lineTrack; x < LogiLcd.MonoWidth - 22; x += 8) - g.DrawLine(Pens.Black, new Point(x, LogiLcd.MonoHeight - 4), new Point(x + 2, LogiLcd.MonoHeight - 4)); + { + DrawTextScroll(g, 3, "Unknown"); + } } - else + else if(showUpNext) { - g.FillRectangle(Brushes.White, new Rectangle((LogiLcd.MonoWidth - 10), LogiLcd.MonoHeight - 6, 2, 5)); - g.FillRectangle(Brushes.White, new Rectangle((LogiLcd.MonoWidth - 13), LogiLcd.MonoHeight - 6, 2, 5)); - } + g.Clear(bgColor); - if(playback.ShuffleState) - { - DrawText(g, 6, "S", mainFont, LogiLcd.MonoWidth - 7, -1); - } - else if(playback.RepeatState == RepeatState.Context) - { - DrawText(g, 6, "h", iconFont, LogiLcd.MonoWidth - 7); - } - else if(playback.RepeatState == RepeatState.Track) - { - DrawText(g, 6, "g", iconFont, LogiLcd.MonoWidth - 7); - } - - if (playback.Context.Type == "album") - { - if(descFlip) + if(cachedPlayback.Context == null) { - DrawText(g, 0, "Playing Album"); + } - else + else if(cachedPlayback.Context.Type == "playlist") { - DrawTextWithinBounds(g, 0, playback.Item.Album.Name, mainFont, 0, 120); + DrawTextScroll(g, 1, "PLAYLIST", bigFont); + DrawTextScroll(g, 3, cachedPlaylist.Name); + DrawTextScroll(g, 4, cachedPlaylist.Description); + + DrawText(g, 0, string.Format("e {0}", cachedPlaylist.Followers.Total), iconFont, 5, 5); + + DrawPlaybackStatus(g, true); } - } - else if (playback.Context.Type == "playlist") - { - try + else if(cachedPlayback.Context.Type == "album") { - var split = playback.Context.ExternalUrls["spotify"].Split('/'); - var newPlaylist = api.GetPlaylist(split[4]); - if(newPlaylist != null) + DrawTextScroll(g, 1, "ALBUM", bigFont); + DrawTextScroll(g, 3, cachedPlayback.Item.Album.Name); + DrawTextScroll(g, 4, GetStringFromArtists(cachedPlayback.Item.Artists.ToArray())); + DrawTextScroll(g, 5, string.Format("Released {0}", cachedPlayback.Item.Album.ReleaseDate)); + + if(cachedAlbum != null) { - cachedPlaylist = api.GetPlaylist(split[4]); + string genres = GetStringFromGenres(cachedAlbum.Genres.ToArray()); + if(genres == "") + { + DrawText(g, 0, string.Format("e {0}", cachedAlbum.Popularity), iconFont, 5, 5); + } + else + { + DrawTextScroll(g, 6, string.Format("{0} - {1} followers", GetStringFromGenres(cachedAlbum.Genres.ToArray()), cachedAlbum.Popularity)); + } } - - } - catch(Exception) - { + DrawPlaybackStatus(g, true); } + } + else + { - var playlist = cachedPlaylist; - if(!playlist.Equals(null)) + var playback = cachedPlayback; + if (playback == null || playback.Item == null) { - if (descFlip) + if (playback == null) + { + g.Clear(bgColor); + DrawTextScroll(g, 1, "ERROR"); + DrawTextScroll(g, 2, "SPOTIFY PLAYBACK NOT DETECTED"); + DoRender(); + return; + } + else if (playback.CurrentlyPlayingType == TrackType.Ad) + { + g.Clear(bgColor); + DrawTextScroll(g, 2, "Advertisement"); + DoRender(); + return; + } + else if (playback.HasError()) { - DrawTextWithinBounds(g, 0, playlist.Type, mainFont, 0, 110); + g.Clear(bgColor); + DrawTextScroll(g, 1, "ERROR"); + DrawTextScroll(g, 2, playback.Error.Message); } else { - DrawTextWithinBounds(g, 0, playlist.Name, mainFont, 0, 110); + return; } } - } - else - { - DrawText(g, 3, "Unknown"); + + + // Draw number of followers + DrawText(g, 5, string.Format("e {0}", cachedPlayback.Item.Popularity), iconFont); + + DrawTextScroll(g, 2, GetStringFromArtists(playback.Item.Artists.ToArray())); + DrawTextScroll(g, 1, playback.Item.Name); + DrawTextScroll(g, 3, playback.Item.Album.Name); + + DrawPlaybackStatus(g, true); + DrawPlaylistStatus(g); } string currentTime = DateTime.Now.ToString("h:mm:ss tt"); Size textSize = TextRenderer.MeasureText(currentTime, mainFont); DrawText(g, 0, currentTime, LogiLcd.MonoWidth - textSize.Width); } - catch (NullReferenceException e) + catch (Exception e) { g.Clear(bgColor); var split = e.StackTrace.Split('\\'); From dff57190a057f48931a1e30706c6c7a2f8e387a5 Mon Sep 17 00:00:00 2001 From: Nick Whitlock Date: Mon, 20 Apr 2020 11:32:03 -0400 Subject: [PATCH 4/8] Fixed errors with artist playback mode, and a NullReferenceException --- Spoti15.cs | 95 ++++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 86 insertions(+), 9 deletions(-) diff --git a/Spoti15.cs b/Spoti15.cs index 953a4cd..df12d24 100644 --- a/Spoti15.cs +++ b/Spoti15.cs @@ -7,6 +7,7 @@ using SpotifyAPI.Web.Auth; using SpotifyAPI.Web.Enums; using SpotifyAPI.Web.Models; +using System.Globalization; namespace Spoti15 { @@ -49,6 +50,7 @@ class Spoti15 private FullPlaylist cachedPlaylist; private FullAlbum cachedAlbum; private PlaybackContext cachedPlayback; + private FullArtist cachedArtist; public Spoti15() { @@ -522,14 +524,52 @@ public void UpdateSpot() if(upNextAlbumTrack != null) { - upNextAlbumTrackFull = api.GetTrack(cachedPlayback.Item.Id); + if(cachedPlayback.Item.TrackNumber + 1 > newAlbum.Tracks.Items.Count) + { // just go back to 0 i guess? + upNextAlbumTrackFull = api.GetTrack(newAlbum.Tracks.Items[0].Id); + } + else + { + upNextAlbumTrackFull = api.GetTrack(newAlbum.Tracks.Items[cachedPlayback.Item.TrackNumber + 1].Id); + } } else { upNextAlbumTrackFull = null; } } - + else if(cachedPlayback.Context.Type == "artist") + { + var split = cachedPlayback.Context.ExternalUrls["spotify"].Split('/'); + var region = RegionInfo.CurrentRegion; + + upNextAlbumTrackFull = null; + + var artistTracks = api.GetArtistsTopTracks(split[4], region.TwoLetterISORegionName); + var thisArtist = api.GetArtist(split[4]); + + if(thisArtist != null) + { + cachedArtist = thisArtist; + } + + for(int i = 0; i < artistTracks.Tracks.Count; i++) + { + if(artistTracks.Tracks[i].Id == cachedPlayback.Item.Id) + { + if(i == artistTracks.Tracks.Count - 1) + { + upNextAlbumTrackFull = artistTracks.Tracks[0]; + break; + } + else + { + upNextAlbumTrackFull = artistTracks.Tracks[i + 1]; + break; + } + } + } + } } @@ -743,6 +783,17 @@ private void DrawPlaylistStatus(Graphics g) } } } + else if(playback.Context.Type == "artist") + { + if(descFlip || cachedArtist == null) + { + DrawText(g, 0, "Playing Artist"); + } + else if(cachedArtist != null) + { + DrawTextWithinBounds(g, 0, cachedArtist.Name, mainFont, 0, 110); + } + } else { DrawText(g, 3, "Unknown"); @@ -890,7 +941,7 @@ public void UpdateLcd() DrawTextScroll(g, 4, GetStringFromArtists(upNextPlaylistTrack.Track.Artists.ToArray())); DrawTextScroll(g, 5, upNextPlaylistTrack.Track.Album.Name); - DrawText(g, 0, string.Format("e {0}", upNextPlaylistTrack.Track.Popularity), iconFont, 5, 5); + DrawText(g, 0, string.Format("e {0}%", upNextPlaylistTrack.Track.Popularity), iconFont, 5, 5); } else if (cachedPlayback.Context.Type == "album" && upNextAlbumTrack != null) { @@ -899,9 +950,15 @@ public void UpdateLcd() if (upNextAlbumTrackFull != null) { - DrawText(g, 0, string.Format("e {0}", upNextAlbumTrackFull.Popularity), iconFont, 5, 5); + DrawText(g, 0, string.Format("e {0}%", upNextAlbumTrackFull.Popularity), iconFont, 5, 5); } } + else if(cachedPlayback.Context.Type == "artist" && upNextAlbumTrackFull != null) + { + DrawTextScroll(g, 3, upNextAlbumTrackFull.Name); + DrawTextScroll(g, 4, upNextAlbumTrackFull.Album.Name); + DrawText(g, 0, string.Format("e {0}%", upNextAlbumTrackFull.Popularity), iconFont, 5, 5); + } else { DrawTextScroll(g, 3, "Unknown"); @@ -947,6 +1004,20 @@ public void UpdateLcd() DrawPlaybackStatus(g, true); } + else if(cachedPlayback.Context.Type == "artist" && cachedArtist != null) + { + DrawTextScroll(g, 1, "ARTIST", bigFont); + DrawTextScroll(g, 3, cachedArtist.Name); + DrawTextScroll(g, 4, GetStringFromGenres(cachedArtist.Genres.ToArray())); + + DrawText(g, 0, string.Format("e {0}", cachedArtist.Followers.Total), iconFont, 5, 5); + + DrawPlaybackStatus(g, true); + } + else + { + DrawTextScroll(g, 1, cachedPlayback.Context.Type); + } } else { @@ -983,11 +1054,17 @@ public void UpdateLcd() // Draw number of followers - DrawText(g, 5, string.Format("e {0}", cachedPlayback.Item.Popularity), iconFont); - - DrawTextScroll(g, 2, GetStringFromArtists(playback.Item.Artists.ToArray())); - DrawTextScroll(g, 1, playback.Item.Name); - DrawTextScroll(g, 3, playback.Item.Album.Name); + if(cachedPlayback != null && cachedPlayback.Item != null) + { + DrawText(g, 5, string.Format("e {0}%", cachedPlayback.Item.Popularity), iconFont); + } + + if(playback != null && playback.Item != null) + { + DrawTextScroll(g, 2, GetStringFromArtists(playback.Item.Artists.ToArray())); + DrawTextScroll(g, 1, playback.Item.Name); + DrawTextScroll(g, 3, playback.Item.Album.Name); + } DrawPlaybackStatus(g, true); DrawPlaylistStatus(g); From 9a9dc54d71bae7f2375710829d813ded5ac2a65b Mon Sep 17 00:00:00 2001 From: Nick Date: Mon, 20 Apr 2020 11:53:05 -0400 Subject: [PATCH 5/8] Update README.md --- README.md | 78 ++++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 54 insertions(+), 24 deletions(-) diff --git a/README.md b/README.md index 6270f56..04de4c8 100644 --- a/README.md +++ b/README.md @@ -2,45 +2,75 @@ ### A Spotify Applet for the Logitech G15/G510S --- -![example gif](https://thumbs.gfycat.com/HalfSpanishDutchshepherddog-size_restricted.gif) ---- - The original developer has abandoned the project so I've kept this applet updated to work with the latest version of [SpotifyAPI-NET](https://github.com/JohnnyCrazy/SpotifyAPI-NET). + The original developer has abandoned the project so I've kept this applet updated to work with the latest version of [SpotifyAPI-NET](https://github.com/JohnnyCrazy/SpotifyAPI-NET) and updated it with new features. -### Features -1. Displays: Artist | Track Title | Album Title | Elapsed Time | Total Time | Play/Pause Status -1. Text scrolling -2. Sleek 11px Native font -3. Support for the latest version Spotify +## Features +1. Three display modes +2. Seeking and playback control (Spotify Premium members only) +3. Follow/Unfollow buttons +4. Text scrolling +5. Sleek display +6. Support for the latest version Spotify + +## Installation +1\. Download the Latest release of Spoti15 from [Here.](https://github.com/eezstreet/Spoti15/releases) + +2\. Create a new applet in the Spotify Developer Dashboard. Save the Client ID and Secret ID. + +3\. Run Spoti15. Use the Client ID and Secret ID in the web browser window that pops up. + +4\. (OPTIONAL): Save the Client ID and Secret ID to environment variables (SPOTIFY_CLIENT_ID and SPOTIFY_SECRET_ID) to save them across sessions. + +5\. Ensure that the application is selected in your Logitech Gaming Software + +6\. Ensure that Spotify is Running in the background. + +7\. Done! -### Installation -1\. Download the Latest release of Spoti15 from [Here.](https://github.com/haidarn2/Spoti15/releases) +## Main Display +![Main display](https://i.imgur.com/359JN6p.png) -2\. Ensure that Spotify is Running in the background. -3\. Run Spoti15.exe! +### Controls -![Running spoti15](http://i.imgur.com/hbvBbMS.png) +Pressing the left button (button 0) at any time will either LIKE or UNLIKE the currently playing song. -4\. Done! +![Liked display](https://i.imgur.com/DfqLbRy.png) +Pressing and holding the left-middle button (button 1) while in the main display will show information about the currently playing playlist, album, or artist. -![example gif](http://gifimgs.com/res/1016/57f883e446259953890092.gif) +![Playlist Information](https://i.imgur.com/7r2hntK.png) -### Customization -Currently, two things can be toggled within Spoti15; album name display, and animated lines. +![Artist Information](https://i.imgur.com/8p9AgLf.png) -![controls](http://i.imgur.com/0euaQrH.png) +![Album Information](https://i.imgur.com/lmsVIOx.png) -### Toggling Album Name ON/OFF -![album on](http://i.imgur.com/b187cNt.png) -![album off](http://i.imgur.com/s2nsfy4.png) +## Seek Display +![Seek display](https://i.imgur.com/oBVqnxA.png) + +Pressing and holding the right-middle button (button 2) while in the main display will bring up the Seek Display. + +### Controls +Note that the Seek Display will only function if you have a Spotify Premium membership. + +Pressing the left-middle button (button 1) will seek left, and pressing the right button (button 3) will seek right. Press the left button (button 0) to go to that section of the song. + +## Up Next Display +![Up Next Display](https://i.imgur.com/QIOX18F.png) + +Pressing and holding the right button (button 3) while in the main display will bring up the Up Next Display. + +### Controls +Note that the Up Next Display will only function if you have a Spotify Premium membership. + +Pressing the left button (button 0) will go to the previous song. Pressing the left-middle button will go to the next song. Pressing the right-middle button will pause/play the current track. -### Toggling Animated Lines ON/OFF -![lines on](http://i.imgur.com/RcnTdDe.png) -![lines off](http://i.imgur.com/eElJoBx.png) ### *Changelog:* ``` +v2.0.0 [April 12 2020] + + Updated SpotifyAPI-NET, total rewrite of the software + v1.0.0.16 [April 28 2018] + Updated SpotifyAPI-NET to 2.18.1 + Support for 1.0.77.338.g758ebd78 From 65224b870e1b752a195eb5b5d422dd4071beedc8 Mon Sep 17 00:00:00 2001 From: Nick Whitlock Date: Mon, 20 Apr 2020 11:54:46 -0400 Subject: [PATCH 6/8] Fixed some overlappign text. --- Spoti15.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Spoti15.cs b/Spoti15.cs index df12d24..b029f02 100644 --- a/Spoti15.cs +++ b/Spoti15.cs @@ -1002,7 +1002,7 @@ public void UpdateLcd() } } - DrawPlaybackStatus(g, true); + DrawPlaybackStatus(g, false); } else if(cachedPlayback.Context.Type == "artist" && cachedArtist != null) { From ea47e2bcb2b3ebb4285ba049df86637b5013e9e3 Mon Sep 17 00:00:00 2001 From: Nick Whitlock Date: Mon, 20 Apr 2020 17:12:54 -0400 Subject: [PATCH 7/8] Fixed token not being refreshed like it should be --- Spoti15.cs | 150 +++++++++++++++++++++++++++++------------------------ 1 file changed, 83 insertions(+), 67 deletions(-) diff --git a/Spoti15.cs b/Spoti15.cs index b029f02..89ecd67 100644 --- a/Spoti15.cs +++ b/Spoti15.cs @@ -440,6 +440,7 @@ private async Task RefreshAccessToken() Authorize(); return; } + UpdateAccessToken(token); } catch(Exception e) { @@ -472,105 +473,114 @@ public void UpdateSpot() if (api == null) return; - var retrievedPlayback = api.GetPlayback(); - if(retrievedPlayback != null) + try { - cachedPlayback = retrievedPlayback; - if(cachedPlayback.HasError() && cachedPlayback.Error.Message == "The access token expired" && authorized) - { - authorized = false; - Authorize(); - } - } + initExcpt = null; - if(cachedPlayback != null && cachedPlayback.Item != null) - { - var ListedItem = new List(1); - ListedItem.Add(cachedPlayback.Item.Id); - var response = api.CheckSavedTracks(ListedItem); - if(response.List == null) + var retrievedPlayback = api.GetPlayback(); + if (retrievedPlayback != null) { - if (response.HasError() && response.Error.Message == "The access token expired" && authorized) + cachedPlayback = retrievedPlayback; + if (cachedPlayback.HasError() && cachedPlayback.Error.Message == "The access token expired" && authorized) { authorized = false; Authorize(); } } - else - { - cachedLikedTrack = response.List[0]; - } - if(cachedPlayback.Context == null) + if (cachedPlayback != null && cachedPlayback.Item != null) { - - } - else if(cachedPlayback.Context.Type == "playlist") - { - var split = cachedPlayback.Context.ExternalUrls["spotify"].Split('/'); - var newPlaylist = api.GetPlaylist(split[4]); - if(newPlaylist != null && !newPlaylist.HasError()) + var ListedItem = new List(1); + ListedItem.Add(cachedPlayback.Item.Id); + var response = api.CheckSavedTracks(ListedItem); + if (response.List == null) { - cachedPlaylist = newPlaylist; + if (response.HasError() && response.Error.Message == "The access token expired" && authorized) + { + authorized = false; + Authorize(); + } } - } - else if(cachedPlayback.Context.Type == "album") - { - var newAlbum = api.GetAlbum(cachedPlayback.Item.Album.Id); - if(newAlbum != null && !newAlbum.HasError()) + else { - cachedAlbum = newAlbum; + cachedLikedTrack = response.List[0]; } - if(upNextAlbumTrack != null) + if (cachedPlayback.Context == null) { - if(cachedPlayback.Item.TrackNumber + 1 > newAlbum.Tracks.Items.Count) - { // just go back to 0 i guess? - upNextAlbumTrackFull = api.GetTrack(newAlbum.Tracks.Items[0].Id); + + } + else if (cachedPlayback.Context.Type == "playlist") + { + var split = cachedPlayback.Context.ExternalUrls["spotify"].Split('/'); + var newPlaylist = api.GetPlaylist(split[4]); + if (newPlaylist != null && !newPlaylist.HasError()) + { + cachedPlaylist = newPlaylist; + } + } + else if (cachedPlayback.Context.Type == "album") + { + var newAlbum = api.GetAlbum(cachedPlayback.Item.Album.Id); + if (newAlbum != null && !newAlbum.HasError()) + { + cachedAlbum = newAlbum; + } + + if (upNextAlbumTrack != null) + { + if (cachedPlayback.Item.TrackNumber + 1 > newAlbum.Tracks.Items.Count) + { // just go back to 0 i guess? + upNextAlbumTrackFull = api.GetTrack(newAlbum.Tracks.Items[0].Id); + } + else + { + upNextAlbumTrackFull = api.GetTrack(newAlbum.Tracks.Items[cachedPlayback.Item.TrackNumber + 1].Id); + } } else { - upNextAlbumTrackFull = api.GetTrack(newAlbum.Tracks.Items[cachedPlayback.Item.TrackNumber + 1].Id); + upNextAlbumTrackFull = null; } } - else + else if (cachedPlayback.Context.Type == "artist") { + var split = cachedPlayback.Context.ExternalUrls["spotify"].Split('/'); + var region = RegionInfo.CurrentRegion; + upNextAlbumTrackFull = null; - } - } - else if(cachedPlayback.Context.Type == "artist") - { - var split = cachedPlayback.Context.ExternalUrls["spotify"].Split('/'); - var region = RegionInfo.CurrentRegion; - upNextAlbumTrackFull = null; + var artistTracks = api.GetArtistsTopTracks(split[4], region.TwoLetterISORegionName); + var thisArtist = api.GetArtist(split[4]); - var artistTracks = api.GetArtistsTopTracks(split[4], region.TwoLetterISORegionName); - var thisArtist = api.GetArtist(split[4]); + if (thisArtist != null) + { + cachedArtist = thisArtist; + } - if(thisArtist != null) - { - cachedArtist = thisArtist; - } - - for(int i = 0; i < artistTracks.Tracks.Count; i++) - { - if(artistTracks.Tracks[i].Id == cachedPlayback.Item.Id) + for (int i = 0; i < artistTracks.Tracks.Count; i++) { - if(i == artistTracks.Tracks.Count - 1) + if (artistTracks.Tracks[i].Id == cachedPlayback.Item.Id) { - upNextAlbumTrackFull = artistTracks.Tracks[0]; - break; - } - else - { - upNextAlbumTrackFull = artistTracks.Tracks[i + 1]; - break; + if (i == artistTracks.Tracks.Count - 1) + { + upNextAlbumTrackFull = artistTracks.Tracks[0]; + break; + } + else + { + upNextAlbumTrackFull = artistTracks.Tracks[i + 1]; + break; + } } } } + } - + } + catch(AggregateException /*e*/) + { + //initExcpt = e; // This is common if the task was dropped } } @@ -698,6 +708,12 @@ private void DrawPlaybackStatus(Graphics g, bool drawTime) { var playback = cachedPlayback; + if(playback.HasError()) + { // if there's an error, don't bother + DrawTextScroll(g, 6, playback.Error.Message); + return; + } + int len = playback.Item.DurationMs; int pos = playback.ProgressMs; double perc = pos / (double)len; From b07bb94b942ab70150e118763de5f0b6364bf233 Mon Sep 17 00:00:00 2001 From: Paolo de Heer Date: Fri, 4 Mar 2022 15:00:28 +0100 Subject: [PATCH 8/8] Update README.md Add missing step in installation instructions (i.e. whitelisting the URI) --- README.md | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 04de4c8..bfc1e08 100644 --- a/README.md +++ b/README.md @@ -17,15 +17,17 @@ 2\. Create a new applet in the Spotify Developer Dashboard. Save the Client ID and Secret ID. -3\. Run Spoti15. Use the Client ID and Secret ID in the web browser window that pops up. +3\. In the Applet page of the Spotify Developer Dashboard, under 'Edit Settings' enter the following URI to the 'Redirect URIs' whitelist: `http://localhost:4002`. Then click 'Add' and finally 'Save'. -4\. (OPTIONAL): Save the Client ID and Secret ID to environment variables (SPOTIFY_CLIENT_ID and SPOTIFY_SECRET_ID) to save them across sessions. +4\. Run Spoti15. Use the Client ID and Secret ID in the web browser window that pops up. -5\. Ensure that the application is selected in your Logitech Gaming Software +5\. (OPTIONAL): Save the Client ID and Secret ID to environment variables (SPOTIFY_CLIENT_ID and SPOTIFY_SECRET_ID) to save them across sessions. -6\. Ensure that Spotify is Running in the background. +6\. Ensure that the application is selected in your Logitech Gaming Software -7\. Done! +7\. Ensure that Spotify is Running in the background. + +8\. Done! ## Main Display ![Main display](https://i.imgur.com/359JN6p.png)