/// <summary> /// Updates the progress messages and the progressbar. /// </summary> /// <param name="fileUrl">The URL of the file that just progressed.</param> /// <param name="bytesReceived">The received bytes for the specified file.</param> private void UpdateProgress(String fileUrl, long bytesReceived) { DateTime now = DateTime.Now; lock (this.filesDownload) { // Compute new progress values TrackFile currentFile = this.filesDownload.Where(f => f.Url == fileUrl).First(); currentFile.BytesReceived = bytesReceived; long totalReceivedBytes = this.filesDownload.Sum(f => f.BytesReceived); long bytesToDownload = this.filesDownload.Sum(f => f.Size); Double bytesPerSecond; if (this.lastTotalReceivedBytes == 0) { // First time we update the progress bytesPerSecond = 0; this.lastTotalReceivedBytes = totalReceivedBytes; this.lastDownloadSpeedUpdate = now; } else if ((now - this.lastDownloadSpeedUpdate).TotalMilliseconds > 500) { // Last update of progress happened more than 500 milliseconds ago // We only update the download speed every 500+ milliseconds bytesPerSecond = ((Double)(totalReceivedBytes - this.lastTotalReceivedBytes)) / (now - this.lastDownloadSpeedUpdate).TotalSeconds; this.lastTotalReceivedBytes = totalReceivedBytes; this.lastDownloadSpeedUpdate = now; // Update UI this.Dispatcher.Invoke(new Action(() => { // Update download speed labelDownloadSpeed.Content = (bytesPerSecond / 1024).ToString("0.0") + " kB/s"; })); } // Update UI this.Dispatcher.Invoke(new Action(() => { if (!this.userCancelled) { // Update progress label labelProgress.Content = ((Double)totalReceivedBytes / (1024 * 1024)).ToString("0.00") + " MB / " + ((Double)bytesToDownload / (1024 * 1024)).ToString("0.00") + " MB"; // Update progress bar progressBar.Value = totalReceivedBytes; // Taskbar progress is between 0 and 1 TaskbarItemInfo.ProgressValue = totalReceivedBytes / progressBar.Maximum; } })); } }
/// <summary> /// Downloads and returns the cover art of the specified album. Depending on UserSettings, save the cover art in /// the album folder. /// </summary> /// <param name="album">The album.</param> private async Task <TagLib.Picture> DownloadCoverArtAsync(Album album) { TagLib.Picture artworkInTags = null; int tries = 0; bool artworkDownloaded = false; TrackFile currentFile = DownloadingFiles.Where(f => f.Url == album.ArtworkUrl).First(); do { if (_cancelDownloads) { // Abort return(null); } using (var webClient = new WebClient()) { ProxyHelper.SetProxy(webClient); // Update progress bar when downloading webClient.DownloadProgressChanged += (s, e) => { currentFile.BytesReceived = e.BytesReceived; }; // Register current download _cancellationTokenSource.Token.Register(webClient.CancelAsync); // Start download try { await webClient.DownloadFileTaskAsync(album.ArtworkUrl, album.ArtworkTempPath); artworkDownloaded = true; } catch (WebException ex) when(ex.Status == WebExceptionStatus.RequestCanceled) { // Downloads cancelled by the user return(null); } catch (TaskCanceledException) { // Downloads cancelled by the user return(null); } catch (WebException) { // Connection closed probably because no response from Bandcamp if (tries < App.UserSettings.DownloadMaxTries) { LogAdded(this, new LogArgs($"Unable to download artwork for album \"{album.Title}\". Try {tries + 1} of {App.UserSettings.DownloadMaxTries}", LogType.Warning)); } else { LogAdded(this, new LogArgs($"Unable to download artwork for album \"{album.Title}\". Hit max retries of {App.UserSettings.DownloadMaxTries}", LogType.Error)); } } if (artworkDownloaded) { // Convert/resize artwork to be saved in album folder if (App.UserSettings.SaveCoverArtInFolder && (App.UserSettings.CoverArtInFolderConvertToJpg || App.UserSettings.CoverArtInFolderResize)) { var settings = new ResizeSettings(); if (App.UserSettings.CoverArtInFolderConvertToJpg) { settings.Format = "jpg"; settings.Quality = 90; } if (App.UserSettings.CoverArtInFolderResize) { settings.MaxHeight = App.UserSettings.CoverArtInFolderMaxSize; settings.MaxWidth = App.UserSettings.CoverArtInFolderMaxSize; } await Task.Run(() => { ImageBuilder.Current.Build(album.ArtworkTempPath, album.ArtworkPath, settings); // Save it to the album folder }); } else if (App.UserSettings.SaveCoverArtInFolder) { await FileHelper.CopyFileAsync(album.ArtworkTempPath, album.ArtworkPath); } // Convert/resize artwork to be saved in tags if (App.UserSettings.SaveCoverArtInTags && (App.UserSettings.CoverArtInTagsConvertToJpg || App.UserSettings.CoverArtInTagsResize)) { var settings = new ResizeSettings(); if (App.UserSettings.CoverArtInTagsConvertToJpg) { settings.Format = "jpg"; settings.Quality = 90; } if (App.UserSettings.CoverArtInTagsResize) { settings.MaxHeight = App.UserSettings.CoverArtInTagsMaxSize; settings.MaxWidth = App.UserSettings.CoverArtInTagsMaxSize; } await Task.Run(() => { ImageBuilder.Current.Build(album.ArtworkTempPath, album.ArtworkTempPath, settings); // Save it to %Temp% }); } artworkInTags = new TagLib.Picture(album.ArtworkTempPath) { Description = "Picture" }; try { File.Delete(album.ArtworkTempPath); } catch { // Could not delete the file. Nevermind, it's in %Temp% folder... } // Note the file as downloaded currentFile.Downloaded = true; LogAdded(this, new LogArgs($"Downloaded artwork for album \"{album.Title}\"", LogType.IntermediateSuccess)); } tries++; if (!artworkDownloaded && tries < App.UserSettings.DownloadMaxTries) { await WaitForCooldownAsync(tries); } } } while (!artworkDownloaded && tries < App.UserSettings.DownloadMaxTries); return(artworkInTags); }
/// <summary> /// Downloads and tags a track. Returns true if the track has been correctly downloaded; false otherwise. /// </summary> /// <param name="album">The album of the track to download.</param> /// <param name="track">The track to download.</param> /// <param name="artwork">The cover art.</param> private async Task <bool> DownloadAndTagTrackAsync(Album album, Track track, TagLib.Picture artwork) { LogAdded(this, new LogArgs($"Downloading track \"{track.Title}\" from url: {track.Mp3Url}", LogType.VerboseInfo)); int tries = 0; bool trackDownloaded = false; TrackFile currentFile = DownloadingFiles.Where(f => f.Url == track.Mp3Url).First(); if (File.Exists(track.Path)) { long length = new FileInfo(track.Path).Length; if (currentFile.Size > length - (currentFile.Size * App.UserSettings.AllowedFileSizeDifference) && currentFile.Size < length + (currentFile.Size * App.UserSettings.AllowedFileSizeDifference)) { LogAdded(this, new LogArgs($"Track already exists within allowed file size range: track \"{Path.GetFileName(track.Path)}\" from album \"{album.Title}\" - Skipping download!", LogType.IntermediateSuccess)); return(false); } } do { if (_cancelDownloads) { // Abort return(false); } using (var webClient = new WebClient()) { ProxyHelper.SetProxy(webClient); // Update progress bar when downloading webClient.DownloadProgressChanged += (s, e) => { currentFile.BytesReceived = e.BytesReceived; }; // Register current download _cancellationTokenSource.Token.Register(webClient.CancelAsync); // Start download try { await webClient.DownloadFileTaskAsync(track.Mp3Url, track.Path); trackDownloaded = true; LogAdded(this, new LogArgs($"Downloaded track \"{track.Title}\" from url: {track.Mp3Url}", LogType.VerboseInfo)); } catch (WebException ex) when(ex.Status == WebExceptionStatus.RequestCanceled) { // Downloads cancelled by the user return(false); } catch (TaskCanceledException) { // Downloads cancelled by the user return(false); } catch (WebException) { // Connection closed probably because no response from Bandcamp if (tries + 1 < App.UserSettings.DownloadMaxTries) { LogAdded(this, new LogArgs($"Unable to download track \"{Path.GetFileName(track.Path)}\" from album \"{album.Title}\". Try {tries + 1} of {App.UserSettings.DownloadMaxTries}", LogType.Warning)); } else { LogAdded(this, new LogArgs($"Unable to download track \"{Path.GetFileName(track.Path)}\" from album \"{album.Title}\". Hit max retries of {App.UserSettings.DownloadMaxTries}", LogType.Error)); } } if (trackDownloaded) { if (App.UserSettings.ModifyTags) { // Tag (ID3) the file when downloaded var tagFile = TagLib.File.Create(track.Path); tagFile = TagHelper.UpdateArtist(tagFile, album.Artist, App.UserSettings.TagArtist); tagFile = TagHelper.UpdateAlbumArtist(tagFile, album.Artist, App.UserSettings.TagAlbumArtist); tagFile = TagHelper.UpdateAlbumTitle(tagFile, album.Title, App.UserSettings.TagAlbumTitle); tagFile = TagHelper.UpdateAlbumYear(tagFile, (uint)album.ReleaseDate.Year, App.UserSettings.TagYear); tagFile = TagHelper.UpdateTrackNumber(tagFile, (uint)track.Number, App.UserSettings.TagTrackNumber); tagFile = TagHelper.UpdateTrackTitle(tagFile, track.Title, App.UserSettings.TagTrackTitle); tagFile = TagHelper.UpdateTrackLyrics(tagFile, track.Lyrics, App.UserSettings.TagLyrics); tagFile = TagHelper.UpdateComments(tagFile, App.UserSettings.TagComments); tagFile.Save(); LogAdded(this, new LogArgs($"Tags saved for track \"{Path.GetFileName(track.Path)}\" from album \"{album.Title}\"", LogType.VerboseInfo)); } if (App.UserSettings.SaveCoverArtInTags && artwork != null) { // Save cover in tags when downloaded var tagFile = TagLib.File.Create(track.Path); tagFile.Tag.Pictures = new TagLib.IPicture[1] { artwork }; tagFile.Save(); LogAdded(this, new LogArgs($"Cover art saved in tags for track \"{Path.GetFileName(track.Path)}\" from album \"{album.Title}\"", LogType.VerboseInfo)); } // Note the file as downloaded currentFile.Downloaded = true; LogAdded(this, new LogArgs($"Downloaded track \"{Path.GetFileName(track.Path)}\" from album \"{album.Title}\"", LogType.IntermediateSuccess)); } tries++; if (!trackDownloaded && tries < App.UserSettings.DownloadMaxTries) { await WaitForCooldownAsync(tries); } } } while (!trackDownloaded && tries < App.UserSettings.DownloadMaxTries); return(trackDownloaded); }
/// <summary> /// Downloads the cover art. /// </summary> /// <param name="album">The album to download.</param> /// <param name="downloadsFolder">The downloads folder.</param> /// <param name="saveCovertArtInFolder">True to save cover art in the downloads folder; false otherwise.</param> /// <param name="convertCoverArtToJpg">True to convert the cover art to jpg; false otherwise.</param> /// <param name="resizeCoverArt">True to resize the covert art; false otherwise.</param> /// <param name="coverArtMaxSize">The maximum width/height of the cover art when resizing.</param> /// <returns></returns> private TagLib.Picture DownloadCoverArt(Album album, String downloadsFolder, Boolean saveCovertArtInFolder, Boolean convertCoverArtToJpg, Boolean resizeCoverArt, int coverArtMaxSize) { // Compute path where to save artwork String artworkPath = (saveCovertArtInFolder ? downloadsFolder : Path.GetTempPath()) + "\\" + album.Title.ToAllowedFileName() + Path.GetExtension(album.ArtworkUrl); if (artworkPath.Length > 256) { // Shorten the path (Windows doesn't support a path > 256 characters) artworkPath = (saveCovertArtInFolder ? downloadsFolder : Path.GetTempPath()) + "\\" + album.Title.ToAllowedFileName().Substring(0, 3) + Path.GetExtension(album.ArtworkUrl); } TagLib.Picture artwork = null; int tries = 0; Boolean artworkDownloaded = false; do { var doneEvent = new AutoResetEvent(false); using (var webClient = new WebClient()) { if (webClient.Proxy != null) { webClient.Proxy.Credentials = System.Net.CredentialCache.DefaultNetworkCredentials; } // Update progress bar when downloading webClient.DownloadProgressChanged += (s, e) => { UpdateProgress(album.ArtworkUrl, e.BytesReceived); }; // Warn when downloaded webClient.DownloadFileCompleted += (s, e) => { if (!e.Cancelled && e.Error == null) { artworkDownloaded = true; // Convert/resize artwork if (userSettings.ConvertCoverArtToJpg || userSettings.ResizeCoverArt) { var settings = new ResizeSettings(); if (convertCoverArtToJpg) { settings.Format = "jpg"; settings.Quality = 90; } if (resizeCoverArt) { settings.MaxHeight = userSettings.CoverArtMaxSize; settings.MaxWidth = userSettings.CoverArtMaxSize; } ImageBuilder.Current.Build(artworkPath, artworkPath, settings); } artwork = new TagLib.Picture(artworkPath) { Description = "Picture" }; // Delete the cover art file if it was saved in Temp if (!saveCovertArtInFolder) { try { System.IO.File.Delete(artworkPath); } catch { // Could not delete the file. Nevermind, it's in Temp/ folder... } } // Note the file as downloaded TrackFile currentFile = this.filesDownload.Where(f => f.Url == album.ArtworkUrl).First(); currentFile.Downloaded = true; Log($"Downloaded artwork for album \"{album.Title}\"", LogType.IntermediateSuccess); } else if (!e.Cancelled && e.Error != null) { if (tries < userSettings.DownloadMaxTries) { Log($"Unable to download artwork for album \"{album.Title}\". Try {tries} of {userSettings.DownloadMaxTries}", LogType.Warning); } else { Log($"Unable to download artwork for album \"{album.Title}\". Hit max retries of {userSettings.DownloadMaxTries}", LogType.Error); } } // Else the download has been cancelled (by the user) doneEvent.Set(); }; lock (this.pendingDownloads) { if (this.userCancelled) { // Abort return(null); } // Register current download this.pendingDownloads.Add(webClient); // Start download webClient.DownloadFileAsync(new Uri(album.ArtworkUrl), artworkPath); } // Wait for download to be finished doneEvent.WaitOne(); lock (this.pendingDownloads) { this.pendingDownloads.Remove(webClient); } } } while (!artworkDownloaded && tries < userSettings.DownloadMaxTries); return(artwork); }
/// <summary> /// Downloads and tags a track. Returns true if the track has been correctly downloaded; false otherwise. /// </summary> /// <param name="albumDirectoryPath">The path where to save the tracks.</param> /// <param name="album">The album of the track to download.</param> /// <param name="track">The track to download.</param> /// <param name="tagTrack">True to tag the track; false otherwise.</param> /// <param name="saveCoverArtInTags">True to save the cover art in the tag tracks; false otherwise.</param> /// <param name="artwork">The cover art.</param> private Boolean DownloadAndTagTrack(String albumDirectoryPath, Album album, Track track, Boolean tagTrack, Boolean saveCoverArtInTags, TagLib.Picture artwork) { Log($"Downloading track \"{track.Title}\" from url: {track.Mp3Url}", LogType.VerboseInfo); // Set location to save the file String trackPath = albumDirectoryPath + "\\" + GetFileName(album, track); if (trackPath.Length > 256) { // Shorten the path (Windows doesn't support a path > 256 characters) trackPath = albumDirectoryPath + "\\" + GetFileName(album, track).Substring(0, 3) + Path.GetExtension(trackPath); } int tries = 0; Boolean trackDownloaded = false; if (File.Exists(trackPath)) { long length = new FileInfo(trackPath).Length; foreach (TrackFile trackFile in filesDownload) { if (track.Mp3Url == trackFile.Url && trackFile.Size > length - (trackFile.Size * userSettings.AllowableFileSizeDifference) && trackFile.Size < length + (trackFile.Size * userSettings.AllowableFileSizeDifference)) { Log($"Track already exists within allowed filesize range: track \"{GetFileName(album, track)}\" from album \"{album.Title}\" - Skipping download!", LogType.IntermediateSuccess); return(false); } } } do { var doneEvent = new AutoResetEvent(false); using (var webClient = new WebClient()) { if (webClient.Proxy != null) { webClient.Proxy.Credentials = System.Net.CredentialCache.DefaultNetworkCredentials; } // Update progress bar when downloading webClient.DownloadProgressChanged += (s, e) => { UpdateProgress(track.Mp3Url, e.BytesReceived); }; // Warn & tag when downloaded webClient.DownloadFileCompleted += (s, e) => { WaitForCooldown(tries); tries++; if (!e.Cancelled && e.Error == null) { trackDownloaded = true; if (tagTrack) { // Tag (ID3) the file when downloaded TagLib.File tagFile = TagLib.File.Create(trackPath); tagFile.Tag.Album = album.Title; tagFile.Tag.AlbumArtists = new String[1] { album.Artist }; tagFile.Tag.Performers = new String[1] { album.Artist }; tagFile.Tag.Title = track.Title; tagFile.Tag.Track = (uint)track.Number; tagFile.Tag.Year = (uint)album.ReleaseDate.Year; tagFile.Tag.Lyrics = track.Lyrics; tagFile.Save(); } if (saveCoverArtInTags && artwork != null) { // Save cover in tags when downloaded TagLib.File tagFile = TagLib.File.Create(trackPath); tagFile.Tag.Pictures = new TagLib.IPicture[1] { artwork }; tagFile.Save(); } // Note the file as downloaded TrackFile currentFile = this.filesDownload.Where(f => f.Url == track.Mp3Url).First(); currentFile.Downloaded = true; Log($"Downloaded track \"{GetFileName(album, track)}\" from album \"{album.Title}\"", LogType.IntermediateSuccess); } else if (!e.Cancelled && e.Error != null) { if (tries < userSettings.DownloadMaxTries) { Log($"Unable to download track \"{GetFileName(album, track)}\" from album \"{album.Title}\". Try {tries} of {userSettings.DownloadMaxTries}", LogType.Warning); } else { Log($"Unable to download track \"{GetFileName(album, track)}\" from album \"{album.Title}\". Hit max retries of {userSettings.DownloadMaxTries}", LogType.Error); } } // Else the download has been cancelled (by the user) doneEvent.Set(); }; lock (this.pendingDownloads) { if (this.userCancelled) { // Abort return(false); } // Register current download this.pendingDownloads.Add(webClient); // Start download webClient.DownloadFileAsync(new Uri(track.Mp3Url), trackPath); } // Wait for download to be finished doneEvent.WaitOne(); lock (this.pendingDownloads) { this.pendingDownloads.Remove(webClient); } } } while (!trackDownloaded && tries < userSettings.DownloadMaxTries); return(trackDownloaded); }