using System; using System.ComponentModel; using System.Diagnostics; using System.IO; using System.Runtime.InteropServices; using System.Timers; using SpotifyAPI.Local.Models; namespace SpotifyAPI.Local { public class SpotifyLocalAPI { [DllImport("user32.dll")] private static extern void keybd_event(byte bVk, byte bScan, uint dwFlags, int dwExtraInfo); [DllImport("nircmd.dll")] private static extern bool DoNirCmd(String nirCmdStr); private bool _listenForEvents; public bool ListenForEvents { get { return _listenForEvents; } set { _listenForEvents = value; _eventTimer.Enabled = value; } } private ISynchronizeInvoke _synchronizingObject; public ISynchronizeInvoke SynchronizingObject { get { return _synchronizingObject; } set { _synchronizingObject = value; _eventTimer.SynchronizingObject = value; } } const byte VkMediaNextTrack = 0xb0; const byte VkMediaPrevTrack = 0xb1; const int KeyeventfExtendedkey = 0x1; const int KeyeventfKeyup = 0x2; readonly RemoteHandler _rh; private readonly Timer _eventTimer; private StatusResponse _eventStatusResponse; public delegate void TrackChangeEventHandler(TrackChangeEventArgs e); public delegate void PlayStateEventHandler(PlayStateEventArgs e); public delegate void VolumeChangeEventHandler(VolumeChangeEventArgs e); public delegate void TrackTimeChangeEventHandler(TrackTimeChangeEventArgs e); public event TrackChangeEventHandler OnTrackChange; public event PlayStateEventHandler OnPlayStateChange; public event VolumeChangeEventHandler OnVolumeChange; public event TrackTimeChangeEventHandler OnTrackTimeChange; public SpotifyLocalAPI() { _rh = new RemoteHandler(); _eventTimer = new Timer { Interval = 50, AutoReset = false, Enabled = false }; _eventTimer.Elapsed += ElapsedTick; } private void ElapsedTick(object sender, ElapsedEventArgs e) { if (_eventStatusResponse == null) { _eventStatusResponse = GetStatus(); _eventTimer.Start(); return; } StatusResponse newStatusResponse = GetStatus(); if (newStatusResponse == null) { _eventTimer.Start(); return; } if (!newStatusResponse.Running && newStatusResponse.Track == null) { _eventTimer.Start(); return; } if (newStatusResponse.Track != null && _eventStatusResponse.Track != null) { if (newStatusResponse.Track.TrackResource?.Name != _eventStatusResponse.Track.TrackResource?.Name) { OnTrackChange?.Invoke(new TrackChangeEventArgs() { OldTrack = _eventStatusResponse.Track, NewTrack = newStatusResponse.Track }); } } if (newStatusResponse.Playing != _eventStatusResponse.Playing) { OnPlayStateChange?.Invoke(new PlayStateEventArgs() { Playing = newStatusResponse.Playing }); } if (newStatusResponse.Volume != _eventStatusResponse.Volume) { OnVolumeChange?.Invoke(new VolumeChangeEventArgs() { OldVolume = _eventStatusResponse.Volume, NewVolume = newStatusResponse.Volume }); } if (newStatusResponse.PlayingPosition != _eventStatusResponse.PlayingPosition) { OnTrackTimeChange?.Invoke(new TrackTimeChangeEventArgs() { TrackTime = newStatusResponse.PlayingPosition }); } _eventStatusResponse = newStatusResponse; _eventTimer.Start(); } /// /// Connects with Spotify. Needs to be called before all other SpotifyAPI functions /// /// Returns true, if it was successful, false if not public Boolean Connect() { return _rh.Init(); } /// /// Update and returns the new StatusResponse from the Spotify-Player /// /// An up-to-date StatusResponse public StatusResponse GetStatus() { return _rh.GetNewStatus(); } /// /// Mutes Spotify (Requires nircmd.dll) /// public void Mute() { if (File.Exists("nircmd.dll")) DoNirCmd("muteappvolume spotify.exe 1"); } /// /// Unmutes Spotify (Requires nircmd.dll) /// public void UnMute() { if (File.Exists("nircmd.dll")) DoNirCmd("muteappvolume spotify.exe 0"); } /// /// Pause function /// public void Pause() { _rh.SendPauseRequest(); } /// /// Play function /// public void Play() { _rh.SendPlayRequest(); } /// /// Simulates a KeyPress /// /// The keycode for the represented Key internal void PressKey(byte keyCode) { keybd_event(keyCode, 0x45, KeyeventfExtendedkey, 0); keybd_event(keyCode, 0x45, KeyeventfExtendedkey | KeyeventfKeyup, 0); } /// /// Plays a Spotify URI within an optional context. /// /// The Spotify URI /// The context in which to play the specified . /// /// Contexts are basically a queue in spotify. a song can be played within a context, meaning that hitting next / previous would lead to another song. Contexts are leveraged by widgets as described in the "Multiple tracks player" section of the following documentation page: https://developer.spotify.com/technologies/widgets/spotify-play-button/ /// public void PlayURL(String uri, String context = "") { _rh.SendPlayRequest(uri, context); } /// /// Adds a Spotify URI to the Queue /// /// The Spotify URI [Obsolete("This method doesn't work with the current spotify version.")] public void AddToQueue(String uri) { _rh.SendQueueRequest(uri); } /// /// Skips the current song (Using keypress simulation) /// public void Skip() { PressKey(VkMediaNextTrack); } /// /// Emulates the "Previous" Key (Using keypress simulation) /// public void Previous() { PressKey(VkMediaPrevTrack); } /// /// Checks if Spotify is running /// /// True, if it's running, false if not public static Boolean IsSpotifyRunning() { return Process.GetProcessesByName("spotify").Length >= 1; } /// /// Checks if Spotify's WebHelper is running (Needed for API Calls) /// /// True, if it's running, false if not public static Boolean IsSpotifyWebHelperRunning() { return Process.GetProcessesByName("spotifywebhelper").Length >= 1; } /// /// Runs Spotify /// public static void RunSpotify() { if (!IsSpotifyRunning()) Process.Start(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), @"spotify\spotify.exe")); } /// /// Runs Spotify's WebHelper /// public static void RunSpotifyWebHelper() { if (!IsSpotifyWebHelperRunning()) Process.Start(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), @"spotify\data\spotifywebhelper.exe")); } } }