From df784be9fd3285fafeb0049d84857f1577651abb Mon Sep 17 00:00:00 2001 From: Samuel Attard Date: Wed, 25 Nov 2015 22:33:28 +1300 Subject: [PATCH] Initial last.fm functionality, basic authentication and user key fetching. API_KEY and API_SECRET must be filled out at build time --- Google Play Music/App.config | 14 +- Google Play Music/Google Play Music.csproj | 30 ++++- Google Play Music/LastFM.Intergration.cs | 121 ++++++++++++++++++ Google Play Music/Program.cs | 5 + .../Properties/Settings.Designer.cs | 24 ++++ .../Properties/Settings.settings | 6 + Google Play Music/SettingsDialog.Designer.cs | 119 +++++++++++++---- Google Play Music/SettingsDialog.cs | 74 +++++++++++ Google Play Music/packages.config | 3 +- 9 files changed, 364 insertions(+), 32 deletions(-) create mode 100644 Google Play Music/LastFM.Intergration.cs diff --git a/Google Play Music/App.config b/Google Play Music/App.config index 632e60b5..510bbb50 100644 --- a/Google Play Music/App.config +++ b/Google Play Music/App.config @@ -34,17 +34,27 @@ True + + Username + + + 1234567 + - + - + + + + + diff --git a/Google Play Music/Google Play Music.csproj b/Google Play Music/Google Play Music.csproj index dc4e766a..3f20ae9a 100644 --- a/Google Play Music/Google Play Music.csproj +++ b/Google Play Music/Google Play Music.csproj @@ -138,18 +138,34 @@ - - ..\packages\Microsoft.Bcl.1.1.8\lib\net40\System.IO.dll + + ..\packages\Microsoft.Bcl.1.1.10\lib\net40\System.IO.dll True - - ..\packages\Microsoft.Bcl.1.1.8\lib\net40\System.Runtime.dll + + ..\packages\Microsoft.Net.Http.2.2.29\lib\net40\System.Net.Http.dll + True + + + ..\packages\Microsoft.Net.Http.2.2.29\lib\net40\System.Net.Http.Extensions.dll + True + + + ..\packages\Microsoft.Net.Http.2.2.29\lib\net40\System.Net.Http.Primitives.dll + True + + + ..\packages\Microsoft.Net.Http.2.2.29\lib\net40\System.Net.Http.WebRequest.dll + True + + + ..\packages\Microsoft.Bcl.1.1.10\lib\net40\System.Runtime.dll True - - ..\packages\Microsoft.Bcl.1.1.8\lib\net40\System.Threading.Tasks.dll + + ..\packages\Microsoft.Bcl.1.1.10\lib\net40\System.Threading.Tasks.dll True @@ -159,7 +175,6 @@ - @@ -189,6 +204,7 @@ CoreMusicApp.cs + diff --git a/Google Play Music/LastFM.Intergration.cs b/Google Play Music/LastFM.Intergration.cs new file mode 100644 index 00000000..f2feb3d4 --- /dev/null +++ b/Google Play Music/LastFM.Intergration.cs @@ -0,0 +1,121 @@ +using System; +using System.Collections.Generic; +using System.Collections.Specialized; +using System.Diagnostics; +using LastFMAPI; +using System.Net; +using System.Net.Http; +using System.Security.Cryptography; +using System.Threading.Tasks; +using System.Text; + +namespace Google_Play_Music +{ + class LastFM + { + private string username; + private string password; + public static string user_key = null; + + private const string API_KEY = ""; + private const string API_SECRET = ""; + + public LastFM() + { + this.username = Properties.Settings.Default.LastFMUsername; + this.password = Properties.Settings.Default.LastFMPassword; + } + + public LastFM(string username, string password) + { + this.username = username; + this.password = password; + } + + public async Task init() + { + await attemptLogIn(); + } + + public async Task attemptLogIn() + { + if (username == "Username" || password == "1234567") + { + return; + } + Dictionary attrs = new Dictionary(); + attrs["password"] = password; + attrs["username"] = username; + string api_sig = signMethod("auth.getMobileSession", attrs); + string requestURL = "https://ws.audioscrobbler.com/2.0/?method=auth.getMobileSession&username=" + username + + "&password=" + password + + "&api_key=" + API_KEY + "&api_sig=" + api_sig + + "&format=json"; + + string auth_response = await fetchURL(requestURL); + + if (auth_response == "") + { + user_key = null; + return; + } + + AuthenticationResponse auth = new System.Web.Script.Serialization.JavaScriptSerializer().Deserialize(auth_response); + user_key = auth.session.key; + return; + } + + private async Task fetchURL(string URL) + { + using (var client = new HttpClient()) + { + Dictionary values = new Dictionary(); + FormUrlEncodedContent content = new FormUrlEncodedContent(values); + + try + { + HttpResponseMessage response = await client.PostAsync(URL, content); + if (response.StatusCode != HttpStatusCode.OK) + { + return ""; + } + string responseString = await response.Content.ReadAsStringAsync(); + + return responseString; + } + catch (Exception e) + { + return ""; + } + } + } + + private string signMethod(string method, Dictionary attributes) + { + string stringToSign = "api_key" + API_KEY + "method" + method; + List keys = new List(attributes.Keys); + keys.Sort(); + foreach (string key in keys) + { + stringToSign += key + attributes[key]; + } + stringToSign += API_SECRET; + return BitConverter.ToString(((HashAlgorithm)CryptoConfig.CreateFromName("MD5")).ComputeHash(new UTF8Encoding().GetBytes(stringToSign))).Replace("-", string.Empty).ToLower(); + } + } +} + +namespace LastFMAPI +{ + public class AuthenticationResponse + { + public AuthenticationSession session { get; set; } + } + + public class AuthenticationSession + { + public int subscriber { get; set; } + public string name { get; set; } + public string key { get; set; } + } +} diff --git a/Google Play Music/Program.cs b/Google Play Music/Program.cs index 053a5755..d23864b5 100644 --- a/Google Play Music/Program.cs +++ b/Google Play Music/Program.cs @@ -1,6 +1,7 @@ using System; using System.Runtime.InteropServices; using System.Threading; +using System.Threading.Tasks; using System.Windows.Forms; namespace Google_Play_Music @@ -17,6 +18,10 @@ namespace Google_Play_Music { if (mutex.WaitOne(TimeSpan.Zero, true)) { + // Init the last.fm authentication with the user set username and password + Task lastFMInit = new LastFM().init(); + lastFMInit.Wait(); + Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); Application.Run(new CoreMusicApp()); diff --git a/Google Play Music/Properties/Settings.Designer.cs b/Google Play Music/Properties/Settings.Designer.cs index 688f32a2..473dcb96 100644 --- a/Google Play Music/Properties/Settings.Designer.cs +++ b/Google Play Music/Properties/Settings.Designer.cs @@ -118,5 +118,29 @@ namespace Google_Play_Music.Properties { this["HoverControls"] = value; } } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("Username")] + public string LastFMUsername { + get { + return ((string)(this["LastFMUsername"])); + } + set { + this["LastFMUsername"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("1234567")] + public string LastFMPassword { + get { + return ((string)(this["LastFMPassword"])); + } + set { + this["LastFMPassword"] = value; + } + } } } diff --git a/Google Play Music/Properties/Settings.settings b/Google Play Music/Properties/Settings.settings index e37151eb..34e47541 100644 --- a/Google Play Music/Properties/Settings.settings +++ b/Google Play Music/Properties/Settings.settings @@ -26,5 +26,11 @@ True + + Username + + + 1234567 + \ No newline at end of file diff --git a/Google Play Music/SettingsDialog.Designer.cs b/Google Play Music/SettingsDialog.Designer.cs index 10986c12..2babf06e 100644 --- a/Google Play Music/SettingsDialog.Designer.cs +++ b/Google Play Music/SettingsDialog.Designer.cs @@ -32,22 +32,26 @@ this.materialLabel1 = new MaterialSkin.Controls.MaterialLabel(); this.materialCheckBox1 = new MaterialSkin.Controls.MaterialCheckBox(); this.materialLabel2 = new MaterialSkin.Controls.MaterialLabel(); - this.colorWheel1 = new Google_Play_Music.ColorWheel(); this.materialRaisedButton1 = new MaterialSkin.Controls.MaterialRaisedButton(); this.materialCheckBox2 = new MaterialSkin.Controls.MaterialCheckBox(); this.materialCheckBox3 = new MaterialSkin.Controls.MaterialCheckBox(); + this.materialLabel3 = new MaterialSkin.Controls.MaterialLabel(); + this.lastFMUsername = new MaterialSkin.Controls.MaterialSingleLineTextField(); + this.lastFMPassword = new MaterialSkin.Controls.MaterialSingleLineTextField(); + this.colorWheel1 = new Google_Play_Music.ColorWheel(); + this.lastFMAuthIndicator = new MaterialSkin.Controls.MaterialLabel(); this.SuspendLayout(); // // materialLabel1 // this.materialLabel1.AutoSize = true; this.materialLabel1.Depth = 0; - this.materialLabel1.Font = new System.Drawing.Font("Roboto", 11F); + this.materialLabel1.Font = new System.Drawing.Font("Microsoft Sans Serif", 11F); this.materialLabel1.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(222)))), ((int)(((byte)(0)))), ((int)(((byte)(0)))), ((int)(((byte)(0))))); this.materialLabel1.Location = new System.Drawing.Point(411, 72); this.materialLabel1.MouseState = MaterialSkin.MouseState.HOVER; this.materialLabel1.Name = "materialLabel1"; - this.materialLabel1.Size = new System.Drawing.Size(176, 19); + this.materialLabel1.Size = new System.Drawing.Size(171, 18); this.materialLabel1.TabIndex = 1; this.materialLabel1.Text = "Custom Theme Highlight"; // @@ -55,7 +59,7 @@ // this.materialCheckBox1.AutoSize = true; this.materialCheckBox1.Depth = 0; - this.materialCheckBox1.Font = new System.Drawing.Font("Roboto", 10F); + this.materialCheckBox1.Font = new System.Drawing.Font("Microsoft Sans Serif", 10F); this.materialCheckBox1.Location = new System.Drawing.Point(13, 97); this.materialCheckBox1.Margin = new System.Windows.Forms.Padding(0); this.materialCheckBox1.MouseLocation = new System.Drawing.Point(-1, -1); @@ -71,31 +75,19 @@ // this.materialLabel2.AutoSize = true; this.materialLabel2.Depth = 0; - this.materialLabel2.Font = new System.Drawing.Font("Roboto", 11F); + this.materialLabel2.Font = new System.Drawing.Font("Microsoft Sans Serif", 11F); this.materialLabel2.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(222)))), ((int)(((byte)(0)))), ((int)(((byte)(0)))), ((int)(((byte)(0))))); this.materialLabel2.Location = new System.Drawing.Point(12, 71); this.materialLabel2.MouseState = MaterialSkin.MouseState.HOVER; this.materialLabel2.Name = "materialLabel2"; - this.materialLabel2.Size = new System.Drawing.Size(119, 19); + this.materialLabel2.Size = new System.Drawing.Size(117, 18); this.materialLabel2.TabIndex = 3; this.materialLabel2.Text = "General Settings"; // - // colorWheel1 - // - this.colorWheel1.Hue = ((byte)(0)); - this.colorWheel1.Lightness = ((byte)(0)); - this.colorWheel1.Location = new System.Drawing.Point(405, 97); - this.colorWheel1.Name = "colorWheel1"; - this.colorWheel1.Saturation = ((byte)(0)); - this.colorWheel1.SecondaryHues = null; - this.colorWheel1.Size = new System.Drawing.Size(183, 191); - this.colorWheel1.TabIndex = 0; - this.colorWheel1.Text = "colorWheel1"; - // // materialRaisedButton1 // this.materialRaisedButton1.Depth = 0; - this.materialRaisedButton1.Location = new System.Drawing.Point(15, 250); + this.materialRaisedButton1.Location = new System.Drawing.Point(13, 350); this.materialRaisedButton1.MouseState = MaterialSkin.MouseState.HOVER; this.materialRaisedButton1.Name = "materialRaisedButton1"; this.materialRaisedButton1.Primary = true; @@ -108,7 +100,7 @@ // this.materialCheckBox2.AutoSize = true; this.materialCheckBox2.Depth = 0; - this.materialCheckBox2.Font = new System.Drawing.Font("Roboto", 10F); + this.materialCheckBox2.Font = new System.Drawing.Font("Microsoft Sans Serif", 10F); this.materialCheckBox2.Location = new System.Drawing.Point(13, 137); this.materialCheckBox2.Margin = new System.Windows.Forms.Padding(0); this.materialCheckBox2.MouseLocation = new System.Drawing.Point(-1, -1); @@ -124,7 +116,7 @@ // this.materialCheckBox3.AutoSize = true; this.materialCheckBox3.Depth = 0; - this.materialCheckBox3.Font = new System.Drawing.Font("Roboto", 10F); + this.materialCheckBox3.Font = new System.Drawing.Font("Microsoft Sans Serif", 10F); this.materialCheckBox3.Location = new System.Drawing.Point(13, 177); this.materialCheckBox3.Margin = new System.Windows.Forms.Padding(0); this.materialCheckBox3.MouseLocation = new System.Drawing.Point(-1, -1); @@ -136,11 +128,90 @@ this.materialCheckBox3.Text = "Controls Always Visible in Mini Player"; this.materialCheckBox3.UseVisualStyleBackColor = true; // + // materialLabel3 + // + this.materialLabel3.AutoSize = true; + this.materialLabel3.Depth = 0; + this.materialLabel3.Font = new System.Drawing.Font("Microsoft Sans Serif", 11F); + this.materialLabel3.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(222)))), ((int)(((byte)(0)))), ((int)(((byte)(0)))), ((int)(((byte)(0))))); + this.materialLabel3.Location = new System.Drawing.Point(12, 248); + this.materialLabel3.MouseState = MaterialSkin.MouseState.HOVER; + this.materialLabel3.Name = "materialLabel3"; + this.materialLabel3.Size = new System.Drawing.Size(115, 18); + this.materialLabel3.TabIndex = 7; + this.materialLabel3.Text = "Last.fm Account"; + // + // lastFMUsername + // + this.lastFMUsername.Depth = 0; + this.lastFMUsername.Hint = ""; + this.lastFMUsername.Location = new System.Drawing.Point(12, 270); + this.lastFMUsername.MaxLength = 32767; + this.lastFMUsername.MouseState = MaterialSkin.MouseState.HOVER; + this.lastFMUsername.Name = "lastFMUsername"; + this.lastFMUsername.PasswordChar = '\0'; + this.lastFMUsername.SelectedText = ""; + this.lastFMUsername.SelectionLength = 0; + this.lastFMUsername.SelectionStart = 0; + this.lastFMUsername.Size = new System.Drawing.Size(283, 23); + this.lastFMUsername.TabIndex = 8; + this.lastFMUsername.TabStop = false; + this.lastFMUsername.Text = "Username"; + this.lastFMUsername.UseSystemPasswordChar = false; + // + // lastFMPassword + // + this.lastFMPassword.Depth = 0; + this.lastFMPassword.Hint = ""; + this.lastFMPassword.Location = new System.Drawing.Point(12, 299); + this.lastFMPassword.MaxLength = 32767; + this.lastFMPassword.MouseState = MaterialSkin.MouseState.HOVER; + this.lastFMPassword.Name = "lastFMPassword"; + this.lastFMPassword.Padding = new System.Windows.Forms.Padding(20, 0, 0, 0); + this.lastFMPassword.PasswordChar = '\0'; + this.lastFMPassword.SelectedText = ""; + this.lastFMPassword.SelectionLength = 0; + this.lastFMPassword.SelectionStart = 0; + this.lastFMPassword.Size = new System.Drawing.Size(283, 23); + this.lastFMPassword.TabIndex = 9; + this.lastFMPassword.TabStop = false; + this.lastFMPassword.Text = "1234567"; + this.lastFMPassword.UseSystemPasswordChar = true; + // + // colorWheel1 + // + this.colorWheel1.Hue = ((byte)(0)); + this.colorWheel1.Lightness = ((byte)(0)); + this.colorWheel1.Location = new System.Drawing.Point(405, 97); + this.colorWheel1.Name = "colorWheel1"; + this.colorWheel1.Saturation = ((byte)(0)); + this.colorWheel1.SecondaryHues = null; + this.colorWheel1.Size = new System.Drawing.Size(183, 191); + this.colorWheel1.TabIndex = 0; + this.colorWheel1.Text = "colorWheel1"; + // + // lastFMAuthIndicator + // + this.lastFMAuthIndicator.AutoSize = true; + this.lastFMAuthIndicator.Depth = 0; + this.lastFMAuthIndicator.Font = new System.Drawing.Font("Microsoft Sans Serif", 8F); + this.lastFMAuthIndicator.ForeColor = System.Drawing.Color.Red; + this.lastFMAuthIndicator.Location = new System.Drawing.Point(29, 325); + this.lastFMAuthIndicator.MouseState = MaterialSkin.MouseState.HOVER; + this.lastFMAuthIndicator.Name = "lastFMAuthIndicator"; + this.lastFMAuthIndicator.Size = new System.Drawing.Size(93, 13); + this.lastFMAuthIndicator.TabIndex = 10; + this.lastFMAuthIndicator.Text = "Not Authenticated"; + // // SettingsDialog // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; - this.ClientSize = new System.Drawing.Size(600, 300); + this.ClientSize = new System.Drawing.Size(600, 400); + this.Controls.Add(this.lastFMAuthIndicator); + this.Controls.Add(this.lastFMPassword); + this.Controls.Add(this.lastFMUsername); + this.Controls.Add(this.materialLabel3); this.Controls.Add(this.materialCheckBox3); this.Controls.Add(this.materialCheckBox2); this.Controls.Add(this.materialRaisedButton1); @@ -167,5 +238,9 @@ private MaterialSkin.Controls.MaterialRaisedButton materialRaisedButton1; private MaterialSkin.Controls.MaterialCheckBox materialCheckBox2; private MaterialSkin.Controls.MaterialCheckBox materialCheckBox3; + private MaterialSkin.Controls.MaterialLabel materialLabel3; + private MaterialSkin.Controls.MaterialSingleLineTextField lastFMUsername; + private MaterialSkin.Controls.MaterialSingleLineTextField lastFMPassword; + private MaterialSkin.Controls.MaterialLabel lastFMAuthIndicator; } } \ No newline at end of file diff --git a/Google Play Music/SettingsDialog.cs b/Google Play Music/SettingsDialog.cs index 6c653f75..809354de 100644 --- a/Google Play Music/SettingsDialog.cs +++ b/Google Play Music/SettingsDialog.cs @@ -82,6 +82,59 @@ namespace Google_Play_Music DialogResult = DialogResult.Abort; Close(); }; + + lastFMUsername.Text = Properties.Settings.Default.LastFMUsername; + lastFMUsername.GotFocus += (res, send) => + { + focusDefaultInputField(lastFMUsername, "Username", true); + }; + lastFMUsername.LostFocus += async (res, send) => + { + focusDefaultInputField(lastFMUsername, "Username", false); + Properties.Settings.Default.LastFMUsername = lastFMUsername.Text; + lastFMAuth(-1); + await new LastFM().init(); + lastFMAuth((LastFM.user_key != null ? 1 : 0)); + }; + lastFMUsername.KeyPress += (send, e) => + { + if (e.KeyChar == (char)13) + { + lastFMPassword.Focus(); + } + }; + + lastFMPassword.Text = Properties.Settings.Default.LastFMPassword; + lastFMPassword.GotFocus += (res, send) => + { + focusDefaultInputField(lastFMPassword, "1234567", true); + }; + lastFMPassword.LostFocus += async (res, send) => + { + focusDefaultInputField(lastFMPassword, "1234567", false); + Properties.Settings.Default.LastFMPassword = lastFMPassword.Text; + lastFMAuth(-1); + await new LastFM().init(); + lastFMAuth((LastFM.user_key != null ? 1 : 0)); + }; + lastFMPassword.KeyPress += (send, e) => + { + if (e.KeyChar == (char)13) + { + lastFMUsername.Focus(); + } + }; + } + + private void focusDefaultInputField(MaterialSingleLineTextField field, string defaultText, bool focus) + { + if (field.Text == defaultText && focus) + { + field.Text = ""; + } else if (field.Text == "" && !focus) + { + field.Text = defaultText; + } } private void Color_Changed(object sender, EventArgs e) @@ -99,10 +152,31 @@ namespace Google_Play_Music { Activated += (res, send) => { + lastFMAuth((LastFM.user_key != null ? 1 : 0)); Location = new Point(X - 300, Y - 125); }; var result = ShowDialog(); return result; } + + private void lastFMAuth(int isAuth) + { + // 1 = Auth Success + // 0 = Auth Failure + // -1 = Auth in Progress + if (isAuth == 1) + { + lastFMAuthIndicator.ForeColor = Color.Green; + lastFMAuthIndicator.Text = "Login Successful"; + } else if (isAuth == 0) + { + lastFMAuthIndicator.ForeColor = Color.Red; + lastFMAuthIndicator.Text = "Login Failed"; + } else if (isAuth == -1) + { + lastFMAuthIndicator.ForeColor = Color.Yellow; + lastFMAuthIndicator.Text = "Logging in..."; + } + } } } diff --git a/Google Play Music/packages.config b/Google Play Music/packages.config index eada7c66..0bc08677 100644 --- a/Google Play Music/packages.config +++ b/Google Play Music/packages.config @@ -4,9 +4,10 @@ - + +