Пример #1
0
        public static void StartWorker(TagWriterWorkerArgs args)
        {
            if (worker.IsBusy)
            {
                Debug.LogError("Cannot start writing tags while already writing tags!");
            }
            else
            {
                // Ensure the manualResetEvent is set (unpaused)
                Paused = false;

                mainForm.ShowProgress(true);
                worker.RunWorkerAsync(args);
            }
        }
Пример #2
0
        // Called on the main thread when the worker is complete, or is cancelled
        private static void worker_Completed(object sender, RunWorkerCompletedEventArgs e)
        {
            // Hide the progress state for the taskbar icon
            TaskbarManager.Instance.SetProgressState(TaskbarProgressBarState.NoProgress);

            // Reset the "Processed" flag on all rows
            foreach (var entry in args.rows.Select(x => x.DataBoundItem).Cast <Entry>().Where(x => x.processed))
            {
                entry.processed = false;
            }

            if (e.Cancelled)
            {
                Debug.Log("--- CANCELLED ---");
            }

            // Check to see if the worker was cancelled.
            // If we weren't already removing/reverting tags, ask the user if they want to revert the changes that were made
            if (e.Cancelled && lastArgs != null && !lastArgs.removeTags)
            {
                if (MessageBox.Show("Do you want to revert changes made to files, and erase the playback statistics tags that have already been written to files?", "Revert", MessageBoxButtons.YesNo, MessageBoxIcon.Warning) == DialogResult.Yes)
                {
                    var args = lastArgs;
                    args.removeTags = true;

                    // Get subset of entries where the tags have already been written
                    args.rows = args.rows.Where(x => ((Entry)x.DataBoundItem).wroteTags);

                    // Start a new worker with the same args, but removeTags true
                    // Takes into account skip arguments too
                    StartWorker(args);

                    return;
                }
            }

            // If any errors occurred, show a dialogue containing the errors
            if (entryErrors != null && entryErrors.Count > 0)
            {
                new TagWriterErrors(entryErrors).Show();
            }

            args = lastArgs = null;
            Console.CursorVisible = true;
            mainForm.ShowProgress(false);
        }
Пример #3
0
        private static void worker_DoWork(object sender, DoWorkEventArgs e)
        {
            args = (TagWriterWorkerArgs)e.Argument;

            // Assign args to static variable. This is mainly used for cancellation/reverting, to understand what files were targeted in the operation
            lastArgs = args;

            int processed = 0;
            int count     = args.rows.Count();
            int rowIndex  = 0;

            // Misc
            int success           = 0;      // <- File was successfully written to
            int unmapped          = 0;      // <- Entry didn't have a file mapped to it
            int unsupportedFormat = 0;      // <- File was of an unsupported format (UnsupportedFormatException)
            int corruptFile       = 0;      // <- File was corrupted (CorruptFileException)
            int ioError           = 0;      // <- Error occured while loading the file (IOException)
            int otherError        = 0;      // <- Another exception occurred

            Debug.Log(string.Format("\nWriting tags for {0} files, using the following parameters:\nfilter:\t\t{1}\nskipDateAdded:\t{3}\nskipLastPlayed:\t{4}\nskipPlayCount:\t{5}\nskipRating:\t{6}\nremoveTags:\t{7}\n\nUse Ctrl-C to terminate", count, args.filter, args.filterNot, args.skipDateAdded, args.skipLastPlayed, args.skipPlayCount, args.skipRating, args.removeTags));

            // Exit here if we have nothing to process
            if (count == 0)
            {
                Debug.LogError("No entries to process!", true);
                return;
            }

            // Wait 3 seconds before starting
            if (Settings.Current.workerDelayStart)
            {
                for (int i = 0; i < 3; i++)
                {
                    Console.Write(".");
                    System.Threading.Thread.Sleep(1000);
                }
            }

            // A dictionary of entries and errors that have occured
            entryErrors = new ConcurrentDictionary <Entry, string>();

            // Progress report counter, gets set to the sw.ElapsedMilliseconds at every report progress interval
            long progressReportCounter = 0;

            // Start a new stopwatch
            var sw = System.Diagnostics.Stopwatch.StartNew();

            // Set the TaskbarProgressState
            TaskbarManager.Instance.SetProgressState(TaskbarProgressBarState.Normal);

            Console.Write("\n\n");

            void ReportProgress()
            {
                // Report progress to main thread to update DataGridView selection
                worker.ReportProgress(0, new ProgressArgs()
                {
                    processed       = processed,
                    count           = count,
                    timeMs          = sw.ElapsedMilliseconds,
                    currentRowIndex = rowIndex,
                });
            }

            int lowestBreakIndex = 0;

            // Set up the ParallelOptions
            var parallelOptions = new ParallelOptions()
            {
                MaxDegreeOfParallelism = Settings.Current.maxParallelThreads
            };

Resume:

            // Split the work into multiple parallel threads
            ParallelLoopResult parallelLoopResult = Parallel.ForEach(args.rows.Skip(lowestBreakIndex), parallelOptions, (row, parallelLoopState) =>
            {
                // Firstly, do some processing stuffs that can only be done in the worker

                // Check if we should pause
                if (isPaused)
                {
                    // Breaking allows current parallel iterations to complete before stopping
                    parallelLoopState.Break();
                    return;
                }

                // Check if we should cancel
                if (worker.CancellationPending)
                {
                    e.Cancel = true;
                    parallelLoopState.Stop();
                    return;
                }

                // Check if we should report progress
                // Only report progress every <progressReportInterval> milliseconds
                if (Settings.Current.workerReportsProgress && sw.ElapsedMilliseconds - progressReportCounter > Settings.Current.workerReportProgressInterval)
                {
                    rowIndex = row.Index;
                    progressReportCounter = sw.ElapsedMilliseconds;
                    ReportProgress();
                }

                // Fetch the DataBoundItem from the DataGridViewRow
                var entry = (Entry)row.DataBoundItem;

                // Check if the entry hasn't already been processed in this session
                // This may be the case if we paused previously
                if (!entry.processed)
                {
                    // Create a new DebugStack
                    // We're unable to log from a parallel for, as the logs would be out of sync - and wouldn't be clustered together
                    // So instead of logging normally, logging within a Parallel is done to a DebugStack, which essentially just collects the logs so that we can fire them all at once
                    // This is done here instead of in WriteTagsForRow, as that method has many exit points, therefore we would have to return a debugstack object from it anyway
                    var ds = new DebugStack();

                    // Write the tags for this Entry
                    var result = WriteTagsForRow(entry, ++processed, ds);

                    // Increment the error/success counter
                    switch (result)
                    {
                    case TagWriterReturnCode.Success: success++; break;

                    case TagWriterReturnCode.Unmapped: unmapped++; break;

                    case TagWriterReturnCode.UnsupportedFormat: unsupportedFormat++; break;

                    case TagWriterReturnCode.CorruptFile: corruptFile++; break;

                    case TagWriterReturnCode.IOEError: ioError++; break;

                    case TagWriterReturnCode.OtherError: otherError++; break;
                    }

                    // Set the processed flag on this entry to true
                    entry.processed = true;

                    // Log the DebugStack
                    ds.Push();
                }
            });

            // If the Parallel.ForEach was paused, isPaused will be set
            if (isPaused)
            {
                // Save the index of the lowest last item processed
                lowestBreakIndex = (int)parallelLoopResult.LowestBreakIteration;

                Debug.Log("--- PAUSED ---");
                sw.Stop();

                // Set the taskbar ProgressState to Paused
                TaskbarManager.Instance.SetProgressState(TaskbarProgressBarState.Paused);

                // Halt execution until unpaused (Reset)
                manualResetEvent.WaitOne();

                // If we're here, the operation was unpaused

                // Handle cancelling while paused
                if (worker.CancellationPending || e.Cancel)
                {
                    e.Cancel = true;
                    return;
                }

                // Otherwise resume
                else
                {
                    Debug.Log("--- RESUMED ---");
                    sw.Start();

                    TaskbarManager.Instance.SetProgressState(TaskbarProgressBarState.Normal);

                    // Jump back up to Resume
                    goto Resume;
                }
            }

            // Return if we cancelled
            if (e.Cancel)
            {
                return;
            }

            sw.Stop();

            // Do final ReportProgress
            rowIndex = args.rows.Last().Index;
            ReportProgress();

            string resultStr = string.Format("Took {0}ms ({1})\n {2} files were written to successfully\n {3} files could not be written to\n  {4} unsupported format\n  {5} corrupt\n  {6} IO errors\n  {7} unmapped\n  {8} other errors occurred", sw.ElapsedMilliseconds, TimeSpan.FromMilliseconds(sw.ElapsedMilliseconds).ToString("m\\:ss"), success, unsupportedFormat + corruptFile + ioError + unmapped + otherError, unsupportedFormat, corruptFile, ioError, unmapped, otherError);

            Debug.Log("Done! " + resultStr);

            mainForm.Invoke(() => mainForm.Flash(false));
            System.Media.SystemSounds.Exclamation.Play();

            MessageBox.Show(resultStr, "Done!", MessageBoxButtons.OK, MessageBoxIcon.Information);

            if (args.removeTags)
            {
                Debug.Log("\n" + Consts.REMOVE_TAGS_FINISHED);
            }
            else
            {
                Debug.Log("\n" + Consts.WRITE_TAGS_FINISHED);
            }
        }