diff --git a/Form1.Designer.cs b/Form1.Designer.cs index dfa060b..11e948c 100644 --- a/Form1.Designer.cs +++ b/Form1.Designer.cs @@ -39,7 +39,10 @@ private void InitializeComponent() this.btn_selectpath = new System.Windows.Forms.Button(); this.label1 = new System.Windows.Forms.Label(); this.btn_opensearch = new System.Windows.Forms.Button(); + this.num_threads = new System.Windows.Forms.NumericUpDown(); + this.label2 = new System.Windows.Forms.Label(); this.box_quality.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.num_threads)).BeginInit(); this.SuspendLayout(); // // txt_urllist @@ -144,11 +147,45 @@ private void InitializeComponent() this.btn_opensearch.UseVisualStyleBackColor = true; this.btn_opensearch.Click += new System.EventHandler(this.btn_opensearch_Click); // + // num_threads + // + this.num_threads.Location = new System.Drawing.Point(449, 252); + this.num_threads.Maximum = new decimal(new int[] { + 1000, + 0, + 0, + 0}); + this.num_threads.Minimum = new decimal(new int[] { + 1, + 0, + 0, + 0}); + this.num_threads.Name = "num_threads"; + this.num_threads.Size = new System.Drawing.Size(59, 20); + this.num_threads.TabIndex = 8; + this.num_threads.Value = new decimal(new int[] { + 1, + 0, + 0, + 0}); + this.num_threads.ValueChanged += new System.EventHandler(this.num_threads_ValueChanged); + // + // label2 + // + this.label2.AutoSize = true; + this.label2.Location = new System.Drawing.Point(394, 254); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(49, 13); + this.label2.TabIndex = 9; + this.label2.Text = "Threads:"; + // // Form1 // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.ClientSize = new System.Drawing.Size(757, 304); + this.Controls.Add(this.label2); + this.Controls.Add(this.num_threads); this.Controls.Add(this.btn_opensearch); this.Controls.Add(this.label1); this.Controls.Add(this.btn_selectpath); @@ -162,6 +199,7 @@ private void InitializeComponent() this.Text = "KhinsiderDownloader"; this.box_quality.ResumeLayout(false); this.box_quality.PerformLayout(); + ((System.ComponentModel.ISupportInitialize)(this.num_threads)).EndInit(); this.ResumeLayout(false); this.PerformLayout(); @@ -179,6 +217,8 @@ private void InitializeComponent() private System.Windows.Forms.Button btn_selectpath; private System.Windows.Forms.Label label1; private System.Windows.Forms.Button btn_opensearch; + private System.Windows.Forms.NumericUpDown num_threads; + private System.Windows.Forms.Label label2; } } diff --git a/Form1.cs b/Form1.cs index 90bf2c3..013c3a0 100644 --- a/Form1.cs +++ b/Form1.cs @@ -5,15 +5,16 @@ using System.Linq; using System.Net; using System.Text; +using System.Threading; using System.Threading.Tasks; using System.Windows.Forms; using HtmlAgilityPack; using Newtonsoft.Json; using Newtonsoft.Json.Linq; +using HtmlDocument = HtmlAgilityPack.HtmlDocument; namespace KhinsiderDownloader { - public partial class Form1 : Form { @@ -41,7 +42,20 @@ private void checkUpdates() Process.Start("https://github.com/weespin/KhinsiderDownloader/releases"); } } + else if (newVersion < currentVersion) + { + if (label1.InvokeRequired) + { + label1.Invoke(new Action(() => + { + label1.Text = "BETA " + label1.Text; + })); + + } + } + } + public Form1() { InitializeComponent(); @@ -49,6 +63,7 @@ public Form1() lbl_path.Text = Downloader.sDownloadPath; LoadConfig(); Task.Run(() => { checkUpdates(); }); + num_threads.Value = 2; } public void Log(string textIn) @@ -70,7 +85,7 @@ public void LoadConfig() { var configLines = File.ReadAllLines("khinsiderdl.config"); lbl_path.Text = Downloader.sDownloadPath = configLines[0]; - + Downloader.eQuality = bool.Parse(configLines[1]) ? Downloader.EDownloadQuality.QUALITY_MP3_ONLY : Downloader.EDownloadQuality.QUALITY_BEST_ONLY; @@ -82,6 +97,8 @@ public void LoadConfig() { radio_betterquality.Checked = true; } + + Downloader.g_parralelopt.MaxDegreeOfParallelism = Int32.Parse(configLines[2]); } catch (Exception) { @@ -91,16 +108,17 @@ public void LoadConfig() void SaveConfig() { - string[] configLines = new string[2]; + string[] configLines = new string[3]; configLines[0] = Downloader.sDownloadPath; configLines[1] = (Downloader.eQuality == Downloader.EDownloadQuality.QUALITY_MP3_ONLY).ToString(); + configLines[2] = Downloader.g_parralelopt.MaxDegreeOfParallelism.ToString(); File.WriteAllLines("khinsiderdl.config", configLines); } private void button1_Click(object sender, EventArgs e) { btn_download.Enabled = false; - List urls = txt_urllist.Text.Split('\n').ToList(); + List urls = txt_urllist.Text.Split(new string[] {Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries).ToList(); Task.Run(() => { foreach (var url in urls) @@ -153,11 +171,19 @@ private void btn_opensearch_Click(object sender, EventArgs e) searchForm.linkbox = txt_urllist ; searchForm.Show(); } + + private void num_threads_ValueChanged(object sender, EventArgs e) + { + Downloader.g_parralelopt.MaxDegreeOfParallelism = (int)num_threads.Value; + SaveConfig(); + } } static class Downloader { + public static ParallelOptions g_parralelopt = new ParallelOptions() { MaxDegreeOfParallelism = Environment.ProcessorCount }; + public enum EDownloadQuality : byte { QUALITY_MP3_ONLY, @@ -189,7 +215,8 @@ public static void DownloadAlbum(string sUrl) string albumName = "error"; if (albumNameNode != null) { - albumName = albumNameNode.InnerText; + albumName = string.Join("_", albumNameNode.InnerText.Split(Path.GetInvalidFileNameChars())).Trim(); + //albumName = albumNameNode.InnerText.Trim(); } else { @@ -224,14 +251,47 @@ public static void DownloadAlbum(string sUrl) } //Thread.Sleep(1); - + List currentTasks = new List(); - - songNodes.AsParallel().ForAll(song => + ThreadPool.SetMinThreads(g_parralelopt.MaxDegreeOfParallelism, g_parralelopt.MaxDegreeOfParallelism); + System.Net.ServicePointManager.DefaultConnectionLimit = Int32.MaxValue; + //foreach (var song in songNodes) + //{ + Parallel.ForEach(songNodes, g_parralelopt,song=> { var songPageURL = "https://downloads.khinsider.com" + song.ChildNodes[0].Attributes["href"].Value; - - var songPageDocument = webContext.Load(songPageURL); + HtmlDocument songPageDocument = new HtmlDocument(); + try + { + HttpWebRequest httpWebRequest = (HttpWebRequest)WebRequest.Create(songPageURL); + httpWebRequest.KeepAlive = false; + httpWebRequest.Timeout = 30 * 1000; //TCP timeout + using (HttpWebResponse httpWebResponse = (HttpWebResponse)httpWebRequest.GetResponse()) + { + if (httpWebResponse.StatusCode == HttpStatusCode.OK) + { + using (Stream responseStream = httpWebResponse.GetResponseStream()) + { + using (StreamReader reader = new StreamReader(responseStream)) + { + var htmlstring = reader.ReadToEnd(); + + songPageDocument.LoadHtml(htmlstring); + } + } + + } + } + } + catch (Exception e) + { + string message = $"Failed to parse {songPageURL} ({e.Message})"; + Program.MainForm.Log(message); +#if DEBUG + Debug.WriteLine(message); +#endif + return; + } var downloadLinkNodes = songPageDocument.DocumentNode.SelectNodes("//span[@class='songDownloadLink']"); //[1].ParentElement.GetAttribute("href"); foreach (var dlsongentry in downloadLinkNodes) @@ -243,18 +303,35 @@ public static void DownloadAlbum(string sUrl) downloadClient.Proxy = null; var name = WebUtility.UrlDecode(songFileURL.Substring(songFileURL.LastIndexOf("/") + 1)); Program.MainForm.Log($"Downloading {name}..."); - Task currentTask = downloadClient.DownloadFileTaskAsync(new Uri(songFileURL), - Downloader.sDownloadPath + "\\" + - string.Join("_", albumName.Split(Path.GetInvalidFileNameChars())) + "\\" + - string.Join("_", name.Split(Path.GetInvalidFileNameChars()))); + + string filename = Downloader.sDownloadPath + "\\" + + albumName + "\\" + + string.Join("_", name.Split(Path.GetInvalidFileNameChars())); + + try + { + Task currentTask = downloadClient.DownloadFileTaskAsync(new Uri(songFileURL), filename); currentTasks.Add(currentTask); currentTask.ContinueWith(( task => { Program.MainForm.Log($"{name} has been downloaded!"); })); + } + catch (Exception e) + { + string message = $"Failed to download {songFileURL} to {filename} ({e.Message})"; + Program.MainForm.Log(message); +#if DEBUG + Debug.WriteLine(message); +#endif + } + } } + //}// for foreach }); Task.WaitAll(currentTasks.ToArray()); Program.MainForm.Log($"Finished downloading {albumName}!"); } + + } } \ No newline at end of file diff --git a/Properties/AssemblyInfo.cs b/Properties/AssemblyInfo.cs index 294c01d..1a71f78 100644 --- a/Properties/AssemblyInfo.cs +++ b/Properties/AssemblyInfo.cs @@ -32,5 +32,5 @@ // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("2.1.0.0")] -[assembly: AssemblyFileVersion("2.1.0.0")] +[assembly: AssemblyVersion("2.2.0.0")] +[assembly: AssemblyFileVersion("2.2.0.0")] diff --git a/SearchForm.cs b/SearchForm.cs index 9986578..b9bd356 100644 --- a/SearchForm.cs +++ b/SearchForm.cs @@ -12,6 +12,8 @@ namespace KhinsiderDownloader { public partial class SearchForm : Form { + static string urlPrefix = "http://downloads.khinsider.com"; + WebClient webClient; public TextBox linkbox = null; public SearchForm() @@ -98,7 +100,7 @@ void ResolveImage(ref SearchItem searchItem) { HtmlWeb webContext = new HtmlWeb(); - var htmlDocument = webContext.Load(searchItem.Url); + var htmlDocument = webContext.Load(urlPrefix+searchItem.Url); var possibleCoverNode = htmlDocument.DocumentNode.SelectSingleNode("/html[1]/body[1]/table[1]/tbody[1]/tr[1]/td[1]/div[1]/div[1]/div[2]/div[2]/table[1]/tbody[1]/tr[1]/td[1]/div[1]/table[1]/tr[1]/td[1]/div[1]/a[1]"); if (possibleCoverNode != null) @@ -155,7 +157,7 @@ private void btn_add_Click(object sender, EventArgs e) return; } SearchItem currentItem = (SearchItem)list_result.SelectedItem; - linkbox.Text += currentItem.Url + Environment.NewLine; + linkbox.Text += urlPrefix + currentItem.Url + Environment.NewLine; } private void btn_add_all_Click(object sender, EventArgs e) @@ -163,7 +165,7 @@ private void btn_add_all_Click(object sender, EventArgs e) foreach (var item in list_result.Items) { SearchItem currentItem = (SearchItem)item; - linkbox.Text += currentItem.Url + Environment.NewLine; + linkbox.Text += urlPrefix + currentItem.Url + Environment.NewLine; } } }