mirror of
https://github.com/Sarsoo/Spotify.NET.git
synced 2024-12-24 23:16:28 +00:00
commit
409a444d53
2
.gitignore
vendored
2
.gitignore
vendored
@ -1,4 +1,4 @@
|
|||||||
# Build Folders (you can keep bin if you'd like, to store dlls and pdbs)
|
# Build Folders (you can keep bin if you'd like, to store dlls and pdbs)
|
||||||
[Bb]in/
|
[Bb]in/
|
||||||
[Oo]bj/
|
[Oo]bj/
|
||||||
|
|
||||||
|
@ -7,6 +7,7 @@ using System.Collections.Generic;
|
|||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
|
using System.Threading.Tasks;
|
||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
using Image = System.Drawing.Image;
|
using Image = System.Drawing.Image;
|
||||||
|
|
||||||
@ -26,14 +27,7 @@ namespace SpotifyAPI.Example
|
|||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
|
|
||||||
_savedTracks = new List<FullTrack>();
|
_savedTracks = new List<FullTrack>();
|
||||||
_auth = new ImplicitGrantAuth
|
|
||||||
{
|
|
||||||
RedirectUri = "http://localhost:8000",
|
|
||||||
ClientId = "26d287105e31491889f3cd293d85bfea",
|
|
||||||
Scope = Scope.UserReadPrivate | Scope.UserReadEmail | Scope.PlaylistReadPrivate | Scope.UserLibraryRead | Scope.UserReadPrivate | Scope.UserFollowRead | Scope.UserReadBirthdate | Scope.UserTopRead,
|
|
||||||
State = "XSS"
|
|
||||||
};
|
|
||||||
_auth.OnResponseReceivedEvent += _auth_OnResponseReceivedEvent;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void _auth_OnResponseReceivedEvent(Token token, string state)
|
private void _auth_OnResponseReceivedEvent(Token token, string state)
|
||||||
@ -57,7 +51,7 @@ namespace SpotifyAPI.Example
|
|||||||
AccessToken = token.AccessToken,
|
AccessToken = token.AccessToken,
|
||||||
TokenType = token.TokenType
|
TokenType = token.TokenType
|
||||||
};
|
};
|
||||||
InitialSetup();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async void InitialSetup()
|
private async void InitialSetup()
|
||||||
@ -129,8 +123,32 @@ namespace SpotifyAPI.Example
|
|||||||
|
|
||||||
private void authButton_Click(object sender, EventArgs e)
|
private void authButton_Click(object sender, EventArgs e)
|
||||||
{
|
{
|
||||||
_auth.StartHttpServer(8000);
|
Task.Run(() => RunAuthentication());
|
||||||
_auth.DoAuth();
|
}
|
||||||
|
|
||||||
|
private async void RunAuthentication()
|
||||||
|
{
|
||||||
|
WebAPIFactory webApiFactory = new WebAPIFactory(
|
||||||
|
"http://localhost",
|
||||||
|
8000,
|
||||||
|
"26d287105e31491889f3cd293d85bfea",
|
||||||
|
Scope.UserReadPrivate | Scope.UserReadEmail | Scope.PlaylistReadPrivate | Scope.UserLibraryRead |
|
||||||
|
Scope.UserReadPrivate | Scope.UserFollowRead | Scope.UserReadBirthdate | Scope.UserTopRead,
|
||||||
|
TimeSpan.FromSeconds(20));
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_spotify = await webApiFactory.GetWebApi();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
MessageBox.Show(ex.Message);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_spotify == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
InitialSetup();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -8,7 +8,7 @@ using System.Timers;
|
|||||||
|
|
||||||
namespace SpotifyAPI.Local
|
namespace SpotifyAPI.Local
|
||||||
{
|
{
|
||||||
public class SpotifyLocalAPI
|
public class SpotifyLocalAPI : IDisposable
|
||||||
{
|
{
|
||||||
[DllImport("user32.dll")]
|
[DllImport("user32.dll")]
|
||||||
private static extern void keybd_event(byte bVk, byte bScan, uint dwFlags, int dwExtraInfo);
|
private static extern void keybd_event(byte bVk, byte bScan, uint dwFlags, int dwExtraInfo);
|
||||||
@ -49,7 +49,7 @@ namespace SpotifyAPI.Local
|
|||||||
private const int KeyeventfKeyup = 0x2;
|
private const int KeyeventfKeyup = 0x2;
|
||||||
|
|
||||||
private readonly RemoteHandler _rh;
|
private readonly RemoteHandler _rh;
|
||||||
private readonly Timer _eventTimer;
|
private Timer _eventTimer;
|
||||||
private StatusResponse _eventStatusResponse;
|
private StatusResponse _eventStatusResponse;
|
||||||
|
|
||||||
public event EventHandler<TrackChangeEventArgs> OnTrackChange;
|
public event EventHandler<TrackChangeEventArgs> OnTrackChange;
|
||||||
@ -60,13 +60,17 @@ namespace SpotifyAPI.Local
|
|||||||
|
|
||||||
public event EventHandler<TrackTimeChangeEventArgs> OnTrackTimeChange;
|
public event EventHandler<TrackTimeChangeEventArgs> OnTrackTimeChange;
|
||||||
|
|
||||||
public SpotifyLocalAPI()
|
public SpotifyLocalAPI(int timerIntervall = 50)
|
||||||
{
|
{
|
||||||
_rh = new RemoteHandler();
|
_rh = new RemoteHandler();
|
||||||
|
AttachTimer(timerIntervall);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AttachTimer(int intervall)
|
||||||
|
{
|
||||||
_eventTimer = new Timer
|
_eventTimer = new Timer
|
||||||
{
|
{
|
||||||
Interval = 50,
|
Interval = intervall,
|
||||||
AutoReset = false,
|
AutoReset = false,
|
||||||
Enabled = false
|
Enabled = false
|
||||||
};
|
};
|
||||||
@ -333,5 +337,13 @@ namespace SpotifyAPI.Local
|
|||||||
Process.Start(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), @"spotify\spotifywebhelper.exe"));
|
Process.Start(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), @"spotify\spotifywebhelper.exe"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
if (_eventTimer == null)
|
||||||
|
return;
|
||||||
|
_eventTimer.Enabled = false;
|
||||||
|
_eventTimer.Elapsed -= ElapsedTick;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -58,6 +58,7 @@
|
|||||||
</Compile>
|
</Compile>
|
||||||
<Compile Include="Local\Models\SpotifyUri.cs" />
|
<Compile Include="Local\Models\SpotifyUri.cs" />
|
||||||
<Compile Include="Local\VolumeMixerControl.cs" />
|
<Compile Include="Local\VolumeMixerControl.cs" />
|
||||||
|
<Compile Include="Web\Auth\WebAPIFactory.cs" />
|
||||||
<Compile Include="Web\Enums\TimeRangeType.cs" />
|
<Compile Include="Web\Enums\TimeRangeType.cs" />
|
||||||
<Compile Include="Web\IClient.cs" />
|
<Compile Include="Web\IClient.cs" />
|
||||||
<Compile Include="Local\Models\CFID.cs" />
|
<Compile Include="Local\Models\CFID.cs" />
|
||||||
|
87
SpotifyAPI/Web/Auth/WebApiFactory.cs
Normal file
87
SpotifyAPI/Web/Auth/WebApiFactory.cs
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
using System;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using SpotifyAPI.Web.Enums;
|
||||||
|
using SpotifyAPI.Web.Models;
|
||||||
|
|
||||||
|
namespace SpotifyAPI.Web.Auth
|
||||||
|
{
|
||||||
|
public class WebAPIFactory
|
||||||
|
{
|
||||||
|
private readonly string _redirectUrl;
|
||||||
|
private readonly int _listeningPort;
|
||||||
|
private readonly string _clientId;
|
||||||
|
private readonly TimeSpan _timeout;
|
||||||
|
private readonly Scope _scope;
|
||||||
|
|
||||||
|
public WebAPIFactory(string redirectUrl, int listeningPort, string clientId, Scope scope, TimeSpan timeout)
|
||||||
|
{
|
||||||
|
_redirectUrl = redirectUrl;
|
||||||
|
_listeningPort = listeningPort;
|
||||||
|
_clientId = clientId;
|
||||||
|
_scope = scope;
|
||||||
|
_timeout = timeout;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task<SpotifyWebAPI> GetWebApi()
|
||||||
|
{
|
||||||
|
var authentication = new ImplicitGrantAuth
|
||||||
|
{
|
||||||
|
RedirectUri = $"{_redirectUrl}:{_listeningPort}",
|
||||||
|
ClientId = _clientId,
|
||||||
|
Scope = _scope,
|
||||||
|
State = "XSS"
|
||||||
|
};
|
||||||
|
|
||||||
|
AutoResetEvent authenticationWaitFlag = new AutoResetEvent(false);
|
||||||
|
SpotifyWebAPI spotifyWebApi = null;
|
||||||
|
authentication.OnResponseReceivedEvent += (token, state) =>
|
||||||
|
{
|
||||||
|
spotifyWebApi = HandleSpotifyResponse(state, token);
|
||||||
|
authenticationWaitFlag.Set();
|
||||||
|
};
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
authentication.StartHttpServer(_listeningPort);
|
||||||
|
|
||||||
|
authentication.DoAuth();
|
||||||
|
|
||||||
|
authenticationWaitFlag.WaitOne(_timeout);
|
||||||
|
if (spotifyWebApi == null)
|
||||||
|
throw new TimeoutException($"No valid response received for the last {_timeout.TotalSeconds} seconds");
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
authentication.StopHttpServer();
|
||||||
|
}
|
||||||
|
|
||||||
|
return Task.FromResult(spotifyWebApi);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static SpotifyWebAPI HandleSpotifyResponse(string state, Token token)
|
||||||
|
{
|
||||||
|
if (state != "XSS")
|
||||||
|
throw new SpotifyWebApiException($"Wrong state '{state}' received.");
|
||||||
|
|
||||||
|
if (token.Error != null)
|
||||||
|
throw new SpotifyWebApiException($"Error: {token.Error}");
|
||||||
|
|
||||||
|
var spotifyWebApi = new SpotifyWebAPI
|
||||||
|
{
|
||||||
|
UseAuth = true,
|
||||||
|
AccessToken = token.AccessToken,
|
||||||
|
TokenType = token.TokenType
|
||||||
|
};
|
||||||
|
|
||||||
|
return spotifyWebApi;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Serializable]
|
||||||
|
public class SpotifyWebApiException : Exception
|
||||||
|
{
|
||||||
|
public SpotifyWebApiException(string message) : base(message)
|
||||||
|
{ }
|
||||||
|
}
|
||||||
|
}
|
@ -1,10 +1,14 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections;
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Collections.Specialized;
|
using System.Collections.Specialized;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Net.Sockets;
|
using System.Net.Sockets;
|
||||||
|
using System.Text;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
using System.Web;
|
using System.Web;
|
||||||
|
|
||||||
// offered to the public domain for any use with no restriction
|
// offered to the public domain for any use with no restriction
|
||||||
@ -33,34 +37,17 @@ namespace SpotifyAPI.Web
|
|||||||
_srv = srv;
|
_srv = srv;
|
||||||
}
|
}
|
||||||
|
|
||||||
private string StreamReadLine(Stream inputStream)
|
private string[] GetIncomingRequest(Stream inputStream)
|
||||||
{
|
{
|
||||||
string data = "";
|
var buffer = new byte[4096];
|
||||||
while (_isActive)
|
var read = inputStream.Read(buffer, 0, buffer.Length);
|
||||||
{
|
|
||||||
var nextChar = inputStream.ReadByte();
|
var inputData = Encoding.ASCII.GetString(buffer.Take(read).ToArray());
|
||||||
if (nextChar == '\n')
|
return inputData.Split('\n').Select(s => s.Trim()).Where(s => !string.IsNullOrEmpty(s)).ToArray();
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (nextChar == '\r')
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (nextChar == -1)
|
|
||||||
{
|
|
||||||
Thread.Sleep(1);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
data += Convert.ToChar(nextChar);
|
|
||||||
}
|
|
||||||
return data;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Process(object tcpClient)
|
public void Process(TcpClient socket)
|
||||||
{
|
{
|
||||||
TcpClient socket = tcpClient as TcpClient;
|
|
||||||
|
|
||||||
// we can't use a StreamReader for input, because it buffers up extra data on us inside it's
|
// we can't use a StreamReader for input, because it buffers up extra data on us inside it's
|
||||||
// "processed" view of the world, and we want the data raw after the headers
|
// "processed" view of the world, and we want the data raw after the headers
|
||||||
_inputStream = new BufferedStream(socket.GetStream());
|
_inputStream = new BufferedStream(socket.GetStream());
|
||||||
@ -69,8 +56,11 @@ namespace SpotifyAPI.Web
|
|||||||
OutputStream = new StreamWriter(new BufferedStream(socket.GetStream()));
|
OutputStream = new StreamWriter(new BufferedStream(socket.GetStream()));
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
ParseRequest();
|
var requestLines = GetIncomingRequest(_inputStream);
|
||||||
ReadHeaders();
|
|
||||||
|
ParseRequest(requestLines.First());
|
||||||
|
ReadHeaders(requestLines.Skip(1));
|
||||||
|
|
||||||
if (HttpMethod.Equals("GET"))
|
if (HttpMethod.Equals("GET"))
|
||||||
{
|
{
|
||||||
HandleGetRequest();
|
HandleGetRequest();
|
||||||
@ -80,19 +70,17 @@ namespace SpotifyAPI.Web
|
|||||||
HandlePostRequest();
|
HandlePostRequest();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
WriteFailure();
|
WriteFailure();
|
||||||
}
|
}
|
||||||
OutputStream.Flush();
|
OutputStream.Flush();
|
||||||
_inputStream = null;
|
_inputStream = null;
|
||||||
OutputStream = null;
|
OutputStream = null;
|
||||||
socket.Close();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ParseRequest()
|
public void ParseRequest(string request)
|
||||||
{
|
{
|
||||||
string request = StreamReadLine(_inputStream);
|
|
||||||
string[] tokens = request.Split(' ');
|
string[] tokens = request.Split(' ');
|
||||||
if (tokens.Length < 2)
|
if (tokens.Length < 2)
|
||||||
{
|
{
|
||||||
@ -102,10 +90,9 @@ namespace SpotifyAPI.Web
|
|||||||
HttpUrl = tokens[1];
|
HttpUrl = tokens[1];
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ReadHeaders()
|
public void ReadHeaders(IEnumerable<string> requestLines)
|
||||||
{
|
{
|
||||||
string line;
|
foreach(var line in requestLines)
|
||||||
while ((line = StreamReadLine(_inputStream)) != null)
|
|
||||||
{
|
{
|
||||||
if (string.IsNullOrEmpty(line))
|
if (string.IsNullOrEmpty(line))
|
||||||
{
|
{
|
||||||
@ -219,16 +206,8 @@ namespace SpotifyAPI.Web
|
|||||||
_listener = new TcpListener(IPAddress.Any, Port);
|
_listener = new TcpListener(IPAddress.Any, Port);
|
||||||
_listener.Start();
|
_listener.Start();
|
||||||
|
|
||||||
using (HttpProcessor processor = new HttpProcessor(this))
|
_listener.BeginAcceptTcpClient(AcceptTcpConnection, _listener);
|
||||||
{
|
|
||||||
while (IsActive)
|
|
||||||
{
|
|
||||||
TcpClient s = _listener.AcceptTcpClient();
|
|
||||||
Thread thread = new Thread(processor.Process);
|
|
||||||
thread.Start(s);
|
|
||||||
Thread.Sleep(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
catch (SocketException e)
|
catch (SocketException e)
|
||||||
{
|
{
|
||||||
@ -237,6 +216,28 @@ namespace SpotifyAPI.Web
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void AcceptTcpConnection(IAsyncResult ar)
|
||||||
|
{
|
||||||
|
TcpListener listener = (TcpListener)ar.AsyncState;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var tcpCLient = listener.EndAcceptTcpClient(ar);
|
||||||
|
using (HttpProcessor processor = new HttpProcessor(this))
|
||||||
|
{
|
||||||
|
processor.Process(tcpCLient);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (ObjectDisposedException)
|
||||||
|
{
|
||||||
|
// Ignore
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!IsActive)
|
||||||
|
return;
|
||||||
|
//listener.Start();
|
||||||
|
listener.BeginAcceptTcpClient(AcceptTcpConnection, listener);
|
||||||
|
}
|
||||||
|
|
||||||
public abstract void HandleGetRequest(HttpProcessor p);
|
public abstract void HandleGetRequest(HttpProcessor p);
|
||||||
|
|
||||||
public abstract void HandlePostRequest(HttpProcessor p, StreamReader inputData);
|
public abstract void HandlePostRequest(HttpProcessor p, StreamReader inputData);
|
||||||
@ -315,6 +316,8 @@ namespace SpotifyAPI.Web
|
|||||||
"window.location = hashes" +
|
"window.location = hashes" +
|
||||||
"</script>" +
|
"</script>" +
|
||||||
"<h1>Spotify Auth successful!<br>Please copy the URL and paste it into the application</h1></body></html>");
|
"<h1>Spotify Auth successful!<br>Please copy the URL and paste it into the application</h1></body></html>");
|
||||||
|
p.OutputStream.Flush();
|
||||||
|
p.OutputStream.Close();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
string url = p.HttpUrl;
|
string url = p.HttpUrl;
|
||||||
@ -345,8 +348,12 @@ namespace SpotifyAPI.Web
|
|||||||
State = col.Get(3)
|
State = col.Get(3)
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
p.OutputStream.Flush();
|
||||||
|
p.OutputStream.Close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
t.Start();
|
t.Start();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user