Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -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)
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you restore this file? I guess VS accidently overwrote it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done!

[Bb]in/
[Oo]bj/

Expand Down
40 changes: 29 additions & 11 deletions SpotifyAPI.Example/WebControl.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
using System.IO;
using System.Linq;
using System.Net;
using System.Threading.Tasks;
using System.Windows.Forms;
using Image = System.Drawing.Image;

Expand All @@ -26,14 +27,7 @@ public WebControl()
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)
Expand All @@ -57,7 +51,7 @@ private void _auth_OnResponseReceivedEvent(Token token, string state)
AccessToken = token.AccessToken,
TokenType = token.TokenType
};
InitialSetup();

}

private async void InitialSetup()
Expand Down Expand Up @@ -129,8 +123,32 @@ private List<SimplePlaylist> GetPlaylists()

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();
}
}
}
20 changes: 16 additions & 4 deletions SpotifyAPI/Local/SpotifyLocalAPI.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

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);
Expand Down Expand Up @@ -49,7 +49,7 @@ public ISynchronizeInvoke SynchronizingObject
private const int KeyeventfKeyup = 0x2;

private readonly RemoteHandler _rh;
private readonly Timer _eventTimer;
private Timer _eventTimer;
private StatusResponse _eventStatusResponse;

public event EventHandler<TrackChangeEventArgs> OnTrackChange;
Expand All @@ -60,13 +60,17 @@ public ISynchronizeInvoke SynchronizingObject

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
};
Expand Down Expand Up @@ -333,5 +337,13 @@ public static void RunSpotifyWebHelper()
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;
}
}
}
1 change: 1 addition & 0 deletions SpotifyAPI/SpotifyAPI.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -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" />
Expand Down
87 changes: 87 additions & 0 deletions SpotifyAPI/Web/Auth/WebApiFactory.cs
Original file line number Diff line number Diff line change
@@ -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)
{ }
}
}
93 changes: 50 additions & 43 deletions SpotifyAPI/Web/SimpleHttpServer.cs
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -33,34 +37,17 @@ public HttpProcessor(HttpServer srv)
_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());
Expand All @@ -69,8 +56,11 @@ public void Process(object tcpClient)
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();
Expand All @@ -80,19 +70,17 @@ public void Process(object tcpClient)
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)
{
Expand All @@ -102,10 +90,9 @@ public void ParseRequest()
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))
{
Expand Down Expand Up @@ -219,16 +206,8 @@ public void Listen()
_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)
{
Expand All @@ -237,6 +216,28 @@ public void Listen()
}
}

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);
Expand Down Expand Up @@ -315,6 +316,8 @@ public override void HandleGetRequest(HttpProcessor p)
"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;
Expand Down Expand Up @@ -345,8 +348,12 @@ public override void HandleGetRequest(HttpProcessor p)
State = col.Get(3)
});
});
p.OutputStream.Flush();
p.OutputStream.Close();
}
}


t.Start();
}

Expand Down