diff --git a/.gitignore b/.gitignore
index 1bc915c..c343de1 100644
--- a/.gitignore
+++ b/.gitignore
@@ -5,7 +5,7 @@
*.suo
*.user
*.sln.docstates
-
+.vs
# Build results
[Dd]ebug/
diff --git a/.vs/VSWorkspaceState.json b/.vs/VSWorkspaceState.json
new file mode 100644
index 0000000..70ebd3c
--- /dev/null
+++ b/.vs/VSWorkspaceState.json
@@ -0,0 +1,10 @@
+{
+ "ExpandedNodes": [
+ "",
+ "\\Downtify",
+ "\\Downtify\\GUI",
+ "\\Downtify\\Libs"
+ ],
+ "SelectedNode": "\\Downtify\\SpotifyDownloader.cs",
+ "PreviewInSolutionExplorer": false
+}
\ No newline at end of file
diff --git a/.vs/slnx.sqlite b/.vs/slnx.sqlite
new file mode 100644
index 0000000..67ac170
Binary files /dev/null and b/.vs/slnx.sqlite differ
diff --git a/Downtify.sln b/Downtify.sln
index e26a7ed..ddee35f 100644
--- a/Downtify.sln
+++ b/Downtify.sln
@@ -1,10 +1,12 @@
Microsoft Visual Studio Solution File, Format Version 12.00
-# Visual Studio 2013
-VisualStudioVersion = 12.0.21005.1
+# Visual Studio 15
+VisualStudioVersion = 15.0.27703.2042
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Downtify", "Downtify\Downtify.csproj", "{311EACED-AFD3-42BE-B5BE-E5D046A69AAF}"
EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{E9280406-7BB6-4F65-BEBF-1845DA46A31D}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -19,4 +21,7 @@ Global
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {FA6814DA-34A1-4F2A-8FC9-E5A97FF3352D}
+ EndGlobalSection
EndGlobal
diff --git a/Downtify/Downtify.csproj b/Downtify/Downtify.csproj
index cd9319c..a675240 100644
--- a/Downtify/Downtify.csproj
+++ b/Downtify/Downtify.csproj
@@ -9,7 +9,7 @@
Properties
Downtify
Downtify
- v4.5
+ v4.6.1
512
false
@@ -57,12 +57,36 @@
+
+ ..\packages\Newtonsoft.Json.11.0.1\lib\net45\Newtonsoft.Json.dll
+
False
Libs\ohLibSpotify.dll
+
+ ..\packages\taglib.2.1.0.0\lib\policy.2.0.taglib-sharp.dll
+
+
+ ..\packages\SpotifyWebApi-Core.0.0.10\lib\netstandard2.0\SpotifyWebApi.dll
+
+
+ ..\packages\System.Net.Http.4.3.3\lib\net46\System.Net.Http.dll
+
+
+ ..\packages\System.Security.Cryptography.Algorithms.4.3.0\lib\net461\System.Security.Cryptography.Algorithms.dll
+
+
+ ..\packages\System.Security.Cryptography.Encoding.4.3.0\lib\net46\System.Security.Cryptography.Encoding.dll
+
+
+ ..\packages\System.Security.Cryptography.Primitives.4.3.0\lib\net46\System.Security.Cryptography.Primitives.dll
+
+
+ ..\packages\System.Security.Cryptography.X509Certificates.4.3.0\lib\net461\System.Security.Cryptography.X509Certificates.dll
+
@@ -71,6 +95,9 @@
+
+ ..\packages\taglib.2.1.0.0\lib\taglib-sharp.dll
+
False
Libs\UltraID3Lib.dll
@@ -112,7 +139,10 @@
Resources.resx
True
-
+
+ Designer
+
+
SettingsSingleFileGenerator
Settings.Designer.cs
@@ -165,6 +195,7 @@
PreserveNewest
+ Designer
diff --git a/Downtify/GUI/frmMain.Designer.cs b/Downtify/GUI/frmMain.Designer.cs
index c69bc2d..d99c5a8 100644
--- a/Downtify/GUI/frmMain.Designer.cs
+++ b/Downtify/GUI/frmMain.Designer.cs
@@ -1,11 +1,16 @@
-namespace Downtify.GUI
+using System;
+using System.ComponentModel;
+using System.Drawing;
+using System.Windows.Forms;
+
+namespace Downtify.GUI
{
partial class frmMain
{
///
/// Erforderliche Designervariable.
///
- private System.ComponentModel.IContainer components = null;
+ private IContainer components = null;
///
/// Verwendete Ressourcen bereinigen.
@@ -32,6 +37,18 @@ private void InitializeComponent()
this.buttonDownload = new System.Windows.Forms.Button();
this.textBoxLink = new Downtify.GUI.PlaceholderTextBox();
this.progressBar1 = new System.Windows.Forms.ProgressBar();
+ this.pictureBoxAlbumCover = new System.Windows.Forms.PictureBox();
+ this.labelTite = new System.Windows.Forms.Label();
+ this.labelAlbum = new System.Windows.Forms.Label();
+ this.labelArtist = new System.Windows.Forms.Label();
+ this.groupBox1 = new System.Windows.Forms.GroupBox();
+ this.statusStripMain = new System.Windows.Forms.StatusStrip();
+ this.toolStripStatusLabelMain = new System.Windows.Forms.ToolStripStatusLabel();
+ this.labelDurationText = new System.Windows.Forms.Label();
+ this.labelDurationTextValue = new System.Windows.Forms.Label();
+ ((System.ComponentModel.ISupportInitialize)(this.pictureBoxAlbumCover)).BeginInit();
+ this.groupBox1.SuspendLayout();
+ this.statusStripMain.SuspendLayout();
this.SuspendLayout();
//
// listBoxTracks
@@ -41,9 +58,10 @@ private void InitializeComponent()
this.listBoxTracks.ItemHeight = 16;
this.listBoxTracks.Location = new System.Drawing.Point(11, 40);
this.listBoxTracks.Name = "listBoxTracks";
- this.listBoxTracks.SelectionMode = System.Windows.Forms.SelectionMode.MultiExtended;
+ this.listBoxTracks.SelectionMode = System.Windows.Forms.SelectionMode.MultiSimple;
this.listBoxTracks.Size = new System.Drawing.Size(494, 196);
this.listBoxTracks.TabIndex = 0;
+ this.listBoxTracks.SelectedIndexChanged += new System.EventHandler(this.listBoxTracks_SelectedIndexChanged);
this.listBoxTracks.KeyDown += new System.Windows.Forms.KeyEventHandler(this.listBoxTracks_KeyDown);
//
// buttonDownload
@@ -61,11 +79,14 @@ private void InitializeComponent()
//
this.textBoxLink.Font = new System.Drawing.Font("Arial", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.textBoxLink.Location = new System.Drawing.Point(12, 12);
+ this.textBoxLink.Multiline = true;
this.textBoxLink.Name = "textBoxLink";
this.textBoxLink.Placeholder = "Put your track or playlist link here";
- this.textBoxLink.Size = new System.Drawing.Size(493, 22);
+ this.textBoxLink.Size = new System.Drawing.Size(493, 20);
this.textBoxLink.TabIndex = 1;
- this.textBoxLink.TextChanged += new System.EventHandler(this.textBoxLink_TextChanged);
+ this.textBoxLink.Text = "https://open.spotify.com/track/39dqDqHv63oMoogN6sgITQ\r\nhttps://open.spotify.com/t" +
+ "rack/5treNJZ0gCdEO3EcWp9aDu";
+ this.textBoxLink.KeyPress += new System.Windows.Forms.KeyPressEventHandler(this.textBoxLink_KeyPress);
//
// progressBar1
//
@@ -74,11 +95,106 @@ private void InitializeComponent()
this.progressBar1.Size = new System.Drawing.Size(381, 23);
this.progressBar1.TabIndex = 3;
//
+ // pictureBoxAlbumCover
+ //
+ this.pictureBoxAlbumCover.Anchor = System.Windows.Forms.AnchorStyles.None;
+ this.pictureBoxAlbumCover.BackColor = System.Drawing.SystemColors.ButtonShadow;
+ this.pictureBoxAlbumCover.Location = new System.Drawing.Point(39, 19);
+ this.pictureBoxAlbumCover.Name = "pictureBoxAlbumCover";
+ this.pictureBoxAlbumCover.Size = new System.Drawing.Size(150, 150);
+ this.pictureBoxAlbumCover.SizeMode = System.Windows.Forms.PictureBoxSizeMode.StretchImage;
+ this.pictureBoxAlbumCover.TabIndex = 4;
+ this.pictureBoxAlbumCover.TabStop = false;
+ //
+ // labelTite
+ //
+ this.labelTite.AutoEllipsis = true;
+ this.labelTite.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(177)));
+ this.labelTite.Location = new System.Drawing.Point(5, 179);
+ this.labelTite.Name = "labelTite";
+ this.labelTite.Padding = new System.Windows.Forms.Padding(10, 0, 10, 0);
+ this.labelTite.Size = new System.Drawing.Size(221, 15);
+ this.labelTite.TabIndex = 6;
+ this.labelTite.Text = "Title";
+ this.labelTite.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
+ //
+ // labelAlbum
+ //
+ this.labelAlbum.AutoEllipsis = true;
+ this.labelAlbum.Location = new System.Drawing.Point(3, 205);
+ this.labelAlbum.Name = "labelAlbum";
+ this.labelAlbum.Padding = new System.Windows.Forms.Padding(10, 0, 10, 0);
+ this.labelAlbum.Size = new System.Drawing.Size(223, 19);
+ this.labelAlbum.TabIndex = 7;
+ this.labelAlbum.Text = "Album";
+ this.labelAlbum.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
+ //
+ // labelArtist
+ //
+ this.labelArtist.AutoEllipsis = true;
+ this.labelArtist.Location = new System.Drawing.Point(2, 229);
+ this.labelArtist.Name = "labelArtist";
+ this.labelArtist.Padding = new System.Windows.Forms.Padding(10, 0, 10, 0);
+ this.labelArtist.Size = new System.Drawing.Size(224, 20);
+ this.labelArtist.TabIndex = 7;
+ this.labelArtist.Text = "Artist";
+ this.labelArtist.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
+ //
+ // groupBox1
+ //
+ this.groupBox1.Controls.Add(this.pictureBoxAlbumCover);
+ this.groupBox1.Controls.Add(this.labelArtist);
+ this.groupBox1.Controls.Add(this.labelTite);
+ this.groupBox1.Controls.Add(this.labelAlbum);
+ this.groupBox1.Location = new System.Drawing.Point(524, 12);
+ this.groupBox1.Name = "groupBox1";
+ this.groupBox1.Size = new System.Drawing.Size(228, 252);
+ this.groupBox1.TabIndex = 8;
+ this.groupBox1.TabStop = false;
+ this.groupBox1.Text = "Selected Track";
+ //
+ // statusStripMain
+ //
+ this.statusStripMain.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
+ this.toolStripStatusLabelMain});
+ this.statusStripMain.Location = new System.Drawing.Point(0, 321);
+ this.statusStripMain.Name = "statusStripMain";
+ this.statusStripMain.Size = new System.Drawing.Size(759, 22);
+ this.statusStripMain.TabIndex = 9;
+ this.statusStripMain.Text = "Ready";
+ //
+ // toolStripStatusLabelMain
+ //
+ this.toolStripStatusLabelMain.Name = "toolStripStatusLabelMain";
+ this.toolStripStatusLabelMain.Size = new System.Drawing.Size(0, 17);
+ //
+ // labelDurationText
+ //
+ this.labelDurationText.AutoSize = true;
+ this.labelDurationText.Location = new System.Drawing.Point(12, 285);
+ this.labelDurationText.Name = "labelDurationText";
+ this.labelDurationText.Size = new System.Drawing.Size(128, 13);
+ this.labelDurationText.TabIndex = 10;
+ this.labelDurationText.Text = "Selected Songs Duration:";
+ //
+ // labelDurationTextValue
+ //
+ this.labelDurationTextValue.AutoSize = true;
+ this.labelDurationTextValue.Location = new System.Drawing.Point(146, 285);
+ this.labelDurationTextValue.Name = "labelDurationTextValue";
+ this.labelDurationTextValue.Size = new System.Drawing.Size(49, 13);
+ this.labelDurationTextValue.TabIndex = 10;
+ this.labelDurationTextValue.Text = "00:00:00";
+ //
// frmMain
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
- this.ClientSize = new System.Drawing.Size(517, 275);
+ this.ClientSize = new System.Drawing.Size(759, 343);
+ this.Controls.Add(this.labelDurationTextValue);
+ this.Controls.Add(this.labelDurationText);
+ this.Controls.Add(this.statusStripMain);
+ this.Controls.Add(this.groupBox1);
this.Controls.Add(this.progressBar1);
this.Controls.Add(this.buttonDownload);
this.Controls.Add(this.textBoxLink);
@@ -90,6 +206,10 @@ private void InitializeComponent()
this.Text = "Downtify";
this.Load += new System.EventHandler(this.frmMain_Load);
this.Shown += new System.EventHandler(this.frmMain_Shown);
+ ((System.ComponentModel.ISupportInitialize)(this.pictureBoxAlbumCover)).EndInit();
+ this.groupBox1.ResumeLayout(false);
+ this.statusStripMain.ResumeLayout(false);
+ this.statusStripMain.PerformLayout();
this.ResumeLayout(false);
this.PerformLayout();
@@ -97,10 +217,19 @@ private void InitializeComponent()
#endregion
- private System.Windows.Forms.ListBox listBoxTracks;
- private Downtify.GUI.PlaceholderTextBox textBoxLink;
- private System.Windows.Forms.Button buttonDownload;
- private System.Windows.Forms.ProgressBar progressBar1;
+ private ListBox listBoxTracks;
+ private PlaceholderTextBox textBoxLink;
+ private Button buttonDownload;
+ private ProgressBar progressBar1;
+ private PictureBox pictureBoxAlbumCover;
+ private Label labelTite;
+ private Label labelAlbum;
+ private Label labelArtist;
+ private GroupBox groupBox1;
+ private StatusStrip statusStripMain;
+ private ToolStripStatusLabel toolStripStatusLabelMain;
+ private Label labelDurationText;
+ private Label labelDurationTextValue;
}
}
diff --git a/Downtify/GUI/frmMain.cs b/Downtify/GUI/frmMain.cs
index b086c40..8b81d93 100644
--- a/Downtify/GUI/frmMain.cs
+++ b/Downtify/GUI/frmMain.cs
@@ -1,12 +1,26 @@
using System;
+using System.Diagnostics;
+using System.Drawing;
using System.IO;
+using System.Threading;
+using System.Threading.Tasks;
using System.Windows.Forms;
+using SpotifySharp;
+using System.Collections.Generic;
+using System.Linq;
+using System.Net;
namespace Downtify.GUI
{
public partial class frmMain : Form
{
+ public const string NoText = "-";
+ public const string StatusTextReady = "";
+ public const string StatusTextInvalidLink = "Invalid Spotify link";
+ public const string StatusTextUpdatingTrackInfo = "Updating track info...";
+
SpotifyDownloader downloader;
+ private long _totalDuration = 0;
public static XmlConfiguration configuration;
public static LanguageXML lang;
@@ -51,7 +65,7 @@ private void downloader_OnDownloadProgress(int value)
if (value > 100 || value < 0)
return;
- progressBar1.Value = value;
+ progressBar1.Value = value;
});
}
@@ -62,9 +76,11 @@ private void frmMain_Load(object sender, EventArgs e)
private void frmMain_Shown(object sender, EventArgs e)
{
- System.Threading.Thread.Sleep(200);
+ Thread.Sleep(200);
this.Activate();
+ ClearTrackInfo();
+
// very ugly, use config parser (json for example) would be nicer
string username = "", password = "";
configuration.LoadConfigurationFile();
@@ -80,10 +96,10 @@ private void frmMain_Shown(object sender, EventArgs e)
private void TransferConfig()
{
- if(File.Exists("config.txt"))
+ if (File.Exists("config.txt"))
{
string username = "", password = "";
- foreach(var currentLine in File.ReadAllLines("config.txt"))
+ foreach (var currentLine in File.ReadAllLines("config.txt"))
{
var line = currentLine.Trim();
if (line.StartsWith("#"))
@@ -121,53 +137,72 @@ private void EnableControls(bool enable)
((Control)control).Enabled = enable;
}
- private async void textBoxLink_TextChanged(object sender, EventArgs e)
+ private async Task FetchSongsFromUrl(string text)
{
- var link = textBoxLink.Text;
- try
- {
- EnableControls(false);
-
- //Validate pasted URI
- if(link.Length > 0 && !link.ToLower().StartsWith("spotify:"))
- {
- MessageBox.Show(lang.GetString("download/invalid_uri"));
- textBoxLink.Clear();
- return;
- }
- if (link.ToLower().Contains("playlist"))
+ string[] urls = text.Split(new[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries);
+
+ foreach (string url in urls) {
+
+ var link = SpotifyDownloader.SpotifyUrlToUri(url);
+
+ try
{
- var playlist = await downloader.FetchPlaylist(textBoxLink.Text);
- for (int i = 0; i < playlist.NumTracks(); i++)
- listBoxTracks.Items.Add(new TrackItem(playlist.Track(i)));
- textBoxLink.Clear();
+ EnableControls(false);
+
+ //Validate pasted URI
+ // if((link.Length > 0 && !link.ToLower().StartsWith("spotify:")))
+ // {
+ // MessageBox.Show(lang.GetString("download/invalid_uri"));
+ // textBoxLink.Clear();
+ // SetStatusStripLabelText(StatusTextInvalidLink);
+ // return;
+ // }
+
+ SetStatusStripLabelText(StatusTextReady);
+
+ if (link.ToLower().Contains("playlist"))
+ {
+ var playlist = await downloader.FetchPlaylist(link);
+ for (int i = 0; i < playlist.NumTracks(); i++)
+ listBoxTracks.Items.Add(new TrackItem(playlist.Track(i)));
+ int a = playlist.NumTracks();
+ textBoxLink.Clear();
+ }
+ else if (link.ToLower().Contains("track"))
+ {
+ var track = await downloader.FetchTrack(link);
+ listBoxTracks.Items.Add(new TrackItem(track));
+ textBoxLink.Clear();
+ }
+ else if (link.ToLower().Contains("album"))
+ {
+ var album = await downloader.FetchAlbum(link);
+ for (int i = 0; i < album.NumTracks(); i++)
+ listBoxTracks.Items.Add(new TrackItem(album.Track(i)));
+ textBoxLink.Clear();
+ }
+ else
+ {
+ SetStatusStripLabelText(StatusTextInvalidLink);
+ }
}
- else if (link.ToLower().Contains("track"))
+ catch (NullReferenceException)
{
- var track = await downloader.FetchTrack(textBoxLink.Text);
- listBoxTracks.Items.Add(new TrackItem(track));
- textBoxLink.Clear();
+ SetStatusStripLabelText(StatusTextInvalidLink);
}
- else if(link.ToLower().Contains("album"))
+ finally
{
- var album = await downloader.FetchAlbum(textBoxLink.Text);
- for (int i = 0; i < album.NumTracks(); i++)
- listBoxTracks.Items.Add(new TrackItem(album.Track(i)));
- textBoxLink.Clear();
+ EnableControls(true);
}
}
- catch (NullReferenceException)
- {
- }
- finally
- {
- EnableControls(true);
- }
}
private void listBoxTracks_KeyDown(object sender, KeyEventArgs e)
{
+
+ ClearTrackInfo();
+
if (e.KeyCode == Keys.Delete)
{
if (listBoxTracks.SelectedItems.Count == 0)
@@ -201,5 +236,72 @@ private void buttonDownload_Click(object sender, EventArgs e)
EnableControls(false);
downloader.Download(((TrackItem)listBoxTracks.SelectedItems[0]).Track);
}
+
+ private async void listBoxTracks_SelectedIndexChanged(object sender, EventArgs e)
+ {
+ _totalDuration = 0;
+
+ if (listBoxTracks.SelectedItem == null)
+ {
+ UpdateTrackInfo(null, null);
+ UpdateTotalDuration();
+ SetStatusStripLabelText(StatusTextReady);
+ return;
+ }
+
+ Track track = null;
+ for (var i = 0; i < listBoxTracks.SelectedIndices.Count; i++)
+ {
+ track = ((TrackItem)listBoxTracks.SelectedItems[i]).Track;
+ _totalDuration += track.Duration();
+ }
+
+ UpdateTotalDuration();
+
+ if (toolStripStatusLabelMain.Text != StatusTextUpdatingTrackInfo && listBoxTracks.SelectedIndices.Count == 1)
+ {
+ Debug.WriteLine("in listBoxTracks_SelectedIndexChanged");
+ SetStatusStripLabelText(StatusTextUpdatingTrackInfo);
+ var bmp = await downloader.DownloadImage(((TrackItem)listBoxTracks.SelectedItem).Track, 1);
+ UpdateTrackInfo(track, bmp);
+ }
+
+ SetStatusStripLabelText(StatusTextReady);
+
+ }
+
+ private void UpdateTrackInfo(Track track, Bitmap bmp)
+ {
+ pictureBoxAlbumCover.Image = track == null ? null : bmp;
+ labelTite.Text = track == null ? "-" : track.Name();
+ labelAlbum.Text = track == null ? "-" : track.Album().Name();
+ labelArtist.Text = track == null ? "-" : SpotifyDownloader.GetTrackArtistsNames(track);
+ }
+
+ private void ClearTrackInfo()
+ {
+ pictureBoxAlbumCover.Image = null;
+ labelTite.Text = NoText;
+ labelAlbum.Text = NoText;
+ labelArtist.Text = NoText;
+ }
+
+ private void SetStatusStripLabelText(string test)
+ {
+ toolStripStatusLabelMain.Text = test;
+ }
+
+ private async void textBoxLink_KeyPress(object sender, KeyPressEventArgs e)
+ {
+ if (e.KeyChar == (char)Keys.Enter)
+ {
+ await FetchSongsFromUrl(textBoxLink.Text);
+ }
+ }
+
+ private void UpdateTotalDuration()
+ {
+ labelDurationTextValue.Text = TimeSpan.FromMilliseconds(_totalDuration).ToString(@"hh\:mm\:ss");
+ }
}
}
diff --git a/Downtify/GUI/frmMain.resx b/Downtify/GUI/frmMain.resx
index 1af7de1..21e2324 100644
--- a/Downtify/GUI/frmMain.resx
+++ b/Downtify/GUI/frmMain.resx
@@ -117,4 +117,7 @@
System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+ 17, 17
+
\ No newline at end of file
diff --git a/Downtify/LanguageXML.cs b/Downtify/LanguageXML.cs
index 1ff4f13..da17984 100644
--- a/Downtify/LanguageXML.cs
+++ b/Downtify/LanguageXML.cs
@@ -33,7 +33,7 @@ public string GetString(string key)
}
catch (XPathException e)
{
- return "Missing Language String: " + key;
+ return "Missing Language String: " + key + ":" + e.Message;
}
}
diff --git a/Downtify/Properties/Resources.Designer.cs b/Downtify/Properties/Resources.Designer.cs
index 7ca8fa6..7d70af4 100644
--- a/Downtify/Properties/Resources.Designer.cs
+++ b/Downtify/Properties/Resources.Designer.cs
@@ -1,10 +1,10 @@
//------------------------------------------------------------------------------
//
-// Dieser Code wurde von einem Tool generiert.
-// Laufzeitversion:4.0.30319.34011
+// This code was generated by a tool.
+// Runtime Version:4.0.30319.42000
//
-// Änderungen an dieser Datei können falsches Verhalten verursachen und gehen verloren, wenn
-// der Code erneut generiert wird.
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
//
//------------------------------------------------------------------------------
@@ -13,13 +13,13 @@ namespace Downtify.Properties {
///
- /// Eine stark typisierte Ressourcenklasse zum Suchen von lokalisierten Zeichenfolgen usw.
+ /// A strongly-typed resource class, for looking up localized strings, etc.
///
- // Diese Klasse wurde von der StronglyTypedResourceBuilder automatisch generiert
- // -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 /str-Option erneut aus, oder Sie erstellen Ihr VS-Projekt neu.
- [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
+ // This class was auto-generated by the StronglyTypedResourceBuilder
+ // class via a tool like ResGen or Visual Studio.
+ // To add or remove a member, edit your .ResX file then rerun ResGen
+ // with the /str option, or rebuild your VS project.
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
internal class Resources {
@@ -33,7 +33,7 @@ internal Resources() {
}
///
- /// Gibt die zwischengespeicherte ResourceManager-Instanz zurück, die von dieser Klasse verwendet wird.
+ /// Returns the cached ResourceManager instance used by this class.
///
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Resources.ResourceManager ResourceManager {
@@ -47,8 +47,8 @@ internal Resources() {
}
///
- /// Überschreibt die CurrentUICulture-Eigenschaft des aktuellen Threads für alle
- /// Ressourcenzuordnungen, die diese stark typisierte Ressourcenklasse verwenden.
+ /// Overrides the current thread's CurrentUICulture property for all
+ /// resource lookups using this strongly typed resource class.
///
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Globalization.CultureInfo Culture {
diff --git a/Downtify/Properties/Settings.Designer.cs b/Downtify/Properties/Settings.Designer.cs
index ad6bc53..7120a4d 100644
--- a/Downtify/Properties/Settings.Designer.cs
+++ b/Downtify/Properties/Settings.Designer.cs
@@ -1,10 +1,10 @@
//------------------------------------------------------------------------------
//
-// Dieser Code wurde von einem Tool generiert.
-// Laufzeitversion:4.0.30319.34011
+// This code was generated by a tool.
+// Runtime Version:4.0.30319.42000
//
-// Änderungen an dieser Datei können falsches Verhalten verursachen und gehen verloren, wenn
-// der Code erneut generiert wird.
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
//
//------------------------------------------------------------------------------
@@ -12,7 +12,7 @@ namespace Downtify.Properties {
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
- [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "12.0.0.0")]
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "15.7.0.0")]
internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
diff --git a/Downtify/SpotifyDownloader.cs b/Downtify/SpotifyDownloader.cs
index 412c6b0..5e21efc 100644
--- a/Downtify/SpotifyDownloader.cs
+++ b/Downtify/SpotifyDownloader.cs
@@ -1,15 +1,21 @@
-using HundredMilesSoftware.UltraID3Lib;
+using System.Diagnostics;
using SpotifySharp;
using System;
using System.ComponentModel;
using System.Drawing;
using System.IO;
using System.Runtime.InteropServices;
+using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using WaveLib;
using Yeti.Lame;
using Yeti.MMedia.Mp3;
+using SpotifyWebApi;
+using SpotifyWebApi.Model.Enum;
+using SpotifyWebApi.Auth;
+using System.Net;
+using SpotifyWebApi.Model.Auth;
namespace Downtify
{
@@ -19,7 +25,7 @@ public class TrackItem
public TrackItem(Track track)
{
- this.Track = track;
+ Track = track;
}
public override string ToString()
@@ -34,6 +40,82 @@ public enum DownloadType
OVERWRITE
}
+ public class SpotifyWeb
+ {
+
+ ISpotifyWebApi _spotifyWebApi;
+ Token _spotifyWebApiToken;
+ string _clientId;
+ string _clientSecret;
+ AuthParameters _authParameters;
+
+
+ public SpotifyWeb(string clientId, string clientSecret)
+ {
+ this._clientId = clientId;
+ this._clientSecret = clientSecret;
+ }
+
+ private AuthParameters getAuthParameters()
+ {
+ return new AuthParameters
+ {
+ ClientId = _clientId,
+ ClientSecret = _clientSecret,
+ Scopes = Scope.All,
+ };
+ }
+
+ private Token GetISpotifyWebApiToken()
+ {
+ return ClientCredentials.GetToken(_authParameters);
+ }
+
+ private ISpotifyWebApi CreatISpotifyWebApi()
+ {
+ return new SpotifyWebApi.SpotifyWebApi(_spotifyWebApiToken);
+ }
+
+ private void RefreshToken(Boolean refreshOnlyIfExpired)
+ {
+ if (!refreshOnlyIfExpired || (refreshOnlyIfExpired && _spotifyWebApiToken.IsExpired))
+ {
+
+ //=====================
+ // ValidationException("Refresh token was null or empty!") is always thrown, since string.IsNullOrEmpty(oldToken.RefreshToken) is alwyas true.
+ //_spotifyWebApiToken = SpotifyWebApi.Auth.AuthorizationCode.RefreshToken(_authParameters, _spotifyWebApiToken);
+ //_spotifyWebApi = CreatISpotifyWebApi();
+ //=====================
+
+
+ Auth(); // Always create new token until refreshing is fixed
+ }
+
+ }
+
+ public void Auth()
+ {
+ // Autenticate _spotifyWebApi
+ _authParameters = getAuthParameters();
+ _spotifyWebApiToken = GetISpotifyWebApiToken();
+ _spotifyWebApi = CreatISpotifyWebApi();
+ }
+
+ public ISpotifyWebApi GetISpotifyWebApi()
+ {
+ RefreshToken(true);
+ return _spotifyWebApi;
+ }
+
+ // forceRefresh = true means that a brand new token must be created.
+ // forceRefresh = false behaves like GetISpotifyWebApi() (no params)
+ public ISpotifyWebApi GetISpotifyWebApi(Boolean forceRefresh)
+ {
+ RefreshToken(!forceRefresh);
+ return _spotifyWebApi;
+ }
+ }
+
public class SpotifyDownloader : SpotifySessionListener
{
public static string GetTrackArtistsNames(Track track)
@@ -51,59 +133,69 @@ public static string GetTrackFullName(Track track)
// TODO: Make these become "real events"
public delegate void OnLoginHandler(bool isLoggedIn);
+
public event OnLoginHandler OnLoginResult;
public delegate void OnDownloadProgressHandler(int value);
+
public event OnDownloadProgressHandler OnDownloadProgress;
public delegate void OnDownloadCompleteHandler(bool successfully);
+
public event OnDownloadCompleteHandler OnDownloadComplete;
- public bool Loaded { get { return session.User().IsLoaded(); } }
+ public bool Loaded
+ {
+ get { return _session.User().IsLoaded(); }
+ }
+
+ SpotifySession _session;
+ Track _downloadingTrack;
+ Mp3Writer _wr;
+ SynchronizationContext _syncContext;
+ SpotifyWeb _spotifyWeb;
+
+ static string _appPath = AppDomain.CurrentDomain.BaseDirectory;
+ static string _tmpPath = _appPath + "cache\\";
+ static string _downloadPath = _appPath + "download\\";
- SpotifySession session;
- Track downloadingTrack;
- Mp3Writer wr;
- SynchronizationContext syncContext;
- static string appPath = AppDomain.CurrentDomain.BaseDirectory;
- static string tmpPath = appPath + "cache\\";
- static string downloadPath = appPath + "download\\";
+ int _counter;
public SpotifyDownloader()
{
- if (!Directory.Exists(tmpPath))
- Directory.CreateDirectory(tmpPath);
+ if (!Directory.Exists(_tmpPath))
+ Directory.CreateDirectory(_tmpPath);
- if (!Directory.Exists(downloadPath))
- Directory.CreateDirectory(downloadPath);
+ if (!Directory.Exists(_downloadPath))
+ Directory.CreateDirectory(_downloadPath);
var config = new SpotifySessionConfig()
{
ApiVersion = 12,
- CacheLocation = tmpPath,
- SettingsLocation = tmpPath,
+ CacheLocation = _tmpPath,
+ SettingsLocation = _tmpPath,
ApplicationKey = File.ReadAllBytes("spotify_appkey.key"),
UserAgent = "downtify",
Listener = this
};
- syncContext = SynchronizationContext.Current;
- session = SpotifySession.Create(config);
+ _syncContext = SynchronizationContext.Current;
+ _session = SpotifySession.Create(config);
}
private void InvokeProcessEvents()
{
- syncContext.Post(obj =>
+ _syncContext.Post(obj =>
{
int limit = 0;
- session.ProcessEvents(ref limit);
+ _session.ProcessEvents(ref limit);
}, null);
}
public void Login(string username, string password)
{
- session.Login(username, password, true, null);
+ _session.Login(username, password, true, null);
}
public override void NotifyMainThread(SpotifySession session)
@@ -121,6 +213,11 @@ public override async void LoggedIn(SpotifySession session, SpotifyError error)
return;
}
+ // Autenticate _spotifyWeb
+ _spotifyWeb = new SpotifyWeb(GUI.frmMain.configuration.GetConfiguration("clientId"), GUI.frmMain.configuration.GetConfiguration("clientSecret"));
+ _spotifyWeb.Auth();
+
+ // SpotifySharp log in
base.LoggedIn(session, error);
await WaitForBool(session.User().IsLoaded);
session.PreferredBitrate(BitRate._320k);
@@ -139,8 +236,7 @@ public override void CredentialsBlobUpdated(SpotifySession session, string blob)
public override void ConnectionstateUpdated(SpotifySession session)
{
if (session.Connectionstate() == ConnectionState.LoggedIn)
- if (OnLoginResult != null)
- OnLoginResult(true);
+ OnLoginResult?.Invoke(true);
Console.WriteLine(session.Connectionstate().ToString());
base.ConnectionstateUpdated(session);
}
@@ -196,32 +292,30 @@ public override void GetAudioBufferStats(SpotifySession session, out AudioBuffer
base.GetAudioBufferStats(session, out stats);
}
- int counter;
-
- public override int MusicDelivery(SpotifySession session, AudioFormat format, IntPtr frames, int num_frames)
+ public override int MusicDelivery(SpotifySession session, AudioFormat format, IntPtr frames, int numFrames)
{
- if (num_frames == 0)
+ if (numFrames == 0)
return 0;
- var size = num_frames * format.channels * 2;
+ var size = numFrames * format.channels * 2;
var data = new byte[size];
Marshal.Copy(frames, data, 0, size);
- wr.Write(data);
+ _wr.Write(data);
if (OnDownloadProgress != null)
{
- counter++;
- var duration = downloadingTrack.Duration();
+ _counter++;
+ var duration = _downloadingTrack.Duration();
// Todo: Find out how to calculate this correctly,
// so far 46.4 is used to calculate the process
// but there should be a way to calculate this
// with the given variables
- var process = (int)Math.Round((double)100 / duration * (46.4 * counter), 0);
+ var process = (int)Math.Round((double)100 / duration * (46.4 * _counter), 0);
OnDownloadProgress(process);
}
- return num_frames;
+ return numFrames;
// return base.MusicDelivery(session, format, frames, num_frames);
}
@@ -232,43 +326,55 @@ public override void StreamingError(SpotifySession session, SpotifyError error)
public override void PlayTokenLost(SpotifySession session)
{
- System.Windows.Forms.MessageBox.Show(Downtify.GUI.frmMain.lang.GetString("error/connection_lost"));
+ System.Windows.Forms.MessageBox.Show(GUI.frmMain.lang.GetString("error/connection_lost"));
base.PlayTokenLost(session);
}
public override async void EndOfTrack(SpotifySession session)
{
session.PlayerPlay(false);
- wr.Close();
+ _wr.Close();
// Move File
- var dir = downloadPath + escape(downloadingTrack.Album().Name()) + "\\";
+ var dir = _downloadPath + escape(GetTrackArtistsNames(_downloadingTrack)) + "\\";
if (!Directory.Exists(dir))
Directory.CreateDirectory(dir);
- var fileName = dir + escape(GetTrackFullName(downloadingTrack)) + ".mp3";
- if(GetDownloadType() == DownloadType.OVERWRITE && File.Exists(fileName))
+
+ var fileName = getUpdatedTrackName(_downloadingTrack); ;
+ if (GetDownloadType() == DownloadType.OVERWRITE && File.Exists(fileName))
File.Delete(fileName);
File.Move("downloading", fileName);
- // Tag
- var u = new UltraID3();
- u.Read(fileName);
- u.Artist = GetTrackArtistsNames(downloadingTrack);
- u.Title = downloadingTrack.Name();
- u.Album = downloadingTrack.Album().Name();
- u.TrackNum = (short)downloadingTrack.Index();
-
- var imageID = downloadingTrack.Album().Cover(ImageSize.Large);
- var image = SpotifySharp.Image.Create(session, imageID);
- await WaitForBool(image.IsLoaded);
-
- var tc = TypeDescriptor.GetConverter(typeof(Bitmap));
- var bmp = (Bitmap)tc.ConvertFrom(image.Data());
-
- var pictureFrame = new ID3v23PictureFrame(bmp, PictureTypes.CoverFront, "image", TextEncodingTypes.ISO88591);
- u.ID3v2Tag.Frames.Add(pictureFrame);
+ // Tags
+ var file = TagLib.File.Create(fileName);
+ file.Tag.Title = _downloadingTrack.Name();
+ file.Tag.Performers = new[] { GetTrackArtistsNames(_downloadingTrack) };
+ file.Tag.Disc = (uint)_downloadingTrack.Disc();
+ file.Tag.Year = (uint)_downloadingTrack.Album().Year();
+ file.Tag.Track = (uint)_downloadingTrack.Index();
+ file.Tag.Album = _downloadingTrack.Album().Name();
+ file.Tag.Comment = Link.CreateFromTrack(_downloadingTrack, 0).AsString();
+
+ // Download img
+ Bitmap bmp = await DownloadImage(_downloadingTrack, 0);
+
+
+ // Set img
+ var pic = new TagLib.Picture();
+ pic.Type = TagLib.PictureType.FrontCover;
+ pic.Description = "Cover";
+ pic.MimeType = System.Net.Mime.MediaTypeNames.Image.Jpeg;
+ var ms = new MemoryStream();
+ if (bmp != null)
+ {
+ bmp.Save(ms, System.Drawing.Imaging.ImageFormat.Jpeg);
+ ms.Position = 0;
+ pic.Data = TagLib.ByteVector.FromStream(ms);
+ file.Tag.Pictures = new TagLib.IPicture[] { pic };
+ }
- u.Write();
+ // Save
+ file.Save();
base.EndOfTrack(session);
@@ -284,7 +390,9 @@ private async Task WaitForBool(Func action)
{
await Task.Factory.StartNew(() =>
{
- while (!action()) { };
+ while (!action())
+ {
+ }
});
return true;
}
@@ -292,45 +400,45 @@ await Task.Factory.StartNew(() =>
public async Task FetchPlaylist(string linkStr)
{
var link = Link.CreateFromString(linkStr);
- var playlist = Playlist.Create(session, link);
+ var playlist = Playlist.Create(_session, link);
await WaitForBool(playlist.IsLoaded);
for (int i = 0; i < playlist.NumTracks(); i++)
await WaitForBool(playlist.Track(i).IsLoaded);
+
return playlist;
}
public async Task FetchAlbum(string linkStr)
{
var link = Link.CreateFromString(linkStr);
- var album = AlbumBrowse.Create(session, link.AsAlbum(), AlbumBrowseCallBack, session.UserData);
+ var album = AlbumBrowse.Create(_session, link.AsAlbum(), AlbumBrowseCallBack, _session.UserData);
await WaitForBool(album.IsLoaded);
for (int i = 0; i < album.NumTracks(); i++)
await WaitForBool(album.Track(i).IsLoaded);
return album;
}
-
+
public async Task