private async Task <bool> DownloadCollectionAsync(EnqueuedCollection collection) { var tracksCollectionLength = collection.MediaCollection.Tracks.Count; var tracksQueue = new Queue <Track>(collection.MediaCollection.Tracks); var trackFiles = new List <TrackFile>(collection.MediaCollection.Tracks.Count); TrackDownloadEventArgs gEventArgs = null; while (tracksQueue.Count > 0) { var currentItem = tracksQueue.Dequeue(); var eventArgs = gEventArgs = new TrackDownloadEventArgs { CurrentItemIndex = (tracksCollectionLength - tracksQueue.Count), PercentCompleted = 0M, State = DownloadState.PreProcess, TotalItems = tracksCollectionLength, Track = currentItem, TrackFile = null }; OnTrackDequeued(eventArgs); try { if (!currentItem.IsDownloadable) { TrackSkipped?.Invoke(this, eventArgs); continue; } OnTrackDownloadProgress(eventArgs); if (currentItem.Album?.CoverPicture != null) { // Download album artwork if it's not cached var albumSmid = currentItem.Album.GetSmid(collection.Service.Info.Name).ToString(); if (!ImageCache.Instance.HasItem(albumSmid)) { eventArgs.State = DownloadState.DownloadingAlbumArtwork; OnTrackDownloadProgress(eventArgs); try { await ImageCache.Instance.AddByDownload(albumSmid, currentItem.Album.CoverPicture); } catch (Exception ex) { ImageCache.Instance.AddNull(albumSmid); Log.WriteException(Level.Warning, Tag, ex, "Exception occurred when download album artwork:"); } } } // Get the TrackFile eventArgs.TrackFile = await collection.Service.GetDownloadableTrackAsync(currentItem); var downloader = collection.Service.GetDownloader(eventArgs.TrackFile); downloader.Progress += (sender, args) => { eventArgs.Update(args); OnTrackDownloadProgress(eventArgs); }; downloader.Done += (sender, args) => { eventArgs.State = DownloadState.PostProcess; OnTrackDownloadProgress(eventArgs); }; // Generate the path var path = collection.GetPath(eventArgs.TrackFile); var tempPath = path; if (UseTempFile) { tempPath += "-temp"; } EnsureParentDirectories(tempPath); eventArgs.State = DownloadState.Downloading; // Write the track name to the logfile //var logpath = Path.Combine(eventArgs.Track.GetBasicPath(collection.PathFormat, collection.MediaCollection), "tracklist.txt"); StreamWriter file2 = new StreamWriter(collection.LogPath(eventArgs.TrackFile), true); file2.WriteLine(eventArgs.Track.TrackNumber + " - " + eventArgs.Track.Title); file2.Close(); // Begin download await downloader.DownloadAsyncTask(eventArgs.TrackFile, tempPath); trackFiles.Add(eventArgs.TrackFile); // Attempt to dispose the downloader, since the most probable case will be that it will // implement IDisposable if it uses sockets var disposableDownloader = downloader as IDisposable; disposableDownloader?.Dispose(); // Write the tag eventArgs.State = DownloadState.WritingTags; OnTrackDownloadProgress(eventArgs); Tagger.Write(collection, currentItem, eventArgs.TrackFile, tempPath); // Rename to proper path if (UseTempFile) { if (File.Exists(path)) { File.Delete(path); } File.Move(tempPath, path); } } catch (Exception ex) { var exEventArgs = new ExceptionEventArgs { CurrentState = eventArgs, Exception = ex }; OnException(exEventArgs); switch (exEventArgs.SkipTo) { case ExceptionSkip.Item: continue; case ExceptionSkip.Collection: case ExceptionSkip.Fail: skip = exEventArgs.SkipTo; return(false); default: throw new ArgumentOutOfRangeException(); } } // Raise the completed event even if an error occurred OnTrackDownloadCompleted(eventArgs); } // Write playlist if possible try { var writer = new PlaylistWriter(collection, trackFiles); switch (SavePlaylist) { case SavePlaylistSetting.DontSave: break; case SavePlaylistSetting.M3U: writer.WriteM3U8(); break; case SavePlaylistSetting.PLS: writer.WritePLS(); break; default: throw new ArgumentOutOfRangeException(); } } catch (Exception ex) { var exEventArgs = new ExceptionEventArgs { CurrentState = gEventArgs, Exception = ex }; OnException(exEventArgs); switch (exEventArgs.SkipTo) { case ExceptionSkip.Item: break; case ExceptionSkip.Collection: case ExceptionSkip.Fail: skip = exEventArgs.SkipTo; return(false); default: throw new ArgumentOutOfRangeException(); } } return(true); }