using System; using System.Collections.Generic; using System.ComponentModel; using System.Threading; using System.Windows.Forms; using Autofac; using BitFactory.Logging; using MusicMetaTagger.Client.AllMusicGuide.RemoteDataAccess; using MusicMetaTagger.Client.AllMusicGuide.Services; using MusicMetaTagger.Core.Model; using MusicMetaTagger.Core.Services; using IContainer = Autofac.IContainer; namespace MusicMetaTagger.UI { public partial class Interface : Form, IProgressCallback { #region Private members private const string LogFilename = "tagger.log"; private readonly Logger _logger; private readonly BindingList _trackQueryStatuses; private CancellationTokenSource _cancellationTokenSource; private bool _exitClicked; private IMusicGuide _musicGuide; private ITrackSearcher _trackSearcher; private IMusicLibraryInterface _musicLibraryInterface; private ITrackRater _trackRater; private Options _options; private readonly IContainer _container; private ITrackOriginalRelease _trackOriginalRelease; #endregion #region Form Event Handlers /// /// Initializes a new instance of the class. /// public Interface() { InitializeComponent(); _trackQueryStatuses = new BindingList(); logDataGridView.DataSource = _trackQueryStatuses; _logger = new FileLogger(LogFilename); _cancellationTokenSource = new CancellationTokenSource(); var builder = new ContainerBuilder(); builder .RegisterAssemblyTypes(typeof(MusicGuideScraper).Assembly) .AsImplementedInterfaces() .Except() .Except(); builder.RegisterType().As(); builder.RegisterType().Named("MusicGuideScraper"); builder.RegisterDecorator((c, inner) => new MusicGuideCache(inner), fromKey: "MusicGuideScraper"); _container = builder.Build(); } /// /// Handles the Load event of the Interface control. /// /// The source of the event. /// The instance containing the event data. private void Interface_Load(object sender, EventArgs e) { _options = new Options(); components.Add(_options); optionsLabel.Text = _options.LibraryQuery.ToString(); var t = new Thread(OpenOrCreateAllMusicGuide); t.Start(); } /// /// Handles the Click event of the startButton control. /// /// The source of the event. /// The instance containing the event data. private void startButton_Click(object sender, EventArgs e) { librarySelector.Enabled = false; startButton.Enabled = false; optionsButton.Enabled = false; _musicLibraryInterface = librarySelector.SelectedLibrary; var t = new Thread(LibraryProcess); t.Start(); } /// /// Handles the Click event of the cancelButton control. /// /// The source of the event. /// The instance containing the event data. private void cancelButton_Click(object sender, EventArgs e) { UiInvoke(() => { toolStripStatusLabel.Text = "Cancelling..."; }); cancelButton.Enabled = false; _cancellationTokenSource.Cancel(); } /// /// Handles the FormClosing event of the Interface control. /// /// The source of the event. /// The instance containing the event data. private void Interface_FormClosing(object sender, FormClosingEventArgs e) { if (!exitButton.Enabled) { _exitClicked = true; _cancellationTokenSource.Cancel(); e.Cancel = true; } } /// /// Handles the Click event of the exitButton control. /// /// The source of the event. /// The instance containing the event data. private void exitButton_Click(object sender, EventArgs e) { Close(); } private void optionsButton_Click(object sender, EventArgs e) { _options.ShowDialog(); optionsLabel.Text = _options.LibraryQuery.ToString(); } #endregion #region Music Guide threaded methods private void OpenOrCreateAllMusicGuide() { UiInvoke(() => { startButton.Enabled = false; }); UiInvoke(() => { exitButton.Enabled = false; }); UiInvoke(() => { toolStripStatusLabel.Text = "Loading DataLayer..."; }); UiInvoke(() => { toolStripStatusLabel.Text = "Loading Complete."; }); UiInvoke(() => { startButton.Enabled = true; }); UiInvoke(() => { exitButton.Enabled = true; }); _musicGuide = _container.Resolve(); _trackSearcher = _container.Resolve(); _trackRater = _container.Resolve(); _trackOriginalRelease = _container.Resolve(); if (_exitClicked) { UiInvoke(() => { toolStripStatusLabel.Text = "Exiting..."; }); UiInvoke(() => exitButton_Click(this, new EventArgs())); } } #endregion #region Utility Methods private void LogStatusUpdate(StatusEventArgs data) { _logger.LogInfo(data.Status); } private static string ConcatToString(string original, string additional) { return !string.IsNullOrEmpty(original) ? string.Join(", ", additional, original) : additional; } #endregion #region Library Process methods private Track SearchTrack(TrackQueryStatus trackQuery) { _trackSearcher.SearchStatusUpdate += trackQuery.UpdateStatus; _trackSearcher.SearchStatusUpdate += LogStatusUpdate; var result = _trackSearcher.SearchTrack(trackQuery, _cancellationTokenSource.Token); _trackSearcher.SearchStatusUpdate -= trackQuery.UpdateStatus; _trackSearcher.SearchStatusUpdate -= LogStatusUpdate; return result; } /// /// Updates the track information to the library. /// /// The track query. /// The track info. private void UpdateTrack(TrackQueryStatus trackQuery, Track track) { var updateText = ""; var albumInfo = string.IsNullOrEmpty(track.AlbumId) ? null : _musicGuide.GetAlbum(track.AlbumId); var artistInfo = string.IsNullOrEmpty(track.PerformerIds[0]) ? null : _musicGuide.GetArtist(track.PerformerIds[0]); // If Track is not null, update ActualTrack from Track if (albumInfo != null) { // set album artwork if (!string.IsNullOrEmpty(albumInfo.CoverUrl)) { try { // TODO: Cover Art Reimplementation //var fileInfo = albumInfo.DownloadCoverArt(); //if (fileInfo != null) //{ // if (_musicLibraryInterface.UpdateAlbumArt(trackQuery, fileInfo, _options.LibraryUpdate.AlbumArtOverwrite)) // { // updateText = ConcatToString(updateText, "Added artwork"); // trackQuery.SetText(updateText); // } // if (mp3FileInfo != null) // _mp3Interface.UpdateAlbumArt(mp3FileInfo, fileInfo, _options.LibraryUpdate.AlbumArtOverwrite); //} } catch (Exception ex) { _logger.LogError(ex.ToString()); updateText = ConcatToString(updateText, "Artwork error: " + ex.Message); trackQuery.Status = updateText; } } if (artistInfo != null) { if (_options.LibraryUpdate.GenreUpdate) { // set genre string genre; if (artistInfo.Genre == "Rock" && albumInfo.Styles != null && albumInfo.Styles.Count > 0) genre = albumInfo.Styles[0]; else genre = artistInfo.Genre; if (_musicLibraryInterface.UpdateGenre(trackQuery, genre, _options.LibraryUpdate.GenreOverwrite)) { updateText = ConcatToString(updateText, "Genre: " + genre); trackQuery.Status = updateText; } } } // set year if (_options.LibraryUpdate.YearUpdate && !string.IsNullOrEmpty(albumInfo.ReleaseDate)) { var year = albumInfo.GetReleaseYear(); if (_musicLibraryInterface.UpdateYear(trackQuery, year, _options.LibraryUpdate.YearOverwrite)) { updateText = ConcatToString(updateText, "Year: " + year); trackQuery.Status = updateText; } } } if (_options.LibraryUpdate.RatingUpdate) { // update rating var rating = _trackRater.RateTrack(track); if (!rating.HasValue) { updateText = ConcatToString(updateText, "Track has no rating"); trackQuery.Status = updateText; } else { if (track.Pick) _musicLibraryInterface.AddComment(trackQuery, "-AMG Pick-"); var ratingInt = Convert.ToInt32(rating.Value*10); if (_musicLibraryInterface.UpdateRating(trackQuery, ratingInt, _options.LibraryUpdate.RatingOverwrite)) { updateText = ConcatToString(updateText, "Rating: " + ratingInt + " / 100"); trackQuery.Status = updateText; } } } } private void UiInvoke(Action action) { BeginInvoke(new MethodInvoker(action)); } private void LibraryProcess() { UiInvoke( () => { cancelButton.Enabled = true; cancelButton.Visible = true; exitButton.Enabled = false; }); Begin(); // Use LibraryQuery to build a list of trackQueries List trackQueries; try { trackQueries = _musicLibraryInterface.GetTrackQueries(_options.LibraryQuery, this); } catch (Exception ex) { _logger.LogError(ex.ToString()); trackQueries = new List(); UiInvoke(() => MessageBox.Show(ex.Message)); } End(); SetRange(0, trackQueries.Count); StepTo(0); // Search for TrackQuery var i = 1; foreach (var trackQuery in trackQueries) { Increment(1); SetText(string.Format("{0} / {1}", i++, trackQueries.Count)); if (_cancellationTokenSource.IsCancellationRequested) break; try { // add the row to the dataview var trackQueryStatus = trackQuery; UiInvoke(() => _trackQueryStatuses.Add(trackQueryStatus)); // scroll the row if it's at the bottom UiInvoke( () => { if (logDataGridView.RowCount > 2 && logDataGridView.Rows[logDataGridView.RowCount - 2].Displayed) { logDataGridView.FirstDisplayedScrollingRowIndex = logDataGridView.RowCount - 1; } }); // run the track query var track = SearchTrack(trackQuery); if (_cancellationTokenSource.IsCancellationRequested) break; // no results, continue on to the next track if (track == null) { trackQuery.Status = "Track not found"; continue; } // check that this is not a compilation track if (_options.LibraryUpdate.FindCompilationTrackOrigins) { track = _trackOriginalRelease. GetTrackOriginalRelease(track) ?? track; } UpdateTrack(trackQuery, track); } catch (Exception ex) { _logger.LogError(ex.ToString()); trackQuery.Status = ex.Message; } } if (_cancellationTokenSource.IsCancellationRequested) { SetText("Cancel complete."); _cancellationTokenSource = new CancellationTokenSource(); } UiInvoke( () => { exitButton.Enabled = true; cancelButton.Enabled = false; cancelButton.Visible = false; startButton.Enabled = true; librarySelector.Enabled = true; optionsButton.Enabled = true; }); if (_exitClicked) { SetText("Exiting..."); UiInvoke(() => exitButton_Click(this, new EventArgs())); } } private void aboutToolStripMenuItem_Click(object sender, EventArgs e) { var a = new About(); a.ShowDialog(this); } #endregion #region IProgressCallback public void Begin() { UiInvoke(() => toolStripProgressBar.Value = toolStripProgressBar.Minimum); } public void SetRange(int minimum, int maximum) { UiInvoke(() => toolStripProgressBar.Minimum = minimum); UiInvoke(() => toolStripProgressBar.Maximum = maximum); } public void SetText(string text) { UiInvoke(() => toolStripStatusLabel.Text = text); } public void StepTo(int val) { UiInvoke(() => toolStripProgressBar.Value = val); } public void Increment(int val) { UiInvoke(() => toolStripProgressBar.Value += val); } public bool IsAborting { get { return _cancellationTokenSource.IsCancellationRequested; } } public void End() { StepTo(toolStripProgressBar.Maximum); } #endregion } }