mirror of
https://github.com/Sarsoo/Spotify.NET.git
synced 2024-12-24 15:06:26 +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/
|
||||
[Oo]bj/
|
||||
|
||||
|
@ -7,6 +7,7 @@ using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Forms;
|
||||
using Image = System.Drawing.Image;
|
||||
|
||||
@ -26,14 +27,7 @@ namespace SpotifyAPI.Example
|
||||
InitializeComponent();
|
||||
|
||||
_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)
|
||||
@ -57,7 +51,7 @@ namespace SpotifyAPI.Example
|
||||
AccessToken = token.AccessToken,
|
||||
TokenType = token.TokenType
|
||||
};
|
||||
InitialSetup();
|
||||
|
||||
}
|
||||
|
||||
private async void InitialSetup()
|
||||
@ -129,8 +123,32 @@ namespace SpotifyAPI.Example
|
||||
|
||||
private void authButton_Click(object sender, EventArgs e)
|
||||
{
|
||||
_auth.StartHttpServer(8000);
|
||||
_auth.DoAuth();
|
||||
Task.Run(() => RunAuthentication());
|
||||
}
|
||||
|
||||
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
|
||||
{
|
||||
public class SpotifyLocalAPI
|
||||
public class SpotifyLocalAPI : IDisposable
|
||||
{
|
||||
[DllImport("user32.dll")]
|
||||
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 readonly RemoteHandler _rh;
|
||||
private readonly Timer _eventTimer;
|
||||
private Timer _eventTimer;
|
||||
private StatusResponse _eventStatusResponse;
|
||||
|
||||
public event EventHandler<TrackChangeEventArgs> OnTrackChange;
|
||||
@ -60,13 +60,17 @@ namespace SpotifyAPI.Local
|
||||
|
||||
public event EventHandler<TrackTimeChangeEventArgs> OnTrackTimeChange;
|
||||
|
||||
public SpotifyLocalAPI()
|
||||
public SpotifyLocalAPI(int timerIntervall = 50)
|
||||
{
|
||||
_rh = new RemoteHandler();
|
||||
AttachTimer(timerIntervall);
|
||||
}
|
||||
|
||||
private void AttachTimer(int intervall)
|
||||
{
|
||||
_eventTimer = new Timer
|
||||
{
|
||||
Interval = 50,
|
||||
Interval = intervall,
|
||||
AutoReset = false,
|
||||
Enabled = false
|
||||
};
|
||||
@ -333,5 +337,13 @@ namespace SpotifyAPI.Local
|
||||
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 Include="Local\Models\SpotifyUri.cs" />
|
||||
<Compile Include="Local\VolumeMixerControl.cs" />
|
||||
<Compile Include="Web\Auth\WebAPIFactory.cs" />
|
||||
<Compile Include="Web\Enums\TimeRangeType.cs" />
|
||||
<Compile Include="Web\IClient.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.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Specialized;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Web;
|
||||
|
||||
// offered to the public domain for any use with no restriction
|
||||
@ -33,34 +37,17 @@ namespace SpotifyAPI.Web
|
||||
_srv = srv;
|
||||
}
|
||||
|
||||
private string StreamReadLine(Stream inputStream)
|
||||
private string[] GetIncomingRequest(Stream inputStream)
|
||||
{
|
||||
string data = "";
|
||||
while (_isActive)
|
||||
{
|
||||
var nextChar = inputStream.ReadByte();
|
||||
if (nextChar == '\n')
|
||||
{
|
||||
break;
|
||||
}
|
||||
if (nextChar == '\r')
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (nextChar == -1)
|
||||
{
|
||||
Thread.Sleep(1);
|
||||
continue;
|
||||
}
|
||||
data += Convert.ToChar(nextChar);
|
||||
}
|
||||
return data;
|
||||
var buffer = new byte[4096];
|
||||
var read = inputStream.Read(buffer, 0, buffer.Length);
|
||||
|
||||
var inputData = Encoding.ASCII.GetString(buffer.Take(read).ToArray());
|
||||
return inputData.Split('\n').Select(s => s.Trim()).Where(s => !string.IsNullOrEmpty(s)).ToArray();
|
||||
}
|
||||
|
||||
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
|
||||
// "processed" view of the world, and we want the data raw after the headers
|
||||
_inputStream = new BufferedStream(socket.GetStream());
|
||||
@ -69,8 +56,11 @@ namespace SpotifyAPI.Web
|
||||
OutputStream = new StreamWriter(new BufferedStream(socket.GetStream()));
|
||||
try
|
||||
{
|
||||
ParseRequest();
|
||||
ReadHeaders();
|
||||
var requestLines = GetIncomingRequest(_inputStream);
|
||||
|
||||
ParseRequest(requestLines.First());
|
||||
ReadHeaders(requestLines.Skip(1));
|
||||
|
||||
if (HttpMethod.Equals("GET"))
|
||||
{
|
||||
HandleGetRequest();
|
||||
@ -80,19 +70,17 @@ namespace SpotifyAPI.Web
|
||||
HandlePostRequest();
|
||||
}
|
||||
}
|
||||
catch
|
||||
catch (Exception ex)
|
||||
{
|
||||
WriteFailure();
|
||||
}
|
||||
OutputStream.Flush();
|
||||
_inputStream = null;
|
||||
OutputStream = null;
|
||||
socket.Close();
|
||||
}
|
||||
|
||||
public void ParseRequest()
|
||||
public void ParseRequest(string request)
|
||||
{
|
||||
string request = StreamReadLine(_inputStream);
|
||||
string[] tokens = request.Split(' ');
|
||||
if (tokens.Length < 2)
|
||||
{
|
||||
@ -102,10 +90,9 @@ namespace SpotifyAPI.Web
|
||||
HttpUrl = tokens[1];
|
||||
}
|
||||
|
||||
public void ReadHeaders()
|
||||
public void ReadHeaders(IEnumerable<string> requestLines)
|
||||
{
|
||||
string line;
|
||||
while ((line = StreamReadLine(_inputStream)) != null)
|
||||
foreach(var line in requestLines)
|
||||
{
|
||||
if (string.IsNullOrEmpty(line))
|
||||
{
|
||||
@ -219,16 +206,8 @@ namespace SpotifyAPI.Web
|
||||
_listener = new TcpListener(IPAddress.Any, Port);
|
||||
_listener.Start();
|
||||
|
||||
using (HttpProcessor processor = new HttpProcessor(this))
|
||||
{
|
||||
while (IsActive)
|
||||
{
|
||||
TcpClient s = _listener.AcceptTcpClient();
|
||||
Thread thread = new Thread(processor.Process);
|
||||
thread.Start(s);
|
||||
Thread.Sleep(1);
|
||||
}
|
||||
}
|
||||
_listener.BeginAcceptTcpClient(AcceptTcpConnection, _listener);
|
||||
|
||||
}
|
||||
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 HandlePostRequest(HttpProcessor p, StreamReader inputData);
|
||||
@ -315,6 +316,8 @@ namespace SpotifyAPI.Web
|
||||
"window.location = hashes" +
|
||||
"</script>" +
|
||||
"<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;
|
||||
}
|
||||
string url = p.HttpUrl;
|
||||
@ -345,8 +348,12 @@ namespace SpotifyAPI.Web
|
||||
State = col.Get(3)
|
||||
});
|
||||
});
|
||||
p.OutputStream.Flush();
|
||||
p.OutputStream.Close();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
t.Start();
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user