diff --git a/SpotifyAPI.Example/App.config b/SpotifyAPI.Example/App.config deleted file mode 100644 index 1f6eeef1..00000000 --- a/SpotifyAPI.Example/App.config +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/SpotifyAPI.Example/ExampleForm.Designer.cs b/SpotifyAPI.Example/ExampleForm.Designer.cs deleted file mode 100644 index 2cf7ab2f..00000000 --- a/SpotifyAPI.Example/ExampleForm.Designer.cs +++ /dev/null @@ -1,114 +0,0 @@ -namespace SpotifyAPI.Example -{ - partial class ExampleForm - { - /// - /// Erforderliche Designervariable. - /// - private System.ComponentModel.IContainer components = null; - - /// - /// Verwendete Ressourcen bereinigen. - /// - /// True, wenn verwaltete Ressourcen gelöscht werden sollen; andernfalls False. - protected override void Dispose(bool disposing) - { - if (disposing && (components != null)) - { - components.Dispose(); - } - base.Dispose(disposing); - } - - #region Vom Windows Form-Designer generierter Code - - /// - /// Erforderliche Methode für die Designerunterstützung. - /// Der Inhalt der Methode darf nicht mit dem Code-Editor geändert werden. - /// - private void InitializeComponent() - { - this.tabControl1 = new System.Windows.Forms.TabControl(); - this.tabPage1 = new System.Windows.Forms.TabPage(); - this.localControl1 = new SpotifyAPI.Example.LocalControl(); - this.tabPage2 = new System.Windows.Forms.TabPage(); - this.webControl2 = new SpotifyAPI.Example.WebControl(); - this.tabControl1.SuspendLayout(); - this.tabPage1.SuspendLayout(); - this.tabPage2.SuspendLayout(); - this.SuspendLayout(); - // - // tabControl1 - // - this.tabControl1.Controls.Add(this.tabPage1); - this.tabControl1.Controls.Add(this.tabPage2); - this.tabControl1.Dock = System.Windows.Forms.DockStyle.Fill; - this.tabControl1.Location = new System.Drawing.Point(0, 0); - this.tabControl1.Name = "tabControl1"; - this.tabControl1.SelectedIndex = 0; - this.tabControl1.Size = new System.Drawing.Size(984, 702); - this.tabControl1.TabIndex = 0; - // - // tabPage1 - // - this.tabPage1.Controls.Add(this.localControl1); - this.tabPage1.Location = new System.Drawing.Point(4, 22); - this.tabPage1.Name = "tabPage1"; - this.tabPage1.Padding = new System.Windows.Forms.Padding(3); - this.tabPage1.Size = new System.Drawing.Size(976, 676); - this.tabPage1.TabIndex = 0; - this.tabPage1.Text = "Local-API"; - this.tabPage1.UseVisualStyleBackColor = true; - // - // localControl1 - // - this.localControl1.Dock = System.Windows.Forms.DockStyle.Fill; - this.localControl1.Location = new System.Drawing.Point(3, 3); - this.localControl1.Name = "localControl1"; - this.localControl1.Size = new System.Drawing.Size(970, 670); - this.localControl1.TabIndex = 0; - // - // tabPage2 - // - this.tabPage2.Controls.Add(this.webControl2); - this.tabPage2.Location = new System.Drawing.Point(4, 22); - this.tabPage2.Name = "tabPage2"; - this.tabPage2.Padding = new System.Windows.Forms.Padding(3); - this.tabPage2.Size = new System.Drawing.Size(976, 676); - this.tabPage2.TabIndex = 1; - this.tabPage2.Text = "Web-API"; - this.tabPage2.UseVisualStyleBackColor = true; - // - // webControl2 - // - this.webControl2.Dock = System.Windows.Forms.DockStyle.Fill; - this.webControl2.Location = new System.Drawing.Point(3, 3); - this.webControl2.Name = "webControl2"; - this.webControl2.Size = new System.Drawing.Size(970, 670); - this.webControl2.TabIndex = 0; - // - // ExampleForm - // - this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); - this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; - this.ClientSize = new System.Drawing.Size(984, 702); - this.Controls.Add(this.tabControl1); - this.Name = "ExampleForm"; - this.Text = "SpoitfyAPI .NET Example"; - this.tabControl1.ResumeLayout(false); - this.tabPage1.ResumeLayout(false); - this.tabPage2.ResumeLayout(false); - this.ResumeLayout(false); - - } - - #endregion - - private System.Windows.Forms.TabControl tabControl1; - private System.Windows.Forms.TabPage tabPage1; - private System.Windows.Forms.TabPage tabPage2; - private LocalControl localControl1; - private WebControl webControl2; - } -} - diff --git a/SpotifyAPI.Example/ExampleForm.cs b/SpotifyAPI.Example/ExampleForm.cs deleted file mode 100644 index d311c0ef..00000000 --- a/SpotifyAPI.Example/ExampleForm.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System.Windows.Forms; - -namespace SpotifyAPI.Example -{ - public partial class ExampleForm : Form - { - public ExampleForm() - { - InitializeComponent(); - } - } -} \ No newline at end of file diff --git a/SpotifyAPI.Example/ExampleForm.resx b/SpotifyAPI.Example/ExampleForm.resx deleted file mode 100644 index 1af7de15..00000000 --- a/SpotifyAPI.Example/ExampleForm.resx +++ /dev/null @@ -1,120 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - \ No newline at end of file diff --git a/SpotifyAPI.Example/LocalControl.Designer.cs b/SpotifyAPI.Example/LocalControl.Designer.cs deleted file mode 100644 index 0a058cb8..00000000 --- a/SpotifyAPI.Example/LocalControl.Designer.cs +++ /dev/null @@ -1,686 +0,0 @@ -namespace SpotifyAPI.Example -{ - partial class LocalControl - { - /// - /// Erforderliche Designervariable. - /// - private System.ComponentModel.IContainer components = null; - - /// - /// Verwendete Ressourcen bereinigen. - /// - /// True, wenn verwaltete Ressourcen gelöscht werden sollen; andernfalls False. - protected override void Dispose(bool disposing) - { - if (disposing && (components != null)) - { - components.Dispose(); - } - base.Dispose(disposing); - } - - #region Vom Komponenten-Designer generierter Code - - /// - /// Erforderliche Methode für die Designerunterstützung. - /// Der Inhalt der Methode darf nicht mit dem Code-Editor geändert werden. - /// - private void InitializeComponent() - { - this.bigAlbumPicture = new System.Windows.Forms.PictureBox(); - this.groupBox1 = new System.Windows.Forms.GroupBox(); - this.volumeMixerLabel = new System.Windows.Forms.Label(); - this.label9 = new System.Windows.Forms.Label(); - this.volumeUpBtn = new System.Windows.Forms.Button(); - this.volumeDownBtn = new System.Windows.Forms.Button(); - this.repeatShuffleLabel = new System.Windows.Forms.Label(); - this.label6 = new System.Windows.Forms.Label(); - this.versionLabel = new System.Windows.Forms.Label(); - this.label11 = new System.Windows.Forms.Label(); - this.clientVersionLabel = new System.Windows.Forms.Label(); - this.label7 = new System.Windows.Forms.Label(); - this.skipBtn = new System.Windows.Forms.Button(); - this.prevBtn = new System.Windows.Forms.Button(); - this.pauseBtn = new System.Windows.Forms.Button(); - this.playBtn = new System.Windows.Forms.Button(); - this.label1 = new System.Windows.Forms.Label(); - this.contextTextBox = new System.Windows.Forms.TextBox(); - this.playUrlBtn = new System.Windows.Forms.Button(); - this.playTextBox = new System.Windows.Forms.TextBox(); - this.label2 = new System.Windows.Forms.Label(); - this.isPlayingLabel = new System.Windows.Forms.Label(); - this.volumeLabel = new System.Windows.Forms.Label(); - this.label10 = new System.Windows.Forms.Label(); - this.label8 = new System.Windows.Forms.Label(); - this.connectBtn = new System.Windows.Forms.Button(); - this.trackInfoBox = new System.Windows.Forms.GroupBox(); - this.advertLabel = new System.Windows.Forms.Label(); - this.timeLabel = new System.Windows.Forms.Label(); - this.timeProgressBar = new System.Windows.Forms.ProgressBar(); - this.label5 = new System.Windows.Forms.Label(); - this.smallAlbumPicture = new System.Windows.Forms.PictureBox(); - this.label4 = new System.Windows.Forms.Label(); - this.artistLinkLabel = new System.Windows.Forms.LinkLabel(); - this.titleLinkLabel = new System.Windows.Forms.LinkLabel(); - this.label3 = new System.Windows.Forms.Label(); - this.albumLinkLabel = new System.Windows.Forms.LinkLabel(); - this.proxyGroupBox = new System.Windows.Forms.GroupBox(); - this.proxyPortUpDown = new System.Windows.Forms.NumericUpDown(); - this.proxyPasswordTextBox = new System.Windows.Forms.TextBox(); - this.label15 = new System.Windows.Forms.Label(); - this.proxyUsernameTextBox = new System.Windows.Forms.TextBox(); - this.label14 = new System.Windows.Forms.Label(); - this.label13 = new System.Windows.Forms.Label(); - this.proxyHostTextBox = new System.Windows.Forms.TextBox(); - this.label12 = new System.Windows.Forms.Label(); - this.applyProxyBtn = new System.Windows.Forms.Button(); - ((System.ComponentModel.ISupportInitialize)(this.bigAlbumPicture)).BeginInit(); - this.groupBox1.SuspendLayout(); - this.trackInfoBox.SuspendLayout(); - ((System.ComponentModel.ISupportInitialize)(this.smallAlbumPicture)).BeginInit(); - this.proxyGroupBox.SuspendLayout(); - ((System.ComponentModel.ISupportInitialize)(this.proxyPortUpDown)).BeginInit(); - this.SuspendLayout(); - // - // bigAlbumPicture - // - this.bigAlbumPicture.Location = new System.Drawing.Point(407, 93); - this.bigAlbumPicture.Name = "bigAlbumPicture"; - this.bigAlbumPicture.Size = new System.Drawing.Size(560, 560); - this.bigAlbumPicture.SizeMode = System.Windows.Forms.PictureBoxSizeMode.StretchImage; - this.bigAlbumPicture.TabIndex = 2; - this.bigAlbumPicture.TabStop = false; - // - // groupBox1 - // - this.groupBox1.Controls.Add(this.volumeMixerLabel); - this.groupBox1.Controls.Add(this.label9); - this.groupBox1.Controls.Add(this.volumeUpBtn); - this.groupBox1.Controls.Add(this.volumeDownBtn); - this.groupBox1.Controls.Add(this.repeatShuffleLabel); - this.groupBox1.Controls.Add(this.label6); - this.groupBox1.Controls.Add(this.versionLabel); - this.groupBox1.Controls.Add(this.label11); - this.groupBox1.Controls.Add(this.clientVersionLabel); - this.groupBox1.Controls.Add(this.label7); - this.groupBox1.Controls.Add(this.skipBtn); - this.groupBox1.Controls.Add(this.prevBtn); - this.groupBox1.Controls.Add(this.pauseBtn); - this.groupBox1.Controls.Add(this.playBtn); - this.groupBox1.Controls.Add(this.label1); - this.groupBox1.Controls.Add(this.contextTextBox); - this.groupBox1.Controls.Add(this.playUrlBtn); - this.groupBox1.Controls.Add(this.playTextBox); - this.groupBox1.Controls.Add(this.label2); - this.groupBox1.Controls.Add(this.isPlayingLabel); - this.groupBox1.Controls.Add(this.volumeLabel); - this.groupBox1.Controls.Add(this.label10); - this.groupBox1.Controls.Add(this.label8); - this.groupBox1.Font = new System.Drawing.Font("Microsoft Sans Serif", 11F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.groupBox1.Location = new System.Drawing.Point(3, 55); - this.groupBox1.Name = "groupBox1"; - this.groupBox1.Size = new System.Drawing.Size(398, 286); - this.groupBox1.TabIndex = 3; - this.groupBox1.TabStop = false; - this.groupBox1.Text = "Spotify Info"; - // - // volumeMixerLabel - // - this.volumeMixerLabel.AutoSize = true; - this.volumeMixerLabel.Font = new System.Drawing.Font("Microsoft Sans Serif", 10F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.volumeMixerLabel.Location = new System.Drawing.Point(168, 117); - this.volumeMixerLabel.Name = "volumeMixerLabel"; - this.volumeMixerLabel.Size = new System.Drawing.Size(13, 17); - this.volumeMixerLabel.TabIndex = 35; - this.volumeMixerLabel.Text = "-"; - // - // label9 - // - this.label9.AutoSize = true; - this.label9.Font = new System.Drawing.Font("Microsoft Sans Serif", 10F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.label9.Location = new System.Drawing.Point(7, 117); - this.label9.Name = "label9"; - this.label9.Size = new System.Drawing.Size(155, 17); - this.label9.TabIndex = 34; - this.label9.Text = "Volume Mixer\'s volume:"; - // - // volumeUpBtn - // - this.volumeUpBtn.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.volumeUpBtn.Location = new System.Drawing.Point(300, 114); - this.volumeUpBtn.Margin = new System.Windows.Forms.Padding(0); - this.volumeUpBtn.Name = "volumeUpBtn"; - this.volumeUpBtn.Size = new System.Drawing.Size(92, 24); - this.volumeUpBtn.TabIndex = 33; - this.volumeUpBtn.Text = "Volume+"; - this.volumeUpBtn.UseVisualStyleBackColor = true; - this.volumeUpBtn.Click += new System.EventHandler(this.volumeUpBtn_Click); - // - // volumeDownBtn - // - this.volumeDownBtn.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.volumeDownBtn.Location = new System.Drawing.Point(300, 138); - this.volumeDownBtn.Margin = new System.Windows.Forms.Padding(0); - this.volumeDownBtn.Name = "volumeDownBtn"; - this.volumeDownBtn.Size = new System.Drawing.Size(92, 24); - this.volumeDownBtn.TabIndex = 32; - this.volumeDownBtn.Text = "Volume-"; - this.volumeDownBtn.UseVisualStyleBackColor = true; - this.volumeDownBtn.Click += new System.EventHandler(this.volumeDownBtn_Click); - // - // repeatShuffleLabel - // - this.repeatShuffleLabel.AutoSize = true; - this.repeatShuffleLabel.Font = new System.Drawing.Font("Microsoft Sans Serif", 10F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.repeatShuffleLabel.Location = new System.Drawing.Point(146, 88); - this.repeatShuffleLabel.Name = "repeatShuffleLabel"; - this.repeatShuffleLabel.Size = new System.Drawing.Size(13, 17); - this.repeatShuffleLabel.TabIndex = 30; - this.repeatShuffleLabel.Text = "-"; - // - // label6 - // - this.label6.AutoSize = true; - this.label6.Font = new System.Drawing.Font("Microsoft Sans Serif", 10F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.label6.Location = new System.Drawing.Point(6, 88); - this.label6.Name = "label6"; - this.label6.Size = new System.Drawing.Size(134, 17); - this.label6.TabIndex = 29; - this.label6.Text = "Repeat and Shuffle:"; - // - // versionLabel - // - this.versionLabel.AutoSize = true; - this.versionLabel.Font = new System.Drawing.Font("Microsoft Sans Serif", 10F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.versionLabel.Location = new System.Drawing.Point(72, 37); - this.versionLabel.Name = "versionLabel"; - this.versionLabel.Size = new System.Drawing.Size(13, 17); - this.versionLabel.TabIndex = 28; - this.versionLabel.Text = "-"; - // - // label11 - // - this.label11.AutoSize = true; - this.label11.Font = new System.Drawing.Font("Microsoft Sans Serif", 10F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.label11.Location = new System.Drawing.Point(6, 37); - this.label11.Name = "label11"; - this.label11.Size = new System.Drawing.Size(60, 17); - this.label11.TabIndex = 27; - this.label11.Text = "Version:"; - // - // clientVersionLabel - // - this.clientVersionLabel.AutoSize = true; - this.clientVersionLabel.Font = new System.Drawing.Font("Microsoft Sans Serif", 10F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.clientVersionLabel.Location = new System.Drawing.Point(108, 20); - this.clientVersionLabel.Name = "clientVersionLabel"; - this.clientVersionLabel.Size = new System.Drawing.Size(13, 17); - this.clientVersionLabel.TabIndex = 26; - this.clientVersionLabel.Text = "-"; - // - // label7 - // - this.label7.AutoSize = true; - this.label7.Font = new System.Drawing.Font("Microsoft Sans Serif", 10F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.label7.Location = new System.Drawing.Point(6, 20); - this.label7.Name = "label7"; - this.label7.Size = new System.Drawing.Size(96, 17); - this.label7.TabIndex = 25; - this.label7.Text = "Client-Version"; - // - // skipBtn - // - this.skipBtn.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.skipBtn.Location = new System.Drawing.Point(300, 252); - this.skipBtn.Name = "skipBtn"; - this.skipBtn.Size = new System.Drawing.Size(92, 23); - this.skipBtn.TabIndex = 24; - this.skipBtn.Text = "Skip"; - this.skipBtn.UseVisualStyleBackColor = true; - this.skipBtn.Click += new System.EventHandler(this.skipBtn_Click); - // - // prevBtn - // - this.prevBtn.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.prevBtn.Location = new System.Drawing.Point(202, 252); - this.prevBtn.Name = "prevBtn"; - this.prevBtn.Size = new System.Drawing.Size(92, 23); - this.prevBtn.TabIndex = 23; - this.prevBtn.Text = "Previous"; - this.prevBtn.UseVisualStyleBackColor = true; - this.prevBtn.Click += new System.EventHandler(this.prevBtn_Click); - // - // pauseBtn - // - this.pauseBtn.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.pauseBtn.Location = new System.Drawing.Point(104, 252); - this.pauseBtn.Name = "pauseBtn"; - this.pauseBtn.Size = new System.Drawing.Size(92, 23); - this.pauseBtn.TabIndex = 22; - this.pauseBtn.Text = "Pause"; - this.pauseBtn.UseVisualStyleBackColor = true; - this.pauseBtn.Click += new System.EventHandler(this.pauseBtn_Click); - // - // playBtn - // - this.playBtn.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.playBtn.Location = new System.Drawing.Point(6, 252); - this.playBtn.Name = "playBtn"; - this.playBtn.Size = new System.Drawing.Size(92, 23); - this.playBtn.TabIndex = 21; - this.playBtn.Text = "Play"; - this.playBtn.UseVisualStyleBackColor = true; - this.playBtn.Click += new System.EventHandler(this.playBtn_Click); - // - // label1 - // - this.label1.AutoSize = true; - this.label1.Font = new System.Drawing.Font("Microsoft Sans Serif", 10F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.label1.Location = new System.Drawing.Point(6, 206); - this.label1.Name = "label1"; - this.label1.Size = new System.Drawing.Size(110, 17); - this.label1.TabIndex = 20; - this.label1.Text = "Playing-Context:"; - // - // contextTextBox - // - this.contextTextBox.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.contextTextBox.Location = new System.Drawing.Point(9, 226); - this.contextTextBox.Name = "contextTextBox"; - this.contextTextBox.Size = new System.Drawing.Size(285, 20); - this.contextTextBox.TabIndex = 19; - // - // playUrlBtn - // - this.playUrlBtn.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.playUrlBtn.Location = new System.Drawing.Point(300, 165); - this.playUrlBtn.Name = "playUrlBtn"; - this.playUrlBtn.Size = new System.Drawing.Size(92, 81); - this.playUrlBtn.TabIndex = 18; - this.playUrlBtn.Text = "PlayURL"; - this.playUrlBtn.UseVisualStyleBackColor = true; - this.playUrlBtn.Click += new System.EventHandler(this.playUrlBtn_Click); - // - // playTextBox - // - this.playTextBox.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.playTextBox.Location = new System.Drawing.Point(6, 185); - this.playTextBox.Name = "playTextBox"; - this.playTextBox.Size = new System.Drawing.Size(288, 20); - this.playTextBox.TabIndex = 17; - this.playTextBox.Text = "https://open.spotify.com/track/4myBMnNWZlgvVelYeTu55w"; - // - // label2 - // - this.label2.AutoSize = true; - this.label2.Font = new System.Drawing.Font("Microsoft Sans Serif", 10F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.label2.Location = new System.Drawing.Point(6, 165); - this.label2.Name = "label2"; - this.label2.Size = new System.Drawing.Size(131, 17); - this.label2.TabIndex = 16; - this.label2.Text = "Spotify URI or URL:"; - // - // isPlayingLabel - // - this.isPlayingLabel.AutoSize = true; - this.isPlayingLabel.Font = new System.Drawing.Font("Microsoft Sans Serif", 10F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.isPlayingLabel.Location = new System.Drawing.Point(84, 71); - this.isPlayingLabel.Name = "isPlayingLabel"; - this.isPlayingLabel.Size = new System.Drawing.Size(13, 17); - this.isPlayingLabel.TabIndex = 14; - this.isPlayingLabel.Text = "-"; - // - // volumeLabel - // - this.volumeLabel.AutoSize = true; - this.volumeLabel.Font = new System.Drawing.Font("Microsoft Sans Serif", 10F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.volumeLabel.Location = new System.Drawing.Point(71, 54); - this.volumeLabel.Name = "volumeLabel"; - this.volumeLabel.Size = new System.Drawing.Size(13, 17); - this.volumeLabel.TabIndex = 13; - this.volumeLabel.Text = "-"; - // - // label10 - // - this.label10.AutoSize = true; - this.label10.Font = new System.Drawing.Font("Microsoft Sans Serif", 10F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.label10.Location = new System.Drawing.Point(6, 54); - this.label10.Name = "label10"; - this.label10.Size = new System.Drawing.Size(59, 17); - this.label10.TabIndex = 11; - this.label10.Text = "Volume:"; - // - // label8 - // - this.label8.AutoSize = true; - this.label8.Font = new System.Drawing.Font("Microsoft Sans Serif", 10F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.label8.Location = new System.Drawing.Point(6, 71); - this.label8.Name = "label8"; - this.label8.Size = new System.Drawing.Size(72, 17); - this.label8.TabIndex = 10; - this.label8.Text = "Is Playing:"; - // - // connectBtn - // - this.connectBtn.Location = new System.Drawing.Point(3, 13); - this.connectBtn.Name = "connectBtn"; - this.connectBtn.Size = new System.Drawing.Size(398, 36); - this.connectBtn.TabIndex = 4; - this.connectBtn.Text = "Connect to Spotify"; - this.connectBtn.UseVisualStyleBackColor = true; - this.connectBtn.Click += new System.EventHandler(this.connectBtn_Click); - // - // trackInfoBox - // - this.trackInfoBox.Controls.Add(this.advertLabel); - this.trackInfoBox.Controls.Add(this.timeLabel); - this.trackInfoBox.Controls.Add(this.timeProgressBar); - this.trackInfoBox.Controls.Add(this.label5); - this.trackInfoBox.Controls.Add(this.smallAlbumPicture); - this.trackInfoBox.Controls.Add(this.label4); - this.trackInfoBox.Controls.Add(this.artistLinkLabel); - this.trackInfoBox.Controls.Add(this.titleLinkLabel); - this.trackInfoBox.Controls.Add(this.label3); - this.trackInfoBox.Controls.Add(this.albumLinkLabel); - this.trackInfoBox.Font = new System.Drawing.Font("Microsoft Sans Serif", 10F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.trackInfoBox.Location = new System.Drawing.Point(3, 347); - this.trackInfoBox.Name = "trackInfoBox"; - this.trackInfoBox.Size = new System.Drawing.Size(398, 306); - this.trackInfoBox.TabIndex = 4; - this.trackInfoBox.TabStop = false; - this.trackInfoBox.Text = "Track Info"; - // - // advertLabel - // - this.advertLabel.AutoSize = true; - this.advertLabel.Location = new System.Drawing.Point(6, 67); - this.advertLabel.Name = "advertLabel"; - this.advertLabel.Size = new System.Drawing.Size(0, 17); - this.advertLabel.TabIndex = 31; - // - // timeLabel - // - this.timeLabel.AutoSize = true; - this.timeLabel.Font = new System.Drawing.Font("Microsoft Sans Serif", 10F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.timeLabel.Location = new System.Drawing.Point(7, 281); - this.timeLabel.Name = "timeLabel"; - this.timeLabel.Size = new System.Drawing.Size(13, 17); - this.timeLabel.TabIndex = 29; - this.timeLabel.Text = "-"; - // - // timeProgressBar - // - this.timeProgressBar.Location = new System.Drawing.Point(6, 255); - this.timeProgressBar.Name = "timeProgressBar"; - this.timeProgressBar.Size = new System.Drawing.Size(386, 23); - this.timeProgressBar.TabIndex = 28; - // - // label5 - // - this.label5.AutoSize = true; - this.label5.Font = new System.Drawing.Font("Microsoft Sans Serif", 10F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.label5.Location = new System.Drawing.Point(10, 227); - this.label5.Name = "label5"; - this.label5.Size = new System.Drawing.Size(51, 17); - this.label5.TabIndex = 27; - this.label5.Text = "Album:"; - // - // smallAlbumPicture - // - this.smallAlbumPicture.Location = new System.Drawing.Point(123, 22); - this.smallAlbumPicture.Name = "smallAlbumPicture"; - this.smallAlbumPicture.Size = new System.Drawing.Size(150, 150); - this.smallAlbumPicture.SizeMode = System.Windows.Forms.PictureBoxSizeMode.StretchImage; - this.smallAlbumPicture.TabIndex = 5; - this.smallAlbumPicture.TabStop = false; - // - // label4 - // - this.label4.AutoSize = true; - this.label4.Font = new System.Drawing.Font("Microsoft Sans Serif", 10F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.label4.Location = new System.Drawing.Point(17, 204); - this.label4.Name = "label4"; - this.label4.Size = new System.Drawing.Size(44, 17); - this.label4.TabIndex = 26; - this.label4.Text = "Artist:"; - // - // artistLinkLabel - // - this.artistLinkLabel.AutoSize = true; - this.artistLinkLabel.Font = new System.Drawing.Font("Microsoft Sans Serif", 10F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.artistLinkLabel.Location = new System.Drawing.Point(67, 204); - this.artistLinkLabel.Name = "artistLinkLabel"; - this.artistLinkLabel.Size = new System.Drawing.Size(13, 17); - this.artistLinkLabel.TabIndex = 6; - this.artistLinkLabel.TabStop = true; - this.artistLinkLabel.Text = "-"; - this.artistLinkLabel.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; - // - // titleLinkLabel - // - this.titleLinkLabel.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) - | System.Windows.Forms.AnchorStyles.Left) - | System.Windows.Forms.AnchorStyles.Right))); - this.titleLinkLabel.AutoSize = true; - this.titleLinkLabel.Font = new System.Drawing.Font("Microsoft Sans Serif", 10F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.titleLinkLabel.Location = new System.Drawing.Point(67, 182); - this.titleLinkLabel.Name = "titleLinkLabel"; - this.titleLinkLabel.Size = new System.Drawing.Size(13, 17); - this.titleLinkLabel.TabIndex = 5; - this.titleLinkLabel.TabStop = true; - this.titleLinkLabel.Text = "-"; - this.titleLinkLabel.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; - // - // label3 - // - this.label3.AutoSize = true; - this.label3.Font = new System.Drawing.Font("Microsoft Sans Serif", 10F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.label3.Location = new System.Drawing.Point(22, 182); - this.label3.Name = "label3"; - this.label3.Size = new System.Drawing.Size(39, 17); - this.label3.TabIndex = 25; - this.label3.Text = "Title:"; - // - // albumLinkLabel - // - this.albumLinkLabel.AutoSize = true; - this.albumLinkLabel.Font = new System.Drawing.Font("Microsoft Sans Serif", 10F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.albumLinkLabel.Location = new System.Drawing.Point(67, 227); - this.albumLinkLabel.Name = "albumLinkLabel"; - this.albumLinkLabel.Size = new System.Drawing.Size(13, 17); - this.albumLinkLabel.TabIndex = 7; - this.albumLinkLabel.TabStop = true; - this.albumLinkLabel.Text = "-"; - this.albumLinkLabel.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; - // - // proxyGroupBox - // - this.proxyGroupBox.Controls.Add(this.proxyPortUpDown); - this.proxyGroupBox.Controls.Add(this.proxyPasswordTextBox); - this.proxyGroupBox.Controls.Add(this.label15); - this.proxyGroupBox.Controls.Add(this.proxyUsernameTextBox); - this.proxyGroupBox.Controls.Add(this.label14); - this.proxyGroupBox.Controls.Add(this.label13); - this.proxyGroupBox.Controls.Add(this.proxyHostTextBox); - this.proxyGroupBox.Controls.Add(this.label12); - this.proxyGroupBox.Controls.Add(this.applyProxyBtn); - this.proxyGroupBox.Font = new System.Drawing.Font("Microsoft Sans Serif", 11F); - this.proxyGroupBox.Location = new System.Drawing.Point(407, 10); - this.proxyGroupBox.Name = "proxyGroupBox"; - this.proxyGroupBox.Size = new System.Drawing.Size(560, 77); - this.proxyGroupBox.TabIndex = 5; - this.proxyGroupBox.TabStop = false; - this.proxyGroupBox.Text = "Proxy Config"; - // - // proxyPortUpDown - // - this.proxyPortUpDown.Location = new System.Drawing.Point(324, 18); - this.proxyPortUpDown.Maximum = new decimal(new int[] { - 65535, - 0, - 0, - 0}); - this.proxyPortUpDown.Minimum = new decimal(new int[] { - 1, - 0, - 0, - 0}); - this.proxyPortUpDown.Name = "proxyPortUpDown"; - this.proxyPortUpDown.Size = new System.Drawing.Size(157, 24); - this.proxyPortUpDown.TabIndex = 6; - this.proxyPortUpDown.Value = new decimal(new int[] { - 80, - 0, - 0, - 0}); - // - // proxyPasswordTextBox - // - this.proxyPasswordTextBox.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.proxyPasswordTextBox.Location = new System.Drawing.Point(359, 47); - this.proxyPasswordTextBox.Name = "proxyPasswordTextBox"; - this.proxyPasswordTextBox.Size = new System.Drawing.Size(122, 20); - this.proxyPasswordTextBox.TabIndex = 42; - // - // label15 - // - this.label15.AutoSize = true; - this.label15.Font = new System.Drawing.Font("Microsoft Sans Serif", 10F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.label15.Location = new System.Drawing.Point(280, 48); - this.label15.Name = "label15"; - this.label15.Size = new System.Drawing.Size(73, 17); - this.label15.TabIndex = 41; - this.label15.Text = "Password:"; - // - // proxyUsernameTextBox - // - this.proxyUsernameTextBox.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.proxyUsernameTextBox.Location = new System.Drawing.Point(89, 47); - this.proxyUsernameTextBox.Name = "proxyUsernameTextBox"; - this.proxyUsernameTextBox.Size = new System.Drawing.Size(185, 20); - this.proxyUsernameTextBox.TabIndex = 40; - // - // label14 - // - this.label14.AutoSize = true; - this.label14.Font = new System.Drawing.Font("Microsoft Sans Serif", 10F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.label14.Location = new System.Drawing.Point(6, 48); - this.label14.Name = "label14"; - this.label14.Size = new System.Drawing.Size(77, 17); - this.label14.TabIndex = 39; - this.label14.Text = "Username:"; - // - // label13 - // - this.label13.AutoSize = true; - this.label13.Font = new System.Drawing.Font("Microsoft Sans Serif", 10F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.label13.Location = new System.Drawing.Point(280, 22); - this.label13.Name = "label13"; - this.label13.Size = new System.Drawing.Size(38, 17); - this.label13.TabIndex = 37; - this.label13.Text = "Port:"; - // - // proxyHostTextBox - // - this.proxyHostTextBox.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.proxyHostTextBox.Location = new System.Drawing.Point(49, 21); - this.proxyHostTextBox.Name = "proxyHostTextBox"; - this.proxyHostTextBox.Size = new System.Drawing.Size(225, 20); - this.proxyHostTextBox.TabIndex = 36; - // - // label12 - // - this.label12.AutoSize = true; - this.label12.Font = new System.Drawing.Font("Microsoft Sans Serif", 10F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.label12.Location = new System.Drawing.Point(6, 22); - this.label12.Name = "label12"; - this.label12.Size = new System.Drawing.Size(41, 17); - this.label12.TabIndex = 36; - this.label12.Text = "Host:"; - // - // applyProxyBtn - // - this.applyProxyBtn.Location = new System.Drawing.Point(487, 15); - this.applyProxyBtn.Name = "applyProxyBtn"; - this.applyProxyBtn.Size = new System.Drawing.Size(67, 56); - this.applyProxyBtn.TabIndex = 0; - this.applyProxyBtn.Text = "Apply"; - this.applyProxyBtn.UseVisualStyleBackColor = true; - this.applyProxyBtn.Click += new System.EventHandler(this.applyProxyBtn_Click); - // - // LocalControl - // - this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); - this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; - this.Controls.Add(this.proxyGroupBox); - this.Controls.Add(this.trackInfoBox); - this.Controls.Add(this.connectBtn); - this.Controls.Add(this.groupBox1); - this.Controls.Add(this.bigAlbumPicture); - this.Name = "LocalControl"; - this.Size = new System.Drawing.Size(970, 670); - ((System.ComponentModel.ISupportInitialize)(this.bigAlbumPicture)).EndInit(); - this.groupBox1.ResumeLayout(false); - this.groupBox1.PerformLayout(); - this.trackInfoBox.ResumeLayout(false); - this.trackInfoBox.PerformLayout(); - ((System.ComponentModel.ISupportInitialize)(this.smallAlbumPicture)).EndInit(); - this.proxyGroupBox.ResumeLayout(false); - this.proxyGroupBox.PerformLayout(); - ((System.ComponentModel.ISupportInitialize)(this.proxyPortUpDown)).EndInit(); - this.ResumeLayout(false); - - } - - #endregion - - private System.Windows.Forms.PictureBox bigAlbumPicture; - private System.Windows.Forms.GroupBox groupBox1; - private System.Windows.Forms.Button connectBtn; - private System.Windows.Forms.GroupBox trackInfoBox; - private System.Windows.Forms.PictureBox smallAlbumPicture; - private System.Windows.Forms.Label label10; - private System.Windows.Forms.Label label8; - private System.Windows.Forms.Label volumeLabel; - private System.Windows.Forms.Label isPlayingLabel; - private System.Windows.Forms.Label label2; - private System.Windows.Forms.TextBox playTextBox; - private System.Windows.Forms.Button playUrlBtn; - private System.Windows.Forms.TextBox contextTextBox; - private System.Windows.Forms.Label label1; - private System.Windows.Forms.Button playBtn; - private System.Windows.Forms.Button skipBtn; - private System.Windows.Forms.Button prevBtn; - private System.Windows.Forms.Button pauseBtn; - private System.Windows.Forms.LinkLabel artistLinkLabel; - private System.Windows.Forms.LinkLabel titleLinkLabel; - private System.Windows.Forms.LinkLabel albumLinkLabel; - private System.Windows.Forms.Label label5; - private System.Windows.Forms.Label label4; - private System.Windows.Forms.Label label3; - private System.Windows.Forms.Label timeLabel; - private System.Windows.Forms.ProgressBar timeProgressBar; - private System.Windows.Forms.Label clientVersionLabel; - private System.Windows.Forms.Label label7; - private System.Windows.Forms.Label versionLabel; - private System.Windows.Forms.Label label11; - private System.Windows.Forms.Label label6; - private System.Windows.Forms.Label repeatShuffleLabel; - private System.Windows.Forms.Label advertLabel; - private System.Windows.Forms.Button volumeUpBtn; - private System.Windows.Forms.Button volumeDownBtn; - private System.Windows.Forms.Label volumeMixerLabel; - private System.Windows.Forms.Label label9; - private System.Windows.Forms.GroupBox proxyGroupBox; - private System.Windows.Forms.TextBox proxyPasswordTextBox; - private System.Windows.Forms.Label label15; - private System.Windows.Forms.TextBox proxyUsernameTextBox; - private System.Windows.Forms.Label label14; - private System.Windows.Forms.Label label13; - private System.Windows.Forms.TextBox proxyHostTextBox; - private System.Windows.Forms.Label label12; - private System.Windows.Forms.Button applyProxyBtn; - private System.Windows.Forms.NumericUpDown proxyPortUpDown; - } -} diff --git a/SpotifyAPI.Example/LocalControl.cs b/SpotifyAPI.Example/LocalControl.cs deleted file mode 100644 index 31ed6af6..00000000 --- a/SpotifyAPI.Example/LocalControl.cs +++ /dev/null @@ -1,251 +0,0 @@ -using SpotifyAPI.Local; -using SpotifyAPI.Local.Enums; -using SpotifyAPI.Local.Models; -using System; -using System.Diagnostics; -using System.Globalization; -using System.Windows.Forms; - -namespace SpotifyAPI.Example -{ - public partial class LocalControl : UserControl - { - private readonly SpotifyLocalAPIConfig _config; - private SpotifyLocalAPI _spotify; - private Track _currentTrack; - - public LocalControl() - { - InitializeComponent(); - - _config = new SpotifyLocalAPIConfig - { - ProxyConfig = new ProxyConfig() - }; - - _spotify = new SpotifyLocalAPI(_config); - _spotify.OnPlayStateChange += _spotify_OnPlayStateChange; - _spotify.OnTrackChange += _spotify_OnTrackChange; - _spotify.OnTrackTimeChange += _spotify_OnTrackTimeChange; - _spotify.OnVolumeChange += _spotify_OnVolumeChange; - //_spotify.SynchronizingObject = this; - - artistLinkLabel.Click += (sender, args) => Process.Start(artistLinkLabel.Tag.ToString()); - albumLinkLabel.Click += (sender, args) => Process.Start(albumLinkLabel.Tag.ToString()); - titleLinkLabel.Click += (sender, args) => Process.Start(titleLinkLabel.Tag.ToString()); - } - - public void Connect() - { - if (!SpotifyLocalAPI.IsSpotifyRunning()) - { - MessageBox.Show(@"Spotify isn't running!"); - return; - } - if (!SpotifyLocalAPI.IsSpotifyWebHelperRunning()) - { - MessageBox.Show(@"SpotifyWebHelper isn't running!"); - return; - } - - bool successful = _spotify.Connect(); - if (successful) - { - connectBtn.Text = @"Connection to Spotify successful"; - connectBtn.Enabled = false; - UpdateInfos(); - _spotify.ListenForEvents = true; - } - else - { - DialogResult res = MessageBox.Show(@"Couldn't connect to the spotify client. Retry?", @"Spotify", MessageBoxButtons.YesNo); - if (res == DialogResult.Yes) - Connect(); - } - } - - public void UpdateInfos() - { - StatusResponse status = _spotify.GetStatus(); - if (status == null) - return; - - //Basic Spotify Infos - UpdatePlayingStatus(status.Playing); - clientVersionLabel.Text = status.ClientVersion; - versionLabel.Text = status.Version.ToString(); - repeatShuffleLabel.Text = status.Repeat + @" and " + status.Shuffle; - - if (status.Track != null) //Update track infos - UpdateTrack(status.Track); - - RefreshVolumeMixerVolume(); - } - - public async void UpdateTrack(Track track) - { - _currentTrack = track; - - advertLabel.Text = track.IsAd() ? "ADVERT" : ""; - timeProgressBar.Maximum = track.Length; - - if (track.IsAd()) - return; //Don't process further, maybe null values - - titleLinkLabel.Text = track.TrackResource?.Name; - titleLinkLabel.Tag = track.TrackResource?.Uri; - - artistLinkLabel.Text = track.ArtistResource?.Name; - artistLinkLabel.Tag = track.ArtistResource?.Uri; - - albumLinkLabel.Text = track.AlbumResource?.Name; - albumLinkLabel.Tag = track.AlbumResource?.Uri; - - SpotifyUri uri = track.TrackResource?.ParseUri(); - - trackInfoBox.Text = $@"Track Info - {uri?.Id}"; - - bigAlbumPicture.Image = track.AlbumResource != null ? await track.GetAlbumArtAsync(AlbumArtSize.Size640, _config.ProxyConfig) : null; - smallAlbumPicture.Image = track.AlbumResource != null ? await track.GetAlbumArtAsync(AlbumArtSize.Size160, _config.ProxyConfig) : null; - } - - public void UpdatePlayingStatus(bool playing) - { - isPlayingLabel.Text = playing.ToString(); - } - - public void RefreshVolumeMixerVolume() - { - volumeMixerLabel.Text = _spotify.GetSpotifyVolume().ToString(CultureInfo.InvariantCulture); - } - - private void applyProxyBtn_Click(object sender, EventArgs e) - { - _config.ProxyConfig.Host = proxyHostTextBox.Text; - _config.ProxyConfig.Port = (int)proxyPortUpDown.Value; - _config.ProxyConfig.Username = proxyUsernameTextBox.Text; - _config.ProxyConfig.Password = proxyPasswordTextBox.Text; - - bool connected = _spotify.ListenForEvents; - if (connected) - { - // Reconnect using new proxy - _spotify.ListenForEvents = false; - _spotify.OnPlayStateChange -= _spotify_OnPlayStateChange; - _spotify.OnTrackChange -= _spotify_OnTrackChange; - _spotify.OnTrackTimeChange -= _spotify_OnTrackTimeChange; - _spotify.OnVolumeChange -= _spotify_OnVolumeChange; - - _spotify.Dispose(); - - _spotify = new SpotifyLocalAPI(_config); - _spotify.OnPlayStateChange += _spotify_OnPlayStateChange; - _spotify.OnTrackChange += _spotify_OnTrackChange; - _spotify.OnTrackTimeChange += _spotify_OnTrackTimeChange; - _spotify.OnVolumeChange += _spotify_OnVolumeChange; - - connectBtn.Text = @"Reconnecting..."; - Connect(); - } - } - - private void _spotify_OnVolumeChange(object sender, VolumeChangeEventArgs e) - { - if (InvokeRequired) - { - Invoke(new Action(() => _spotify_OnVolumeChange(sender, e))); - return; - } - volumeLabel.Text = (e.NewVolume * 100).ToString(CultureInfo.InvariantCulture); - } - - private void _spotify_OnTrackTimeChange(object sender, TrackTimeChangeEventArgs e) - { - if (InvokeRequired) - { - Invoke(new Action(() => _spotify_OnTrackTimeChange(sender, e))); - return; - } - timeLabel.Text = $@"{FormatTime(e.TrackTime)}/{FormatTime(_currentTrack.Length)}"; - if(e.TrackTime < _currentTrack.Length) - timeProgressBar.Value = (int)e.TrackTime; - } - - private void _spotify_OnTrackChange(object sender, TrackChangeEventArgs e) - { - if (InvokeRequired) - { - Invoke(new Action(() => _spotify_OnTrackChange(sender, e))); - return; - } - UpdateTrack(e.NewTrack); - } - - private void _spotify_OnPlayStateChange(object sender, PlayStateEventArgs e) - { - if (InvokeRequired) - { - Invoke(new Action(() => _spotify_OnPlayStateChange(sender, e))); - return; - } - UpdatePlayingStatus(e.Playing); - } - - private void connectBtn_Click(object sender, EventArgs e) - { - Connect(); - } - - private async void playUrlBtn_Click(object sender, EventArgs e) - { - await _spotify.PlayURL(playTextBox.Text, contextTextBox.Text); - } - - private async void playBtn_Click(object sender, EventArgs e) - { - await _spotify.Play(); - } - - private async void pauseBtn_Click(object sender, EventArgs e) - { - await _spotify.Pause(); - } - - private void prevBtn_Click(object sender, EventArgs e) - { - _spotify.Previous(); - } - - private void skipBtn_Click(object sender, EventArgs e) - { - _spotify.Skip(); - } - - private void volumeUpBtn_Click(object sender, EventArgs e) - { - float currentVolume = _spotify.GetSpotifyVolume(); - float newVolume = currentVolume + 2.0f; - _spotify.SetSpotifyVolume(newVolume >= 100.0f ? 100.0f : newVolume); - - RefreshVolumeMixerVolume(); - } - - private void volumeDownBtn_Click(object sender, EventArgs e) - { - float currentVolume = _spotify.GetSpotifyVolume(); - float newVolume = currentVolume - 2.0f; - _spotify.SetSpotifyVolume(newVolume <= 0.0f ? 0.0f : newVolume); - - RefreshVolumeMixerVolume(); - } - - private static String FormatTime(double sec) - { - TimeSpan span = TimeSpan.FromSeconds(sec); - String secs = span.Seconds.ToString(), mins = span.Minutes.ToString(); - if (secs.Length < 2) - secs = "0" + secs; - return mins + ":" + secs; - } - } -} \ No newline at end of file diff --git a/SpotifyAPI.Example/LocalControl.resx b/SpotifyAPI.Example/LocalControl.resx deleted file mode 100644 index 1af7de15..00000000 --- a/SpotifyAPI.Example/LocalControl.resx +++ /dev/null @@ -1,120 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - \ No newline at end of file diff --git a/SpotifyAPI.Example/Program.cs b/SpotifyAPI.Example/Program.cs deleted file mode 100644 index bdfac8ea..00000000 --- a/SpotifyAPI.Example/Program.cs +++ /dev/null @@ -1,19 +0,0 @@ -using System; -using System.Windows.Forms; - -namespace SpotifyAPI.Example -{ - internal static class Program - { - /// - /// Der Haupteinstiegspunkt für die Anwendung. - /// - [STAThread] - private static void Main() - { - Application.EnableVisualStyles(); - Application.SetCompatibleTextRenderingDefault(false); - Application.Run(new ExampleForm()); - } - } -} \ No newline at end of file diff --git a/SpotifyAPI.Example/Properties/AssemblyInfo.cs b/SpotifyAPI.Example/Properties/AssemblyInfo.cs deleted file mode 100644 index 2dad8ee7..00000000 --- a/SpotifyAPI.Example/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,35 +0,0 @@ -using System.Reflection; -using System.Runtime.InteropServices; - -// Allgemeine Informationen über eine Assembly werden über die folgenden -// Attribute gesteuert. Ändern Sie diese Attributwerte, um die Informationen zu ändern, -// die mit einer Assembly verknüpft sind. -[assembly: AssemblyTitle("SpotifyAPI.Example")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("SpotifyAPI.Example")] -[assembly: AssemblyCopyright("Copyright © 2015")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// Durch Festlegen von ComVisible auf "false" werden die Typen in dieser Assembly unsichtbar -// für COM-Komponenten. Wenn Sie auf einen Typ in dieser Assembly von -// COM zugreifen müssen, legen Sie das ComVisible-Attribut für diesen Typ auf "true" fest. -[assembly: ComVisible(false)] - -// Die folgende GUID bestimmt die ID der Typbibliothek, wenn dieses Projekt für COM verfügbar gemacht wird -[assembly: Guid("22aa2afd-11be-4d66-b7d8-c337877ce14b")] - -// Versionsinformationen für eine Assembly bestehen aus den folgenden vier Werten: -// -// Hauptversion -// Nebenversion -// Buildnummer -// Revision -// -// Sie können alle Werte angeben oder die standardmäßigen Build- und Revisionsnummern -// übernehmen, indem Sie "*" eingeben: -// [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] \ No newline at end of file diff --git a/SpotifyAPI.Example/Properties/Resources.Designer.cs b/SpotifyAPI.Example/Properties/Resources.Designer.cs deleted file mode 100644 index 92913fed..00000000 --- a/SpotifyAPI.Example/Properties/Resources.Designer.cs +++ /dev/null @@ -1,71 +0,0 @@ -//------------------------------------------------------------------------------ -// -// Dieser Code wurde von einem Tool generiert. -// Laufzeitversion:4.0.30319.34209 -// -// Änderungen an dieser Datei können fehlerhaftes Verhalten verursachen und gehen verloren, wenn -// der Code neu generiert wird. -// -//------------------------------------------------------------------------------ - -namespace SpotifyAPI.Example.Properties -{ - - - /// - /// Eine stark typisierte Ressourcenklasse zum Suchen von lokalisierten Zeichenfolgen usw. - /// - // Diese Klasse wurde von der StronglyTypedResourceBuilder-Klasse - // über ein Tool wie ResGen oder Visual Studio automatisch generiert. - // Um einen Member hinzuzufügen oder zu entfernen, bearbeiten Sie die .ResX-Datei und führen dann ResGen - // mit der Option /str erneut aus, oder erstellen Sie Ihr VS-Projekt neu. - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - internal class Resources - { - - private static global::System.Resources.ResourceManager resourceMan; - - private static global::System.Globalization.CultureInfo resourceCulture; - - [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] - internal Resources() - { - } - - /// - /// Gibt die zwischengespeicherte ResourceManager-Instanz zurück, die von dieser Klasse verwendet wird. - /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - internal static global::System.Resources.ResourceManager ResourceManager - { - get - { - if ((resourceMan == null)) - { - global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("SpotifyAPI.Example.Properties.Resources", typeof(Resources).Assembly); - resourceMan = temp; - } - return resourceMan; - } - } - - /// - /// Überschreibt die CurrentUICulture-Eigenschaft des aktuellen Threads für alle - /// Ressourcenlookups, die diese stark typisierte Ressourcenklasse verwenden. - /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - internal static global::System.Globalization.CultureInfo Culture - { - get - { - return resourceCulture; - } - set - { - resourceCulture = value; - } - } - } -} diff --git a/SpotifyAPI.Example/Properties/Resources.resx b/SpotifyAPI.Example/Properties/Resources.resx deleted file mode 100644 index af7dbebb..00000000 --- a/SpotifyAPI.Example/Properties/Resources.resx +++ /dev/null @@ -1,117 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - \ No newline at end of file diff --git a/SpotifyAPI.Example/Properties/Settings.Designer.cs b/SpotifyAPI.Example/Properties/Settings.Designer.cs deleted file mode 100644 index 3444495f..00000000 --- a/SpotifyAPI.Example/Properties/Settings.Designer.cs +++ /dev/null @@ -1,30 +0,0 @@ -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// Runtime Version:4.0.30319.34209 -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ - -namespace SpotifyAPI.Example.Properties -{ - - - [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")] - internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase - { - - private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); - - public static Settings Default - { - get - { - return defaultInstance; - } - } - } -} diff --git a/SpotifyAPI.Example/Properties/Settings.settings b/SpotifyAPI.Example/Properties/Settings.settings deleted file mode 100644 index 39645652..00000000 --- a/SpotifyAPI.Example/Properties/Settings.settings +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/SpotifyAPI.Example/SpotifyAPI.Example.csproj b/SpotifyAPI.Example/SpotifyAPI.Example.csproj deleted file mode 100644 index c8d5e78a..00000000 --- a/SpotifyAPI.Example/SpotifyAPI.Example.csproj +++ /dev/null @@ -1,112 +0,0 @@ - - - - - Debug - AnyCPU - {C8968A03-497E-4BAB-B492-5651AE7E0C06} - WinExe - Properties - SpotifyAPI.Example - SpotifyAPI.Example - v4.5 - 512 - - - AnyCPU - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - - - AnyCPU - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - - - - - - - - - - - - - - - - Form - - - ExampleForm.cs - - - UserControl - - - LocalControl.cs - - - - - UserControl - - - WebControl.cs - - - ExampleForm.cs - - - LocalControl.cs - - - ResXFileCodeGenerator - Resources.Designer.cs - Designer - - - True - Resources.resx - - - WebControl.cs - - - SettingsSingleFileGenerator - Settings.Designer.cs - - - True - Settings.settings - True - - - - - - - - {ebbe35e2-7b91-4d7d-b8fc-3a0472f5119d} - SpotifyAPI - - - - - \ No newline at end of file diff --git a/SpotifyAPI.Example/WebControl.Designer.cs b/SpotifyAPI.Example/WebControl.Designer.cs deleted file mode 100644 index 4bbb6ba2..00000000 --- a/SpotifyAPI.Example/WebControl.Designer.cs +++ /dev/null @@ -1,420 +0,0 @@ -namespace SpotifyAPI.Example -{ - partial class WebControl - { - /// - /// Erforderliche Designervariable. - /// - private System.ComponentModel.IContainer components = null; - - /// - /// Verwendete Ressourcen bereinigen. - /// - /// True, wenn verwaltete Ressourcen gelöscht werden sollen; andernfalls False. - protected override void Dispose(bool disposing) - { - if (disposing && (components != null)) - { - components.Dispose(); - } - base.Dispose(disposing); - } - - #region Vom Komponenten-Designer generierter Code - - /// - /// Erforderliche Methode für die Designerunterstützung. - /// Der Inhalt der Methode darf nicht mit dem Code-Editor geändert werden. - /// - private void InitializeComponent() - { - this.authButton = new System.Windows.Forms.Button(); - this.label3 = new System.Windows.Forms.Label(); - this.label4 = new System.Windows.Forms.Label(); - this.label5 = new System.Windows.Forms.Label(); - this.label1 = new System.Windows.Forms.Label(); - this.label2 = new System.Windows.Forms.Label(); - this.savedTracksListView = new System.Windows.Forms.ListView(); - this.columnHeader1 = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); - this.columnHeader2 = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); - this.columnHeader3 = ((System.Windows.Forms.ColumnHeader)(new System.Windows.Forms.ColumnHeader())); - this.savedTracksCountLabel = new System.Windows.Forms.Label(); - this.playlistsListBox = new System.Windows.Forms.ListBox(); - this.playlistsCountLabel = new System.Windows.Forms.Label(); - this.label7 = new System.Windows.Forms.Label(); - this.displayNameLabel = new System.Windows.Forms.Label(); - this.countryLabel = new System.Windows.Forms.Label(); - this.emailLabel = new System.Windows.Forms.Label(); - this.accountLabel = new System.Windows.Forms.Label(); - this.avatarPictureBox = new System.Windows.Forms.PictureBox(); - this.proxyGroupBox = new System.Windows.Forms.GroupBox(); - this.proxyPortUpDown = new System.Windows.Forms.NumericUpDown(); - this.proxyPasswordTextBox = new System.Windows.Forms.TextBox(); - this.label15 = new System.Windows.Forms.Label(); - this.proxyUsernameTextBox = new System.Windows.Forms.TextBox(); - this.label14 = new System.Windows.Forms.Label(); - this.label13 = new System.Windows.Forms.Label(); - this.proxyHostTextBox = new System.Windows.Forms.TextBox(); - this.label12 = new System.Windows.Forms.Label(); - this.applyProxyBtn = new System.Windows.Forms.Button(); - ((System.ComponentModel.ISupportInitialize)(this.avatarPictureBox)).BeginInit(); - this.proxyGroupBox.SuspendLayout(); - ((System.ComponentModel.ISupportInitialize)(this.proxyPortUpDown)).BeginInit(); - this.SuspendLayout(); - // - // authButton - // - this.authButton.Location = new System.Drawing.Point(3, 3); - this.authButton.Name = "authButton"; - this.authButton.Size = new System.Drawing.Size(964, 48); - this.authButton.TabIndex = 0; - this.authButton.Text = "Authenticate SpotifyWeb API"; - this.authButton.UseVisualStyleBackColor = true; - this.authButton.Click += new System.EventHandler(this.authButton_Click); - // - // label3 - // - this.label3.AutoSize = true; - this.label3.Font = new System.Drawing.Font("Microsoft Sans Serif", 10F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.label3.Location = new System.Drawing.Point(3, 251); - this.label3.Name = "label3"; - this.label3.Size = new System.Drawing.Size(100, 17); - this.label3.TabIndex = 5; - this.label3.Text = "Display-Name:"; - // - // label4 - // - this.label4.AutoSize = true; - this.label4.Font = new System.Drawing.Font("Microsoft Sans Serif", 10F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.label4.Location = new System.Drawing.Point(3, 268); - this.label4.Name = "label4"; - this.label4.Size = new System.Drawing.Size(61, 17); - this.label4.TabIndex = 6; - this.label4.Text = "Country:"; - // - // label5 - // - this.label5.AutoSize = true; - this.label5.Font = new System.Drawing.Font("Microsoft Sans Serif", 10F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.label5.Location = new System.Drawing.Point(3, 285); - this.label5.Name = "label5"; - this.label5.Size = new System.Drawing.Size(51, 17); - this.label5.TabIndex = 7; - this.label5.Text = "E-Mail:"; - // - // label1 - // - this.label1.AutoSize = true; - this.label1.Font = new System.Drawing.Font("Microsoft Sans Serif", 10F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.label1.Location = new System.Drawing.Point(3, 302); - this.label1.Name = "label1"; - this.label1.Size = new System.Drawing.Size(100, 17); - this.label1.TabIndex = 8; - this.label1.Text = "Account-Type:"; - // - // label2 - // - this.label2.AutoSize = true; - this.label2.Font = new System.Drawing.Font("Microsoft Sans Serif", 10F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.label2.Location = new System.Drawing.Point(240, 73); - this.label2.Name = "label2"; - this.label2.Size = new System.Drawing.Size(100, 17); - this.label2.TabIndex = 9; - this.label2.Text = "Saved-Tracks:"; - // - // savedTracksListView - // - this.savedTracksListView.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] { - this.columnHeader1, - this.columnHeader2, - this.columnHeader3}); - this.savedTracksListView.FullRowSelect = true; - this.savedTracksListView.Location = new System.Drawing.Point(243, 93); - this.savedTracksListView.Name = "savedTracksListView"; - this.savedTracksListView.Size = new System.Drawing.Size(385, 563); - this.savedTracksListView.TabIndex = 10; - this.savedTracksListView.UseCompatibleStateImageBehavior = false; - this.savedTracksListView.View = System.Windows.Forms.View.Details; - // - // columnHeader1 - // - this.columnHeader1.Text = "Title"; - this.columnHeader1.Width = 120; - // - // columnHeader2 - // - this.columnHeader2.Text = "Artist"; - this.columnHeader2.Width = 117; - // - // columnHeader3 - // - this.columnHeader3.Text = "Album"; - this.columnHeader3.Width = 131; - // - // savedTracksCountLabel - // - this.savedTracksCountLabel.AutoSize = true; - this.savedTracksCountLabel.Font = new System.Drawing.Font("Microsoft Sans Serif", 10F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.savedTracksCountLabel.Location = new System.Drawing.Point(346, 73); - this.savedTracksCountLabel.Name = "savedTracksCountLabel"; - this.savedTracksCountLabel.Size = new System.Drawing.Size(13, 17); - this.savedTracksCountLabel.TabIndex = 11; - this.savedTracksCountLabel.Text = "-"; - // - // playlistsListBox - // - this.playlistsListBox.FormattingEnabled = true; - this.playlistsListBox.Location = new System.Drawing.Point(634, 93); - this.playlistsListBox.Name = "playlistsListBox"; - this.playlistsListBox.Size = new System.Drawing.Size(305, 563); - this.playlistsListBox.TabIndex = 12; - // - // playlistsCountLabel - // - this.playlistsCountLabel.AutoSize = true; - this.playlistsCountLabel.Font = new System.Drawing.Font("Microsoft Sans Serif", 10F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.playlistsCountLabel.Location = new System.Drawing.Point(700, 73); - this.playlistsCountLabel.Name = "playlistsCountLabel"; - this.playlistsCountLabel.Size = new System.Drawing.Size(13, 17); - this.playlistsCountLabel.TabIndex = 14; - this.playlistsCountLabel.Text = "-"; - // - // label7 - // - this.label7.AutoSize = true; - this.label7.Font = new System.Drawing.Font("Microsoft Sans Serif", 10F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.label7.Location = new System.Drawing.Point(631, 73); - this.label7.Name = "label7"; - this.label7.Size = new System.Drawing.Size(63, 17); - this.label7.TabIndex = 13; - this.label7.Text = "Playlists:"; - // - // displayNameLabel - // - this.displayNameLabel.AutoSize = true; - this.displayNameLabel.Font = new System.Drawing.Font("Microsoft Sans Serif", 10F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.displayNameLabel.Location = new System.Drawing.Point(109, 251); - this.displayNameLabel.Name = "displayNameLabel"; - this.displayNameLabel.Size = new System.Drawing.Size(13, 17); - this.displayNameLabel.TabIndex = 15; - this.displayNameLabel.Text = "-"; - // - // countryLabel - // - this.countryLabel.AutoSize = true; - this.countryLabel.Font = new System.Drawing.Font("Microsoft Sans Serif", 10F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.countryLabel.Location = new System.Drawing.Point(70, 268); - this.countryLabel.Name = "countryLabel"; - this.countryLabel.Size = new System.Drawing.Size(13, 17); - this.countryLabel.TabIndex = 16; - this.countryLabel.Text = "-"; - // - // emailLabel - // - this.emailLabel.AutoSize = true; - this.emailLabel.Font = new System.Drawing.Font("Microsoft Sans Serif", 10F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.emailLabel.Location = new System.Drawing.Point(60, 285); - this.emailLabel.Name = "emailLabel"; - this.emailLabel.Size = new System.Drawing.Size(13, 17); - this.emailLabel.TabIndex = 17; - this.emailLabel.Text = "-"; - // - // accountLabel - // - this.accountLabel.AutoSize = true; - this.accountLabel.Font = new System.Drawing.Font("Microsoft Sans Serif", 10F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.accountLabel.Location = new System.Drawing.Point(109, 302); - this.accountLabel.Name = "accountLabel"; - this.accountLabel.Size = new System.Drawing.Size(13, 17); - this.accountLabel.TabIndex = 18; - this.accountLabel.Text = "-"; - // - // avatarPictureBox - // - this.avatarPictureBox.Location = new System.Drawing.Point(3, 322); - this.avatarPictureBox.Name = "avatarPictureBox"; - this.avatarPictureBox.Size = new System.Drawing.Size(234, 212); - this.avatarPictureBox.TabIndex = 19; - this.avatarPictureBox.TabStop = false; - // - // proxyGroupBox - // - this.proxyGroupBox.Controls.Add(this.proxyPortUpDown); - this.proxyGroupBox.Controls.Add(this.proxyPasswordTextBox); - this.proxyGroupBox.Controls.Add(this.label15); - this.proxyGroupBox.Controls.Add(this.proxyUsernameTextBox); - this.proxyGroupBox.Controls.Add(this.label14); - this.proxyGroupBox.Controls.Add(this.label13); - this.proxyGroupBox.Controls.Add(this.proxyHostTextBox); - this.proxyGroupBox.Controls.Add(this.label12); - this.proxyGroupBox.Controls.Add(this.applyProxyBtn); - this.proxyGroupBox.Font = new System.Drawing.Font("Microsoft Sans Serif", 11F); - this.proxyGroupBox.Location = new System.Drawing.Point(6, 73); - this.proxyGroupBox.Name = "proxyGroupBox"; - this.proxyGroupBox.Size = new System.Drawing.Size(231, 167); - this.proxyGroupBox.TabIndex = 20; - this.proxyGroupBox.TabStop = false; - this.proxyGroupBox.Text = "Proxy Config"; - // - // proxyPortUpDown - // - this.proxyPortUpDown.Location = new System.Drawing.Point(50, 47); - this.proxyPortUpDown.Maximum = new decimal(new int[] { - 65535, - 0, - 0, - 0}); - this.proxyPortUpDown.Minimum = new decimal(new int[] { - 1, - 0, - 0, - 0}); - this.proxyPortUpDown.Name = "proxyPortUpDown"; - this.proxyPortUpDown.Size = new System.Drawing.Size(76, 24); - this.proxyPortUpDown.TabIndex = 6; - this.proxyPortUpDown.Value = new decimal(new int[] { - 80, - 0, - 0, - 0}); - // - // proxyPasswordTextBox - // - this.proxyPasswordTextBox.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.proxyPasswordTextBox.Location = new System.Drawing.Point(85, 103); - this.proxyPasswordTextBox.Name = "proxyPasswordTextBox"; - this.proxyPasswordTextBox.Size = new System.Drawing.Size(140, 20); - this.proxyPasswordTextBox.TabIndex = 42; - // - // label15 - // - this.label15.AutoSize = true; - this.label15.Font = new System.Drawing.Font("Microsoft Sans Serif", 10F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.label15.Location = new System.Drawing.Point(6, 104); - this.label15.Name = "label15"; - this.label15.Size = new System.Drawing.Size(73, 17); - this.label15.TabIndex = 41; - this.label15.Text = "Password:"; - // - // proxyUsernameTextBox - // - this.proxyUsernameTextBox.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.proxyUsernameTextBox.Location = new System.Drawing.Point(89, 77); - this.proxyUsernameTextBox.Name = "proxyUsernameTextBox"; - this.proxyUsernameTextBox.Size = new System.Drawing.Size(136, 20); - this.proxyUsernameTextBox.TabIndex = 40; - // - // label14 - // - this.label14.AutoSize = true; - this.label14.Font = new System.Drawing.Font("Microsoft Sans Serif", 10F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.label14.Location = new System.Drawing.Point(6, 78); - this.label14.Name = "label14"; - this.label14.Size = new System.Drawing.Size(77, 17); - this.label14.TabIndex = 39; - this.label14.Text = "Username:"; - // - // label13 - // - this.label13.AutoSize = true; - this.label13.Font = new System.Drawing.Font("Microsoft Sans Serif", 10F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.label13.Location = new System.Drawing.Point(6, 51); - this.label13.Name = "label13"; - this.label13.Size = new System.Drawing.Size(38, 17); - this.label13.TabIndex = 37; - this.label13.Text = "Port:"; - // - // proxyHostTextBox - // - this.proxyHostTextBox.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.proxyHostTextBox.Location = new System.Drawing.Point(49, 21); - this.proxyHostTextBox.Name = "proxyHostTextBox"; - this.proxyHostTextBox.Size = new System.Drawing.Size(176, 20); - this.proxyHostTextBox.TabIndex = 36; - // - // label12 - // - this.label12.AutoSize = true; - this.label12.Font = new System.Drawing.Font("Microsoft Sans Serif", 10F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.label12.Location = new System.Drawing.Point(6, 22); - this.label12.Name = "label12"; - this.label12.Size = new System.Drawing.Size(41, 17); - this.label12.TabIndex = 36; - this.label12.Text = "Host:"; - // - // applyProxyBtn - // - this.applyProxyBtn.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.applyProxyBtn.Location = new System.Drawing.Point(147, 136); - this.applyProxyBtn.Name = "applyProxyBtn"; - this.applyProxyBtn.Size = new System.Drawing.Size(78, 25); - this.applyProxyBtn.TabIndex = 0; - this.applyProxyBtn.Text = "Apply"; - this.applyProxyBtn.UseVisualStyleBackColor = true; - this.applyProxyBtn.Click += new System.EventHandler(this.applyProxyBtn_Click); - // - // WebControl - // - this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); - this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; - this.Controls.Add(this.proxyGroupBox); - this.Controls.Add(this.avatarPictureBox); - this.Controls.Add(this.accountLabel); - this.Controls.Add(this.emailLabel); - this.Controls.Add(this.countryLabel); - this.Controls.Add(this.displayNameLabel); - this.Controls.Add(this.playlistsCountLabel); - this.Controls.Add(this.label7); - this.Controls.Add(this.playlistsListBox); - this.Controls.Add(this.savedTracksCountLabel); - this.Controls.Add(this.savedTracksListView); - this.Controls.Add(this.label2); - this.Controls.Add(this.label1); - this.Controls.Add(this.label5); - this.Controls.Add(this.label4); - this.Controls.Add(this.label3); - this.Controls.Add(this.authButton); - this.Name = "WebControl"; - this.Size = new System.Drawing.Size(970, 670); - ((System.ComponentModel.ISupportInitialize)(this.avatarPictureBox)).EndInit(); - this.proxyGroupBox.ResumeLayout(false); - this.proxyGroupBox.PerformLayout(); - ((System.ComponentModel.ISupportInitialize)(this.proxyPortUpDown)).EndInit(); - this.ResumeLayout(false); - this.PerformLayout(); - - } - - #endregion - - private System.Windows.Forms.Button authButton; - private System.Windows.Forms.Label label3; - private System.Windows.Forms.Label label4; - private System.Windows.Forms.Label label5; - private System.Windows.Forms.Label label1; - private System.Windows.Forms.Label label2; - private System.Windows.Forms.ListView savedTracksListView; - private System.Windows.Forms.ColumnHeader columnHeader1; - private System.Windows.Forms.ColumnHeader columnHeader2; - private System.Windows.Forms.ColumnHeader columnHeader3; - private System.Windows.Forms.Label savedTracksCountLabel; - private System.Windows.Forms.ListBox playlistsListBox; - private System.Windows.Forms.Label playlistsCountLabel; - private System.Windows.Forms.Label label7; - private System.Windows.Forms.Label displayNameLabel; - private System.Windows.Forms.Label countryLabel; - private System.Windows.Forms.Label emailLabel; - private System.Windows.Forms.Label accountLabel; - private System.Windows.Forms.PictureBox avatarPictureBox; - private System.Windows.Forms.GroupBox proxyGroupBox; - private System.Windows.Forms.NumericUpDown proxyPortUpDown; - private System.Windows.Forms.TextBox proxyPasswordTextBox; - private System.Windows.Forms.Label label15; - private System.Windows.Forms.TextBox proxyUsernameTextBox; - private System.Windows.Forms.Label label14; - private System.Windows.Forms.Label label13; - private System.Windows.Forms.TextBox proxyHostTextBox; - private System.Windows.Forms.Label label12; - private System.Windows.Forms.Button applyProxyBtn; - } -} diff --git a/SpotifyAPI.Example/WebControl.cs b/SpotifyAPI.Example/WebControl.cs deleted file mode 100644 index a195803f..00000000 --- a/SpotifyAPI.Example/WebControl.cs +++ /dev/null @@ -1,140 +0,0 @@ -using SpotifyAPI.Web; -using SpotifyAPI.Web.Auth; -using SpotifyAPI.Web.Enums; -using SpotifyAPI.Web.Models; -using System; -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; - -namespace SpotifyAPI.Example -{ - public partial class WebControl : UserControl - { - private readonly ProxyConfig _proxyConfig; - private SpotifyWebAPI _spotify; - - private PrivateProfile _profile; - private List _savedTracks; - private List _playlists; - - public WebControl() - { - InitializeComponent(); - - _proxyConfig = new ProxyConfig(); - - _savedTracks = new List(); - } - - private async void InitialSetup() - { - if (InvokeRequired) - { - Invoke(new Action(InitialSetup)); - return; - } - - authButton.Enabled = false; - _profile = await _spotify.GetPrivateProfileAsync(); - - _savedTracks = GetSavedTracks(); - savedTracksCountLabel.Text = _savedTracks.Count.ToString(); - _savedTracks.ForEach(track => savedTracksListView.Items.Add(new ListViewItem() - { - Text = track.Name, - SubItems = { string.Join(",", track.Artists.Select(source => source.Name)), track.Album.Name } - })); - - _playlists = GetPlaylists(); - playlistsCountLabel.Text = _playlists.Count.ToString(); - _playlists.ForEach(playlist => playlistsListBox.Items.Add(playlist.Name)); - - displayNameLabel.Text = _profile.DisplayName; - countryLabel.Text = _profile.Country; - emailLabel.Text = _profile.Email; - accountLabel.Text = _profile.Product; - - if (_profile.Images != null && _profile.Images.Count > 0) - { - using (WebClient wc = new WebClient()) - { - byte[] imageBytes = await wc.DownloadDataTaskAsync(new Uri(_profile.Images[0].Url)); - using (MemoryStream stream = new MemoryStream(imageBytes)) - avatarPictureBox.Image = Image.FromStream(stream); - } - } - } - - private List GetSavedTracks() - { - Paging savedTracks = _spotify.GetSavedTracks(); - List list = savedTracks.Items.Select(track => track.Track).ToList(); - - while (savedTracks.Next != null) - { - savedTracks = _spotify.GetSavedTracks(20, savedTracks.Offset + savedTracks.Limit); - list.AddRange(savedTracks.Items.Select(track => track.Track)); - } - - return list; - } - - private List GetPlaylists() - { - Paging playlists = _spotify.GetUserPlaylists(_profile.Id); - List list = playlists.Items.ToList(); - - while (playlists.Next != null) - { - playlists = _spotify.GetUserPlaylists(_profile.Id, 20, playlists.Offset + playlists.Limit); - list.AddRange(playlists.Items); - } - - return list; - } - - private void authButton_Click(object sender, EventArgs e) - { - 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 | Scope.PlaylistReadCollaborative | - Scope.UserReadRecentlyPlayed | Scope.UserReadPlaybackState | Scope.UserModifyPlaybackState, - _proxyConfig); - - try - { - _spotify = await webApiFactory.GetWebApi(); - } - catch (Exception ex) - { - MessageBox.Show(ex.Message); - } - - if (_spotify == null) - return; - - InitialSetup(); - } - - private void applyProxyBtn_Click(object sender, EventArgs e) - { - _proxyConfig.Host = proxyHostTextBox.Text; - _proxyConfig.Port = (int)proxyPortUpDown.Value; - _proxyConfig.Username = proxyUsernameTextBox.Text; - _proxyConfig.Password = proxyPasswordTextBox.Text; - } - } -} \ No newline at end of file diff --git a/SpotifyAPI.Example/WebControl.resx b/SpotifyAPI.Example/WebControl.resx deleted file mode 100644 index 1af7de15..00000000 --- a/SpotifyAPI.Example/WebControl.resx +++ /dev/null @@ -1,120 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - \ No newline at end of file diff --git a/SpotifyAPI.Tests/Properties/AssemblyInfo.cs b/SpotifyAPI.Tests/Properties/AssemblyInfo.cs deleted file mode 100644 index a395e345..00000000 --- a/SpotifyAPI.Tests/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,35 +0,0 @@ -using System.Reflection; -using System.Runtime.InteropServices; - -// Allgemeine Informationen über eine Assembly werden über die folgenden -// Attribute gesteuert. Ändern Sie diese Attributwerte, um die Informationen zu ändern, -// die mit einer Assembly verknüpft sind. -[assembly: AssemblyTitle("SpotifyAPI.Tests")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("HEAVEN KILLERS RELEASE GROUP")] -[assembly: AssemblyProduct("SpotifyAPI.Tests")] -[assembly: AssemblyCopyright("Copyright © HEAVEN KILLERS RELEASE GROUP 2015")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// Durch Festlegen von ComVisible auf "false" werden die Typen in dieser Assembly unsichtbar -// für COM-Komponenten. Wenn Sie auf einen Typ in dieser Assembly von -// COM zugreifen müssen, legen Sie das ComVisible-Attribut für diesen Typ auf "true" fest. -[assembly: ComVisible(false)] - -// Die folgende GUID bestimmt die ID der Typbibliothek, wenn dieses Projekt für COM verfügbar gemacht wird -[assembly: Guid("015955d9-d411-4fcc-afc9-bc785216b9b2")] - -// Versionsinformationen für eine Assembly bestehen aus den folgenden vier Werten: -// -// Hauptversion -// Nebenversion -// Buildnummer -// Revision -// -// Sie können alle Werte angeben oder die standardmäßigen Build- und Revisionsnummern -// übernehmen, indem Sie "*" eingeben: -// [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] \ No newline at end of file diff --git a/SpotifyAPI.Tests/SpotifyAPI.Tests.csproj b/SpotifyAPI.Tests/SpotifyAPI.Tests.csproj deleted file mode 100644 index 51203cdd..00000000 --- a/SpotifyAPI.Tests/SpotifyAPI.Tests.csproj +++ /dev/null @@ -1,86 +0,0 @@ - - - - - Debug - AnyCPU - {A06CE1BA-B2C4-4C3C-9E79-D2E8D36DDF0B} - Library - Properties - SpotifyAPI.Tests - SpotifyAPI.Tests - v4.5 - 512 - ..\ - true - - - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - - - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - - - - ..\packages\Castle.Core.4.2.1\lib\net45\Castle.Core.dll - - - ..\packages\Moq.4.7.145\lib\net45\Moq.dll - - - ..\packages\Newtonsoft.Json.10.0.3\lib\net45\Newtonsoft.Json.dll - - - ..\packages\NUnit.3.8.1\lib\net45\nunit.framework.dll - - - - - - - - - - - - - - - - - - - - {ebbe35e2-7b91-4d7d-b8fc-3a0472f5119d} - SpotifyAPI - - - - - - - - - - - - - - \ No newline at end of file diff --git a/SpotifyAPI.Tests/SpotifyUriTest.cs b/SpotifyAPI.Tests/SpotifyUriTest.cs deleted file mode 100644 index 9326a626..00000000 --- a/SpotifyAPI.Tests/SpotifyUriTest.cs +++ /dev/null @@ -1,54 +0,0 @@ -using NUnit.Framework; -using SpotifyAPI.Local.Models; -using SpotifyAPI.Local.Enums; -using System; - -namespace SpotifyAPI.Tests -{ - [TestFixture] - public class SpotifyUriTest - { - [Test] - public void ShouldThrowArgumentExceptionForInvalidUri() - { - Assert.Throws(() => SpotifyUri.Parse("asdafadfgsrsegqejfa")); - } - - [Test] - public void ShouldCorrectlyParseTrackUri() - { - string testUri = "spotify:track:3QOruXa2lvqIFvOOa2rYyJ"; - SpotifyUri uri = SpotifyUri.Parse(testUri); - - Assert.AreEqual(uri.Base, "spotify"); - - Assert.AreEqual(uri.Type, UriType.track); - Assert.AreEqual(uri.Id, "3QOruXa2lvqIFvOOa2rYyJ"); - Assert.AreEqual(uri.ToString(), testUri); - } - - [Test] - public void ShouldCorrectlyParsePlaylistUri() - { - string testUri = "spotify:user:spotifycharts:playlist:37i9dQZEVXbMDoHDwVN2tF"; - SpotifyUri uri = SpotifyUri.Parse(testUri); - - Assert.AreEqual(uri.Base, "spotify"); - - Assert.AreEqual(uri.Type, UriType.playlist); - Assert.AreEqual(uri.Id, "37i9dQZEVXbMDoHDwVN2tF"); - - Assert.AreEqual(uri.GetUriPropValue(UriType.user), "spotifycharts"); - Assert.AreEqual(uri.ToString(), testUri); - } - - [Test] - public void ShouldHandleNotExistingUriProperty() - { - string testUri = "spotify:track:3QOruXa2lvqIFvOOa2rYyJ"; - SpotifyUri uri = SpotifyUri.Parse(testUri); - Assert.DoesNotThrow(() => uri.GetUriPropValue(UriType.user)); - Assert.IsNull(uri.GetUriPropValue(UriType.user)); - } - } -} \ No newline at end of file diff --git a/SpotifyAPI.Tests/packages.config b/SpotifyAPI.Tests/packages.config deleted file mode 100644 index 1b3f7008..00000000 --- a/SpotifyAPI.Tests/packages.config +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/SpotifyAPI.Web.Auth/AuthUtil.cs b/SpotifyAPI.Web.Auth/AuthUtil.cs new file mode 100644 index 00000000..a6ff6545 --- /dev/null +++ b/SpotifyAPI.Web.Auth/AuthUtil.cs @@ -0,0 +1,29 @@ +using System.Diagnostics; +using System.Runtime.InteropServices; + +namespace SpotifyAPI.Web.Auth +{ + internal static class AuthUtil + { + public static void OpenBrowser(string url) + { + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + url = url.Replace("&", "^&"); + Process.Start(new ProcessStartInfo("cmd", $"/c start {url}")); + } + else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) + { + Process.Start("xdg-open", url); + } + else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) + { + Process.Start("open", url); + } + else + { + // throw + } + } + } +} \ No newline at end of file diff --git a/SpotifyAPI.Web.Auth/AuthorizationCodeAuth.cs b/SpotifyAPI.Web.Auth/AuthorizationCodeAuth.cs new file mode 100644 index 00000000..beaec0cb --- /dev/null +++ b/SpotifyAPI.Web.Auth/AuthorizationCodeAuth.cs @@ -0,0 +1,115 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net; +using System.Net.Http; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using Newtonsoft.Json; +using SpotifyAPI.Web.Enums; +using SpotifyAPI.Web.Models; +using Unosquare.Labs.EmbedIO; +using Unosquare.Labs.EmbedIO.Constants; +using Unosquare.Labs.EmbedIO.Modules; + +namespace SpotifyAPI.Web.Auth +{ + public class AuthorizationCodeAuth : SpotifyAuthServer + { + public string SecretId { get; set; } + + public AuthorizationCodeAuth(string redirectUri, string serverUri, Scope scope = Scope.None, string state = "") + : base("code", "AuthorizationCodeAuth", redirectUri, serverUri, scope, state) + { + } + + public AuthorizationCodeAuth(string clientId, string secretId, string redirectUri, string serverUri, Scope scope = Scope.None, string state = "") + : this(redirectUri, serverUri, scope, state) + { + ClientId = clientId; + SecretId = secretId; + } + + private bool ShouldRegisterNewApp() + { + return string.IsNullOrEmpty(SecretId) || string.IsNullOrEmpty(ClientId); + } + + public override string GetUri() + { + return ShouldRegisterNewApp() ? $"{RedirectUri}/start.html#{State}" : base.GetUri(); + } + + protected override WebServer AdaptWebServer(WebServer webServer) => webServer.WithWebApiController(); + + public async Task ExchangeCode(string code) + { + string auth = Convert.ToBase64String(Encoding.UTF8.GetBytes(ClientId + ":" + SecretId)); + + List> args = new List>() + { + new KeyValuePair("grant_type", "authorization_code"), + new KeyValuePair("code", code), + new KeyValuePair("redirect_uri", RedirectUri) + }; + + HttpClient client = new HttpClient(); + client.DefaultRequestHeaders.Add("Authorization", $"Basic {auth}"); + HttpContent content = new FormUrlEncodedContent(args); + + HttpResponseMessage resp = await client.PostAsync("https://accounts.spotify.com/api/token", content); + string msg = await resp.Content.ReadAsStringAsync(); + + return JsonConvert.DeserializeObject(msg); + } + } + + public class AuthorizationCode + { + public string Code { get; set; } + + public string Error { get; set; } + } + + internal class AuthorizationCodeAuthController : WebApiController + { + [WebApiHandler(HttpVerbs.Get, "/")] + public Task GetEmpty(WebServer server, HttpListenerContext context) + { + string state = context.Request.QueryString["state"]; + AuthorizationCodeAuth.Instances.TryGetValue(state, out SpotifyAuthServer auth); + + string code = null; + string error = context.Request.QueryString["error"]; + if (error == null) + code = context.Request.QueryString["code"]; + + Task.Factory.StartNew(() => auth?.TriggerAuth(new AuthorizationCode + { + Code = code, + Error = error + })); + + return context.StringResponseAsync("OK - This window can be closed now"); + } + + [WebApiHandler(HttpVerbs.Post, "/")] + public bool PostValues(WebServer server, HttpListenerContext context) + { + Dictionary formParams = context.RequestFormDataDictionary(); + + string state = (string) formParams["state"]; + AuthorizationCodeAuth.Instances.TryGetValue(state, out SpotifyAuthServer authServer); + + AuthorizationCodeAuth auth = (AuthorizationCodeAuth) authServer; + auth.ClientId = (string) formParams["clientId"]; + auth.SecretId = (string) formParams["secretId"]; + + string uri = auth.GetUri(); + context.Response.Redirect(uri); + + return true; + } + } +} \ No newline at end of file diff --git a/SpotifyAPI.Web.Auth/CredentialsAuth.cs b/SpotifyAPI.Web.Auth/CredentialsAuth.cs new file mode 100644 index 00000000..45b3d44b --- /dev/null +++ b/SpotifyAPI.Web.Auth/CredentialsAuth.cs @@ -0,0 +1,43 @@ +using System; +using System.Collections.Generic; +using System.Net; +using System.Net.Http; +using System.Text; +using System.Threading.Tasks; +using Newtonsoft.Json; +using SpotifyAPI.Web.Models; + +namespace SpotifyAPI.Web.Auth +{ + public class CredentialsAuth + { + public string ClientSecret { get; set; } + + public string ClientId { get; set; } + + public CredentialsAuth(string clientId, string clientSecret) + { + ClientId = clientId; + ClientSecret = clientSecret; + } + + public async Task GetToken() + { + string auth = Convert.ToBase64String(Encoding.UTF8.GetBytes(ClientId + ":" + ClientSecret)); + + List> args = new List>() + { + new KeyValuePair("grant_type", "client_credentials") + }; + + HttpClient client = new HttpClient(); + client.DefaultRequestHeaders.Add("Authorization", $"Basic {auth}"); + HttpContent content = new FormUrlEncodedContent(args); + + HttpResponseMessage resp = await client.PostAsync("https://accounts.spotify.com/api/token", content); + string msg = await resp.Content.ReadAsStringAsync(); + + return JsonConvert.DeserializeObject(msg); + } + } +} diff --git a/SpotifyAPI.Web.Auth/ImplictGrantAuth.cs b/SpotifyAPI.Web.Auth/ImplictGrantAuth.cs new file mode 100644 index 00000000..80477126 --- /dev/null +++ b/SpotifyAPI.Web.Auth/ImplictGrantAuth.cs @@ -0,0 +1,62 @@ +using System.Net; +using System.Threading.Tasks; +using SpotifyAPI.Web.Enums; +using SpotifyAPI.Web.Models; +using Unosquare.Labs.EmbedIO; +using Unosquare.Labs.EmbedIO.Constants; +using Unosquare.Labs.EmbedIO.Modules; + +namespace SpotifyAPI.Web.Auth +{ + public class ImplictGrantAuth : SpotifyAuthServer + { + public ImplictGrantAuth(string clientId, string redirectUri, string serverUri, Scope scope = Scope.None, string state = "") : + base("token", "ImplicitGrantAuth", redirectUri, serverUri, scope, state) + { + ClientId = clientId; + } + + protected override WebServer AdaptWebServer(WebServer webServer) + { + return webServer.WithWebApiController(); + } + } + + public class ImplictGrantAuthController : WebApiController + { + [WebApiHandler(HttpVerbs.Get, "/auth")] + public Task GetAuth(WebServer server, HttpListenerContext context) + { + string state = context.Request.QueryString["state"]; + SpotifyAuthServer auth = ImplictGrantAuth.GetByState(state); + if (auth == null) + return context.StringResponseAsync( + $"Failed - Unable to find auth request with state \"{state}\" - Please retry"); + + Token token; + string error = context.Request.QueryString["error"]; + if (error == null) + { + string accessToken = context.Request.QueryString["access_token"]; + string tokenType = context.Request.QueryString["token_type"]; + string expiresIn = context.Request.QueryString["expires_in"]; + token = new Token + { + AccessToken = accessToken, + ExpiresIn = double.Parse(expiresIn), + TokenType = tokenType + }; + } + else + { + token = new Token() + { + Error = error + }; + } + + Task.Factory.StartNew(() => auth?.TriggerAuth(token)); + return context.StringResponseAsync("OK - This window can be closed now"); + } + } +} \ No newline at end of file diff --git a/SpotifyAPI.Web.Auth/Resources/AuthorizationCodeAuth/css/bulma.min.css b/SpotifyAPI.Web.Auth/Resources/AuthorizationCodeAuth/css/bulma.min.css new file mode 100644 index 00000000..59825f7f --- /dev/null +++ b/SpotifyAPI.Web.Auth/Resources/AuthorizationCodeAuth/css/bulma.min.css @@ -0,0 +1 @@ +/*! bulma.io v0.7.1 | MIT License | github.com/jgthms/bulma */@-webkit-keyframes spinAround{from{-webkit-transform:rotate(0);transform:rotate(0)}to{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}@keyframes spinAround{from{-webkit-transform:rotate(0);transform:rotate(0)}to{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}.breadcrumb,.button,.delete,.file,.is-unselectable,.modal-close,.pagination-ellipsis,.pagination-link,.pagination-next,.pagination-previous,.tabs{-webkit-touch-callout:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.navbar-link::after,.select:not(.is-multiple):not(.is-loading)::after{border:3px solid transparent;border-radius:2px;border-right:0;border-top:0;content:" ";display:block;height:.625em;margin-top:-.4375em;pointer-events:none;position:absolute;top:50%;-webkit-transform:rotate(-45deg);transform:rotate(-45deg);-webkit-transform-origin:center;transform-origin:center;width:.625em}.block:not(:last-child),.box:not(:last-child),.breadcrumb:not(:last-child),.content:not(:last-child),.highlight:not(:last-child),.level:not(:last-child),.message:not(:last-child),.notification:not(:last-child),.progress:not(:last-child),.subtitle:not(:last-child),.table-container:not(:last-child),.table:not(:last-child),.tabs:not(:last-child),.title:not(:last-child){margin-bottom:1.5rem}.delete,.modal-close{-moz-appearance:none;-webkit-appearance:none;background-color:rgba(10,10,10,.2);border:none;border-radius:290486px;cursor:pointer;display:inline-block;flex-grow:0;flex-shrink:0;font-size:0;height:20px;max-height:20px;max-width:20px;min-height:20px;min-width:20px;outline:0;position:relative;vertical-align:top;width:20px}.delete::after,.delete::before,.modal-close::after,.modal-close::before{background-color:#fff;content:"";display:block;left:50%;position:absolute;top:50%;-webkit-transform:translateX(-50%) translateY(-50%) rotate(45deg);transform:translateX(-50%) translateY(-50%) rotate(45deg);-webkit-transform-origin:center center;transform-origin:center center}.delete::before,.modal-close::before{height:2px;width:50%}.delete::after,.modal-close::after{height:50%;width:2px}.delete:focus,.delete:hover,.modal-close:focus,.modal-close:hover{background-color:rgba(10,10,10,.3)}.delete:active,.modal-close:active{background-color:rgba(10,10,10,.4)}.is-small.delete,.is-small.modal-close{height:16px;max-height:16px;max-width:16px;min-height:16px;min-width:16px;width:16px}.is-medium.delete,.is-medium.modal-close{height:24px;max-height:24px;max-width:24px;min-height:24px;min-width:24px;width:24px}.is-large.delete,.is-large.modal-close{height:32px;max-height:32px;max-width:32px;min-height:32px;min-width:32px;width:32px}.button.is-loading::after,.control.is-loading::after,.loader,.select.is-loading::after{-webkit-animation:spinAround .5s infinite linear;animation:spinAround .5s infinite linear;border:2px solid #dbdbdb;border-radius:290486px;border-right-color:transparent;border-top-color:transparent;content:"";display:block;height:1em;position:relative;width:1em}.hero-video,.image.is-16by9 img,.image.is-1by1 img,.image.is-1by2 img,.image.is-1by3 img,.image.is-2by1 img,.image.is-2by3 img,.image.is-3by1 img,.image.is-3by2 img,.image.is-3by4 img,.image.is-3by5 img,.image.is-4by3 img,.image.is-4by5 img,.image.is-5by3 img,.image.is-5by4 img,.image.is-9by16 img,.image.is-square img,.is-overlay,.modal,.modal-background{bottom:0;left:0;position:absolute;right:0;top:0}.button,.file-cta,.file-name,.input,.pagination-ellipsis,.pagination-link,.pagination-next,.pagination-previous,.select select,.textarea{-moz-appearance:none;-webkit-appearance:none;align-items:center;border:1px solid transparent;border-radius:4px;box-shadow:none;display:inline-flex;font-size:1rem;height:2.25em;justify-content:flex-start;line-height:1.5;padding-bottom:calc(.375em - 1px);padding-left:calc(.625em - 1px);padding-right:calc(.625em - 1px);padding-top:calc(.375em - 1px);position:relative;vertical-align:top}.button:active,.button:focus,.file-cta:active,.file-cta:focus,.file-name:active,.file-name:focus,.input:active,.input:focus,.is-active.button,.is-active.file-cta,.is-active.file-name,.is-active.input,.is-active.pagination-ellipsis,.is-active.pagination-link,.is-active.pagination-next,.is-active.pagination-previous,.is-active.textarea,.is-focused.button,.is-focused.file-cta,.is-focused.file-name,.is-focused.input,.is-focused.pagination-ellipsis,.is-focused.pagination-link,.is-focused.pagination-next,.is-focused.pagination-previous,.is-focused.textarea,.pagination-ellipsis:active,.pagination-ellipsis:focus,.pagination-link:active,.pagination-link:focus,.pagination-next:active,.pagination-next:focus,.pagination-previous:active,.pagination-previous:focus,.select select.is-active,.select select.is-focused,.select select:active,.select select:focus,.textarea:active,.textarea:focus{outline:0}.button[disabled],.file-cta[disabled],.file-name[disabled],.input[disabled],.pagination-ellipsis[disabled],.pagination-link[disabled],.pagination-next[disabled],.pagination-previous[disabled],.select select[disabled],.textarea[disabled]{cursor:not-allowed}/*! minireset.css v0.0.3 | MIT License | github.com/jgthms/minireset.css */blockquote,body,dd,dl,dt,fieldset,figure,h1,h2,h3,h4,h5,h6,hr,html,iframe,legend,li,ol,p,pre,textarea,ul{margin:0;padding:0}h1,h2,h3,h4,h5,h6{font-size:100%;font-weight:400}ul{list-style:none}button,input,select,textarea{margin:0}html{box-sizing:border-box}*,::after,::before{box-sizing:inherit}audio,img,video{height:auto;max-width:100%}iframe{border:0}table{border-collapse:collapse;border-spacing:0}td,th{padding:0;text-align:left}html{background-color:#fff;font-size:16px;-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased;min-width:300px;overflow-x:hidden;overflow-y:scroll;text-rendering:optimizeLegibility;-webkit-text-size-adjust:100%;-moz-text-size-adjust:100%;-ms-text-size-adjust:100%;text-size-adjust:100%}article,aside,figure,footer,header,hgroup,section{display:block}body,button,input,select,textarea{font-family:BlinkMacSystemFont,-apple-system,"Segoe UI",Roboto,Oxygen,Ubuntu,Cantarell,"Fira Sans","Droid Sans","Helvetica Neue",Helvetica,Arial,sans-serif}code,pre{-moz-osx-font-smoothing:auto;-webkit-font-smoothing:auto;font-family:monospace}body{color:#4a4a4a;font-size:1rem;font-weight:400;line-height:1.5}a{color:#3273dc;cursor:pointer;text-decoration:none}a strong{color:currentColor}a:hover{color:#363636}code{background-color:#f5f5f5;color:#ff3860;font-size:.875em;font-weight:400;padding:.25em .5em .25em}hr{background-color:#f5f5f5;border:none;display:block;height:2px;margin:1.5rem 0}img{height:auto;max-width:100%}input[type=checkbox],input[type=radio]{vertical-align:baseline}small{font-size:.875em}span{font-style:inherit;font-weight:inherit}strong{color:#363636;font-weight:700}pre{-webkit-overflow-scrolling:touch;background-color:#f5f5f5;color:#4a4a4a;font-size:.875em;overflow-x:auto;padding:1.25rem 1.5rem;white-space:pre;word-wrap:normal}pre code{background-color:transparent;color:currentColor;font-size:1em;padding:0}table td,table th{text-align:left;vertical-align:top}table th{color:#363636}.is-clearfix::after{clear:both;content:" ";display:table}.is-pulled-left{float:left!important}.is-pulled-right{float:right!important}.is-clipped{overflow:hidden!important}.is-size-1{font-size:3rem!important}.is-size-2{font-size:2.5rem!important}.is-size-3{font-size:2rem!important}.is-size-4{font-size:1.5rem!important}.is-size-5{font-size:1.25rem!important}.is-size-6{font-size:1rem!important}.is-size-7{font-size:.75rem!important}@media screen and (max-width:768px){.is-size-1-mobile{font-size:3rem!important}.is-size-2-mobile{font-size:2.5rem!important}.is-size-3-mobile{font-size:2rem!important}.is-size-4-mobile{font-size:1.5rem!important}.is-size-5-mobile{font-size:1.25rem!important}.is-size-6-mobile{font-size:1rem!important}.is-size-7-mobile{font-size:.75rem!important}}@media screen and (min-width:769px),print{.is-size-1-tablet{font-size:3rem!important}.is-size-2-tablet{font-size:2.5rem!important}.is-size-3-tablet{font-size:2rem!important}.is-size-4-tablet{font-size:1.5rem!important}.is-size-5-tablet{font-size:1.25rem!important}.is-size-6-tablet{font-size:1rem!important}.is-size-7-tablet{font-size:.75rem!important}}@media screen and (max-width:1087px){.is-size-1-touch{font-size:3rem!important}.is-size-2-touch{font-size:2.5rem!important}.is-size-3-touch{font-size:2rem!important}.is-size-4-touch{font-size:1.5rem!important}.is-size-5-touch{font-size:1.25rem!important}.is-size-6-touch{font-size:1rem!important}.is-size-7-touch{font-size:.75rem!important}}@media screen and (min-width:1088px){.is-size-1-desktop{font-size:3rem!important}.is-size-2-desktop{font-size:2.5rem!important}.is-size-3-desktop{font-size:2rem!important}.is-size-4-desktop{font-size:1.5rem!important}.is-size-5-desktop{font-size:1.25rem!important}.is-size-6-desktop{font-size:1rem!important}.is-size-7-desktop{font-size:.75rem!important}}@media screen and (min-width:1280px){.is-size-1-widescreen{font-size:3rem!important}.is-size-2-widescreen{font-size:2.5rem!important}.is-size-3-widescreen{font-size:2rem!important}.is-size-4-widescreen{font-size:1.5rem!important}.is-size-5-widescreen{font-size:1.25rem!important}.is-size-6-widescreen{font-size:1rem!important}.is-size-7-widescreen{font-size:.75rem!important}}@media screen and (min-width:1472px){.is-size-1-fullhd{font-size:3rem!important}.is-size-2-fullhd{font-size:2.5rem!important}.is-size-3-fullhd{font-size:2rem!important}.is-size-4-fullhd{font-size:1.5rem!important}.is-size-5-fullhd{font-size:1.25rem!important}.is-size-6-fullhd{font-size:1rem!important}.is-size-7-fullhd{font-size:.75rem!important}}.has-text-centered{text-align:center!important}.has-text-justified{text-align:justify!important}.has-text-left{text-align:left!important}.has-text-right{text-align:right!important}@media screen and (max-width:768px){.has-text-centered-mobile{text-align:center!important}}@media screen and (min-width:769px),print{.has-text-centered-tablet{text-align:center!important}}@media screen and (min-width:769px) and (max-width:1087px){.has-text-centered-tablet-only{text-align:center!important}}@media screen and (max-width:1087px){.has-text-centered-touch{text-align:center!important}}@media screen and (min-width:1088px){.has-text-centered-desktop{text-align:center!important}}@media screen and (min-width:1088px) and (max-width:1279px){.has-text-centered-desktop-only{text-align:center!important}}@media screen and (min-width:1280px){.has-text-centered-widescreen{text-align:center!important}}@media screen and (min-width:1280px) and (max-width:1471px){.has-text-centered-widescreen-only{text-align:center!important}}@media screen and (min-width:1472px){.has-text-centered-fullhd{text-align:center!important}}@media screen and (max-width:768px){.has-text-justified-mobile{text-align:justify!important}}@media screen and (min-width:769px),print{.has-text-justified-tablet{text-align:justify!important}}@media screen and (min-width:769px) and (max-width:1087px){.has-text-justified-tablet-only{text-align:justify!important}}@media screen and (max-width:1087px){.has-text-justified-touch{text-align:justify!important}}@media screen and (min-width:1088px){.has-text-justified-desktop{text-align:justify!important}}@media screen and (min-width:1088px) and (max-width:1279px){.has-text-justified-desktop-only{text-align:justify!important}}@media screen and (min-width:1280px){.has-text-justified-widescreen{text-align:justify!important}}@media screen and (min-width:1280px) and (max-width:1471px){.has-text-justified-widescreen-only{text-align:justify!important}}@media screen and (min-width:1472px){.has-text-justified-fullhd{text-align:justify!important}}@media screen and (max-width:768px){.has-text-left-mobile{text-align:left!important}}@media screen and (min-width:769px),print{.has-text-left-tablet{text-align:left!important}}@media screen and (min-width:769px) and (max-width:1087px){.has-text-left-tablet-only{text-align:left!important}}@media screen and (max-width:1087px){.has-text-left-touch{text-align:left!important}}@media screen and (min-width:1088px){.has-text-left-desktop{text-align:left!important}}@media screen and (min-width:1088px) and (max-width:1279px){.has-text-left-desktop-only{text-align:left!important}}@media screen and (min-width:1280px){.has-text-left-widescreen{text-align:left!important}}@media screen and (min-width:1280px) and (max-width:1471px){.has-text-left-widescreen-only{text-align:left!important}}@media screen and (min-width:1472px){.has-text-left-fullhd{text-align:left!important}}@media screen and (max-width:768px){.has-text-right-mobile{text-align:right!important}}@media screen and (min-width:769px),print{.has-text-right-tablet{text-align:right!important}}@media screen and (min-width:769px) and (max-width:1087px){.has-text-right-tablet-only{text-align:right!important}}@media screen and (max-width:1087px){.has-text-right-touch{text-align:right!important}}@media screen and (min-width:1088px){.has-text-right-desktop{text-align:right!important}}@media screen and (min-width:1088px) and (max-width:1279px){.has-text-right-desktop-only{text-align:right!important}}@media screen and (min-width:1280px){.has-text-right-widescreen{text-align:right!important}}@media screen and (min-width:1280px) and (max-width:1471px){.has-text-right-widescreen-only{text-align:right!important}}@media screen and (min-width:1472px){.has-text-right-fullhd{text-align:right!important}}.is-capitalized{text-transform:capitalize!important}.is-lowercase{text-transform:lowercase!important}.is-uppercase{text-transform:uppercase!important}.is-italic{font-style:italic!important}.has-text-white{color:#fff!important}a.has-text-white:focus,a.has-text-white:hover{color:#e6e6e6!important}.has-background-white{background-color:#fff!important}.has-text-black{color:#0a0a0a!important}a.has-text-black:focus,a.has-text-black:hover{color:#000!important}.has-background-black{background-color:#0a0a0a!important}.has-text-light{color:#f5f5f5!important}a.has-text-light:focus,a.has-text-light:hover{color:#dbdbdb!important}.has-background-light{background-color:#f5f5f5!important}.has-text-dark{color:#363636!important}a.has-text-dark:focus,a.has-text-dark:hover{color:#1c1c1c!important}.has-background-dark{background-color:#363636!important}.has-text-primary{color:#00d1b2!important}a.has-text-primary:focus,a.has-text-primary:hover{color:#009e86!important}.has-background-primary{background-color:#00d1b2!important}.has-text-link{color:#3273dc!important}a.has-text-link:focus,a.has-text-link:hover{color:#205bbc!important}.has-background-link{background-color:#3273dc!important}.has-text-info{color:#209cee!important}a.has-text-info:focus,a.has-text-info:hover{color:#0f81cc!important}.has-background-info{background-color:#209cee!important}.has-text-success{color:#23d160!important}a.has-text-success:focus,a.has-text-success:hover{color:#1ca64c!important}.has-background-success{background-color:#23d160!important}.has-text-warning{color:#ffdd57!important}a.has-text-warning:focus,a.has-text-warning:hover{color:#ffd324!important}.has-background-warning{background-color:#ffdd57!important}.has-text-danger{color:#ff3860!important}a.has-text-danger:focus,a.has-text-danger:hover{color:#ff0537!important}.has-background-danger{background-color:#ff3860!important}.has-text-black-bis{color:#121212!important}.has-background-black-bis{background-color:#121212!important}.has-text-black-ter{color:#242424!important}.has-background-black-ter{background-color:#242424!important}.has-text-grey-darker{color:#363636!important}.has-background-grey-darker{background-color:#363636!important}.has-text-grey-dark{color:#4a4a4a!important}.has-background-grey-dark{background-color:#4a4a4a!important}.has-text-grey{color:#7a7a7a!important}.has-background-grey{background-color:#7a7a7a!important}.has-text-grey-light{color:#b5b5b5!important}.has-background-grey-light{background-color:#b5b5b5!important}.has-text-grey-lighter{color:#dbdbdb!important}.has-background-grey-lighter{background-color:#dbdbdb!important}.has-text-white-ter{color:#f5f5f5!important}.has-background-white-ter{background-color:#f5f5f5!important}.has-text-white-bis{color:#fafafa!important}.has-background-white-bis{background-color:#fafafa!important}.has-text-weight-light{font-weight:300!important}.has-text-weight-normal{font-weight:400!important}.has-text-weight-semibold{font-weight:600!important}.has-text-weight-bold{font-weight:700!important}.is-block{display:block!important}@media screen and (max-width:768px){.is-block-mobile{display:block!important}}@media screen and (min-width:769px),print{.is-block-tablet{display:block!important}}@media screen and (min-width:769px) and (max-width:1087px){.is-block-tablet-only{display:block!important}}@media screen and (max-width:1087px){.is-block-touch{display:block!important}}@media screen and (min-width:1088px){.is-block-desktop{display:block!important}}@media screen and (min-width:1088px) and (max-width:1279px){.is-block-desktop-only{display:block!important}}@media screen and (min-width:1280px){.is-block-widescreen{display:block!important}}@media screen and (min-width:1280px) and (max-width:1471px){.is-block-widescreen-only{display:block!important}}@media screen and (min-width:1472px){.is-block-fullhd{display:block!important}}.is-flex{display:flex!important}@media screen and (max-width:768px){.is-flex-mobile{display:flex!important}}@media screen and (min-width:769px),print{.is-flex-tablet{display:flex!important}}@media screen and (min-width:769px) and (max-width:1087px){.is-flex-tablet-only{display:flex!important}}@media screen and (max-width:1087px){.is-flex-touch{display:flex!important}}@media screen and (min-width:1088px){.is-flex-desktop{display:flex!important}}@media screen and (min-width:1088px) and (max-width:1279px){.is-flex-desktop-only{display:flex!important}}@media screen and (min-width:1280px){.is-flex-widescreen{display:flex!important}}@media screen and (min-width:1280px) and (max-width:1471px){.is-flex-widescreen-only{display:flex!important}}@media screen and (min-width:1472px){.is-flex-fullhd{display:flex!important}}.is-inline{display:inline!important}@media screen and (max-width:768px){.is-inline-mobile{display:inline!important}}@media screen and (min-width:769px),print{.is-inline-tablet{display:inline!important}}@media screen and (min-width:769px) and (max-width:1087px){.is-inline-tablet-only{display:inline!important}}@media screen and (max-width:1087px){.is-inline-touch{display:inline!important}}@media screen and (min-width:1088px){.is-inline-desktop{display:inline!important}}@media screen and (min-width:1088px) and (max-width:1279px){.is-inline-desktop-only{display:inline!important}}@media screen and (min-width:1280px){.is-inline-widescreen{display:inline!important}}@media screen and (min-width:1280px) and (max-width:1471px){.is-inline-widescreen-only{display:inline!important}}@media screen and (min-width:1472px){.is-inline-fullhd{display:inline!important}}.is-inline-block{display:inline-block!important}@media screen and (max-width:768px){.is-inline-block-mobile{display:inline-block!important}}@media screen and (min-width:769px),print{.is-inline-block-tablet{display:inline-block!important}}@media screen and (min-width:769px) and (max-width:1087px){.is-inline-block-tablet-only{display:inline-block!important}}@media screen and (max-width:1087px){.is-inline-block-touch{display:inline-block!important}}@media screen and (min-width:1088px){.is-inline-block-desktop{display:inline-block!important}}@media screen and (min-width:1088px) and (max-width:1279px){.is-inline-block-desktop-only{display:inline-block!important}}@media screen and (min-width:1280px){.is-inline-block-widescreen{display:inline-block!important}}@media screen and (min-width:1280px) and (max-width:1471px){.is-inline-block-widescreen-only{display:inline-block!important}}@media screen and (min-width:1472px){.is-inline-block-fullhd{display:inline-block!important}}.is-inline-flex{display:inline-flex!important}@media screen and (max-width:768px){.is-inline-flex-mobile{display:inline-flex!important}}@media screen and (min-width:769px),print{.is-inline-flex-tablet{display:inline-flex!important}}@media screen and (min-width:769px) and (max-width:1087px){.is-inline-flex-tablet-only{display:inline-flex!important}}@media screen and (max-width:1087px){.is-inline-flex-touch{display:inline-flex!important}}@media screen and (min-width:1088px){.is-inline-flex-desktop{display:inline-flex!important}}@media screen and (min-width:1088px) and (max-width:1279px){.is-inline-flex-desktop-only{display:inline-flex!important}}@media screen and (min-width:1280px){.is-inline-flex-widescreen{display:inline-flex!important}}@media screen and (min-width:1280px) and (max-width:1471px){.is-inline-flex-widescreen-only{display:inline-flex!important}}@media screen and (min-width:1472px){.is-inline-flex-fullhd{display:inline-flex!important}}.is-hidden{display:none!important}@media screen and (max-width:768px){.is-hidden-mobile{display:none!important}}@media screen and (min-width:769px),print{.is-hidden-tablet{display:none!important}}@media screen and (min-width:769px) and (max-width:1087px){.is-hidden-tablet-only{display:none!important}}@media screen and (max-width:1087px){.is-hidden-touch{display:none!important}}@media screen and (min-width:1088px){.is-hidden-desktop{display:none!important}}@media screen and (min-width:1088px) and (max-width:1279px){.is-hidden-desktop-only{display:none!important}}@media screen and (min-width:1280px){.is-hidden-widescreen{display:none!important}}@media screen and (min-width:1280px) and (max-width:1471px){.is-hidden-widescreen-only{display:none!important}}@media screen and (min-width:1472px){.is-hidden-fullhd{display:none!important}}.is-invisible{visibility:hidden!important}@media screen and (max-width:768px){.is-invisible-mobile{visibility:hidden!important}}@media screen and (min-width:769px),print{.is-invisible-tablet{visibility:hidden!important}}@media screen and (min-width:769px) and (max-width:1087px){.is-invisible-tablet-only{visibility:hidden!important}}@media screen and (max-width:1087px){.is-invisible-touch{visibility:hidden!important}}@media screen and (min-width:1088px){.is-invisible-desktop{visibility:hidden!important}}@media screen and (min-width:1088px) and (max-width:1279px){.is-invisible-desktop-only{visibility:hidden!important}}@media screen and (min-width:1280px){.is-invisible-widescreen{visibility:hidden!important}}@media screen and (min-width:1280px) and (max-width:1471px){.is-invisible-widescreen-only{visibility:hidden!important}}@media screen and (min-width:1472px){.is-invisible-fullhd{visibility:hidden!important}}.is-marginless{margin:0!important}.is-paddingless{padding:0!important}.is-radiusless{border-radius:0!important}.is-shadowless{box-shadow:none!important}.box{background-color:#fff;border-radius:6px;box-shadow:0 2px 3px rgba(10,10,10,.1),0 0 0 1px rgba(10,10,10,.1);color:#4a4a4a;display:block;padding:1.25rem}a.box:focus,a.box:hover{box-shadow:0 2px 3px rgba(10,10,10,.1),0 0 0 1px #3273dc}a.box:active{box-shadow:inset 0 1px 2px rgba(10,10,10,.2),0 0 0 1px #3273dc}.button{background-color:#fff;border-color:#dbdbdb;border-width:1px;color:#363636;cursor:pointer;justify-content:center;padding-bottom:calc(.375em - 1px);padding-left:.75em;padding-right:.75em;padding-top:calc(.375em - 1px);text-align:center;white-space:nowrap}.button strong{color:inherit}.button .icon,.button .icon.is-large,.button .icon.is-medium,.button .icon.is-small{height:1.5em;width:1.5em}.button .icon:first-child:not(:last-child){margin-left:calc(-.375em - 1px);margin-right:.1875em}.button .icon:last-child:not(:first-child){margin-left:.1875em;margin-right:calc(-.375em - 1px)}.button .icon:first-child:last-child{margin-left:calc(-.375em - 1px);margin-right:calc(-.375em - 1px)}.button.is-hovered,.button:hover{border-color:#b5b5b5;color:#363636}.button.is-focused,.button:focus{border-color:#3273dc;color:#363636}.button.is-focused:not(:active),.button:focus:not(:active){box-shadow:0 0 0 .125em rgba(50,115,220,.25)}.button.is-active,.button:active{border-color:#4a4a4a;color:#363636}.button.is-text{background-color:transparent;border-color:transparent;color:#4a4a4a;text-decoration:underline}.button.is-text.is-focused,.button.is-text.is-hovered,.button.is-text:focus,.button.is-text:hover{background-color:#f5f5f5;color:#363636}.button.is-text.is-active,.button.is-text:active{background-color:#e8e8e8;color:#363636}.button.is-text[disabled]{background-color:transparent;border-color:transparent;box-shadow:none}.button.is-white{background-color:#fff;border-color:transparent;color:#0a0a0a}.button.is-white.is-hovered,.button.is-white:hover{background-color:#f9f9f9;border-color:transparent;color:#0a0a0a}.button.is-white.is-focused,.button.is-white:focus{border-color:transparent;color:#0a0a0a}.button.is-white.is-focused:not(:active),.button.is-white:focus:not(:active){box-shadow:0 0 0 .125em rgba(255,255,255,.25)}.button.is-white.is-active,.button.is-white:active{background-color:#f2f2f2;border-color:transparent;color:#0a0a0a}.button.is-white[disabled]{background-color:#fff;border-color:transparent;box-shadow:none}.button.is-white.is-inverted{background-color:#0a0a0a;color:#fff}.button.is-white.is-inverted:hover{background-color:#000}.button.is-white.is-inverted[disabled]{background-color:#0a0a0a;border-color:transparent;box-shadow:none;color:#fff}.button.is-white.is-loading::after{border-color:transparent transparent #0a0a0a #0a0a0a!important}.button.is-white.is-outlined{background-color:transparent;border-color:#fff;color:#fff}.button.is-white.is-outlined:focus,.button.is-white.is-outlined:hover{background-color:#fff;border-color:#fff;color:#0a0a0a}.button.is-white.is-outlined.is-loading::after{border-color:transparent transparent #fff #fff!important}.button.is-white.is-outlined[disabled]{background-color:transparent;border-color:#fff;box-shadow:none;color:#fff}.button.is-white.is-inverted.is-outlined{background-color:transparent;border-color:#0a0a0a;color:#0a0a0a}.button.is-white.is-inverted.is-outlined:focus,.button.is-white.is-inverted.is-outlined:hover{background-color:#0a0a0a;color:#fff}.button.is-white.is-inverted.is-outlined[disabled]{background-color:transparent;border-color:#0a0a0a;box-shadow:none;color:#0a0a0a}.button.is-black{background-color:#0a0a0a;border-color:transparent;color:#fff}.button.is-black.is-hovered,.button.is-black:hover{background-color:#040404;border-color:transparent;color:#fff}.button.is-black.is-focused,.button.is-black:focus{border-color:transparent;color:#fff}.button.is-black.is-focused:not(:active),.button.is-black:focus:not(:active){box-shadow:0 0 0 .125em rgba(10,10,10,.25)}.button.is-black.is-active,.button.is-black:active{background-color:#000;border-color:transparent;color:#fff}.button.is-black[disabled]{background-color:#0a0a0a;border-color:transparent;box-shadow:none}.button.is-black.is-inverted{background-color:#fff;color:#0a0a0a}.button.is-black.is-inverted:hover{background-color:#f2f2f2}.button.is-black.is-inverted[disabled]{background-color:#fff;border-color:transparent;box-shadow:none;color:#0a0a0a}.button.is-black.is-loading::after{border-color:transparent transparent #fff #fff!important}.button.is-black.is-outlined{background-color:transparent;border-color:#0a0a0a;color:#0a0a0a}.button.is-black.is-outlined:focus,.button.is-black.is-outlined:hover{background-color:#0a0a0a;border-color:#0a0a0a;color:#fff}.button.is-black.is-outlined.is-loading::after{border-color:transparent transparent #0a0a0a #0a0a0a!important}.button.is-black.is-outlined[disabled]{background-color:transparent;border-color:#0a0a0a;box-shadow:none;color:#0a0a0a}.button.is-black.is-inverted.is-outlined{background-color:transparent;border-color:#fff;color:#fff}.button.is-black.is-inverted.is-outlined:focus,.button.is-black.is-inverted.is-outlined:hover{background-color:#fff;color:#0a0a0a}.button.is-black.is-inverted.is-outlined[disabled]{background-color:transparent;border-color:#fff;box-shadow:none;color:#fff}.button.is-light{background-color:#f5f5f5;border-color:transparent;color:#363636}.button.is-light.is-hovered,.button.is-light:hover{background-color:#eee;border-color:transparent;color:#363636}.button.is-light.is-focused,.button.is-light:focus{border-color:transparent;color:#363636}.button.is-light.is-focused:not(:active),.button.is-light:focus:not(:active){box-shadow:0 0 0 .125em rgba(245,245,245,.25)}.button.is-light.is-active,.button.is-light:active{background-color:#e8e8e8;border-color:transparent;color:#363636}.button.is-light[disabled]{background-color:#f5f5f5;border-color:transparent;box-shadow:none}.button.is-light.is-inverted{background-color:#363636;color:#f5f5f5}.button.is-light.is-inverted:hover{background-color:#292929}.button.is-light.is-inverted[disabled]{background-color:#363636;border-color:transparent;box-shadow:none;color:#f5f5f5}.button.is-light.is-loading::after{border-color:transparent transparent #363636 #363636!important}.button.is-light.is-outlined{background-color:transparent;border-color:#f5f5f5;color:#f5f5f5}.button.is-light.is-outlined:focus,.button.is-light.is-outlined:hover{background-color:#f5f5f5;border-color:#f5f5f5;color:#363636}.button.is-light.is-outlined.is-loading::after{border-color:transparent transparent #f5f5f5 #f5f5f5!important}.button.is-light.is-outlined[disabled]{background-color:transparent;border-color:#f5f5f5;box-shadow:none;color:#f5f5f5}.button.is-light.is-inverted.is-outlined{background-color:transparent;border-color:#363636;color:#363636}.button.is-light.is-inverted.is-outlined:focus,.button.is-light.is-inverted.is-outlined:hover{background-color:#363636;color:#f5f5f5}.button.is-light.is-inverted.is-outlined[disabled]{background-color:transparent;border-color:#363636;box-shadow:none;color:#363636}.button.is-dark{background-color:#363636;border-color:transparent;color:#f5f5f5}.button.is-dark.is-hovered,.button.is-dark:hover{background-color:#2f2f2f;border-color:transparent;color:#f5f5f5}.button.is-dark.is-focused,.button.is-dark:focus{border-color:transparent;color:#f5f5f5}.button.is-dark.is-focused:not(:active),.button.is-dark:focus:not(:active){box-shadow:0 0 0 .125em rgba(54,54,54,.25)}.button.is-dark.is-active,.button.is-dark:active{background-color:#292929;border-color:transparent;color:#f5f5f5}.button.is-dark[disabled]{background-color:#363636;border-color:transparent;box-shadow:none}.button.is-dark.is-inverted{background-color:#f5f5f5;color:#363636}.button.is-dark.is-inverted:hover{background-color:#e8e8e8}.button.is-dark.is-inverted[disabled]{background-color:#f5f5f5;border-color:transparent;box-shadow:none;color:#363636}.button.is-dark.is-loading::after{border-color:transparent transparent #f5f5f5 #f5f5f5!important}.button.is-dark.is-outlined{background-color:transparent;border-color:#363636;color:#363636}.button.is-dark.is-outlined:focus,.button.is-dark.is-outlined:hover{background-color:#363636;border-color:#363636;color:#f5f5f5}.button.is-dark.is-outlined.is-loading::after{border-color:transparent transparent #363636 #363636!important}.button.is-dark.is-outlined[disabled]{background-color:transparent;border-color:#363636;box-shadow:none;color:#363636}.button.is-dark.is-inverted.is-outlined{background-color:transparent;border-color:#f5f5f5;color:#f5f5f5}.button.is-dark.is-inverted.is-outlined:focus,.button.is-dark.is-inverted.is-outlined:hover{background-color:#f5f5f5;color:#363636}.button.is-dark.is-inverted.is-outlined[disabled]{background-color:transparent;border-color:#f5f5f5;box-shadow:none;color:#f5f5f5}.button.is-primary{background-color:#00d1b2;border-color:transparent;color:#fff}.button.is-primary.is-hovered,.button.is-primary:hover{background-color:#00c4a7;border-color:transparent;color:#fff}.button.is-primary.is-focused,.button.is-primary:focus{border-color:transparent;color:#fff}.button.is-primary.is-focused:not(:active),.button.is-primary:focus:not(:active){box-shadow:0 0 0 .125em rgba(0,209,178,.25)}.button.is-primary.is-active,.button.is-primary:active{background-color:#00b89c;border-color:transparent;color:#fff}.button.is-primary[disabled]{background-color:#00d1b2;border-color:transparent;box-shadow:none}.button.is-primary.is-inverted{background-color:#fff;color:#00d1b2}.button.is-primary.is-inverted:hover{background-color:#f2f2f2}.button.is-primary.is-inverted[disabled]{background-color:#fff;border-color:transparent;box-shadow:none;color:#00d1b2}.button.is-primary.is-loading::after{border-color:transparent transparent #fff #fff!important}.button.is-primary.is-outlined{background-color:transparent;border-color:#00d1b2;color:#00d1b2}.button.is-primary.is-outlined:focus,.button.is-primary.is-outlined:hover{background-color:#00d1b2;border-color:#00d1b2;color:#fff}.button.is-primary.is-outlined.is-loading::after{border-color:transparent transparent #00d1b2 #00d1b2!important}.button.is-primary.is-outlined[disabled]{background-color:transparent;border-color:#00d1b2;box-shadow:none;color:#00d1b2}.button.is-primary.is-inverted.is-outlined{background-color:transparent;border-color:#fff;color:#fff}.button.is-primary.is-inverted.is-outlined:focus,.button.is-primary.is-inverted.is-outlined:hover{background-color:#fff;color:#00d1b2}.button.is-primary.is-inverted.is-outlined[disabled]{background-color:transparent;border-color:#fff;box-shadow:none;color:#fff}.button.is-link{background-color:#3273dc;border-color:transparent;color:#fff}.button.is-link.is-hovered,.button.is-link:hover{background-color:#276cda;border-color:transparent;color:#fff}.button.is-link.is-focused,.button.is-link:focus{border-color:transparent;color:#fff}.button.is-link.is-focused:not(:active),.button.is-link:focus:not(:active){box-shadow:0 0 0 .125em rgba(50,115,220,.25)}.button.is-link.is-active,.button.is-link:active{background-color:#2366d1;border-color:transparent;color:#fff}.button.is-link[disabled]{background-color:#3273dc;border-color:transparent;box-shadow:none}.button.is-link.is-inverted{background-color:#fff;color:#3273dc}.button.is-link.is-inverted:hover{background-color:#f2f2f2}.button.is-link.is-inverted[disabled]{background-color:#fff;border-color:transparent;box-shadow:none;color:#3273dc}.button.is-link.is-loading::after{border-color:transparent transparent #fff #fff!important}.button.is-link.is-outlined{background-color:transparent;border-color:#3273dc;color:#3273dc}.button.is-link.is-outlined:focus,.button.is-link.is-outlined:hover{background-color:#3273dc;border-color:#3273dc;color:#fff}.button.is-link.is-outlined.is-loading::after{border-color:transparent transparent #3273dc #3273dc!important}.button.is-link.is-outlined[disabled]{background-color:transparent;border-color:#3273dc;box-shadow:none;color:#3273dc}.button.is-link.is-inverted.is-outlined{background-color:transparent;border-color:#fff;color:#fff}.button.is-link.is-inverted.is-outlined:focus,.button.is-link.is-inverted.is-outlined:hover{background-color:#fff;color:#3273dc}.button.is-link.is-inverted.is-outlined[disabled]{background-color:transparent;border-color:#fff;box-shadow:none;color:#fff}.button.is-info{background-color:#209cee;border-color:transparent;color:#fff}.button.is-info.is-hovered,.button.is-info:hover{background-color:#1496ed;border-color:transparent;color:#fff}.button.is-info.is-focused,.button.is-info:focus{border-color:transparent;color:#fff}.button.is-info.is-focused:not(:active),.button.is-info:focus:not(:active){box-shadow:0 0 0 .125em rgba(32,156,238,.25)}.button.is-info.is-active,.button.is-info:active{background-color:#118fe4;border-color:transparent;color:#fff}.button.is-info[disabled]{background-color:#209cee;border-color:transparent;box-shadow:none}.button.is-info.is-inverted{background-color:#fff;color:#209cee}.button.is-info.is-inverted:hover{background-color:#f2f2f2}.button.is-info.is-inverted[disabled]{background-color:#fff;border-color:transparent;box-shadow:none;color:#209cee}.button.is-info.is-loading::after{border-color:transparent transparent #fff #fff!important}.button.is-info.is-outlined{background-color:transparent;border-color:#209cee;color:#209cee}.button.is-info.is-outlined:focus,.button.is-info.is-outlined:hover{background-color:#209cee;border-color:#209cee;color:#fff}.button.is-info.is-outlined.is-loading::after{border-color:transparent transparent #209cee #209cee!important}.button.is-info.is-outlined[disabled]{background-color:transparent;border-color:#209cee;box-shadow:none;color:#209cee}.button.is-info.is-inverted.is-outlined{background-color:transparent;border-color:#fff;color:#fff}.button.is-info.is-inverted.is-outlined:focus,.button.is-info.is-inverted.is-outlined:hover{background-color:#fff;color:#209cee}.button.is-info.is-inverted.is-outlined[disabled]{background-color:transparent;border-color:#fff;box-shadow:none;color:#fff}.button.is-success{background-color:#23d160;border-color:transparent;color:#fff}.button.is-success.is-hovered,.button.is-success:hover{background-color:#22c65b;border-color:transparent;color:#fff}.button.is-success.is-focused,.button.is-success:focus{border-color:transparent;color:#fff}.button.is-success.is-focused:not(:active),.button.is-success:focus:not(:active){box-shadow:0 0 0 .125em rgba(35,209,96,.25)}.button.is-success.is-active,.button.is-success:active{background-color:#20bc56;border-color:transparent;color:#fff}.button.is-success[disabled]{background-color:#23d160;border-color:transparent;box-shadow:none}.button.is-success.is-inverted{background-color:#fff;color:#23d160}.button.is-success.is-inverted:hover{background-color:#f2f2f2}.button.is-success.is-inverted[disabled]{background-color:#fff;border-color:transparent;box-shadow:none;color:#23d160}.button.is-success.is-loading::after{border-color:transparent transparent #fff #fff!important}.button.is-success.is-outlined{background-color:transparent;border-color:#23d160;color:#23d160}.button.is-success.is-outlined:focus,.button.is-success.is-outlined:hover{background-color:#23d160;border-color:#23d160;color:#fff}.button.is-success.is-outlined.is-loading::after{border-color:transparent transparent #23d160 #23d160!important}.button.is-success.is-outlined[disabled]{background-color:transparent;border-color:#23d160;box-shadow:none;color:#23d160}.button.is-success.is-inverted.is-outlined{background-color:transparent;border-color:#fff;color:#fff}.button.is-success.is-inverted.is-outlined:focus,.button.is-success.is-inverted.is-outlined:hover{background-color:#fff;color:#23d160}.button.is-success.is-inverted.is-outlined[disabled]{background-color:transparent;border-color:#fff;box-shadow:none;color:#fff}.button.is-warning{background-color:#ffdd57;border-color:transparent;color:rgba(0,0,0,.7)}.button.is-warning.is-hovered,.button.is-warning:hover{background-color:#ffdb4a;border-color:transparent;color:rgba(0,0,0,.7)}.button.is-warning.is-focused,.button.is-warning:focus{border-color:transparent;color:rgba(0,0,0,.7)}.button.is-warning.is-focused:not(:active),.button.is-warning:focus:not(:active){box-shadow:0 0 0 .125em rgba(255,221,87,.25)}.button.is-warning.is-active,.button.is-warning:active{background-color:#ffd83d;border-color:transparent;color:rgba(0,0,0,.7)}.button.is-warning[disabled]{background-color:#ffdd57;border-color:transparent;box-shadow:none}.button.is-warning.is-inverted{background-color:rgba(0,0,0,.7);color:#ffdd57}.button.is-warning.is-inverted:hover{background-color:rgba(0,0,0,.7)}.button.is-warning.is-inverted[disabled]{background-color:rgba(0,0,0,.7);border-color:transparent;box-shadow:none;color:#ffdd57}.button.is-warning.is-loading::after{border-color:transparent transparent rgba(0,0,0,.7) rgba(0,0,0,.7)!important}.button.is-warning.is-outlined{background-color:transparent;border-color:#ffdd57;color:#ffdd57}.button.is-warning.is-outlined:focus,.button.is-warning.is-outlined:hover{background-color:#ffdd57;border-color:#ffdd57;color:rgba(0,0,0,.7)}.button.is-warning.is-outlined.is-loading::after{border-color:transparent transparent #ffdd57 #ffdd57!important}.button.is-warning.is-outlined[disabled]{background-color:transparent;border-color:#ffdd57;box-shadow:none;color:#ffdd57}.button.is-warning.is-inverted.is-outlined{background-color:transparent;border-color:rgba(0,0,0,.7);color:rgba(0,0,0,.7)}.button.is-warning.is-inverted.is-outlined:focus,.button.is-warning.is-inverted.is-outlined:hover{background-color:rgba(0,0,0,.7);color:#ffdd57}.button.is-warning.is-inverted.is-outlined[disabled]{background-color:transparent;border-color:rgba(0,0,0,.7);box-shadow:none;color:rgba(0,0,0,.7)}.button.is-danger{background-color:#ff3860;border-color:transparent;color:#fff}.button.is-danger.is-hovered,.button.is-danger:hover{background-color:#ff2b56;border-color:transparent;color:#fff}.button.is-danger.is-focused,.button.is-danger:focus{border-color:transparent;color:#fff}.button.is-danger.is-focused:not(:active),.button.is-danger:focus:not(:active){box-shadow:0 0 0 .125em rgba(255,56,96,.25)}.button.is-danger.is-active,.button.is-danger:active{background-color:#ff1f4b;border-color:transparent;color:#fff}.button.is-danger[disabled]{background-color:#ff3860;border-color:transparent;box-shadow:none}.button.is-danger.is-inverted{background-color:#fff;color:#ff3860}.button.is-danger.is-inverted:hover{background-color:#f2f2f2}.button.is-danger.is-inverted[disabled]{background-color:#fff;border-color:transparent;box-shadow:none;color:#ff3860}.button.is-danger.is-loading::after{border-color:transparent transparent #fff #fff!important}.button.is-danger.is-outlined{background-color:transparent;border-color:#ff3860;color:#ff3860}.button.is-danger.is-outlined:focus,.button.is-danger.is-outlined:hover{background-color:#ff3860;border-color:#ff3860;color:#fff}.button.is-danger.is-outlined.is-loading::after{border-color:transparent transparent #ff3860 #ff3860!important}.button.is-danger.is-outlined[disabled]{background-color:transparent;border-color:#ff3860;box-shadow:none;color:#ff3860}.button.is-danger.is-inverted.is-outlined{background-color:transparent;border-color:#fff;color:#fff}.button.is-danger.is-inverted.is-outlined:focus,.button.is-danger.is-inverted.is-outlined:hover{background-color:#fff;color:#ff3860}.button.is-danger.is-inverted.is-outlined[disabled]{background-color:transparent;border-color:#fff;box-shadow:none;color:#fff}.button.is-small{border-radius:2px;font-size:.75rem}.button.is-medium{font-size:1.25rem}.button.is-large{font-size:1.5rem}.button[disabled]{background-color:#fff;border-color:#dbdbdb;box-shadow:none;opacity:.5}.button.is-fullwidth{display:flex;width:100%}.button.is-loading{color:transparent!important;pointer-events:none}.button.is-loading::after{position:absolute;left:calc(50% - (1em / 2));top:calc(50% - (1em / 2));position:absolute!important}.button.is-static{background-color:#f5f5f5;border-color:#dbdbdb;color:#7a7a7a;box-shadow:none;pointer-events:none}.button.is-rounded{border-radius:290486px;padding-left:1em;padding-right:1em}.buttons{align-items:center;display:flex;flex-wrap:wrap;justify-content:flex-start}.buttons .button{margin-bottom:.5rem}.buttons .button:not(:last-child){margin-right:.5rem}.buttons:last-child{margin-bottom:-.5rem}.buttons:not(:last-child){margin-bottom:1rem}.buttons.has-addons .button:not(:first-child){border-bottom-left-radius:0;border-top-left-radius:0}.buttons.has-addons .button:not(:last-child){border-bottom-right-radius:0;border-top-right-radius:0;margin-right:-1px}.buttons.has-addons .button:last-child{margin-right:0}.buttons.has-addons .button.is-hovered,.buttons.has-addons .button:hover{z-index:2}.buttons.has-addons .button.is-active,.buttons.has-addons .button.is-focused,.buttons.has-addons .button.is-selected,.buttons.has-addons .button:active,.buttons.has-addons .button:focus{z-index:3}.buttons.has-addons .button.is-active:hover,.buttons.has-addons .button.is-focused:hover,.buttons.has-addons .button.is-selected:hover,.buttons.has-addons .button:active:hover,.buttons.has-addons .button:focus:hover{z-index:4}.buttons.has-addons .button.is-expanded{flex-grow:1}.buttons.is-centered{justify-content:center}.buttons.is-right{justify-content:flex-end}.container{margin:0 auto;position:relative}@media screen and (min-width:1088px){.container{max-width:960px;width:960px}.container.is-fluid{margin-left:64px;margin-right:64px;max-width:none;width:auto}}@media screen and (max-width:1279px){.container.is-widescreen{max-width:1152px;width:auto}}@media screen and (max-width:1471px){.container.is-fullhd{max-width:1344px;width:auto}}@media screen and (min-width:1280px){.container{max-width:1152px;width:1152px}}@media screen and (min-width:1472px){.container{max-width:1344px;width:1344px}}.content li+li{margin-top:.25em}.content blockquote:not(:last-child),.content dl:not(:last-child),.content ol:not(:last-child),.content p:not(:last-child),.content pre:not(:last-child),.content table:not(:last-child),.content ul:not(:last-child){margin-bottom:1em}.content h1,.content h2,.content h3,.content h4,.content h5,.content h6{color:#363636;font-weight:600;line-height:1.125}.content h1{font-size:2em;margin-bottom:.5em}.content h1:not(:first-child){margin-top:1em}.content h2{font-size:1.75em;margin-bottom:.5714em}.content h2:not(:first-child){margin-top:1.1428em}.content h3{font-size:1.5em;margin-bottom:.6666em}.content h3:not(:first-child){margin-top:1.3333em}.content h4{font-size:1.25em;margin-bottom:.8em}.content h5{font-size:1.125em;margin-bottom:.8888em}.content h6{font-size:1em;margin-bottom:1em}.content blockquote{background-color:#f5f5f5;border-left:5px solid #dbdbdb;padding:1.25em 1.5em}.content ol{list-style:decimal outside;margin-left:2em;margin-top:1em}.content ul{list-style:disc outside;margin-left:2em;margin-top:1em}.content ul ul{list-style-type:circle;margin-top:.5em}.content ul ul ul{list-style-type:square}.content dd{margin-left:2em}.content figure{margin-left:2em;margin-right:2em;text-align:center}.content figure:not(:first-child){margin-top:2em}.content figure:not(:last-child){margin-bottom:2em}.content figure img{display:inline-block}.content figure figcaption{font-style:italic}.content pre{-webkit-overflow-scrolling:touch;overflow-x:auto;padding:1.25em 1.5em;white-space:pre;word-wrap:normal}.content sub,.content sup{font-size:75%}.content table{width:100%}.content table td,.content table th{border:1px solid #dbdbdb;border-width:0 0 1px;padding:.5em .75em;vertical-align:top}.content table th{color:#363636;text-align:left}.content table thead td,.content table thead th{border-width:0 0 2px;color:#363636}.content table tfoot td,.content table tfoot th{border-width:2px 0 0;color:#363636}.content table tbody tr:last-child td,.content table tbody tr:last-child th{border-bottom-width:0}.content.is-small{font-size:.75rem}.content.is-medium{font-size:1.25rem}.content.is-large{font-size:1.5rem}.input,.textarea{background-color:#fff;border-color:#dbdbdb;color:#363636;box-shadow:inset 0 1px 2px rgba(10,10,10,.1);max-width:100%;width:100%}.input::-moz-placeholder,.textarea::-moz-placeholder{color:rgba(54,54,54,.3)}.input::-webkit-input-placeholder,.textarea::-webkit-input-placeholder{color:rgba(54,54,54,.3)}.input:-moz-placeholder,.textarea:-moz-placeholder{color:rgba(54,54,54,.3)}.input:-ms-input-placeholder,.textarea:-ms-input-placeholder{color:rgba(54,54,54,.3)}.input.is-hovered,.input:hover,.textarea.is-hovered,.textarea:hover{border-color:#b5b5b5}.input.is-active,.input.is-focused,.input:active,.input:focus,.textarea.is-active,.textarea.is-focused,.textarea:active,.textarea:focus{border-color:#3273dc;box-shadow:0 0 0 .125em rgba(50,115,220,.25)}.input[disabled],.textarea[disabled]{background-color:#f5f5f5;border-color:#f5f5f5;box-shadow:none;color:#7a7a7a}.input[disabled]::-moz-placeholder,.textarea[disabled]::-moz-placeholder{color:rgba(122,122,122,.3)}.input[disabled]::-webkit-input-placeholder,.textarea[disabled]::-webkit-input-placeholder{color:rgba(122,122,122,.3)}.input[disabled]:-moz-placeholder,.textarea[disabled]:-moz-placeholder{color:rgba(122,122,122,.3)}.input[disabled]:-ms-input-placeholder,.textarea[disabled]:-ms-input-placeholder{color:rgba(122,122,122,.3)}.input[readonly],.textarea[readonly]{box-shadow:none}.input.is-white,.textarea.is-white{border-color:#fff}.input.is-white.is-active,.input.is-white.is-focused,.input.is-white:active,.input.is-white:focus,.textarea.is-white.is-active,.textarea.is-white.is-focused,.textarea.is-white:active,.textarea.is-white:focus{box-shadow:0 0 0 .125em rgba(255,255,255,.25)}.input.is-black,.textarea.is-black{border-color:#0a0a0a}.input.is-black.is-active,.input.is-black.is-focused,.input.is-black:active,.input.is-black:focus,.textarea.is-black.is-active,.textarea.is-black.is-focused,.textarea.is-black:active,.textarea.is-black:focus{box-shadow:0 0 0 .125em rgba(10,10,10,.25)}.input.is-light,.textarea.is-light{border-color:#f5f5f5}.input.is-light.is-active,.input.is-light.is-focused,.input.is-light:active,.input.is-light:focus,.textarea.is-light.is-active,.textarea.is-light.is-focused,.textarea.is-light:active,.textarea.is-light:focus{box-shadow:0 0 0 .125em rgba(245,245,245,.25)}.input.is-dark,.textarea.is-dark{border-color:#363636}.input.is-dark.is-active,.input.is-dark.is-focused,.input.is-dark:active,.input.is-dark:focus,.textarea.is-dark.is-active,.textarea.is-dark.is-focused,.textarea.is-dark:active,.textarea.is-dark:focus{box-shadow:0 0 0 .125em rgba(54,54,54,.25)}.input.is-primary,.textarea.is-primary{border-color:#00d1b2}.input.is-primary.is-active,.input.is-primary.is-focused,.input.is-primary:active,.input.is-primary:focus,.textarea.is-primary.is-active,.textarea.is-primary.is-focused,.textarea.is-primary:active,.textarea.is-primary:focus{box-shadow:0 0 0 .125em rgba(0,209,178,.25)}.input.is-link,.textarea.is-link{border-color:#3273dc}.input.is-link.is-active,.input.is-link.is-focused,.input.is-link:active,.input.is-link:focus,.textarea.is-link.is-active,.textarea.is-link.is-focused,.textarea.is-link:active,.textarea.is-link:focus{box-shadow:0 0 0 .125em rgba(50,115,220,.25)}.input.is-info,.textarea.is-info{border-color:#209cee}.input.is-info.is-active,.input.is-info.is-focused,.input.is-info:active,.input.is-info:focus,.textarea.is-info.is-active,.textarea.is-info.is-focused,.textarea.is-info:active,.textarea.is-info:focus{box-shadow:0 0 0 .125em rgba(32,156,238,.25)}.input.is-success,.textarea.is-success{border-color:#23d160}.input.is-success.is-active,.input.is-success.is-focused,.input.is-success:active,.input.is-success:focus,.textarea.is-success.is-active,.textarea.is-success.is-focused,.textarea.is-success:active,.textarea.is-success:focus{box-shadow:0 0 0 .125em rgba(35,209,96,.25)}.input.is-warning,.textarea.is-warning{border-color:#ffdd57}.input.is-warning.is-active,.input.is-warning.is-focused,.input.is-warning:active,.input.is-warning:focus,.textarea.is-warning.is-active,.textarea.is-warning.is-focused,.textarea.is-warning:active,.textarea.is-warning:focus{box-shadow:0 0 0 .125em rgba(255,221,87,.25)}.input.is-danger,.textarea.is-danger{border-color:#ff3860}.input.is-danger.is-active,.input.is-danger.is-focused,.input.is-danger:active,.input.is-danger:focus,.textarea.is-danger.is-active,.textarea.is-danger.is-focused,.textarea.is-danger:active,.textarea.is-danger:focus{box-shadow:0 0 0 .125em rgba(255,56,96,.25)}.input.is-small,.textarea.is-small{border-radius:2px;font-size:.75rem}.input.is-medium,.textarea.is-medium{font-size:1.25rem}.input.is-large,.textarea.is-large{font-size:1.5rem}.input.is-fullwidth,.textarea.is-fullwidth{display:block;width:100%}.input.is-inline,.textarea.is-inline{display:inline;width:auto}.input.is-rounded{border-radius:290486px;padding-left:1em;padding-right:1em}.input.is-static{background-color:transparent;border-color:transparent;box-shadow:none;padding-left:0;padding-right:0}.textarea{display:block;max-width:100%;min-width:100%;padding:.625em;resize:vertical}.textarea:not([rows]){max-height:600px;min-height:120px}.textarea[rows]{height:initial}.textarea.has-fixed-size{resize:none}.checkbox,.radio{cursor:pointer;display:inline-block;line-height:1.25;position:relative}.checkbox input,.radio input{cursor:pointer}.checkbox:hover,.radio:hover{color:#363636}.checkbox[disabled],.radio[disabled]{color:#7a7a7a;cursor:not-allowed}.radio+.radio{margin-left:.5em}.select{display:inline-block;max-width:100%;position:relative;vertical-align:top}.select:not(.is-multiple){height:2.25em}.select:not(.is-multiple):not(.is-loading)::after{border-color:#3273dc;right:1.125em;z-index:4}.select.is-rounded select{border-radius:290486px;padding-left:1em}.select select{background-color:#fff;border-color:#dbdbdb;color:#363636;cursor:pointer;display:block;font-size:1em;max-width:100%;outline:0}.select select::-moz-placeholder{color:rgba(54,54,54,.3)}.select select::-webkit-input-placeholder{color:rgba(54,54,54,.3)}.select select:-moz-placeholder{color:rgba(54,54,54,.3)}.select select:-ms-input-placeholder{color:rgba(54,54,54,.3)}.select select.is-hovered,.select select:hover{border-color:#b5b5b5}.select select.is-active,.select select.is-focused,.select select:active,.select select:focus{border-color:#3273dc;box-shadow:0 0 0 .125em rgba(50,115,220,.25)}.select select[disabled]{background-color:#f5f5f5;border-color:#f5f5f5;box-shadow:none;color:#7a7a7a}.select select[disabled]::-moz-placeholder{color:rgba(122,122,122,.3)}.select select[disabled]::-webkit-input-placeholder{color:rgba(122,122,122,.3)}.select select[disabled]:-moz-placeholder{color:rgba(122,122,122,.3)}.select select[disabled]:-ms-input-placeholder{color:rgba(122,122,122,.3)}.select select::-ms-expand{display:none}.select select[disabled]:hover{border-color:#f5f5f5}.select select:not([multiple]){padding-right:2.5em}.select select[multiple]{height:initial;padding:0}.select select[multiple] option{padding:.5em 1em}.select:not(.is-multiple):not(.is-loading):hover::after{border-color:#363636}.select.is-white:not(:hover)::after{border-color:#fff}.select.is-white select{border-color:#fff}.select.is-white select.is-hovered,.select.is-white select:hover{border-color:#f2f2f2}.select.is-white select.is-active,.select.is-white select.is-focused,.select.is-white select:active,.select.is-white select:focus{box-shadow:0 0 0 .125em rgba(255,255,255,.25)}.select.is-black:not(:hover)::after{border-color:#0a0a0a}.select.is-black select{border-color:#0a0a0a}.select.is-black select.is-hovered,.select.is-black select:hover{border-color:#000}.select.is-black select.is-active,.select.is-black select.is-focused,.select.is-black select:active,.select.is-black select:focus{box-shadow:0 0 0 .125em rgba(10,10,10,.25)}.select.is-light:not(:hover)::after{border-color:#f5f5f5}.select.is-light select{border-color:#f5f5f5}.select.is-light select.is-hovered,.select.is-light select:hover{border-color:#e8e8e8}.select.is-light select.is-active,.select.is-light select.is-focused,.select.is-light select:active,.select.is-light select:focus{box-shadow:0 0 0 .125em rgba(245,245,245,.25)}.select.is-dark:not(:hover)::after{border-color:#363636}.select.is-dark select{border-color:#363636}.select.is-dark select.is-hovered,.select.is-dark select:hover{border-color:#292929}.select.is-dark select.is-active,.select.is-dark select.is-focused,.select.is-dark select:active,.select.is-dark select:focus{box-shadow:0 0 0 .125em rgba(54,54,54,.25)}.select.is-primary:not(:hover)::after{border-color:#00d1b2}.select.is-primary select{border-color:#00d1b2}.select.is-primary select.is-hovered,.select.is-primary select:hover{border-color:#00b89c}.select.is-primary select.is-active,.select.is-primary select.is-focused,.select.is-primary select:active,.select.is-primary select:focus{box-shadow:0 0 0 .125em rgba(0,209,178,.25)}.select.is-link:not(:hover)::after{border-color:#3273dc}.select.is-link select{border-color:#3273dc}.select.is-link select.is-hovered,.select.is-link select:hover{border-color:#2366d1}.select.is-link select.is-active,.select.is-link select.is-focused,.select.is-link select:active,.select.is-link select:focus{box-shadow:0 0 0 .125em rgba(50,115,220,.25)}.select.is-info:not(:hover)::after{border-color:#209cee}.select.is-info select{border-color:#209cee}.select.is-info select.is-hovered,.select.is-info select:hover{border-color:#118fe4}.select.is-info select.is-active,.select.is-info select.is-focused,.select.is-info select:active,.select.is-info select:focus{box-shadow:0 0 0 .125em rgba(32,156,238,.25)}.select.is-success:not(:hover)::after{border-color:#23d160}.select.is-success select{border-color:#23d160}.select.is-success select.is-hovered,.select.is-success select:hover{border-color:#20bc56}.select.is-success select.is-active,.select.is-success select.is-focused,.select.is-success select:active,.select.is-success select:focus{box-shadow:0 0 0 .125em rgba(35,209,96,.25)}.select.is-warning:not(:hover)::after{border-color:#ffdd57}.select.is-warning select{border-color:#ffdd57}.select.is-warning select.is-hovered,.select.is-warning select:hover{border-color:#ffd83d}.select.is-warning select.is-active,.select.is-warning select.is-focused,.select.is-warning select:active,.select.is-warning select:focus{box-shadow:0 0 0 .125em rgba(255,221,87,.25)}.select.is-danger:not(:hover)::after{border-color:#ff3860}.select.is-danger select{border-color:#ff3860}.select.is-danger select.is-hovered,.select.is-danger select:hover{border-color:#ff1f4b}.select.is-danger select.is-active,.select.is-danger select.is-focused,.select.is-danger select:active,.select.is-danger select:focus{box-shadow:0 0 0 .125em rgba(255,56,96,.25)}.select.is-small{border-radius:2px;font-size:.75rem}.select.is-medium{font-size:1.25rem}.select.is-large{font-size:1.5rem}.select.is-disabled::after{border-color:#7a7a7a}.select.is-fullwidth{width:100%}.select.is-fullwidth select{width:100%}.select.is-loading::after{margin-top:0;position:absolute;right:.625em;top:.625em;-webkit-transform:none;transform:none}.select.is-loading.is-small:after{font-size:.75rem}.select.is-loading.is-medium:after{font-size:1.25rem}.select.is-loading.is-large:after{font-size:1.5rem}.file{align-items:stretch;display:flex;justify-content:flex-start;position:relative}.file.is-white .file-cta{background-color:#fff;border-color:transparent;color:#0a0a0a}.file.is-white.is-hovered .file-cta,.file.is-white:hover .file-cta{background-color:#f9f9f9;border-color:transparent;color:#0a0a0a}.file.is-white.is-focused .file-cta,.file.is-white:focus .file-cta{border-color:transparent;box-shadow:0 0 .5em rgba(255,255,255,.25);color:#0a0a0a}.file.is-white.is-active .file-cta,.file.is-white:active .file-cta{background-color:#f2f2f2;border-color:transparent;color:#0a0a0a}.file.is-black .file-cta{background-color:#0a0a0a;border-color:transparent;color:#fff}.file.is-black.is-hovered .file-cta,.file.is-black:hover .file-cta{background-color:#040404;border-color:transparent;color:#fff}.file.is-black.is-focused .file-cta,.file.is-black:focus .file-cta{border-color:transparent;box-shadow:0 0 .5em rgba(10,10,10,.25);color:#fff}.file.is-black.is-active .file-cta,.file.is-black:active .file-cta{background-color:#000;border-color:transparent;color:#fff}.file.is-light .file-cta{background-color:#f5f5f5;border-color:transparent;color:#363636}.file.is-light.is-hovered .file-cta,.file.is-light:hover .file-cta{background-color:#eee;border-color:transparent;color:#363636}.file.is-light.is-focused .file-cta,.file.is-light:focus .file-cta{border-color:transparent;box-shadow:0 0 .5em rgba(245,245,245,.25);color:#363636}.file.is-light.is-active .file-cta,.file.is-light:active .file-cta{background-color:#e8e8e8;border-color:transparent;color:#363636}.file.is-dark .file-cta{background-color:#363636;border-color:transparent;color:#f5f5f5}.file.is-dark.is-hovered .file-cta,.file.is-dark:hover .file-cta{background-color:#2f2f2f;border-color:transparent;color:#f5f5f5}.file.is-dark.is-focused .file-cta,.file.is-dark:focus .file-cta{border-color:transparent;box-shadow:0 0 .5em rgba(54,54,54,.25);color:#f5f5f5}.file.is-dark.is-active .file-cta,.file.is-dark:active .file-cta{background-color:#292929;border-color:transparent;color:#f5f5f5}.file.is-primary .file-cta{background-color:#00d1b2;border-color:transparent;color:#fff}.file.is-primary.is-hovered .file-cta,.file.is-primary:hover .file-cta{background-color:#00c4a7;border-color:transparent;color:#fff}.file.is-primary.is-focused .file-cta,.file.is-primary:focus .file-cta{border-color:transparent;box-shadow:0 0 .5em rgba(0,209,178,.25);color:#fff}.file.is-primary.is-active .file-cta,.file.is-primary:active .file-cta{background-color:#00b89c;border-color:transparent;color:#fff}.file.is-link .file-cta{background-color:#3273dc;border-color:transparent;color:#fff}.file.is-link.is-hovered .file-cta,.file.is-link:hover .file-cta{background-color:#276cda;border-color:transparent;color:#fff}.file.is-link.is-focused .file-cta,.file.is-link:focus .file-cta{border-color:transparent;box-shadow:0 0 .5em rgba(50,115,220,.25);color:#fff}.file.is-link.is-active .file-cta,.file.is-link:active .file-cta{background-color:#2366d1;border-color:transparent;color:#fff}.file.is-info .file-cta{background-color:#209cee;border-color:transparent;color:#fff}.file.is-info.is-hovered .file-cta,.file.is-info:hover .file-cta{background-color:#1496ed;border-color:transparent;color:#fff}.file.is-info.is-focused .file-cta,.file.is-info:focus .file-cta{border-color:transparent;box-shadow:0 0 .5em rgba(32,156,238,.25);color:#fff}.file.is-info.is-active .file-cta,.file.is-info:active .file-cta{background-color:#118fe4;border-color:transparent;color:#fff}.file.is-success .file-cta{background-color:#23d160;border-color:transparent;color:#fff}.file.is-success.is-hovered .file-cta,.file.is-success:hover .file-cta{background-color:#22c65b;border-color:transparent;color:#fff}.file.is-success.is-focused .file-cta,.file.is-success:focus .file-cta{border-color:transparent;box-shadow:0 0 .5em rgba(35,209,96,.25);color:#fff}.file.is-success.is-active .file-cta,.file.is-success:active .file-cta{background-color:#20bc56;border-color:transparent;color:#fff}.file.is-warning .file-cta{background-color:#ffdd57;border-color:transparent;color:rgba(0,0,0,.7)}.file.is-warning.is-hovered .file-cta,.file.is-warning:hover .file-cta{background-color:#ffdb4a;border-color:transparent;color:rgba(0,0,0,.7)}.file.is-warning.is-focused .file-cta,.file.is-warning:focus .file-cta{border-color:transparent;box-shadow:0 0 .5em rgba(255,221,87,.25);color:rgba(0,0,0,.7)}.file.is-warning.is-active .file-cta,.file.is-warning:active .file-cta{background-color:#ffd83d;border-color:transparent;color:rgba(0,0,0,.7)}.file.is-danger .file-cta{background-color:#ff3860;border-color:transparent;color:#fff}.file.is-danger.is-hovered .file-cta,.file.is-danger:hover .file-cta{background-color:#ff2b56;border-color:transparent;color:#fff}.file.is-danger.is-focused .file-cta,.file.is-danger:focus .file-cta{border-color:transparent;box-shadow:0 0 .5em rgba(255,56,96,.25);color:#fff}.file.is-danger.is-active .file-cta,.file.is-danger:active .file-cta{background-color:#ff1f4b;border-color:transparent;color:#fff}.file.is-small{font-size:.75rem}.file.is-medium{font-size:1.25rem}.file.is-medium .file-icon .fa{font-size:21px}.file.is-large{font-size:1.5rem}.file.is-large .file-icon .fa{font-size:28px}.file.has-name .file-cta{border-bottom-right-radius:0;border-top-right-radius:0}.file.has-name .file-name{border-bottom-left-radius:0;border-top-left-radius:0}.file.has-name.is-empty .file-cta{border-radius:4px}.file.has-name.is-empty .file-name{display:none}.file.is-boxed .file-label{flex-direction:column}.file.is-boxed .file-cta{flex-direction:column;height:auto;padding:1em 3em}.file.is-boxed .file-name{border-width:0 1px 1px}.file.is-boxed .file-icon{height:1.5em;width:1.5em}.file.is-boxed .file-icon .fa{font-size:21px}.file.is-boxed.is-small .file-icon .fa{font-size:14px}.file.is-boxed.is-medium .file-icon .fa{font-size:28px}.file.is-boxed.is-large .file-icon .fa{font-size:35px}.file.is-boxed.has-name .file-cta{border-radius:4px 4px 0 0}.file.is-boxed.has-name .file-name{border-radius:0 0 4px 4px;border-width:0 1px 1px}.file.is-centered{justify-content:center}.file.is-fullwidth .file-label{width:100%}.file.is-fullwidth .file-name{flex-grow:1;max-width:none}.file.is-right{justify-content:flex-end}.file.is-right .file-cta{border-radius:0 4px 4px 0}.file.is-right .file-name{border-radius:4px 0 0 4px;border-width:1px 0 1px 1px;order:-1}.file-label{align-items:stretch;display:flex;cursor:pointer;justify-content:flex-start;overflow:hidden;position:relative}.file-label:hover .file-cta{background-color:#eee;color:#363636}.file-label:hover .file-name{border-color:#d5d5d5}.file-label:active .file-cta{background-color:#e8e8e8;color:#363636}.file-label:active .file-name{border-color:#cfcfcf}.file-input{height:.01em;left:0;outline:0;position:absolute;top:0;width:.01em}.file-cta,.file-name{border-color:#dbdbdb;border-radius:4px;font-size:1em;padding-left:1em;padding-right:1em;white-space:nowrap}.file-cta{background-color:#f5f5f5;color:#4a4a4a}.file-name{border-color:#dbdbdb;border-style:solid;border-width:1px 1px 1px 0;display:block;max-width:16em;overflow:hidden;text-align:left;text-overflow:ellipsis}.file-icon{align-items:center;display:flex;height:1em;justify-content:center;margin-right:.5em;width:1em}.file-icon .fa{font-size:14px}.label{color:#363636;display:block;font-size:1rem;font-weight:700}.label:not(:last-child){margin-bottom:.5em}.label.is-small{font-size:.75rem}.label.is-medium{font-size:1.25rem}.label.is-large{font-size:1.5rem}.help{display:block;font-size:.75rem;margin-top:.25rem}.help.is-white{color:#fff}.help.is-black{color:#0a0a0a}.help.is-light{color:#f5f5f5}.help.is-dark{color:#363636}.help.is-primary{color:#00d1b2}.help.is-link{color:#3273dc}.help.is-info{color:#209cee}.help.is-success{color:#23d160}.help.is-warning{color:#ffdd57}.help.is-danger{color:#ff3860}.field:not(:last-child){margin-bottom:.75rem}.field.has-addons{display:flex;justify-content:flex-start}.field.has-addons .control:not(:last-child){margin-right:-1px}.field.has-addons .control:not(:first-child):not(:last-child) .button,.field.has-addons .control:not(:first-child):not(:last-child) .input,.field.has-addons .control:not(:first-child):not(:last-child) .select select{border-radius:0}.field.has-addons .control:first-child .button,.field.has-addons .control:first-child .input,.field.has-addons .control:first-child .select select{border-bottom-right-radius:0;border-top-right-radius:0}.field.has-addons .control:last-child .button,.field.has-addons .control:last-child .input,.field.has-addons .control:last-child .select select{border-bottom-left-radius:0;border-top-left-radius:0}.field.has-addons .control .button.is-hovered,.field.has-addons .control .button:hover,.field.has-addons .control .input.is-hovered,.field.has-addons .control .input:hover,.field.has-addons .control .select select.is-hovered,.field.has-addons .control .select select:hover{z-index:2}.field.has-addons .control .button.is-active,.field.has-addons .control .button.is-focused,.field.has-addons .control .button:active,.field.has-addons .control .button:focus,.field.has-addons .control .input.is-active,.field.has-addons .control .input.is-focused,.field.has-addons .control .input:active,.field.has-addons .control .input:focus,.field.has-addons .control .select select.is-active,.field.has-addons .control .select select.is-focused,.field.has-addons .control .select select:active,.field.has-addons .control .select select:focus{z-index:3}.field.has-addons .control .button.is-active:hover,.field.has-addons .control .button.is-focused:hover,.field.has-addons .control .button:active:hover,.field.has-addons .control .button:focus:hover,.field.has-addons .control .input.is-active:hover,.field.has-addons .control .input.is-focused:hover,.field.has-addons .control .input:active:hover,.field.has-addons .control .input:focus:hover,.field.has-addons .control .select select.is-active:hover,.field.has-addons .control .select select.is-focused:hover,.field.has-addons .control .select select:active:hover,.field.has-addons .control .select select:focus:hover{z-index:4}.field.has-addons .control.is-expanded{flex-grow:1}.field.has-addons.has-addons-centered{justify-content:center}.field.has-addons.has-addons-right{justify-content:flex-end}.field.has-addons.has-addons-fullwidth .control{flex-grow:1;flex-shrink:0}.field.is-grouped{display:flex;justify-content:flex-start}.field.is-grouped>.control{flex-shrink:0}.field.is-grouped>.control:not(:last-child){margin-bottom:0;margin-right:.75rem}.field.is-grouped>.control.is-expanded{flex-grow:1;flex-shrink:1}.field.is-grouped.is-grouped-centered{justify-content:center}.field.is-grouped.is-grouped-right{justify-content:flex-end}.field.is-grouped.is-grouped-multiline{flex-wrap:wrap}.field.is-grouped.is-grouped-multiline>.control:last-child,.field.is-grouped.is-grouped-multiline>.control:not(:last-child){margin-bottom:.75rem}.field.is-grouped.is-grouped-multiline:last-child{margin-bottom:-.75rem}.field.is-grouped.is-grouped-multiline:not(:last-child){margin-bottom:0}@media screen and (min-width:769px),print{.field.is-horizontal{display:flex}}.field-label .label{font-size:inherit}@media screen and (max-width:768px){.field-label{margin-bottom:.5rem}}@media screen and (min-width:769px),print{.field-label{flex-basis:0;flex-grow:1;flex-shrink:0;margin-right:1.5rem;text-align:right}.field-label.is-small{font-size:.75rem;padding-top:.375em}.field-label.is-normal{padding-top:.375em}.field-label.is-medium{font-size:1.25rem;padding-top:.375em}.field-label.is-large{font-size:1.5rem;padding-top:.375em}}.field-body .field .field{margin-bottom:0}@media screen and (min-width:769px),print{.field-body{display:flex;flex-basis:0;flex-grow:5;flex-shrink:1}.field-body .field{margin-bottom:0}.field-body>.field{flex-shrink:1}.field-body>.field:not(.is-narrow){flex-grow:1}.field-body>.field:not(:last-child){margin-right:.75rem}}.control{font-size:1rem;position:relative;text-align:left}.control.has-icon .icon{color:#dbdbdb;height:2.25em;pointer-events:none;position:absolute;top:0;width:2.25em;z-index:4}.control.has-icon .input:focus+.icon{color:#7a7a7a}.control.has-icon .input.is-small+.icon{font-size:.75rem}.control.has-icon .input.is-medium+.icon{font-size:1.25rem}.control.has-icon .input.is-large+.icon{font-size:1.5rem}.control.has-icon:not(.has-icon-right) .icon{left:0}.control.has-icon:not(.has-icon-right) .input{padding-left:2.25em}.control.has-icon.has-icon-right .icon{right:0}.control.has-icon.has-icon-right .input{padding-right:2.25em}.control.has-icons-left .input:focus~.icon,.control.has-icons-left .select:focus~.icon,.control.has-icons-right .input:focus~.icon,.control.has-icons-right .select:focus~.icon{color:#7a7a7a}.control.has-icons-left .input.is-small~.icon,.control.has-icons-left .select.is-small~.icon,.control.has-icons-right .input.is-small~.icon,.control.has-icons-right .select.is-small~.icon{font-size:.75rem}.control.has-icons-left .input.is-medium~.icon,.control.has-icons-left .select.is-medium~.icon,.control.has-icons-right .input.is-medium~.icon,.control.has-icons-right .select.is-medium~.icon{font-size:1.25rem}.control.has-icons-left .input.is-large~.icon,.control.has-icons-left .select.is-large~.icon,.control.has-icons-right .input.is-large~.icon,.control.has-icons-right .select.is-large~.icon{font-size:1.5rem}.control.has-icons-left .icon,.control.has-icons-right .icon{color:#dbdbdb;height:2.25em;pointer-events:none;position:absolute;top:0;width:2.25em;z-index:4}.control.has-icons-left .input,.control.has-icons-left .select select{padding-left:2.25em}.control.has-icons-left .icon.is-left{left:0}.control.has-icons-right .input,.control.has-icons-right .select select{padding-right:2.25em}.control.has-icons-right .icon.is-right{right:0}.control.is-loading::after{position:absolute!important;right:.625em;top:.625em;z-index:4}.control.is-loading.is-small:after{font-size:.75rem}.control.is-loading.is-medium:after{font-size:1.25rem}.control.is-loading.is-large:after{font-size:1.5rem}.icon{align-items:center;display:inline-flex;justify-content:center;height:1.5rem;width:1.5rem}.icon.is-small{height:1rem;width:1rem}.icon.is-medium{height:2rem;width:2rem}.icon.is-large{height:3rem;width:3rem}.image{display:block;position:relative}.image img{display:block;height:auto;width:100%}.image img.is-rounded{border-radius:290486px}.image.is-16by9 img,.image.is-1by1 img,.image.is-1by2 img,.image.is-1by3 img,.image.is-2by1 img,.image.is-2by3 img,.image.is-3by1 img,.image.is-3by2 img,.image.is-3by4 img,.image.is-3by5 img,.image.is-4by3 img,.image.is-4by5 img,.image.is-5by3 img,.image.is-5by4 img,.image.is-9by16 img,.image.is-square img{height:100%;width:100%}.image.is-1by1,.image.is-square{padding-top:100%}.image.is-5by4{padding-top:80%}.image.is-4by3{padding-top:75%}.image.is-3by2{padding-top:66.6666%}.image.is-5by3{padding-top:60%}.image.is-16by9{padding-top:56.25%}.image.is-2by1{padding-top:50%}.image.is-3by1{padding-top:33.3333%}.image.is-4by5{padding-top:125%}.image.is-3by4{padding-top:133.3333%}.image.is-2by3{padding-top:150%}.image.is-3by5{padding-top:166.6666%}.image.is-9by16{padding-top:177.7777%}.image.is-1by2{padding-top:200%}.image.is-1by3{padding-top:300%}.image.is-16x16{height:16px;width:16px}.image.is-24x24{height:24px;width:24px}.image.is-32x32{height:32px;width:32px}.image.is-48x48{height:48px;width:48px}.image.is-64x64{height:64px;width:64px}.image.is-96x96{height:96px;width:96px}.image.is-128x128{height:128px;width:128px}.notification{background-color:#f5f5f5;border-radius:4px;padding:1.25rem 2.5rem 1.25rem 1.5rem;position:relative}.notification a:not(.button){color:currentColor;text-decoration:underline}.notification strong{color:currentColor}.notification code,.notification pre{background:#fff}.notification pre code{background:0 0}.notification>.delete{position:absolute;right:.5rem;top:.5rem}.notification .content,.notification .subtitle,.notification .title{color:currentColor}.notification.is-white{background-color:#fff;color:#0a0a0a}.notification.is-black{background-color:#0a0a0a;color:#fff}.notification.is-light{background-color:#f5f5f5;color:#363636}.notification.is-dark{background-color:#363636;color:#f5f5f5}.notification.is-primary{background-color:#00d1b2;color:#fff}.notification.is-link{background-color:#3273dc;color:#fff}.notification.is-info{background-color:#209cee;color:#fff}.notification.is-success{background-color:#23d160;color:#fff}.notification.is-warning{background-color:#ffdd57;color:rgba(0,0,0,.7)}.notification.is-danger{background-color:#ff3860;color:#fff}.progress{-moz-appearance:none;-webkit-appearance:none;border:none;border-radius:290486px;display:block;height:1rem;overflow:hidden;padding:0;width:100%}.progress::-webkit-progress-bar{background-color:#dbdbdb}.progress::-webkit-progress-value{background-color:#4a4a4a}.progress::-moz-progress-bar{background-color:#4a4a4a}.progress::-ms-fill{background-color:#4a4a4a;border:none}.progress.is-white::-webkit-progress-value{background-color:#fff}.progress.is-white::-moz-progress-bar{background-color:#fff}.progress.is-white::-ms-fill{background-color:#fff}.progress.is-black::-webkit-progress-value{background-color:#0a0a0a}.progress.is-black::-moz-progress-bar{background-color:#0a0a0a}.progress.is-black::-ms-fill{background-color:#0a0a0a}.progress.is-light::-webkit-progress-value{background-color:#f5f5f5}.progress.is-light::-moz-progress-bar{background-color:#f5f5f5}.progress.is-light::-ms-fill{background-color:#f5f5f5}.progress.is-dark::-webkit-progress-value{background-color:#363636}.progress.is-dark::-moz-progress-bar{background-color:#363636}.progress.is-dark::-ms-fill{background-color:#363636}.progress.is-primary::-webkit-progress-value{background-color:#00d1b2}.progress.is-primary::-moz-progress-bar{background-color:#00d1b2}.progress.is-primary::-ms-fill{background-color:#00d1b2}.progress.is-link::-webkit-progress-value{background-color:#3273dc}.progress.is-link::-moz-progress-bar{background-color:#3273dc}.progress.is-link::-ms-fill{background-color:#3273dc}.progress.is-info::-webkit-progress-value{background-color:#209cee}.progress.is-info::-moz-progress-bar{background-color:#209cee}.progress.is-info::-ms-fill{background-color:#209cee}.progress.is-success::-webkit-progress-value{background-color:#23d160}.progress.is-success::-moz-progress-bar{background-color:#23d160}.progress.is-success::-ms-fill{background-color:#23d160}.progress.is-warning::-webkit-progress-value{background-color:#ffdd57}.progress.is-warning::-moz-progress-bar{background-color:#ffdd57}.progress.is-warning::-ms-fill{background-color:#ffdd57}.progress.is-danger::-webkit-progress-value{background-color:#ff3860}.progress.is-danger::-moz-progress-bar{background-color:#ff3860}.progress.is-danger::-ms-fill{background-color:#ff3860}.progress.is-small{height:.75rem}.progress.is-medium{height:1.25rem}.progress.is-large{height:1.5rem}.table{background-color:#fff;color:#363636}.table td,.table th{border:1px solid #dbdbdb;border-width:0 0 1px;padding:.5em .75em;vertical-align:top}.table td.is-white,.table th.is-white{background-color:#fff;border-color:#fff;color:#0a0a0a}.table td.is-black,.table th.is-black{background-color:#0a0a0a;border-color:#0a0a0a;color:#fff}.table td.is-light,.table th.is-light{background-color:#f5f5f5;border-color:#f5f5f5;color:#363636}.table td.is-dark,.table th.is-dark{background-color:#363636;border-color:#363636;color:#f5f5f5}.table td.is-primary,.table th.is-primary{background-color:#00d1b2;border-color:#00d1b2;color:#fff}.table td.is-link,.table th.is-link{background-color:#3273dc;border-color:#3273dc;color:#fff}.table td.is-info,.table th.is-info{background-color:#209cee;border-color:#209cee;color:#fff}.table td.is-success,.table th.is-success{background-color:#23d160;border-color:#23d160;color:#fff}.table td.is-warning,.table th.is-warning{background-color:#ffdd57;border-color:#ffdd57;color:rgba(0,0,0,.7)}.table td.is-danger,.table th.is-danger{background-color:#ff3860;border-color:#ff3860;color:#fff}.table td.is-narrow,.table th.is-narrow{white-space:nowrap;width:1%}.table td.is-selected,.table th.is-selected{background-color:#00d1b2;color:#fff}.table td.is-selected a,.table td.is-selected strong,.table th.is-selected a,.table th.is-selected strong{color:currentColor}.table th{color:#363636;text-align:left}.table tr.is-selected{background-color:#00d1b2;color:#fff}.table tr.is-selected a,.table tr.is-selected strong{color:currentColor}.table tr.is-selected td,.table tr.is-selected th{border-color:#fff;color:currentColor}.table thead td,.table thead th{border-width:0 0 2px;color:#363636}.table tfoot td,.table tfoot th{border-width:2px 0 0;color:#363636}.table tbody tr:last-child td,.table tbody tr:last-child th{border-bottom-width:0}.table.is-bordered td,.table.is-bordered th{border-width:1px}.table.is-bordered tr:last-child td,.table.is-bordered tr:last-child th{border-bottom-width:1px}.table.is-fullwidth{width:100%}.table.is-hoverable tbody tr:not(.is-selected):hover{background-color:#fafafa}.table.is-hoverable.is-striped tbody tr:not(.is-selected):hover{background-color:#f5f5f5}.table.is-narrow td,.table.is-narrow th{padding:.25em .5em}.table.is-striped tbody tr:not(.is-selected):nth-child(even){background-color:#fafafa}.table-container{-webkit-overflow-scrolling:touch;overflow:auto;overflow-y:hidden;max-width:100%}.tags{align-items:center;display:flex;flex-wrap:wrap;justify-content:flex-start}.tags .tag{margin-bottom:.5rem}.tags .tag:not(:last-child){margin-right:.5rem}.tags:last-child{margin-bottom:-.5rem}.tags:not(:last-child){margin-bottom:1rem}.tags.has-addons .tag{margin-right:0}.tags.has-addons .tag:not(:first-child){border-bottom-left-radius:0;border-top-left-radius:0}.tags.has-addons .tag:not(:last-child){border-bottom-right-radius:0;border-top-right-radius:0}.tags.is-centered{justify-content:center}.tags.is-centered .tag{margin-right:.25rem;margin-left:.25rem}.tags.is-right{justify-content:flex-end}.tags.is-right .tag:not(:first-child){margin-left:.5rem}.tags.is-right .tag:not(:last-child){margin-right:0}.tag:not(body){align-items:center;background-color:#f5f5f5;border-radius:4px;color:#4a4a4a;display:inline-flex;font-size:.75rem;height:2em;justify-content:center;line-height:1.5;padding-left:.75em;padding-right:.75em;white-space:nowrap}.tag:not(body) .delete{margin-left:.25rem;margin-right:-.375rem}.tag:not(body).is-white{background-color:#fff;color:#0a0a0a}.tag:not(body).is-black{background-color:#0a0a0a;color:#fff}.tag:not(body).is-light{background-color:#f5f5f5;color:#363636}.tag:not(body).is-dark{background-color:#363636;color:#f5f5f5}.tag:not(body).is-primary{background-color:#00d1b2;color:#fff}.tag:not(body).is-link{background-color:#3273dc;color:#fff}.tag:not(body).is-info{background-color:#209cee;color:#fff}.tag:not(body).is-success{background-color:#23d160;color:#fff}.tag:not(body).is-warning{background-color:#ffdd57;color:rgba(0,0,0,.7)}.tag:not(body).is-danger{background-color:#ff3860;color:#fff}.tag:not(body).is-medium{font-size:1rem}.tag:not(body).is-large{font-size:1.25rem}.tag:not(body) .icon:first-child:not(:last-child){margin-left:-.375em;margin-right:.1875em}.tag:not(body) .icon:last-child:not(:first-child){margin-left:.1875em;margin-right:-.375em}.tag:not(body) .icon:first-child:last-child{margin-left:-.375em;margin-right:-.375em}.tag:not(body).is-delete{margin-left:1px;padding:0;position:relative;width:2em}.tag:not(body).is-delete::after,.tag:not(body).is-delete::before{background-color:currentColor;content:"";display:block;left:50%;position:absolute;top:50%;-webkit-transform:translateX(-50%) translateY(-50%) rotate(45deg);transform:translateX(-50%) translateY(-50%) rotate(45deg);-webkit-transform-origin:center center;transform-origin:center center}.tag:not(body).is-delete::before{height:1px;width:50%}.tag:not(body).is-delete::after{height:50%;width:1px}.tag:not(body).is-delete:focus,.tag:not(body).is-delete:hover{background-color:#e8e8e8}.tag:not(body).is-delete:active{background-color:#dbdbdb}.tag:not(body).is-rounded{border-radius:290486px}a.tag:hover{text-decoration:underline}.subtitle,.title{word-break:break-word}.subtitle em,.subtitle span,.title em,.title span{font-weight:inherit}.subtitle sub,.title sub{font-size:.75em}.subtitle sup,.title sup{font-size:.75em}.subtitle .tag,.title .tag{vertical-align:middle}.title{color:#363636;font-size:2rem;font-weight:600;line-height:1.125}.title strong{color:inherit;font-weight:inherit}.title+.highlight{margin-top:-.75rem}.title:not(.is-spaced)+.subtitle{margin-top:-1.25rem}.title.is-1{font-size:3rem}.title.is-2{font-size:2.5rem}.title.is-3{font-size:2rem}.title.is-4{font-size:1.5rem}.title.is-5{font-size:1.25rem}.title.is-6{font-size:1rem}.title.is-7{font-size:.75rem}.subtitle{color:#4a4a4a;font-size:1.25rem;font-weight:400;line-height:1.25}.subtitle strong{color:#363636;font-weight:600}.subtitle:not(.is-spaced)+.title{margin-top:-1.25rem}.subtitle.is-1{font-size:3rem}.subtitle.is-2{font-size:2.5rem}.subtitle.is-3{font-size:2rem}.subtitle.is-4{font-size:1.5rem}.subtitle.is-5{font-size:1.25rem}.subtitle.is-6{font-size:1rem}.subtitle.is-7{font-size:.75rem}.heading{display:block;font-size:11px;letter-spacing:1px;margin-bottom:5px;text-transform:uppercase}.highlight{font-weight:400;max-width:100%;overflow:hidden;padding:0}.highlight pre{overflow:auto;max-width:100%}.number{align-items:center;background-color:#f5f5f5;border-radius:290486px;display:inline-flex;font-size:1.25rem;height:2em;justify-content:center;margin-right:1.5rem;min-width:2.5em;padding:.25rem .5rem;text-align:center;vertical-align:top}.breadcrumb{font-size:1rem;white-space:nowrap}.breadcrumb a{align-items:center;color:#3273dc;display:flex;justify-content:center;padding:0 .75em}.breadcrumb a:hover{color:#363636}.breadcrumb li{align-items:center;display:flex}.breadcrumb li:first-child a{padding-left:0}.breadcrumb li.is-active a{color:#363636;cursor:default;pointer-events:none}.breadcrumb li+li::before{color:#b5b5b5;content:"\0002f"}.breadcrumb ol,.breadcrumb ul{align-items:flex-start;display:flex;flex-wrap:wrap;justify-content:flex-start}.breadcrumb .icon:first-child{margin-right:.5em}.breadcrumb .icon:last-child{margin-left:.5em}.breadcrumb.is-centered ol,.breadcrumb.is-centered ul{justify-content:center}.breadcrumb.is-right ol,.breadcrumb.is-right ul{justify-content:flex-end}.breadcrumb.is-small{font-size:.75rem}.breadcrumb.is-medium{font-size:1.25rem}.breadcrumb.is-large{font-size:1.5rem}.breadcrumb.has-arrow-separator li+li::before{content:"\02192"}.breadcrumb.has-bullet-separator li+li::before{content:"\02022"}.breadcrumb.has-dot-separator li+li::before{content:"\000b7"}.breadcrumb.has-succeeds-separator li+li::before{content:"\0227B"}.card{background-color:#fff;box-shadow:0 2px 3px rgba(10,10,10,.1),0 0 0 1px rgba(10,10,10,.1);color:#4a4a4a;max-width:100%;position:relative}.card-header{background-color:none;align-items:stretch;box-shadow:0 1px 2px rgba(10,10,10,.1);display:flex}.card-header-title{align-items:center;color:#363636;display:flex;flex-grow:1;font-weight:700;padding:.75rem}.card-header-title.is-centered{justify-content:center}.card-header-icon{align-items:center;cursor:pointer;display:flex;justify-content:center;padding:.75rem}.card-image{display:block;position:relative}.card-content{background-color:none;padding:1.5rem}.card-footer{background-color:none;border-top:1px solid #dbdbdb;align-items:stretch;display:flex}.card-footer-item{align-items:center;display:flex;flex-basis:0;flex-grow:1;flex-shrink:0;justify-content:center;padding:.75rem}.card-footer-item:not(:last-child){border-right:1px solid #dbdbdb}.card .media:not(:last-child){margin-bottom:.75rem}.dropdown{display:inline-flex;position:relative;vertical-align:top}.dropdown.is-active .dropdown-menu,.dropdown.is-hoverable:hover .dropdown-menu{display:block}.dropdown.is-right .dropdown-menu{left:auto;right:0}.dropdown.is-up .dropdown-menu{bottom:100%;padding-bottom:4px;padding-top:initial;top:auto}.dropdown-menu{display:none;left:0;min-width:12rem;padding-top:4px;position:absolute;top:100%;z-index:20}.dropdown-content{background-color:#fff;border-radius:4px;box-shadow:0 2px 3px rgba(10,10,10,.1),0 0 0 1px rgba(10,10,10,.1);padding-bottom:.5rem;padding-top:.5rem}.dropdown-item{color:#4a4a4a;display:block;font-size:.875rem;line-height:1.5;padding:.375rem 1rem;position:relative}a.dropdown-item{padding-right:3rem;white-space:nowrap}a.dropdown-item:hover{background-color:#f5f5f5;color:#0a0a0a}a.dropdown-item.is-active{background-color:#3273dc;color:#fff}.dropdown-divider{background-color:#dbdbdb;border:none;display:block;height:1px;margin:.5rem 0}.level{align-items:center;justify-content:space-between}.level code{border-radius:4px}.level img{display:inline-block;vertical-align:top}.level.is-mobile{display:flex}.level.is-mobile .level-left,.level.is-mobile .level-right{display:flex}.level.is-mobile .level-left+.level-right{margin-top:0}.level.is-mobile .level-item{margin-right:.75rem}.level.is-mobile .level-item:not(:last-child){margin-bottom:0}.level.is-mobile .level-item:not(.is-narrow){flex-grow:1}@media screen and (min-width:769px),print{.level{display:flex}.level>.level-item:not(.is-narrow){flex-grow:1}}.level-item{align-items:center;display:flex;flex-basis:auto;flex-grow:0;flex-shrink:0;justify-content:center}.level-item .subtitle,.level-item .title{margin-bottom:0}@media screen and (max-width:768px){.level-item:not(:last-child){margin-bottom:.75rem}}.level-left,.level-right{flex-basis:auto;flex-grow:0;flex-shrink:0}.level-left .level-item.is-flexible,.level-right .level-item.is-flexible{flex-grow:1}@media screen and (min-width:769px),print{.level-left .level-item:not(:last-child),.level-right .level-item:not(:last-child){margin-right:.75rem}}.level-left{align-items:center;justify-content:flex-start}@media screen and (max-width:768px){.level-left+.level-right{margin-top:1.5rem}}@media screen and (min-width:769px),print{.level-left{display:flex}}.level-right{align-items:center;justify-content:flex-end}@media screen and (min-width:769px),print{.level-right{display:flex}}.media{align-items:flex-start;display:flex;text-align:left}.media .content:not(:last-child){margin-bottom:.75rem}.media .media{border-top:1px solid rgba(219,219,219,.5);display:flex;padding-top:.75rem}.media .media .content:not(:last-child),.media .media .control:not(:last-child){margin-bottom:.5rem}.media .media .media{padding-top:.5rem}.media .media .media+.media{margin-top:.5rem}.media+.media{border-top:1px solid rgba(219,219,219,.5);margin-top:1rem;padding-top:1rem}.media.is-large+.media{margin-top:1.5rem;padding-top:1.5rem}.media-left,.media-right{flex-basis:auto;flex-grow:0;flex-shrink:0}.media-left{margin-right:1rem}.media-right{margin-left:1rem}.media-content{flex-basis:auto;flex-grow:1;flex-shrink:1;text-align:left}.menu{font-size:1rem}.menu.is-small{font-size:.75rem}.menu.is-medium{font-size:1.25rem}.menu.is-large{font-size:1.5rem}.menu-list{line-height:1.25}.menu-list a{border-radius:2px;color:#4a4a4a;display:block;padding:.5em .75em}.menu-list a:hover{background-color:#f5f5f5;color:#363636}.menu-list a.is-active{background-color:#3273dc;color:#fff}.menu-list li ul{border-left:1px solid #dbdbdb;margin:.75em;padding-left:.75em}.menu-label{color:#7a7a7a;font-size:.75em;letter-spacing:.1em;text-transform:uppercase}.menu-label:not(:first-child){margin-top:1em}.menu-label:not(:last-child){margin-bottom:1em}.message{background-color:#f5f5f5;border-radius:4px;font-size:1rem}.message strong{color:currentColor}.message a:not(.button):not(.tag){color:currentColor;text-decoration:underline}.message.is-small{font-size:.75rem}.message.is-medium{font-size:1.25rem}.message.is-large{font-size:1.5rem}.message.is-white{background-color:#fff}.message.is-white .message-header{background-color:#fff;color:#0a0a0a}.message.is-white .message-body{border-color:#fff;color:#4d4d4d}.message.is-black{background-color:#fafafa}.message.is-black .message-header{background-color:#0a0a0a;color:#fff}.message.is-black .message-body{border-color:#0a0a0a;color:#090909}.message.is-light{background-color:#fafafa}.message.is-light .message-header{background-color:#f5f5f5;color:#363636}.message.is-light .message-body{border-color:#f5f5f5;color:#505050}.message.is-dark{background-color:#fafafa}.message.is-dark .message-header{background-color:#363636;color:#f5f5f5}.message.is-dark .message-body{border-color:#363636;color:#2a2a2a}.message.is-primary{background-color:#f5fffd}.message.is-primary .message-header{background-color:#00d1b2;color:#fff}.message.is-primary .message-body{border-color:#00d1b2;color:#021310}.message.is-link{background-color:#f6f9fe}.message.is-link .message-header{background-color:#3273dc;color:#fff}.message.is-link .message-body{border-color:#3273dc;color:#22509a}.message.is-info{background-color:#f6fbfe}.message.is-info .message-header{background-color:#209cee;color:#fff}.message.is-info .message-body{border-color:#209cee;color:#12537e}.message.is-success{background-color:#f6fef9}.message.is-success .message-header{background-color:#23d160;color:#fff}.message.is-success .message-body{border-color:#23d160;color:#0e301a}.message.is-warning{background-color:#fffdf5}.message.is-warning .message-header{background-color:#ffdd57;color:rgba(0,0,0,.7)}.message.is-warning .message-body{border-color:#ffdd57;color:#3b3108}.message.is-danger{background-color:#fff5f7}.message.is-danger .message-header{background-color:#ff3860;color:#fff}.message.is-danger .message-body{border-color:#ff3860;color:#cd0930}.message-header{align-items:center;background-color:#4a4a4a;border-radius:4px 4px 0 0;color:#fff;display:flex;font-weight:700;justify-content:space-between;line-height:1.25;padding:.75em 1em;position:relative}.message-header .delete{flex-grow:0;flex-shrink:0;margin-left:.75em}.message-header+.message-body{border-width:0;border-top-left-radius:0;border-top-right-radius:0}.message-body{border-color:#dbdbdb;border-radius:4px;border-style:solid;border-width:0 0 0 4px;color:#4a4a4a;padding:1.25em 1.5em}.message-body code,.message-body pre{background-color:#fff}.message-body pre code{background-color:transparent}.modal{align-items:center;display:none;justify-content:center;overflow:hidden;position:fixed;z-index:40}.modal.is-active{display:flex}.modal-background{background-color:rgba(10,10,10,.86)}.modal-card,.modal-content{margin:0 20px;max-height:calc(100vh - 160px);overflow:auto;position:relative;width:100%}@media screen and (min-width:769px),print{.modal-card,.modal-content{margin:0 auto;max-height:calc(100vh - 40px);width:640px}}.modal-close{background:0 0;height:40px;position:fixed;right:20px;top:20px;width:40px}.modal-card{display:flex;flex-direction:column;max-height:calc(100vh - 40px);overflow:hidden}.modal-card-foot,.modal-card-head{align-items:center;background-color:#f5f5f5;display:flex;flex-shrink:0;justify-content:flex-start;padding:20px;position:relative}.modal-card-head{border-bottom:1px solid #dbdbdb;border-top-left-radius:6px;border-top-right-radius:6px}.modal-card-title{color:#363636;flex-grow:1;flex-shrink:0;font-size:1.5rem;line-height:1}.modal-card-foot{border-bottom-left-radius:6px;border-bottom-right-radius:6px;border-top:1px solid #dbdbdb}.modal-card-foot .button:not(:last-child){margin-right:10px}.modal-card-body{-webkit-overflow-scrolling:touch;background-color:#fff;flex-grow:1;flex-shrink:1;overflow:auto;padding:20px}.navbar{background-color:#fff;min-height:3.25rem;position:relative;z-index:30}.navbar.is-white{background-color:#fff;color:#0a0a0a}.navbar.is-white .navbar-brand .navbar-link,.navbar.is-white .navbar-brand>.navbar-item{color:#0a0a0a}.navbar.is-white .navbar-brand .navbar-link.is-active,.navbar.is-white .navbar-brand .navbar-link:hover,.navbar.is-white .navbar-brand>a.navbar-item.is-active,.navbar.is-white .navbar-brand>a.navbar-item:hover{background-color:#f2f2f2;color:#0a0a0a}.navbar.is-white .navbar-brand .navbar-link::after{border-color:#0a0a0a}@media screen and (min-width:1088px){.navbar.is-white .navbar-end .navbar-link,.navbar.is-white .navbar-end>.navbar-item,.navbar.is-white .navbar-start .navbar-link,.navbar.is-white .navbar-start>.navbar-item{color:#0a0a0a}.navbar.is-white .navbar-end .navbar-link.is-active,.navbar.is-white .navbar-end .navbar-link:hover,.navbar.is-white .navbar-end>a.navbar-item.is-active,.navbar.is-white .navbar-end>a.navbar-item:hover,.navbar.is-white .navbar-start .navbar-link.is-active,.navbar.is-white .navbar-start .navbar-link:hover,.navbar.is-white .navbar-start>a.navbar-item.is-active,.navbar.is-white .navbar-start>a.navbar-item:hover{background-color:#f2f2f2;color:#0a0a0a}.navbar.is-white .navbar-end .navbar-link::after,.navbar.is-white .navbar-start .navbar-link::after{border-color:#0a0a0a}.navbar.is-white .navbar-item.has-dropdown.is-active .navbar-link,.navbar.is-white .navbar-item.has-dropdown:hover .navbar-link{background-color:#f2f2f2;color:#0a0a0a}.navbar.is-white .navbar-dropdown a.navbar-item.is-active{background-color:#fff;color:#0a0a0a}}.navbar.is-black{background-color:#0a0a0a;color:#fff}.navbar.is-black .navbar-brand .navbar-link,.navbar.is-black .navbar-brand>.navbar-item{color:#fff}.navbar.is-black .navbar-brand .navbar-link.is-active,.navbar.is-black .navbar-brand .navbar-link:hover,.navbar.is-black .navbar-brand>a.navbar-item.is-active,.navbar.is-black .navbar-brand>a.navbar-item:hover{background-color:#000;color:#fff}.navbar.is-black .navbar-brand .navbar-link::after{border-color:#fff}@media screen and (min-width:1088px){.navbar.is-black .navbar-end .navbar-link,.navbar.is-black .navbar-end>.navbar-item,.navbar.is-black .navbar-start .navbar-link,.navbar.is-black .navbar-start>.navbar-item{color:#fff}.navbar.is-black .navbar-end .navbar-link.is-active,.navbar.is-black .navbar-end .navbar-link:hover,.navbar.is-black .navbar-end>a.navbar-item.is-active,.navbar.is-black .navbar-end>a.navbar-item:hover,.navbar.is-black .navbar-start .navbar-link.is-active,.navbar.is-black .navbar-start .navbar-link:hover,.navbar.is-black .navbar-start>a.navbar-item.is-active,.navbar.is-black .navbar-start>a.navbar-item:hover{background-color:#000;color:#fff}.navbar.is-black .navbar-end .navbar-link::after,.navbar.is-black .navbar-start .navbar-link::after{border-color:#fff}.navbar.is-black .navbar-item.has-dropdown.is-active .navbar-link,.navbar.is-black .navbar-item.has-dropdown:hover .navbar-link{background-color:#000;color:#fff}.navbar.is-black .navbar-dropdown a.navbar-item.is-active{background-color:#0a0a0a;color:#fff}}.navbar.is-light{background-color:#f5f5f5;color:#363636}.navbar.is-light .navbar-brand .navbar-link,.navbar.is-light .navbar-brand>.navbar-item{color:#363636}.navbar.is-light .navbar-brand .navbar-link.is-active,.navbar.is-light .navbar-brand .navbar-link:hover,.navbar.is-light .navbar-brand>a.navbar-item.is-active,.navbar.is-light .navbar-brand>a.navbar-item:hover{background-color:#e8e8e8;color:#363636}.navbar.is-light .navbar-brand .navbar-link::after{border-color:#363636}@media screen and (min-width:1088px){.navbar.is-light .navbar-end .navbar-link,.navbar.is-light .navbar-end>.navbar-item,.navbar.is-light .navbar-start .navbar-link,.navbar.is-light .navbar-start>.navbar-item{color:#363636}.navbar.is-light .navbar-end .navbar-link.is-active,.navbar.is-light .navbar-end .navbar-link:hover,.navbar.is-light .navbar-end>a.navbar-item.is-active,.navbar.is-light .navbar-end>a.navbar-item:hover,.navbar.is-light .navbar-start .navbar-link.is-active,.navbar.is-light .navbar-start .navbar-link:hover,.navbar.is-light .navbar-start>a.navbar-item.is-active,.navbar.is-light .navbar-start>a.navbar-item:hover{background-color:#e8e8e8;color:#363636}.navbar.is-light .navbar-end .navbar-link::after,.navbar.is-light .navbar-start .navbar-link::after{border-color:#363636}.navbar.is-light .navbar-item.has-dropdown.is-active .navbar-link,.navbar.is-light .navbar-item.has-dropdown:hover .navbar-link{background-color:#e8e8e8;color:#363636}.navbar.is-light .navbar-dropdown a.navbar-item.is-active{background-color:#f5f5f5;color:#363636}}.navbar.is-dark{background-color:#363636;color:#f5f5f5}.navbar.is-dark .navbar-brand .navbar-link,.navbar.is-dark .navbar-brand>.navbar-item{color:#f5f5f5}.navbar.is-dark .navbar-brand .navbar-link.is-active,.navbar.is-dark .navbar-brand .navbar-link:hover,.navbar.is-dark .navbar-brand>a.navbar-item.is-active,.navbar.is-dark .navbar-brand>a.navbar-item:hover{background-color:#292929;color:#f5f5f5}.navbar.is-dark .navbar-brand .navbar-link::after{border-color:#f5f5f5}@media screen and (min-width:1088px){.navbar.is-dark .navbar-end .navbar-link,.navbar.is-dark .navbar-end>.navbar-item,.navbar.is-dark .navbar-start .navbar-link,.navbar.is-dark .navbar-start>.navbar-item{color:#f5f5f5}.navbar.is-dark .navbar-end .navbar-link.is-active,.navbar.is-dark .navbar-end .navbar-link:hover,.navbar.is-dark .navbar-end>a.navbar-item.is-active,.navbar.is-dark .navbar-end>a.navbar-item:hover,.navbar.is-dark .navbar-start .navbar-link.is-active,.navbar.is-dark .navbar-start .navbar-link:hover,.navbar.is-dark .navbar-start>a.navbar-item.is-active,.navbar.is-dark .navbar-start>a.navbar-item:hover{background-color:#292929;color:#f5f5f5}.navbar.is-dark .navbar-end .navbar-link::after,.navbar.is-dark .navbar-start .navbar-link::after{border-color:#f5f5f5}.navbar.is-dark .navbar-item.has-dropdown.is-active .navbar-link,.navbar.is-dark .navbar-item.has-dropdown:hover .navbar-link{background-color:#292929;color:#f5f5f5}.navbar.is-dark .navbar-dropdown a.navbar-item.is-active{background-color:#363636;color:#f5f5f5}}.navbar.is-primary{background-color:#00d1b2;color:#fff}.navbar.is-primary .navbar-brand .navbar-link,.navbar.is-primary .navbar-brand>.navbar-item{color:#fff}.navbar.is-primary .navbar-brand .navbar-link.is-active,.navbar.is-primary .navbar-brand .navbar-link:hover,.navbar.is-primary .navbar-brand>a.navbar-item.is-active,.navbar.is-primary .navbar-brand>a.navbar-item:hover{background-color:#00b89c;color:#fff}.navbar.is-primary .navbar-brand .navbar-link::after{border-color:#fff}@media screen and (min-width:1088px){.navbar.is-primary .navbar-end .navbar-link,.navbar.is-primary .navbar-end>.navbar-item,.navbar.is-primary .navbar-start .navbar-link,.navbar.is-primary .navbar-start>.navbar-item{color:#fff}.navbar.is-primary .navbar-end .navbar-link.is-active,.navbar.is-primary .navbar-end .navbar-link:hover,.navbar.is-primary .navbar-end>a.navbar-item.is-active,.navbar.is-primary .navbar-end>a.navbar-item:hover,.navbar.is-primary .navbar-start .navbar-link.is-active,.navbar.is-primary .navbar-start .navbar-link:hover,.navbar.is-primary .navbar-start>a.navbar-item.is-active,.navbar.is-primary .navbar-start>a.navbar-item:hover{background-color:#00b89c;color:#fff}.navbar.is-primary .navbar-end .navbar-link::after,.navbar.is-primary .navbar-start .navbar-link::after{border-color:#fff}.navbar.is-primary .navbar-item.has-dropdown.is-active .navbar-link,.navbar.is-primary .navbar-item.has-dropdown:hover .navbar-link{background-color:#00b89c;color:#fff}.navbar.is-primary .navbar-dropdown a.navbar-item.is-active{background-color:#00d1b2;color:#fff}}.navbar.is-link{background-color:#3273dc;color:#fff}.navbar.is-link .navbar-brand .navbar-link,.navbar.is-link .navbar-brand>.navbar-item{color:#fff}.navbar.is-link .navbar-brand .navbar-link.is-active,.navbar.is-link .navbar-brand .navbar-link:hover,.navbar.is-link .navbar-brand>a.navbar-item.is-active,.navbar.is-link .navbar-brand>a.navbar-item:hover{background-color:#2366d1;color:#fff}.navbar.is-link .navbar-brand .navbar-link::after{border-color:#fff}@media screen and (min-width:1088px){.navbar.is-link .navbar-end .navbar-link,.navbar.is-link .navbar-end>.navbar-item,.navbar.is-link .navbar-start .navbar-link,.navbar.is-link .navbar-start>.navbar-item{color:#fff}.navbar.is-link .navbar-end .navbar-link.is-active,.navbar.is-link .navbar-end .navbar-link:hover,.navbar.is-link .navbar-end>a.navbar-item.is-active,.navbar.is-link .navbar-end>a.navbar-item:hover,.navbar.is-link .navbar-start .navbar-link.is-active,.navbar.is-link .navbar-start .navbar-link:hover,.navbar.is-link .navbar-start>a.navbar-item.is-active,.navbar.is-link .navbar-start>a.navbar-item:hover{background-color:#2366d1;color:#fff}.navbar.is-link .navbar-end .navbar-link::after,.navbar.is-link .navbar-start .navbar-link::after{border-color:#fff}.navbar.is-link .navbar-item.has-dropdown.is-active .navbar-link,.navbar.is-link .navbar-item.has-dropdown:hover .navbar-link{background-color:#2366d1;color:#fff}.navbar.is-link .navbar-dropdown a.navbar-item.is-active{background-color:#3273dc;color:#fff}}.navbar.is-info{background-color:#209cee;color:#fff}.navbar.is-info .navbar-brand .navbar-link,.navbar.is-info .navbar-brand>.navbar-item{color:#fff}.navbar.is-info .navbar-brand .navbar-link.is-active,.navbar.is-info .navbar-brand .navbar-link:hover,.navbar.is-info .navbar-brand>a.navbar-item.is-active,.navbar.is-info .navbar-brand>a.navbar-item:hover{background-color:#118fe4;color:#fff}.navbar.is-info .navbar-brand .navbar-link::after{border-color:#fff}@media screen and (min-width:1088px){.navbar.is-info .navbar-end .navbar-link,.navbar.is-info .navbar-end>.navbar-item,.navbar.is-info .navbar-start .navbar-link,.navbar.is-info .navbar-start>.navbar-item{color:#fff}.navbar.is-info .navbar-end .navbar-link.is-active,.navbar.is-info .navbar-end .navbar-link:hover,.navbar.is-info .navbar-end>a.navbar-item.is-active,.navbar.is-info .navbar-end>a.navbar-item:hover,.navbar.is-info .navbar-start .navbar-link.is-active,.navbar.is-info .navbar-start .navbar-link:hover,.navbar.is-info .navbar-start>a.navbar-item.is-active,.navbar.is-info .navbar-start>a.navbar-item:hover{background-color:#118fe4;color:#fff}.navbar.is-info .navbar-end .navbar-link::after,.navbar.is-info .navbar-start .navbar-link::after{border-color:#fff}.navbar.is-info .navbar-item.has-dropdown.is-active .navbar-link,.navbar.is-info .navbar-item.has-dropdown:hover .navbar-link{background-color:#118fe4;color:#fff}.navbar.is-info .navbar-dropdown a.navbar-item.is-active{background-color:#209cee;color:#fff}}.navbar.is-success{background-color:#23d160;color:#fff}.navbar.is-success .navbar-brand .navbar-link,.navbar.is-success .navbar-brand>.navbar-item{color:#fff}.navbar.is-success .navbar-brand .navbar-link.is-active,.navbar.is-success .navbar-brand .navbar-link:hover,.navbar.is-success .navbar-brand>a.navbar-item.is-active,.navbar.is-success .navbar-brand>a.navbar-item:hover{background-color:#20bc56;color:#fff}.navbar.is-success .navbar-brand .navbar-link::after{border-color:#fff}@media screen and (min-width:1088px){.navbar.is-success .navbar-end .navbar-link,.navbar.is-success .navbar-end>.navbar-item,.navbar.is-success .navbar-start .navbar-link,.navbar.is-success .navbar-start>.navbar-item{color:#fff}.navbar.is-success .navbar-end .navbar-link.is-active,.navbar.is-success .navbar-end .navbar-link:hover,.navbar.is-success .navbar-end>a.navbar-item.is-active,.navbar.is-success .navbar-end>a.navbar-item:hover,.navbar.is-success .navbar-start .navbar-link.is-active,.navbar.is-success .navbar-start .navbar-link:hover,.navbar.is-success .navbar-start>a.navbar-item.is-active,.navbar.is-success .navbar-start>a.navbar-item:hover{background-color:#20bc56;color:#fff}.navbar.is-success .navbar-end .navbar-link::after,.navbar.is-success .navbar-start .navbar-link::after{border-color:#fff}.navbar.is-success .navbar-item.has-dropdown.is-active .navbar-link,.navbar.is-success .navbar-item.has-dropdown:hover .navbar-link{background-color:#20bc56;color:#fff}.navbar.is-success .navbar-dropdown a.navbar-item.is-active{background-color:#23d160;color:#fff}}.navbar.is-warning{background-color:#ffdd57;color:rgba(0,0,0,.7)}.navbar.is-warning .navbar-brand .navbar-link,.navbar.is-warning .navbar-brand>.navbar-item{color:rgba(0,0,0,.7)}.navbar.is-warning .navbar-brand .navbar-link.is-active,.navbar.is-warning .navbar-brand .navbar-link:hover,.navbar.is-warning .navbar-brand>a.navbar-item.is-active,.navbar.is-warning .navbar-brand>a.navbar-item:hover{background-color:#ffd83d;color:rgba(0,0,0,.7)}.navbar.is-warning .navbar-brand .navbar-link::after{border-color:rgba(0,0,0,.7)}@media screen and (min-width:1088px){.navbar.is-warning .navbar-end .navbar-link,.navbar.is-warning .navbar-end>.navbar-item,.navbar.is-warning .navbar-start .navbar-link,.navbar.is-warning .navbar-start>.navbar-item{color:rgba(0,0,0,.7)}.navbar.is-warning .navbar-end .navbar-link.is-active,.navbar.is-warning .navbar-end .navbar-link:hover,.navbar.is-warning .navbar-end>a.navbar-item.is-active,.navbar.is-warning .navbar-end>a.navbar-item:hover,.navbar.is-warning .navbar-start .navbar-link.is-active,.navbar.is-warning .navbar-start .navbar-link:hover,.navbar.is-warning .navbar-start>a.navbar-item.is-active,.navbar.is-warning .navbar-start>a.navbar-item:hover{background-color:#ffd83d;color:rgba(0,0,0,.7)}.navbar.is-warning .navbar-end .navbar-link::after,.navbar.is-warning .navbar-start .navbar-link::after{border-color:rgba(0,0,0,.7)}.navbar.is-warning .navbar-item.has-dropdown.is-active .navbar-link,.navbar.is-warning .navbar-item.has-dropdown:hover .navbar-link{background-color:#ffd83d;color:rgba(0,0,0,.7)}.navbar.is-warning .navbar-dropdown a.navbar-item.is-active{background-color:#ffdd57;color:rgba(0,0,0,.7)}}.navbar.is-danger{background-color:#ff3860;color:#fff}.navbar.is-danger .navbar-brand .navbar-link,.navbar.is-danger .navbar-brand>.navbar-item{color:#fff}.navbar.is-danger .navbar-brand .navbar-link.is-active,.navbar.is-danger .navbar-brand .navbar-link:hover,.navbar.is-danger .navbar-brand>a.navbar-item.is-active,.navbar.is-danger .navbar-brand>a.navbar-item:hover{background-color:#ff1f4b;color:#fff}.navbar.is-danger .navbar-brand .navbar-link::after{border-color:#fff}@media screen and (min-width:1088px){.navbar.is-danger .navbar-end .navbar-link,.navbar.is-danger .navbar-end>.navbar-item,.navbar.is-danger .navbar-start .navbar-link,.navbar.is-danger .navbar-start>.navbar-item{color:#fff}.navbar.is-danger .navbar-end .navbar-link.is-active,.navbar.is-danger .navbar-end .navbar-link:hover,.navbar.is-danger .navbar-end>a.navbar-item.is-active,.navbar.is-danger .navbar-end>a.navbar-item:hover,.navbar.is-danger .navbar-start .navbar-link.is-active,.navbar.is-danger .navbar-start .navbar-link:hover,.navbar.is-danger .navbar-start>a.navbar-item.is-active,.navbar.is-danger .navbar-start>a.navbar-item:hover{background-color:#ff1f4b;color:#fff}.navbar.is-danger .navbar-end .navbar-link::after,.navbar.is-danger .navbar-start .navbar-link::after{border-color:#fff}.navbar.is-danger .navbar-item.has-dropdown.is-active .navbar-link,.navbar.is-danger .navbar-item.has-dropdown:hover .navbar-link{background-color:#ff1f4b;color:#fff}.navbar.is-danger .navbar-dropdown a.navbar-item.is-active{background-color:#ff3860;color:#fff}}.navbar>.container{align-items:stretch;display:flex;min-height:3.25rem;width:100%}.navbar.has-shadow{box-shadow:0 2px 0 0 #f5f5f5}.navbar.is-fixed-bottom,.navbar.is-fixed-top{left:0;position:fixed;right:0;z-index:30}.navbar.is-fixed-bottom{bottom:0}.navbar.is-fixed-bottom.has-shadow{box-shadow:0 -2px 0 0 #f5f5f5}.navbar.is-fixed-top{top:0}body.has-navbar-fixed-top,html.has-navbar-fixed-top{padding-top:3.25rem}body.has-navbar-fixed-bottom,html.has-navbar-fixed-bottom{padding-bottom:3.25rem}.navbar-brand,.navbar-tabs{align-items:stretch;display:flex;flex-shrink:0;min-height:3.25rem}.navbar-brand a.navbar-item:hover{background-color:transparent}.navbar-tabs{-webkit-overflow-scrolling:touch;max-width:100vw;overflow-x:auto;overflow-y:hidden}.navbar-burger{cursor:pointer;display:block;height:3.25rem;position:relative;width:3.25rem;margin-left:auto}.navbar-burger span{background-color:currentColor;display:block;height:1px;left:calc(50% - 8px);position:absolute;-webkit-transform-origin:center;transform-origin:center;transition-duration:86ms;transition-property:background-color,opacity,-webkit-transform;transition-property:background-color,opacity,transform;transition-property:background-color,opacity,transform,-webkit-transform;transition-timing-function:ease-out;width:16px}.navbar-burger span:nth-child(1){top:calc(50% - 6px)}.navbar-burger span:nth-child(2){top:calc(50% - 1px)}.navbar-burger span:nth-child(3){top:calc(50% + 4px)}.navbar-burger:hover{background-color:rgba(0,0,0,.05)}.navbar-burger.is-active span:nth-child(1){-webkit-transform:translateY(5px) rotate(45deg);transform:translateY(5px) rotate(45deg)}.navbar-burger.is-active span:nth-child(2){opacity:0}.navbar-burger.is-active span:nth-child(3){-webkit-transform:translateY(-5px) rotate(-45deg);transform:translateY(-5px) rotate(-45deg)}.navbar-menu{display:none}.navbar-item,.navbar-link{color:#4a4a4a;display:block;line-height:1.5;padding:.5rem .75rem;position:relative}.navbar-item .icon:only-child,.navbar-link .icon:only-child{margin-left:-.25rem;margin-right:-.25rem}.navbar-link,a.navbar-item{cursor:pointer}.navbar-link.is-active,.navbar-link:hover,a.navbar-item.is-active,a.navbar-item:hover{background-color:#fafafa;color:#3273dc}.navbar-item{display:block;flex-grow:0;flex-shrink:0}.navbar-item img{max-height:1.75rem}.navbar-item.has-dropdown{padding:0}.navbar-item.is-expanded{flex-grow:1;flex-shrink:1}.navbar-item.is-tab{border-bottom:1px solid transparent;min-height:3.25rem;padding-bottom:calc(.5rem - 1px)}.navbar-item.is-tab:hover{background-color:transparent;border-bottom-color:#3273dc}.navbar-item.is-tab.is-active{background-color:transparent;border-bottom-color:#3273dc;border-bottom-style:solid;border-bottom-width:3px;color:#3273dc;padding-bottom:calc(.5rem - 3px)}.navbar-content{flex-grow:1;flex-shrink:1}.navbar-link{padding-right:2.5em}.navbar-link::after{border-color:#3273dc;margin-top:-.375em;right:1.125em}.navbar-dropdown{font-size:.875rem;padding-bottom:.5rem;padding-top:.5rem}.navbar-dropdown .navbar-item{padding-left:1.5rem;padding-right:1.5rem}.navbar-divider{background-color:#f5f5f5;border:none;display:none;height:2px;margin:.5rem 0}@media screen and (max-width:1087px){.navbar>.container{display:block}.navbar-brand .navbar-item,.navbar-tabs .navbar-item{align-items:center;display:flex}.navbar-link::after{display:none}.navbar-menu{background-color:#fff;box-shadow:0 8px 16px rgba(10,10,10,.1);padding:.5rem 0}.navbar-menu.is-active{display:block}.navbar.is-fixed-bottom-touch,.navbar.is-fixed-top-touch{left:0;position:fixed;right:0;z-index:30}.navbar.is-fixed-bottom-touch{bottom:0}.navbar.is-fixed-bottom-touch.has-shadow{box-shadow:0 -2px 3px rgba(10,10,10,.1)}.navbar.is-fixed-top-touch{top:0}.navbar.is-fixed-top .navbar-menu,.navbar.is-fixed-top-touch .navbar-menu{-webkit-overflow-scrolling:touch;max-height:calc(100vh - 3.25rem);overflow:auto}body.has-navbar-fixed-top-touch,html.has-navbar-fixed-top-touch{padding-top:3.25rem}body.has-navbar-fixed-bottom-touch,html.has-navbar-fixed-bottom-touch{padding-bottom:3.25rem}}@media screen and (min-width:1088px){.navbar,.navbar-end,.navbar-menu,.navbar-start{align-items:stretch;display:flex}.navbar{min-height:3.25rem}.navbar.is-spaced{padding:1rem 2rem}.navbar.is-spaced .navbar-end,.navbar.is-spaced .navbar-start{align-items:center}.navbar.is-spaced .navbar-link,.navbar.is-spaced a.navbar-item{border-radius:4px}.navbar.is-transparent .navbar-link.is-active,.navbar.is-transparent .navbar-link:hover,.navbar.is-transparent a.navbar-item.is-active,.navbar.is-transparent a.navbar-item:hover{background-color:transparent!important}.navbar.is-transparent .navbar-item.has-dropdown.is-active .navbar-link,.navbar.is-transparent .navbar-item.has-dropdown.is-hoverable:hover .navbar-link{background-color:transparent!important}.navbar.is-transparent .navbar-dropdown a.navbar-item:hover{background-color:#f5f5f5;color:#0a0a0a}.navbar.is-transparent .navbar-dropdown a.navbar-item.is-active{background-color:#f5f5f5;color:#3273dc}.navbar-burger{display:none}.navbar-item,.navbar-link{align-items:center;display:flex}.navbar-item{display:flex}.navbar-item.has-dropdown{align-items:stretch}.navbar-item.has-dropdown-up .navbar-link::after{-webkit-transform:rotate(135deg) translate(.25em,-.25em);transform:rotate(135deg) translate(.25em,-.25em)}.navbar-item.has-dropdown-up .navbar-dropdown{border-bottom:2px solid #dbdbdb;border-radius:6px 6px 0 0;border-top:none;bottom:100%;box-shadow:0 -8px 8px rgba(10,10,10,.1);top:auto}.navbar-item.is-active .navbar-dropdown,.navbar-item.is-hoverable:hover .navbar-dropdown{display:block}.navbar-item.is-active .navbar-dropdown.is-boxed,.navbar-item.is-hoverable:hover .navbar-dropdown.is-boxed,.navbar.is-spaced .navbar-item.is-active .navbar-dropdown,.navbar.is-spaced .navbar-item.is-hoverable:hover .navbar-dropdown{opacity:1;pointer-events:auto;-webkit-transform:translateY(0);transform:translateY(0)}.navbar-menu{flex-grow:1;flex-shrink:0}.navbar-start{justify-content:flex-start;margin-right:auto}.navbar-end{justify-content:flex-end;margin-left:auto}.navbar-dropdown{background-color:#fff;border-bottom-left-radius:6px;border-bottom-right-radius:6px;border-top:2px solid #dbdbdb;box-shadow:0 8px 8px rgba(10,10,10,.1);display:none;font-size:.875rem;left:0;min-width:100%;position:absolute;top:100%;z-index:20}.navbar-dropdown .navbar-item{padding:.375rem 1rem;white-space:nowrap}.navbar-dropdown a.navbar-item{padding-right:3rem}.navbar-dropdown a.navbar-item:hover{background-color:#f5f5f5;color:#0a0a0a}.navbar-dropdown a.navbar-item.is-active{background-color:#f5f5f5;color:#3273dc}.navbar-dropdown.is-boxed,.navbar.is-spaced .navbar-dropdown{border-radius:6px;border-top:none;box-shadow:0 8px 8px rgba(10,10,10,.1),0 0 0 1px rgba(10,10,10,.1);display:block;opacity:0;pointer-events:none;top:calc(100% + (-4px));-webkit-transform:translateY(-5px);transform:translateY(-5px);transition-duration:86ms;transition-property:opacity,-webkit-transform;transition-property:opacity,transform;transition-property:opacity,transform,-webkit-transform}.navbar-dropdown.is-right{left:auto;right:0}.navbar-divider{display:block}.container>.navbar .navbar-brand,.navbar>.container .navbar-brand{margin-left:-1rem}.container>.navbar .navbar-menu,.navbar>.container .navbar-menu{margin-right:-1rem}.navbar.is-fixed-bottom-desktop,.navbar.is-fixed-top-desktop{left:0;position:fixed;right:0;z-index:30}.navbar.is-fixed-bottom-desktop{bottom:0}.navbar.is-fixed-bottom-desktop.has-shadow{box-shadow:0 -2px 3px rgba(10,10,10,.1)}.navbar.is-fixed-top-desktop{top:0}body.has-navbar-fixed-top-desktop,html.has-navbar-fixed-top-desktop{padding-top:3.25rem}body.has-navbar-fixed-bottom-desktop,html.has-navbar-fixed-bottom-desktop{padding-bottom:3.25rem}body.has-spaced-navbar-fixed-top,html.has-spaced-navbar-fixed-top{padding-top:5.25rem}body.has-spaced-navbar-fixed-bottom,html.has-spaced-navbar-fixed-bottom{padding-bottom:5.25rem}.navbar-link.is-active,a.navbar-item.is-active{color:#0a0a0a}.navbar-link.is-active:not(:hover),a.navbar-item.is-active:not(:hover){background-color:transparent}.navbar-item.has-dropdown.is-active .navbar-link,.navbar-item.has-dropdown:hover .navbar-link{background-color:#fafafa}}.pagination{font-size:1rem;margin:-.25rem}.pagination.is-small{font-size:.75rem}.pagination.is-medium{font-size:1.25rem}.pagination.is-large{font-size:1.5rem}.pagination.is-rounded .pagination-next,.pagination.is-rounded .pagination-previous{padding-left:1em;padding-right:1em;border-radius:290486px}.pagination.is-rounded .pagination-link{border-radius:290486px}.pagination,.pagination-list{align-items:center;display:flex;justify-content:center;text-align:center}.pagination-ellipsis,.pagination-link,.pagination-next,.pagination-previous{font-size:1em;padding-left:.5em;padding-right:.5em;justify-content:center;margin:.25rem;text-align:center}.pagination-link,.pagination-next,.pagination-previous{border-color:#dbdbdb;color:#363636;min-width:2.25em}.pagination-link:hover,.pagination-next:hover,.pagination-previous:hover{border-color:#b5b5b5;color:#363636}.pagination-link:focus,.pagination-next:focus,.pagination-previous:focus{border-color:#3273dc}.pagination-link:active,.pagination-next:active,.pagination-previous:active{box-shadow:inset 0 1px 2px rgba(10,10,10,.2)}.pagination-link[disabled],.pagination-next[disabled],.pagination-previous[disabled]{background-color:#dbdbdb;border-color:#dbdbdb;box-shadow:none;color:#7a7a7a;opacity:.5}.pagination-next,.pagination-previous{padding-left:.75em;padding-right:.75em;white-space:nowrap}.pagination-link.is-current{background-color:#3273dc;border-color:#3273dc;color:#fff}.pagination-ellipsis{color:#b5b5b5;pointer-events:none}.pagination-list{flex-wrap:wrap}@media screen and (max-width:768px){.pagination{flex-wrap:wrap}.pagination-next,.pagination-previous{flex-grow:1;flex-shrink:1}.pagination-list li{flex-grow:1;flex-shrink:1}}@media screen and (min-width:769px),print{.pagination-list{flex-grow:1;flex-shrink:1;justify-content:flex-start;order:1}.pagination-previous{order:2}.pagination-next{order:3}.pagination{justify-content:space-between}.pagination.is-centered .pagination-previous{order:1}.pagination.is-centered .pagination-list{justify-content:center;order:2}.pagination.is-centered .pagination-next{order:3}.pagination.is-right .pagination-previous{order:1}.pagination.is-right .pagination-next{order:2}.pagination.is-right .pagination-list{justify-content:flex-end;order:3}}.panel{font-size:1rem}.panel:not(:last-child){margin-bottom:1.5rem}.panel-block,.panel-heading,.panel-tabs{border-bottom:1px solid #dbdbdb;border-left:1px solid #dbdbdb;border-right:1px solid #dbdbdb}.panel-block:first-child,.panel-heading:first-child,.panel-tabs:first-child{border-top:1px solid #dbdbdb}.panel-heading{background-color:#f5f5f5;border-radius:4px 4px 0 0;color:#363636;font-size:1.25em;font-weight:300;line-height:1.25;padding:.5em .75em}.panel-tabs{align-items:flex-end;display:flex;font-size:.875em;justify-content:center}.panel-tabs a{border-bottom:1px solid #dbdbdb;margin-bottom:-1px;padding:.5em}.panel-tabs a.is-active{border-bottom-color:#4a4a4a;color:#363636}.panel-list a{color:#4a4a4a}.panel-list a:hover{color:#3273dc}.panel-block{align-items:center;color:#363636;display:flex;justify-content:flex-start;padding:.5em .75em}.panel-block input[type=checkbox]{margin-right:.75em}.panel-block>.control{flex-grow:1;flex-shrink:1;width:100%}.panel-block.is-wrapped{flex-wrap:wrap}.panel-block.is-active{border-left-color:#3273dc;color:#363636}.panel-block.is-active .panel-icon{color:#3273dc}a.panel-block,label.panel-block{cursor:pointer}a.panel-block:hover,label.panel-block:hover{background-color:#f5f5f5}.panel-icon{display:inline-block;font-size:14px;height:1em;line-height:1em;text-align:center;vertical-align:top;width:1em;color:#7a7a7a;margin-right:.75em}.panel-icon .fa{font-size:inherit;line-height:inherit}.tabs{-webkit-overflow-scrolling:touch;align-items:stretch;display:flex;font-size:1rem;justify-content:space-between;overflow:hidden;overflow-x:auto;white-space:nowrap}.tabs a{align-items:center;border-bottom-color:#dbdbdb;border-bottom-style:solid;border-bottom-width:1px;color:#4a4a4a;display:flex;justify-content:center;margin-bottom:-1px;padding:.5em 1em;vertical-align:top}.tabs a:hover{border-bottom-color:#363636;color:#363636}.tabs li{display:block}.tabs li.is-active a{border-bottom-color:#3273dc;color:#3273dc}.tabs ul{align-items:center;border-bottom-color:#dbdbdb;border-bottom-style:solid;border-bottom-width:1px;display:flex;flex-grow:1;flex-shrink:0;justify-content:flex-start}.tabs ul.is-left{padding-right:.75em}.tabs ul.is-center{flex:none;justify-content:center;padding-left:.75em;padding-right:.75em}.tabs ul.is-right{justify-content:flex-end;padding-left:.75em}.tabs .icon:first-child{margin-right:.5em}.tabs .icon:last-child{margin-left:.5em}.tabs.is-centered ul{justify-content:center}.tabs.is-right ul{justify-content:flex-end}.tabs.is-boxed a{border:1px solid transparent;border-radius:4px 4px 0 0}.tabs.is-boxed a:hover{background-color:#f5f5f5;border-bottom-color:#dbdbdb}.tabs.is-boxed li.is-active a{background-color:#fff;border-color:#dbdbdb;border-bottom-color:transparent!important}.tabs.is-fullwidth li{flex-grow:1;flex-shrink:0}.tabs.is-toggle a{border-color:#dbdbdb;border-style:solid;border-width:1px;margin-bottom:0;position:relative}.tabs.is-toggle a:hover{background-color:#f5f5f5;border-color:#b5b5b5;z-index:2}.tabs.is-toggle li+li{margin-left:-1px}.tabs.is-toggle li:first-child a{border-radius:4px 0 0 4px}.tabs.is-toggle li:last-child a{border-radius:0 4px 4px 0}.tabs.is-toggle li.is-active a{background-color:#3273dc;border-color:#3273dc;color:#fff;z-index:1}.tabs.is-toggle ul{border-bottom:none}.tabs.is-toggle.is-toggle-rounded li:first-child a{border-bottom-left-radius:290486px;border-top-left-radius:290486px;padding-left:1.25em}.tabs.is-toggle.is-toggle-rounded li:last-child a{border-bottom-right-radius:290486px;border-top-right-radius:290486px;padding-right:1.25em}.tabs.is-small{font-size:.75rem}.tabs.is-medium{font-size:1.25rem}.tabs.is-large{font-size:1.5rem}.column{display:block;flex-basis:0;flex-grow:1;flex-shrink:1;padding:.75rem}.columns.is-mobile>.column.is-narrow{flex:none}.columns.is-mobile>.column.is-full{flex:none;width:100%}.columns.is-mobile>.column.is-three-quarters{flex:none;width:75%}.columns.is-mobile>.column.is-two-thirds{flex:none;width:66.6666%}.columns.is-mobile>.column.is-half{flex:none;width:50%}.columns.is-mobile>.column.is-one-third{flex:none;width:33.3333%}.columns.is-mobile>.column.is-one-quarter{flex:none;width:25%}.columns.is-mobile>.column.is-one-fifth{flex:none;width:20%}.columns.is-mobile>.column.is-two-fifths{flex:none;width:40%}.columns.is-mobile>.column.is-three-fifths{flex:none;width:60%}.columns.is-mobile>.column.is-four-fifths{flex:none;width:80%}.columns.is-mobile>.column.is-offset-three-quarters{margin-left:75%}.columns.is-mobile>.column.is-offset-two-thirds{margin-left:66.6666%}.columns.is-mobile>.column.is-offset-half{margin-left:50%}.columns.is-mobile>.column.is-offset-one-third{margin-left:33.3333%}.columns.is-mobile>.column.is-offset-one-quarter{margin-left:25%}.columns.is-mobile>.column.is-offset-one-fifth{margin-left:20%}.columns.is-mobile>.column.is-offset-two-fifths{margin-left:40%}.columns.is-mobile>.column.is-offset-three-fifths{margin-left:60%}.columns.is-mobile>.column.is-offset-four-fifths{margin-left:80%}.columns.is-mobile>.column.is-1{flex:none;width:8.33333%}.columns.is-mobile>.column.is-offset-1{margin-left:8.33333%}.columns.is-mobile>.column.is-2{flex:none;width:16.66667%}.columns.is-mobile>.column.is-offset-2{margin-left:16.66667%}.columns.is-mobile>.column.is-3{flex:none;width:25%}.columns.is-mobile>.column.is-offset-3{margin-left:25%}.columns.is-mobile>.column.is-4{flex:none;width:33.33333%}.columns.is-mobile>.column.is-offset-4{margin-left:33.33333%}.columns.is-mobile>.column.is-5{flex:none;width:41.66667%}.columns.is-mobile>.column.is-offset-5{margin-left:41.66667%}.columns.is-mobile>.column.is-6{flex:none;width:50%}.columns.is-mobile>.column.is-offset-6{margin-left:50%}.columns.is-mobile>.column.is-7{flex:none;width:58.33333%}.columns.is-mobile>.column.is-offset-7{margin-left:58.33333%}.columns.is-mobile>.column.is-8{flex:none;width:66.66667%}.columns.is-mobile>.column.is-offset-8{margin-left:66.66667%}.columns.is-mobile>.column.is-9{flex:none;width:75%}.columns.is-mobile>.column.is-offset-9{margin-left:75%}.columns.is-mobile>.column.is-10{flex:none;width:83.33333%}.columns.is-mobile>.column.is-offset-10{margin-left:83.33333%}.columns.is-mobile>.column.is-11{flex:none;width:91.66667%}.columns.is-mobile>.column.is-offset-11{margin-left:91.66667%}.columns.is-mobile>.column.is-12{flex:none;width:100%}.columns.is-mobile>.column.is-offset-12{margin-left:100%}@media screen and (max-width:768px){.column.is-narrow-mobile{flex:none}.column.is-full-mobile{flex:none;width:100%}.column.is-three-quarters-mobile{flex:none;width:75%}.column.is-two-thirds-mobile{flex:none;width:66.6666%}.column.is-half-mobile{flex:none;width:50%}.column.is-one-third-mobile{flex:none;width:33.3333%}.column.is-one-quarter-mobile{flex:none;width:25%}.column.is-one-fifth-mobile{flex:none;width:20%}.column.is-two-fifths-mobile{flex:none;width:40%}.column.is-three-fifths-mobile{flex:none;width:60%}.column.is-four-fifths-mobile{flex:none;width:80%}.column.is-offset-three-quarters-mobile{margin-left:75%}.column.is-offset-two-thirds-mobile{margin-left:66.6666%}.column.is-offset-half-mobile{margin-left:50%}.column.is-offset-one-third-mobile{margin-left:33.3333%}.column.is-offset-one-quarter-mobile{margin-left:25%}.column.is-offset-one-fifth-mobile{margin-left:20%}.column.is-offset-two-fifths-mobile{margin-left:40%}.column.is-offset-three-fifths-mobile{margin-left:60%}.column.is-offset-four-fifths-mobile{margin-left:80%}.column.is-1-mobile{flex:none;width:8.33333%}.column.is-offset-1-mobile{margin-left:8.33333%}.column.is-2-mobile{flex:none;width:16.66667%}.column.is-offset-2-mobile{margin-left:16.66667%}.column.is-3-mobile{flex:none;width:25%}.column.is-offset-3-mobile{margin-left:25%}.column.is-4-mobile{flex:none;width:33.33333%}.column.is-offset-4-mobile{margin-left:33.33333%}.column.is-5-mobile{flex:none;width:41.66667%}.column.is-offset-5-mobile{margin-left:41.66667%}.column.is-6-mobile{flex:none;width:50%}.column.is-offset-6-mobile{margin-left:50%}.column.is-7-mobile{flex:none;width:58.33333%}.column.is-offset-7-mobile{margin-left:58.33333%}.column.is-8-mobile{flex:none;width:66.66667%}.column.is-offset-8-mobile{margin-left:66.66667%}.column.is-9-mobile{flex:none;width:75%}.column.is-offset-9-mobile{margin-left:75%}.column.is-10-mobile{flex:none;width:83.33333%}.column.is-offset-10-mobile{margin-left:83.33333%}.column.is-11-mobile{flex:none;width:91.66667%}.column.is-offset-11-mobile{margin-left:91.66667%}.column.is-12-mobile{flex:none;width:100%}.column.is-offset-12-mobile{margin-left:100%}}@media screen and (min-width:769px),print{.column.is-narrow,.column.is-narrow-tablet{flex:none}.column.is-full,.column.is-full-tablet{flex:none;width:100%}.column.is-three-quarters,.column.is-three-quarters-tablet{flex:none;width:75%}.column.is-two-thirds,.column.is-two-thirds-tablet{flex:none;width:66.6666%}.column.is-half,.column.is-half-tablet{flex:none;width:50%}.column.is-one-third,.column.is-one-third-tablet{flex:none;width:33.3333%}.column.is-one-quarter,.column.is-one-quarter-tablet{flex:none;width:25%}.column.is-one-fifth,.column.is-one-fifth-tablet{flex:none;width:20%}.column.is-two-fifths,.column.is-two-fifths-tablet{flex:none;width:40%}.column.is-three-fifths,.column.is-three-fifths-tablet{flex:none;width:60%}.column.is-four-fifths,.column.is-four-fifths-tablet{flex:none;width:80%}.column.is-offset-three-quarters,.column.is-offset-three-quarters-tablet{margin-left:75%}.column.is-offset-two-thirds,.column.is-offset-two-thirds-tablet{margin-left:66.6666%}.column.is-offset-half,.column.is-offset-half-tablet{margin-left:50%}.column.is-offset-one-third,.column.is-offset-one-third-tablet{margin-left:33.3333%}.column.is-offset-one-quarter,.column.is-offset-one-quarter-tablet{margin-left:25%}.column.is-offset-one-fifth,.column.is-offset-one-fifth-tablet{margin-left:20%}.column.is-offset-two-fifths,.column.is-offset-two-fifths-tablet{margin-left:40%}.column.is-offset-three-fifths,.column.is-offset-three-fifths-tablet{margin-left:60%}.column.is-offset-four-fifths,.column.is-offset-four-fifths-tablet{margin-left:80%}.column.is-1,.column.is-1-tablet{flex:none;width:8.33333%}.column.is-offset-1,.column.is-offset-1-tablet{margin-left:8.33333%}.column.is-2,.column.is-2-tablet{flex:none;width:16.66667%}.column.is-offset-2,.column.is-offset-2-tablet{margin-left:16.66667%}.column.is-3,.column.is-3-tablet{flex:none;width:25%}.column.is-offset-3,.column.is-offset-3-tablet{margin-left:25%}.column.is-4,.column.is-4-tablet{flex:none;width:33.33333%}.column.is-offset-4,.column.is-offset-4-tablet{margin-left:33.33333%}.column.is-5,.column.is-5-tablet{flex:none;width:41.66667%}.column.is-offset-5,.column.is-offset-5-tablet{margin-left:41.66667%}.column.is-6,.column.is-6-tablet{flex:none;width:50%}.column.is-offset-6,.column.is-offset-6-tablet{margin-left:50%}.column.is-7,.column.is-7-tablet{flex:none;width:58.33333%}.column.is-offset-7,.column.is-offset-7-tablet{margin-left:58.33333%}.column.is-8,.column.is-8-tablet{flex:none;width:66.66667%}.column.is-offset-8,.column.is-offset-8-tablet{margin-left:66.66667%}.column.is-9,.column.is-9-tablet{flex:none;width:75%}.column.is-offset-9,.column.is-offset-9-tablet{margin-left:75%}.column.is-10,.column.is-10-tablet{flex:none;width:83.33333%}.column.is-offset-10,.column.is-offset-10-tablet{margin-left:83.33333%}.column.is-11,.column.is-11-tablet{flex:none;width:91.66667%}.column.is-offset-11,.column.is-offset-11-tablet{margin-left:91.66667%}.column.is-12,.column.is-12-tablet{flex:none;width:100%}.column.is-offset-12,.column.is-offset-12-tablet{margin-left:100%}}@media screen and (max-width:1087px){.column.is-narrow-touch{flex:none}.column.is-full-touch{flex:none;width:100%}.column.is-three-quarters-touch{flex:none;width:75%}.column.is-two-thirds-touch{flex:none;width:66.6666%}.column.is-half-touch{flex:none;width:50%}.column.is-one-third-touch{flex:none;width:33.3333%}.column.is-one-quarter-touch{flex:none;width:25%}.column.is-one-fifth-touch{flex:none;width:20%}.column.is-two-fifths-touch{flex:none;width:40%}.column.is-three-fifths-touch{flex:none;width:60%}.column.is-four-fifths-touch{flex:none;width:80%}.column.is-offset-three-quarters-touch{margin-left:75%}.column.is-offset-two-thirds-touch{margin-left:66.6666%}.column.is-offset-half-touch{margin-left:50%}.column.is-offset-one-third-touch{margin-left:33.3333%}.column.is-offset-one-quarter-touch{margin-left:25%}.column.is-offset-one-fifth-touch{margin-left:20%}.column.is-offset-two-fifths-touch{margin-left:40%}.column.is-offset-three-fifths-touch{margin-left:60%}.column.is-offset-four-fifths-touch{margin-left:80%}.column.is-1-touch{flex:none;width:8.33333%}.column.is-offset-1-touch{margin-left:8.33333%}.column.is-2-touch{flex:none;width:16.66667%}.column.is-offset-2-touch{margin-left:16.66667%}.column.is-3-touch{flex:none;width:25%}.column.is-offset-3-touch{margin-left:25%}.column.is-4-touch{flex:none;width:33.33333%}.column.is-offset-4-touch{margin-left:33.33333%}.column.is-5-touch{flex:none;width:41.66667%}.column.is-offset-5-touch{margin-left:41.66667%}.column.is-6-touch{flex:none;width:50%}.column.is-offset-6-touch{margin-left:50%}.column.is-7-touch{flex:none;width:58.33333%}.column.is-offset-7-touch{margin-left:58.33333%}.column.is-8-touch{flex:none;width:66.66667%}.column.is-offset-8-touch{margin-left:66.66667%}.column.is-9-touch{flex:none;width:75%}.column.is-offset-9-touch{margin-left:75%}.column.is-10-touch{flex:none;width:83.33333%}.column.is-offset-10-touch{margin-left:83.33333%}.column.is-11-touch{flex:none;width:91.66667%}.column.is-offset-11-touch{margin-left:91.66667%}.column.is-12-touch{flex:none;width:100%}.column.is-offset-12-touch{margin-left:100%}}@media screen and (min-width:1088px){.column.is-narrow-desktop{flex:none}.column.is-full-desktop{flex:none;width:100%}.column.is-three-quarters-desktop{flex:none;width:75%}.column.is-two-thirds-desktop{flex:none;width:66.6666%}.column.is-half-desktop{flex:none;width:50%}.column.is-one-third-desktop{flex:none;width:33.3333%}.column.is-one-quarter-desktop{flex:none;width:25%}.column.is-one-fifth-desktop{flex:none;width:20%}.column.is-two-fifths-desktop{flex:none;width:40%}.column.is-three-fifths-desktop{flex:none;width:60%}.column.is-four-fifths-desktop{flex:none;width:80%}.column.is-offset-three-quarters-desktop{margin-left:75%}.column.is-offset-two-thirds-desktop{margin-left:66.6666%}.column.is-offset-half-desktop{margin-left:50%}.column.is-offset-one-third-desktop{margin-left:33.3333%}.column.is-offset-one-quarter-desktop{margin-left:25%}.column.is-offset-one-fifth-desktop{margin-left:20%}.column.is-offset-two-fifths-desktop{margin-left:40%}.column.is-offset-three-fifths-desktop{margin-left:60%}.column.is-offset-four-fifths-desktop{margin-left:80%}.column.is-1-desktop{flex:none;width:8.33333%}.column.is-offset-1-desktop{margin-left:8.33333%}.column.is-2-desktop{flex:none;width:16.66667%}.column.is-offset-2-desktop{margin-left:16.66667%}.column.is-3-desktop{flex:none;width:25%}.column.is-offset-3-desktop{margin-left:25%}.column.is-4-desktop{flex:none;width:33.33333%}.column.is-offset-4-desktop{margin-left:33.33333%}.column.is-5-desktop{flex:none;width:41.66667%}.column.is-offset-5-desktop{margin-left:41.66667%}.column.is-6-desktop{flex:none;width:50%}.column.is-offset-6-desktop{margin-left:50%}.column.is-7-desktop{flex:none;width:58.33333%}.column.is-offset-7-desktop{margin-left:58.33333%}.column.is-8-desktop{flex:none;width:66.66667%}.column.is-offset-8-desktop{margin-left:66.66667%}.column.is-9-desktop{flex:none;width:75%}.column.is-offset-9-desktop{margin-left:75%}.column.is-10-desktop{flex:none;width:83.33333%}.column.is-offset-10-desktop{margin-left:83.33333%}.column.is-11-desktop{flex:none;width:91.66667%}.column.is-offset-11-desktop{margin-left:91.66667%}.column.is-12-desktop{flex:none;width:100%}.column.is-offset-12-desktop{margin-left:100%}}@media screen and (min-width:1280px){.column.is-narrow-widescreen{flex:none}.column.is-full-widescreen{flex:none;width:100%}.column.is-three-quarters-widescreen{flex:none;width:75%}.column.is-two-thirds-widescreen{flex:none;width:66.6666%}.column.is-half-widescreen{flex:none;width:50%}.column.is-one-third-widescreen{flex:none;width:33.3333%}.column.is-one-quarter-widescreen{flex:none;width:25%}.column.is-one-fifth-widescreen{flex:none;width:20%}.column.is-two-fifths-widescreen{flex:none;width:40%}.column.is-three-fifths-widescreen{flex:none;width:60%}.column.is-four-fifths-widescreen{flex:none;width:80%}.column.is-offset-three-quarters-widescreen{margin-left:75%}.column.is-offset-two-thirds-widescreen{margin-left:66.6666%}.column.is-offset-half-widescreen{margin-left:50%}.column.is-offset-one-third-widescreen{margin-left:33.3333%}.column.is-offset-one-quarter-widescreen{margin-left:25%}.column.is-offset-one-fifth-widescreen{margin-left:20%}.column.is-offset-two-fifths-widescreen{margin-left:40%}.column.is-offset-three-fifths-widescreen{margin-left:60%}.column.is-offset-four-fifths-widescreen{margin-left:80%}.column.is-1-widescreen{flex:none;width:8.33333%}.column.is-offset-1-widescreen{margin-left:8.33333%}.column.is-2-widescreen{flex:none;width:16.66667%}.column.is-offset-2-widescreen{margin-left:16.66667%}.column.is-3-widescreen{flex:none;width:25%}.column.is-offset-3-widescreen{margin-left:25%}.column.is-4-widescreen{flex:none;width:33.33333%}.column.is-offset-4-widescreen{margin-left:33.33333%}.column.is-5-widescreen{flex:none;width:41.66667%}.column.is-offset-5-widescreen{margin-left:41.66667%}.column.is-6-widescreen{flex:none;width:50%}.column.is-offset-6-widescreen{margin-left:50%}.column.is-7-widescreen{flex:none;width:58.33333%}.column.is-offset-7-widescreen{margin-left:58.33333%}.column.is-8-widescreen{flex:none;width:66.66667%}.column.is-offset-8-widescreen{margin-left:66.66667%}.column.is-9-widescreen{flex:none;width:75%}.column.is-offset-9-widescreen{margin-left:75%}.column.is-10-widescreen{flex:none;width:83.33333%}.column.is-offset-10-widescreen{margin-left:83.33333%}.column.is-11-widescreen{flex:none;width:91.66667%}.column.is-offset-11-widescreen{margin-left:91.66667%}.column.is-12-widescreen{flex:none;width:100%}.column.is-offset-12-widescreen{margin-left:100%}}@media screen and (min-width:1472px){.column.is-narrow-fullhd{flex:none}.column.is-full-fullhd{flex:none;width:100%}.column.is-three-quarters-fullhd{flex:none;width:75%}.column.is-two-thirds-fullhd{flex:none;width:66.6666%}.column.is-half-fullhd{flex:none;width:50%}.column.is-one-third-fullhd{flex:none;width:33.3333%}.column.is-one-quarter-fullhd{flex:none;width:25%}.column.is-one-fifth-fullhd{flex:none;width:20%}.column.is-two-fifths-fullhd{flex:none;width:40%}.column.is-three-fifths-fullhd{flex:none;width:60%}.column.is-four-fifths-fullhd{flex:none;width:80%}.column.is-offset-three-quarters-fullhd{margin-left:75%}.column.is-offset-two-thirds-fullhd{margin-left:66.6666%}.column.is-offset-half-fullhd{margin-left:50%}.column.is-offset-one-third-fullhd{margin-left:33.3333%}.column.is-offset-one-quarter-fullhd{margin-left:25%}.column.is-offset-one-fifth-fullhd{margin-left:20%}.column.is-offset-two-fifths-fullhd{margin-left:40%}.column.is-offset-three-fifths-fullhd{margin-left:60%}.column.is-offset-four-fifths-fullhd{margin-left:80%}.column.is-1-fullhd{flex:none;width:8.33333%}.column.is-offset-1-fullhd{margin-left:8.33333%}.column.is-2-fullhd{flex:none;width:16.66667%}.column.is-offset-2-fullhd{margin-left:16.66667%}.column.is-3-fullhd{flex:none;width:25%}.column.is-offset-3-fullhd{margin-left:25%}.column.is-4-fullhd{flex:none;width:33.33333%}.column.is-offset-4-fullhd{margin-left:33.33333%}.column.is-5-fullhd{flex:none;width:41.66667%}.column.is-offset-5-fullhd{margin-left:41.66667%}.column.is-6-fullhd{flex:none;width:50%}.column.is-offset-6-fullhd{margin-left:50%}.column.is-7-fullhd{flex:none;width:58.33333%}.column.is-offset-7-fullhd{margin-left:58.33333%}.column.is-8-fullhd{flex:none;width:66.66667%}.column.is-offset-8-fullhd{margin-left:66.66667%}.column.is-9-fullhd{flex:none;width:75%}.column.is-offset-9-fullhd{margin-left:75%}.column.is-10-fullhd{flex:none;width:83.33333%}.column.is-offset-10-fullhd{margin-left:83.33333%}.column.is-11-fullhd{flex:none;width:91.66667%}.column.is-offset-11-fullhd{margin-left:91.66667%}.column.is-12-fullhd{flex:none;width:100%}.column.is-offset-12-fullhd{margin-left:100%}}.columns{margin-left:-.75rem;margin-right:-.75rem;margin-top:-.75rem}.columns:last-child{margin-bottom:-.75rem}.columns:not(:last-child){margin-bottom:calc(1.5rem - .75rem)}.columns.is-centered{justify-content:center}.columns.is-gapless{margin-left:0;margin-right:0;margin-top:0}.columns.is-gapless>.column{margin:0;padding:0!important}.columns.is-gapless:not(:last-child){margin-bottom:1.5rem}.columns.is-gapless:last-child{margin-bottom:0}.columns.is-mobile{display:flex}.columns.is-multiline{flex-wrap:wrap}.columns.is-vcentered{align-items:center}@media screen and (min-width:769px),print{.columns:not(.is-desktop){display:flex}}@media screen and (min-width:1088px){.columns.is-desktop{display:flex}}.columns.is-variable{--columnGap:0.75rem;margin-left:calc(-1 * var(--columnGap));margin-right:calc(-1 * var(--columnGap))}.columns.is-variable .column{padding-left:var(--columnGap);padding-right:var(--columnGap)}.columns.is-variable.is-0{--columnGap:0rem}.columns.is-variable.is-1{--columnGap:0.25rem}.columns.is-variable.is-2{--columnGap:0.5rem}.columns.is-variable.is-3{--columnGap:0.75rem}.columns.is-variable.is-4{--columnGap:1rem}.columns.is-variable.is-5{--columnGap:1.25rem}.columns.is-variable.is-6{--columnGap:1.5rem}.columns.is-variable.is-7{--columnGap:1.75rem}.columns.is-variable.is-8{--columnGap:2rem}.tile{align-items:stretch;display:block;flex-basis:0;flex-grow:1;flex-shrink:1;min-height:-webkit-min-content;min-height:-moz-min-content;min-height:min-content}.tile.is-ancestor{margin-left:-.75rem;margin-right:-.75rem;margin-top:-.75rem}.tile.is-ancestor:last-child{margin-bottom:-.75rem}.tile.is-ancestor:not(:last-child){margin-bottom:.75rem}.tile.is-child{margin:0!important}.tile.is-parent{padding:.75rem}.tile.is-vertical{flex-direction:column}.tile.is-vertical>.tile.is-child:not(:last-child){margin-bottom:1.5rem!important}@media screen and (min-width:769px),print{.tile:not(.is-child){display:flex}.tile.is-1{flex:none;width:8.33333%}.tile.is-2{flex:none;width:16.66667%}.tile.is-3{flex:none;width:25%}.tile.is-4{flex:none;width:33.33333%}.tile.is-5{flex:none;width:41.66667%}.tile.is-6{flex:none;width:50%}.tile.is-7{flex:none;width:58.33333%}.tile.is-8{flex:none;width:66.66667%}.tile.is-9{flex:none;width:75%}.tile.is-10{flex:none;width:83.33333%}.tile.is-11{flex:none;width:91.66667%}.tile.is-12{flex:none;width:100%}}.hero{align-items:stretch;display:flex;flex-direction:column;justify-content:space-between}.hero .navbar{background:0 0}.hero .tabs ul{border-bottom:none}.hero.is-white{background-color:#fff;color:#0a0a0a}.hero.is-white a:not(.button):not(.dropdown-item):not(.tag),.hero.is-white strong{color:inherit}.hero.is-white .title{color:#0a0a0a}.hero.is-white .subtitle{color:rgba(10,10,10,.9)}.hero.is-white .subtitle a:not(.button),.hero.is-white .subtitle strong{color:#0a0a0a}@media screen and (max-width:1087px){.hero.is-white .navbar-menu{background-color:#fff}}.hero.is-white .navbar-item,.hero.is-white .navbar-link{color:rgba(10,10,10,.7)}.hero.is-white .navbar-link.is-active,.hero.is-white .navbar-link:hover,.hero.is-white a.navbar-item.is-active,.hero.is-white a.navbar-item:hover{background-color:#f2f2f2;color:#0a0a0a}.hero.is-white .tabs a{color:#0a0a0a;opacity:.9}.hero.is-white .tabs a:hover{opacity:1}.hero.is-white .tabs li.is-active a{opacity:1}.hero.is-white .tabs.is-boxed a,.hero.is-white .tabs.is-toggle a{color:#0a0a0a}.hero.is-white .tabs.is-boxed a:hover,.hero.is-white .tabs.is-toggle a:hover{background-color:rgba(10,10,10,.1)}.hero.is-white .tabs.is-boxed li.is-active a,.hero.is-white .tabs.is-boxed li.is-active a:hover,.hero.is-white .tabs.is-toggle li.is-active a,.hero.is-white .tabs.is-toggle li.is-active a:hover{background-color:#0a0a0a;border-color:#0a0a0a;color:#fff}.hero.is-white.is-bold{background-image:linear-gradient(141deg,#e6e6e6 0,#fff 71%,#fff 100%)}@media screen and (max-width:768px){.hero.is-white.is-bold .navbar-menu{background-image:linear-gradient(141deg,#e6e6e6 0,#fff 71%,#fff 100%)}}.hero.is-black{background-color:#0a0a0a;color:#fff}.hero.is-black a:not(.button):not(.dropdown-item):not(.tag),.hero.is-black strong{color:inherit}.hero.is-black .title{color:#fff}.hero.is-black .subtitle{color:rgba(255,255,255,.9)}.hero.is-black .subtitle a:not(.button),.hero.is-black .subtitle strong{color:#fff}@media screen and (max-width:1087px){.hero.is-black .navbar-menu{background-color:#0a0a0a}}.hero.is-black .navbar-item,.hero.is-black .navbar-link{color:rgba(255,255,255,.7)}.hero.is-black .navbar-link.is-active,.hero.is-black .navbar-link:hover,.hero.is-black a.navbar-item.is-active,.hero.is-black a.navbar-item:hover{background-color:#000;color:#fff}.hero.is-black .tabs a{color:#fff;opacity:.9}.hero.is-black .tabs a:hover{opacity:1}.hero.is-black .tabs li.is-active a{opacity:1}.hero.is-black .tabs.is-boxed a,.hero.is-black .tabs.is-toggle a{color:#fff}.hero.is-black .tabs.is-boxed a:hover,.hero.is-black .tabs.is-toggle a:hover{background-color:rgba(10,10,10,.1)}.hero.is-black .tabs.is-boxed li.is-active a,.hero.is-black .tabs.is-boxed li.is-active a:hover,.hero.is-black .tabs.is-toggle li.is-active a,.hero.is-black .tabs.is-toggle li.is-active a:hover{background-color:#fff;border-color:#fff;color:#0a0a0a}.hero.is-black.is-bold{background-image:linear-gradient(141deg,#000 0,#0a0a0a 71%,#181616 100%)}@media screen and (max-width:768px){.hero.is-black.is-bold .navbar-menu{background-image:linear-gradient(141deg,#000 0,#0a0a0a 71%,#181616 100%)}}.hero.is-light{background-color:#f5f5f5;color:#363636}.hero.is-light a:not(.button):not(.dropdown-item):not(.tag),.hero.is-light strong{color:inherit}.hero.is-light .title{color:#363636}.hero.is-light .subtitle{color:rgba(54,54,54,.9)}.hero.is-light .subtitle a:not(.button),.hero.is-light .subtitle strong{color:#363636}@media screen and (max-width:1087px){.hero.is-light .navbar-menu{background-color:#f5f5f5}}.hero.is-light .navbar-item,.hero.is-light .navbar-link{color:rgba(54,54,54,.7)}.hero.is-light .navbar-link.is-active,.hero.is-light .navbar-link:hover,.hero.is-light a.navbar-item.is-active,.hero.is-light a.navbar-item:hover{background-color:#e8e8e8;color:#363636}.hero.is-light .tabs a{color:#363636;opacity:.9}.hero.is-light .tabs a:hover{opacity:1}.hero.is-light .tabs li.is-active a{opacity:1}.hero.is-light .tabs.is-boxed a,.hero.is-light .tabs.is-toggle a{color:#363636}.hero.is-light .tabs.is-boxed a:hover,.hero.is-light .tabs.is-toggle a:hover{background-color:rgba(10,10,10,.1)}.hero.is-light .tabs.is-boxed li.is-active a,.hero.is-light .tabs.is-boxed li.is-active a:hover,.hero.is-light .tabs.is-toggle li.is-active a,.hero.is-light .tabs.is-toggle li.is-active a:hover{background-color:#363636;border-color:#363636;color:#f5f5f5}.hero.is-light.is-bold{background-image:linear-gradient(141deg,#dfd8d9 0,#f5f5f5 71%,#fff 100%)}@media screen and (max-width:768px){.hero.is-light.is-bold .navbar-menu{background-image:linear-gradient(141deg,#dfd8d9 0,#f5f5f5 71%,#fff 100%)}}.hero.is-dark{background-color:#363636;color:#f5f5f5}.hero.is-dark a:not(.button):not(.dropdown-item):not(.tag),.hero.is-dark strong{color:inherit}.hero.is-dark .title{color:#f5f5f5}.hero.is-dark .subtitle{color:rgba(245,245,245,.9)}.hero.is-dark .subtitle a:not(.button),.hero.is-dark .subtitle strong{color:#f5f5f5}@media screen and (max-width:1087px){.hero.is-dark .navbar-menu{background-color:#363636}}.hero.is-dark .navbar-item,.hero.is-dark .navbar-link{color:rgba(245,245,245,.7)}.hero.is-dark .navbar-link.is-active,.hero.is-dark .navbar-link:hover,.hero.is-dark a.navbar-item.is-active,.hero.is-dark a.navbar-item:hover{background-color:#292929;color:#f5f5f5}.hero.is-dark .tabs a{color:#f5f5f5;opacity:.9}.hero.is-dark .tabs a:hover{opacity:1}.hero.is-dark .tabs li.is-active a{opacity:1}.hero.is-dark .tabs.is-boxed a,.hero.is-dark .tabs.is-toggle a{color:#f5f5f5}.hero.is-dark .tabs.is-boxed a:hover,.hero.is-dark .tabs.is-toggle a:hover{background-color:rgba(10,10,10,.1)}.hero.is-dark .tabs.is-boxed li.is-active a,.hero.is-dark .tabs.is-boxed li.is-active a:hover,.hero.is-dark .tabs.is-toggle li.is-active a,.hero.is-dark .tabs.is-toggle li.is-active a:hover{background-color:#f5f5f5;border-color:#f5f5f5;color:#363636}.hero.is-dark.is-bold{background-image:linear-gradient(141deg,#1f191a 0,#363636 71%,#46403f 100%)}@media screen and (max-width:768px){.hero.is-dark.is-bold .navbar-menu{background-image:linear-gradient(141deg,#1f191a 0,#363636 71%,#46403f 100%)}}.hero.is-primary{background-color:#00d1b2;color:#fff}.hero.is-primary a:not(.button):not(.dropdown-item):not(.tag),.hero.is-primary strong{color:inherit}.hero.is-primary .title{color:#fff}.hero.is-primary .subtitle{color:rgba(255,255,255,.9)}.hero.is-primary .subtitle a:not(.button),.hero.is-primary .subtitle strong{color:#fff}@media screen and (max-width:1087px){.hero.is-primary .navbar-menu{background-color:#00d1b2}}.hero.is-primary .navbar-item,.hero.is-primary .navbar-link{color:rgba(255,255,255,.7)}.hero.is-primary .navbar-link.is-active,.hero.is-primary .navbar-link:hover,.hero.is-primary a.navbar-item.is-active,.hero.is-primary a.navbar-item:hover{background-color:#00b89c;color:#fff}.hero.is-primary .tabs a{color:#fff;opacity:.9}.hero.is-primary .tabs a:hover{opacity:1}.hero.is-primary .tabs li.is-active a{opacity:1}.hero.is-primary .tabs.is-boxed a,.hero.is-primary .tabs.is-toggle a{color:#fff}.hero.is-primary .tabs.is-boxed a:hover,.hero.is-primary .tabs.is-toggle a:hover{background-color:rgba(10,10,10,.1)}.hero.is-primary .tabs.is-boxed li.is-active a,.hero.is-primary .tabs.is-boxed li.is-active a:hover,.hero.is-primary .tabs.is-toggle li.is-active a,.hero.is-primary .tabs.is-toggle li.is-active a:hover{background-color:#fff;border-color:#fff;color:#00d1b2}.hero.is-primary.is-bold{background-image:linear-gradient(141deg,#009e6c 0,#00d1b2 71%,#00e7eb 100%)}@media screen and (max-width:768px){.hero.is-primary.is-bold .navbar-menu{background-image:linear-gradient(141deg,#009e6c 0,#00d1b2 71%,#00e7eb 100%)}}.hero.is-link{background-color:#3273dc;color:#fff}.hero.is-link a:not(.button):not(.dropdown-item):not(.tag),.hero.is-link strong{color:inherit}.hero.is-link .title{color:#fff}.hero.is-link .subtitle{color:rgba(255,255,255,.9)}.hero.is-link .subtitle a:not(.button),.hero.is-link .subtitle strong{color:#fff}@media screen and (max-width:1087px){.hero.is-link .navbar-menu{background-color:#3273dc}}.hero.is-link .navbar-item,.hero.is-link .navbar-link{color:rgba(255,255,255,.7)}.hero.is-link .navbar-link.is-active,.hero.is-link .navbar-link:hover,.hero.is-link a.navbar-item.is-active,.hero.is-link a.navbar-item:hover{background-color:#2366d1;color:#fff}.hero.is-link .tabs a{color:#fff;opacity:.9}.hero.is-link .tabs a:hover{opacity:1}.hero.is-link .tabs li.is-active a{opacity:1}.hero.is-link .tabs.is-boxed a,.hero.is-link .tabs.is-toggle a{color:#fff}.hero.is-link .tabs.is-boxed a:hover,.hero.is-link .tabs.is-toggle a:hover{background-color:rgba(10,10,10,.1)}.hero.is-link .tabs.is-boxed li.is-active a,.hero.is-link .tabs.is-boxed li.is-active a:hover,.hero.is-link .tabs.is-toggle li.is-active a,.hero.is-link .tabs.is-toggle li.is-active a:hover{background-color:#fff;border-color:#fff;color:#3273dc}.hero.is-link.is-bold{background-image:linear-gradient(141deg,#1577c6 0,#3273dc 71%,#4366e5 100%)}@media screen and (max-width:768px){.hero.is-link.is-bold .navbar-menu{background-image:linear-gradient(141deg,#1577c6 0,#3273dc 71%,#4366e5 100%)}}.hero.is-info{background-color:#209cee;color:#fff}.hero.is-info a:not(.button):not(.dropdown-item):not(.tag),.hero.is-info strong{color:inherit}.hero.is-info .title{color:#fff}.hero.is-info .subtitle{color:rgba(255,255,255,.9)}.hero.is-info .subtitle a:not(.button),.hero.is-info .subtitle strong{color:#fff}@media screen and (max-width:1087px){.hero.is-info .navbar-menu{background-color:#209cee}}.hero.is-info .navbar-item,.hero.is-info .navbar-link{color:rgba(255,255,255,.7)}.hero.is-info .navbar-link.is-active,.hero.is-info .navbar-link:hover,.hero.is-info a.navbar-item.is-active,.hero.is-info a.navbar-item:hover{background-color:#118fe4;color:#fff}.hero.is-info .tabs a{color:#fff;opacity:.9}.hero.is-info .tabs a:hover{opacity:1}.hero.is-info .tabs li.is-active a{opacity:1}.hero.is-info .tabs.is-boxed a,.hero.is-info .tabs.is-toggle a{color:#fff}.hero.is-info .tabs.is-boxed a:hover,.hero.is-info .tabs.is-toggle a:hover{background-color:rgba(10,10,10,.1)}.hero.is-info .tabs.is-boxed li.is-active a,.hero.is-info .tabs.is-boxed li.is-active a:hover,.hero.is-info .tabs.is-toggle li.is-active a,.hero.is-info .tabs.is-toggle li.is-active a:hover{background-color:#fff;border-color:#fff;color:#209cee}.hero.is-info.is-bold{background-image:linear-gradient(141deg,#04a6d7 0,#209cee 71%,#3287f5 100%)}@media screen and (max-width:768px){.hero.is-info.is-bold .navbar-menu{background-image:linear-gradient(141deg,#04a6d7 0,#209cee 71%,#3287f5 100%)}}.hero.is-success{background-color:#23d160;color:#fff}.hero.is-success a:not(.button):not(.dropdown-item):not(.tag),.hero.is-success strong{color:inherit}.hero.is-success .title{color:#fff}.hero.is-success .subtitle{color:rgba(255,255,255,.9)}.hero.is-success .subtitle a:not(.button),.hero.is-success .subtitle strong{color:#fff}@media screen and (max-width:1087px){.hero.is-success .navbar-menu{background-color:#23d160}}.hero.is-success .navbar-item,.hero.is-success .navbar-link{color:rgba(255,255,255,.7)}.hero.is-success .navbar-link.is-active,.hero.is-success .navbar-link:hover,.hero.is-success a.navbar-item.is-active,.hero.is-success a.navbar-item:hover{background-color:#20bc56;color:#fff}.hero.is-success .tabs a{color:#fff;opacity:.9}.hero.is-success .tabs a:hover{opacity:1}.hero.is-success .tabs li.is-active a{opacity:1}.hero.is-success .tabs.is-boxed a,.hero.is-success .tabs.is-toggle a{color:#fff}.hero.is-success .tabs.is-boxed a:hover,.hero.is-success .tabs.is-toggle a:hover{background-color:rgba(10,10,10,.1)}.hero.is-success .tabs.is-boxed li.is-active a,.hero.is-success .tabs.is-boxed li.is-active a:hover,.hero.is-success .tabs.is-toggle li.is-active a,.hero.is-success .tabs.is-toggle li.is-active a:hover{background-color:#fff;border-color:#fff;color:#23d160}.hero.is-success.is-bold{background-image:linear-gradient(141deg,#12af2f 0,#23d160 71%,#2ce28a 100%)}@media screen and (max-width:768px){.hero.is-success.is-bold .navbar-menu{background-image:linear-gradient(141deg,#12af2f 0,#23d160 71%,#2ce28a 100%)}}.hero.is-warning{background-color:#ffdd57;color:rgba(0,0,0,.7)}.hero.is-warning a:not(.button):not(.dropdown-item):not(.tag),.hero.is-warning strong{color:inherit}.hero.is-warning .title{color:rgba(0,0,0,.7)}.hero.is-warning .subtitle{color:rgba(0,0,0,.9)}.hero.is-warning .subtitle a:not(.button),.hero.is-warning .subtitle strong{color:rgba(0,0,0,.7)}@media screen and (max-width:1087px){.hero.is-warning .navbar-menu{background-color:#ffdd57}}.hero.is-warning .navbar-item,.hero.is-warning .navbar-link{color:rgba(0,0,0,.7)}.hero.is-warning .navbar-link.is-active,.hero.is-warning .navbar-link:hover,.hero.is-warning a.navbar-item.is-active,.hero.is-warning a.navbar-item:hover{background-color:#ffd83d;color:rgba(0,0,0,.7)}.hero.is-warning .tabs a{color:rgba(0,0,0,.7);opacity:.9}.hero.is-warning .tabs a:hover{opacity:1}.hero.is-warning .tabs li.is-active a{opacity:1}.hero.is-warning .tabs.is-boxed a,.hero.is-warning .tabs.is-toggle a{color:rgba(0,0,0,.7)}.hero.is-warning .tabs.is-boxed a:hover,.hero.is-warning .tabs.is-toggle a:hover{background-color:rgba(10,10,10,.1)}.hero.is-warning .tabs.is-boxed li.is-active a,.hero.is-warning .tabs.is-boxed li.is-active a:hover,.hero.is-warning .tabs.is-toggle li.is-active a,.hero.is-warning .tabs.is-toggle li.is-active a:hover{background-color:rgba(0,0,0,.7);border-color:rgba(0,0,0,.7);color:#ffdd57}.hero.is-warning.is-bold{background-image:linear-gradient(141deg,#ffaf24 0,#ffdd57 71%,#fffa70 100%)}@media screen and (max-width:768px){.hero.is-warning.is-bold .navbar-menu{background-image:linear-gradient(141deg,#ffaf24 0,#ffdd57 71%,#fffa70 100%)}}.hero.is-danger{background-color:#ff3860;color:#fff}.hero.is-danger a:not(.button):not(.dropdown-item):not(.tag),.hero.is-danger strong{color:inherit}.hero.is-danger .title{color:#fff}.hero.is-danger .subtitle{color:rgba(255,255,255,.9)}.hero.is-danger .subtitle a:not(.button),.hero.is-danger .subtitle strong{color:#fff}@media screen and (max-width:1087px){.hero.is-danger .navbar-menu{background-color:#ff3860}}.hero.is-danger .navbar-item,.hero.is-danger .navbar-link{color:rgba(255,255,255,.7)}.hero.is-danger .navbar-link.is-active,.hero.is-danger .navbar-link:hover,.hero.is-danger a.navbar-item.is-active,.hero.is-danger a.navbar-item:hover{background-color:#ff1f4b;color:#fff}.hero.is-danger .tabs a{color:#fff;opacity:.9}.hero.is-danger .tabs a:hover{opacity:1}.hero.is-danger .tabs li.is-active a{opacity:1}.hero.is-danger .tabs.is-boxed a,.hero.is-danger .tabs.is-toggle a{color:#fff}.hero.is-danger .tabs.is-boxed a:hover,.hero.is-danger .tabs.is-toggle a:hover{background-color:rgba(10,10,10,.1)}.hero.is-danger .tabs.is-boxed li.is-active a,.hero.is-danger .tabs.is-boxed li.is-active a:hover,.hero.is-danger .tabs.is-toggle li.is-active a,.hero.is-danger .tabs.is-toggle li.is-active a:hover{background-color:#fff;border-color:#fff;color:#ff3860}.hero.is-danger.is-bold{background-image:linear-gradient(141deg,#ff0561 0,#ff3860 71%,#ff5257 100%)}@media screen and (max-width:768px){.hero.is-danger.is-bold .navbar-menu{background-image:linear-gradient(141deg,#ff0561 0,#ff3860 71%,#ff5257 100%)}}.hero.is-small .hero-body{padding-bottom:1.5rem;padding-top:1.5rem}@media screen and (min-width:769px),print{.hero.is-medium .hero-body{padding-bottom:9rem;padding-top:9rem}}@media screen and (min-width:769px),print{.hero.is-large .hero-body{padding-bottom:18rem;padding-top:18rem}}.hero.is-fullheight .hero-body,.hero.is-halfheight .hero-body{align-items:center;display:flex}.hero.is-fullheight .hero-body>.container,.hero.is-halfheight .hero-body>.container{flex-grow:1;flex-shrink:1}.hero.is-halfheight{min-height:50vh}.hero.is-fullheight{min-height:100vh}.hero-video{overflow:hidden}.hero-video video{left:50%;min-height:100%;min-width:100%;position:absolute;top:50%;-webkit-transform:translate3d(-50%,-50%,0);transform:translate3d(-50%,-50%,0)}.hero-video.is-transparent{opacity:.3}@media screen and (max-width:768px){.hero-video{display:none}}.hero-buttons{margin-top:1.5rem}@media screen and (max-width:768px){.hero-buttons .button{display:flex}.hero-buttons .button:not(:last-child){margin-bottom:.75rem}}@media screen and (min-width:769px),print{.hero-buttons{display:flex;justify-content:center}.hero-buttons .button:not(:last-child){margin-right:1.5rem}}.hero-foot,.hero-head{flex-grow:0;flex-shrink:0}.hero-body{flex-grow:1;flex-shrink:0;padding:3rem 1.5rem}.section{padding:3rem 1.5rem}@media screen and (min-width:1088px){.section.is-medium{padding:9rem 1.5rem}.section.is-large{padding:18rem 1.5rem}}.footer{background-color:#fafafa;padding:3rem 1.5rem 6rem} \ No newline at end of file diff --git a/SpotifyAPI.Web.Auth/Resources/AuthorizationCodeAuth/images/1.png b/SpotifyAPI.Web.Auth/Resources/AuthorizationCodeAuth/images/1.png new file mode 100644 index 00000000..4bb428a7 Binary files /dev/null and b/SpotifyAPI.Web.Auth/Resources/AuthorizationCodeAuth/images/1.png differ diff --git a/SpotifyAPI.Web.Auth/Resources/AuthorizationCodeAuth/images/2.png b/SpotifyAPI.Web.Auth/Resources/AuthorizationCodeAuth/images/2.png new file mode 100644 index 00000000..a01a53ad Binary files /dev/null and b/SpotifyAPI.Web.Auth/Resources/AuthorizationCodeAuth/images/2.png differ diff --git a/SpotifyAPI.Web.Auth/Resources/AuthorizationCodeAuth/start.html b/SpotifyAPI.Web.Auth/Resources/AuthorizationCodeAuth/start.html new file mode 100644 index 00000000..9b883d4c --- /dev/null +++ b/SpotifyAPI.Web.Auth/Resources/AuthorizationCodeAuth/start.html @@ -0,0 +1,77 @@ + + + + + + + + + + + +
+
+

Spotify Authentication

+
+
+

Introduction

+ +
+
+ +
+ +
+
+
+ +
+ +
+
+ +
+
+ +
+
+
+
+
+ + + + + \ No newline at end of file diff --git a/SpotifyAPI.Web.Auth/Resources/ImplicitGrantAuth/index.html b/SpotifyAPI.Web.Auth/Resources/ImplicitGrantAuth/index.html new file mode 100644 index 00000000..6197f88c --- /dev/null +++ b/SpotifyAPI.Web.Auth/Resources/ImplicitGrantAuth/index.html @@ -0,0 +1,45 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/SpotifyAPI.Web.Auth/SpotifyAPI.Web.Auth.csproj b/SpotifyAPI.Web.Auth/SpotifyAPI.Web.Auth.csproj new file mode 100644 index 00000000..74dea624 --- /dev/null +++ b/SpotifyAPI.Web.Auth/SpotifyAPI.Web.Auth.csproj @@ -0,0 +1,23 @@ + + + + netstandard2.0 + + + + + + + + + + + + + + + + + + + diff --git a/SpotifyAPI.Web.Auth/SpotifyAuthServer.cs b/SpotifyAPI.Web.Auth/SpotifyAuthServer.cs new file mode 100644 index 00000000..9b7d6199 --- /dev/null +++ b/SpotifyAPI.Web.Auth/SpotifyAuthServer.cs @@ -0,0 +1,88 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading; +using SpotifyAPI.Web.Enums; +using Unosquare.Labs.EmbedIO; +using Unosquare.Labs.EmbedIO.Modules; + +namespace SpotifyAPI.Web.Auth +{ + public abstract class SpotifyAuthServer + { + public string ClientId { get; set; } + public string ServerUri { get; set; } + public string RedirectUri { get; set; } + public string State { get; set; } + public Scope Scope { get; set; } + public bool ShowDialog { get; set; } + + private readonly string _folder; + private readonly string _type; + protected CancellationTokenSource _serverSource; + + public delegate void OnAuthReceived(object sender, T payload); + public event OnAuthReceived AuthReceived; + + internal static readonly Dictionary> Instances = new Dictionary>(); + + internal SpotifyAuthServer(string type, string folder, string redirectUri, string serverUri, Scope scope = Scope.None, string state = "") + { + _type = type; + _folder = folder; + ServerUri = serverUri; + RedirectUri = redirectUri; + Scope = scope; + State = string.IsNullOrEmpty(state) ? string.Join("", Guid.NewGuid().ToString("n").Take(8)) : state; + } + + public void Start() + { + Instances.Add(State, this); + _serverSource = new CancellationTokenSource(); + WebServer server = AdaptWebServer(WebServer.Create(ServerUri)); + server.RegisterModule(new ResourceFilesModule(typeof(ImplictGrantAuth).Assembly, $"SpotifyAPI.Web.Auth.Resources.{_folder}")); +#pragma warning disable 4014 + server.RunAsync(_serverSource.Token); +#pragma warning restore 4014 + } + + public virtual string GetUri() + { + StringBuilder builder = new StringBuilder("https://accounts.spotify.com/authorize/?"); + builder.Append("client_id=" + ClientId); + builder.Append($"&response_type={_type}"); + builder.Append("&redirect_uri=" + RedirectUri); + builder.Append("&state=" + State); + builder.Append("&scope=" + Scope.GetStringAttribute(" ")); + builder.Append("&show_dialog=" + ShowDialog); + return Uri.EscapeUriString(builder.ToString()); + } + + public void Stop(int delay = 2000) + { + if (_serverSource == null) return; + _serverSource.CancelAfter(delay); + Instances.Remove(State); + } + + public void OpenBrowser() + { + string uri = GetUri(); + AuthUtil.OpenBrowser(uri); + } + + internal void TriggerAuth(T payload) + { + AuthReceived?.Invoke(this, payload); + } + + internal static SpotifyAuthServer GetByState(string state) + { + return Instances.TryGetValue(state, out SpotifyAuthServer auth) ? auth : null; + } + + protected abstract WebServer AdaptWebServer(WebServer webServer); + } +} \ No newline at end of file diff --git a/SpotifyAPI.Web.Example/Program.cs b/SpotifyAPI.Web.Example/Program.cs new file mode 100644 index 00000000..ece744d9 --- /dev/null +++ b/SpotifyAPI.Web.Example/Program.cs @@ -0,0 +1,74 @@ +using System; +using System.Threading.Tasks; +using SpotifyAPI.Web.Auth; +using SpotifyAPI.Web.Enums; +using SpotifyAPI.Web.Models; + +namespace SpotifyAPI.Web.Example +{ + static class Program + { + private static string _clientId = ""; //""; + private static string _secretId = ""; //""; + + static void Main(string[] args) + { + _clientId = string.IsNullOrEmpty(_clientId) + ? System.Environment.GetEnvironmentVariable("SPOTIFY_CLIENT_ID") + : _clientId; + + _secretId = string.IsNullOrEmpty(_secretId) + ? System.Environment.GetEnvironmentVariable("SPOTIFY_SECRET_ID") + : _secretId; + + Console.WriteLine("####### Spotify API Example #######"); + Console.WriteLine("This example uses AuthorizationCodeAuth."); + Console.WriteLine( + "Tip: If you want to supply your ClientID and SecretId beforehand, use env variables (SPOTIFY_CLIENT_ID and SPOTIFY_SECRET_ID)"); + + + AuthorizationCodeAuth auth = + new AuthorizationCodeAuth(_clientId, _secretId, "http://localhost:4002", "http://localhost:4002", + Scope.PlaylistReadPrivate | Scope.PlaylistReadCollaborative); + auth.AuthReceived += AuthOnAuthReceived; + auth.Start(); + auth.OpenBrowser(); + + Console.ReadLine(); + } + + private static async void AuthOnAuthReceived(object sender, AuthorizationCode payload) + { + AuthorizationCodeAuth auth = (AuthorizationCodeAuth) sender; + auth.Stop(); + + Token token = await auth.ExchangeCode(payload.Code); + SpotifyWebAPI api = new SpotifyWebAPI() + { + AccessToken = token.AccessToken, + TokenType = token.TokenType + }; + PrintUsefulDate(api); + } + + private static async void PrintUsefulDate(SpotifyWebAPI api) + { + PrivateProfile profile = await api.GetPrivateProfileAsync(); + string name = string.IsNullOrEmpty(profile.DisplayName) ? profile.Id : profile.DisplayName; + Console.WriteLine($"Hello there, {name}!"); + + Console.WriteLine("Your playlists:"); + Paging playlists = await api.GetUserPlaylistsAsync(profile.Id); + do + { + playlists.Items.ForEach(playlist => + { + Console.WriteLine($"- {playlist.Name}"); + }); + playlists = await api.GetNextPageAsync(playlists); + } while (playlists.HasNextPage()); + + + } + } +} \ No newline at end of file diff --git a/SpotifyAPI.Web.Example/SpotifyAPI.Web.Example.csproj b/SpotifyAPI.Web.Example/SpotifyAPI.Web.Example.csproj new file mode 100644 index 00000000..84fe660e --- /dev/null +++ b/SpotifyAPI.Web.Example/SpotifyAPI.Web.Example.csproj @@ -0,0 +1,14 @@ + + + + Exe + netcoreapp2.0 + latest + + + + + + + + diff --git a/SpotifyAPI.Web.Example/SpotifyAPI.Web.Example.csproj.DotSettings b/SpotifyAPI.Web.Example/SpotifyAPI.Web.Example.csproj.DotSettings new file mode 100644 index 00000000..58ad6c88 --- /dev/null +++ b/SpotifyAPI.Web.Example/SpotifyAPI.Web.Example.csproj.DotSettings @@ -0,0 +1,2 @@ + + CSharp71 \ No newline at end of file diff --git a/SpotifyAPI.Tests/ProxyConfigTest.cs b/SpotifyAPI.Web.Tests/ProxyConfigTest.cs similarity index 99% rename from SpotifyAPI.Tests/ProxyConfigTest.cs rename to SpotifyAPI.Web.Tests/ProxyConfigTest.cs index d4b20b01..bc32e046 100644 --- a/SpotifyAPI.Tests/ProxyConfigTest.cs +++ b/SpotifyAPI.Web.Tests/ProxyConfigTest.cs @@ -1,7 +1,7 @@ using System; using NUnit.Framework; -namespace SpotifyAPI.Tests +namespace SpotifyAPI.Web.Tests { [TestFixture] public class ProxyConfigTest diff --git a/SpotifyAPI.Web.Tests/SpotifyAPI.Web.Tests.csproj b/SpotifyAPI.Web.Tests/SpotifyAPI.Web.Tests.csproj new file mode 100644 index 00000000..c0827cd8 --- /dev/null +++ b/SpotifyAPI.Web.Tests/SpotifyAPI.Web.Tests.csproj @@ -0,0 +1,21 @@ + + + + netcoreapp2.0 + + false + + + + + + + + + + + + + + + diff --git a/SpotifyAPI.Tests/SpotifyWebAPITest.cs b/SpotifyAPI.Web.Tests/SpotifyWebAPITest.cs similarity index 95% rename from SpotifyAPI.Tests/SpotifyWebAPITest.cs rename to SpotifyAPI.Web.Tests/SpotifyWebAPITest.cs index a07c585b..8d4181af 100644 --- a/SpotifyAPI.Tests/SpotifyWebAPITest.cs +++ b/SpotifyAPI.Web.Tests/SpotifyWebAPITest.cs @@ -1,19 +1,18 @@ -using Moq; -using Newtonsoft.Json; -using NUnit.Framework; -using SpotifyAPI.Web; -using SpotifyAPI.Web.Models; -using System; +using System; using System.Collections.Generic; using System.IO; using System.Linq; +using Moq; +using Newtonsoft.Json; +using NUnit.Framework; +using SpotifyAPI.Web.Models; -namespace SpotifyAPI.Tests +namespace SpotifyAPI.Web.Tests { [TestFixture] public class SpotifyWebAPITest { - private static readonly string FixtureDir = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "../../fixtures/"); + private static readonly string FixtureDir = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "../../../fixtures/"); private Mock _mock; private SpotifyWebAPI _spotify; diff --git a/SpotifyAPI.Tests/UtilTest.cs b/SpotifyAPI.Web.Tests/UtilTest.cs similarity index 88% rename from SpotifyAPI.Tests/UtilTest.cs rename to SpotifyAPI.Web.Tests/UtilTest.cs index c78c14ae..67c243d1 100644 --- a/SpotifyAPI.Tests/UtilTest.cs +++ b/SpotifyAPI.Web.Tests/UtilTest.cs @@ -1,8 +1,7 @@ using System; using NUnit.Framework; -using SpotifyAPI.Web; -namespace SpotifyAPI.Tests +namespace SpotifyAPI.Web.Tests { [TestFixture] public class UtilTest diff --git a/SpotifyAPI.Tests/fixtures/private-user.json b/SpotifyAPI.Web.Tests/fixtures/private-user.json similarity index 100% rename from SpotifyAPI.Tests/fixtures/private-user.json rename to SpotifyAPI.Web.Tests/fixtures/private-user.json diff --git a/SpotifyAPI.Tests/fixtures/public-user.json b/SpotifyAPI.Web.Tests/fixtures/public-user.json similarity index 100% rename from SpotifyAPI.Tests/fixtures/public-user.json rename to SpotifyAPI.Web.Tests/fixtures/public-user.json diff --git a/SpotifyAPI/Web/Enums/AlbumType.cs b/SpotifyAPI.Web/Enums/AlbumType.cs similarity index 100% rename from SpotifyAPI/Web/Enums/AlbumType.cs rename to SpotifyAPI.Web/Enums/AlbumType.cs diff --git a/SpotifyAPI/Web/Enums/FollowType.cs b/SpotifyAPI.Web/Enums/FollowType.cs similarity index 100% rename from SpotifyAPI/Web/Enums/FollowType.cs rename to SpotifyAPI.Web/Enums/FollowType.cs diff --git a/SpotifyAPI/Web/Enums/RepeatState.cs b/SpotifyAPI.Web/Enums/RepeatState.cs similarity index 100% rename from SpotifyAPI/Web/Enums/RepeatState.cs rename to SpotifyAPI.Web/Enums/RepeatState.cs diff --git a/SpotifyAPI/Web/Enums/Scope.cs b/SpotifyAPI.Web/Enums/Scope.cs similarity index 100% rename from SpotifyAPI/Web/Enums/Scope.cs rename to SpotifyAPI.Web/Enums/Scope.cs diff --git a/SpotifyAPI/Web/Enums/SearchType.cs b/SpotifyAPI.Web/Enums/SearchType.cs similarity index 100% rename from SpotifyAPI/Web/Enums/SearchType.cs rename to SpotifyAPI.Web/Enums/SearchType.cs diff --git a/SpotifyAPI/Web/Enums/TimeRangeType.cs b/SpotifyAPI.Web/Enums/TimeRangeType.cs similarity index 100% rename from SpotifyAPI/Web/Enums/TimeRangeType.cs rename to SpotifyAPI.Web/Enums/TimeRangeType.cs diff --git a/SpotifyAPI/Web/IClient.cs b/SpotifyAPI.Web/IClient.cs similarity index 99% rename from SpotifyAPI/Web/IClient.cs rename to SpotifyAPI.Web/IClient.cs index 1fcef62e..c0960a63 100644 --- a/SpotifyAPI/Web/IClient.cs +++ b/SpotifyAPI.Web/IClient.cs @@ -1,7 +1,6 @@ using Newtonsoft.Json; using System; using System.Collections.Generic; -using System.Net.Http.Headers; using System.Threading.Tasks; using SpotifyAPI.Web.Models; diff --git a/SpotifyAPI/Web/Models/AnalysisMeta.cs b/SpotifyAPI.Web/Models/AnalysisMeta.cs similarity index 100% rename from SpotifyAPI/Web/Models/AnalysisMeta.cs rename to SpotifyAPI.Web/Models/AnalysisMeta.cs diff --git a/SpotifyAPI/Web/Models/AnalysisSection.cs b/SpotifyAPI.Web/Models/AnalysisSection.cs similarity index 100% rename from SpotifyAPI/Web/Models/AnalysisSection.cs rename to SpotifyAPI.Web/Models/AnalysisSection.cs diff --git a/SpotifyAPI/Web/Models/AnalysisSegment.cs b/SpotifyAPI.Web/Models/AnalysisSegment.cs similarity index 100% rename from SpotifyAPI/Web/Models/AnalysisSegment.cs rename to SpotifyAPI.Web/Models/AnalysisSegment.cs diff --git a/SpotifyAPI/Web/Models/AnalysisTimeSlice.cs b/SpotifyAPI.Web/Models/AnalysisTimeSlice.cs similarity index 100% rename from SpotifyAPI/Web/Models/AnalysisTimeSlice.cs rename to SpotifyAPI.Web/Models/AnalysisTimeSlice.cs diff --git a/SpotifyAPI/Web/Models/AnalysisTrack.cs b/SpotifyAPI.Web/Models/AnalysisTrack.cs similarity index 100% rename from SpotifyAPI/Web/Models/AnalysisTrack.cs rename to SpotifyAPI.Web/Models/AnalysisTrack.cs diff --git a/SpotifyAPI/Web/Models/ArrayResponse.cs b/SpotifyAPI.Web/Models/ArrayResponse.cs similarity index 100% rename from SpotifyAPI/Web/Models/ArrayResponse.cs rename to SpotifyAPI.Web/Models/ArrayResponse.cs diff --git a/SpotifyAPI/Web/Models/AudioAnalysis.cs b/SpotifyAPI.Web/Models/AudioAnalysis.cs similarity index 100% rename from SpotifyAPI/Web/Models/AudioAnalysis.cs rename to SpotifyAPI.Web/Models/AudioAnalysis.cs diff --git a/SpotifyAPI/Web/Models/AudioFeatures.cs b/SpotifyAPI.Web/Models/AudioFeatures.cs similarity index 100% rename from SpotifyAPI/Web/Models/AudioFeatures.cs rename to SpotifyAPI.Web/Models/AudioFeatures.cs diff --git a/SpotifyAPI/Web/Models/AvailabeDevices.cs b/SpotifyAPI.Web/Models/AvailabeDevices.cs similarity index 100% rename from SpotifyAPI/Web/Models/AvailabeDevices.cs rename to SpotifyAPI.Web/Models/AvailabeDevices.cs diff --git a/SpotifyAPI/Web/Models/BasicModel.cs b/SpotifyAPI.Web/Models/BasicModel.cs similarity index 100% rename from SpotifyAPI/Web/Models/BasicModel.cs rename to SpotifyAPI.Web/Models/BasicModel.cs diff --git a/SpotifyAPI/Web/Models/Category.cs b/SpotifyAPI.Web/Models/Category.cs similarity index 100% rename from SpotifyAPI/Web/Models/Category.cs rename to SpotifyAPI.Web/Models/Category.cs diff --git a/SpotifyAPI/Web/Models/CategoryList.cs b/SpotifyAPI.Web/Models/CategoryList.cs similarity index 100% rename from SpotifyAPI/Web/Models/CategoryList.cs rename to SpotifyAPI.Web/Models/CategoryList.cs diff --git a/SpotifyAPI/Web/Models/CategoryPlaylist.cs b/SpotifyAPI.Web/Models/CategoryPlaylist.cs similarity index 100% rename from SpotifyAPI/Web/Models/CategoryPlaylist.cs rename to SpotifyAPI.Web/Models/CategoryPlaylist.cs diff --git a/SpotifyAPI/Web/Models/CursorPaging.cs b/SpotifyAPI.Web/Models/CursorPaging.cs similarity index 100% rename from SpotifyAPI/Web/Models/CursorPaging.cs rename to SpotifyAPI.Web/Models/CursorPaging.cs diff --git a/SpotifyAPI/Web/Models/Device.cs b/SpotifyAPI.Web/Models/Device.cs similarity index 100% rename from SpotifyAPI/Web/Models/Device.cs rename to SpotifyAPI.Web/Models/Device.cs diff --git a/SpotifyAPI/Web/Models/FeaturedPlaylists.cs b/SpotifyAPI.Web/Models/FeaturedPlaylists.cs similarity index 100% rename from SpotifyAPI/Web/Models/FeaturedPlaylists.cs rename to SpotifyAPI.Web/Models/FeaturedPlaylists.cs diff --git a/SpotifyAPI/Web/Models/FollowedArtists.cs b/SpotifyAPI.Web/Models/FollowedArtists.cs similarity index 100% rename from SpotifyAPI/Web/Models/FollowedArtists.cs rename to SpotifyAPI.Web/Models/FollowedArtists.cs diff --git a/SpotifyAPI/Web/Models/FullAlbum.cs b/SpotifyAPI.Web/Models/FullAlbum.cs similarity index 100% rename from SpotifyAPI/Web/Models/FullAlbum.cs rename to SpotifyAPI.Web/Models/FullAlbum.cs diff --git a/SpotifyAPI/Web/Models/FullArtist.cs b/SpotifyAPI.Web/Models/FullArtist.cs similarity index 100% rename from SpotifyAPI/Web/Models/FullArtist.cs rename to SpotifyAPI.Web/Models/FullArtist.cs diff --git a/SpotifyAPI/Web/Models/FullPlaylist.cs b/SpotifyAPI.Web/Models/FullPlaylist.cs similarity index 100% rename from SpotifyAPI/Web/Models/FullPlaylist.cs rename to SpotifyAPI.Web/Models/FullPlaylist.cs diff --git a/SpotifyAPI/Web/Models/FullTrack.cs b/SpotifyAPI.Web/Models/FullTrack.cs similarity index 100% rename from SpotifyAPI/Web/Models/FullTrack.cs rename to SpotifyAPI.Web/Models/FullTrack.cs diff --git a/SpotifyAPI/Web/Models/GeneralModels.cs b/SpotifyAPI.Web/Models/GeneralModels.cs similarity index 100% rename from SpotifyAPI/Web/Models/GeneralModels.cs rename to SpotifyAPI.Web/Models/GeneralModels.cs diff --git a/SpotifyAPI/Web/Models/NewAlbumReleases.cs b/SpotifyAPI.Web/Models/NewAlbumReleases.cs similarity index 100% rename from SpotifyAPI/Web/Models/NewAlbumReleases.cs rename to SpotifyAPI.Web/Models/NewAlbumReleases.cs diff --git a/SpotifyAPI/Web/Models/Paging.cs b/SpotifyAPI.Web/Models/Paging.cs similarity index 100% rename from SpotifyAPI/Web/Models/Paging.cs rename to SpotifyAPI.Web/Models/Paging.cs diff --git a/SpotifyAPI/Web/Models/PlayHistory.cs b/SpotifyAPI.Web/Models/PlayHistory.cs similarity index 100% rename from SpotifyAPI/Web/Models/PlayHistory.cs rename to SpotifyAPI.Web/Models/PlayHistory.cs diff --git a/SpotifyAPI/Web/Models/PlaybackContext.cs b/SpotifyAPI.Web/Models/PlaybackContext.cs similarity index 100% rename from SpotifyAPI/Web/Models/PlaybackContext.cs rename to SpotifyAPI.Web/Models/PlaybackContext.cs diff --git a/SpotifyAPI/Web/Models/PrivateProfile.cs b/SpotifyAPI.Web/Models/PrivateProfile.cs similarity index 100% rename from SpotifyAPI/Web/Models/PrivateProfile.cs rename to SpotifyAPI.Web/Models/PrivateProfile.cs diff --git a/SpotifyAPI/Web/Models/PublicProfile.cs b/SpotifyAPI.Web/Models/PublicProfile.cs similarity index 100% rename from SpotifyAPI/Web/Models/PublicProfile.cs rename to SpotifyAPI.Web/Models/PublicProfile.cs diff --git a/SpotifyAPI/Web/Models/RecommendationSeed .cs b/SpotifyAPI.Web/Models/RecommendationSeed .cs similarity index 100% rename from SpotifyAPI/Web/Models/RecommendationSeed .cs rename to SpotifyAPI.Web/Models/RecommendationSeed .cs diff --git a/SpotifyAPI/Web/Models/RecommendationSeedGenres.cs b/SpotifyAPI.Web/Models/RecommendationSeedGenres.cs similarity index 100% rename from SpotifyAPI/Web/Models/RecommendationSeedGenres.cs rename to SpotifyAPI.Web/Models/RecommendationSeedGenres.cs diff --git a/SpotifyAPI/Web/Models/Recommendations.cs b/SpotifyAPI.Web/Models/Recommendations.cs similarity index 100% rename from SpotifyAPI/Web/Models/Recommendations.cs rename to SpotifyAPI.Web/Models/Recommendations.cs diff --git a/SpotifyAPI/Web/Models/ResponseInfo.cs b/SpotifyAPI.Web/Models/ResponseInfo.cs similarity index 100% rename from SpotifyAPI/Web/Models/ResponseInfo.cs rename to SpotifyAPI.Web/Models/ResponseInfo.cs diff --git a/SpotifyAPI/Web/Models/SearchItem.cs b/SpotifyAPI.Web/Models/SearchItem.cs similarity index 100% rename from SpotifyAPI/Web/Models/SearchItem.cs rename to SpotifyAPI.Web/Models/SearchItem.cs diff --git a/SpotifyAPI/Web/Models/SeveralAlbums.cs b/SpotifyAPI.Web/Models/SeveralAlbums.cs similarity index 100% rename from SpotifyAPI/Web/Models/SeveralAlbums.cs rename to SpotifyAPI.Web/Models/SeveralAlbums.cs diff --git a/SpotifyAPI/Web/Models/SeveralArtists.cs b/SpotifyAPI.Web/Models/SeveralArtists.cs similarity index 100% rename from SpotifyAPI/Web/Models/SeveralArtists.cs rename to SpotifyAPI.Web/Models/SeveralArtists.cs diff --git a/SpotifyAPI/Web/Models/SeveralAudioFeatures.cs b/SpotifyAPI.Web/Models/SeveralAudioFeatures.cs similarity index 100% rename from SpotifyAPI/Web/Models/SeveralAudioFeatures.cs rename to SpotifyAPI.Web/Models/SeveralAudioFeatures.cs diff --git a/SpotifyAPI/Web/Models/SeveralTracks.cs b/SpotifyAPI.Web/Models/SeveralTracks.cs similarity index 100% rename from SpotifyAPI/Web/Models/SeveralTracks.cs rename to SpotifyAPI.Web/Models/SeveralTracks.cs diff --git a/SpotifyAPI/Web/Models/SimpleAlbum.cs b/SpotifyAPI.Web/Models/SimpleAlbum.cs similarity index 100% rename from SpotifyAPI/Web/Models/SimpleAlbum.cs rename to SpotifyAPI.Web/Models/SimpleAlbum.cs diff --git a/SpotifyAPI/Web/Models/SimpleArtist.cs b/SpotifyAPI.Web/Models/SimpleArtist.cs similarity index 100% rename from SpotifyAPI/Web/Models/SimpleArtist.cs rename to SpotifyAPI.Web/Models/SimpleArtist.cs diff --git a/SpotifyAPI/Web/Models/SimplePlaylist.cs b/SpotifyAPI.Web/Models/SimplePlaylist.cs similarity index 100% rename from SpotifyAPI/Web/Models/SimplePlaylist.cs rename to SpotifyAPI.Web/Models/SimplePlaylist.cs diff --git a/SpotifyAPI/Web/Models/SimpleTrack.cs b/SpotifyAPI.Web/Models/SimpleTrack.cs similarity index 100% rename from SpotifyAPI/Web/Models/SimpleTrack.cs rename to SpotifyAPI.Web/Models/SimpleTrack.cs diff --git a/SpotifyAPI/Web/Models/Snapshot.cs b/SpotifyAPI.Web/Models/Snapshot.cs similarity index 100% rename from SpotifyAPI/Web/Models/Snapshot.cs rename to SpotifyAPI.Web/Models/Snapshot.cs diff --git a/SpotifyAPI/Web/Models/Token.cs b/SpotifyAPI.Web/Models/Token.cs similarity index 85% rename from SpotifyAPI/Web/Models/Token.cs rename to SpotifyAPI.Web/Models/Token.cs index d6c79d49..941d6f95 100644 --- a/SpotifyAPI/Web/Models/Token.cs +++ b/SpotifyAPI.Web/Models/Token.cs @@ -17,7 +17,7 @@ namespace SpotifyAPI.Web.Models public string TokenType { get; set; } [JsonProperty("expires_in")] - public int ExpiresIn { get; set; } + public double ExpiresIn { get; set; } [JsonProperty("refresh_token")] public string RefreshToken { get; set; } @@ -34,9 +34,14 @@ namespace SpotifyAPI.Web.Models /// Checks if the token has expired /// /// - public Boolean IsExpired() + public bool IsExpired() { return CreateDate.Add(TimeSpan.FromSeconds(ExpiresIn)) <= DateTime.Now; } + + public bool HasError() + { + return Error != null; + } } } \ No newline at end of file diff --git a/SpotifyAPI/Web/Models/TuneableTrack.cs b/SpotifyAPI.Web/Models/TuneableTrack.cs similarity index 100% rename from SpotifyAPI/Web/Models/TuneableTrack.cs rename to SpotifyAPI.Web/Models/TuneableTrack.cs diff --git a/SpotifyAPI/ProxyConfig.cs b/SpotifyAPI.Web/ProxyConfig.cs similarity index 100% rename from SpotifyAPI/ProxyConfig.cs rename to SpotifyAPI.Web/ProxyConfig.cs diff --git a/SpotifyAPI.Web/SpotifyAPI.Web.csproj b/SpotifyAPI.Web/SpotifyAPI.Web.csproj new file mode 100644 index 00000000..afada826 --- /dev/null +++ b/SpotifyAPI.Web/SpotifyAPI.Web.csproj @@ -0,0 +1,11 @@ + + + + netstandard2.0 + + + + + + + diff --git a/SpotifyAPI/Web/SpotifyWebAPI.cs b/SpotifyAPI.Web/SpotifyWebAPI.cs similarity index 99% rename from SpotifyAPI/Web/SpotifyWebAPI.cs rename to SpotifyAPI.Web/SpotifyWebAPI.cs index 6704803c..90f26e49 100644 --- a/SpotifyAPI/Web/SpotifyWebAPI.cs +++ b/SpotifyAPI.Web/SpotifyWebAPI.cs @@ -4,9 +4,7 @@ using SpotifyAPI.Web.Enums; using SpotifyAPI.Web.Models; using System; using System.Collections.Generic; -using System.Dynamic; using System.Linq; -using System.Runtime.InteropServices; using System.Threading; using System.Threading.Tasks; @@ -15,9 +13,6 @@ namespace SpotifyAPI.Web // ReSharper disable once InconsistentNaming public sealed class SpotifyWebAPI : IDisposable { - [Obsolete("This Property will be removed soon. Please use SpotifyWebBuilder.APIBase")] - public const string APIBase = SpotifyWebBuilder.APIBase; - private readonly SpotifyWebBuilder _builder; public SpotifyWebAPI() : this(null) diff --git a/SpotifyAPI/Web/SpotifyWebBuilder.cs b/SpotifyAPI.Web/SpotifyWebBuilder.cs similarity index 100% rename from SpotifyAPI/Web/SpotifyWebBuilder.cs rename to SpotifyAPI.Web/SpotifyWebBuilder.cs diff --git a/SpotifyAPI/Web/SpotifyWebClient.cs b/SpotifyAPI.Web/SpotifyWebClient.cs similarity index 99% rename from SpotifyAPI/Web/SpotifyWebClient.cs rename to SpotifyAPI.Web/SpotifyWebClient.cs index 0cd4a647..3789aa50 100644 --- a/SpotifyAPI/Web/SpotifyWebClient.cs +++ b/SpotifyAPI.Web/SpotifyWebClient.cs @@ -1,8 +1,6 @@ using Newtonsoft.Json; using System; using System.Collections.Generic; -using System.IO; -using System.Linq; using System.Net; using System.Net.Http; using System.Net.Http.Headers; diff --git a/SpotifyAPI/Web/Util.cs b/SpotifyAPI.Web/Util.cs similarity index 100% rename from SpotifyAPI/Web/Util.cs rename to SpotifyAPI.Web/Util.cs diff --git a/SpotifyAPI.sln b/SpotifyAPI.sln index 7d231038..30424096 100644 --- a/SpotifyAPI.sln +++ b/SpotifyAPI.sln @@ -1,13 +1,15 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 14 -VisualStudioVersion = 14.0.24720.0 +# Visual Studio 15 +VisualStudioVersion = 15.0.27004.2009 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SpotifyAPI", "SpotifyAPI\SpotifyAPI.csproj", "{EBBE35E2-7B91-4D7D-B8FC-3A0472F5119D}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SpotifyAPI.Web", "SpotifyAPI.Web\SpotifyAPI.Web.csproj", "{EC8A93BA-27C4-4642-B7A0-11B809C35BE4}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SpotifyAPI.Tests", "SpotifyAPI.Tests\SpotifyAPI.Tests.csproj", "{A06CE1BA-B2C4-4C3C-9E79-D2E8D36DDF0B}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SpotifyAPI.Web.Tests", "SpotifyAPI.Web.Tests\SpotifyAPI.Web.Tests.csproj", "{47FD780C-47B2-4634-9BB4-83C660101FAE}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SpotifyAPI.Example", "SpotifyAPI.Example\SpotifyAPI.Example.csproj", "{C8968A03-497E-4BAB-B492-5651AE7E0C06}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SpotifyAPI.Web.Example", "SpotifyAPI.Web.Example\SpotifyAPI.Web.Example.csproj", "{8D1CCA36-C7C2-4A70-B194-B23D7E03F00A}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SpotifyAPI.Web.Auth", "SpotifyAPI.Web.Auth\SpotifyAPI.Web.Auth.csproj", "{400A3787-FDBE-4A4C-9DDD-AAEB3DCE1DF5}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -15,20 +17,27 @@ Global Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {EBBE35E2-7B91-4D7D-B8FC-3A0472F5119D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {EBBE35E2-7B91-4D7D-B8FC-3A0472F5119D}.Debug|Any CPU.Build.0 = Debug|Any CPU - {EBBE35E2-7B91-4D7D-B8FC-3A0472F5119D}.Release|Any CPU.ActiveCfg = Release|Any CPU - {EBBE35E2-7B91-4D7D-B8FC-3A0472F5119D}.Release|Any CPU.Build.0 = Release|Any CPU - {A06CE1BA-B2C4-4C3C-9E79-D2E8D36DDF0B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {A06CE1BA-B2C4-4C3C-9E79-D2E8D36DDF0B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {A06CE1BA-B2C4-4C3C-9E79-D2E8D36DDF0B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {A06CE1BA-B2C4-4C3C-9E79-D2E8D36DDF0B}.Release|Any CPU.Build.0 = Release|Any CPU - {C8968A03-497E-4BAB-B492-5651AE7E0C06}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {C8968A03-497E-4BAB-B492-5651AE7E0C06}.Debug|Any CPU.Build.0 = Debug|Any CPU - {C8968A03-497E-4BAB-B492-5651AE7E0C06}.Release|Any CPU.ActiveCfg = Release|Any CPU - {C8968A03-497E-4BAB-B492-5651AE7E0C06}.Release|Any CPU.Build.0 = Release|Any CPU + {EC8A93BA-27C4-4642-B7A0-11B809C35BE4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {EC8A93BA-27C4-4642-B7A0-11B809C35BE4}.Debug|Any CPU.Build.0 = Debug|Any CPU + {EC8A93BA-27C4-4642-B7A0-11B809C35BE4}.Release|Any CPU.ActiveCfg = Release|Any CPU + {EC8A93BA-27C4-4642-B7A0-11B809C35BE4}.Release|Any CPU.Build.0 = Release|Any CPU + {47FD780C-47B2-4634-9BB4-83C660101FAE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {47FD780C-47B2-4634-9BB4-83C660101FAE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {47FD780C-47B2-4634-9BB4-83C660101FAE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {47FD780C-47B2-4634-9BB4-83C660101FAE}.Release|Any CPU.Build.0 = Release|Any CPU + {8D1CCA36-C7C2-4A70-B194-B23D7E03F00A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8D1CCA36-C7C2-4A70-B194-B23D7E03F00A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8D1CCA36-C7C2-4A70-B194-B23D7E03F00A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8D1CCA36-C7C2-4A70-B194-B23D7E03F00A}.Release|Any CPU.Build.0 = Release|Any CPU + {400A3787-FDBE-4A4C-9DDD-AAEB3DCE1DF5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {400A3787-FDBE-4A4C-9DDD-AAEB3DCE1DF5}.Debug|Any CPU.Build.0 = Debug|Any CPU + {400A3787-FDBE-4A4C-9DDD-AAEB3DCE1DF5}.Release|Any CPU.ActiveCfg = Release|Any CPU + {400A3787-FDBE-4A4C-9DDD-AAEB3DCE1DF5}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {097062B8-0E87-43C8-BD98-61843A68BE6D} + EndGlobalSection EndGlobal diff --git a/SpotifyAPI/Local/Enums/AlbumArtSize.cs b/SpotifyAPI/Local/Enums/AlbumArtSize.cs deleted file mode 100644 index a475193e..00000000 --- a/SpotifyAPI/Local/Enums/AlbumArtSize.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace SpotifyAPI.Local.Enums -{ - public enum AlbumArtSize - { - Size160, - Size320, - Size640 - } -} \ No newline at end of file diff --git a/SpotifyAPI/Local/Enums/UriType.cs b/SpotifyAPI/Local/Enums/UriType.cs deleted file mode 100644 index c5170755..00000000 --- a/SpotifyAPI/Local/Enums/UriType.cs +++ /dev/null @@ -1,12 +0,0 @@ -namespace SpotifyAPI.Local.Enums -{ - public enum UriType - { - none, - track, - album, - artist, - playlist, - user - } -} \ No newline at end of file diff --git a/SpotifyAPI/Local/ExtendedWebClient.cs b/SpotifyAPI/Local/ExtendedWebClient.cs deleted file mode 100644 index e1ca9f85..00000000 --- a/SpotifyAPI/Local/ExtendedWebClient.cs +++ /dev/null @@ -1,29 +0,0 @@ -using System; -using System.Net; -using System.Text; - -namespace SpotifyAPI.Local -{ - internal class ExtendedWebClient : WebClient - { - public int Timeout { get; set; } - - public ExtendedWebClient() - { - // TODO Remove once SSL Issues are resolved #115 - ServicePointManager.ServerCertificateValidationCallback = (s, certificate, chain, sslPolicyErrors) => true; - Encoding = Encoding.UTF8; - Timeout = 2000; - Headers.Add("Origin", "https://embed.spotify.com"); - Headers.Add("Referer", "https://embed.spotify.com/?uri=spotify:track:5Zp4SWOpbuOdnsxLqwgutt"); - } - - protected override WebRequest GetWebRequest(Uri address) - { - WebRequest webRequest = base.GetWebRequest(address); - if (webRequest != null) - webRequest.Timeout = Timeout; - return webRequest; - } - } -} \ No newline at end of file diff --git a/SpotifyAPI/Local/LocalEvents.cs b/SpotifyAPI/Local/LocalEvents.cs deleted file mode 100644 index b0e16d62..00000000 --- a/SpotifyAPI/Local/LocalEvents.cs +++ /dev/null @@ -1,39 +0,0 @@ -using SpotifyAPI.Local.Models; -using System; - -namespace SpotifyAPI.Local -{ - /// - /// Event gets triggered, when the Track is changed - /// - public class TrackChangeEventArgs - { - public Track OldTrack { get; set; } - public Track NewTrack { get; set; } - } - - /// - /// Event gets triggered, when the Playin-state is changed (e.g Play --> Pause) - /// - public class PlayStateEventArgs - { - public Boolean Playing { get; set; } - } - - /// - /// Event gets triggered, when the volume changes - /// - public class VolumeChangeEventArgs - { - public double OldVolume { get; set; } - public double NewVolume { get; set; } - } - - /// - /// Event gets triggered, when the tracktime changes - /// - public class TrackTimeChangeEventArgs - { - public double TrackTime { get; set; } - } -} \ No newline at end of file diff --git a/SpotifyAPI/Local/Models/CFID.cs b/SpotifyAPI/Local/Models/CFID.cs deleted file mode 100644 index 001a21b2..00000000 --- a/SpotifyAPI/Local/Models/CFID.cs +++ /dev/null @@ -1,25 +0,0 @@ -using System; - -namespace SpotifyAPI.Local.Models -{ - /// - /// JSON Response, used internaly - /// - internal class Cfid - { - public Error Error { get; set; } - public string Token { get; set; } - public string Version { get; set; } - public string ClientVersion { get; set; } - public Boolean Running { get; set; } - } - - /// - /// JSON Response, used internaly - /// - internal class Error - { - public string Type { get; set; } - public string Message { get; set; } - } -} \ No newline at end of file diff --git a/SpotifyAPI/Local/Models/OpenGraphState.cs b/SpotifyAPI/Local/Models/OpenGraphState.cs deleted file mode 100644 index 8f9df2b5..00000000 --- a/SpotifyAPI/Local/Models/OpenGraphState.cs +++ /dev/null @@ -1,14 +0,0 @@ -using Newtonsoft.Json; -using System; - -namespace SpotifyAPI.Local.Models -{ - public class OpenGraphState - { - [JsonProperty("private_session")] - public Boolean PrivateSession { get; set; } - - [JsonProperty("posting_disabled")] - public Boolean PostingDisabled { get; set; } - } -} \ No newline at end of file diff --git a/SpotifyAPI/Local/Models/SpotifyResource.cs b/SpotifyAPI/Local/Models/SpotifyResource.cs deleted file mode 100644 index 1dd5afda..00000000 --- a/SpotifyAPI/Local/Models/SpotifyResource.cs +++ /dev/null @@ -1,22 +0,0 @@ -using Newtonsoft.Json; -using System; - -namespace SpotifyAPI.Local.Models -{ - public class SpotifyResource - { - [JsonProperty("name")] - public string Name { get; set; } - - [JsonProperty("uri")] - public string Uri { get; set; } - - [JsonProperty("location")] - public TrackResourceLocation Location { get; set; } - - public SpotifyUri ParseUri() - { - return SpotifyUri.Parse(Uri); - } - } -} \ No newline at end of file diff --git a/SpotifyAPI/Local/Models/SpotifyUri.cs b/SpotifyAPI/Local/Models/SpotifyUri.cs deleted file mode 100644 index 16bd9122..00000000 --- a/SpotifyAPI/Local/Models/SpotifyUri.cs +++ /dev/null @@ -1,60 +0,0 @@ -using SpotifyAPI.Local.Enums; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace SpotifyAPI.Local.Models -{ - public class SpotifyUri - { - private readonly Dictionary _properties = new Dictionary(); - - public string Base { get; } - public UriType Type => _properties?.LastOrDefault().Key ?? UriType.none; - public string Id => _properties?.LastOrDefault().Value; - - public SpotifyUri(string uriBase, Dictionary properties) - { - Base = uriBase; - _properties = properties; - } - - public SpotifyUri(string uriBase, UriType uriType, string uriId) - { - Base = uriBase; - _properties.Add(uriType, uriId); - } - - public string GetUriPropValue(UriType type) - { - return !_properties.ContainsKey(type) ? null : _properties[type]; - } - - public static SpotifyUri Parse(string uri) - { - if (string.IsNullOrEmpty(uri)) - throw new ArgumentNullException("Uri"); - - string[] props = uri.Split(':'); - if (props.Length < 3 || !Enum.TryParse(props[1], out UriType uriType)) - throw new ArgumentException("Unexpected Uri"); - - Dictionary properties = new Dictionary { { uriType, props[2] } }; - - for (int index = 3; index < props.Length; index += 2) - { - if (Enum.TryParse(props[index], out UriType type)) - properties.Add(type, props[index + 1]); - } - - return new SpotifyUri(props[0], properties); - } - - public override string ToString() - { - return $"{Base}:{string.Join(":", _properties.SelectMany(x => new[] { x.Key.ToString(), x.Value }))}"; - } - } -} diff --git a/SpotifyAPI/Local/Models/StatusResponse.cs b/SpotifyAPI/Local/Models/StatusResponse.cs deleted file mode 100644 index f3af9a66..00000000 --- a/SpotifyAPI/Local/Models/StatusResponse.cs +++ /dev/null @@ -1,52 +0,0 @@ -using Newtonsoft.Json; - -namespace SpotifyAPI.Local.Models -{ - public class StatusResponse - { - [JsonProperty("version")] - public int Version { get; set; } - - [JsonProperty("client_version")] - public string ClientVersion { get; set; } - - [JsonProperty("playing")] - public bool Playing { get; set; } - - [JsonProperty("shuffle")] - public bool Shuffle { get; set; } - - [JsonProperty("repeat")] - public bool Repeat { get; set; } - - [JsonProperty("play_enabled")] - public bool PlayEnabled { get; set; } - - [JsonProperty("prev_enabled")] - public bool PrevEnabled { get; set; } - - [JsonProperty("next_enabled")] - public bool NextEnabled { get; set; } - - [JsonProperty("track")] - public Track Track { get; set; } - - [JsonProperty("playing_position")] - public double PlayingPosition { get; set; } - - [JsonProperty("server_time")] - public int ServerTime { get; set; } - - [JsonProperty("volume")] - public double Volume { get; set; } - - [JsonProperty("online")] - public bool Online { get; set; } - - [JsonProperty("open_graph_state")] - public OpenGraphState OpenGraphState { get; set; } - - [JsonProperty("running")] - public bool Running { get; set; } - } -} \ No newline at end of file diff --git a/SpotifyAPI/Local/Models/Track.cs b/SpotifyAPI/Local/Models/Track.cs deleted file mode 100755 index 2f489276..00000000 --- a/SpotifyAPI/Local/Models/Track.cs +++ /dev/null @@ -1,181 +0,0 @@ -using Newtonsoft.Json; -using SpotifyAPI.Local.Enums; -using System; -using System.Drawing; -using System.IO; -using System.Net; -using System.Text.RegularExpressions; -using System.Threading.Tasks; - -namespace SpotifyAPI.Local.Models -{ - public class Track - { - [JsonProperty("track_resource")] - public SpotifyResource TrackResource { get; set; } - - [JsonProperty("artist_resource")] - public SpotifyResource ArtistResource { get; set; } - - [JsonProperty("album_resource")] - public SpotifyResource AlbumResource { get; set; } - - [JsonProperty("length")] - public int Length { get; set; } - - [JsonProperty("track_type")] - public string TrackType { get; set; } - - /// - /// Checks if the track is an advert - /// - /// true if the track is an advert, false otherwise - public bool IsAd() => TrackType == "ad" || Length == 0; - - /// - /// Checks if the track id of type "other" - /// - /// true if the track is neither an advert nor a normal track, for example a podcast - public bool IsOtherTrackType() - { - return TrackType == "other"; - } - - /// - /// Returns a URL to the album cover in the provided size - /// - /// AlbumArtSize (160,320,640) - /// Optional proxy settings - /// A String, which is the URL to the Albumart - public string GetAlbumArtUrl(AlbumArtSize size, ProxyConfig proxyConfig = null) - { - return GetAlbumArtUrl(size, proxyConfig?.CreateWebProxy()); - } - - private string GetAlbumArtUrl(AlbumArtSize size, IWebProxy proxy = null) - { - if (AlbumResource.Uri == null || !AlbumResource.Uri.Contains("spotify:album:") || AlbumResource.Uri.Contains("spotify:album:0000000000000000000000")) - return ""; - - int albumsize = 0; - switch (size) - { - case AlbumArtSize.Size160: - albumsize = 160; - break; - - case AlbumArtSize.Size320: - albumsize = 320; - break; - - case AlbumArtSize.Size640: - albumsize = 640; - break; - } - string raw; - using (WebClient wc = new WebClient()) - { - wc.Proxy = proxy; - wc.Headers.Add(HttpRequestHeader.UserAgent, "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36"); - raw = wc.DownloadString("http://open.spotify.com/album/" + AlbumResource.Uri.Split(new[] { ":" }, StringSplitOptions.None)[2]); - } - raw = raw.Replace("\t", ""); - - // 0) - { - string content = matches[0].Groups[1].Value; - string[] l = content.Split(new[] { "/" }, StringSplitOptions.None); - return "http://o.scdn.co/" + albumsize + @"/" + l[4]; - } - } - return ""; - } - - /// - /// Returns a Bitmap of the album cover in the provided size asynchronous - /// - /// AlbumArtSize (160,320,640) - /// Optional proxy settings - /// A Bitmap, which is the albumart - public async Task GetAlbumArtAsync(AlbumArtSize size, ProxyConfig proxyConfig = null) - { - var data = await GetAlbumArtAsByteArrayAsync(size, proxyConfig).ConfigureAwait(false); - if (data != null) - { - using (MemoryStream ms = new MemoryStream(data)) - { - return (Bitmap)Image.FromStream(ms); - } - } - - return null; - } - - /// - /// Returns a byte[] of the the album cover in the provided size asynchronous - /// - /// AlbumArtSize (160,320,640) - /// Optional proxy settings - /// A byte[], which is the albumart in binary data - public Task GetAlbumArtAsByteArrayAsync(AlbumArtSize size, ProxyConfig proxyConfig = null) - { - using (WebClient wc = new WebClient()) - { - IWebProxy proxy = proxyConfig?.CreateWebProxy(); - wc.Proxy = proxy; - - string url = GetAlbumArtUrl(size, proxy); - if (url == "") - return null; - return wc.DownloadDataTaskAsync(url); - } - } - - /// - /// Returns a Bitmap of the album cover in the provided size - /// - /// AlbumArtSize (160,320,640) - /// Optional proxy settings - /// A Bitmap, which is the albumart - public Bitmap GetAlbumArt(AlbumArtSize size, ProxyConfig proxyConfig = null) - { - var data = GetAlbumArtAsByteArray(size, proxyConfig); - if (data != null) - { - using (MemoryStream ms = new MemoryStream(data)) - { - return (Bitmap)Image.FromStream(ms); - } - } - - return null; - } - - /// - /// Returns a byte[] of the album cover in the provided size - /// - /// AlbumArtSize (160,320,640) - /// Optional proxy settings - /// A byte[], which is the albumart in binary data - public byte[] GetAlbumArtAsByteArray(AlbumArtSize size, ProxyConfig proxyConfig = null) - { - using (WebClient wc = new WebClient()) - { - IWebProxy proxy = proxyConfig?.CreateWebProxy(); - wc.Proxy = proxy; - - string url = GetAlbumArtUrl(size, proxy); - if (string.IsNullOrEmpty(url)) - return null; - return wc.DownloadData(url); - } - } - } -} \ No newline at end of file diff --git a/SpotifyAPI/Local/Models/TrackResourceLocation.cs b/SpotifyAPI/Local/Models/TrackResourceLocation.cs deleted file mode 100644 index b4301866..00000000 --- a/SpotifyAPI/Local/Models/TrackResourceLocation.cs +++ /dev/null @@ -1,11 +0,0 @@ -using Newtonsoft.Json; -using System; - -namespace SpotifyAPI.Local.Models -{ - public class TrackResourceLocation - { - [JsonProperty("og")] - public string Og { get; set; } - } -} \ No newline at end of file diff --git a/SpotifyAPI/Local/RemoteHandler.cs b/SpotifyAPI/Local/RemoteHandler.cs deleted file mode 100644 index d21a79b3..00000000 --- a/SpotifyAPI/Local/RemoteHandler.cs +++ /dev/null @@ -1,194 +0,0 @@ -using Newtonsoft.Json; -using SpotifyAPI.Local.Models; -using System; -using System.Collections.Generic; -using System.Collections.Specialized; -using System.Net; -using System.Runtime.CompilerServices; -using System.Threading.Tasks; -using System.Web; - -[assembly: InternalsVisibleTo("SpotifyAPI.Tests")] - - -namespace SpotifyAPI.Local -{ - - internal class RemoteHandler - { - public string OauthKey { get; private set; } - public string CfidKey { get; private set; } - - private SpotifyLocalAPIConfig _config; - - public RemoteHandler(SpotifyLocalAPIConfig config) - { - _config = config; - } - - internal bool Init() - { - OauthKey = GetOAuthKey(); - CfidKey = GetCfid(); - return !string.IsNullOrEmpty(CfidKey); - } - - internal async Task SendPauseRequest() - { - NameValueCollection @params = new NameValueCollection { { "pause", "true" } }; - await QueryAsync("remote/pause.json", true, true, -1, @params).ConfigureAwait(false); - } - - internal async Task SendPlayRequest() - { - NameValueCollection @params = new NameValueCollection { { "pause", "false" } }; - await QueryAsync("remote/pause.json", true, true, -1, @params).ConfigureAwait(false); - } - - internal async Task SendPlayRequest(string url, string context = "") - { - - // TODO: instead of having an empty context, one way to fix the bug with the playback time beyond the length of a song would be to provide a 1-song context, and it would be fixed. - NameValueCollection @params = new NameValueCollection - { - {"uri", url}, - {"context", context} - }; - await QueryAsync($"remote/play.json", true, true, -1, @params).ConfigureAwait(false); - - } - - internal async Task SendQueueRequest(string url) - { - NameValueCollection @params = new NameValueCollection - { - {"uri", url}, - {"action", "queue"} - }; - await QueryAsync("remote/play.json", true, true, -1, @params).ConfigureAwait(false); - - } - - internal StatusResponse GetNewStatus() - { - string response = Query("remote/status.json", true, true, -1); - if (string.IsNullOrEmpty(response)) - return null; - response = response.Replace("\\n", ""); - List raw = JsonConvert.DeserializeObject>(response); - return raw[0]; - } - - internal string GetOAuthKey() - { - string raw; - using (WebClient wc = GetWebClientWithUserAgentHeader()) - { - raw = wc.DownloadString("http://open.spotify.com/token"); - } - Dictionary dic = JsonConvert.DeserializeObject>(raw); - return dic == null ? "" : (string)dic["t"]; - } - - internal string GetCfid() - { - string response = Query("simplecsrf/token.json", false, false, -1); - response = response.Replace(@"\", ""); - List cfidList = (List)JsonConvert.DeserializeObject(response, typeof(List)); - if (cfidList == null) - return ""; - if (cfidList.Count != 1) - throw new Exception("CFID could not be loaded"); - return cfidList[0].Error == null ? cfidList[0].Token : ""; - } - - internal string BuildQueryString(bool oauth, bool cfid, int wait, NameValueCollection @params = null) - { - if (@params == null) - { - @params = new NameValueCollection(); - } - NameValueCollection queryParameter = HttpUtility.ParseQueryString(string.Empty); - queryParameter.Add(@params); - queryParameter.Add(new NameValueCollection() { - { "ref", string.Empty}, - { "cors", string.Empty}, - { "_", GetTimestamp().ToString()} - }); - if (oauth) - { - queryParameter.Add("oauth", OauthKey); - } - if (cfid) - { - queryParameter.Add("csrf", CfidKey); - } - - if (wait != -1) - { - queryParameter.Add("returnafter", wait.ToString()); - queryParameter.Add("returnon", "login%2Clogout%2Cplay%2Cpause%2Cerror%2Cap"); - } - - return queryParameter.ToString(); - } - internal string Query(string baseUrl, bool oauth, bool cfid, int wait, NameValueCollection @params = null) - { - string parameters = BuildQueryString(oauth, cfid, wait, @params); - string address = $"{_config.HostUrl}:{_config.Port}/{baseUrl}?{parameters}"; - string response = string.Empty; - try - { - using (ExtendedWebClient wc = new ExtendedWebClient()) - { - if (SpotifyLocalAPI.IsSpotifyRunning()) - { - response = "[ " + wc.DownloadString(address) + " ]"; - } - } - } - catch - { - return string.Empty; - } - - return response; - } - - internal async Task QueryAsync(string baseUrl, bool oauth, bool cfid, int wait, NameValueCollection @params = null) - { - string parameters = BuildQueryString(oauth, cfid, wait, @params); - string address = $"{_config.HostUrl}:{_config.Port}/{baseUrl}?{parameters}"; - string response = ""; - try - { - using (ExtendedWebClient wc = new ExtendedWebClient()) - { - if (SpotifyLocalAPI.IsSpotifyRunning()) - response = "[ " + await wc.DownloadStringTaskAsync(new Uri(address)).ConfigureAwait(false) + " ]"; - } - } - catch - { - return string.Empty; - } - - return response; - } - internal int GetTimestamp() - { - return Convert.ToInt32((DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0)).TotalSeconds); - } - - internal WebClient GetWebClientWithUserAgentHeader() - { - WebClient wc = new WebClient - { - Proxy = _config?.ProxyConfig?.CreateWebProxy() - }; - wc.Headers.Add(HttpRequestHeader.UserAgent, _config?.UserAgent); - - return wc; - } - } -} \ No newline at end of file diff --git a/SpotifyAPI/Local/SpotifyLocalAPI.cs b/SpotifyAPI/Local/SpotifyLocalAPI.cs deleted file mode 100644 index 4ebba799..00000000 --- a/SpotifyAPI/Local/SpotifyLocalAPI.cs +++ /dev/null @@ -1,368 +0,0 @@ -using SpotifyAPI.Local.Models; -using System; -using System.ComponentModel; -using System.Diagnostics; -using System.IO; -using System.Runtime.InteropServices; -using System.Threading.Tasks; -using System.Timers; - -namespace SpotifyAPI.Local -{ - public class SpotifyLocalAPI : IDisposable - { - [DllImport("user32.dll")] - private static extern void keybd_event(byte bVk, byte bScan, uint dwFlags, int dwExtraInfo); - - 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; - } - } - - private const int WindowsSevenMajorVersion = 6; - private const int WindowsSevenMinorVersion = 1; - private const byte VkMediaNextTrack = 0xb0; - private const byte VkMediaPrevTrack = 0xb1; - private const int KeyeventfExtendedkey = 0x1; - private const int KeyeventfKeyup = 0x2; - - private readonly RemoteHandler _rh; - private Timer _eventTimer; - private StatusResponse _eventStatusResponse; - private Track _eventStatusTrack; - - public event EventHandler OnTrackChange; - - public event EventHandler OnPlayStateChange; - - public event EventHandler OnVolumeChange; - - public event EventHandler OnTrackTimeChange; - - public SpotifyLocalAPI(int timerIntervall = 50) - { - _rh = new RemoteHandler(new SpotifyLocalAPIConfig()); - AttachTimer(timerIntervall); - } - - public SpotifyLocalAPI(SpotifyLocalAPIConfig config) - { - _rh = new RemoteHandler(config); - AttachTimer(config.TimerInterval); - } - - private void AttachTimer(int intervall) - { - _eventTimer = new Timer - { - Interval = intervall, - 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 && _eventStatusTrack != null) - { - if (newStatusResponse.Track.TrackResource?.Uri != _eventStatusTrack.TrackResource?.Uri || - newStatusResponse.Track.IsOtherTrackType() && newStatusResponse.Track.Length != _eventStatusTrack.Length) - { - OnTrackChange?.Invoke(this, new TrackChangeEventArgs() - { - OldTrack = _eventStatusTrack, - NewTrack = newStatusResponse.Track - }); - } - } - if (newStatusResponse.Playing != _eventStatusResponse.Playing) - { - OnPlayStateChange?.Invoke(this, new PlayStateEventArgs() - { - Playing = newStatusResponse.Playing - }); - } - if (newStatusResponse.Volume != _eventStatusResponse.Volume) - { - OnVolumeChange?.Invoke(this, new VolumeChangeEventArgs() - { - OldVolume = _eventStatusResponse.Volume, - NewVolume = newStatusResponse.Volume - }); - } - if (newStatusResponse.PlayingPosition != _eventStatusResponse.PlayingPosition) - { - OnTrackTimeChange?.Invoke(this, new TrackTimeChangeEventArgs() - { - TrackTime = newStatusResponse.PlayingPosition - }); - } - _eventStatusResponse = newStatusResponse; - if (newStatusResponse.Track != null) - { - _eventStatusTrack = newStatusResponse.Track; - } - _eventTimer.Start(); - } - - private bool IsOSCompatible(int minMajor, int minMinor) - { - return Environment.OSVersion.Version.Major > minMajor || (Environment.OSVersion.Version.Major == minMajor && Environment.OSVersion.Version.Minor >= minMinor); - } - /// - /// 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 Windows 7 or newer) - /// - public void Mute() - { - if(!IsOSCompatible(WindowsSevenMajorVersion, WindowsSevenMinorVersion)) - throw new NotSupportedException("This feature is only available on Windows 7 or newer"); - VolumeMixerControl.MuteSpotify(true); - } - - /// - /// Unmutes Spotify (Requires Windows 7 or newer) - /// - public void UnMute() - { - if (!IsOSCompatible(WindowsSevenMajorVersion, WindowsSevenMinorVersion)) - throw new NotSupportedException("This feature is only available on Windows 7 or newer"); - VolumeMixerControl.MuteSpotify(false); - } - - /// - /// Checks whether Spotify is muted in the Volume Mixer control (required Windows 7 or newer) - /// - /// Null if an error occured, otherwise the muted state - public bool IsSpotifyMuted() - { - if (!IsOSCompatible(WindowsSevenMajorVersion, WindowsSevenMinorVersion)) - throw new NotSupportedException("This feature is only available on Windows 7 or newer"); - return VolumeMixerControl.IsSpotifyMuted(); - } - - /// - /// Sets the Volume Mixer volume (requires Windows 7 or newer) - /// - /// A value between 0 and 100 - public void SetSpotifyVolume(float volume = 100) - { - if (!IsOSCompatible(WindowsSevenMajorVersion, WindowsSevenMinorVersion)) - throw new NotSupportedException("This feature is only available on Windows 7 or newer"); - if (volume < 0 || volume > 100) - throw new ArgumentOutOfRangeException(nameof(volume)); - VolumeMixerControl.SetSpotifyVolume(volume); - } - - /// - /// Return the Volume Mixer volume of Spotify (requires Windows 7 or newer) - /// - /// Null if an error occured, otherwise a float between 0 and 100 - public float GetSpotifyVolume() - { - if (!IsOSCompatible(WindowsSevenMajorVersion, WindowsSevenMinorVersion)) - throw new NotSupportedException("This feature is only available on Windows 7 or newer"); - return VolumeMixerControl.GetSpotifyVolume(); - } - - /// - /// Pause function - /// - public async Task Pause() - { - await _rh.SendPauseRequest(); - } - - /// - /// Play function - /// - public async Task Play() - { - await _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 async Task PlayURL(string uri, string context = "") - { - await _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 async Task AddToQueue(string uri) - { - await _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; - } - - /// - /// Determines whether [spotify is installed]. - /// - /// - /// true if [spotify is installed]; otherwise, false. - /// - public static bool IsSpotifyInstalled() - { - bool isInstalled = false; - - // Checks if UWP Spotify is installed. - string uwpSpotifyPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), @"Packages\SpotifyAB.SpotifyMusic_zpdnekdrzrea0"); - - isInstalled = Directory.Exists(uwpSpotifyPath); - - // If UWP Spotify is not installed, try look for desktop version - if (!isInstalled) - { - string desktopSpotifyPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), @"Spotify\Spotify.exe"); - isInstalled = File.Exists(desktopSpotifyPath); - } - - return isInstalled; - } - - /// - /// Runs Spotify - /// - public static void RunSpotify() - { - if (!IsSpotifyRunning() && File.Exists(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), @"spotify\spotify.exe"))) - Process.Start(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), @"spotify\spotify.exe")); - } - - /// - /// Runs Spotify's WebHelper - /// - public static void RunSpotifyWebHelper() - { - if (!IsSpotifyWebHelperRunning() && File.Exists(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), @"spotify\data\spotifywebhelper.exe"))) - { - Process.Start(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), @"spotify\data\spotifywebhelper.exe")); - } - else if (!IsSpotifyWebHelperRunning() && File.Exists(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; - } - } -} \ No newline at end of file diff --git a/SpotifyAPI/Local/SpotifyLocalAPIConfig.cs b/SpotifyAPI/Local/SpotifyLocalAPIConfig.cs deleted file mode 100644 index 19cec30b..00000000 --- a/SpotifyAPI/Local/SpotifyLocalAPIConfig.cs +++ /dev/null @@ -1,16 +0,0 @@ -namespace SpotifyAPI.Local -{ - // ReSharper disable once InconsistentNaming - public class SpotifyLocalAPIConfig - { - public int TimerInterval { get; set; } = 50; - - public string HostUrl { get; set; } = "http://127.0.0.1"; - - public int Port { get; set; } = 4381; - - public ProxyConfig ProxyConfig { get; set; } - - public string UserAgent { get; set; } = "Spotify (1.0.85.257.g0f8531bd)"; - } -} \ No newline at end of file diff --git a/SpotifyAPI/Local/VolumeMixerControl.cs b/SpotifyAPI/Local/VolumeMixerControl.cs deleted file mode 100644 index 2c0133a2..00000000 --- a/SpotifyAPI/Local/VolumeMixerControl.cs +++ /dev/null @@ -1,302 +0,0 @@ -using System; -using System.Diagnostics; -using System.Linq; -using System.Runtime.InteropServices; - -namespace SpotifyAPI.Local -{ - internal static class VolumeMixerControl - { - private const string SpotifyProcessName = "spotify"; - - internal static float GetSpotifyVolume() - { - ISimpleAudioVolume volume = GetSpotifyVolumeObject(); - - if (volume == null) - { - throw new COMException("Volume object creation failed"); - } - - volume.GetMasterVolume(out float level); - Marshal.ReleaseComObject(volume); - return level * 100; - } - - internal static bool IsSpotifyMuted() - { - ISimpleAudioVolume volume = GetSpotifyVolumeObject(); - - if (volume == null) - { - throw new COMException("Volume object creation failed"); - } - - volume.GetMute(out bool mute); - Marshal.ReleaseComObject(volume); - return mute; - } - - internal static void SetSpotifyVolume(float level) - { - ISimpleAudioVolume volume = GetSpotifyVolumeObject(); - - if (volume == null) - { - throw new COMException("Volume object creation failed"); - } - - Guid guid = Guid.Empty; - volume.SetMasterVolume(level / 100, ref guid); - Marshal.ReleaseComObject(volume); - } - - internal static void MuteSpotify(bool mute) - { - ISimpleAudioVolume volume = GetSpotifyVolumeObject(); - - if (volume == null) - { - throw new COMException("Volume object creation failed"); - } - - Guid guid = Guid.Empty; - volume.SetMute(mute, ref guid); - Marshal.ReleaseComObject(volume); - } - - private static ISimpleAudioVolume GetSpotifyVolumeObject() - { - var audioVolumeObjects = from p in Process.GetProcessesByName(SpotifyProcessName) - let vol = GetVolumeObject(p.Id) - where vol != null - select vol; - return audioVolumeObjects.FirstOrDefault(); - } - - private static ISimpleAudioVolume GetVolumeObject(int pid) - { - // get the speakers (1st render + multimedia) device - IMmDeviceEnumerator deviceEnumerator = (IMmDeviceEnumerator) new MMDeviceEnumerator(); - deviceEnumerator.GetDefaultAudioEndpoint(EDataFlow.ERender, ERole.EMultimedia, out IMmDevice speakers); - - speakers.GetId(out string defaultDeviceId); - - ISimpleAudioVolume volumeControl = GetVolumeObject(pid, speakers); - Marshal.ReleaseComObject(speakers); - - if (volumeControl == null) - { - // If volumeControl is null, then the process's volume object might be on a different device. - // This happens if the process doesn't use the default device. - // - // As far as Spotify is concerned, if using the "--enable-audio-graph" command line argument, - // a new option becomes available in the Settings that makes it possible to change the playback device. - - deviceEnumerator.EnumAudioEndpoints(EDataFlow.ERender, EDeviceState.Active, out IMmDeviceCollection deviceCollection); - - deviceCollection.GetCount(out int count); - for (int i = 0; i < count; i++) - { - deviceCollection.Item(i, out IMmDevice device); - device.GetId(out string deviceId); - - try - { - if (deviceId == defaultDeviceId) - continue; - - volumeControl = GetVolumeObject(pid, device); - if (volumeControl != null) - break; - } - finally - { - Marshal.ReleaseComObject(device); - } - } - } - - Marshal.ReleaseComObject(deviceEnumerator); - return volumeControl; - } - - private static ISimpleAudioVolume GetVolumeObject(int pid, IMmDevice device) - { - // activate the session manager. we need the enumerator - Guid iidIAudioSessionManager2 = typeof(IAudioSessionManager2).GUID; - device.Activate(ref iidIAudioSessionManager2, 0, IntPtr.Zero, out object o); - IAudioSessionManager2 mgr = (IAudioSessionManager2) o; - - // enumerate sessions for on this device - mgr.GetSessionEnumerator(out IAudioSessionEnumerator sessionEnumerator); - sessionEnumerator.GetCount(out int count); - - // search for an audio session with the required name - // NOTE: we could also use the process id instead of the app name (with IAudioSessionControl2) - ISimpleAudioVolume volumeControl = null; - for (int i = 0; i < count; i++) - { - sessionEnumerator.GetSession(i, out IAudioSessionControl2 ctl); - ctl.GetProcessId(out int cpid); - - if (cpid == pid) - { - volumeControl = (ISimpleAudioVolume) ctl; - break; - } - Marshal.ReleaseComObject(ctl); - } - Marshal.ReleaseComObject(sessionEnumerator); - Marshal.ReleaseComObject(mgr); - return volumeControl; - } - - [ComImport] - [Guid("BCDE0395-E52F-467C-8E3D-C4579291692E")] - private class MMDeviceEnumerator - { - } - - private enum EDataFlow - { - ERender, - ECapture, - EAll, - EDataFlowEnumCount - } - - private enum ERole - { - EConsole, - EMultimedia, - ECommunications, - ERoleEnumCount - } - - [Flags] - private enum EDeviceState - { - Active = 0x00000001, - Disabled = 0x00000002, - NotPresent = 0x00000004, - UnPlugged = 0x00000008, - All = 0x0000000F - } - - [Guid("A95664D2-9614-4F35-A746-DE8DB63617E6"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] - private interface IMmDeviceEnumerator - { - [PreserveSig] - int EnumAudioEndpoints(EDataFlow dataFlow, EDeviceState stateMask, [Out] out IMmDeviceCollection deviceCollection); - - [PreserveSig] - int GetDefaultAudioEndpoint(EDataFlow dataFlow, ERole role, out IMmDevice ppDevice); - } - - [Guid("D666063F-1587-4E43-81F1-B948E807363F"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] - private interface IMmDevice - { - [PreserveSig] - int Activate(ref Guid iid, int dwClsCtx, IntPtr pActivationParams, [MarshalAs(UnmanagedType.IUnknown)] out object ppInterface); - - int OpenPropertyStore_NotImpl(); - - [PreserveSig] - int GetId([Out, MarshalAs(UnmanagedType.LPWStr)] out string ppstrId); - } - - [Guid("0BD7A1BE-7A1A-44DB-8397-CC5392387B5E"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] - private interface IMmDeviceCollection - { - [PreserveSig] - int GetCount(out int deviceCount); - - [PreserveSig] - int Item(int deviceIndex, [Out] out IMmDevice device); - } - - [Guid("77AA99A0-1BD6-484F-8BC7-2C654C9A9B6F"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] - private interface IAudioSessionManager2 - { - int NotImpl1(); - - int NotImpl2(); - - [PreserveSig] - int GetSessionEnumerator(out IAudioSessionEnumerator sessionEnum); - } - - [Guid("E2F5BB11-0570-40CA-ACDD-3AA01277DEE8"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] - private interface IAudioSessionEnumerator - { - [PreserveSig] - int GetCount(out int sessionCount); - - [PreserveSig] - int GetSession(int sessionCount, out IAudioSessionControl2 session); - } - - [Guid("87CE5498-68D6-44E5-9215-6DA47EF883D8"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] - private interface ISimpleAudioVolume - { - [PreserveSig] - int SetMasterVolume(float fLevel, ref Guid eventContext); - - [PreserveSig] - int GetMasterVolume(out float pfLevel); - - [PreserveSig] - int SetMute(bool bMute, ref Guid eventContext); - - [PreserveSig] - int GetMute(out bool pbMute); - } - - [Guid("bfb7ff88-7239-4fc9-8fa2-07c950be9c6d"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] - private interface IAudioSessionControl2 - { - [PreserveSig] - int NotImpl0(); - - [PreserveSig] - int GetDisplayName([MarshalAs(UnmanagedType.LPWStr)] out string pRetVal); - - [PreserveSig] - int SetDisplayName([MarshalAs(UnmanagedType.LPWStr)]string value, [MarshalAs(UnmanagedType.LPStruct)] Guid eventContext); - - [PreserveSig] - int GetIconPath([MarshalAs(UnmanagedType.LPWStr)] out string pRetVal); - - [PreserveSig] - int SetIconPath([MarshalAs(UnmanagedType.LPWStr)] string value, [MarshalAs(UnmanagedType.LPStruct)] Guid eventContext); - - [PreserveSig] - int GetGroupingParam(out Guid pRetVal); - - [PreserveSig] - int SetGroupingParam([MarshalAs(UnmanagedType.LPStruct)] Guid Override, [MarshalAs(UnmanagedType.LPStruct)] Guid eventContext); - - [PreserveSig] - int NotImpl1(); - - [PreserveSig] - int NotImpl2(); - - [PreserveSig] - int GetSessionIdentifier([MarshalAs(UnmanagedType.LPWStr)] out string pRetVal); - - [PreserveSig] - int GetSessionInstanceIdentifier([MarshalAs(UnmanagedType.LPWStr)] out string pRetVal); - - [PreserveSig] - int GetProcessId(out int pRetVal); - - [PreserveSig] - int IsSystemSoundsSession(); - - [PreserveSig] - int SetDuckingPreference(bool optOut); - } - } -} \ No newline at end of file diff --git a/SpotifyAPI/Properties/AssemblyInfo.cs b/SpotifyAPI/Properties/AssemblyInfo.cs deleted file mode 100644 index 5c4ae80d..00000000 --- a/SpotifyAPI/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,35 +0,0 @@ -using System.Reflection; -using System.Runtime.InteropServices; - -// Allgemeine Informationen über eine Assembly werden über die folgenden -// Attribute gesteuert. Ändern Sie diese Attributwerte, um die Informationen zu ändern, -// die mit einer Assembly verknüpft sind. -[assembly: AssemblyTitle("SpotifyLocalAPIClass")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("SpotifyLocalAPIClass")] -[assembly: AssemblyCopyright("Copyright © 2014")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// Durch Festlegen von ComVisible auf "false" werden die Typen in dieser Assembly unsichtbar -// für COM-Komponenten. Wenn Sie auf einen Typ in dieser Assembly von -// COM zugreifen müssen, legen Sie das ComVisible-Attribut für diesen Typ auf "true" fest. -[assembly: ComVisible(false)] - -// Die folgende GUID bestimmt die ID der Typbibliothek, wenn dieses Projekt für COM verfügbar gemacht wird -[assembly: Guid("3a30f0ac-4874-4d18-ba0e-d20d0f67ed32")] - -// Versionsinformationen für eine Assembly bestehen aus den folgenden vier Werten: -// -// Hauptversion -// Nebenversion -// Buildnummer -// Revision -// -// Sie können alle Werte angeben oder die standardmäßigen Build- und Revisionsnummern -// übernehmen, indem Sie "*" eingeben: -// [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] \ No newline at end of file diff --git a/SpotifyAPI/SpotifyAPI.csproj b/SpotifyAPI/SpotifyAPI.csproj deleted file mode 100644 index 4fa3577e..00000000 --- a/SpotifyAPI/SpotifyAPI.csproj +++ /dev/null @@ -1,151 +0,0 @@ - - - - - Debug - AnyCPU - {EBBE35E2-7B91-4D7D-B8FC-3A0472F5119D} - Library - Properties - SpotifyAPI - SpotifyAPI - v4.5 - 512 - ..\ - true - - - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - bin\Debug\SpotifyAPI.XML - 1591 - MinimumRecommendedRules.ruleset - - - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - bin\Release\SpotifyAPI.XML - 1591 - - - - ..\packages\Newtonsoft.Json.10.0.3\lib\net45\Newtonsoft.Json.dll - - - - - - - - - - - - - - - - - - Component - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/SpotifyAPI/SpotifyAPI.nuspec b/SpotifyAPI/SpotifyAPI.nuspec deleted file mode 100644 index 49889587..00000000 --- a/SpotifyAPI/SpotifyAPI.nuspec +++ /dev/null @@ -1,25 +0,0 @@ - - - - SpotifyAPI-NET - 1.0.0 - SpotifyAPI-NET - JohnnyCrazy - JohnnyCrazy - https://github.com/JohnnyCrazy/SpotifyAPI-NET/blob/master/LICENSE - https://github.com/JohnnyCrazy/SpotifyAPI-NET/ - false - An API for the Spotify-Client and Spotify's Web API, written in .NET - -For more infos, visit https://github.com/JohnnyCrazy/SpotifyAPI-NET - - spotify api music .net c# spotify-client - - - - - - - - - diff --git a/SpotifyAPI/Web/Auth/AutorizationCodeAuth.cs b/SpotifyAPI/Web/Auth/AutorizationCodeAuth.cs deleted file mode 100644 index a6785e56..00000000 --- a/SpotifyAPI/Web/Auth/AutorizationCodeAuth.cs +++ /dev/null @@ -1,160 +0,0 @@ -using Newtonsoft.Json; -using SpotifyAPI.Web.Enums; -using SpotifyAPI.Web.Models; -using System; -using System.Collections.Specialized; -using System.Diagnostics; -using System.IO; -using System.Net; -using System.Text; -using System.Threading; - -namespace SpotifyAPI.Web.Auth -{ - public class AutorizationCodeAuth - { - public delegate void OnResponseReceived(AutorizationCodeAuthResponse response); - - private SimpleHttpServer _httpServer; - private Thread _httpThread; - public string ClientId { get; set; } - public string RedirectUri { get; set; } - public string State { get; set; } - public Scope Scope { get; set; } - public Boolean ShowDialog { get; set; } - - /// - /// Will be fired once the user authenticated - /// - public event OnResponseReceived OnResponseReceivedEvent; - - /// - /// Start the auth process (Make sure the internal HTTP-Server ist started) - /// - public void DoAuth() - { - string uri = GetUri(); - Process.Start(uri); - } - - /// - /// Refreshes auth by providing the clientsecret (Don't use this if you're on a client) - /// - /// The refresh-token of the earlier gathered token - /// Your Client-Secret, don't provide it if this is running on a client! - public Token RefreshToken(string refreshToken, string clientSecret) - { - using (WebClient wc = new WebClient()) - { - wc.Headers.Add("Authorization", - "Basic " + Convert.ToBase64String(Encoding.UTF8.GetBytes(ClientId + ":" + clientSecret))); - NameValueCollection col = new NameValueCollection - { - {"grant_type", "refresh_token"}, - {"refresh_token", refreshToken} - }; - - string response; - try - { - byte[] data = wc.UploadValues("https://accounts.spotify.com/api/token", "POST", col); - response = Encoding.UTF8.GetString(data); - } - catch (WebException e) - { - using (StreamReader reader = new StreamReader(e.Response.GetResponseStream())) - { - response = reader.ReadToEnd(); - } - } - return JsonConvert.DeserializeObject(response); - } - } - - private string GetUri() - { - StringBuilder builder = new StringBuilder("https://accounts.spotify.com/authorize/?"); - builder.Append("client_id=" + ClientId); - builder.Append("&response_type=code"); - builder.Append("&redirect_uri=" + RedirectUri); - builder.Append("&state=" + State); - builder.Append("&scope=" + Scope.GetStringAttribute(" ")); - builder.Append("&show_dialog=" + ShowDialog); - return builder.ToString(); - } - - /// - /// Start the internal HTTP-Server - /// - public void StartHttpServer(int port = 80) - { - _httpServer = new SimpleHttpServer(port, AuthType.Authorization); - _httpServer.OnAuth += HttpServerOnOnAuth; - - _httpThread = new Thread(_httpServer.Listen); - _httpThread.Start(); - } - - private void HttpServerOnOnAuth(AuthEventArgs e) - { - OnResponseReceivedEvent?.Invoke(new AutorizationCodeAuthResponse() - { - Code = e.Code, - State = e.State, - Error = e.Error - }); - } - - /// - /// This will stop the internal HTTP-Server (Should be called after you got the Token) - /// - public void StopHttpServer() - { - _httpServer.Dispose(); - _httpServer = null; - } - - /// - /// Exchange a code for a Token (Don't use this if you're on a client) - /// - /// The gathered code from the response - /// Your Client-Secret, don't provide it if this is running on a client! - /// - public Token ExchangeAuthCode(string code, string clientSecret) - { - using (WebClient wc = new WebClient()) - { - NameValueCollection col = new NameValueCollection - { - {"grant_type", "authorization_code"}, - {"code", code}, - {"redirect_uri", RedirectUri}, - {"client_id", ClientId}, - {"client_secret", clientSecret} - }; - - string response; - try - { - byte[] data = wc.UploadValues("https://accounts.spotify.com/api/token", "POST", col); - response = Encoding.UTF8.GetString(data); - } - catch (WebException e) - { - using (StreamReader reader = new StreamReader(e.Response.GetResponseStream())) - { - response = reader.ReadToEnd(); - } - } - return JsonConvert.DeserializeObject(response); - } - } - } - - public struct AutorizationCodeAuthResponse - { - public string Code { get; set; } - public string State { get; set; } - public string Error { get; set; } - } -} diff --git a/SpotifyAPI/Web/Auth/ClientCredentialsAuth.cs b/SpotifyAPI/Web/Auth/ClientCredentialsAuth.cs deleted file mode 100644 index 9e68c2fb..00000000 --- a/SpotifyAPI/Web/Auth/ClientCredentialsAuth.cs +++ /dev/null @@ -1,85 +0,0 @@ -using Newtonsoft.Json; -using SpotifyAPI.Web.Enums; -using SpotifyAPI.Web.Models; -using System; -using System.Collections.Specialized; -using System.IO; -using System.Net; -using System.Text; -using System.Threading.Tasks; - -namespace SpotifyAPI.Web.Auth -{ - public class ClientCredentialsAuth - { - public Scope Scope { get; set; } - public string ClientId { get; set; } - public string ClientSecret { get; set; } - - /// - /// Starts the auth process and - /// - /// A new Token - public Token DoAuth() - { - using (WebClient wc = new WebClient()) - { - wc.Headers.Add("Authorization", - "Basic " + Convert.ToBase64String(Encoding.UTF8.GetBytes(ClientId + ":" + ClientSecret))); - - NameValueCollection col = new NameValueCollection - { - {"grant_type", "client_credentials"}, - {"scope", Scope.GetStringAttribute(" ")} - }; - - byte[] data; - try - { - data = wc.UploadValues("https://accounts.spotify.com/api/token", "POST", col); - } - catch (WebException e) - { - using (StreamReader reader = new StreamReader(e.Response.GetResponseStream())) - { - data = Encoding.UTF8.GetBytes(reader.ReadToEnd()); - } - } - return JsonConvert.DeserializeObject(Encoding.UTF8.GetString(data)); - } - } - - /// - /// Starts the auth process async and - /// - /// A new Token - public async Task DoAuthAsync() - { - using (WebClient wc = new WebClient()) - { - wc.Headers.Add("Authorization", - "Basic " + Convert.ToBase64String(Encoding.UTF8.GetBytes(ClientId + ":" + ClientSecret))); - - NameValueCollection col = new NameValueCollection - { - {"grant_type", "client_credentials"}, - {"scope", Scope.GetStringAttribute(" ")} - }; - - byte[] data; - try - { - data = await wc.UploadValuesTaskAsync("https://accounts.spotify.com/api/token", "POST", col); - } - catch (WebException e) - { - using (StreamReader reader = new StreamReader(e.Response.GetResponseStream())) - { - data = Encoding.UTF8.GetBytes(await reader.ReadToEndAsync()); - } - } - return JsonConvert.DeserializeObject(Encoding.UTF8.GetString(data)); - } - } - } -} \ No newline at end of file diff --git a/SpotifyAPI/Web/Auth/ImplicitGrantAuth.cs b/SpotifyAPI/Web/Auth/ImplicitGrantAuth.cs deleted file mode 100644 index 53a5c65c..00000000 --- a/SpotifyAPI/Web/Auth/ImplicitGrantAuth.cs +++ /dev/null @@ -1,77 +0,0 @@ -using SpotifyAPI.Web.Enums; -using SpotifyAPI.Web.Models; -using System; -using System.Diagnostics; -using System.Text; -using System.Threading; - -namespace SpotifyAPI.Web.Auth -{ - public class ImplicitGrantAuth - { - public delegate void OnResponseReceived(Token token, string state); - - private SimpleHttpServer _httpServer; - private Thread _httpThread; - public string ClientId { get; set; } - public string RedirectUri { get; set; } - public string State { get; set; } - public Scope Scope { get; set; } - public Boolean ShowDialog { get; set; } - - public event OnResponseReceived OnResponseReceivedEvent; - - /// - /// Start the auth process (Make sure the internal HTTP-Server ist started) - /// - public void DoAuth() - { - string uri = GetUri(); - Process.Start(uri); - } - - private string GetUri() - { - StringBuilder builder = new StringBuilder("https://accounts.spotify.com/authorize/?"); - builder.Append("client_id=" + ClientId); - builder.Append("&response_type=token"); - builder.Append("&redirect_uri=" + RedirectUri); - builder.Append("&state=" + State); - builder.Append("&scope=" + Scope.GetStringAttribute(" ")); - builder.Append("&show_dialog=" + ShowDialog); - return builder.ToString(); - } - - /// - /// Start the internal HTTP-Server - /// - public void StartHttpServer(int port = 80) - { - _httpServer = new SimpleHttpServer(port, AuthType.Implicit); - _httpServer.OnAuth += HttpServerOnOnAuth; - - _httpThread = new Thread(_httpServer.Listen); - _httpThread.Start(); - } - - private void HttpServerOnOnAuth(AuthEventArgs e) - { - OnResponseReceivedEvent?.Invoke(new Token - { - AccessToken = e.Code, - TokenType = e.TokenType, - ExpiresIn = e.ExpiresIn, - Error = e.Error - }, e.State); - } - - /// - /// This will stop the internal HTTP-Server (Should be called after you got the Token) - /// - public void StopHttpServer() - { - _httpServer.Dispose(); - _httpServer = null; - } - } -} \ No newline at end of file diff --git a/SpotifyAPI/Web/Auth/WebAPIFactory.cs b/SpotifyAPI/Web/Auth/WebAPIFactory.cs deleted file mode 100644 index 94e21980..00000000 --- a/SpotifyAPI/Web/Auth/WebAPIFactory.cs +++ /dev/null @@ -1,107 +0,0 @@ -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; - private readonly ProxyConfig _proxyConfig; - private readonly string _xss; - - public WebAPIFactory(string redirectUrl, int listeningPort, string clientId, Scope scope) - : this(redirectUrl, listeningPort, clientId, scope, null) - { - } - - public WebAPIFactory(string redirectUrl, int listeningPort, string clientId, Scope scope, ProxyConfig proxyConfig) - : this(redirectUrl, listeningPort, clientId, scope, TimeSpan.FromSeconds(20), proxyConfig) - { - } - - public WebAPIFactory(string redirectUrl, int listeningPort, string clientId, Scope scope, TimeSpan timeout, string xss = "XSS") - : this(redirectUrl, listeningPort, clientId, scope, timeout, null, xss) - { - } - - public WebAPIFactory(string redirectUrl, int listeningPort, string clientId, Scope scope, TimeSpan timeout, ProxyConfig proxyConfig, string xss = "XSS") - { - _redirectUrl = redirectUrl; - _listeningPort = listeningPort; - _clientId = clientId; - _scope = scope; - _timeout = timeout; - _proxyConfig = proxyConfig; - _xss = xss; - } - - public Task GetWebApi(bool showDialog = false) - { - var authentication = new ImplicitGrantAuth - { - RedirectUri = new UriBuilder(_redirectUrl) { Port = _listeningPort }.Uri.OriginalString.TrimEnd('/'), - ClientId = _clientId, - Scope = _scope, - ShowDialog = showDialog, - 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 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(_proxyConfig) - { - UseAuth = true, - AccessToken = token.AccessToken, - TokenType = token.TokenType - }; - - return spotifyWebApi; - } - } - - [Serializable] - public class SpotifyWebApiException : Exception - { - public SpotifyWebApiException(string message) : base(message) - { } - } -} diff --git a/SpotifyAPI/Web/SimpleHttpServer.cs b/SpotifyAPI/Web/SimpleHttpServer.cs deleted file mode 100644 index 7c1563af..00000000 --- a/SpotifyAPI/Web/SimpleHttpServer.cs +++ /dev/null @@ -1,370 +0,0 @@ -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 -// and also with no warranty of any kind, please enjoy. - David Jeske. - -// simple HTTP explanation -// http://www.jmarshall.com/easy/http/ - -namespace SpotifyAPI.Web -{ - public class HttpProcessor : IDisposable - { - private const int MaxPostSize = 10 * 1024 * 1024; // 10MB - private const int BufSize = 4096; - private readonly HttpServer _srv; - private Stream _inputStream; - private readonly Hashtable _httpHeaders = new Hashtable(); - private string _httpMethod; - public string HttpProtocolVersionstring; - public string HttpUrl; - public StreamWriter OutputStream; - - public HttpProcessor(HttpServer srv) - { - _srv = srv; - } - - private string[] GetIncomingRequest(Stream inputStream) - { - 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(TcpClient socket) - { - // 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()); - - // we probably shouldn't be using a streamwriter for all output from handlers either - OutputStream = new StreamWriter(new BufferedStream(socket.GetStream())); - try - { - var requestLines = GetIncomingRequest(_inputStream); - - ParseRequest(requestLines.First()); - ReadHeaders(requestLines.Skip(1)); - - if (_httpMethod.Equals("GET")) - { - HandleGetRequest(); - } - else if (_httpMethod.Equals("POST")) - { - HandlePostRequest(); - } - } - catch (Exception) - { - WriteFailure(); - } - OutputStream.Flush(); - _inputStream = null; - OutputStream = null; - } - - public void ParseRequest(string request) - { - string[] tokens = request.Split(' '); - if (tokens.Length < 2) - { - throw new Exception("Invalid HTTP request line"); - } - _httpMethod = tokens[0].ToUpper(); - HttpUrl = tokens[1]; - } - - public void ReadHeaders(IEnumerable requestLines) - { - foreach(var line in requestLines) - { - if (string.IsNullOrEmpty(line)) - { - return; - } - - int separator = line.IndexOf(':'); - if (separator == -1) - { - throw new Exception("Invalid HTTP header line: " + line); - } - string name = line.Substring(0, separator); - int pos = separator + 1; - while ((pos < line.Length) && (line[pos] == ' ')) - { - pos++; // strip any spaces - } - - string value = line.Substring(pos, line.Length - pos); - _httpHeaders[name] = value; - } - } - - public void HandleGetRequest() - { - _srv.HandleGetRequest(this); - } - - public void HandlePostRequest() - { - // this post data processing just reads everything into a memory stream. - // this is fine for smallish things, but for large stuff we should really - // hand an input stream to the request processor. However, the input stream - // we hand him needs to let him see the "end of the stream" at this content - // length, because otherwise he won't know when he's seen it all! - - MemoryStream ms = new MemoryStream(); - if (_httpHeaders.ContainsKey("Content-Length")) - { - var contentLen = Convert.ToInt32(_httpHeaders["Content-Length"]); - if (contentLen > MaxPostSize) - { - throw new Exception($"POST Content-Length({contentLen}) too big for this simple server"); - } - byte[] buf = new byte[BufSize]; - int toRead = contentLen; - while (toRead > 0) - { - int numread = _inputStream.Read(buf, 0, Math.Min(BufSize, toRead)); - if (numread == 0) - { - if (toRead == 0) - { - break; - } - throw new Exception("Client disconnected during post"); - } - toRead -= numread; - ms.Write(buf, 0, numread); - } - ms.Seek(0, SeekOrigin.Begin); - } - _srv.HandlePostRequest(this, new StreamReader(ms)); - } - - public void WriteSuccess(string contentType = "text/html") - { - OutputStream.WriteLine("HTTP/1.0 200 OK"); - OutputStream.WriteLine("Content-Type: " + contentType); - OutputStream.WriteLine("Connection: close"); - OutputStream.WriteLine(""); - } - - public void WriteFailure() - { - OutputStream.WriteLine("HTTP/1.0 404 File not found"); - OutputStream.WriteLine("Connection: close"); - OutputStream.WriteLine(""); - } - - public void Dispose() - { - - } - } - - public abstract class HttpServer : IDisposable - { - private TcpListener _listener; - protected int Port; - - protected HttpServer(int port) - { - IsActive = true; - Port = port; - } - - public bool IsActive { get; set; } - - public void Dispose() - { - IsActive = false; - _listener.Stop(); - GC.SuppressFinalize(this); - } - - public void Listen() - { - try - { - _listener = new TcpListener(IPAddress.Any, Port); - _listener.Start(); - - _listener.BeginAcceptTcpClient(AcceptTcpConnection, _listener); - - } - catch (SocketException e) - { - if (e.ErrorCode != 10004) //Ignore 10004, which is thrown when the thread gets terminated - throw; - } - } - - 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); - } - - public class AuthEventArgs - { - //Code can be an AccessToken or an Exchange Code - public string Code { get; set; } - - public string TokenType { get; set; } - public string State { get; set; } - public string Error { get; set; } - public int ExpiresIn { get; set; } - } - - public class SimpleHttpServer : HttpServer - { - private readonly AuthType _type; - - public delegate void AuthEventHandler(AuthEventArgs e); - - public event AuthEventHandler OnAuth; - - public SimpleHttpServer(int port, AuthType type) : base(port) - { - _type = type; - } - - public override void HandleGetRequest(HttpProcessor p) - { - p.WriteSuccess(); - if (p.HttpUrl == "/favicon.ico") - return; - - Thread t; - if (_type == AuthType.Authorization) - { - string url = p.HttpUrl; - url = url.Substring(2, url.Length - 2); - NameValueCollection col = HttpUtility.ParseQueryString(url); - if (col.Keys.Get(0) != "code") - { - p.OutputStream.WriteLine("

Spotify Auth canceled!

"); - t = new Thread(o => - { - OnAuth?.Invoke(new AuthEventArgs() - { - State = col.Get(1), - Error = col.Get(0), - }); - }); - } - else - { - p.OutputStream.WriteLine("

Spotify Auth successful!

"); - t = new Thread(o => - { - OnAuth?.Invoke(new AuthEventArgs() - { - Code = col.Get(0), - State = col.Get(1) - }); - }); - } - } - else - { - if (p.HttpUrl == "/") - { - p.OutputStream.WriteLine("" + - "" + - "

Spotify Auth successful!
Please copy the URL and paste it into the application

"); - p.OutputStream.Flush(); - p.OutputStream.Close(); - return; - } - string url = p.HttpUrl; - url = url.Substring(2, url.Length - 2); - NameValueCollection col = HttpUtility.ParseQueryString(url); - if (col.Keys.Get(0) != "access_token") - { - p.OutputStream.WriteLine("

Spotify Auth canceled!

"); - t = new Thread(o => - { - OnAuth?.Invoke(new AuthEventArgs() - { - Error = col.Get(0), - State = col.Get(1) - }); - }); - } - else - { - p.OutputStream.WriteLine("

Spotify Auth successful!

"); - t = new Thread(o => - { - OnAuth?.Invoke(new AuthEventArgs() - { - Code = col.Get(0), - TokenType = col.Get(1), - ExpiresIn = Convert.ToInt32(col.Get(2)), - State = col.Get(3) - }); - }); - p.OutputStream.Flush(); - p.OutputStream.Close(); - } - } - - - t.Start(); - } - - public override void HandlePostRequest(HttpProcessor p, StreamReader inputData) - { - p.WriteSuccess(); - } - } - - public enum AuthType - { - Implicit, - Authorization - } -} \ No newline at end of file diff --git a/SpotifyAPI/app.config b/SpotifyAPI/app.config deleted file mode 100644 index 49cc43e1..00000000 --- a/SpotifyAPI/app.config +++ /dev/null @@ -1,3 +0,0 @@ - - - \ No newline at end of file diff --git a/SpotifyAPI/packages.config b/SpotifyAPI/packages.config deleted file mode 100644 index ee51c237..00000000 --- a/SpotifyAPI/packages.config +++ /dev/null @@ -1,4 +0,0 @@ - - - - \ No newline at end of file diff --git a/SpotifyAPI/publish.cmd b/SpotifyAPI/publish.cmd deleted file mode 100644 index 4af64632..00000000 --- a/SpotifyAPI/publish.cmd +++ /dev/null @@ -1,12 +0,0 @@ -if "%APPVEYOR_REPO_TAG%" == "true" ( -echo Publishing... - -cd ./SpotifyAPI - -nuget pack ./SpotifyAPI.nuspec -Version %APPVEYOR_REPO_TAG_NAME% -nuget push ./SpotifyAPI-NET.%APPVEYOR_REPO_TAG_NAME%.nupkg -ApiKey %NUGET_TOKEN% -NonInteractive -Source https://www.nuget.org/api/v2/package -cd ../ - -) else ( - echo Skipping Publishing -)